dsh-better-display 0.1.1 → 0.3.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/CHANGELOG.md +188 -4
- package/README.en.md +19 -3
- package/README.md +19 -3
- package/compat/harness-rc2.json +59 -0
- package/docs/official-rendering-bridge.md +65 -0
- package/lib/client.js +8827 -7639
- package/lib/client.js.map +1 -1
- package/lib/dsh-better-display.js +51 -55
- package/lib/types/client/Blocks.d.ts +6 -2
- package/lib/types/client/Blocks.d.ts.map +1 -1
- package/lib/types/client/Blocks.js +13 -6
- package/lib/types/client/Blocks.js.map +1 -1
- package/lib/types/client/ChoreographedFlow.d.ts.map +1 -1
- package/lib/types/client/ChoreographedFlow.js +68 -20
- package/lib/types/client/ChoreographedFlow.js.map +1 -1
- package/lib/types/client/ClosedProcessSummary.d.ts.map +1 -1
- package/lib/types/client/ClosedProcessSummary.js +3 -2
- package/lib/types/client/ClosedProcessSummary.js.map +1 -1
- package/lib/types/client/DiffPanel.d.ts +13 -0
- package/lib/types/client/DiffPanel.d.ts.map +1 -0
- package/lib/types/client/DiffPanel.js +115 -0
- package/lib/types/client/DiffPanel.js.map +1 -0
- package/lib/types/client/OfficialContent.d.ts +25 -0
- package/lib/types/client/OfficialContent.d.ts.map +1 -0
- package/lib/types/client/OfficialContent.js +42 -0
- package/lib/types/client/OfficialContent.js.map +1 -0
- package/lib/types/client/Reader.d.ts.map +1 -1
- package/lib/types/client/Reader.js +123 -57
- package/lib/types/client/Reader.js.map +1 -1
- package/lib/types/client/ReasoningCard.d.ts.map +1 -1
- package/lib/types/client/ReasoningCard.js +29 -8
- package/lib/types/client/ReasoningCard.js.map +1 -1
- package/lib/types/client/SettingsSection.d.ts +35 -0
- package/lib/types/client/SettingsSection.d.ts.map +1 -0
- package/lib/types/client/SettingsSection.js +62 -0
- package/lib/types/client/SettingsSection.js.map +1 -0
- package/lib/types/client/StickyLane.d.ts.map +1 -1
- package/lib/types/client/StickyLane.js +16 -5
- package/lib/types/client/StickyLane.js.map +1 -1
- package/lib/types/client/ToolActivity.d.ts.map +1 -1
- package/lib/types/client/ToolActivity.js +59 -7
- package/lib/types/client/ToolActivity.js.map +1 -1
- package/lib/types/client/WaitClock.d.ts +16 -0
- package/lib/types/client/WaitClock.d.ts.map +1 -0
- package/lib/types/client/WaitClock.js +29 -0
- package/lib/types/client/WaitClock.js.map +1 -0
- package/lib/types/client/WaitingClock.d.ts +15 -0
- package/lib/types/client/WaitingClock.d.ts.map +1 -0
- package/lib/types/client/WaitingClock.js +30 -0
- package/lib/types/client/WaitingClock.js.map +1 -0
- package/lib/types/client/WaitingStatus.d.ts.map +1 -1
- package/lib/types/client/WaitingStatus.js +4 -14
- package/lib/types/client/WaitingStatus.js.map +1 -1
- package/lib/types/client/fold-choreography.d.ts +12 -0
- package/lib/types/client/fold-choreography.d.ts.map +1 -1
- package/lib/types/client/fold-choreography.js +18 -1
- package/lib/types/client/fold-choreography.js.map +1 -1
- package/lib/types/client/fold-intensity.d.ts +9 -0
- package/lib/types/client/fold-intensity.d.ts.map +1 -0
- package/lib/types/client/fold-intensity.js +25 -0
- package/lib/types/client/fold-intensity.js.map +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +62 -20
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/live-turn.d.ts.map +1 -1
- package/lib/types/client/live-turn.js +46 -0
- package/lib/types/client/live-turn.js.map +1 -1
- package/lib/types/client/markdown/MarkdownText.d.ts +3 -1
- package/lib/types/client/markdown/MarkdownText.d.ts.map +1 -1
- package/lib/types/client/markdown/MarkdownText.js +14 -7
- package/lib/types/client/markdown/MarkdownText.js.map +1 -1
- package/lib/types/client/markdown/render.d.ts +2 -0
- package/lib/types/client/markdown/render.d.ts.map +1 -1
- package/lib/types/client/markdown/render.js +22 -6
- package/lib/types/client/markdown/render.js.map +1 -1
- package/lib/types/client/motion.d.ts.map +1 -1
- package/lib/types/client/motion.js +102 -16
- package/lib/types/client/motion.js.map +1 -1
- package/lib/types/client/native/ContextInjectionRow.d.ts +12 -3
- package/lib/types/client/native/ContextInjectionRow.d.ts.map +1 -1
- package/lib/types/client/native/ContextInjectionRow.js +6 -3
- package/lib/types/client/native/ContextInjectionRow.js.map +1 -1
- package/lib/types/client/official-slots.d.ts +71 -0
- package/lib/types/client/official-slots.d.ts.map +1 -0
- package/lib/types/client/official-slots.js +183 -0
- package/lib/types/client/official-slots.js.map +1 -0
- package/lib/types/client/open-file.d.ts +6 -1
- package/lib/types/client/open-file.d.ts.map +1 -0
- package/lib/types/client/open-file.js +71 -0
- package/lib/types/client/open-file.js.map +1 -0
- package/lib/types/client/pending-submission.d.ts +28 -0
- package/lib/types/client/pending-submission.d.ts.map +1 -0
- package/lib/types/client/pending-submission.js +28 -0
- package/lib/types/client/pending-submission.js.map +1 -0
- package/lib/types/client/platform-media.d.ts +6 -0
- package/lib/types/client/platform-media.d.ts.map +1 -0
- package/lib/types/client/platform-media.js +13 -0
- package/lib/types/client/platform-media.js.map +1 -0
- package/lib/types/client/reasoning-follow.d.ts +5 -1
- package/lib/types/client/reasoning-follow.d.ts.map +1 -1
- package/lib/types/client/reasoning-follow.js +13 -4
- package/lib/types/client/reasoning-follow.js.map +1 -1
- package/lib/types/client/resource-address.d.ts +3 -0
- package/lib/types/client/resource-address.d.ts.map +1 -0
- package/lib/types/client/resource-address.js +33 -0
- package/lib/types/client/resource-address.js.map +1 -0
- package/lib/types/client/settings-copy.d.ts +3 -2
- package/lib/types/client/settings-copy.d.ts.map +1 -0
- package/lib/types/client/settings-copy.js +49 -0
- package/lib/types/client/settings-copy.js.map +1 -0
- package/lib/types/client/settings-slots.d.ts +7 -0
- package/lib/types/client/settings-slots.d.ts.map +1 -0
- package/lib/types/client/settings-slots.js +2 -0
- package/lib/types/client/settings-slots.js.map +1 -0
- package/lib/types/client/settings.d.ts +4 -0
- package/lib/types/client/settings.d.ts.map +1 -0
- package/lib/types/client/settings.js +60 -0
- package/lib/types/client/settings.js.map +1 -0
- package/lib/types/client/skill-status.d.ts +25 -0
- package/lib/types/client/skill-status.d.ts.map +1 -0
- package/lib/types/client/skill-status.js +44 -0
- package/lib/types/client/skill-status.js.map +1 -0
- package/lib/types/client/store.d.ts +15 -0
- package/lib/types/client/store.d.ts.map +1 -1
- package/lib/types/client/store.js +24 -2
- package/lib/types/client/store.js.map +1 -1
- package/lib/types/client/stream-buffer.d.ts +11 -3
- package/lib/types/client/stream-buffer.d.ts.map +1 -1
- package/lib/types/client/stream-buffer.js +39 -2
- package/lib/types/client/stream-buffer.js.map +1 -1
- package/lib/types/client/streaming.d.ts.map +1 -1
- package/lib/types/client/streaming.js +9 -2
- package/lib/types/client/streaming.js.map +1 -1
- package/lib/types/client/tool-activity.d.ts +47 -1
- package/lib/types/client/tool-activity.d.ts.map +1 -1
- package/lib/types/client/tool-activity.js +125 -2
- package/lib/types/client/tool-activity.js.map +1 -1
- package/lib/types/client/types.d.ts +28 -5
- package/lib/types/client/types.d.ts.map +1 -1
- package/lib/types/client/waiting-clock.d.ts +34 -2
- package/lib/types/client/waiting-clock.d.ts.map +1 -1
- package/lib/types/client/waiting-clock.js +62 -6
- package/lib/types/client/waiting-clock.js.map +1 -1
- package/lib/types/client/word-motion.d.ts.map +1 -1
- package/lib/types/client/word-motion.js +2 -1
- package/lib/types/client/word-motion.js.map +1 -1
- package/lib/types/client/word-timeline.d.ts +4 -0
- package/lib/types/client/word-timeline.d.ts.map +1 -1
- package/lib/types/client/word-timeline.js +21 -2
- package/lib/types/client/word-timeline.js.map +1 -1
- package/lib/types/dsh-better-display.d.ts.map +1 -1
- package/lib/types/dsh-better-display.js +42 -2
- package/lib/types/dsh-better-display.js.map +1 -1
- package/lib/types/skill-roots.d.ts +22 -0
- package/lib/types/skill-roots.d.ts.map +1 -0
- package/lib/types/skill-roots.js +126 -0
- package/lib/types/skill-roots.js.map +1 -0
- package/lib/types/skill-status.d.ts +36 -0
- package/lib/types/skill-status.d.ts.map +1 -0
- package/lib/types/skill-status.js +51 -0
- package/lib/types/skill-status.js.map +1 -0
- package/package.json +22 -16
- package/scripts/check-harness-compat.mjs +71 -0
- package/scripts/link-harness-dependencies.mjs +1 -0
- package/scripts/verify-auto-fold.mjs +114 -0
- package/scripts/verify-official-bridge.mjs +119 -0
- package/src/client/Blocks.tsx +13 -5
- package/src/client/ChoreographedFlow.tsx +68 -18
- package/src/client/ClosedProcessSummary.tsx +6 -2
- package/src/client/DiffPanel.tsx +124 -0
- package/src/client/OfficialContent.tsx +57 -0
- package/src/client/Reader.module.css +341 -13
- package/src/client/Reader.tsx +113 -49
- package/src/client/ReasoningCard.tsx +27 -6
- package/src/client/SettingsSection.module.css +37 -0
- package/src/client/SettingsSection.tsx +64 -6
- package/src/client/StickyLane.tsx +13 -3
- package/src/client/ToolActivity.tsx +67 -7
- package/src/client/WaitClock.tsx +33 -0
- package/src/client/WaitingStatus.tsx +6 -15
- package/src/client/fold-choreography.ts +18 -1
- package/src/client/fold-intensity.ts +28 -0
- package/src/client/index.tsx +29 -7
- package/src/client/live-turn.ts +43 -0
- package/src/client/markdown/MarkdownText.tsx +14 -6
- package/src/client/markdown/render.tsx +23 -7
- package/src/client/motion.tsx +91 -19
- package/src/client/native/ContextInjectionRow.tsx +19 -7
- package/src/client/official-slots.tsx +211 -0
- package/src/client/platform-media.ts +11 -0
- package/src/client/reasoning-follow.ts +20 -4
- package/src/client/settings-copy.ts +21 -0
- package/src/client/settings.tsx +1 -1
- package/src/client/store.ts +42 -2
- package/src/client/stream-buffer.ts +38 -2
- package/src/client/streaming.tsx +9 -1
- package/src/client/tool-activity.ts +148 -2
- package/src/client/types.ts +27 -6
- package/src/client/waiting-clock.ts +64 -6
- package/src/client/word-motion.tsx +2 -1
- package/src/client/word-timeline.ts +19 -2
- package/tests/browser-auto-fold.tsx +64 -0
- package/tests/browser-official-bridge.tsx +144 -0
- package/tests/fold-choreography.test.ts +16 -0
- package/tests/fold-intensity.test.ts +94 -0
- package/tests/footer-precedence.test.ts +129 -0
- package/tests/official-slots.test.ts +112 -0
- package/tests/open-file.test.ts +11 -0
- package/tests/platform-media.test.ts +17 -0
- package/tests/stock-install.test.ts +8 -0
- package/tests/stream-buffer.test.ts +8 -3
- package/tests/tool-diff.test.ts +57 -0
- package/tests/waiting-clock.test.ts +78 -19
- package/tests/word-timeline.test.ts +14 -4
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { DiffBlock, diffTotals } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
+
import css from './Reader.module.css';
|
|
5
|
+
import { diffBlockLabels } from './primitive-labels.js';
|
|
6
|
+
import { FOLD_TIMING } from './fold-choreography.js';
|
|
7
|
+
import { foldDiffHunks } from './tool-activity.js';
|
|
8
|
+
/**
|
|
9
|
+
* Reveals and withdraws its child by animating real height, in the plugin's own
|
|
10
|
+
* motion language. Height is the animated property rather than transform because
|
|
11
|
+
* the panel pushes the transcript down and the reading scroll measures that
|
|
12
|
+
* growth; the curve and duration are the fold choreography's, so this reads as
|
|
13
|
+
* the same gesture as opening a digest.
|
|
14
|
+
*
|
|
15
|
+
* Closing animates too: the child stays mounted until the shrink finishes, which
|
|
16
|
+
* is why the owner keeps it alive on an `open` flag instead of unmounting it.
|
|
17
|
+
*/
|
|
18
|
+
function MorphPanel({ open, children, onSettled, onClosed }) {
|
|
19
|
+
const ref = useRef(null);
|
|
20
|
+
useLayoutEffect(() => {
|
|
21
|
+
const element = ref.current;
|
|
22
|
+
if (!element)
|
|
23
|
+
return;
|
|
24
|
+
element.dispatchEvent(new CustomEvent('reader-layout-start', { bubbles: true }));
|
|
25
|
+
let released = false;
|
|
26
|
+
const release = () => { if (!released) {
|
|
27
|
+
released = true;
|
|
28
|
+
element.dispatchEvent(new CustomEvent('reader-layout-end', { bubbles: true }));
|
|
29
|
+
} };
|
|
30
|
+
if (typeof element.animate !== 'function') {
|
|
31
|
+
release();
|
|
32
|
+
if (!open)
|
|
33
|
+
onClosed();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const height = element.scrollHeight;
|
|
37
|
+
const frames = open
|
|
38
|
+
? [{ height: '0px', opacity: 0, transform: 'translateY(-6px)' },
|
|
39
|
+
{ height: `${height}px`, opacity: 1, transform: 'translateY(0)' }]
|
|
40
|
+
: [{ height: `${height}px`, opacity: 1 },
|
|
41
|
+
{ height: '0px', opacity: 0 }];
|
|
42
|
+
const animation = element.animate(frames, {
|
|
43
|
+
duration: FOLD_TIMING.reveal + FOLD_TIMING.settle,
|
|
44
|
+
easing: 'cubic-bezier(.4,0,.2,1)',
|
|
45
|
+
});
|
|
46
|
+
let settled = false;
|
|
47
|
+
const settle = () => {
|
|
48
|
+
if (settled)
|
|
49
|
+
return;
|
|
50
|
+
settled = true;
|
|
51
|
+
release();
|
|
52
|
+
if (open) {
|
|
53
|
+
element.style.height = 'auto';
|
|
54
|
+
element.style.overflow = 'visible';
|
|
55
|
+
onSettled?.(element);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
onClosed();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
animation.onfinish = settle;
|
|
62
|
+
const deadline = window.setTimeout(settle, FOLD_TIMING.reveal + FOLD_TIMING.settle + 240);
|
|
63
|
+
return () => { window.clearTimeout(deadline); animation.cancel(); release(); };
|
|
64
|
+
}, [open]);
|
|
65
|
+
return _jsx("div", { ref: ref, className: css.diffOverlay, children: children });
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Keeps a newly opened panel on screen.
|
|
69
|
+
*
|
|
70
|
+
* Opening grows the row downward, so a badge near the fold reveals its card below
|
|
71
|
+
* the viewport and the reader has to chase it. The scroll container is not known
|
|
72
|
+
* here, so the nearest scrollable ancestor is found and the card's head is brought
|
|
73
|
+
* to the top of it — the same thing a jump-to-anchor does.
|
|
74
|
+
*/
|
|
75
|
+
function revealPanel(element) {
|
|
76
|
+
let port = element.parentElement;
|
|
77
|
+
while (port && port.scrollHeight <= port.clientHeight + 1)
|
|
78
|
+
port = port.parentElement;
|
|
79
|
+
if (!port) {
|
|
80
|
+
element.scrollIntoView({ block: 'nearest' });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const gap = element.getBoundingClientRect().top - port.getBoundingClientRect().top;
|
|
84
|
+
// Already fully visible: leave the scroll alone rather than jumping on every open.
|
|
85
|
+
if (gap >= 0 && element.getBoundingClientRect().bottom <= port.getBoundingClientRect().bottom)
|
|
86
|
+
return;
|
|
87
|
+
port.scrollTop += gap - 8;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Line counts for everything a folded run of steps changed.
|
|
91
|
+
*
|
|
92
|
+
* A run that touched no files renders nothing, so an ordinary process digest is
|
|
93
|
+
* unchanged. Clicking the counts opens the same diff surface the official tool
|
|
94
|
+
* row uses — one file at a time, red removed / green added, expandable.
|
|
95
|
+
*/
|
|
96
|
+
export function DiffStat({ steps, label }) {
|
|
97
|
+
const hunks = useMemo(() => foldDiffHunks(steps), [steps]);
|
|
98
|
+
const [open, setOpen] = useState(false);
|
|
99
|
+
const [visible, setVisible] = useState(false);
|
|
100
|
+
const totals = useMemo(() => diffTotals(hunks), [hunks]);
|
|
101
|
+
const [active, setActive] = useState(0);
|
|
102
|
+
if (!hunks.length)
|
|
103
|
+
return null;
|
|
104
|
+
const current = hunks[Math.min(active, hunks.length - 1)];
|
|
105
|
+
return _jsxs(_Fragment, { children: [_jsx("span", { className: css.diffStatRoot, children: _jsxs("button", { type: "button", className: css.diffStatButton, "aria-expanded": open, "aria-label": `${label} · 改动 ${totals.added} 行,删除 ${totals.removed} 行`, onClick: event => {
|
|
106
|
+
event.stopPropagation();
|
|
107
|
+
if (open) {
|
|
108
|
+
setOpen(false);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
setVisible(true);
|
|
112
|
+
setOpen(true);
|
|
113
|
+
}, children: [_jsx("span", { className: css.diffCaret, "aria-hidden": true, children: open ? '\u25be' : '\u25b8' }), totals.added > 0 && _jsxs("span", { className: css.diffAdded, children: ["+", totals.added] }), totals.removed > 0 && _jsxs("span", { className: css.diffRemoved, children: ["-", totals.removed] })] }) }), visible && _jsx("div", { className: css.diffOverlayRow, children: _jsxs(MorphPanel, { open: open, onSettled: revealPanel, onClosed: () => setVisible(false), children: [hunks.length > 1 && _jsx("div", { className: css.diffTabs, children: hunks.map((hunk, index) => _jsx("button", { type: "button", className: css.diffTab, "data-active": index === active || undefined, onClick: () => setActive(index), title: hunk.path, children: hunk.path.split(/[/\\]+/).filter(Boolean).slice(-1)[0] ?? hunk.path }, `${hunk.path}:${index}`)) }), _jsx("div", { className: css.diffScrollArea, children: _jsx(DiffBlock, { diffs: [current], maxLines: 36, labels: diffBlockLabels }) })] }) })] });
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=DiffPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DiffPanel.js","sourceRoot":"","sources":["../../../src/client/DiffPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { ChatConversationViewNode, TurnTailOwnerProps } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
3
|
+
import type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
4
|
+
import type { AssistantActionOwnerProps } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
5
|
+
type MessageId = AssistantActionOwnerProps['messageId'];
|
|
6
|
+
import type { BlockRenderProps } from './types.js';
|
|
7
|
+
export declare function OfficialActions({ official, messageId }: Pick<BlockRenderProps, 'official'> & {
|
|
8
|
+
messageId?: MessageId;
|
|
9
|
+
}): import("react").JSX.Element | null;
|
|
10
|
+
export declare function OfficialTool({ official, block, toolName, cwd, openFile, fallback }: BlockRenderProps & {
|
|
11
|
+
official: NonNullable<BlockRenderProps['official']>;
|
|
12
|
+
block: ToolCallBlock;
|
|
13
|
+
toolName: string;
|
|
14
|
+
fallback: ReactNode;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
16
|
+
export declare function OfficialNode({ node, fallback, ...render }: BlockRenderProps & {
|
|
17
|
+
node: ChatConversationViewNode;
|
|
18
|
+
fallback: ReactNode;
|
|
19
|
+
}): string | number | boolean | Iterable<ReactNode> | import("react").JSX.Element | null | undefined;
|
|
20
|
+
export declare function OfficialTail({ official, owner, produced }: Pick<BlockRenderProps, 'official'> & {
|
|
21
|
+
owner?: TurnTailOwnerProps;
|
|
22
|
+
produced: readonly string[];
|
|
23
|
+
}): import("react").JSX.Element | null;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=OfficialContent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfficialContent.d.ts","sourceRoot":"","sources":["../../../src/client/OfficialContent.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7D,OAAO,KAAK,EAAE,wBAAwB,EAAsB,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC/H,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AACxF,KAAK,SAAS,GAAG,yBAAyB,CAAC,WAAW,CAAC,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,SAAS,CAAA;CAAE,sCAKtH;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,gBAAgB,GAAG;IACtG,QAAQ,EAAE,WAAW,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;CAClH,+BAUA;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,oGAoBrI;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,sCAK3J"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useSyncExternalStore } from 'react';
|
|
3
|
+
import { OFFICIAL_SEATS } from './official-slots.js';
|
|
4
|
+
export function OfficialActions({ official, messageId }) {
|
|
5
|
+
if (!official || !messageId)
|
|
6
|
+
return null;
|
|
7
|
+
return _jsx("span", { "data-reader-official-actions": true, style: { display: 'contents' }, children: official.renderSlot(OFFICIAL_SEATS.actions, { messageId }) });
|
|
8
|
+
}
|
|
9
|
+
export function OfficialTool({ official, block, toolName, cwd, openFile, fallback }) {
|
|
10
|
+
const home = useSyncExternalStore(official.officialHost.subscribe, official.officialHost.getSnapshot).home;
|
|
11
|
+
return _jsx("div", { "data-reader-tool-official": true, children: official.renderSlot(OFFICIAL_SEATS.tools, {
|
|
12
|
+
callId: block.callId, toolName, block, cwd, home,
|
|
13
|
+
openFile: openFile ?? (() => { }),
|
|
14
|
+
loadImage: official.officialImageLoader,
|
|
15
|
+
inspect: () => official.openView('trajectory', block.callId),
|
|
16
|
+
}, { entryKey: toolName, fallback }) });
|
|
17
|
+
}
|
|
18
|
+
export function OfficialNode({ node, fallback, ...render }) {
|
|
19
|
+
const official = render.official;
|
|
20
|
+
if (!official)
|
|
21
|
+
return fallback;
|
|
22
|
+
const owner = {
|
|
23
|
+
cwd: render.cwd,
|
|
24
|
+
openFile: render.openFile ?? (() => { }),
|
|
25
|
+
forkAt: render.forkAt ?? (() => { }),
|
|
26
|
+
inspectCall: callId => official.openView('trajectory', callId),
|
|
27
|
+
loadImage: official.officialImageLoader,
|
|
28
|
+
renderMessageImages: images => official.renderSlot(OFFICIAL_SEATS.images, {
|
|
29
|
+
...images, loadImage: official.officialImageLoader,
|
|
30
|
+
}),
|
|
31
|
+
fileMentions: official.officialFileMentions,
|
|
32
|
+
};
|
|
33
|
+
const turn = node.location.kind === 'turn' || node.location.kind === 'step' ? node.location.turn : undefined;
|
|
34
|
+
// The runtime node domain is open; the public SlotMap enumerates the known kinds.
|
|
35
|
+
const renderNode = official.renderSlot;
|
|
36
|
+
return _jsx("div", { "data-reader-official-node": node.kind, "data-chat-anchor-key": node.key, "data-chat-flow-kind": node.kind, children: renderNode(OFFICIAL_SEATS.nodes, { ...owner, node }, { entryKey: node.kind, hookContext: turn?.data, fallback }) });
|
|
37
|
+
}
|
|
38
|
+
export function OfficialTail({ official, owner, produced }) {
|
|
39
|
+
const tailOwner = owner && official ? { ...owner, openFile: official.officialPreviewFile, readerProducedPaths: produced } : undefined;
|
|
40
|
+
return official && tailOwner ? _jsx("div", { "data-reader-official-tail": true, style: { display: 'contents' }, children: official.renderSlotChain(OFFICIAL_SEATS.tail, tailOwner) }) : null;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=OfficialContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfficialContent.js","sourceRoot":"","sources":["../../../src/client/OfficialContent.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Reader.d.ts","sourceRoot":"","sources":["../../../src/client/Reader.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Reader.d.ts","sourceRoot":"","sources":["../../../src/client/Reader.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAoB,WAAW,EAAE,MAAM,YAAY,CAAC;AA+kBhE,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAiQxC"}
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { Fragment, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
|
|
3
3
|
import { JsonBlock, MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { BlockBoundary, Blocks, contentBlocks, CopyAnswer, UserMessageActions } from './Blocks.js';
|
|
5
5
|
import { ReasoningCard } from './ReasoningCard.js';
|
|
6
6
|
import { ToolActivity, ToolMedia } from './ToolActivity.js';
|
|
7
|
+
import { OfficialActions, OfficialNode, OfficialTail } from './OfficialContent.js';
|
|
7
8
|
import { preparingLabel, readerFlow } from './tool-activity.js';
|
|
8
9
|
import { Disclosure, ProcessFragment, RetiringContent, StatusText, useMotionAllowed, usePinnedSelection, useReadingScroll } from './motion.js';
|
|
9
10
|
import { StreamMotionContext } from './streaming.js';
|
|
10
11
|
import { assistantSegments, boundaryOf, forkAnchorSeq, groupNodes, hasProcessContent, hasVisibleBody, isEarlierNarration, processChoiceKey, processExpanded, terminalLabel } from './projection.js';
|
|
11
12
|
import { basename, createProducedFileMentions, dirname, getTurnDeliverables, showDeliverablesRow } from './deliverables.js';
|
|
13
|
+
import { deliverableOpenModeOf } from './open-file.js';
|
|
14
|
+
import { asReadonlyArray, pendingSubmissionImages } from './pending-submission.js';
|
|
12
15
|
import { WaitingStatus } from './WaitingStatus.js';
|
|
13
|
-
import { waitingAnchor } from './waiting-clock.js';
|
|
16
|
+
import { handsBackToModel, waitingAnchor } from './waiting-clock.js';
|
|
14
17
|
import { ContextInjectionRow } from './native/ContextInjectionRow.js';
|
|
15
18
|
import { TimelineRail } from './TimelineRail.js';
|
|
16
19
|
import { landTurn, scrollerOf } from './conversation-scroll.js';
|
|
17
20
|
import { mergeTimelineItems } from './timeline.js';
|
|
18
21
|
import { presentLiveTurn, segmentLiveTurn } from './live-turn.js';
|
|
22
|
+
import { frostedGlassOf } from './fold-intensity.js';
|
|
19
23
|
import { ChoreographedFlow, useFlowChat } from './ChoreographedFlow.js';
|
|
20
24
|
import { ClosedProcessSummary } from './ClosedProcessSummary.js';
|
|
21
25
|
import { StickyLane } from './StickyLane.js';
|
|
@@ -111,7 +115,7 @@ const AssistantNode = memo(function AssistantNode({ useChat, nodeKey, boundary,
|
|
|
111
115
|
// of its own; passing it would fork the whole session instead.
|
|
112
116
|
const anchor = forkAnchorSeq([data.finalNode, { seq: render.forkSeq }]);
|
|
113
117
|
return render.forkAt && anchor !== undefined ? () => render.forkAt(anchor) : undefined;
|
|
114
|
-
})(), metrics: render.metrics }))] }) }, part.start);
|
|
118
|
+
})(), metrics: render.metrics, extraActions: _jsx(OfficialActions, { official: render.official, messageId: data.finalNode?.messageId }) }))] }) }, part.start);
|
|
115
119
|
}) });
|
|
116
120
|
});
|
|
117
121
|
const MainNode = memo(function MainNode({ useChat, nodeKey, boundary, pinned, processOpen = false, ...render }) {
|
|
@@ -137,6 +141,8 @@ const MainNode = memo(function MainNode({ useChat, nodeKey, boundary, pinned, pr
|
|
|
137
141
|
if (isNode(node, 'model-retry'))
|
|
138
142
|
return node.data.current.retryState === 'scheduled'
|
|
139
143
|
? _jsx("div", { className: css.notice, role: "status", children: "\u6A21\u578B\u8BF7\u6C42\u672A\u6210\u529F\uFF0C\u6B63\u5728\u7B49\u5F85\u91CD\u8BD5\u3002\u8BE6\u60C5\u4FDD\u7559\u5728\u6267\u884C\u8FC7\u7A0B\u4E2D\u3002" }) : null;
|
|
144
|
+
if (isNode(node, 'command') && render.official)
|
|
145
|
+
return _jsx(OfficialNode, { ...render, node: node, fallback: _jsx(JsonBlock, { label: "\u547D\u4EE4\u8BB0\u5F55", payload: node.data, truncatedLabel: truncatedJsonLabel }) });
|
|
140
146
|
if (isNode(node, 'command')) {
|
|
141
147
|
if (node.data.outcome?.kind === 'error')
|
|
142
148
|
return _jsxs("div", { className: css.error, role: "alert", children: [_jsxs("svg", { className: css.errorIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: [_jsx("circle", { cx: "8", cy: "8", r: "6.5", strokeWidth: "1.2" }), _jsx("path", { d: "M8 5v3.5M8 11.2h.01", strokeWidth: "1.5", strokeLinecap: "round" })] }), _jsxs("div", { className: css.errorCopy, children: [_jsx("div", { className: css.errorTitle, children: _jsx("strong", { children: "\u547D\u4EE4\u6267\u884C\u672A\u6210\u529F" }) }), _jsx("p", { className: css.errorMessage, children: node.data.outcome.text ?? node.data.name ?? '查看原对话中的命令记录' })] })] });
|
|
@@ -146,12 +152,31 @@ const MainNode = memo(function MainNode({ useChat, nodeKey, boundary, pinned, pr
|
|
|
146
152
|
return null;
|
|
147
153
|
if (node.kind === 'context' || node.kind === 'turn-tail' || node.kind === 'system-prompt' || node.kind === 'turn-process')
|
|
148
154
|
return null;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return _jsxs("div", { className: css.unknown, "data-reader-anchor": true, children: [_jsxs("p", { children: ["\u6B64\u8BB0\u5F55\u7C7B\u578B\u6682\u672A\u63A5\u5165\u9605\u8BFB\u9875\uFF1A", node.kind] }), _jsx(JsonBlock, { label: "\u67E5\u770B\u539F\u59CB\u8BB0\u5F55", payload: node.data, truncatedLabel: truncatedJsonLabel })] });
|
|
155
|
+
// No 'command-input' branch: that string is not a chat node kind in any released
|
|
156
|
+
// host (it appears nowhere in the installed packages), so a slash command arrives
|
|
157
|
+
// as 'command' above and is rendered there. Keeping the branch only made the file
|
|
158
|
+
// look like it handled a case that cannot occur.
|
|
159
|
+
return _jsx(OfficialNode, { ...render, node: node, fallback: _jsxs("div", { className: css.unknown, "data-reader-anchor": true, children: [_jsxs("p", { children: ["\u6B64\u8BB0\u5F55\u7C7B\u578B\u6682\u672A\u63A5\u5165\u9605\u8BFB\u9875\uFF1A", node.kind] }), render.official && _jsx("button", { type: "button", className: css.textButton, onClick: () => render.official.openView('chat', node.key), children: "\u5728\u5BF9\u8BDD\u4E2D\u67E5\u770B" }), _jsx(JsonBlock, { label: "\u67E5\u770B\u539F\u59CB\u8BB0\u5F55", payload: node.data, truncatedLabel: truncatedJsonLabel })] }) });
|
|
154
160
|
});
|
|
161
|
+
/**
|
|
162
|
+
* Sub-agents this turn dispatched, read from the host's own Turn-process row.
|
|
163
|
+
*
|
|
164
|
+
* The host already derives this from the dispatch tree (`subagentCount` on
|
|
165
|
+
* `TurnProcessChatData`), so counting calls here would only risk disagreeing with
|
|
166
|
+
* it. The row is otherwise hidden by the reader, which is why this is the one place
|
|
167
|
+
* its counters are read.
|
|
168
|
+
*/
|
|
169
|
+
function subagentCount(snapshot, keys) {
|
|
170
|
+
for (const key of keys) {
|
|
171
|
+
const node = snapshot.nodes.get(key);
|
|
172
|
+
if (!node || node.kind !== 'turn-process')
|
|
173
|
+
continue;
|
|
174
|
+
const value = node.data.subagentCount;
|
|
175
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0)
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
155
180
|
function GroupStatus({ group, sessionId, useChat, useSessionPendingInteraction, motion }) {
|
|
156
181
|
const pending = useSessionPendingInteraction(snapshot => snapshot.get(sessionId));
|
|
157
182
|
const text = useChat(snapshot => {
|
|
@@ -168,8 +193,10 @@ function GroupStatus({ group, sessionId, useChat, useSessionPendingInteraction,
|
|
|
168
193
|
return '等待你的操作';
|
|
169
194
|
const current = turn.steps.at(-1)?.data.get('assistant-step');
|
|
170
195
|
const last = current?.blocks.at(-1);
|
|
171
|
-
if (current?.status === 'running' && last?.kind === 'tool-call')
|
|
172
|
-
|
|
196
|
+
if (current?.status === 'running' && last?.kind === 'tool-call') {
|
|
197
|
+
const spawned = subagentCount(snapshot, group.keys);
|
|
198
|
+
return spawned ? `${preparingLabel(last.name)}·${spawned} 个子代理` : preparingLabel(last.name);
|
|
199
|
+
}
|
|
173
200
|
for (let index = group.keys.length - 1; index >= 0; index--) {
|
|
174
201
|
const node = snapshot.nodes.get(group.keys[index]);
|
|
175
202
|
if (!node)
|
|
@@ -186,7 +213,7 @@ function GroupStatus({ group, sessionId, useChat, useSessionPendingInteraction,
|
|
|
186
213
|
const busy = useChat(snapshot => group.turn !== null && snapshot.timeline.turns.get(group.turn)?.status === 'open' && pending === undefined);
|
|
187
214
|
return _jsx(StatusText, { text: text, motion: motion, shimmer: busy });
|
|
188
215
|
}
|
|
189
|
-
const DeliverableChip = memo(function DeliverableChip({ path, openFile, revealFile }) {
|
|
216
|
+
const DeliverableChip = memo(function DeliverableChip({ path, openFile, revealFile, openMode }) {
|
|
190
217
|
const [status, setStatus] = useState('idle');
|
|
191
218
|
const timer = useRef();
|
|
192
219
|
const flash = (next) => {
|
|
@@ -231,9 +258,9 @@ const DeliverableChip = memo(function DeliverableChip({ path, openFile, revealFi
|
|
|
231
258
|
};
|
|
232
259
|
const name = basename(path);
|
|
233
260
|
const folder = dirname(path);
|
|
234
|
-
return (_jsxs("div", { className: css.deliverableChip, "data-status": status, title: path, children: [_jsxs("button", { type: "button", className: css.chipMain, onClick: onOpen, onDoubleClick: onOpen, "aria-label":
|
|
261
|
+
return (_jsxs("div", { className: css.deliverableChip, "data-status": status, title: path, children: [_jsxs("button", { type: "button", className: css.chipMain, onClick: onOpen, onDoubleClick: onOpen, "aria-label": openMode === 'sidebar' ? `在内置面板中打开 ${path}` : `用系统应用打开 ${path}`, children: [status === 'opened' ? (_jsx("svg", { className: css.statusIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M3.5 8.5l3 3 6-7", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) })) : (_jsxs("svg", { className: css.deliverableIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: [_jsx("path", { d: "M4 2.5h5l3 3V13.5H4V2.5z", strokeWidth: "1.2", strokeLinejoin: "round" }), _jsx("path", { d: "M9 2.5v3h3", strokeWidth: "1.2", strokeLinejoin: "round" })] })), _jsx("span", { className: css.deliverableName, children: status === 'opened' ? (openMode === 'sidebar' ? '已在面板打开' : '已在外部打开') : name })] }), _jsxs("div", { className: css.chipActions, "aria-label": "\u6587\u4EF6\u64CD\u4F5C", children: [_jsx("button", { type: "button", className: css.chipActionBtn, title: `在访达中定位所在目录 (${folder})`, "aria-label": "\u5728\u8BBF\u8FBE\u4E2D\u663E\u793A\u6240\u5728\u76EE\u5F55", onClick: onReveal, children: status === 'revealed' ? (_jsx("svg", { className: css.actionIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M3.5 8.5l3 3 6-7", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) })) : (_jsx("svg", { className: css.actionIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M2 4.5h4l1.5 2H14v6.5H2V4.5z", strokeWidth: "1.2", strokeLinejoin: "round" }) })) }), _jsx("button", { type: "button", className: css.chipActionBtn, title: "\u590D\u5236\u76F8\u5BF9\u8DEF\u5F84", "aria-label": "\u590D\u5236\u76F8\u5BF9\u8DEF\u5F84", onClick: onCopy, children: status === 'copied' ? (_jsx("svg", { className: css.actionIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M3.5 8.5l3 3 6-7", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) })) : (_jsxs("svg", { className: css.actionIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: [_jsx("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5", strokeWidth: "1.2" }), _jsx("path", { d: "M4 10.5H3a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v1", strokeWidth: "1.2", strokeLinecap: "round" })] })) })] })] }));
|
|
235
262
|
});
|
|
236
|
-
function DeliverablesRow({ deliverables, openFile, revealFile }) {
|
|
263
|
+
function DeliverablesRow({ deliverables, openFile, revealFile, openMode }) {
|
|
237
264
|
const [folderStatus, setFolderStatus] = useState('idle');
|
|
238
265
|
const onOpenWorkspace = () => {
|
|
239
266
|
try {
|
|
@@ -245,7 +272,7 @@ function DeliverablesRow({ deliverables, openFile, revealFile }) {
|
|
|
245
272
|
// ignore
|
|
246
273
|
}
|
|
247
274
|
};
|
|
248
|
-
return (_jsxs("div", { className: css.deliverablesRoot, "data-reader-deliverables": true, children: [_jsx("span", { className: css.deliverablesLabel, children: "\u4EA7\u7269" }), _jsx("div", { className: css.deliverablesLane, children: _jsxs("div", { className: css.deliverablesRow, children: [deliverables.slice(0, 8).map(path => (_jsx(DeliverableChip, { path: path, openFile: openFile, revealFile: revealFile }, path))), deliverables.length > 8 && (_jsxs("span", { className: css.deliverablesMore, children: ["+ ", deliverables.length - 8, " \u4E2A\u6587\u4EF6"] })), deliverables.length > 1 && (_jsxs("button", { type: "button", className: css.deliverablesShowFolder, "data-status": folderStatus, onClick: onOpenWorkspace, title: "\u5728\u8BBF\u8FBE\u4E2D\u6253\u5F00\u6574\u4E2A\u5DE5\u4F5C\u533A\u76EE\u5F55", children: [folderStatus === 'opened' && (_jsx("svg", { className: css.statusIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M3.5 8.5l3 3 6-7", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) })), _jsx("span", { children: folderStatus === 'opened' ? '已打开访达' : '在文件夹中显示' })] }))] }) })] }));
|
|
275
|
+
return (_jsxs("div", { className: css.deliverablesRoot, "data-reader-deliverables": true, children: [_jsx("span", { className: css.deliverablesLabel, children: "\u4EA7\u7269" }), _jsx("div", { className: css.deliverablesLane, children: _jsxs("div", { className: css.deliverablesRow, children: [deliverables.slice(0, 8).map(path => (_jsx(DeliverableChip, { path: path, openFile: openFile, revealFile: revealFile, openMode: openMode }, path))), deliverables.length > 8 && (_jsxs("span", { className: css.deliverablesMore, children: ["+ ", deliverables.length - 8, " \u4E2A\u6587\u4EF6"] })), deliverables.length > 1 && (_jsxs("button", { type: "button", className: css.deliverablesShowFolder, "data-status": folderStatus, onClick: onOpenWorkspace, title: "\u5728\u8BBF\u8FBE\u4E2D\u6253\u5F00\u6574\u4E2A\u5DE5\u4F5C\u533A\u76EE\u5F55", children: [folderStatus === 'opened' && (_jsx("svg", { className: css.statusIcon, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M3.5 8.5l3 3 6-7", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) })), _jsx("span", { children: folderStatus === 'opened' ? '已打开访达' : '在文件夹中显示' })] }))] }) })] }));
|
|
249
276
|
}
|
|
250
277
|
const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys, selectedProcessKeys, isAwaitingModel = false, ...props }) {
|
|
251
278
|
const snapshot = props.useChat(snapshot => snapshot);
|
|
@@ -266,6 +293,7 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
266
293
|
const flow = useMemo(() => readerFlow({ ...group, keys: mainKeys }, turn, key => nodes.get(key)), [nodes, group, mainKeys, turn]);
|
|
267
294
|
const steps = useMemo(() => segmentLiveTurn(flow, key => nodes.get(key)), [flow, nodes]);
|
|
268
295
|
const liveItems = useMemo(() => presentLiveTurn(steps, boundary, autoFold), [steps, boundary, autoFold]);
|
|
296
|
+
const openMode = deliverableOpenModeOf(useSyncExternalStore(props.openPrefs?.subscribe ?? ((fn) => { void fn; return () => { }; }), () => props.openPrefs?.getSnapshot()?.deliverableOpenMode, () => 'external'));
|
|
269
297
|
const hasProcess = flow.some(item => item.kind === 'tool' || hasProcessContent(nodes.get(item.nodeKey), boundary));
|
|
270
298
|
// Only a real, still-active text selection delays folding. Merely clicking,
|
|
271
299
|
// focusing or scrolling the live card does not create a permanent override.
|
|
@@ -273,10 +301,14 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
273
301
|
|| liveItems.some(item => item.kind === 'fold'
|
|
274
302
|
? item.key === key || item.steps.some(step => step.key === key || ('nodeKey' in step && step.nodeKey === key))
|
|
275
303
|
: item.key === key || ('nodeKey' in item.step && item.step.nodeKey === key)));
|
|
276
|
-
const expanded = holdingSelection || processExpanded(expansionChoice, boundary);
|
|
304
|
+
const expanded = !autoFold || holdingSelection || processExpanded(expansionChoice, boundary);
|
|
277
305
|
const [foldOpenByKey, setFoldOpenByKey] = useState({});
|
|
306
|
+
useLayoutEffect(() => {
|
|
307
|
+
// Restoring automatic folding also releases live "prior steps" disclosures.
|
|
308
|
+
if (autoFold)
|
|
309
|
+
setFoldOpenByKey(current => Object.keys(current).length ? {} : current);
|
|
310
|
+
}, [autoFold]);
|
|
278
311
|
const deliverables = useMemo(() => getTurnDeliverables(turn, flow), [turn, flow]);
|
|
279
|
-
const fileMentions = useMemo(() => deliverables.length > 0 && props.openFile ? createProducedFileMentions(deliverables, props.openFile) : undefined, [deliverables, props.openFile]);
|
|
280
312
|
const tailData = useMemo(() => {
|
|
281
313
|
for (const key of group.keys) {
|
|
282
314
|
const n = nodes.get(key);
|
|
@@ -285,6 +317,21 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
285
317
|
}
|
|
286
318
|
return undefined;
|
|
287
319
|
}, [group.keys, nodes]);
|
|
320
|
+
const tailOwner = useMemo(() => turn && tailData ? {
|
|
321
|
+
turn, seq: tailData.closing?.finalNode.seq ?? tailData.seq, openFile: props.openFile,
|
|
322
|
+
} : undefined, [turn, tailData, props.openFile]);
|
|
323
|
+
const fileMentions = useMemo(() => {
|
|
324
|
+
const official = tailOwner ? props.officialFileMentions(tailOwner) : undefined;
|
|
325
|
+
const reader = deliverables.length > 0 ? createProducedFileMentions(deliverables, props.openFile) : undefined;
|
|
326
|
+
return official || reader ? { resolve: (value) => official?.resolve(value) ?? reader?.resolve(value) } : undefined;
|
|
327
|
+
}, [tailOwner, props.officialFileMentions, deliverables, props.openFile]);
|
|
328
|
+
const official = useMemo(() => ({
|
|
329
|
+
renderSlot: props.renderSlot, renderSlotChain: props.renderSlotChain,
|
|
330
|
+
officialImageLoader: props.officialImageLoader, officialFileMentions: props.officialFileMentions,
|
|
331
|
+
officialPreviewFile: props.officialPreviewFile,
|
|
332
|
+
officialHost: props.officialHost, openView: props.openView,
|
|
333
|
+
}), [props.renderSlot, props.renderSlotChain, props.officialImageLoader, props.officialFileMentions, props.officialPreviewFile, props.officialHost, props.openView]);
|
|
334
|
+
const cwd = props.useSessions(state => state.byId[props.sessionId]?.cwd);
|
|
288
335
|
const runMs = turn?.start && turn?.end ? Math.max(0, turn.end.time - turn.start.time) : undefined;
|
|
289
336
|
const metrics = useMemo(() => ({
|
|
290
337
|
usage: tailData?.tokenUsage,
|
|
@@ -315,10 +362,18 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
315
362
|
forkSeq,
|
|
316
363
|
fileMentions,
|
|
317
364
|
metrics,
|
|
365
|
+
official,
|
|
366
|
+
cwd,
|
|
318
367
|
};
|
|
319
368
|
const terminal = terminalLabel(boundary.reason);
|
|
320
369
|
const hasTurnError = flow.some(item => item.kind === 'node' && nodes.get(item.nodeKey)?.kind === 'turn-error');
|
|
321
|
-
|
|
370
|
+
// A stopped turn normally stays quiet, but a stopped turn that produced no
|
|
371
|
+
// prose at all reads as if its answer vanished into the process fold. Say so.
|
|
372
|
+
const hasAnswerProse = steps.some(step => step.kind === 'body'
|
|
373
|
+
&& step.blocks.some(block => block.kind === 'text' && block.text.trim() !== ''));
|
|
374
|
+
const stoppedWithoutAnswer = (boundary.reason === 'interrupted' || boundary.reason === 'aborted') && !hasAnswerProse;
|
|
375
|
+
const showTerminalNotice = terminal && !hasTurnError
|
|
376
|
+
&& (stoppedWithoutAnswer || (boundary.reason !== 'interrupted' && boundary.reason !== 'aborted'));
|
|
322
377
|
const renderStep = (step, folded) => {
|
|
323
378
|
const processOpen = folded || expanded;
|
|
324
379
|
if (step.kind === 'reasoning' || step.kind === 'body')
|
|
@@ -329,7 +384,7 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
329
384
|
return _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, boundary: boundary, nodeKey: step.nodeKey }) });
|
|
330
385
|
return _jsxs(Fragment, { children: [_jsx(BlockBoundary, { children: _jsx(ProcessNode, { useChat: useFlowChat, t: props.t, nodeKey: step.nodeKey, open: processOpen, motion: motion, onRead: pinProcess, returnFocusTo: processButton }) }), _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, boundary: boundary, nodeKey: step.nodeKey, pinned: pinnedKeys.includes(step.nodeKey), processOpen: processOpen }) })] });
|
|
331
386
|
};
|
|
332
|
-
return _jsxs("section", { className: css.turn, "data-reader-turn": group.turn ?? 'unresolved', "data-reader-turn-state": boundary.status, "data-reader-turn-result": boundary.reason ?? undefined, children: [startsWithUser && _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, useChat: props.useChat, boundary: boundary, nodeKey: group.keys[0] }) }), hasProcess && !isAwaitingModel && _jsx(StickyLane, { kind: "status", className: css.turnProcessSticky, children: _jsx(Disclosure, { open: expanded, onChange: setExpanded, controls: flowId, buttonRef: processButton, label: _jsx(GroupStatus, { group: group, sessionId: props.sessionId, useChat: props.useChat, useSessionPendingInteraction: props.useSessionPendingInteraction, motion: motion }) }) }), boundary.status === 'closed' && hasProcess && _jsx(ClosedProcessSummary, { open: expanded, onChange: setExpanded, controls: flowId, steps: steps.filter(step => {
|
|
387
|
+
return _jsxs("section", { className: css.turn, "data-reader-turn": group.turn ?? 'unresolved', "data-reader-turn-state": boundary.status, "data-reader-turn-result": boundary.reason ?? undefined, children: [startsWithUser && _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, useChat: props.useChat, boundary: boundary, nodeKey: group.keys[0] }) }), hasProcess && !isAwaitingModel && _jsx(StickyLane, { kind: "status", className: css.turnProcessSticky, children: _jsx(Disclosure, { open: expanded, onChange: setExpanded, controls: flowId, buttonRef: processButton, label: _jsx(GroupStatus, { group: group, sessionId: props.sessionId, useChat: props.useChat, useSessionPendingInteraction: props.useSessionPendingInteraction, motion: motion }) }) }), boundary.status === 'closed' && hasProcess && autoFold && _jsx(ClosedProcessSummary, { open: expanded, onChange: setExpanded, controls: flowId, steps: steps.filter(step => {
|
|
333
388
|
if (step.kind === 'user')
|
|
334
389
|
return false;
|
|
335
390
|
if (step.kind !== 'body')
|
|
@@ -338,7 +393,7 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
338
393
|
return !!node && isNode(node, 'assistant-step') && (isEarlierNarration(node.data, boundary)
|
|
339
394
|
|| node.data.blocks.some(block => block.kind === 'tool-call')
|
|
340
395
|
|| (boundary.latestStep > 0 && node.data.step < boundary.latestStep));
|
|
341
|
-
}) }), _jsx(ChoreographedFlow, { id: flowId, frame: presentation, motion: motion, enabled: autoFold && boundary.status === 'open' && !holdingSelection, urgent: hasTurnError || interaction !== undefined || !sessionRunning, open: foldOpenByKey, processOpen: expanded, onOpenChange: (key, value) => { pinProcess(); setFoldOpenByKey(current => ({ ...current, [key]: value })); }, renderStep: renderStep }), !hasProcess && boundary.status === 'open' && !isAwaitingModel && _jsx("div", { className: css.disclosure, "data-reader-status-only": true, children: _jsx(GroupStatus, { group: group, sessionId: props.sessionId, useChat: props.useChat, useSessionPendingInteraction: props.useSessionPendingInteraction, motion: motion }) }), showDeliverablesRow(boundary.status, deliverables) && _jsx(DeliverablesRow, { deliverables: deliverables, openFile: props.openFile, revealFile: props.revealFile }), showTerminalNotice && _jsx("div", { className: css.notice, "data-reader-terminal": true, children: terminal })] });
|
|
396
|
+
}) }), _jsx(ChoreographedFlow, { id: flowId, frame: presentation, motion: motion, enabled: autoFold && boundary.status === 'open' && !holdingSelection, urgent: hasTurnError || interaction !== undefined || !sessionRunning, open: foldOpenByKey, processOpen: expanded, onOpenChange: (key, value) => { pinProcess(); setFoldOpenByKey(current => ({ ...current, [key]: value })); }, renderStep: renderStep }), !hasProcess && boundary.status === 'open' && !isAwaitingModel && _jsx("div", { className: css.disclosure, "data-reader-status-only": true, children: _jsx(GroupStatus, { group: group, sessionId: props.sessionId, useChat: props.useChat, useSessionPendingInteraction: props.useSessionPendingInteraction, motion: motion }) }), showDeliverablesRow(boundary.status, deliverables) && _jsx(DeliverablesRow, { deliverables: deliverables, openFile: props.openFile, revealFile: props.revealFile, openMode: openMode }), boundary.status === 'closed' && _jsx(BlockBoundary, { children: _jsx(OfficialTail, { official: official, owner: tailOwner, produced: deliverables }) }), showTerminalNotice && _jsx("div", { className: css.notice, "data-reader-terminal": true, children: terminal })] });
|
|
342
397
|
});
|
|
343
398
|
export function Reader(props) {
|
|
344
399
|
const root = useRef(null);
|
|
@@ -353,51 +408,56 @@ export function Reader(props) {
|
|
|
353
408
|
const hasMore = props.useSession(snapshot => snapshot.hasMore);
|
|
354
409
|
const loadingOlder = props.useSession(snapshot => snapshot.loadingOlder);
|
|
355
410
|
const pendingSubmissions = props.useSession(snapshot => snapshot.pendingSubmissions);
|
|
356
|
-
const
|
|
411
|
+
const pendingList = asReadonlyArray(pendingSubmissions);
|
|
412
|
+
const waitAnchor = waitingAnchor(order, key => nodes.get(key), pendingList);
|
|
357
413
|
const motionPreference = props.useStore(state => state.motion);
|
|
358
414
|
const motion = useMotionAllowed(motionPreference);
|
|
359
|
-
const
|
|
415
|
+
const prefsSnap = useSyncExternalStore(props.openPrefs?.subscribe ?? ((fn) => { void fn; return () => { }; }), () => props.openPrefs?.getSnapshot?.(), () => undefined);
|
|
416
|
+
const storeAutoFold = props.useStore(state => state.autoFold);
|
|
417
|
+
const autoFold = prefsSnap?.autoFold ?? (prefsSnap?.foldIntensity !== undefined ? prefsSnap.foldIntensity !== 0 : undefined) ?? storeAutoFold ?? true;
|
|
418
|
+
const previousAutoFold = useRef(autoFold);
|
|
419
|
+
useLayoutEffect(() => {
|
|
420
|
+
const restored = autoFold && !previousAutoFold.current;
|
|
421
|
+
previousAutoFold.current = autoFold;
|
|
422
|
+
// Watch the effective preference so toolbar and Settings changes behave alike.
|
|
423
|
+
// Reading while folding is off must not pin completed process content forever.
|
|
424
|
+
if (restored)
|
|
425
|
+
props.actions.resetExpanded();
|
|
426
|
+
}, [autoFold, props.actions]);
|
|
427
|
+
const frostedGlass = frostedGlassOf(prefsSnap);
|
|
360
428
|
const streamMotion = useMemo(() => ({ enabled: motion, activatedAt: activatedAt.current }), [motion]);
|
|
361
429
|
const groups = useMemo(() => groupNodes(order, key => nodes.get(key)), [order, nodes, timeline]);
|
|
362
430
|
const isAwaitingModel = useMemo(() => {
|
|
363
431
|
// 状态机铁律:「深度求索中」与「正在处理/思考」永远互斥。
|
|
364
432
|
// 1. 仅空闲发送(!running)的本地回显窗口:用户按下回车第 0 毫秒立即反馈。
|
|
365
433
|
// 忙碌时的 queued/steering 回显不算等待——模型本来就在工作。
|
|
366
|
-
if (!running &&
|
|
434
|
+
if (!running && pendingList.length > 0)
|
|
367
435
|
return true;
|
|
368
436
|
const lastKey = order.at(-1);
|
|
369
437
|
const lastNode = lastKey ? nodes.get(lastKey) : undefined;
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
//
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const turn = lastGroup?.turn === null || lastGroup?.turn === undefined ? undefined : timeline.turns.get(lastGroup.turn);
|
|
376
|
-
if (turn?.status === 'closed')
|
|
377
|
-
return false;
|
|
378
|
-
const keys = lastGroup?.keys ?? [];
|
|
379
|
-
const userIdx = keys.lastIndexOf(lastKey);
|
|
380
|
-
const hasOutputAfter = keys.slice(userIdx + 1).some(key => {
|
|
381
|
-
const node = nodes.get(key);
|
|
382
|
-
if (!node || node.visibility === 'hidden')
|
|
383
|
-
return false;
|
|
384
|
-
if (node.kind === 'assistant-step') {
|
|
385
|
-
const data = node.data;
|
|
386
|
-
return Array.isArray(data.blocks) && data.blocks.length > 0;
|
|
387
|
-
}
|
|
388
|
-
return node.kind === 'tool-call' || node.kind === 'tool-return' || node.kind === 'command-input';
|
|
389
|
-
});
|
|
390
|
-
return !hasOutputAfter;
|
|
391
|
-
}
|
|
392
|
-
// 3. assistant-step 已创建但一个 block 都没有:模型收到请求、内容未吐出的瞬间。
|
|
393
|
-
if (lastNode?.kind === 'assistant-step') {
|
|
438
|
+
if (!lastKey || !lastNode)
|
|
439
|
+
return false;
|
|
440
|
+
// 2. 模型已经产出内容:等待结束,计时器必须立刻消失。
|
|
441
|
+
// 「产出」= assistant-step 已带 block(正文/思维链/工具调用都算)。
|
|
442
|
+
if (lastNode.kind === 'assistant-step') {
|
|
394
443
|
const data = lastNode.data;
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
444
|
+
const blocks = data.blocks ?? [];
|
|
445
|
+
// 空 block 且仍在跑 = 请求已发出、模型还没吐字,正是要计时的那一刻。
|
|
446
|
+
return blocks.length === 0 && data.status === 'running';
|
|
398
447
|
}
|
|
399
|
-
|
|
400
|
-
|
|
448
|
+
// 3. 球在模型脚下:用户刚发言,或工具已返回、上下文已注入、命令已执行。
|
|
449
|
+
// 这些时刻模型随时可能卡住,正是要计时的地方;工具执行期间不计时,
|
|
450
|
+
// 因为那时忙的是工具而不是模型。
|
|
451
|
+
// handsBackToModel 内部按 chat 层 kind 判定,并区分「工具已返回」与
|
|
452
|
+
// 「工具仍在跑」——后者不算等待。
|
|
453
|
+
const modelOwesResponse = handsBackToModel(lastNode);
|
|
454
|
+
if (!modelOwesResponse)
|
|
455
|
+
return false;
|
|
456
|
+
// 轮次已结束就没什么可等的。
|
|
457
|
+
const lastGroup = groups.at(-1);
|
|
458
|
+
const turn = lastGroup?.turn === null || lastGroup?.turn === undefined ? undefined : timeline.turns.get(lastGroup.turn);
|
|
459
|
+
return turn?.status !== 'closed';
|
|
460
|
+
}, [running, pendingList, order, nodes, groups, timeline]);
|
|
401
461
|
const scroll = useReadingScroll(root, motion);
|
|
402
462
|
const pinnedKeys = usePinnedSelection(root);
|
|
403
463
|
const selectedProcessKeys = usePinnedSelection(root, '[data-reader-process]');
|
|
@@ -505,7 +565,7 @@ export function Reader(props) {
|
|
|
505
565
|
}, [props.loadThrough, props.loadOlder, scroll.jump, scroll.release, timelineItems]);
|
|
506
566
|
const lastKey = order.at(-1);
|
|
507
567
|
const lastNode = lastKey ? nodes.get(lastKey) : undefined;
|
|
508
|
-
const lastSubmissionId =
|
|
568
|
+
const lastSubmissionId = pendingList.length ? pendingList[pendingList.length - 1].requestId : null;
|
|
509
569
|
const lastOrderKeyRef = useRef(lastKey);
|
|
510
570
|
const lastSubmissionRef = useRef(lastSubmissionId);
|
|
511
571
|
useLayoutEffect(() => {
|
|
@@ -518,13 +578,16 @@ export function Reader(props) {
|
|
|
518
578
|
}
|
|
519
579
|
}, [lastKey, lastNode?.kind, lastSubmissionId, scroll]);
|
|
520
580
|
const visibleSubmissions = useMemo(() => {
|
|
521
|
-
if (
|
|
581
|
+
if (pendingList.length === 0)
|
|
522
582
|
return [];
|
|
523
|
-
return
|
|
524
|
-
}, [
|
|
583
|
+
return pendingList.filter(sub => sub.placement !== 'queued');
|
|
584
|
+
}, [pendingList]);
|
|
525
585
|
// ChatView publishes data-chat-flow="" on its column. Skins treat a
|
|
526
586
|
// scrollport without that hook as inspect-only and hide [data-composer-seat].
|
|
527
|
-
return _jsx(StreamMotionContext.Provider, { value: streamMotion, children: _jsxs("div", { ref: root, className: css.root, "data-dsh-better-display": "0.
|
|
587
|
+
return _jsx(StreamMotionContext.Provider, { value: streamMotion, children: _jsxs("div", { ref: root, className: css.root, "data-reader-build": "0.3.0-preview.1", "data-dsh-better-display": "0.3.0-preview.1", "data-reader-wait-clock-version": "input-v1", "data-reader-wait-start": waitAnchor.time ?? undefined, "data-motion": motion ? 'on' : 'off', "data-reader-glass": frostedGlass || undefined, "data-reader-auto-fold": autoFold ? 'on' : 'off', children: [_jsx(TimelineRail, { items: timelineItems, activeTurn: activeTurn, busyTurn: busyTurn, onNavigate: onNavigateTurn }), _jsxs("div", { className: css.column, "data-chat-flow": "", children: [_jsx(StickyLane, { kind: "toolbar", className: css.toolbar, children: _jsx("button", { type: "button", className: css.textButton, "aria-pressed": autoFold, onClick: () => {
|
|
588
|
+
props.actions.setAutoFold(!autoFold);
|
|
589
|
+
props.openPrefs?.actions?.setAutoFold?.(!autoFold);
|
|
590
|
+
}, title: "\u65B0\u601D\u8003\u4EA7\u751F\u65F6\uFF0C\u662F\u5426\u81EA\u52A8\u5C06\u6B64\u524D\u6B65\u9AA4\u6536\u62E2\u4E3A\u4E00\u884C\u6C47\u603B\u3002\u5173\u95ED\u540E\u5B8C\u6574\u4FDD\u7559\u539F\u59CB\u8FC7\u7A0B\u4E0E\u6D41\u5F0F\u8F93\u51FA\u3002", children: `自动折叠${autoFold ? '开' : '关'}` }) }), hasMore && _jsx("button", { type: "button", className: css.historyButton, disabled: loadingOlder, onClick: async () => {
|
|
528
591
|
setHistoryError(false);
|
|
529
592
|
try {
|
|
530
593
|
await props.loadOlder();
|
|
@@ -532,6 +595,9 @@ export function Reader(props) {
|
|
|
532
595
|
catch {
|
|
533
596
|
setHistoryError(true);
|
|
534
597
|
}
|
|
535
|
-
}, children: loadingOlder ? '正在加载更早记录' : '加载更早记录' }), historyError && _jsx("div", { className: css.notice, children: "\u5386\u53F2\u8BB0\u5F55\u52A0\u8F7D\u5931\u8D25\uFF0C\u53EF\u518D\u6B21\u5C1D\u8BD5\uFF1B\u73B0\u6709\u5185\u5BB9\u672A\u6539\u53D8\u3002" }), openError && _jsxs("div", { className: css.error, role: "alert", children: ["\u4F1A\u8BDD\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\uFF1A", openError.message] }), loading && groups.length === 0 && _jsx("p", { className: css.empty, role: "status", children: "\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u2026" }), groups.map(group => _jsx(TurnGroup, { ...props, group: group, motion: motion, autoFold: autoFold, pinnedKeys: pinnedKeys, selectedProcessKeys: selectedProcessKeys, isAwaitingModel: isAwaitingModel && group.key === groups.at(-1)?.key }, group.key)), visibleSubmissions.map(submission =>
|
|
598
|
+
}, children: loadingOlder ? '正在加载更早记录' : '加载更早记录' }), historyError && _jsx("div", { className: css.notice, children: "\u5386\u53F2\u8BB0\u5F55\u52A0\u8F7D\u5931\u8D25\uFF0C\u53EF\u518D\u6B21\u5C1D\u8BD5\uFF1B\u73B0\u6709\u5185\u5BB9\u672A\u6539\u53D8\u3002" }), openError && _jsxs("div", { className: css.error, role: "alert", children: ["\u4F1A\u8BDD\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\uFF1A", openError.message] }), loading && groups.length === 0 && _jsx("p", { className: css.empty, role: "status", children: "\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u2026" }), groups.map(group => _jsx(TurnGroup, { ...props, group: group, motion: motion, autoFold: autoFold, pinnedKeys: pinnedKeys, selectedProcessKeys: selectedProcessKeys, isAwaitingModel: isAwaitingModel && group.key === groups.at(-1)?.key }, group.key)), visibleSubmissions.map(submission => {
|
|
599
|
+
const images = pendingSubmissionImages(submission);
|
|
600
|
+
return (_jsxs("div", { className: css.userCluster, "data-reader-pending-submission": true, children: [images.length > 0 && (_jsx("div", { className: css.userImages, children: images.map((item, idx) => (_jsx("figure", { className: css.imageFigure, children: _jsx("div", { className: css.imageFrame, style: { aspectRatio: `${item.width || 4} / ${item.height || 3}` }, children: _jsx("img", { src: item.previewUrl, alt: item.name ?? '发送的图片', className: css.pendingImage }) }) }, idx))) })), submission.text ? (_jsx("div", { className: css.user, children: _jsx("div", { className: css.blocks, children: submission.text }) })) : null, _jsx(UserMessageActions, { text: submission.text ?? '', time: submission.time })] }, submission.requestId));
|
|
601
|
+
}), isAwaitingModel && _jsx(WaitingStatus, { anchor: waitAnchor, label: props.t ? props.t('chat.deepDiving') : '深度求索中...' }), pending !== undefined && _jsxs("div", { className: css.attention, role: "alert", "data-reader-attention": true, children: [_jsx("strong", { children: pending.kind === 'question' ? '需要你回答一个问题' : '需要你的确认' }), _jsx("span", { children: "\u8BF7\u5728\u4E0B\u65B9\u539F\u751F\u64CD\u4F5C\u533A\u5904\u7406\u3002\u6B64\u63D0\u793A\u4E0D\u4F1A\u6536\u8FDB\u6267\u884C\u8FC7\u7A0B\u3002" })] }), scroll.detached && _jsx("div", { className: css.jumpDock, children: _jsx("button", { type: "button", className: css.jump, "aria-label": "\u56DE\u5230\u5E95\u90E8", title: "\u56DE\u5230\u5E95\u90E8", onClick: scroll.jump, children: _jsx("svg", { viewBox: "0 0 14 14", width: "14", height: "14", "aria-hidden": "true", children: _jsx("path", { d: "M3 5.5 7 9.5 11 5.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }) }) }) })] })] }) });
|
|
536
602
|
}
|
|
537
603
|
//# sourceMappingURL=Reader.js.map
|