diff --git a/.changeset/redesign-login-dialog.md b/.changeset/redesign-login-dialog.md new file mode 100644 index 000000000..673ec33a2 --- /dev/null +++ b/.changeset/redesign-login-dialog.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Redesign the web OAuth login dialog: lead with a single "Authorize in browser" button that opens the verification link with the device code already embedded, demote manual code entry to a clearly secondary fallback, and drop the duplicate open-browser and cancel controls so the order of steps is unambiguous. diff --git a/.changeset/web-login-command-copy.md b/.changeset/web-login-command-copy.md new file mode 100644 index 000000000..8d62d2825 --- /dev/null +++ b/.changeset/web-login-command-copy.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix the web login slash command description to match the browser authorization flow. diff --git a/apps/kimi-web/src/components/LoginDialog.vue b/apps/kimi-web/src/components/LoginDialog.vue index 71a50bee2..1a932c46b 100644 --- a/apps/kimi-web/src/components/LoginDialog.vue +++ b/apps/kimi-web/src/components/LoginDialog.vue @@ -158,12 +158,6 @@ async function retryFlow(): Promise { await startFlow(); } -function openBrowser(): void { - if (flow.value) { - window.open(flow.value.verificationUriComplete, '_blank', 'noopener,noreferrer'); - } -} - async function copyCode(): Promise { if (!flow.value) return; try { @@ -224,34 +218,39 @@ function formatSeconds(s: number): string { @@ -335,7 +327,6 @@ function formatSeconds(s: number): string { - @@ -420,72 +411,96 @@ function formatSeconds(s: number): string { } /* Device-code body */ -.dc-body { - padding: 16px 16px 8px; +.nb { + padding: 18px 16px 14px; display: flex; flex-direction: column; gap: 14px; } -.dc-instruction { +.nb-lead { font-size: var(--ui-font-size); color: var(--text); line-height: 1.6; } -.dc-uri-row { display: flex; } -.dc-uri-btn { - display: inline-flex; + +/* Primary path: open the complete URI (device code embedded) */ +.nb-primary { + display: flex; align-items: center; - gap: 6px; + justify-content: center; + gap: 8px; + width: 100%; + background: var(--blue); + color: var(--bg); + border: 1px solid var(--blue); + border-radius: 5px; font-family: var(--mono); font-size: var(--ui-font-size); - color: var(--blue); - background: var(--soft); - border: 1px solid var(--bd); - border-radius: 3px; - padding: 5px 10px; + font-weight: 600; + padding: 11px 14px; cursor: pointer; text-decoration: none; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 100%; } -.dc-uri-btn:hover { background: var(--bd); } -.dc-link-icon { flex: none; } +.nb-primary:hover { background: var(--blue2); border-color: var(--blue2); } -.dc-code-wrap { - border: 1px solid var(--line); - border-radius: 4px; - background: var(--panel); - padding: 10px 12px; -} -.dc-code-label { - font-size: max(9px, calc(var(--ui-font-size) - 4px)); +/* "or" divider */ +.nb-or { + display: flex; + align-items: center; + gap: 10px; color: var(--muted); - text-transform: uppercase; - letter-spacing: 0.05em; - margin-bottom: 6px; + font-size: max(9px, calc(var(--ui-font-size) - 2.5px)); + letter-spacing: 0.06em; } -.dc-code-row { +.nb-or::before, +.nb-or::after { + content: ""; + flex: 1; + height: 1px; + background: var(--line); +} + +/* Fallback path: open plain URI, type the code */ +.nb-fallback { + display: flex; + flex-direction: column; + gap: 9px; +} +.nb-fb-text { + font-size: calc(var(--ui-font-size) - 1.5px); + color: var(--dim); + line-height: 1.6; +} +.nb-fb-link { + color: var(--blue); + text-decoration: none; + border-bottom: 1px solid var(--bd); +} +.nb-fb-link:hover { border-bottom-color: var(--blue); } +.nb-code-row { display: flex; align-items: center; gap: 12px; + background: var(--panel); + border: 1px solid var(--line); + border-radius: 4px; + padding: 9px 11px; } -.dc-code-value { - font-size: calc(var(--ui-font-size) + 8px); - font-weight: 700; - color: var(--ink); - letter-spacing: 0.12em; +.nb-code { flex: 1; font-family: var(--mono); + font-size: calc(var(--ui-font-size) + 5px); + font-weight: 700; + color: var(--ink); + letter-spacing: 0.14em; } -.dc-copy-btn { +.nb-copy { display: inline-flex; align-items: center; gap: 5px; font-family: var(--mono); font-size: calc(var(--ui-font-size) - 2.5px); - padding: 4px 10px; + padding: 5px 11px; border: 1px solid var(--line); border-radius: 3px; background: none; @@ -494,19 +509,20 @@ function formatSeconds(s: number): string { flex: none; transition: background 0.1s; } -.dc-copy-btn:hover { background: var(--soft); } -.dc-copy-btn.is-copied { color: var(--ok); border-color: var(--ok); } +.nb-copy:hover { background: var(--soft); } +.nb-copy.is-copied { color: var(--ok); border-color: var(--ok); } -.dc-status-row { +/* Status */ +.nb-status { display: flex; align-items: center; gap: 8px; - padding: 8px 0; + padding-top: 11px; border-top: 1px solid var(--line2); } -.dc-spinner { display: flex; align-items: center; } -.dc-status-text { font-size: var(--ui-font-size); color: var(--dim); flex: 1; } -.dc-countdown { +.nb-spinner { display: flex; align-items: center; } +.nb-status-text { font-size: calc(var(--ui-font-size) - 1.5px); color: var(--dim); flex: 1; } +.nb-countdown { font-size: calc(var(--ui-font-size) - 2.5px); color: var(--muted); font-variant-numeric: tabular-nums; @@ -536,16 +552,6 @@ function formatSeconds(s: number): string { } .act-btn.primary:hover { background: var(--blue2); } -/* Footer */ -.footer-hint { - padding: 6px 14px; - font-size: max(9px, calc(var(--ui-font-size) - 3.5px)); - color: var(--faint); - border-top: 1px solid var(--line2); - background: var(--panel); - border-radius: 0 0 4px 4px; -} - @media (max-width: 640px) { .backdrop { align-items: stretch; @@ -563,24 +569,27 @@ function formatSeconds(s: number): string { overflow: hidden; } .center-body, - .dc-body { + .nb { overflow-y: auto; -webkit-overflow-scrolling: touch; } - .dc-code-row, - .dc-status-row, + .nb-code-row, + .nb-status, .actions { flex-wrap: wrap; } - .dc-code-value { + .nb-code { min-width: 0; overflow-wrap: anywhere; letter-spacing: 0.08em; } - .dc-copy-btn { + .nb-copy { min-height: 34px; } - .dc-status-text { + .nb-primary { + min-height: 44px; + } + .nb-status-text { min-width: 0; } .actions { diff --git a/apps/kimi-web/src/i18n/locales/en/commands.ts b/apps/kimi-web/src/i18n/locales/en/commands.ts index 796452963..1027cb50c 100644 --- a/apps/kimi-web/src/i18n/locales/en/commands.ts +++ b/apps/kimi-web/src/i18n/locales/en/commands.ts @@ -5,7 +5,7 @@ export default { clear: { desc: 'Clear and start a new session' }, model: { desc: 'Switch model' }, provider: { desc: 'Manage providers (add / remove / refresh)' }, - login: { desc: 'Sign in to the platform with an API key' }, + login: { desc: 'Sign in to Kimi in the browser' }, permission: { desc: 'Switch approval mode (manual / auto / yolo)' }, plan: { desc: 'Toggle plan mode on/off' }, swarm: { desc: 'Toggle swarm mode; /swarm runs a task in swarm' }, diff --git a/apps/kimi-web/src/i18n/locales/en/login.ts b/apps/kimi-web/src/i18n/locales/en/login.ts index f0520a555..3be4f32fd 100644 --- a/apps/kimi-web/src/i18n/locales/en/login.ts +++ b/apps/kimi-web/src/i18n/locales/en/login.ts @@ -2,22 +2,21 @@ export default { title: 'Sign in to Kimi Code', close: 'Close (Esc)', starting: 'Starting authorization flow…', - instruction: 'Open the link below in your browser and enter the device code to authorize:', - deviceCode: 'Device code', + lead: 'Click the button below to authorize in a new browser tab.', + authorizeInBrowser: 'Authorize in browser', + orDivider: 'or', + fallbackPrefix: 'On another device? Open ', + fallbackSuffix: ' and enter the device code:', copy: 'Copy', copied: 'Copied', waitingAuth: 'Waiting for authorization', - waitingAuthEllipsis: 'Waiting for authorization…', - openBrowser: 'Open browser', - cancel: 'Cancel', - footerHint: 'This dialog will close automatically once authorization completes · Esc to close', + waitingAutoClose: 'Waiting for authorization, signs in automatically…', success: 'Authorized', successHint: 'Loading, will close automatically…', expiredTitle: 'Authorization code expired', expiredHint: 'Please restart the authorization flow', retry: 'Retry', closeBtn: 'Close', - escClose: 'Esc to close', errorTitle: 'The current daemon does not support login yet', errorHint: 'Please upgrade kimi-code and try again', } as const; diff --git a/apps/kimi-web/src/i18n/locales/zh/commands.ts b/apps/kimi-web/src/i18n/locales/zh/commands.ts index d2a6513e9..9f0b6e323 100644 --- a/apps/kimi-web/src/i18n/locales/zh/commands.ts +++ b/apps/kimi-web/src/i18n/locales/zh/commands.ts @@ -5,7 +5,7 @@ export default { clear: { desc: '清空并新建会话' }, model: { desc: '切换模型' }, provider: { desc: '管理提供商 (添加/删除/刷新)' }, - login: { desc: '通过 API Key 登录平台' }, + login: { desc: '在浏览器中登录 Kimi' }, permission: { desc: '切换审批模式 (manual/auto/yolo)' }, plan: { desc: '切换计划模式 开/关' }, swarm: { desc: '切换 swarm 模式;/swarm <任务> 直接在 swarm 下执行' }, diff --git a/apps/kimi-web/src/i18n/locales/zh/login.ts b/apps/kimi-web/src/i18n/locales/zh/login.ts index f03e17f45..041550c62 100644 --- a/apps/kimi-web/src/i18n/locales/zh/login.ts +++ b/apps/kimi-web/src/i18n/locales/zh/login.ts @@ -2,22 +2,21 @@ export default { title: '登录 Kimi Code', close: '关闭 (Esc)', starting: '正在启动授权流程…', - instruction: '在浏览器中打开以下链接,输入设备码完成授权:', - deviceCode: '设备码', + lead: '点击下方按钮,在新标签页中完成授权。', + authorizeInBrowser: '在浏览器中授权', + orDivider: '或者', + fallbackPrefix: '换个设备?在浏览器打开 ', + fallbackSuffix: ' 输入设备码:', copy: '复制', copied: '已复制', waitingAuth: '等待授权', - waitingAuthEllipsis: '等待授权…', - openBrowser: '打开浏览器', - cancel: '取消', - footerHint: '授权完成后对话框将自动关闭 · Esc 关闭', + waitingAutoClose: '等待授权,完成后自动登录…', success: '已授权', successHint: '正在加载,稍后自动关闭…', expiredTitle: '授权码已过期', expiredHint: '请重新开始授权流程', retry: '重试', closeBtn: '关闭', - escClose: 'Esc 关闭', errorTitle: '当前 daemon 暂不支持登录', errorHint: '请升级 kimi-code 后重试', } as const;