@deepseek-harness-tui/dsh-tui 0.15.0 → 0.16.0
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.md +92 -5
- package/guide/dsh-tui-guide/codex-backend.en.md +13 -3
- package/guide/dsh-tui-guide/codex-backend.md +9 -2
- package/guide/dsh-tui-guide/configuration.en.md +39 -2
- package/guide/dsh-tui-guide/configuration.md +34 -2
- package/guide/dsh-tui-guide/getting-started.en.md +15 -0
- package/guide/dsh-tui-guide/getting-started.md +15 -0
- package/guide/dsh-tui-guide/interaction.en.md +8 -1
- package/guide/dsh-tui-guide/interaction.md +8 -1
- package/guide/dsh-tui-guide/plugins.en.md +66 -5
- package/guide/dsh-tui-guide/plugins.md +37 -3
- package/guide/dsh-tui-guide/user-guide.en.md +3 -2
- package/guide/dsh-tui-guide/user-guide.md +3 -2
- package/lib/settings.json +1 -1
- package/lib/types/adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/adapter/channel/projection.js +1 -0
- package/lib/types/adapter/channel/ui-policy.d.ts +1 -1
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +1 -0
- package/lib/types/adapter/ports/channel-catalog.d.ts +5 -1
- package/lib/types/adapter/ports/channel-catalog.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-settings.d.ts +5 -3
- package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-ui.d.ts +7 -1
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +47 -0
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel.d.ts +2 -0
- package/lib/types/adapter/ports/channel.d.ts.map +1 -1
- package/lib/types/agent/events.d.ts +3 -2
- package/lib/types/agent/events.d.ts.map +1 -1
- package/lib/types/backends/codex/contract.d.ts +3 -0
- package/lib/types/backends/codex/contract.d.ts.map +1 -1
- package/lib/types/backends/codex/contract.js +3 -0
- package/lib/types/backends/codex/session/async-questions.d.ts +17 -0
- package/lib/types/backends/codex/session/async-questions.d.ts.map +1 -0
- package/lib/types/backends/codex/session/async-questions.js +89 -0
- package/lib/types/backends/codex/session/session.d.ts.map +1 -1
- package/lib/types/backends/codex/session/session.js +35 -9
- package/lib/types/backends/codex/text.d.ts +3 -3
- package/lib/types/backends/codex/text.js +1 -1
- package/lib/types/backends/codex/translate/items.d.ts.map +1 -1
- package/lib/types/backends/codex/translate/items.js +3 -1
- package/lib/types/backends/codex/translate/question-reply.d.ts +11 -0
- package/lib/types/backends/codex/translate/question-reply.d.ts.map +1 -0
- package/lib/types/backends/codex/translate/question-reply.js +53 -0
- package/lib/types/channel/projection.d.ts.map +1 -1
- package/lib/types/channel/projection.js +10 -0
- package/lib/types/commands.d.ts +10 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +11 -2
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
- package/lib/types/components/CommandSuggestions.js +4 -1
- package/lib/types/components/LogoV2.d.ts +4 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +5 -3
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +95 -14
- package/lib/types/components/PermissionsPicker.d.ts +10 -1
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -1
- package/lib/types/components/PermissionsPicker.js +24 -16
- package/lib/types/components/PluginInstallPane.d.ts +21 -0
- package/lib/types/components/PluginInstallPane.d.ts.map +1 -0
- package/lib/types/components/PluginInstallPane.js +84 -0
- package/lib/types/components/PluginManagePane.d.ts +21 -0
- package/lib/types/components/PluginManagePane.d.ts.map +1 -0
- package/lib/types/components/PluginManagePane.js +69 -0
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +7 -3
- package/lib/types/components/SubagentCard.d.ts +13 -1
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +27 -12
- package/lib/types/components/SubagentDashboard.d.ts +8 -1
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +58 -16
- package/lib/types/components/launchpadLayout.d.ts +14 -1
- package/lib/types/components/launchpadLayout.d.ts.map +1 -1
- package/lib/types/components/launchpadLayout.js +36 -17
- package/lib/types/components/messages/TeamMessageBubble.d.ts +20 -0
- package/lib/types/components/messages/TeamMessageBubble.d.ts.map +1 -0
- package/lib/types/components/messages/TeamMessageBubble.js +15 -0
- package/lib/types/components/sidePanel/PanelHost.d.ts.map +1 -1
- package/lib/types/components/sidePanel/PanelHost.js +5 -1
- package/lib/types/components/sidePanel/PanelStore.d.ts +8 -0
- package/lib/types/components/sidePanel/PanelStore.d.ts.map +1 -1
- package/lib/types/components/sidePanel/PanelStore.js +11 -0
- package/lib/types/components/sidePanel/SidePanelColumn.d.ts.map +1 -1
- package/lib/types/components/sidePanel/SidePanelColumn.js +13 -1
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts +7 -0
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts.map +1 -1
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.js +4 -0
- package/lib/types/components/sidePanel/btw/BtwComposer.d.ts +3 -0
- package/lib/types/components/sidePanel/btw/BtwComposer.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwComposer.js +20 -8
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.d.ts +2 -1
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.js +104 -13
- package/lib/types/components/sidePanel/btw/BtwThreadScene.d.ts +6 -2
- package/lib/types/components/sidePanel/btw/BtwThreadScene.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwThreadScene.js +61 -15
- package/lib/types/components/sidePanel/btw/BtwThreadView.d.ts +14 -4
- package/lib/types/components/sidePanel/btw/BtwThreadView.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwThreadView.js +90 -5
- package/lib/types/components/sidePanel/btw/threads.d.ts +18 -0
- package/lib/types/components/sidePanel/btw/threads.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/threads.js +51 -0
- package/lib/types/components/sidePanel/builtinPanels.d.ts.map +1 -1
- package/lib/types/components/sidePanel/builtinPanels.js +5 -1
- package/lib/types/components/sidePanel/companion/SplashMascot.d.ts +5 -1
- package/lib/types/components/sidePanel/companion/SplashMascot.d.ts.map +1 -1
- package/lib/types/components/sidePanel/companion/SplashMascot.js +19 -4
- package/lib/types/components/sidePanel/companion/skins.d.ts +8 -0
- package/lib/types/components/sidePanel/companion/skins.d.ts.map +1 -1
- package/lib/types/components/sidePanel/companion/skins.js +57 -12
- package/lib/types/components/sidePanel/types.d.ts +6 -0
- package/lib/types/components/sidePanel/types.d.ts.map +1 -1
- package/lib/types/components/splashArtRows.d.ts +29 -0
- package/lib/types/components/splashArtRows.d.ts.map +1 -0
- package/lib/types/components/splashArtRows.js +31 -0
- package/lib/types/components/teamRoster.d.ts +70 -0
- package/lib/types/components/teamRoster.d.ts.map +1 -0
- package/lib/types/components/teamRoster.js +78 -0
- package/lib/types/dsh-adapter/admission-loader.d.ts +82 -0
- package/lib/types/dsh-adapter/admission-loader.d.ts.map +1 -1
- package/lib/types/dsh-adapter/admission-loader.js +73 -28
- package/lib/types/dsh-adapter/agent-view.d.ts.map +1 -1
- package/lib/types/dsh-adapter/agent-view.js +3 -1
- package/lib/types/dsh-adapter/approvals.d.ts +7 -0
- package/lib/types/dsh-adapter/approvals.d.ts.map +1 -1
- package/lib/types/dsh-adapter/approvals.js +45 -1
- package/lib/types/dsh-adapter/backend/translate.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backend/translate.js +68 -8
- package/lib/types/dsh-adapter/backend-assembly.d.ts +31 -0
- package/lib/types/dsh-adapter/backend-assembly.d.ts.map +1 -0
- package/lib/types/dsh-adapter/backend-assembly.js +225 -0
- package/lib/types/dsh-adapter/backend-contribution.d.ts +36 -0
- package/lib/types/dsh-adapter/backend-contribution.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backend-contribution.js +41 -0
- package/lib/types/dsh-adapter/backend-registry.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backend-registry.js +22 -3
- package/lib/types/dsh-adapter/backends.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backends.js +4 -5
- package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/bundled-presets.js +60 -3
- package/lib/types/dsh-adapter/channel/binding-events.d.ts +1 -0
- package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding-events.js +1 -0
- package/lib/types/dsh-adapter/channel/command-completions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/command-completions.js +64 -7
- package/lib/types/dsh-adapter/channel/compaction.d.ts +7 -2
- package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/compaction.js +100 -26
- package/lib/types/dsh-adapter/channel/core/binding-feed.d.ts +3 -0
- package/lib/types/dsh-adapter/channel/core/binding-feed.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/binding-feed.js +2 -1
- package/lib/types/dsh-adapter/channel/core/compose.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/compose.js +12 -0
- package/lib/types/dsh-adapter/channel/extensions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/extensions.js +68 -8
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts +6 -0
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/mode-permission-actions.js +104 -12
- package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-actions.js +8 -1
- package/lib/types/dsh-adapter/channel/permissions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/permissions.js +4 -1
- package/lib/types/dsh-adapter/channel/projection-helpers.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection-helpers.js +2 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-metadata.js +4 -3
- package/lib/types/dsh-adapter/channel/session-reset.js +2 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +7 -0
- package/lib/types/dsh-adapter/channel/settings-host.d.ts +3 -0
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/settings-host.js +135 -5
- package/lib/types/dsh-adapter/channel/state.d.ts +6 -0
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.js +18 -6
- package/lib/types/dsh-adapter/channel/transcript.d.ts +7 -0
- package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/transcript.js +19 -2
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.d.ts +55 -0
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.js +96 -0
- package/lib/types/dsh-adapter/contract.d.ts +4 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +5 -5
- package/lib/types/dsh-adapter/effect-ledger.d.ts +7 -0
- package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
- package/lib/types/dsh-adapter/effect-ledger.js +8 -1
- package/lib/types/dsh-adapter/entry-slot.d.ts +3 -1
- package/lib/types/dsh-adapter/entry-slot.d.ts.map +1 -1
- package/lib/types/dsh-adapter/entry-slot.js +2 -2
- package/lib/types/dsh-adapter/fresh-agent.d.ts.map +1 -1
- package/lib/types/dsh-adapter/fresh-agent.js +6 -1
- package/lib/types/dsh-adapter/host-access.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-access.js +95 -0
- package/lib/types/dsh-adapter/host-contract.d.ts +1 -1
- package/lib/types/dsh-adapter/host-contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-contract.js +3 -2
- package/lib/types/dsh-adapter/host-dsh.d.ts +8 -2
- package/lib/types/dsh-adapter/host-dsh.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-dsh.js +45 -30
- package/lib/types/dsh-adapter/host-entry.js +35 -10
- package/lib/types/dsh-adapter/index.d.ts +9 -10
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +8 -6
- package/lib/types/dsh-adapter/invariant.d.ts +7 -2
- package/lib/types/dsh-adapter/invariant.d.ts.map +1 -1
- package/lib/types/dsh-adapter/invariant.js +6 -3
- package/lib/types/dsh-adapter/migrate/index.js +2 -2
- package/lib/types/dsh-adapter/panels.d.ts.map +1 -1
- package/lib/types/dsh-adapter/panels.js +6 -0
- package/lib/types/dsh-adapter/plugin-declaration.d.ts +99 -1
- package/lib/types/dsh-adapter/plugin-declaration.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-declaration.js +132 -2
- package/lib/types/dsh-adapter/plugin-feed.d.ts +94 -0
- package/lib/types/dsh-adapter/plugin-feed.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-feed.js +135 -0
- package/lib/types/dsh-adapter/plugin-guard.d.ts +115 -0
- package/lib/types/dsh-adapter/plugin-guard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-guard.js +108 -0
- package/lib/types/dsh-adapter/plugin-install.d.ts +160 -0
- package/lib/types/dsh-adapter/plugin-install.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-install.js +545 -0
- package/lib/types/dsh-adapter/plugin-manage.d.ts +178 -0
- package/lib/types/dsh-adapter/plugin-manage.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-manage.js +468 -0
- package/lib/types/dsh-adapter/plugin-manager.d.ts +78 -2
- package/lib/types/dsh-adapter/plugin-manager.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-manager.js +32 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +49 -2
- package/lib/types/dsh-adapter/plugins-info.d.ts +87 -1
- package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugins-info.js +131 -26
- package/lib/types/dsh-adapter/process-exit.d.ts +1 -1
- package/lib/types/dsh-adapter/process-exit.d.ts.map +1 -1
- package/lib/types/dsh-adapter/process-exit.js +1 -1
- package/lib/types/dsh-adapter/session-projection-store.d.ts +66 -0
- package/lib/types/dsh-adapter/session-projection-store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-projection-store.js +149 -0
- package/lib/types/dsh-adapter/settingsEditor.js +2 -2
- package/lib/types/dsh-adapter/sideQuestion.d.ts +21 -0
- package/lib/types/dsh-adapter/sideQuestion.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sideQuestion.js +53 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +2 -1
- package/lib/types/dsh-adapter/team-mode.d.ts +74 -0
- package/lib/types/dsh-adapter/team-mode.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-mode.js +188 -0
- package/lib/types/dsh-adapter/team-roster.d.ts +67 -0
- package/lib/types/dsh-adapter/team-roster.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-roster.js +136 -0
- package/lib/types/dsh-adapter/team-tools.d.ts +58 -0
- package/lib/types/dsh-adapter/team-tools.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-tools.js +208 -0
- package/lib/types/dsh-adapter/types.d.ts +1 -1
- package/lib/types/dsh-adapter/types.d.ts.map +1 -1
- package/lib/types/i18n.d.ts +607 -16
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +295 -9
- package/lib/types/ink/components/ScrollBox.d.ts +9 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +23 -0
- package/lib/types/ink/dom.d.ts.map +1 -1
- package/lib/types/kernelPrefs.d.ts +1 -1
- package/lib/types/kernelPrefs.d.ts.map +1 -1
- package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -1
- package/lib/types/native-ts/yoga-layout/index.js +8 -1
- package/lib/types/permissionPresetLabels.d.ts +25 -0
- package/lib/types/permissionPresetLabels.d.ts.map +1 -0
- package/lib/types/permissionPresetLabels.js +33 -0
- package/lib/types/preset-patch.d.ts +22 -0
- package/lib/types/preset-patch.d.ts.map +1 -0
- package/lib/types/preset-patch.js +14 -0
- package/lib/types/screens/Chat.d.ts +33 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +301 -9
- package/lib/types/screens/Launchpad.d.ts +7 -1
- package/lib/types/screens/Launchpad.d.ts.map +1 -1
- package/lib/types/screens/Launchpad.js +19 -4
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +1 -0
- package/lib/types/screens/chat/usePluginWriteFlow.d.ts +68 -0
- package/lib/types/screens/chat/usePluginWriteFlow.d.ts.map +1 -0
- package/lib/types/screens/chat/usePluginWriteFlow.js +92 -0
- package/lib/types/screens/chatOverlay.d.ts +19 -1
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/sessionModes.d.ts +5 -3
- package/lib/types/sessionModes.d.ts.map +1 -1
- package/lib/types/sessionModes.js +9 -3
- package/lib/types/tips.js +2 -2
- package/lib/types/utils/paths.d.ts +11 -0
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +13 -0
- package/package.json +126 -120
- package/tui-profile/feed/README.md +63 -0
- package/tui-profile/feed/first-party.json +10 -0
|
@@ -4,7 +4,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
* 滚动、composer 追问。Esc 返回侧栏,面板状态原样(mountPolicy=enabled
|
|
5
5
|
* 不卸载,thread 与 draft 都在线程 store)。
|
|
6
6
|
*
|
|
7
|
-
* 键层级:composer 编辑(Esc
|
|
7
|
+
* 键层级:composer 编辑(Esc 收起草稿、↑ 上到标题)> 场景动作(↑/↓ 选轮、
|
|
8
|
+
* s 送聊天/撤回、c 复制)> Esc 退出场景。选中态与侧栏面板同源(线程 store),
|
|
9
|
+
* 两个形态的键位与落点逐条对齐。
|
|
8
10
|
*/
|
|
9
11
|
import React from 'react';
|
|
10
12
|
import { Box, Text, useInput, useTerminalSize } from '../../../ui.js';
|
|
@@ -14,7 +16,7 @@ import { btwThreads } from './threads.js';
|
|
|
14
16
|
import { getBtwContextBudget, getBtwContextTurns } from '../../../tuiDisplayPrefs.js';
|
|
15
17
|
import { BtwComposer, btwComposerKey } from './BtwComposer.js';
|
|
16
18
|
import { BtwThreadView } from './BtwThreadView.js';
|
|
17
|
-
export function BtwThreadScene({ channel, onClose, }) {
|
|
19
|
+
export function BtwThreadScene({ channel, onClose, onFocusChat, }) {
|
|
18
20
|
const { columns, rows } = useTerminalSize();
|
|
19
21
|
const sessionId = String(channel.agentId);
|
|
20
22
|
const thread = React.useSyncExternalStore(btwThreads.subscribe, () => btwThreads.get(sessionId));
|
|
@@ -29,11 +31,15 @@ export function BtwThreadScene({ channel, onClose, }) {
|
|
|
29
31
|
const [notice, setNotice] = React.useState(null);
|
|
30
32
|
const scrollRef = React.useRef(null);
|
|
31
33
|
const busy = thread !== undefined && thread.activeTurnId !== null;
|
|
34
|
+
// 选中轮与侧栏面板同源(线程 store):↑/↓ 移动、s 送聊天/撤回、c 复制。
|
|
35
|
+
const turns = thread?.turns ?? [];
|
|
36
|
+
const selectedId = thread?.selectedTurnId ?? null;
|
|
37
|
+
const selectedTurn = turns.find(turn => turn.turnId === selectedId);
|
|
32
38
|
const attachTurn = React.useCallback((turn) => {
|
|
33
39
|
if (turn.phase !== 'completed')
|
|
34
|
-
return;
|
|
40
|
+
return false;
|
|
35
41
|
if (typeof channel.attachContext !== 'function')
|
|
36
|
-
return;
|
|
42
|
+
return false;
|
|
37
43
|
channel.attachContext({
|
|
38
44
|
source: 'panel',
|
|
39
45
|
sourceId: turn.turnId,
|
|
@@ -42,7 +48,42 @@ export function BtwThreadScene({ channel, onClose, }) {
|
|
|
42
48
|
});
|
|
43
49
|
const staged = channel.attachedContexts.find(entry => entry.sourceId === turn.turnId);
|
|
44
50
|
channel.notify(staged?.truncated === true ? t('btw-thread-answer-truncated') : t('btw-thread-answer-attached'), { color: 'success' });
|
|
51
|
+
return true;
|
|
45
52
|
}, [channel]);
|
|
53
|
+
/**
|
|
54
|
+
* `s` = 送聊天,再按一次 = 撤回(与侧栏面板、jobs/companion 同一契约)。
|
|
55
|
+
* 送出去之后这个整屏场景就让位:关掉场景 + 焦点交回主输入框——等价于侧栏
|
|
56
|
+
* 形态里「面板留着、焦点回到输入框」,两种形态的落点一致。
|
|
57
|
+
*/
|
|
58
|
+
const sendToChat = React.useCallback((turn) => {
|
|
59
|
+
// oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime guard: partial fixture channels
|
|
60
|
+
const staged = (channel.attachedContexts ?? []).find(entry => entry.sourceId === turn.turnId);
|
|
61
|
+
if (staged !== undefined) {
|
|
62
|
+
if (typeof channel.detachContext === 'function')
|
|
63
|
+
channel.detachContext(staged.id);
|
|
64
|
+
channel.notify(t('panel-context-detached'), { color: 'success' });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (!attachTurn(turn))
|
|
68
|
+
return;
|
|
69
|
+
onFocusChat?.();
|
|
70
|
+
onClose();
|
|
71
|
+
}, [attachTurn, channel, onFocusChat, onClose]);
|
|
72
|
+
/** 点标题行:选中那一轮并离开编辑层(高亮只在列表态显示)。 */
|
|
73
|
+
const selectTurn = React.useCallback((turnId) => {
|
|
74
|
+
btwThreads.select(sessionId, turnId);
|
|
75
|
+
setComposerFocus(false);
|
|
76
|
+
}, [sessionId]);
|
|
77
|
+
const copySelected = React.useCallback(() => {
|
|
78
|
+
const turn = btwThreads.selectedTurn(sessionId);
|
|
79
|
+
const answer = turn !== undefined && turn.answer !== ''
|
|
80
|
+
? turn.answer
|
|
81
|
+
: [...(btwThreads.get(sessionId)?.turns ?? [])].reverse().find(candidate => candidate.phase === 'completed')?.answer;
|
|
82
|
+
if (answer === undefined)
|
|
83
|
+
return;
|
|
84
|
+
void setClipboard(answer);
|
|
85
|
+
channel.notify(t('copied-chars', { n: answer.length }), { timeoutMs: 1500 });
|
|
86
|
+
}, [sessionId, channel]);
|
|
46
87
|
useInput((input, key, event) => {
|
|
47
88
|
if (composerFocus) {
|
|
48
89
|
const text = btwThreads.get(sessionId)?.draft ?? '';
|
|
@@ -80,14 +121,23 @@ export function BtwThreadScene({ channel, onClose, }) {
|
|
|
80
121
|
onClose();
|
|
81
122
|
return;
|
|
82
123
|
}
|
|
124
|
+
// ↑/↓ 只挪选中,滚动交给 BtwThreadView 的「最小揭示」(出了视口才滚)。
|
|
83
125
|
if (key.upArrow) {
|
|
84
|
-
scrollRef.current?.scrollBy(-3);
|
|
85
126
|
event.stopImmediatePropagation();
|
|
127
|
+
btwThreads.moveSelection(sessionId, -1);
|
|
86
128
|
return;
|
|
87
129
|
}
|
|
88
130
|
if (key.downArrow) {
|
|
89
|
-
scrollRef.current?.scrollBy(3);
|
|
90
131
|
event.stopImmediatePropagation();
|
|
132
|
+
const list = btwThreads.get(sessionId)?.turns ?? [];
|
|
133
|
+
const current = btwThreads.selectedTurn(sessionId);
|
|
134
|
+
const at = current === undefined ? list.length - 1 : list.findIndex(turn => turn.turnId === current.turnId);
|
|
135
|
+
// 已在最后一条:↓ 回输入框(与输入框里按 ↑ 上到标题互为往返)。
|
|
136
|
+
if (list.length === 0 || at === list.length - 1) {
|
|
137
|
+
setComposerFocus(true);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
btwThreads.moveSelection(sessionId, 1);
|
|
91
141
|
return;
|
|
92
142
|
}
|
|
93
143
|
if (key.pageUp) {
|
|
@@ -114,20 +164,16 @@ export function BtwThreadScene({ channel, onClose, }) {
|
|
|
114
164
|
return;
|
|
115
165
|
}
|
|
116
166
|
if (input === 's' && !key.ctrl && !key.meta) {
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
119
|
-
|
|
167
|
+
const turn = btwThreads.selectedTurn(sessionId);
|
|
168
|
+
if (turn !== undefined)
|
|
169
|
+
sendToChat(turn);
|
|
120
170
|
event.stopImmediatePropagation();
|
|
121
171
|
return;
|
|
122
172
|
}
|
|
123
173
|
if (input === 'c' && !key.ctrl && !key.meta) {
|
|
124
|
-
|
|
125
|
-
if (latest !== undefined) {
|
|
126
|
-
void setClipboard(latest.answer);
|
|
127
|
-
channel.notify(t('copied-chars', { n: latest.answer.length }), { timeoutMs: 1500 });
|
|
128
|
-
}
|
|
174
|
+
copySelected();
|
|
129
175
|
event.stopImmediatePropagation();
|
|
130
176
|
}
|
|
131
177
|
});
|
|
132
|
-
return (_jsxs(Box, { flexDirection: "column", width: columns, height: rows, paddingX: 1, children: [_jsxs(Box, { flexDirection: "row", width: "100%", height: 1, flexShrink: 0, children: [_jsx(Text, { color: "warning", bold: true, children: t('btw-fullscreen-title') }), _jsx(Text, { dimColor: true, wrap: "truncate", children: ' ' + t('btw-scene-hints') })] }), _jsx(BtwThreadView, { thread: thread, width: columns - 2, height: Math.max(3, rows - 4), alive: true,
|
|
178
|
+
return (_jsxs(Box, { flexDirection: "column", width: columns, height: rows, paddingX: 1, children: [_jsxs(Box, { flexDirection: "row", width: "100%", height: 1, flexShrink: 0, children: [_jsx(Text, { color: "warning", bold: true, children: t('btw-fullscreen-title') }), _jsx(Text, { dimColor: true, wrap: "truncate", children: ' ' + t('btw-scene-hints') })] }), _jsx(BtwThreadView, { thread: thread, width: columns - 2, height: Math.max(3, rows - 4), alive: true, selectedTurnId: composerFocus ? undefined : selectedId ?? undefined, onSelectTurn: selectTurn, scrollHandleRef: scrollRef }), _jsx(BtwComposer, { state: { text: thread?.draft ?? '', caret }, focused: composerFocus, busy: busy, notice: notice === null ? undefined : notice, onActivate: activateComposer })] }));
|
|
133
179
|
}
|
|
@@ -7,13 +7,23 @@
|
|
|
7
7
|
* 跟尾:默认跟随尾部;用户上滚后暂停 follow,
|
|
8
8
|
* 出现「有新回答」跳尾提示(点击回底)。ScrollBox 的 sticky 位就是
|
|
9
9
|
* follow 信号——上滚打破 sticky,scrollToBottom/贴底恢复。
|
|
10
|
+
*
|
|
11
|
+
* 消息区不是控件:点它跟点面板其它地方一样,只由宿主把列焦点交给面板
|
|
12
|
+
* (见 BtwComposer 的点击注释),注入/复制的入口是选中轮 + `s`/`c`。
|
|
13
|
+
* 选中只看标题那一行:反色只覆盖标题本身的字,终端光标也声明在那儿。
|
|
14
|
+
* 选中轮滚出视口时只滚到「恰好露出这一行」:往上走贴视口顶、往下走贴视口底;
|
|
15
|
+
* 已经在视口里就只挪高亮,不动滚动位置。
|
|
10
16
|
*/
|
|
11
17
|
import React from 'react';
|
|
12
18
|
import { type ScrollBoxHandle } from '../../../ui.js';
|
|
13
|
-
import type { BtwThreadSnapshot
|
|
19
|
+
import type { BtwThreadSnapshot } from './threads.js';
|
|
14
20
|
export declare function BtwThreadView({ thread, width, height,
|
|
15
21
|
/** 渲时钟的开关(面板不可见/场景失焦时停表)。 */
|
|
16
|
-
alive,
|
|
22
|
+
alive,
|
|
23
|
+
/** 当前选中的轮(标题反色 + 光标声明);undefined = 列表没有选中态。 */
|
|
24
|
+
selectedTurnId,
|
|
25
|
+
/** 点标题行把选中挪到那一轮(继续冒泡,宿主照旧拿列焦点)。 */
|
|
26
|
+
onSelectTurn,
|
|
17
27
|
/** 宿主(全屏场景/浮层)接管键盘滚动用的句柄透传。 */
|
|
18
28
|
scrollHandleRef, }: {
|
|
19
29
|
readonly thread: BtwThreadSnapshot | undefined;
|
|
@@ -21,8 +31,8 @@ scrollHandleRef, }: {
|
|
|
21
31
|
/** 滚动区行预算(含滚动区自身;跳尾提示一行另计)。 */
|
|
22
32
|
readonly height: number;
|
|
23
33
|
readonly alive: boolean;
|
|
24
|
-
|
|
25
|
-
readonly
|
|
34
|
+
readonly selectedTurnId?: string | undefined;
|
|
35
|
+
readonly onSelectTurn?: ((turnId: string) => void) | undefined;
|
|
26
36
|
readonly scrollHandleRef?: {
|
|
27
37
|
current: ScrollBoxHandle | null;
|
|
28
38
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BtwThreadView.d.ts","sourceRoot":"","sources":["../../../../../src/components/sidePanel/btw/BtwThreadView.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"BtwThreadView.d.ts","sourceRoot":"","sources":["../../../../../src/components/sidePanel/btw/BtwThreadView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAO3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AA2DrD,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,KAAK,EACL,MAAM;AACN,6BAA6B;AAC7B,KAAK;AACL,+CAA+C;AAC/C,cAAc;AACd,mCAAmC;AACnC,YAAY;AACZ,+BAA+B;AAC/B,eAAe,GAChB,EAAE;IACD,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAA;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC9D,QAAQ,CAAC,eAAe,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAAA;KAAE,CAAA;CAC/D,GAAG,KAAK,CAAC,SAAS,CA6JlB"}
|
|
@@ -8,12 +8,19 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
8
8
|
* 跟尾:默认跟随尾部;用户上滚后暂停 follow,
|
|
9
9
|
* 出现「有新回答」跳尾提示(点击回底)。ScrollBox 的 sticky 位就是
|
|
10
10
|
* follow 信号——上滚打破 sticky,scrollToBottom/贴底恢复。
|
|
11
|
+
*
|
|
12
|
+
* 消息区不是控件:点它跟点面板其它地方一样,只由宿主把列焦点交给面板
|
|
13
|
+
* (见 BtwComposer 的点击注释),注入/复制的入口是选中轮 + `s`/`c`。
|
|
14
|
+
* 选中只看标题那一行:反色只覆盖标题本身的字,终端光标也声明在那儿。
|
|
15
|
+
* 选中轮滚出视口时只滚到「恰好露出这一行」:往上走贴视口顶、往下走贴视口底;
|
|
16
|
+
* 已经在视口里就只挪高亮,不动滚动位置。
|
|
11
17
|
*/
|
|
12
18
|
import React from 'react';
|
|
13
19
|
import { Box, Text, ScrollBox } from '../../../ui.js';
|
|
14
20
|
import { Markdown } from '../../Markdown.js';
|
|
15
21
|
import { SpinnerGlyph } from '../../Spinner/SpinnerGlyph.js';
|
|
16
22
|
import { t } from '../../../i18n.js';
|
|
23
|
+
import { useDeclaredCursor, useNativeCursor } from '../../../ink/hooks/use-declared-cursor.js';
|
|
17
24
|
import { getBtwContextTurns, subscribeBtwContextTurns } from '../../../tuiDisplayPrefs.js';
|
|
18
25
|
/** 80ms spinner 帧(只在有 running 轮且其还没收到首段文本时走时钟)。 */
|
|
19
26
|
const SPINNER_MS = 80;
|
|
@@ -21,9 +28,36 @@ const SPINNER_MS = 80;
|
|
|
21
28
|
function BtwEmptyState({ contextTurns }) {
|
|
22
29
|
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "subtle", wrap: "truncate", children: t('btw-thread-empty') }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "subtle", wrap: "wrap", children: t('btw-empty-lines') }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, italic: true, wrap: "wrap", children: t('btw-thread-context-recent', { n: contextTurns }) }) })] }));
|
|
23
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* 一轮的标题行(问题)。选中 = 只反色标题本身的字(不铺满整行),并把终端
|
|
33
|
+
* 光标声明在这一行上:按 ↑/↓ 时原生光标的拖尾是「在标题之间上下走」,而不是
|
|
34
|
+
* 掉到屏幕左下角(没有声明时渲染器的兜底位置)。点这一行 = 把高亮挪过来
|
|
35
|
+
* (`onSelect`),点击继续冒泡,宿主照旧拿到列焦点。
|
|
36
|
+
*/
|
|
37
|
+
function QuestionRow({ turnId, text, selected, onSelect, onRegister }) {
|
|
38
|
+
const native = useNativeCursor();
|
|
39
|
+
// 与 ListItem 同款:移动期间可见、静止 500ms 后收起,形状/闪烁归终端。
|
|
40
|
+
const cursorRef = useDeclaredCursor({ line: 0, column: 0, active: selected, visible: native, hideOnIdle: native });
|
|
41
|
+
// 组装 ref 时必须把 cursorRef 的 cleanup 转交出去(React 19 用返回值当卸载
|
|
42
|
+
// 清理),否则这一行被删掉时光标声明会留在原地。
|
|
43
|
+
const setNode = React.useCallback((node) => {
|
|
44
|
+
const cleanup = cursorRef(node);
|
|
45
|
+
onRegister?.(turnId, node);
|
|
46
|
+
return () => {
|
|
47
|
+
if (typeof cleanup === 'function')
|
|
48
|
+
cleanup();
|
|
49
|
+
onRegister?.(turnId, null);
|
|
50
|
+
};
|
|
51
|
+
}, [cursorRef, onRegister, turnId]);
|
|
52
|
+
return (_jsx(Box, { ref: setNode, onClick: onSelect === undefined ? undefined : () => onSelect(turnId), children: _jsx(Text, { color: "accent", bold: true, inverse: selected, wrap: "wrap", children: text }) }));
|
|
53
|
+
}
|
|
24
54
|
export function BtwThreadView({ thread, width, height,
|
|
25
55
|
/** 渲时钟的开关(面板不可见/场景失焦时停表)。 */
|
|
26
|
-
alive,
|
|
56
|
+
alive,
|
|
57
|
+
/** 当前选中的轮(标题反色 + 光标声明);undefined = 列表没有选中态。 */
|
|
58
|
+
selectedTurnId,
|
|
59
|
+
/** 点标题行把选中挪到那一轮(继续冒泡,宿主照旧拿列焦点)。 */
|
|
60
|
+
onSelectTurn,
|
|
27
61
|
/** 宿主(全屏场景/浮层)接管键盘滚动用的句柄透传。 */
|
|
28
62
|
scrollHandleRef, }) {
|
|
29
63
|
const localScrollRef = React.useRef(null);
|
|
@@ -77,6 +111,60 @@ scrollHandleRef, }) {
|
|
|
77
111
|
setPendingJump(true);
|
|
78
112
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
79
113
|
}, [version, answerLengths, follow]);
|
|
114
|
+
// ── 选中轮的最小滚动揭示 ─────────────────────────────────────────────
|
|
115
|
+
// 行节点表:QuestionRow 挂载时交进来;滚动位置一律走 ScrollBox 的
|
|
116
|
+
// scrollToElement(位置在渲染期读元素 Yoga top,不会用到过期数字)。
|
|
117
|
+
const rowNodes = React.useRef(new Map());
|
|
118
|
+
const registerRow = React.useCallback((turnId, node) => {
|
|
119
|
+
if (node === null)
|
|
120
|
+
rowNodes.current.delete(turnId);
|
|
121
|
+
else
|
|
122
|
+
rowNodes.current.set(turnId, node);
|
|
123
|
+
}, []);
|
|
124
|
+
/**
|
|
125
|
+
* 节点在滚动内容里的行号(不含滚动位移):沿祖先累加 Yoga top,直到撞上滚动
|
|
126
|
+
* 容器(ScrollBox 的节点上才有 scrollTop)。没撞到(浮层回退等无滚动区形态)
|
|
127
|
+
* 返回 undefined——宁可不滚,也不要按错误的坐标系乱跳。
|
|
128
|
+
*/
|
|
129
|
+
const contentRowOf = (node) => {
|
|
130
|
+
let rows = 0;
|
|
131
|
+
let current = node;
|
|
132
|
+
while (current !== undefined && current.parentNode !== undefined) {
|
|
133
|
+
if (current.parentNode.scrollTop !== undefined)
|
|
134
|
+
return rows;
|
|
135
|
+
rows += current.yogaNode?.getComputedTop() ?? 0;
|
|
136
|
+
current = current.parentNode;
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
};
|
|
140
|
+
const previousSelectedRef = React.useRef(undefined);
|
|
141
|
+
React.useLayoutEffect(() => {
|
|
142
|
+
const previous = previousSelectedRef.current;
|
|
143
|
+
previousSelectedRef.current = selectedTurnId;
|
|
144
|
+
// 从编辑层或主输入框回列表时也要揭示:长答案跟尾后,标题可能在视口外。
|
|
145
|
+
if (selectedTurnId === undefined || previous === selectedTurnId)
|
|
146
|
+
return;
|
|
147
|
+
const handle = scrollRef.current;
|
|
148
|
+
const node = rowNodes.current.get(selectedTurnId);
|
|
149
|
+
if (handle === null || node === undefined)
|
|
150
|
+
return;
|
|
151
|
+
const viewport = handle.getViewportHeight();
|
|
152
|
+
if (viewport <= 0)
|
|
153
|
+
return;
|
|
154
|
+
const rows = Math.max(1, Math.ceil(node.yogaNode?.getComputedHeight() ?? 1));
|
|
155
|
+
const top = contentRowOf(node);
|
|
156
|
+
if (top === undefined)
|
|
157
|
+
return;
|
|
158
|
+
const scrollTop = handle.getScrollTop();
|
|
159
|
+
// 用算好的内容行号直接 scrollTo:scrollToElement 读的是元素相对**父节点**的
|
|
160
|
+
// top,标题盒子嵌在 turn 里,用它会把滚动甩到内容顶部。布局已稳定(只有选中
|
|
161
|
+
// 变了),这里的数字不会过期。
|
|
162
|
+
// 往上走出视口 → 贴视口顶;往下走出视口 → 整行贴视口底(恰好露出,不多滚)。
|
|
163
|
+
if (top < scrollTop)
|
|
164
|
+
handle.scrollTo(top);
|
|
165
|
+
else if (top + rows > scrollTop + viewport)
|
|
166
|
+
handle.scrollTo(top + rows - viewport);
|
|
167
|
+
}, [selectedTurnId]);
|
|
80
168
|
const jumpTail = React.useCallback(() => {
|
|
81
169
|
scrollRef.current?.scrollToBottom();
|
|
82
170
|
setFollow(true);
|
|
@@ -84,10 +172,7 @@ scrollHandleRef, }) {
|
|
|
84
172
|
}, []);
|
|
85
173
|
if (thread === undefined || thread.turns.length === 0)
|
|
86
174
|
return _jsx(BtwEmptyState, { contextTurns: contextTurns });
|
|
87
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", paddingX: 1, children: [_jsx(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, stickyScroll: true, children: thread.turns.map((turn, index) => (_jsxs(Box, { flexDirection: "column", children: [index > 0 && _jsx(Text, { dimColor: true, children: '┈'.repeat(Math.max(4, width - 2)) }), _jsx(
|
|
88
|
-
event.stopImmediatePropagation();
|
|
89
|
-
onAttachTurn(turn);
|
|
90
|
-
}, children: _jsx(Markdown, { cacheTokens: false, children: turn.answer }) })) : turn.phase === 'failed' || turn.phase === 'cancelled' ? (_jsxs(_Fragment, { children: [turn.answer !== '' && _jsx(Markdown, { cacheTokens: false, children: turn.answer }), turn.phase === 'failed'
|
|
175
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", paddingX: 1, children: [_jsx(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, stickyScroll: true, children: thread.turns.map((turn, index) => (_jsxs(Box, { flexDirection: "column", children: [index > 0 && _jsx(Text, { dimColor: true, children: '┈'.repeat(Math.max(4, width - 2)) }), _jsx(QuestionRow, { turnId: turn.turnId, text: turn.question, selected: turn.turnId === selectedTurnId, onSelect: onSelectTurn, onRegister: registerRow }), _jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [turn.phase === 'completed' ? (_jsx(Markdown, { cacheTokens: false, children: turn.answer })) : turn.phase === 'failed' || turn.phase === 'cancelled' ? (_jsxs(_Fragment, { children: [turn.answer !== '' && _jsx(Markdown, { cacheTokens: false, children: turn.answer }), turn.phase === 'failed'
|
|
91
176
|
? _jsxs(Text, { color: "error", wrap: "wrap", children: [t('btw-thread-error'), turn.error === undefined ? '' : ': ' + turn.error] })
|
|
92
177
|
: _jsx(Text, { dimColor: true, italic: true, children: t('btw-thread-cancelled') })] })) : turn.answer !== '' ? (_jsx(Markdown, { cacheTokens: false, children: turn.answer })) : (_jsxs(Box, { flexShrink: 0, children: [_jsx(SpinnerGlyph, { frame: frame, messageColor: "warning" }), _jsxs(Text, { color: "warning", children: [" ", t('btw-answering')] })] })), turn.omittedOlderCount > 0 && (_jsx(Text, { dimColor: true, italic: true, wrap: "truncate", children: t('btw-thread-context-omitted', { n: turn.omittedOlderCount }) }))] })] }, turn.turnId))) }), pendingJump && !follow && (_jsx(Box, { flexShrink: 0, onClick: event => { event.stopImmediatePropagation(); jumpTail(); }, children: _jsxs(Text, { color: "permission", children: ["\u2193 ", t('btw-thread-unread')] }) }))] }));
|
|
93
178
|
}
|
|
@@ -22,6 +22,9 @@ export interface BtwThreadSnapshot {
|
|
|
22
22
|
readonly turns: readonly BtwTurn[];
|
|
23
23
|
readonly activeTurnId: string | null;
|
|
24
24
|
readonly draft: string;
|
|
25
|
+
/** 列表选中的轮(`s` 送聊天 / `c` 复制的对象);null = 跟最后一条。
|
|
26
|
+
* 与 draft 同理放在 store 里:面板与全屏场景共用同一份选中态。 */
|
|
27
|
+
readonly selectedTurnId: string | null;
|
|
25
28
|
/** seen-through 标尺之后的完成/失败轮(cancelled 不计:用户自己中止,
|
|
26
29
|
* 不是没看到的新答案)。 */
|
|
27
30
|
readonly unread: {
|
|
@@ -55,6 +58,12 @@ export type BtwSubmitResult = {
|
|
|
55
58
|
readonly ok: false;
|
|
56
59
|
readonly reason: 'empty' | 'busy' | 'congested';
|
|
57
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* 有效选中轮:钉住的 id 还在就继续用它,否则跟最后一条(新轮到达自动跟尾、
|
|
63
|
+
* 轮被清掉/换话题自动落回)。面板与全屏场景共用这一条解析,保证两个形态
|
|
64
|
+
* 的选中语义逐字一致。
|
|
65
|
+
*/
|
|
66
|
+
export declare function effectiveSelectedTurnId(turns: readonly BtwTurn[], pinned: string | null): string | null;
|
|
58
67
|
/** 钳到 1–8;非有限值取默认 4。 */
|
|
59
68
|
export declare function normalizeRecentTurnsLimit(value: number | undefined): number;
|
|
60
69
|
export interface BtwContextSelection {
|
|
@@ -97,6 +106,15 @@ declare class BtwThreadStore {
|
|
|
97
106
|
*/
|
|
98
107
|
newTopic(sessionId: string): boolean;
|
|
99
108
|
setDraft(sessionId: string, text: string): void;
|
|
109
|
+
/** 指定选中某一轮(点标题行;null = 回到跟最后一条)。 */
|
|
110
|
+
select(sessionId: string, turnId: string | null): void;
|
|
111
|
+
/** 当前选中轮(实时解析,不依赖渲染期快照)。 */
|
|
112
|
+
selectedTurn(sessionId: string): BtwTurn | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* ↑/↓ 移动选中:从当前有效选中出发、夹在两端(delta = ±1)。落点即钉住,
|
|
115
|
+
* 所以移动之后不再跟尾;返回新选中轮(线程为空 = undefined)。
|
|
116
|
+
*/
|
|
117
|
+
moveSelection(sessionId: string, delta: number): BtwTurn | undefined;
|
|
100
118
|
/** 看到最新线程:把已落定轮全部记为已读(running 轮落定时再见分晓)。 */
|
|
101
119
|
markSeen(sessionId: string): void;
|
|
102
120
|
/** 测试/多夹具隔离用:清空全部线程状态。 */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../../../../src/components/sidePanel/btw/threads.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../../../../src/components/sidePanel/btw/threads.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEtF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAA;IAClD,gDAAgD;IAChD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;CACnC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAA;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;gDAC4C;IAC5C,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IACtC;qBACiB;IACjB,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IACpE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,4CAA4C;AAC5C,eAAO,MAAM,wBAAwB,IAAI,CAAA;AACzC,eAAO,MAAM,oBAAoB,IAAI,CAAA;AACrC,eAAO,MAAM,oBAAoB,IAAI,CAAA;AACrC,4BAA4B;AAC5B,eAAO,MAAM,uBAAuB,QAAS,CAAA;AAC7C,uBAAuB;AACvB,eAAO,MAAM,sBAAsB,OAAQ,CAAA;AAC3C,2BAA2B;AAC3B,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,0DAA0D;AAC1D,MAAM,MAAM,YAAY,GAAG,CACzB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,KACnF,OAAO,CAAC;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEzE,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,WAAW,CAAA;CAAE,CAAA;AAE3E;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,MAAM,GAAG,IAAI,CAGf;AAED,wBAAwB;AACxB,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAG3E;AAUD,MAAM,WAAW,mBAAmB;IAClC,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;IACrC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;CACnC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,KAAK,GAAE,MAAiC,EACxC,OAAO,GAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACrE,mBAAmB,CAoBrB;AA2CD,cAAM,cAAc;IAClB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,SAAS,CAAI;IAErB,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAG/C;IAED,6CAA6C;IAC7C,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIrD;;;;OAIG;IACH,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,YAAY,EACjB,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7E,eAAe;IAkFlB,4CAA4C;IAC5C,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAavC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAepC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAO/C,oCAAoC;IACpC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAOtD,4BAA4B;IAC5B,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAOpD;;;OAGG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAepE,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAYjC,0BAA0B;IAC1B,YAAY,IAAI,IAAI;IAMpB,OAAO,CAAC,QAAQ;IAgChB,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,IAAI;CAGb;AAED,oCAAoC;AACpC,eAAO,MAAM,UAAU,gBAAuB,CAAA"}
|
|
@@ -24,6 +24,16 @@ export const BTW_CONTEXT_CHAR_BUDGET = 24_000;
|
|
|
24
24
|
export const BTW_ANSWER_CHAR_BUDGET = 8_000;
|
|
25
25
|
/** 跨线程并发上限:超过显示忙,不隐式取消。 */
|
|
26
26
|
export const BTW_MAX_CONCURRENT_ASKS = 2;
|
|
27
|
+
/**
|
|
28
|
+
* 有效选中轮:钉住的 id 还在就继续用它,否则跟最后一条(新轮到达自动跟尾、
|
|
29
|
+
* 轮被清掉/换话题自动落回)。面板与全屏场景共用这一条解析,保证两个形态
|
|
30
|
+
* 的选中语义逐字一致。
|
|
31
|
+
*/
|
|
32
|
+
export function effectiveSelectedTurnId(turns, pinned) {
|
|
33
|
+
if (pinned !== null && turns.some(turn => turn.turnId === pinned))
|
|
34
|
+
return pinned;
|
|
35
|
+
return turns.length === 0 ? null : turns[turns.length - 1].turnId;
|
|
36
|
+
}
|
|
27
37
|
/** 钳到 1–8;非有限值取默认 4。 */
|
|
28
38
|
export function normalizeRecentTurnsLimit(value) {
|
|
29
39
|
if (value === undefined || !Number.isFinite(value))
|
|
@@ -92,6 +102,7 @@ function buildSnapshot(record) {
|
|
|
92
102
|
turns: Object.freeze([...record.turns]),
|
|
93
103
|
activeTurnId: active === null ? null : active.turnId,
|
|
94
104
|
draft: record.draft,
|
|
105
|
+
selectedTurnId: effectiveSelectedTurnId(record.turns, record.selectedTurnId),
|
|
95
106
|
unread: Object.freeze({ count: unreadCount, error: unreadError }),
|
|
96
107
|
version: record.version,
|
|
97
108
|
});
|
|
@@ -147,6 +158,8 @@ class BtwThreadStore {
|
|
|
147
158
|
omittedOlderCount: selection.omittedOlderCount,
|
|
148
159
|
};
|
|
149
160
|
record.turns.push(turn);
|
|
161
|
+
// 新轮到达回到跟尾:选中态跟的是「最后一条」,上一轮的钉位不再有意义。
|
|
162
|
+
record.selectedTurnId = null;
|
|
150
163
|
this.bump(record);
|
|
151
164
|
const controller = new AbortController();
|
|
152
165
|
this.controllers.set(turn.turnId, controller);
|
|
@@ -230,6 +243,7 @@ class BtwThreadStore {
|
|
|
230
243
|
record.generation += 1;
|
|
231
244
|
record.turns = [];
|
|
232
245
|
record.draft = '';
|
|
246
|
+
record.selectedTurnId = null;
|
|
233
247
|
record.seenThroughSeq = this.turnSeq;
|
|
234
248
|
this.bump(record);
|
|
235
249
|
return true;
|
|
@@ -241,6 +255,41 @@ class BtwThreadStore {
|
|
|
241
255
|
record.draft = text;
|
|
242
256
|
this.bump(record);
|
|
243
257
|
}
|
|
258
|
+
/** 指定选中某一轮(点标题行;null = 回到跟最后一条)。 */
|
|
259
|
+
select(sessionId, turnId) {
|
|
260
|
+
const record = this.recordOf(sessionId);
|
|
261
|
+
if (record.selectedTurnId === turnId)
|
|
262
|
+
return;
|
|
263
|
+
record.selectedTurnId = turnId;
|
|
264
|
+
this.bump(record);
|
|
265
|
+
}
|
|
266
|
+
/** 当前选中轮(实时解析,不依赖渲染期快照)。 */
|
|
267
|
+
selectedTurn(sessionId) {
|
|
268
|
+
const record = this.threads.get(sessionId);
|
|
269
|
+
if (record === undefined)
|
|
270
|
+
return undefined;
|
|
271
|
+
const id = effectiveSelectedTurnId(record.turns, record.selectedTurnId);
|
|
272
|
+
return id === null ? undefined : record.turns.find(turn => turn.turnId === id);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* ↑/↓ 移动选中:从当前有效选中出发、夹在两端(delta = ±1)。落点即钉住,
|
|
276
|
+
* 所以移动之后不再跟尾;返回新选中轮(线程为空 = undefined)。
|
|
277
|
+
*/
|
|
278
|
+
moveSelection(sessionId, delta) {
|
|
279
|
+
const record = this.recordOf(sessionId);
|
|
280
|
+
if (record.turns.length === 0)
|
|
281
|
+
return undefined;
|
|
282
|
+
const current = effectiveSelectedTurnId(record.turns, record.selectedTurnId);
|
|
283
|
+
const at = record.turns.findIndex(turn => turn.turnId === current);
|
|
284
|
+
const from = at === -1 ? record.turns.length - 1 : at;
|
|
285
|
+
const next = Math.min(record.turns.length - 1, Math.max(0, from + delta));
|
|
286
|
+
const turnId = record.turns[next].turnId;
|
|
287
|
+
if (record.selectedTurnId !== turnId) {
|
|
288
|
+
record.selectedTurnId = turnId;
|
|
289
|
+
this.bump(record);
|
|
290
|
+
}
|
|
291
|
+
return record.turns[next];
|
|
292
|
+
}
|
|
244
293
|
/** 看到最新线程:把已落定轮全部记为已读(running 轮落定时再见分晓)。 */
|
|
245
294
|
markSeen(sessionId) {
|
|
246
295
|
const record = this.threads.get(sessionId);
|
|
@@ -269,6 +318,7 @@ class BtwThreadStore {
|
|
|
269
318
|
generation: 0,
|
|
270
319
|
turns: [],
|
|
271
320
|
draft: '',
|
|
321
|
+
selectedTurnId: null,
|
|
272
322
|
seenThroughSeq: this.turnSeq,
|
|
273
323
|
version: 0,
|
|
274
324
|
snapshot: Object.freeze({
|
|
@@ -279,6 +329,7 @@ class BtwThreadStore {
|
|
|
279
329
|
turns: Object.freeze([]),
|
|
280
330
|
activeTurnId: null,
|
|
281
331
|
draft: '',
|
|
332
|
+
selectedTurnId: null,
|
|
282
333
|
unread: Object.freeze({ count: 0, error: false }),
|
|
283
334
|
version: 0,
|
|
284
335
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtinPanels.d.ts","sourceRoot":"","sources":["../../../../src/components/sidePanel/builtinPanels.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"builtinPanels.d.ts","sourceRoot":"","sources":["../../../../src/components/sidePanel/builtinPanels.tsx"],"names":[],"mappings":"AAiOA,2BAA2B;AAC3B,wBAAgB,qBAAqB,IAAI,IAAI,CAmG5C"}
|
|
@@ -141,6 +141,10 @@ function AgentsPanelAdapter({ focused, visible }) {
|
|
|
141
141
|
React.useEffect(() => {
|
|
142
142
|
const running = subagents.filter(s => s.status === 'running' || s.status === 'starting').length;
|
|
143
143
|
const failedIds = new Set(subagents.filter(s => s.status === 'failed').map(s => s.agentId));
|
|
144
|
+
// A failed team member is durable; it counts once even beside a failed run.
|
|
145
|
+
for (const member of channel.team?.members ?? [])
|
|
146
|
+
if (member.phase === 'failed')
|
|
147
|
+
failedIds.add(member.id);
|
|
144
148
|
if (visible) {
|
|
145
149
|
seenFailedRef.current = failedIds;
|
|
146
150
|
panelStore.setBadge('agents', running > 0 ? { level: 'info', unread: 0 } : null);
|
|
@@ -162,7 +166,7 @@ function AgentsPanelAdapter({ focused, visible }) {
|
|
|
162
166
|
if (typeof route === 'object' && detail !== undefined) {
|
|
163
167
|
return (_jsx(SubagentDetailScene, { subagent: detail, variant: "panel", focused: focused, visible: visible, onBack: () => setRoute('dashboard'), onInterrupt: (id) => channel.subagentControl?.interrupt(id), ...(channel.subagentControl?.history === undefined ? {} : { loadTranscript: channel.subagentControl.history }), onOpenView: () => agentViewStore.request(detail.agentId, 'agent-detail', true), messages: detailMessages, ...(detailCompose === undefined ? {} : { compose: detailCompose }) }));
|
|
164
168
|
}
|
|
165
|
-
return (_jsx(SubagentDashboard, { subagents: subagents, variant: "panel", focused: focused, visible: visible, onSelect: (id) => setRoute({ detail: id }), onOpenView: (id) => agentViewStore.request(id, 'agents-dashboard', true), messages: messageControl?.messages() }));
|
|
169
|
+
return (_jsx(SubagentDashboard, { subagents: subagents, team: channel.team, sessionId: channel.sessionId, variant: "panel", focused: focused, visible: visible, onSelect: (id) => setRoute({ detail: id }), onOpenView: (id) => agentViewStore.request(id, 'agents-dashboard', true), messages: messageControl?.messages() }));
|
|
166
170
|
}
|
|
167
171
|
let registered = false;
|
|
168
172
|
/** 幂等:重复调用(夹具多次挂载)不再注册。 */
|
|
@@ -19,13 +19,17 @@
|
|
|
19
19
|
* 渲染路径(LogoV2 底部块,welcomePad 缩进)。
|
|
20
20
|
*/
|
|
21
21
|
import React from 'react';
|
|
22
|
+
import { type SplashImageBox } from '../../splashArtRows.js';
|
|
22
23
|
export type SplashMascotSkin = 'deepy' | 'whaleGirl';
|
|
23
24
|
/** 读 companion.skin(LogoV2 的测试缝 prop 优先);非吉祥物皮肤返回
|
|
24
25
|
* undefined(LogoV2 走原鲸鱼/女仆娘路径,零改动)。 */
|
|
25
26
|
export declare function useSplashMascotSkin(pinned?: string): SplashMascotSkin | undefined;
|
|
27
|
+
/** 图片原生尺寸的紧凑槽;仅启动页使用,协议/像元/素材未就绪时保持原回退。 */
|
|
28
|
+
export declare function useSplashMascotBox(skin: SplashMascotSkin | undefined, compact: boolean): SplashImageBox | undefined;
|
|
26
29
|
/** 启动页吉祥物本体:按 skin 渲染动画,点击交互(poke/连点)在内层盒。 */
|
|
27
|
-
export declare function SplashMascot({ skin, active }: {
|
|
30
|
+
export declare function SplashMascot({ skin, active, imageBox }: {
|
|
28
31
|
readonly skin: SplashMascotSkin;
|
|
32
|
+
readonly imageBox?: SplashImageBox;
|
|
29
33
|
/** 动画许可:false(冻结/落地页 whaleIdle=false)时定格 idle 帧 0、零时钟。 */
|
|
30
34
|
readonly active: boolean;
|
|
31
35
|
}): React.ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SplashMascot.d.ts","sourceRoot":"","sources":["../../../../../src/components/sidePanel/companion/SplashMascot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"SplashMascot.d.ts","sourceRoot":"","sources":["../../../../../src/components/sidePanel/companion/SplashMascot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAiB/E,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,WAAW,CAAA;AAEpD;wCACwC;AACxC,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAIjF;AAGD,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAUnH;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;IACvD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;IAClC,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB,GAAG,KAAK,CAAC,SAAS,CAsElB"}
|
|
@@ -20,10 +20,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
20
20
|
* 渲染路径(LogoV2 底部块,welcomePad 缩进)。
|
|
21
21
|
*/
|
|
22
22
|
import React from 'react';
|
|
23
|
-
import { Box } from '../../../ui.js';
|
|
23
|
+
import { Box, useTerminalImages, useTerminalImageCellSize, useTerminalImageProtocol } from '../../../ui.js';
|
|
24
|
+
import { fitSplashImageBox } from '../../splashArtRows.js';
|
|
25
|
+
import { loadWhaleGirlKit } from './deepy.js';
|
|
24
26
|
import { getCompanionSkin, subscribeCompanionSkin } from '../../../tuiDisplayPrefs.js';
|
|
25
27
|
import { TICKLE_MIN_CLICKS, TICKLE_WINDOW_MS } from './mood.js';
|
|
26
|
-
import { DeepySkin, WhaleGirlSkin } from './skins.js';
|
|
28
|
+
import { DeepySkin, WhaleGirlSkin, loadWhaleGirlImageKit } from './skins.js';
|
|
27
29
|
import { initialCompanionPoseState, nextCompanionPoseStep } from './pose.js';
|
|
28
30
|
/** 点击反应档时长:poke 帧表一轮 2100ms,取头段(同 CompanionPanel 的口径)。 */
|
|
29
31
|
const SPLASH_POKE_MS = 1400;
|
|
@@ -40,8 +42,20 @@ export function useSplashMascotSkin(pinned) {
|
|
|
40
42
|
const id = pinned ?? setting;
|
|
41
43
|
return id === 'deepy' || id === 'whaleGirl' ? id : undefined;
|
|
42
44
|
}
|
|
45
|
+
/** 图片原生尺寸的紧凑槽;仅启动页使用,协议/像元/素材未就绪时保持原回退。 */
|
|
46
|
+
export function useSplashMascotBox(skin, compact) {
|
|
47
|
+
// 高度降级可能暂时撤掉吉祥物,因此能力探测不能只依赖吉祥物挂载。
|
|
48
|
+
const available = useTerminalImages(compact && skin === 'whaleGirl');
|
|
49
|
+
const protocol = useTerminalImageProtocol();
|
|
50
|
+
const cell = useTerminalImageCellSize();
|
|
51
|
+
const canvas = available && compact && skin === 'whaleGirl' && cell !== undefined
|
|
52
|
+
&& (protocol === 'kitty' || protocol === 'sixel') && loadWhaleGirlKit() !== undefined
|
|
53
|
+
? loadWhaleGirlImageKit()?.canvas : undefined;
|
|
54
|
+
return React.useMemo(() => canvas !== undefined && cell !== undefined
|
|
55
|
+
? fitSplashImageBox(canvas, cell, WhaleGirlSkin.cells) : undefined, [canvas, cell]);
|
|
56
|
+
}
|
|
43
57
|
/** 启动页吉祥物本体:按 skin 渲染动画,点击交互(poke/连点)在内层盒。 */
|
|
44
|
-
export function SplashMascot({ skin, active }) {
|
|
58
|
+
export function SplashMascot({ skin, active, imageBox }) {
|
|
45
59
|
const [, setBump] = React.useState(0);
|
|
46
60
|
// 稳态 idle 从挂载起循环;冻结时 now 钉在挂载值 → elapsed 恒 0 → 帧 0。
|
|
47
61
|
const mountedAtRef = React.useRef(Date.now());
|
|
@@ -78,6 +92,7 @@ export function SplashMascot({ skin, active }) {
|
|
|
78
92
|
now,
|
|
79
93
|
width: SPLASH_MASCOT_COLUMNS,
|
|
80
94
|
animationSemantic,
|
|
95
|
+
...(imageBox === undefined ? {} : { imageBox }),
|
|
81
96
|
};
|
|
82
97
|
return (_jsx(Box, { flexDirection: "column", alignItems: "center", flexShrink: 0, children: _jsx(Box, { onClick: (event) => {
|
|
83
98
|
event.stopImmediatePropagation();
|
|
@@ -93,7 +108,7 @@ export function SplashMascot({ skin, active }) {
|
|
|
93
108
|
overrideRef.current = rapid
|
|
94
109
|
? { semantic: 'tickle', since: at, until: at + TICKLE_WINDOW_MS }
|
|
95
110
|
: {
|
|
96
|
-
semantic: event.localCol < SPLASH_MASCOT_COLUMNS / 2 ? 'poke-left' : 'poke-right',
|
|
111
|
+
semantic: event.localCol < (imageBox?.columns ?? (skin === 'whaleGirl' ? WhaleGirlSkin.cells.columns : SPLASH_MASCOT_COLUMNS)) / 2 ? 'poke-left' : 'poke-right',
|
|
97
112
|
since: at,
|
|
98
113
|
until: at + SPLASH_POKE_MS,
|
|
99
114
|
};
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* 皮肤 = id + cells + render(pose, ctx),自定义皮肤走同一接口进入。
|
|
12
12
|
*/
|
|
13
13
|
import React from 'react';
|
|
14
|
+
import type { SplashImageBox } from '../../splashArtRows.js';
|
|
14
15
|
import type { I18nKey } from '../../../i18n.js';
|
|
15
16
|
import type { TerminalImageSource } from '../../../ink/terminal-image.js';
|
|
16
17
|
import type { CompanionMood } from './mood.js';
|
|
@@ -29,6 +30,8 @@ export interface CompanionSkinRenderInput {
|
|
|
29
30
|
* 旧调用方(缺省)皮肤退回 pose.mood 分层,属皮肤接口「未支持即
|
|
30
31
|
* 退化」契约。 */
|
|
31
32
|
readonly animationSemantic?: string;
|
|
33
|
+
/** 启动页专属紧凑图片槽;侧栏不传,保持原来的固定几何。 */
|
|
34
|
+
readonly imageBox?: SplashImageBox;
|
|
32
35
|
}
|
|
33
36
|
export interface CompanionSkin {
|
|
34
37
|
readonly id: string;
|
|
@@ -86,6 +89,11 @@ interface WhaleGirlImageAnimation {
|
|
|
86
89
|
interface WhaleGirlImageKit {
|
|
87
90
|
/** img/ 目录的绝对路径(帧 PNG 解码用)。 */
|
|
88
91
|
readonly dir: string;
|
|
92
|
+
/** idle 首帧 PNG 头部声明的稳定画布,不等待像素解码。 */
|
|
93
|
+
readonly canvas?: {
|
|
94
|
+
readonly width: number;
|
|
95
|
+
readonly height: number;
|
|
96
|
+
};
|
|
89
97
|
readonly byKey: Readonly<Record<string, WhaleGirlImageAnimation>>;
|
|
90
98
|
}
|
|
91
99
|
/** 惰性加载 + 缓存图像 timings;缺失/校验失败返回 undefined(退字母格)。 */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skins.d.ts","sourceRoot":"","sources":["../../../../../src/components/sidePanel/companion/skins.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AAYzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAE9C,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,sCAAsC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;;;iBAKa;IACb,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"skins.d.ts","sourceRoot":"","sources":["../../../../../src/components/sidePanel/companion/skins.tsx"],"names":[],"mappings":"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"}
|