cli-jaw 2.17.62 → 2.17.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +17 -17
- package/README.ko.md +17 -17
- package/README.md +17 -17
- package/README.zh-CN.md +17 -17
- package/dist/bin/commands/tui/input-handler.js +19 -0
- package/dist/bin/commands/tui/input-handler.js.map +1 -1
- package/dist/bin/commands/tui/overlays.js +8 -4
- package/dist/bin/commands/tui/overlays.js.map +1 -1
- package/dist/bin/star-prompt.js +1 -1
- package/dist/bin/star-prompt.js.map +1 -1
- package/dist/server.js +14 -1
- package/dist/server.js.map +1 -1
- package/dist/src/agent/claude-runtime-pool.js +2 -1
- package/dist/src/agent/claude-runtime-pool.js.map +1 -1
- package/dist/src/agent/claude-runtime-run.js +28 -7
- package/dist/src/agent/claude-runtime-run.js.map +1 -1
- package/dist/src/agent/lifecycle-handler.js +34 -6
- package/dist/src/agent/lifecycle-handler.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-events.js +136 -9
- package/dist/src/agent/runtime/claude-sdk-events.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-history-loader.js +24 -0
- package/dist/src/agent/runtime/claude-sdk-history-loader.js.map +1 -0
- package/dist/src/agent/runtime/claude-sdk-hooks.js +10 -4
- package/dist/src/agent/runtime/claude-sdk-hooks.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-messages.js +25 -2
- package/dist/src/agent/runtime/claude-sdk-messages.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-options.js +36 -3
- package/dist/src/agent/runtime/claude-sdk-options.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-permissions.js +32 -9
- package/dist/src/agent/runtime/claude-sdk-permissions.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-session.js +165 -17
- package/dist/src/agent/runtime/claude-sdk-session.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-version.js +8 -0
- package/dist/src/agent/runtime/claude-sdk-version.js.map +1 -0
- package/dist/src/agent/spawn/backend-codex-app.js +619 -0
- package/dist/src/agent/spawn/backend-codex-app.js.map +1 -0
- package/dist/src/agent/spawn/backend-context.js +2 -0
- package/dist/src/agent/spawn/backend-context.js.map +1 -0
- package/dist/src/agent/spawn/backend-copilot.js +385 -0
- package/dist/src/agent/spawn/backend-copilot.js.map +1 -0
- package/dist/src/agent/spawn/backend-native-acp.js +379 -0
- package/dist/src/agent/spawn/backend-native-acp.js.map +1 -0
- package/dist/src/agent/spawn/backend-pi.js +565 -0
- package/dist/src/agent/spawn/backend-pi.js.map +1 -0
- package/dist/src/agent/spawn/types.js +2 -0
- package/dist/src/agent/spawn/types.js.map +1 -0
- package/dist/src/agent/spawn.js +44 -1858
- package/dist/src/agent/spawn.js.map +1 -1
- package/dist/src/cli/claude-default-model-boot.js +36 -0
- package/dist/src/cli/claude-default-model-boot.js.map +1 -0
- package/dist/src/cli/claude-model-discovery.js +13 -1
- package/dist/src/cli/claude-model-discovery.js.map +1 -1
- package/dist/src/cli/claude-models.js +7 -1
- package/dist/src/cli/claude-models.js.map +1 -1
- package/dist/src/cli/commands.js +5 -15
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/tui/composer.js +3 -3
- package/dist/src/cli/tui/composer.js.map +1 -1
- package/dist/src/cli/tui/skill-mention.js +15 -0
- package/dist/src/cli/tui/skill-mention.js.map +1 -0
- package/dist/src/code-mode/manager.js +174 -9
- package/dist/src/code-mode/manager.js.map +1 -1
- package/dist/src/code-mode/providers/catalog.js +5 -3
- package/dist/src/code-mode/providers/catalog.js.map +1 -1
- package/dist/src/code-mode/providers/claude-history.js +132 -0
- package/dist/src/code-mode/providers/claude-history.js.map +1 -0
- package/dist/src/code-mode/providers/claude.js +74 -7
- package/dist/src/code-mode/providers/claude.js.map +1 -1
- package/dist/src/code-mode/providers/codex-app.js +7 -1
- package/dist/src/code-mode/providers/codex-app.js.map +1 -1
- package/dist/src/code-mode/session.js +158 -11
- package/dist/src/code-mode/session.js.map +1 -1
- package/dist/src/code-mode/store.js +416 -28
- package/dist/src/code-mode/store.js.map +1 -1
- package/dist/src/command-contract/catalog.js +3 -22
- package/dist/src/command-contract/catalog.js.map +1 -1
- package/dist/src/core/codex-config.js +3 -62
- package/dist/src/core/codex-config.js.map +1 -1
- package/dist/src/core/config.js +116 -14
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/runtime-settings.js +20 -10
- package/dist/src/core/runtime-settings.js.map +1 -1
- package/dist/src/core/settings-watch.js +1 -0
- package/dist/src/core/settings-watch.js.map +1 -1
- package/dist/src/core/skill-mentions.js +83 -0
- package/dist/src/core/skill-mentions.js.map +1 -0
- package/dist/src/manager/registry.js +5 -1
- package/dist/src/manager/registry.js.map +1 -1
- package/dist/src/manager/routes/desktop-status.js +1 -1
- package/dist/src/manager/routes/desktop-status.js.map +1 -1
- package/dist/src/orchestrator/pipeline.js +11 -1
- package/dist/src/orchestrator/pipeline.js.map +1 -1
- package/dist/src/prompt/builder.js +10 -4
- package/dist/src/prompt/builder.js.map +1 -1
- package/dist/src/prompt/templates/a1-system.md +1 -1
- package/dist/src/routes/code-native.js +51 -3
- package/dist/src/routes/code-native.js.map +1 -1
- package/dist/src/routes/settings.js +1 -4
- package/dist/src/routes/settings.js.map +1 -1
- package/dist/src/routes/skills.js +7 -0
- package/dist/src/routes/skills.js.map +1 -1
- package/dist/src/shared/skill-mention.js +61 -0
- package/dist/src/shared/skill-mention.js.map +1 -0
- package/dist/src/shared/tool-input.js +137 -0
- package/dist/src/shared/tool-input.js.map +1 -0
- package/package.json +7 -5
- package/public/css/activity.css +83 -20
- package/public/css/layout.css +2 -2
- package/public/css/sidebar.css +18 -0
- package/public/css/variables.css +23 -2
- package/public/dist/.vite/manifest.json +302 -334
- package/public/dist/assets/Activity-DPLlOJ46.js +1 -0
- package/public/dist/assets/AdvancedExport-BgOgGvc5.js +1 -0
- package/public/dist/assets/Agent-O_U8ny90.js +1 -0
- package/public/dist/assets/Browser-m6VynQXV.js +1 -0
- package/public/dist/assets/BrowserPanel-Bb-Bge6B.js +3 -0
- package/public/dist/assets/ChannelEnablementControl-BSuKQhTW.js +85 -0
- package/public/dist/assets/ChannelSetupEntry-DHLoZEzJ.js +1 -0
- package/public/dist/assets/ChannelsDiscord-B-G8vsYy.js +1 -0
- package/public/dist/assets/ChannelsSlack-oPuh922z.js +1 -0
- package/public/dist/assets/ChannelsTelegram-PmTv43KQ.js +1 -0
- package/public/dist/assets/CodeCanvas-CVbdp0S-.js +3 -0
- package/public/dist/assets/DashboardMeta-BqEiJrvr.js +1 -0
- package/public/dist/assets/DesignPanel-BOb-HMbl.js +2 -0
- package/public/dist/assets/{Developer-BdKPcw8Z.js → Developer-CbySB7_Z.js} +1 -1
- package/public/dist/assets/{DiffPanel-DtJuvU5y.js → DiffPanel-D9Cch5FG.js} +1 -1
- package/public/dist/assets/Display-C0Zdamcb.js +1 -0
- package/public/dist/assets/Display-DU4_76cS.js +1 -0
- package/public/dist/assets/DocPanel-DvtFw8AP.js +1 -0
- package/public/dist/assets/{Embedding-DvdTj1BF.js → Embedding-CCFMOB_2.js} +1 -1
- package/public/dist/assets/Employees-BanvF58d.js +1 -0
- package/public/dist/assets/FolderPanel-CJvwRwIV.js +3 -0
- package/public/dist/assets/Heartbeat-uyi_MpUd.js +1 -0
- package/public/dist/assets/InlineWarn-Cq4z28F3.js +1 -0
- package/public/dist/assets/MarkdownRenderer-Cc3DeHvm.js +1 -0
- package/public/dist/assets/{MarkdownRenderer-DhonsAPC.js → MarkdownRenderer-DzN66XVb.js} +1 -1
- package/public/dist/assets/Mcp-rjPoL_Zo.js +4 -0
- package/public/dist/assets/Memory-C9I33pIi.js +1 -0
- package/public/dist/assets/{MilkdownWysiwygEditor-D6JaLDE4.js → MilkdownWysiwygEditor-xObhviw7.js} +1 -1
- package/public/dist/assets/ModelProvider-fiUktMaO.js +1 -0
- package/public/dist/assets/Network-CuqFrOvR.js +1 -0
- package/public/dist/assets/NotesGraphWorkspace-DO9LtVGF.js +1 -0
- package/public/dist/assets/PanelLayoutProvider-DRZ4QkBC.js +1 -0
- package/public/dist/assets/Permissions-BltXiZnz.js +1 -0
- package/public/dist/assets/{Permissions-BwiMEoTV.js → Permissions-DcCbFMIq.js} +1 -1
- package/public/dist/assets/Profile-DUL7-RE_.js +1 -0
- package/public/dist/assets/Prompts-C9vY8ivQ.js +1 -0
- package/public/dist/assets/SpeechKeys-BJ8V7U5E.js +1 -0
- package/public/dist/assets/TelegramHub-DtyjLGFR.js +1 -0
- package/public/dist/assets/{TerminalPanel-Bu9i-P2f.js → TerminalPanel-c1xhiy53.js} +1 -1
- package/public/dist/assets/app-BjKzqXCv.css +1 -0
- package/public/dist/assets/app-CZwx0V6K.js +59 -0
- package/public/dist/assets/bgtask-badge-CnVkcp3c.js +1 -0
- package/public/dist/assets/{bgtask-badge-DfiHuzxT.js → bgtask-badge-Cp26S8Uq.js} +1 -1
- package/public/dist/assets/client-platform-WO8m6kPQ.js +1 -0
- package/public/dist/assets/{employees-BQaJNp-u.js → employees-CsvI0_5V.js} +1 -1
- package/public/dist/assets/error-normalize-clOi-AAA.js +1 -0
- package/public/dist/assets/fields-CicOWY-X.js +1 -0
- package/public/dist/assets/iframe-renderer-DWx9v2Iz.js +1 -0
- package/public/dist/assets/iframe-renderer-DmHYOI1A.js +142 -0
- package/public/dist/assets/{react-C_pPTFsM.js → jsx-runtime-CkEUZM6i.js} +1 -1
- package/public/dist/assets/manager-CCXm5Yfe.js +5 -0
- package/public/dist/assets/manager-D9rhKFKc.css +1 -0
- package/public/dist/assets/manager-shortcuts-Cqmr0mEq.js +1 -0
- package/public/dist/assets/{memory-EG1B51up.js → memory-C6qtAMdJ.js} +1 -1
- package/public/dist/assets/memory-PPofQPeB.js +1 -0
- package/public/dist/assets/message-history-DcjBuubA.js +4 -0
- package/public/dist/assets/message-history-bw4qF01Z.js +1 -0
- package/public/dist/assets/page-shell-DfinU3mb.js +1 -0
- package/public/dist/assets/react-dom-URBLzV3w.js +1 -0
- package/public/dist/assets/{render-Dp07lZAJ.js → render-DfYro-fG.js} +1 -1
- package/public/dist/assets/{settings-jlI4X-zt.js → settings-CvsLWlJn.js} +38 -30
- package/public/dist/assets/settings-DMiQe469.js +1 -0
- package/public/dist/assets/settings-DgAlohx9.js +1 -0
- package/public/dist/assets/settings-core-BH7J_Rbm.js +1 -0
- package/public/dist/assets/{settings-core-B5Uf_lWY.js → settings-core-pnpw-eZv.js} +1 -1
- package/public/dist/assets/{settings-embedding-BfWzWKMM.js → settings-embedding-BRW0WZvh.js} +3 -3
- package/public/dist/assets/{settings-embedding-DhEdT86W.css → settings-embedding-CUQ8Is3y.css} +1 -1
- package/public/dist/assets/settings-types-CYKy0xz4.js +1 -0
- package/public/dist/assets/shared-D1begqvi.js +1 -0
- package/public/dist/assets/{sidebar-BUemG2ZN.js → sidebar-C_80w4iY.js} +3 -3
- package/public/dist/assets/{skills-CKUcEN9m.js → skills-D_2adqge.js} +1 -1
- package/public/dist/assets/skills-DeNVAVgt.js +1 -0
- package/public/dist/assets/{slash-commands-D34GgHFX.js → slash-commands-B_Hq5RkC.js} +2 -2
- package/public/dist/assets/slash-commands-Cj5qoRWc.js +1 -0
- package/public/dist/assets/tool-input-DI7uu8Ah.js +2 -0
- package/public/dist/assets/{trace-drawer-DBmK-5F_.js → trace-drawer-B6XuPodh.js} +1 -1
- package/public/dist/assets/ui-BbR_fFiK.js +1 -0
- package/public/dist/assets/{ui-vm2iuLcS.js → ui-D8xzm7DN.js} +1 -1
- package/public/dist/index.html +11 -3
- package/public/dist/manager/index.html +3 -3
- package/public/dist/settings/index.html +2 -2
- package/public/index.html +9 -1
- package/public/js/features/activity-history.ts +50 -21
- package/public/js/features/activity-rows.ts +158 -10
- package/public/js/features/activity-view.ts +5 -5
- package/public/js/features/chat.ts +2 -0
- package/public/js/features/employees.ts +1 -0
- package/public/js/features/resize-guard.ts +39 -0
- package/public/js/features/settings-cli-status.ts +49 -17
- package/public/js/features/settings-types.ts +39 -1
- package/public/js/features/skill-mentions.ts +143 -0
- package/public/js/main.ts +15 -0
- package/public/js/virtual-scroll.ts +67 -1
- package/public/locales/en.json +2 -0
- package/public/locales/ja.json +2 -0
- package/public/locales/ko.json +2 -0
- package/public/locales/zh.json +2 -0
- package/public/manager/src/AppChrome.tsx +2 -0
- package/public/manager/src/InstancePreview.tsx +1 -1
- package/public/manager/src/SidebarRailRouter.tsx +3 -1
- package/public/manager/src/browser-panel/BrowserPanel.tsx +47 -12
- package/public/manager/src/browser-panel/browser-address-state.ts +11 -0
- package/public/manager/src/client-platform.ts +18 -0
- package/public/manager/src/code/CodeCanvas.tsx +4 -2
- package/public/manager/src/code/CodeComposer.tsx +22 -10
- package/public/manager/src/code/CodeDraftEmptyState.tsx +64 -0
- package/public/manager/src/code/CodeSessionList.tsx +154 -40
- package/public/manager/src/code/CodeTranscript.tsx +104 -16
- package/public/manager/src/code/CodeWorkbench.tsx +21 -11
- package/public/manager/src/code/CodeWorkspaceHeader.tsx +5 -10
- package/public/manager/src/code/ComposerFooter.tsx +9 -1
- package/public/manager/src/code/code-composer.css +1 -1
- package/public/manager/src/code/code-controller-draft-storage.ts +16 -8
- package/public/manager/src/code/code-controller-drafts.ts +33 -6
- package/public/manager/src/code/code-controller-runtime.ts +189 -17
- package/public/manager/src/code/code-controller-types.ts +19 -1
- package/public/manager/src/code/code-recent-workspaces.ts +25 -0
- package/public/manager/src/code/code-session-client.ts +19 -1
- package/public/manager/src/code/code-session-sidebar.css +38 -0
- package/public/manager/src/code/code-session-state.ts +2 -0
- package/public/manager/src/code/code-types.ts +47 -0
- package/public/manager/src/code/code-workbench.css +36 -1
- package/public/manager/src/code/code.css +171 -7
- package/public/manager/src/code/session-order.ts +80 -0
- package/public/manager/src/code/tool-summary.ts +45 -11
- package/public/manager/src/folder-panel/folder-shortcuts.ts +5 -9
- package/public/manager/src/hooks/useCommandPalette.ts +3 -7
- package/public/manager/src/main.tsx +1 -0
- package/public/manager/src/manager-components.css +25 -23
- package/public/manager/src/manager-dashboard-reminders.css +2 -2
- package/public/manager/src/manager-layout.css +1 -1
- package/public/manager/src/manager-shortcut-runner.ts +9 -2
- package/public/manager/src/manager-shortcuts.ts +83 -21
- package/public/manager/src/panels/desktop-bridge.ts +4 -1
- package/public/manager/src/panels/panels.css +9 -0
- package/public/manager/src/preview.ts +19 -4
- package/public/manager/src/settings/pages/AdvancedExport.tsx +28 -30
- package/public/manager/src/settings/pages/Browser.tsx +45 -47
- package/public/manager/src/settings/pages/ChannelsDiscord.tsx +51 -26
- package/public/manager/src/settings/pages/ChannelsSlack.tsx +59 -17
- package/public/manager/src/settings/pages/ChannelsTelegram.tsx +51 -26
- package/public/manager/src/settings/pages/Display.tsx +2 -1
- package/public/manager/src/settings/pages/Employees.tsx +3 -3
- package/public/manager/src/settings/pages/Mcp.tsx +220 -200
- package/public/manager/src/settings/pages/Memory.tsx +35 -41
- package/public/manager/src/settings/pages/ModelProvider.tsx +23 -98
- package/public/manager/src/settings/pages/Network.tsx +5 -4
- package/public/manager/src/settings/pages/Permissions.tsx +38 -16
- package/public/manager/src/settings/pages/Prompts.tsx +12 -9
- package/public/manager/src/settings/pages/SpeechKeys.tsx +132 -139
- package/public/manager/src/settings/pages/TelegramHub.tsx +67 -20
- package/public/manager/src/settings/pages/components/AvatarCard.tsx +9 -8
- package/public/manager/src/settings/pages/components/ChannelSetupEntry.tsx +2 -2
- package/public/manager/src/settings/pages/components/EmployeeRow.tsx +6 -12
- package/public/manager/src/settings/pages/components/HealthBadge.tsx +11 -6
- package/public/manager/src/settings/pages/components/HeartbeatJobRow.tsx +35 -16
- package/public/manager/src/settings/pages/components/HeartbeatJobsSection.tsx +11 -11
- package/public/manager/src/settings/pages/components/HeartbeatMarkdownSection.tsx +7 -7
- package/public/manager/src/settings/pages/components/InlineWarn.tsx +4 -9
- package/public/manager/src/settings/pages/components/McpServerCard.tsx +39 -27
- package/public/manager/src/settings/pages/components/MemoryRow.tsx +21 -16
- package/public/manager/src/settings/pages/components/PerCliRow.tsx +122 -121
- package/public/manager/src/settings/pages/components/PiProfileDialog.tsx +9 -6
- package/public/manager/src/settings/pages/components/SlackSetup.tsx +8 -8
- package/public/manager/src/settings/pages/components/TransportStatusChips.tsx +14 -10
- package/public/manager/src/settings/pages/components/agent/AgentEmployeesSection.tsx +26 -26
- package/public/manager/src/settings/pages/components/agent/PermissionQuickSection.tsx +5 -5
- package/public/manager/src/settings/pages/components/agent/RuntimeEmployeeRow.tsx +14 -11
- package/public/manager/src/settings/pages/components/agent/RuntimeHeader.tsx +1 -3
- package/public/manager/src/settings/pages/components/agent/agent-meta.ts +0 -2
- package/public/manager/src/settings/pages/components/runtime-transport-field.tsx +2 -2
- package/public/manager/src/settings/pages/manager/Activity.tsx +6 -2
- package/public/manager/src/settings/pages/manager/Display.tsx +3 -8
- package/public/manager/src/settings/pages/manager/shared.tsx +44 -33
- package/public/manager/src/settings/pages/page-shell.tsx +66 -0
- package/public/manager/src/settings-agent.css +185 -36
- package/public/manager/src/settings-standalone.tsx +1 -0
- package/public/manager/src/settings-surface.css +96 -0
- package/public/manager/src/types.ts +1 -0
- package/scripts/ci/install-ripgrep.sh +35 -0
- package/scripts/collect-fresh-install-evidence.sh +2 -2
- package/scripts/install-risk-gate.mjs +2 -3
- package/scripts/install-wsl.sh +1 -1
- package/scripts/install.ps1 +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/release-1.6.0.sh +1 -1
- package/scripts/release-gates.mjs +48 -13
- package/scripts/release-preview.sh +2 -2
- package/scripts/restack-pr.sh +88 -0
- package/scripts/verify-electron-update-metadata.mjs +1 -1
- package/scripts/windows-support-contract.json +1 -1
- package/public/dist/assets/Activity-BUrp73Re.js +0 -1
- package/public/dist/assets/AdvancedExport-B7h_ZvBu.js +0 -1
- package/public/dist/assets/Agent-DnbZJP7b.js +0 -1
- package/public/dist/assets/Browser-CiBoK5Qx.js +0 -1
- package/public/dist/assets/BrowserPanel-SZAUc9T2.js +0 -3
- package/public/dist/assets/ChannelEnablementControl-Dl0uYx38.js +0 -85
- package/public/dist/assets/ChannelSetupEntry-DxtOrN01.js +0 -1
- package/public/dist/assets/ChannelsDiscord-BEGGwd_3.js +0 -1
- package/public/dist/assets/ChannelsSlack-BryC2Ahr.js +0 -1
- package/public/dist/assets/ChannelsTelegram-CGSpaImJ.js +0 -1
- package/public/dist/assets/CodeCanvas-DpkFi5Au.js +0 -2
- package/public/dist/assets/DashboardMeta-Dh-tasbY.js +0 -1
- package/public/dist/assets/DesignPanel-NLNK9wCo.js +0 -2
- package/public/dist/assets/Display-BHGFX3O0.js +0 -1
- package/public/dist/assets/Display-BW-_pKRf.js +0 -1
- package/public/dist/assets/DocPanel-BM41cYpS.js +0 -1
- package/public/dist/assets/Employees-BuFw6-fT.js +0 -1
- package/public/dist/assets/FolderPanel-C-eQT3Yq.js +0 -3
- package/public/dist/assets/Heartbeat-CIVtKWNc.js +0 -1
- package/public/dist/assets/InlineWarn-DIMbcbzk.js +0 -1
- package/public/dist/assets/MarkdownRenderer-DyVx6DxM.js +0 -1
- package/public/dist/assets/Mcp-uKjor7vh.js +0 -4
- package/public/dist/assets/Memory-BSDIXT0B.js +0 -1
- package/public/dist/assets/ModelProvider-DMh2tXUx.js +0 -1
- package/public/dist/assets/Network-4Pa6JkhK.js +0 -1
- package/public/dist/assets/NotesGraphWorkspace-CgdC-fM6.js +0 -1
- package/public/dist/assets/PanelLayoutProvider-mn8sI0h-.js +0 -1
- package/public/dist/assets/Permissions-CodIW-Yd.js +0 -1
- package/public/dist/assets/Profile-DPqrYA44.js +0 -1
- package/public/dist/assets/Prompts-CvAx7QqR.js +0 -1
- package/public/dist/assets/SpeechKeys-CVBCqoBn.js +0 -1
- package/public/dist/assets/TelegramHub-4zKwpk0P.js +0 -1
- package/public/dist/assets/app-ByMfeXp-.js +0 -55
- package/public/dist/assets/app-DAJWqs33.css +0 -1
- package/public/dist/assets/bgtask-badge-Ch88fnhf.js +0 -1
- package/public/dist/assets/error-normalize-CL69Bl7z.js +0 -1
- package/public/dist/assets/fields-mlGpofqQ.js +0 -1
- package/public/dist/assets/iframe-renderer-Bj1JolO8.js +0 -142
- package/public/dist/assets/iframe-renderer-DWnvUSrx.js +0 -1
- package/public/dist/assets/jsx-runtime-NZYk81nU.js +0 -1
- package/public/dist/assets/manager-Bi_xw7TW.js +0 -5
- package/public/dist/assets/manager-BnQHAKk6.css +0 -1
- package/public/dist/assets/manager-shortcuts-BDOzd8zm.js +0 -1
- package/public/dist/assets/memory-Bk_cb3sw.js +0 -1
- package/public/dist/assets/message-history-D2vniu04.js +0 -4
- package/public/dist/assets/message-history-GAtF_wbZ.js +0 -1
- package/public/dist/assets/page-shell-BeGD0rRQ.js +0 -1
- package/public/dist/assets/react-dom-DyUOYpVL.js +0 -1
- package/public/dist/assets/settings-W4KG9ivu.js +0 -1
- package/public/dist/assets/settings-WVGhtneN.js +0 -1
- package/public/dist/assets/settings-client-CmwRsVd1.js +0 -1
- package/public/dist/assets/settings-core-CgKGnMA5.js +0 -1
- package/public/dist/assets/settings-types-D3cd6SWy.js +0 -1
- package/public/dist/assets/shared-CiNP20HB.js +0 -1
- package/public/dist/assets/skills-Co7Mr-qP.js +0 -1
- package/public/dist/assets/slash-commands-BEr-vmIr.js +0 -1
- package/public/dist/assets/ui-TUyJsMUf.js +0 -1
- package/public/manager/src/settings/pages/components/EngineSubsection.tsx +0 -25
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// ── Resize Guard ──
|
|
2
|
+
// A viewport-resize burst (manager sidebar drag resizes the iframe, or a window
|
|
3
|
+
// edge drag) reflows every scrollable subtree each frame. Browser scroll
|
|
4
|
+
// anchoring then rewrites scrollTop per frame and content visibly jumps.
|
|
5
|
+
// While a burst is active `body.is-resizing` lets CSS suppress scroll
|
|
6
|
+
// anchoring so the page only reflows.
|
|
7
|
+
|
|
8
|
+
export const RESIZE_GUARD_SETTLE_MS = 160;
|
|
9
|
+
|
|
10
|
+
export type ResizeGuardHandlers = {
|
|
11
|
+
onResizeStart: () => void;
|
|
12
|
+
onResizeEnd: () => void;
|
|
13
|
+
settleMs?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function createResizeGuard({ onResizeStart, onResizeEnd, settleMs = RESIZE_GUARD_SETTLE_MS }: ResizeGuardHandlers): () => void {
|
|
17
|
+
let active = false;
|
|
18
|
+
let settleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
19
|
+
return () => {
|
|
20
|
+
if (!active) {
|
|
21
|
+
active = true;
|
|
22
|
+
onResizeStart();
|
|
23
|
+
}
|
|
24
|
+
clearTimeout(settleTimer);
|
|
25
|
+
settleTimer = setTimeout(() => {
|
|
26
|
+
active = false;
|
|
27
|
+
settleTimer = undefined;
|
|
28
|
+
onResizeEnd();
|
|
29
|
+
}, settleMs);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function initResizeGuard(): void {
|
|
34
|
+
const notify = createResizeGuard({
|
|
35
|
+
onResizeStart: () => document.body.classList.add('is-resizing'),
|
|
36
|
+
onResizeEnd: () => document.body.classList.remove('is-resizing'),
|
|
37
|
+
});
|
|
38
|
+
window.addEventListener('resize', notify, { passive: true });
|
|
39
|
+
}
|
|
@@ -5,7 +5,7 @@ import { t } from './i18n.js';
|
|
|
5
5
|
import { state } from '../state.js';
|
|
6
6
|
import { ICONS } from '../icons.js';
|
|
7
7
|
import { providerIcon, providerLabel } from '../provider-icons.js';
|
|
8
|
-
import { describeCliProbe, describeNativeStartFailure, resolveQuotaWindowDisplay, type CliStatusInfo, type QuotaEntry } from './settings-types.js';
|
|
8
|
+
import { describeCliProbe, describeNativeStartFailure, hasUsableCliStatus, isCliStatusUsable, resolveQuotaWindowDisplay, type CliStatusInfo, type QuotaEntry } from './settings-types.js';
|
|
9
9
|
import {
|
|
10
10
|
buildAccountParts,
|
|
11
11
|
normalizeQuotaWindowLabel,
|
|
@@ -34,6 +34,7 @@ let cliStatusLoadInFlight: Promise<void> | null = null;
|
|
|
34
34
|
let cliStatusRefreshFeedbackTimer: number | null = null;
|
|
35
35
|
|
|
36
36
|
const CLI_STATUS_COLLAPSED_KEY = 'cliStatusCollapsed';
|
|
37
|
+
const CLI_STATUS_UNDETECTED_EXPANDED_KEY = 'cliStatusUndetectedExpanded';
|
|
37
38
|
|
|
38
39
|
export function isEmbeddedPreviewFrame(): boolean {
|
|
39
40
|
try {
|
|
@@ -53,6 +54,16 @@ function saveCliStatusCollapsed(collapsed: boolean): void {
|
|
|
53
54
|
catch { /* ignore */ }
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
function readCliStatusUndetectedExpanded(): boolean {
|
|
58
|
+
try { return localStorage.getItem(CLI_STATUS_UNDETECTED_EXPANDED_KEY) === 'true'; }
|
|
59
|
+
catch { return false; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function saveCliStatusUndetectedExpanded(expanded: boolean): void {
|
|
63
|
+
try { localStorage.setItem(CLI_STATUS_UNDETECTED_EXPANDED_KEY, expanded ? 'true' : 'false'); }
|
|
64
|
+
catch { /* ignore */ }
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
function formatCliStatusRefreshTime(date: Date): string {
|
|
57
68
|
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`;
|
|
58
69
|
}
|
|
@@ -295,7 +306,8 @@ function renderCliStatus(data: { cliStatus: Record<string, CliStatusInfo> | null
|
|
|
295
306
|
copilot: { install: 'npm i -g copilot', auth: t('cli.copilot.authHint') },
|
|
296
307
|
};
|
|
297
308
|
|
|
298
|
-
|
|
309
|
+
const usableCards: string[] = [];
|
|
310
|
+
const undetectedCards: string[] = [];
|
|
299
311
|
|
|
300
312
|
if (!cliStatus || typeof cliStatus !== 'object') {
|
|
301
313
|
if (el) el.innerHTML = '<div style="color:var(--text-dim);font-size:11px">Failed to load CLI status</div>';
|
|
@@ -431,7 +443,7 @@ function renderCliStatus(data: { cliStatus: Record<string, CliStatusInfo> | null
|
|
|
431
443
|
? `<div role="alert" style="font-size:10px;color:var(--warning);margin:2px 0 0 16px">${escapeHtml(startFailure.message)}</div>`
|
|
432
444
|
: '';
|
|
433
445
|
|
|
434
|
-
|
|
446
|
+
const cardHtml = `
|
|
435
447
|
<div class="settings-group" style="margin-bottom:6px;padding:8px 10px">
|
|
436
448
|
<div class="cli-status-row" style="display:flex;align-items:center">
|
|
437
449
|
<span class="cli-dot ${dotClass}"></span>
|
|
@@ -445,24 +457,32 @@ function renderCliStatus(data: { cliStatus: Record<string, CliStatusInfo> | null
|
|
|
445
457
|
${windowsHtml}
|
|
446
458
|
</div>
|
|
447
459
|
`;
|
|
460
|
+
(isCliStatusUsable(info, q) ? usableCards : undetectedCards).push(cardHtml);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
let html = usableCards.join('');
|
|
464
|
+
if (undetectedCards.length) {
|
|
465
|
+
// Collapsed by default: CLIs needing install/auth stop crowding the
|
|
466
|
+
// working list, but stay one click away in the same order.
|
|
467
|
+
const undetectedExpanded = readCliStatusUndetectedExpanded();
|
|
468
|
+
html += `
|
|
469
|
+
<div class="cli-undetected-group">
|
|
470
|
+
<button type="button" id="cliUndetectedToggle" class="cli-status-header cli-undetected-toggle${undetectedExpanded ? ' expanded' : ''}"
|
|
471
|
+
aria-expanded="${undetectedExpanded}" aria-controls="cliUndetectedList">
|
|
472
|
+
${escapeHtml(t('cli.undetectedGroup', { count: undetectedCards.length }))}
|
|
473
|
+
</button>
|
|
474
|
+
<div id="cliUndetectedList"${undetectedExpanded ? '' : ' style="display:none"'}>${undetectedCards.join('')}</div>
|
|
475
|
+
</div>
|
|
476
|
+
`;
|
|
448
477
|
}
|
|
449
478
|
|
|
450
479
|
if (el) el.innerHTML = html;
|
|
451
480
|
|
|
452
|
-
const allEntries = Object.entries(cliStatus);
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
//
|
|
456
|
-
|
|
457
|
-
const state = describeCliProbe(info);
|
|
458
|
-
return state === 'probe-failing' || state === 'unknown';
|
|
459
|
-
});
|
|
460
|
-
const hasReadyCli = allEntries.some(([name, info]) => {
|
|
461
|
-
if (describeCliProbe(info) !== 'ready') return false;
|
|
462
|
-
const q = quota?.[name];
|
|
463
|
-
return !q || q.authenticated !== false;
|
|
464
|
-
});
|
|
465
|
-
if (!hasReadyCli && !anyProbeUnavailable && allEntries.length > 0 && el) {
|
|
481
|
+
const allEntries = Object.entries(cliStatus).filter(([name]) => !SIDEBAR_HIDDEN_CLIS.has(name));
|
|
482
|
+
// The alarm shares the partition's usable rule: a stale-but-preserved row
|
|
483
|
+
// or an unsettled probe is last-known-good, not evidence of absence —
|
|
484
|
+
// otherwise it false-alarms through every re-check window (#277).
|
|
485
|
+
if (allEntries.length > 0 && !hasUsableCliStatus(allEntries, quota) && el) {
|
|
466
486
|
el.insertAdjacentHTML('afterbegin',
|
|
467
487
|
`<div style="padding:8px 10px;margin-bottom:8px;background:var(--warning-dim);border:1px solid var(--warning);border-radius:6px;font-size:11px;color:var(--warning)">
|
|
468
488
|
${ICONS.warning} ${t('cli.noReadyCli')}
|
|
@@ -470,6 +490,18 @@ function renderCliStatus(data: { cliStatus: Record<string, CliStatusInfo> | null
|
|
|
470
490
|
);
|
|
471
491
|
}
|
|
472
492
|
|
|
493
|
+
const undetectedToggle = document.getElementById('cliUndetectedToggle');
|
|
494
|
+
if (undetectedToggle) {
|
|
495
|
+
undetectedToggle.addEventListener('click', () => {
|
|
496
|
+
const expanded = undetectedToggle.getAttribute('aria-expanded') !== 'true';
|
|
497
|
+
undetectedToggle.setAttribute('aria-expanded', String(expanded));
|
|
498
|
+
undetectedToggle.classList.toggle('expanded', expanded);
|
|
499
|
+
const list = document.getElementById('cliUndetectedList');
|
|
500
|
+
if (list) list.style.display = expanded ? 'block' : 'none';
|
|
501
|
+
saveCliStatusUndetectedExpanded(expanded);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
473
505
|
const kcBtn = document.getElementById('copilotKeychainBtn');
|
|
474
506
|
if (kcBtn) {
|
|
475
507
|
kcBtn.addEventListener('click', async () => {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { RuntimeTransport } from '../../../src/shared/runtime-contract.js';
|
|
4
4
|
import type { PresentationMode } from '../../../src/shared/presentation.js';
|
|
5
5
|
|
|
6
|
-
export interface PerCliConfig { provider?: string; model?: string; effort?: string; transport?: RuntimeTransport; fastMode?: boolean;
|
|
6
|
+
export interface PerCliConfig { provider?: string; model?: string; effort?: string; transport?: RuntimeTransport; fastMode?: boolean; }
|
|
7
7
|
export interface TelegramConfig { enabled?: boolean; token?: string; allowedChatIds?: number[]; forwardAll?: boolean; mentionOnly?: boolean; }
|
|
8
8
|
export interface DiscordConfig { enabled?: boolean; token?: string; guildId?: string; channelIds?: string[]; forwardAll?: boolean; allowBots?: boolean; mentionOnly?: boolean; }
|
|
9
9
|
// Slack needs TWO distinctly-scoped tokens: the bot token drives the Web API,
|
|
@@ -93,6 +93,44 @@ export function describeCliProbe(info: CliStatusInfo): 'checking' | 'unknown' |
|
|
|
93
93
|
return info.available === true && info.capabilityReady !== false ? 'ready' : 'unavailable';
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Sidebar partition: a card stays in the top group only while current or
|
|
98
|
+
* preserved evidence says the CLI is usable — installed, capability-ready and
|
|
99
|
+
* authenticated. `unavailable`/`capability-failed` rows demote, and a `ready`
|
|
100
|
+
* or `stale` row demotes only on concrete evidence (missing binary/capability
|
|
101
|
+
* or `authenticated === false` from either the status probe or the quota
|
|
102
|
+
* reader). Indeterminate probe states (checking/unknown, or failing with no
|
|
103
|
+
* prior snapshot) are not proof of absence, and a stale row is last-known-good — a previously working
|
|
104
|
+
* CLI keeps its place until a fresh probe says otherwise.
|
|
105
|
+
*/
|
|
106
|
+
export function isCliStatusUsable(info: CliStatusInfo, quota: QuotaEntry | null | undefined): boolean {
|
|
107
|
+
const probe = describeCliProbe(info);
|
|
108
|
+
if (probe === 'checking' || probe === 'unknown') return true;
|
|
109
|
+
if (probe === 'probe-failing') {
|
|
110
|
+
// A failing probe overlays the last successful snapshot; its concrete
|
|
111
|
+
// install/auth facts still hold, only a cold (null) row is unknown.
|
|
112
|
+
if (info.available == null) return true;
|
|
113
|
+
return info.available === true && info.capabilityReady !== false && info.authenticated !== false;
|
|
114
|
+
}
|
|
115
|
+
if (probe === 'unavailable' || probe === 'capability-failed') return false;
|
|
116
|
+
if (info.available !== true || info.capabilityReady === false) return false;
|
|
117
|
+
return info.authenticated !== false && quota?.authenticated !== false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* "Any usable CLI" rule the sidebar banner shares with the partition: true
|
|
122
|
+
* while at least one row reads usable under {@link isCliStatusUsable}. An
|
|
123
|
+
* alarm gated on this cannot false-fire through the stale re-check window or
|
|
124
|
+
* the initial probe pass — those rows count as usable, not as evidence of
|
|
125
|
+
* absence (#277).
|
|
126
|
+
*/
|
|
127
|
+
export function hasUsableCliStatus(entries: Iterable<readonly [string, CliStatusInfo]>, quota: Record<string, QuotaEntry> | null | undefined): boolean {
|
|
128
|
+
for (const [name, info] of entries) {
|
|
129
|
+
if (isCliStatusUsable(info, quota?.[name])) return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
96
134
|
export type CliProbeAvailabilityPresentation = {
|
|
97
135
|
kind: 'checking' | 'unknown' | 'failing' | 'none';
|
|
98
136
|
message: string | null;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// ── Inline `$skill` mentions ──
|
|
2
|
+
// Typing `$` anywhere in the composer lists the active skills; picking one
|
|
3
|
+
// inserts `$<id> ` at the caret. The server expands the mention when the
|
|
4
|
+
// message runs (src/core/skill-mentions.ts); the chat keeps the typed text.
|
|
5
|
+
import { state } from '../state.js';
|
|
6
|
+
import { fetchWithLocale } from './i18n.js';
|
|
7
|
+
import { escapeHtml } from '../render.js';
|
|
8
|
+
import { isDropdownOpen as isSlashDropdownOpen } from './slash-commands.js';
|
|
9
|
+
import { findSkillMentionToken, rankSkillMentions, skillMentionKey, type SkillMentionToken } from '../../../src/shared/skill-mention.js';
|
|
10
|
+
|
|
11
|
+
interface ApiSkill { id: string; mention?: string | undefined; description?: string | undefined; enabled?: boolean | undefined }
|
|
12
|
+
/** `name` carries the server-computed mention word so skillMentionKey() returns it. */
|
|
13
|
+
interface MentionSkill { id: string; name: string; description?: string | undefined }
|
|
14
|
+
|
|
15
|
+
let filtered: MentionSkill[] = [];
|
|
16
|
+
let selectedIdx = 0;
|
|
17
|
+
let isOpen = false;
|
|
18
|
+
let token: SkillMentionToken | null = null;
|
|
19
|
+
let loading: Promise<void> | null = null;
|
|
20
|
+
/** Bumped by every update() and close(): an older async update never renders. */
|
|
21
|
+
let updateSeq = 0;
|
|
22
|
+
|
|
23
|
+
const dropdown = (): HTMLElement | null => document.getElementById('skillMentionDropdown');
|
|
24
|
+
const input = (): HTMLTextAreaElement | null => document.getElementById('chatInput') as HTMLTextAreaElement | null;
|
|
25
|
+
|
|
26
|
+
function activeSkills(): MentionSkill[] {
|
|
27
|
+
return (state.allSkills as ApiSkill[])
|
|
28
|
+
.filter(skill => skill && skill.enabled && typeof skill.id === 'string')
|
|
29
|
+
.map(skill => ({ id: skill.id, name: skill.mention || skill.id, description: skill.description }));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The skills panel owns `state.allSkills`; load it once if that panel never opened. */
|
|
33
|
+
function ensureSkillsLoaded(): Promise<void> {
|
|
34
|
+
if ((state.allSkills as unknown[]).length) return Promise.resolve();
|
|
35
|
+
loading ??= fetchWithLocale('/api/skills')
|
|
36
|
+
.then(res => res.json())
|
|
37
|
+
.then((list: unknown) => {
|
|
38
|
+
if (Array.isArray(list) && !(state.allSkills as unknown[]).length) state.allSkills = list;
|
|
39
|
+
})
|
|
40
|
+
.catch(() => { /* popup stays closed; sending does not depend on it */ })
|
|
41
|
+
.finally(() => { loading = null; });
|
|
42
|
+
return loading;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The `/` list fades for 150ms after it closes; clear it so the two never stack. */
|
|
46
|
+
function hideFadingSlashList(): void {
|
|
47
|
+
const slash = document.getElementById('cmdDropdown');
|
|
48
|
+
if (!slash) return;
|
|
49
|
+
slash.classList.remove('visible');
|
|
50
|
+
slash.style.display = 'none';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function render(): void {
|
|
54
|
+
const el = dropdown();
|
|
55
|
+
const inp = input();
|
|
56
|
+
if (!el || !inp) return;
|
|
57
|
+
hideFadingSlashList();
|
|
58
|
+
el.innerHTML = filtered.map((skill, i) => `<div class="cmd-item${i === selectedIdx ? ' selected' : ''}"
|
|
59
|
+
role="option" id="skill-mention-item-${i}" aria-selected="${i === selectedIdx}" data-index="${i}">
|
|
60
|
+
<span class="cmd-name">$${escapeHtml(skillMentionKey(skill))}</span>
|
|
61
|
+
<span class="cmd-desc">${escapeHtml(skill.description || '')}</span>
|
|
62
|
+
</div>`).join('');
|
|
63
|
+
el.style.display = 'block';
|
|
64
|
+
requestAnimationFrame(() => el.classList.add('visible'));
|
|
65
|
+
isOpen = true;
|
|
66
|
+
inp.setAttribute('aria-controls', 'skillMentionDropdown');
|
|
67
|
+
inp.setAttribute('aria-expanded', 'true');
|
|
68
|
+
inp.setAttribute('aria-activedescendant', `skill-mention-item-${selectedIdx}`);
|
|
69
|
+
el.querySelector('.selected')?.scrollIntoView({ block: 'nearest' });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function close(): void {
|
|
73
|
+
updateSeq++; // also cancels an update() still awaiting the skill list
|
|
74
|
+
if (!isOpen) return;
|
|
75
|
+
isOpen = false; filtered = []; selectedIdx = 0; token = null;
|
|
76
|
+
const el = dropdown();
|
|
77
|
+
if (el) { el.classList.remove('visible'); el.style.display = 'none'; el.innerHTML = ''; }
|
|
78
|
+
const inp = input();
|
|
79
|
+
if (!inp) return;
|
|
80
|
+
// The textarea points back at the `/` list either way; if that list just
|
|
81
|
+
// opened on the same frame it owns expanded/activedescendant, so leave those.
|
|
82
|
+
inp.setAttribute('aria-controls', 'cmdDropdown');
|
|
83
|
+
if (!isSlashDropdownOpen()) {
|
|
84
|
+
inp.setAttribute('aria-expanded', 'false');
|
|
85
|
+
inp.setAttribute('aria-activedescendant', '');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function update(inp: HTMLTextAreaElement | null): Promise<void> {
|
|
90
|
+
const seq = ++updateSeq;
|
|
91
|
+
if (!inp || isSlashDropdownOpen() || inp.selectionStart !== inp.selectionEnd) { close(); return; }
|
|
92
|
+
if (!findSkillMentionToken(inp.value, inp.selectionStart ?? inp.value.length)) { close(); return; }
|
|
93
|
+
await ensureSkillsLoaded();
|
|
94
|
+
if (seq !== updateSeq) return; // a newer keystroke, a close() or a send owns the popup now
|
|
95
|
+
// Re-read after the await: the text or caret may have moved while the list loaded.
|
|
96
|
+
const next = findSkillMentionToken(inp.value, inp.selectionStart ?? inp.value.length);
|
|
97
|
+
if (!next || isSlashDropdownOpen()) { close(); return; }
|
|
98
|
+
const ranked = rankSkillMentions(activeSkills(), next.query);
|
|
99
|
+
if (!ranked.length) { close(); return; }
|
|
100
|
+
token = next; filtered = ranked; selectedIdx = 0;
|
|
101
|
+
render();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function applySelection(): void {
|
|
105
|
+
const skill = filtered[selectedIdx];
|
|
106
|
+
const inp = input();
|
|
107
|
+
const at = token;
|
|
108
|
+
close();
|
|
109
|
+
if (!skill || !inp || !at) return;
|
|
110
|
+
const caret = inp.selectionStart ?? inp.value.length;
|
|
111
|
+
const inserted = `$${skillMentionKey(skill)} `;
|
|
112
|
+
inp.value = inp.value.slice(0, at.start) + inserted + inp.value.slice(caret);
|
|
113
|
+
const pos = at.start + inserted.length;
|
|
114
|
+
inp.focus();
|
|
115
|
+
inp.selectionStart = inp.selectionEnd = pos;
|
|
116
|
+
inp.dispatchEvent(new Event('input', { bubbles: true })); // auto-resize + draft listeners
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function handleKeydown(e: KeyboardEvent): boolean {
|
|
120
|
+
if (!isOpen) return false;
|
|
121
|
+
if (e.isComposing) return false;
|
|
122
|
+
if (e.key === 'ArrowDown') { e.preventDefault(); selectedIdx = Math.min(filtered.length - 1, selectedIdx + 1); render(); return true; }
|
|
123
|
+
if (e.key === 'ArrowUp') { e.preventDefault(); selectedIdx = Math.max(0, selectedIdx - 1); render(); return true; }
|
|
124
|
+
if (e.key === 'Tab' || (e.key === 'Enter' && !e.shiftKey)) { e.preventDefault(); applySelection(); return true; }
|
|
125
|
+
if (e.key === 'Escape') { e.preventDefault(); close(); return true; }
|
|
126
|
+
return false; // caret keys fall through; the keyup listener re-evaluates the token
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function handleClick(e: Event): void {
|
|
130
|
+
const item = (e.target as HTMLElement).closest('.cmd-item') as HTMLElement | null;
|
|
131
|
+
if (!item) return;
|
|
132
|
+
const idx = Number.parseInt(item.dataset['index'] || '-1', 10);
|
|
133
|
+
if (Number.isNaN(idx) || idx < 0 || idx >= filtered.length) return;
|
|
134
|
+
selectedIdx = idx;
|
|
135
|
+
applySelection();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function handleOutsideClick(e: Event): void {
|
|
139
|
+
if (!isOpen) return;
|
|
140
|
+
const el = dropdown();
|
|
141
|
+
// Same test as the `/` list: clicks in the textarea are caret moves, re-evaluated by update().
|
|
142
|
+
if (el && !el.contains(e.target as Node) && e.target !== input()) close();
|
|
143
|
+
}
|
package/public/js/main.ts
CHANGED
|
@@ -45,6 +45,10 @@ import {
|
|
|
45
45
|
loadCommands, update as updateSlashDropdown, handleKeydown as handleSlashKeydown,
|
|
46
46
|
handleClick as handleSlashClick, handleOutsideClick as handleSlashOutsideClick,
|
|
47
47
|
} from './features/slash-commands.js';
|
|
48
|
+
import {
|
|
49
|
+
update as updateSkillMentions, handleKeydown as handleSkillMentionKeydown,
|
|
50
|
+
handleClick as handleSkillMentionClick, handleOutsideClick as handleSkillMentionOutsideClick,
|
|
51
|
+
} from './features/skill-mentions.js';
|
|
48
52
|
import { toggleSkill, filterSkills, searchSkills } from './features/skills.js';
|
|
49
53
|
import {
|
|
50
54
|
loadSettings, setPerm, onCliChange,
|
|
@@ -77,6 +81,7 @@ import { loadCliRegistry } from './constants.js';
|
|
|
77
81
|
import { initAppName } from './features/appname.js';
|
|
78
82
|
import { initAvatar } from './features/avatar.js';
|
|
79
83
|
import { initSidebar, toggleLeft, toggleRight } from './features/sidebar.js';
|
|
84
|
+
import { initResizeGuard } from './features/resize-guard.js';
|
|
80
85
|
import { initTheme } from './features/theme.js';
|
|
81
86
|
import { initPreviewShortcutBridge } from './features/preview-shortcut-bridge.js';
|
|
82
87
|
import { ensurePreviewCapabilityListener, ensurePreviewInsertTextListener } from './preview-parent-origin.js';
|
|
@@ -117,6 +122,7 @@ function isVoiceRecordingShortcut(e: KeyboardEvent): boolean {
|
|
|
117
122
|
document.getElementById('btnSend')?.addEventListener('click', () => { void sendMessage('button'); });
|
|
118
123
|
const chatInput = document.getElementById('chatInput') as HTMLTextAreaElement | null;
|
|
119
124
|
chatInput?.addEventListener('keydown', (e) => {
|
|
125
|
+
if (handleSkillMentionKeydown(e as KeyboardEvent)) return;
|
|
120
126
|
if (handleSlashKeydown(e as KeyboardEvent)) return;
|
|
121
127
|
handleKey(e as KeyboardEvent);
|
|
122
128
|
});
|
|
@@ -126,6 +132,7 @@ chatInput?.addEventListener('input', (e) => {
|
|
|
126
132
|
if (slashInputRaf) cancelAnimationFrame(slashInputRaf);
|
|
127
133
|
slashInputRaf = requestAnimationFrame(() => {
|
|
128
134
|
updateSlashDropdown((e.target as HTMLTextAreaElement)?.value || '');
|
|
135
|
+
void updateSkillMentions(e.target as HTMLTextAreaElement | null);
|
|
129
136
|
slashInputRaf = 0;
|
|
130
137
|
});
|
|
131
138
|
});
|
|
@@ -134,6 +141,13 @@ chatInput?.addEventListener('cmd-execute', () => {
|
|
|
134
141
|
});
|
|
135
142
|
document.getElementById('cmdDropdown')?.addEventListener('click', handleSlashClick);
|
|
136
143
|
document.addEventListener('click', handleSlashOutsideClick);
|
|
144
|
+
document.getElementById('skillMentionDropdown')?.addEventListener('click', handleSkillMentionClick);
|
|
145
|
+
document.addEventListener('click', handleSkillMentionOutsideClick);
|
|
146
|
+
// Caret moves without typing (click, arrows, Home/End) re-evaluate the `$` token under the caret.
|
|
147
|
+
chatInput?.addEventListener('click', () => { void updateSkillMentions(chatInput); });
|
|
148
|
+
chatInput?.addEventListener('keyup', (e) => {
|
|
149
|
+
if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes((e as KeyboardEvent).key)) void updateSkillMentions(chatInput);
|
|
150
|
+
});
|
|
137
151
|
initPendingQueue();
|
|
138
152
|
initBgtaskBadge();
|
|
139
153
|
document.getElementById('filePreviewClear')?.addEventListener('click', clearAttachedFiles);
|
|
@@ -462,6 +476,7 @@ async function bootstrap(): Promise<void> {
|
|
|
462
476
|
if (isCliStatusExpanded()) loadCliStatus();
|
|
463
477
|
// loadMessages() is handled by ws.js onopen (clear + reload)
|
|
464
478
|
initSidebar();
|
|
479
|
+
initResizeGuard();
|
|
465
480
|
loadEmployees();
|
|
466
481
|
initHeartbeatBadge();
|
|
467
482
|
void refreshMemorySidebar();
|
|
@@ -14,6 +14,8 @@ const THRESHOLD = 1;
|
|
|
14
14
|
const EST_HEIGHT = 80;
|
|
15
15
|
const OVERSCAN = 5;
|
|
16
16
|
const BOTTOM_THRESHOLD = 80;
|
|
17
|
+
/** Quiet period after the last width change before a resize drag counts as done. */
|
|
18
|
+
const RESIZE_SETTLE_MS = 160;
|
|
17
19
|
|
|
18
20
|
export type RestoreReason =
|
|
19
21
|
| 'pageshow'
|
|
@@ -45,6 +47,13 @@ interface ScrollAnchor {
|
|
|
45
47
|
el: HTMLElement;
|
|
46
48
|
top: number;
|
|
47
49
|
}
|
|
50
|
+
/** Where the reader was when a resize drag began. Captured once per drag so
|
|
51
|
+
* per-frame corrections never inherit the previous frame's drift. */
|
|
52
|
+
interface ResizePin {
|
|
53
|
+
follow: boolean;
|
|
54
|
+
anchor: { index: number; top: number } | null;
|
|
55
|
+
settleTimer: number;
|
|
56
|
+
}
|
|
48
57
|
|
|
49
58
|
function readMeasuredHeight(el: MeasurableVirtualElement): number {
|
|
50
59
|
const height = Math.ceil(el.getBoundingClientRect().height);
|
|
@@ -84,6 +93,7 @@ export class VirtualScroll {
|
|
|
84
93
|
private itemGap = 0;
|
|
85
94
|
private restorePassTimers = new Set<number>();
|
|
86
95
|
private shouldFollowAfterRestore: RestoreFollowPredicate = () => true;
|
|
96
|
+
private resizePin: ResizePin | null = null;
|
|
87
97
|
|
|
88
98
|
onLazyRender: LazyRenderCallback | null = null;
|
|
89
99
|
onPostRender: ((viewport: HTMLElement) => void) | null = null;
|
|
@@ -182,6 +192,56 @@ export class VirtualScroll {
|
|
|
182
192
|
this.renderItems();
|
|
183
193
|
}
|
|
184
194
|
|
|
195
|
+
/** Width-driven re-measure that keeps the reader's place for a whole drag.
|
|
196
|
+
* Follow intent and the anchor are read once, at the first frame: later
|
|
197
|
+
* frames see scroll events caused by the reflow itself, and TanStack's own
|
|
198
|
+
* item measurements land a frame after ours, so re-reading either would
|
|
199
|
+
* compound the drift this exists to remove. */
|
|
200
|
+
private invalidateLayoutPinned(): void {
|
|
201
|
+
if (!this.virtualizer) return;
|
|
202
|
+
let pin = this.resizePin;
|
|
203
|
+
if (!pin) {
|
|
204
|
+
const follow = this.shouldFollowAfterRestore();
|
|
205
|
+
pin = { follow, anchor: follow ? null : this.captureIndexAnchor(), settleTimer: 0 };
|
|
206
|
+
this.resizePin = pin;
|
|
207
|
+
}
|
|
208
|
+
window.clearTimeout(pin.settleTimer);
|
|
209
|
+
this.invalidateLayout();
|
|
210
|
+
this.applyResizePin(pin);
|
|
211
|
+
const settling = pin;
|
|
212
|
+
settling.settleTimer = window.setTimeout(() => {
|
|
213
|
+
requestAnimationFrame(() => {
|
|
214
|
+
if (this.resizePin !== settling) return;
|
|
215
|
+
this.resizePin = null;
|
|
216
|
+
if (this.virtualizer) this.applyResizePin(settling);
|
|
217
|
+
});
|
|
218
|
+
}, RESIZE_SETTLE_MS);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private applyResizePin(pin: ResizePin): void {
|
|
222
|
+
if (pin.follow) {
|
|
223
|
+
this.scrollToBottom();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (!pin.anchor) return;
|
|
227
|
+
const el = this.mounted.get(pin.anchor.index);
|
|
228
|
+
if (!el?.isConnected) return;
|
|
229
|
+
const delta = el.getBoundingClientRect().top - pin.anchor.top;
|
|
230
|
+
if (Number.isFinite(delta) && delta !== 0) this.container.scrollTop += delta;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private captureIndexAnchor(): ResizePin['anchor'] {
|
|
234
|
+
const el = this.firstVisibleMountedItem();
|
|
235
|
+
if (!el) return null;
|
|
236
|
+
const index = Number(el.dataset['vsIdx'] ?? '-1');
|
|
237
|
+
return index >= 0 ? { index, top: el.getBoundingClientRect().top } : null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private clearResizePin(): void {
|
|
241
|
+
if (this.resizePin) window.clearTimeout(this.resizePin.settleTimer);
|
|
242
|
+
this.resizePin = null;
|
|
243
|
+
}
|
|
244
|
+
|
|
185
245
|
/** Bulk-load items. Call AFTER registering onLazyRender/onPostRender. */
|
|
186
246
|
setItems(
|
|
187
247
|
items: VirtualItem[],
|
|
@@ -483,11 +543,16 @@ export class VirtualScroll {
|
|
|
483
543
|
// TanStack's ResizeObserver watches individual items, but not the
|
|
484
544
|
// container width — a window resize doesn't trigger item RO callbacks
|
|
485
545
|
// because items are position:absolute (width from left:0 + right:0).
|
|
546
|
+
// Re-measuring alone leaves scrollTop wherever the height deltas put it,
|
|
547
|
+
// so a drag (Manager sidebar, window edge) drifts a few px per frame
|
|
548
|
+
// until chat-scroll reads the gap as "user scrolled up" and stops
|
|
549
|
+
// following. Re-pin every pass: bottom while following, else the first
|
|
550
|
+
// visible row's on-screen top.
|
|
486
551
|
let resizeRaf = 0;
|
|
487
552
|
const scheduleInvalidateLayout = () => {
|
|
488
553
|
cancelAnimationFrame(resizeRaf);
|
|
489
554
|
resizeRaf = requestAnimationFrame(() => {
|
|
490
|
-
this.
|
|
555
|
+
this.invalidateLayoutPinned();
|
|
491
556
|
});
|
|
492
557
|
};
|
|
493
558
|
const onResize = () => scheduleInvalidateLayout();
|
|
@@ -495,6 +560,7 @@ export class VirtualScroll {
|
|
|
495
560
|
cleanupFns.push(() => {
|
|
496
561
|
window.removeEventListener('resize', onResize);
|
|
497
562
|
cancelAnimationFrame(resizeRaf);
|
|
563
|
+
this.clearResizePin();
|
|
498
564
|
});
|
|
499
565
|
|
|
500
566
|
if (typeof ResizeObserver !== 'undefined') {
|
package/public/locales/en.json
CHANGED
|
@@ -317,9 +317,11 @@
|
|
|
317
317
|
"cli.showMore": "More ▸",
|
|
318
318
|
"cli.authRequired": "Install / auth required",
|
|
319
319
|
"cli.notAuthenticated": "Auth required — run command below",
|
|
320
|
+
"cli.undetectedGroup": "Needs install or sign-in ({count})",
|
|
320
321
|
"drag.drop": "Drop files here",
|
|
321
322
|
"status.responding": "Responding",
|
|
322
323
|
"aria.cmdList": "Command list",
|
|
324
|
+
"aria.skillMentionList": "Skill list",
|
|
323
325
|
"btn.attach": "Attach file",
|
|
324
326
|
"input.placeholder": "Type a message...",
|
|
325
327
|
"queue.pendingTitle": "Pending",
|
package/public/locales/ja.json
CHANGED
|
@@ -306,9 +306,11 @@
|
|
|
306
306
|
"cli.showMore": "もっと見る ▸",
|
|
307
307
|
"cli.authRequired": "インストール/認証が必要です",
|
|
308
308
|
"cli.notAuthenticated": "認証が必要です — 下のコマンドを実行してください",
|
|
309
|
+
"cli.undetectedGroup": "インストール・ログインが必要 ({count})",
|
|
309
310
|
"drag.drop": "ここにファイルをドロップ",
|
|
310
311
|
"status.responding": "応答中",
|
|
311
312
|
"aria.cmdList": "コマンド一覧",
|
|
313
|
+
"aria.skillMentionList": "スキル一覧",
|
|
312
314
|
"btn.attach": "ファイルを添付",
|
|
313
315
|
"input.placeholder": "メッセージを入力...",
|
|
314
316
|
"queue.pendingTitle": "待機中",
|
package/public/locales/ko.json
CHANGED
|
@@ -317,9 +317,11 @@
|
|
|
317
317
|
"cli.showMore": "더 보기 ▸",
|
|
318
318
|
"cli.authRequired": "설치 / 인증 필요",
|
|
319
319
|
"cli.notAuthenticated": "인증 필요 — 아래 명령어 실행",
|
|
320
|
+
"cli.undetectedGroup": "설치·로그인 필요 ({count})",
|
|
320
321
|
"drag.drop": "파일을 여기에 드랍하세요",
|
|
321
322
|
"status.responding": "응답 중",
|
|
322
323
|
"aria.cmdList": "커맨드 목록",
|
|
324
|
+
"aria.skillMentionList": "스킬 목록",
|
|
323
325
|
"btn.attach": "파일 첨부",
|
|
324
326
|
"input.placeholder": "메시지 입력...",
|
|
325
327
|
"queue.pendingTitle": "대기 중",
|
package/public/locales/zh.json
CHANGED
|
@@ -306,9 +306,11 @@
|
|
|
306
306
|
"cli.showMore": "更多 ▸",
|
|
307
307
|
"cli.authRequired": "需要安装/认证",
|
|
308
308
|
"cli.notAuthenticated": "需要认证 — 请运行下面的命令",
|
|
309
|
+
"cli.undetectedGroup": "需要安装或登录 ({count})",
|
|
309
310
|
"drag.drop": "将文件拖到这里",
|
|
310
311
|
"status.responding": "正在回复",
|
|
311
312
|
"aria.cmdList": "命令列表",
|
|
313
|
+
"aria.skillMentionList": "技能列表",
|
|
312
314
|
"btn.attach": "附加文件",
|
|
313
315
|
"input.placeholder": "输入消息...",
|
|
314
316
|
"queue.pendingTitle": "待处理",
|
|
@@ -22,6 +22,7 @@ import { useWindowFullscreen } from './hooks/useWindowFullscreen';
|
|
|
22
22
|
import type { useDashboardView } from './hooks/useDashboardView';
|
|
23
23
|
import type { DashboardDetailTab, DashboardInstance, DashboardNotesAuthoringMode, DashboardNotesGraphSettings, DashboardNotesViewMode, DashboardScanResult, DashboardShortcutAction, ManagerEvent, NoteMetadata } from './types';
|
|
24
24
|
import { createManagerCaptureKeydownHandler } from './manager-shortcut-runner';
|
|
25
|
+
import { normalizeManagerShortcutKeymap } from './manager-shortcuts';
|
|
25
26
|
|
|
26
27
|
type AppChromeProps = {
|
|
27
28
|
view: ReturnType<typeof useDashboardView>;
|
|
@@ -158,6 +159,7 @@ export function AppChrome(props: AppChromeProps) {
|
|
|
158
159
|
dashboardSettingsUi={props.dashboardSettingsUi} titleSupport={props.titleSupport} onDashboardSettingsPatch={props.handleDashboardSettingsPatch}
|
|
159
160
|
viewMode={props.view.viewMode} onViewModeChange={props.view.setViewMode}
|
|
160
161
|
port={Number(window.location.port) || 3457} workingDir={props.view.rightFolderRootPath || ''}
|
|
162
|
+
codeNewSessionShortcut={normalizeManagerShortcutKeymap(props.view.dashboardShortcutKeymap).newCodeSession}
|
|
161
163
|
query={props.query} onQueryChange={props.setQuery} onSelectInstance={props.handleSelectInstance} />
|
|
162
164
|
)}
|
|
163
165
|
activityHeight={props.view.activityDockCollapsed ? 48 : props.view.activityDockHeight}
|
|
@@ -281,7 +281,7 @@ export function InstancePreview(props: InstancePreviewProps) {
|
|
|
281
281
|
// Keep the dedicated preview origin as the default. The legacy `/i`
|
|
282
282
|
// path is only a fallback because root-relative `/api` and `/ws`
|
|
283
283
|
// requests must keep targeting the managed instance.
|
|
284
|
-
{ theme: props.theme },
|
|
284
|
+
{ theme: props.theme, desktop: isElectron() },
|
|
285
285
|
);
|
|
286
286
|
const disabledReason = props.instance?.ok
|
|
287
287
|
? 'Preview is off. Turn it on from the header to mount the iframe.'
|
|
@@ -176,6 +176,8 @@ type Props = {
|
|
|
176
176
|
onViewModeChange: (mode: DashboardViewMode) => void;
|
|
177
177
|
port: number;
|
|
178
178
|
workingDir: string;
|
|
179
|
+
/** Resolved `newCodeSession` chord, shown as the sidebar button's hint. */
|
|
180
|
+
codeNewSessionShortcut?: string;
|
|
179
181
|
query: string;
|
|
180
182
|
onQueryChange: (value: string) => void;
|
|
181
183
|
onSelectInstance: (instance: DashboardInstance) => void;
|
|
@@ -607,7 +609,7 @@ export function SidebarRailRouter(props: Props) {
|
|
|
607
609
|
{props.viewMode === 'code' && props.sidebarMode === 'instances' ? (
|
|
608
610
|
<WorkspaceSurface active>
|
|
609
611
|
<Suspense fallback={<div style={{ padding: '24px', color: 'var(--text-dim)', fontSize: '13px' }}>Loading Code workspace...</div>}>
|
|
610
|
-
<CodeCanvas port={props.port} workingDir={codeWorkingDir} onWorkingDirChange={handleCodeWorkingDirChange} onOpenLocalFile={handleRightPreviewFile} />
|
|
612
|
+
<CodeCanvas port={props.port} workingDir={codeWorkingDir} onWorkingDirChange={handleCodeWorkingDirChange} onOpenLocalFile={handleRightPreviewFile} newSessionShortcut={props.codeNewSessionShortcut} />
|
|
611
613
|
</Suspense>
|
|
612
614
|
</WorkspaceSurface>
|
|
613
615
|
) : null}
|