@khorsheed/dsh-client-message-tools 0.3.0 → 0.3.1
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 +13 -0
- package/README.en.md +9 -9
- package/README.i18n.yaml +2 -2
- package/README.md +8 -8
- package/lib/client.js +270 -112
- package/lib/index.js +186 -45
- package/lib/typert.host.js +40 -28
- package/lib/typert.remote-client.js +33 -21
- package/lib/types/client/ModelChip.js +4 -4
- package/lib/types/client/RestoredMessageView.js +2 -2
- package/lib/types/client/UserMessageView.js +5 -4
- package/lib/types/client/WithdrawnDividerView.d.ts +3 -2
- package/lib/types/client/WithdrawnDividerView.js +11 -5
- package/lib/types/client/chat-hook.js +1 -0
- package/lib/types/client/dom-hider.js +11 -4
- package/lib/types/client/icons-local.d.ts +23 -0
- package/lib/types/client/icons-local.js +11 -0
- package/lib/types/client/icons.d.ts +18 -19
- package/lib/types/client/icons.js +5 -10
- package/lib/types/client/index.js +6 -1
- package/lib/types/client/withdrawn-node.d.ts +17 -10
- package/lib/types/client/withdrawn-node.js +37 -19
- package/lib/types/index.d.ts +11 -11
- package/lib/types/index.js +19 -14
- package/lib/types/marker.d.ts +45 -13
- package/lib/types/marker.js +57 -30
- package/lib/types/restore-projection.d.ts +55 -0
- package/lib/types/restore-projection.js +123 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +8 -27
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
let _khorsheed_dsh_client_message_tools_messageTools_edit_parameter_0$schema$value
|
|
5
|
+
const _khorsheed_dsh_client_message_tools_messageTools_edit_parameter_0$schema = () => (_khorsheed_dsh_client_message_tools_messageTools_edit_parameter_0$schema$value ??= z.object({
|
|
5
6
|
'sessionId': z.intersection(z.string(), z.unknown()).readonly(),
|
|
6
7
|
'targetSeq': z.number().readonly(),
|
|
7
8
|
'text': z.string().readonly(),
|
|
8
|
-
})
|
|
9
|
-
|
|
9
|
+
}))
|
|
10
|
+
let _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema$value
|
|
11
|
+
const _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema = () => (_khorsheed_dsh_client_message_tools_messageTools_edit_result$schema$value ??= z.union([z.object({
|
|
10
12
|
'ok': z.literal(true).readonly(),
|
|
11
13
|
'value': z.object({
|
|
12
14
|
'replacementSeq': z.number().readonly(),
|
|
@@ -25,12 +27,14 @@ const _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema = z.un
|
|
|
25
27
|
}), z.object({
|
|
26
28
|
'code': z.literal("empty-text").readonly(),
|
|
27
29
|
})]).readonly(),
|
|
28
|
-
})])
|
|
29
|
-
|
|
30
|
+
})]))
|
|
31
|
+
let _khorsheed_dsh_client_message_tools_messageTools_restore_parameter_0$schema$value
|
|
32
|
+
const _khorsheed_dsh_client_message_tools_messageTools_restore_parameter_0$schema = () => (_khorsheed_dsh_client_message_tools_messageTools_restore_parameter_0$schema$value ??= z.object({
|
|
30
33
|
'sessionId': z.intersection(z.string(), z.unknown()).readonly(),
|
|
31
34
|
'targetSeq': z.number().readonly(),
|
|
32
|
-
})
|
|
33
|
-
|
|
35
|
+
}))
|
|
36
|
+
let _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema$value
|
|
37
|
+
const _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema = () => (_khorsheed_dsh_client_message_tools_messageTools_restore_result$schema$value ??= z.union([z.object({
|
|
34
38
|
'ok': z.literal(true).readonly(),
|
|
35
39
|
'value': z.object({
|
|
36
40
|
'restoredSeq': z.number().readonly(),
|
|
@@ -46,12 +50,14 @@ const _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema = z
|
|
|
46
50
|
}), z.object({
|
|
47
51
|
'code': z.literal("not-withdrawn").readonly(),
|
|
48
52
|
})]).readonly(),
|
|
49
|
-
})])
|
|
50
|
-
|
|
53
|
+
})]))
|
|
54
|
+
let _khorsheed_dsh_client_message_tools_messageTools_withdraw_parameter_0$schema$value
|
|
55
|
+
const _khorsheed_dsh_client_message_tools_messageTools_withdraw_parameter_0$schema = () => (_khorsheed_dsh_client_message_tools_messageTools_withdraw_parameter_0$schema$value ??= z.object({
|
|
51
56
|
'sessionId': z.intersection(z.string(), z.unknown()).readonly(),
|
|
52
57
|
'targetSeq': z.number().readonly(),
|
|
53
|
-
})
|
|
54
|
-
|
|
58
|
+
}))
|
|
59
|
+
let _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema$value
|
|
60
|
+
const _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema = () => (_khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema$value ??= z.union([z.object({
|
|
55
61
|
'ok': z.literal(true).readonly(),
|
|
56
62
|
'value': z.object({
|
|
57
63
|
'replacementSeq': z.number().readonly(),
|
|
@@ -68,7 +74,7 @@ const _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema =
|
|
|
68
74
|
}), z.object({
|
|
69
75
|
'code': z.literal("already-withdrawn").readonly(),
|
|
70
76
|
})]).readonly(),
|
|
71
|
-
})])
|
|
77
|
+
})]))
|
|
72
78
|
|
|
73
79
|
export const TYPERT_REMOTE = {
|
|
74
80
|
package: '@khorsheed/dsh-client-message-tools',
|
|
@@ -87,16 +93,18 @@ export const TYPERT_REMOTE = {
|
|
|
87
93
|
codec: {
|
|
88
94
|
mode: 'strict',
|
|
89
95
|
typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsEditRequest',
|
|
90
|
-
|
|
96
|
+
create: _khorsheed_dsh_client_message_tools_messageTools_edit_parameter_0$schema,
|
|
97
|
+
schema: _khorsheed_dsh_client_message_tools_messageTools_edit_parameter_0$schema(),
|
|
91
98
|
},
|
|
92
99
|
},
|
|
93
100
|
],
|
|
94
101
|
result: {
|
|
95
102
|
mode: 'strict',
|
|
96
103
|
typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsEditResult',
|
|
97
|
-
|
|
104
|
+
create: _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema,
|
|
105
|
+
schema: _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema(),
|
|
98
106
|
},
|
|
99
|
-
sourceLocation: {"file":"packages/message-tools/src/index.ts","line":
|
|
107
|
+
sourceLocation: {"file":"packages/message-tools/src/index.ts","line":140,"column":9},
|
|
100
108
|
},
|
|
101
109
|
{
|
|
102
110
|
id: '@khorsheed/dsh-client-message-tools#messageTools/restore',
|
|
@@ -112,16 +120,18 @@ export const TYPERT_REMOTE = {
|
|
|
112
120
|
codec: {
|
|
113
121
|
mode: 'strict',
|
|
114
122
|
typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsRestoreRequest',
|
|
115
|
-
|
|
123
|
+
create: _khorsheed_dsh_client_message_tools_messageTools_restore_parameter_0$schema,
|
|
124
|
+
schema: _khorsheed_dsh_client_message_tools_messageTools_restore_parameter_0$schema(),
|
|
116
125
|
},
|
|
117
126
|
},
|
|
118
127
|
],
|
|
119
128
|
result: {
|
|
120
129
|
mode: 'strict',
|
|
121
130
|
typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsRestoreResult',
|
|
122
|
-
|
|
131
|
+
create: _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema,
|
|
132
|
+
schema: _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema(),
|
|
123
133
|
},
|
|
124
|
-
sourceLocation: {"file":"packages/message-tools/src/index.ts","line":
|
|
134
|
+
sourceLocation: {"file":"packages/message-tools/src/index.ts","line":102,"column":9},
|
|
125
135
|
},
|
|
126
136
|
{
|
|
127
137
|
id: '@khorsheed/dsh-client-message-tools#messageTools/withdraw',
|
|
@@ -137,16 +147,18 @@ export const TYPERT_REMOTE = {
|
|
|
137
147
|
codec: {
|
|
138
148
|
mode: 'strict',
|
|
139
149
|
typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsWithdrawRequest',
|
|
140
|
-
|
|
150
|
+
create: _khorsheed_dsh_client_message_tools_messageTools_withdraw_parameter_0$schema,
|
|
151
|
+
schema: _khorsheed_dsh_client_message_tools_messageTools_withdraw_parameter_0$schema(),
|
|
141
152
|
},
|
|
142
153
|
},
|
|
143
154
|
],
|
|
144
155
|
result: {
|
|
145
156
|
mode: 'strict',
|
|
146
157
|
typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsWithdrawResult',
|
|
147
|
-
|
|
158
|
+
create: _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema,
|
|
159
|
+
schema: _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema(),
|
|
148
160
|
},
|
|
149
|
-
sourceLocation: {"file":"packages/message-tools/src/index.ts","line":
|
|
161
|
+
sourceLocation: {"file":"packages/message-tools/src/index.ts","line":68,"column":9},
|
|
150
162
|
},
|
|
151
163
|
],
|
|
152
164
|
}
|
|
@@ -8,7 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
* seat (ModelSelect), simplified to one scrollable menu.
|
|
9
9
|
*/
|
|
10
10
|
import { useEffect, useRef, useState } from 'react';
|
|
11
|
-
import {
|
|
11
|
+
import { IconCheckOutlineMedium, IconChevronDownOutlineMedium } from './icons.js';
|
|
12
12
|
import css from './ModelChip.module.css';
|
|
13
13
|
/** The editor's model chip: trigger with current model/effort plus the switch menu. */
|
|
14
14
|
export function ModelChip({ useModelDirectory, loadModels, selectModel, t }) {
|
|
@@ -74,13 +74,13 @@ export function ModelChip({ useModelDirectory, loadModels, selectModel, t }) {
|
|
|
74
74
|
});
|
|
75
75
|
};
|
|
76
76
|
return (_jsxs("div", { ref: rootRef, className: css.chip, children: [_jsxs("button", { type: "button", className: css.trigger, "aria-label": t('model.aria', { model: modelLabel }), "aria-haspopup": "menu", "aria-expanded": open, title: effortLabel === undefined ? modelLabel : `${modelLabel} · ${effortLabel}`, disabled: busy, onClick: () => { setOpen(!open); if (!open)
|
|
77
|
-
loadModels(); }, children: [_jsx("span", { className: css.triggerLabel, children: modelLabel }), effortLabel !== undefined && _jsx("span", { className: css.triggerEffort, children: effortLabel }), _jsx(
|
|
77
|
+
loadModels(); }, children: [_jsx("span", { className: css.triggerLabel, children: modelLabel }), effortLabel !== undefined && _jsx("span", { className: css.triggerEffort, children: effortLabel }), _jsx(IconChevronDownOutlineMedium, {})] }), open && (_jsxs("div", { className: css.menu, role: "menu", "aria-label": t('model.menuAria'), "aria-busy": state.status === 'loading', children: [reasoning !== undefined && current !== null && (_jsxs("section", { className: css.group, children: [_jsx("div", { className: css.groupTitle, children: t('model.effort') }), [
|
|
78
78
|
...reasoning.defaultEffort === undefined
|
|
79
79
|
? [{ key: 'provider-default', effort: undefined, label: t('model.providerDefault') }]
|
|
80
80
|
: [],
|
|
81
81
|
...reasoning.efforts.map(level => ({ key: level.id, effort: level.id, label: level.name })),
|
|
82
|
-
].map(level => (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": effectiveEffort === level.effort, className: css.option, disabled: busy, onClick: () => { chooseEffort(level.effort); }, children: [_jsx("span", { className: css.optionLabel, children: level.label }), _jsx("span", { className: css.check, children: effectiveEffort === level.effort ? _jsx(
|
|
82
|
+
].map(level => (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": effectiveEffort === level.effort, className: css.option, disabled: busy, onClick: () => { chooseEffort(level.effort); }, children: [_jsx("span", { className: css.optionLabel, children: level.label }), _jsx("span", { className: css.check, children: effectiveEffort === level.effort ? _jsx(IconCheckOutlineMedium, {}) : null })] }, level.key)))] })), state.groups.map(group => (_jsxs("section", { className: css.group, children: [_jsx("div", { className: css.groupTitle, children: group.name }), group.models.map((model) => {
|
|
83
83
|
const selected = current?.provider === group.id && current.model === model.id;
|
|
84
|
-
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": selected, className: css.option, disabled: busy, onClick: () => { chooseModel(group.id, model.id, model.reasoning?.defaultEffort); }, children: [_jsx("span", { className: css.optionLabel, children: model.name }), _jsx("span", { className: css.check, children: selected ? _jsx(
|
|
84
|
+
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": selected, className: css.option, disabled: busy, onClick: () => { chooseModel(group.id, model.id, model.reasoning?.defaultEffort); }, children: [_jsx("span", { className: css.optionLabel, children: model.name }), _jsx("span", { className: css.check, children: selected ? _jsx(IconCheckOutlineMedium, {}) : null })] }, model.id));
|
|
85
85
|
})] }, group.id))), state.status === 'ready' && state.groups.length === 0 && (_jsx("div", { className: css.empty, children: t('model.empty') }))] })), failed && _jsx("span", { className: css.selectFailed, role: "status", children: t('model.selectFailed') })] }));
|
|
86
86
|
}
|
|
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
*/
|
|
12
12
|
import { useMemo } from 'react';
|
|
13
13
|
import { MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
14
|
-
import {
|
|
14
|
+
import { IconUndoOutlineMedium } from './icons-local.js';
|
|
15
15
|
import css from './RestoredMessageView.module.css';
|
|
16
16
|
/** Localized Markdown chrome (code-fence copy buttons, footnotes) over the message-tools namespace. */
|
|
17
17
|
function markdownLabels(t) {
|
|
@@ -24,5 +24,5 @@ function markdownLabels(t) {
|
|
|
24
24
|
export function RestoredMessageView({ node, t }) {
|
|
25
25
|
const data = node.data;
|
|
26
26
|
const labels = useMemo(() => markdownLabels(t), [t]);
|
|
27
|
-
return (_jsxs("div", { className: css.restoredAssistantRow, children: [_jsxs("div", { className: css.restoredLabel, children: [_jsx(
|
|
27
|
+
return (_jsxs("div", { className: css.restoredAssistantRow, children: [_jsxs("div", { className: css.restoredLabel, children: [_jsx(IconUndoOutlineMedium, {}), _jsx("span", { children: t('restored.assistant') })] }), _jsx(MarkdownText, { text: data.text, labels: labels })] }));
|
|
28
28
|
}
|
|
@@ -12,8 +12,9 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
12
12
|
*/
|
|
13
13
|
import { memo, useEffect, useRef, useState } from 'react';
|
|
14
14
|
import { shallowEqual } from '@deepseek-ai/dsh-client-store';
|
|
15
|
-
import { Button,
|
|
16
|
-
import {
|
|
15
|
+
import { Button, JsonBlock, RiskConfirmation, Tooltip, writeClipboard } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
16
|
+
import { IconCheckOutlineMedium, IconCopyOutlineMedium, IconEditOutlineMedium } from './icons.js';
|
|
17
|
+
import { IconUndoOutlineMedium } from './icons-local.js';
|
|
17
18
|
import { foldHiddenRanges, isSeqHidden } from './withdrawn-node.js';
|
|
18
19
|
import { chatHookOf } from './chat-hook.js';
|
|
19
20
|
import { withdrawAndBackfill } from './withdraw-backfill.js';
|
|
@@ -95,7 +96,7 @@ function CopyButton({ text, t }) {
|
|
|
95
96
|
}, 1000);
|
|
96
97
|
});
|
|
97
98
|
};
|
|
98
|
-
return (_jsx(Tooltip, { label: copied ? t('copied') : t('copy'), side: "bottom", children: _jsx("button", { type: "button", className: css.action, "aria-label": copied ? t('copied') : t('copy'), onClick: onCopy, children: copied ? _jsx(
|
|
99
|
+
return (_jsx(Tooltip, { label: copied ? t('copied') : t('copy'), side: "bottom", children: _jsx("button", { type: "button", className: css.action, "aria-label": copied ? t('copied') : t('copy'), onClick: onCopy, children: copied ? _jsx(IconCheckOutlineMedium, {}) : _jsx(IconCopyOutlineMedium, {}) }) }));
|
|
99
100
|
}
|
|
100
101
|
/** Cap for the auto-grown edit box height; longer text scrolls inside it. */
|
|
101
102
|
const EDITOR_MAX_HEIGHT = 240;
|
|
@@ -174,5 +175,5 @@ export const UserMessageView = memo(function UserMessageView({ node, renderMessa
|
|
|
174
175
|
? { useModelDirectory, loadModels, selectModel, t }
|
|
175
176
|
: undefined }), failed === 'edit' && _jsx("div", { className: css.actionError, role: "status", children: t('error.edit') })] }) }));
|
|
176
177
|
}
|
|
177
|
-
return (_jsxs("div", { className: css.userRow, "data-time-hover-root": true, children: [_jsxs("div", { className: css.userStack, children: [node.kind === 'message-tools-edited' && (_jsx("div", { className: css.editedLabel, children: t('edited.badge') })), node.kind === 'message-tools-restored' && (_jsx("div", { className: css.editedLabel, children: t('withdrawn.restored') })), images.length > 0 ? renderMessageImages({ images, align: 'end' }) : null, showBubble && (_jsxs("div", { className: css.bubble, children: [projectUserText(text), rest.map((block, index) => (_jsx(JsonBlock, { label: t('message.extraBlock'), payload: block, truncatedLabel: truncated }, index)))] }))] }), _jsxs("div", { className: css.actions, children: [_jsx(CopyButton, { text: text, t: t }), _jsx(Tooltip, { label: t('edit'), side: "bottom", children: _jsx("button", { type: "button", className: css.action, "aria-label": t('edit'), onClick: () => { setFailed(null); setEditing(true); }, children: _jsx(
|
|
178
|
+
return (_jsxs("div", { className: css.userRow, "data-time-hover-root": true, children: [_jsxs("div", { className: css.userStack, children: [node.kind === 'message-tools-edited' && (_jsx("div", { className: css.editedLabel, children: t('edited.badge') })), node.kind === 'message-tools-restored' && (_jsx("div", { className: css.editedLabel, children: t('withdrawn.restored') })), images.length > 0 ? renderMessageImages({ images, align: 'end' }) : null, showBubble && (_jsxs("div", { className: css.bubble, children: [projectUserText(text), rest.map((block, index) => (_jsx(JsonBlock, { label: t('message.extraBlock'), payload: block, truncatedLabel: truncated }, index)))] }))] }), _jsxs("div", { className: css.actions, children: [_jsx(CopyButton, { text: text, t: t }), _jsx(Tooltip, { label: t('edit'), side: "bottom", children: _jsx("button", { type: "button", className: css.action, "aria-label": t('edit'), onClick: () => { setFailed(null); setEditing(true); }, children: _jsx(IconEditOutlineMedium, {}) }) }), _jsx(Tooltip, { label: t('action.withdraw'), side: "bottom", children: _jsx("button", { type: "button", className: css.action, "aria-label": t('action.withdraw'), onClick: () => { setFailed(null); setAcknowledged(false); setConfirming(true); }, children: _jsx(IconUndoOutlineMedium, {}) }) })] }), failed === 'withdraw' && _jsx("div", { className: css.actionError, role: "status", children: t('error.withdraw') }), confirming && (_jsx(RiskConfirmation, { open: true, title: t('withdraw.title'), description: t('withdraw.description'), acknowledgeLabel: t('withdraw.acknowledge'), cancelLabel: t('cancel'), closeLabel: t('close'), confirmLabel: t('withdraw.confirm'), acknowledged: acknowledged, disabled: busy, onAcknowledgedChange: setAcknowledged, onCancel: () => { setConfirming(false); setAcknowledged(false); }, onConfirm: confirmWithdraw }))] }));
|
|
178
179
|
});
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Withdrawal divider row ('message-tools-withdrawn' keyed renderer), redone
|
|
3
3
|
* in the official compaction-marker visual language: one dim clickable row
|
|
4
4
|
* (chevron +「已撤回 N 条消息」), expanding in place to a read-only replay of
|
|
5
|
-
* the withdrawn span (user originals
|
|
5
|
+
* the withdrawn span (user originals — text and attached images — plus
|
|
6
|
+
* assistant text) with a「恢复到对话
|
|
6
7
|
* 末尾」action. The restore tail-replays the span's replayable entries (user
|
|
7
8
|
* messages + assistant text) — the surface fold is positional, so in-place
|
|
8
9
|
* restoration is impossible — and the divider carries a「已恢复」badge while
|
|
@@ -13,5 +14,5 @@
|
|
|
13
14
|
import { type ReactNode } from 'react';
|
|
14
15
|
import type { WithdrawnDividerViewProps } from './slots.ts';
|
|
15
16
|
/** The withdrawal divider: collapsed marker row plus the expandable replay. */
|
|
16
|
-
export declare function WithdrawnDividerView({ node, t, restoreMessage, ...standard }: WithdrawnDividerViewProps): ReactNode;
|
|
17
|
+
export declare function WithdrawnDividerView({ node, t, restoreMessage, renderMessageImages, ...standard }: WithdrawnDividerViewProps): ReactNode;
|
|
17
18
|
//# sourceMappingURL=WithdrawnDividerView.d.ts.map
|
|
@@ -3,7 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
* Withdrawal divider row ('message-tools-withdrawn' keyed renderer), redone
|
|
4
4
|
* in the official compaction-marker visual language: one dim clickable row
|
|
5
5
|
* (chevron +「已撤回 N 条消息」), expanding in place to a read-only replay of
|
|
6
|
-
* the withdrawn span (user originals
|
|
6
|
+
* the withdrawn span (user originals — text and attached images — plus
|
|
7
|
+
* assistant text) with a「恢复到对话
|
|
7
8
|
* 末尾」action. The restore tail-replays the span's replayable entries (user
|
|
8
9
|
* messages + assistant text) — the surface fold is positional, so in-place
|
|
9
10
|
* restoration is impossible — and the divider carries a「已恢复」badge while
|
|
@@ -12,14 +13,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
13
|
* once withdrawals backfill the draft automatically.
|
|
13
14
|
*/
|
|
14
15
|
import { useState } from 'react';
|
|
15
|
-
import { Button
|
|
16
|
-
import {
|
|
16
|
+
import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
17
|
+
import { IconChevronDownOutlineMedium, IconChevronRightOutlineMedium } from './icons.js';
|
|
18
|
+
import { IconUndoOutlineMedium } from './icons-local.js';
|
|
17
19
|
import { collectWithdrawnEntries, countHiddenInSpan, foldHiddenRanges, hasRestoreForSpan, isRestoreSuperseded, } from './withdrawn-node.js';
|
|
18
20
|
import { chatHookOf } from './chat-hook.js';
|
|
19
21
|
import css from './WithdrawnDividerView.module.css';
|
|
20
22
|
/** The withdrawal divider: collapsed marker row plus the expandable replay. */
|
|
21
|
-
export function WithdrawnDividerView({ node, t, restoreMessage, ...standard }) {
|
|
23
|
+
export function WithdrawnDividerView({ node, t, restoreMessage, renderMessageImages, ...standard }) {
|
|
22
24
|
const data = node.data;
|
|
25
|
+
// The gallery closure is a required owner prop of every `conversation.chat.node`
|
|
26
|
+
// renderer; widening it to `| undefined` keeps the degrade path explicit — a
|
|
27
|
+
// composition without the attachment UI replays text only instead of throwing.
|
|
28
|
+
const renderImages = renderMessageImages;
|
|
23
29
|
const [expanded, setExpanded] = useState(false);
|
|
24
30
|
const [entries, setEntries] = useState(null);
|
|
25
31
|
const [busy, setBusy] = useState(false);
|
|
@@ -59,5 +65,5 @@ export function WithdrawnDividerView({ node, t, restoreMessage, ...standard }) {
|
|
|
59
65
|
.catch(() => { setFailed(true); })
|
|
60
66
|
.finally(() => { setBusy(false); });
|
|
61
67
|
};
|
|
62
|
-
return (_jsxs("div", { className: css.row, children: [_jsxs("button", { type: "button", className: css.marker, "aria-expanded": expanded, "aria-label": t('withdrawn.expand'), onClick: toggle, children: [_jsx("span", { className: css.leading, "aria-hidden": true, children: expanded ? _jsx(
|
|
68
|
+
return (_jsxs("div", { className: css.row, children: [_jsxs("button", { type: "button", className: css.marker, "aria-expanded": expanded, "aria-label": t('withdrawn.expand'), onClick: toggle, children: [_jsx("span", { className: css.leading, "aria-hidden": true, children: expanded ? _jsx(IconChevronDownOutlineMedium, {}) : _jsx(IconChevronRightOutlineMedium, {}) }), _jsx("span", { className: css.title, children: t('withdrawn.divider', { count }) }), restored && (_jsxs("span", { className: css.badge, children: [_jsx(IconUndoOutlineMedium, {}), t('withdrawn.restored')] }))] }), expanded && (_jsxs("div", { className: css.body, children: [entries === null || entries.length === 0 ? (_jsx("div", { className: css.empty, children: t('withdrawn.empty') })) : (entries.map((entry, index) => (_jsxs("div", { className: entry.kind === 'user' ? css.entryUser : css.entryAssistant, children: [_jsx("div", { className: css.entryKind, children: entry.kind === 'user' ? t('withdrawn.entryUser') : t('withdrawn.entryAssistant') }), entry.text !== '' && _jsx("div", { className: css.textRun, children: entry.text }), renderImages !== undefined && entry.images.length > 0 && (_jsx("div", { className: css.entryImages, children: renderImages({ images: entry.images, align: 'end' }) }))] }, index)))), _jsxs("div", { className: css.restoreRow, children: [_jsx(Button, { variant: "outline", disabled: busy || restored || superseded, onClick: restore, children: t('withdrawn.restore') }), failed && _jsx("span", { className: css.restoreFailed, role: "status", children: t('withdrawn.restoreFailed') })] })] }))] }));
|
|
63
69
|
}
|
|
@@ -9,6 +9,7 @@ const EMPTY_CHAT = {
|
|
|
9
9
|
nodes: {
|
|
10
10
|
get: () => undefined,
|
|
11
11
|
source: () => ({ getSnapshot: () => undefined, subscribe: () => () => { } }),
|
|
12
|
+
turnDataSource: () => ({ getSnapshot: () => [], subscribe: () => () => { } }),
|
|
12
13
|
processSource: () => ({ getSnapshot: () => undefined, subscribe: () => () => { } }),
|
|
13
14
|
values: () => [],
|
|
14
15
|
},
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { chatSourceOf } from './chat-hook.js';
|
|
2
|
-
import {
|
|
2
|
+
import { isMessageToolsSource, messageToolsOp } from '../marker.js';
|
|
3
3
|
import { foldHiddenRanges, isSeqHidden } from './withdrawn-node.js';
|
|
4
|
+
/**
|
|
5
|
+
* The main-view session: host 0.1.6-alpha.2 dropped SessionListState.current
|
|
6
|
+
* for the retainedBy.mainView count on each summary (the label is ui-session's
|
|
7
|
+
* declaration merge, read here as a plain duck-typed count so this package
|
|
8
|
+
* needs no ui-session edge), with the legacy field as the older-host fallback.
|
|
9
|
+
*/
|
|
10
|
+
const mainSessionId = (list) => Object.values(list.byId ?? {}).find(session => (session.retainedBy?.mainView ?? 0) > 0)?.id
|
|
11
|
+
?? list.current;
|
|
4
12
|
/**
|
|
5
13
|
* Flow keys of every chat node inside a hidden span. The withdrawal divider
|
|
6
14
|
* itself (`message-tools-withdrawn`, anchored at the span's exclusive end) is
|
|
@@ -25,12 +33,11 @@ export function hiddenFlowKeys(nodes, ranges) {
|
|
|
25
33
|
continue;
|
|
26
34
|
if (node.kind === 'context') {
|
|
27
35
|
const data = node.data;
|
|
28
|
-
const source = data.source;
|
|
29
36
|
// Every message-tools context row is a presentation duplicate or a
|
|
30
37
|
// wake-only trigger: restore replays are rendered by the plugin's own
|
|
31
38
|
// rows, edit triggers are never transcript content, and withdrawal /
|
|
32
39
|
// edit replacements are not append-surface context rows at all.
|
|
33
|
-
if (source
|
|
40
|
+
if (isMessageToolsSource(data.source)
|
|
34
41
|
|| restoredSeqs.has(data.seq ?? -1)
|
|
35
42
|
|| messageToolsOp(data.source) === 'edit-trigger') {
|
|
36
43
|
keys.push(node.key);
|
|
@@ -167,7 +174,7 @@ export function installDomHider(ctx, options = {}) {
|
|
|
167
174
|
});
|
|
168
175
|
};
|
|
169
176
|
const bindCurrent = () => {
|
|
170
|
-
const current = ctx.sessions.list.getSnapshot()
|
|
177
|
+
const current = mainSessionId(ctx.sessions.list.getSnapshot());
|
|
171
178
|
if (current === activeSession && stopSession !== undefined)
|
|
172
179
|
return;
|
|
173
180
|
stopSession?.();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned icons matching the official outline-icon conventions (16×16
|
|
3
|
+
* viewBox, `fill="currentColor"` path geometry) for glyphs ui-primitives
|
|
4
|
+
* does not ship.
|
|
5
|
+
*/
|
|
6
|
+
/** Props of one package-owned icon. */
|
|
7
|
+
export interface LocalIconProps {
|
|
8
|
+
/** Rendered edge length in px (icons are authored at 16). */
|
|
9
|
+
size?: number;
|
|
10
|
+
/** Optional composed class. */
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Undo arrow (withdraw/restore), rotate-ccw style: a near-full circle arc
|
|
15
|
+
* (~290°, opening at the top) with a corner arrowhead at its left end
|
|
16
|
+
* pointing the counterclockwise direction. ui-primitives ships no undo/revert
|
|
17
|
+
* glyph, and its refresh glyph carries reload semantics, so this package
|
|
18
|
+
* draws its own.
|
|
19
|
+
* @param props - size and className.
|
|
20
|
+
* @returns the icon element.
|
|
21
|
+
*/
|
|
22
|
+
export declare const IconUndoOutlineMedium: ({ size, className }: LocalIconProps) => import("react").JSX.Element;
|
|
23
|
+
//# sourceMappingURL=icons-local.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Undo arrow (withdraw/restore), rotate-ccw style: a near-full circle arc
|
|
4
|
+
* (~290°, opening at the top) with a corner arrowhead at its left end
|
|
5
|
+
* pointing the counterclockwise direction. ui-primitives ships no undo/revert
|
|
6
|
+
* glyph, and its refresh glyph carries reload semantics, so this package
|
|
7
|
+
* draws its own.
|
|
8
|
+
* @param props - size and className.
|
|
9
|
+
* @returns the icon element.
|
|
10
|
+
*/
|
|
11
|
+
export const IconUndoOutlineMedium = ({ size = 16, className }) => (_jsxs("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.4, strokeLinecap: "round", strokeLinejoin: "round", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M2.2 8.7 A5.8 5.8 0 1 0 5.9 3.3 L2.6 6.6" }), _jsx("path", { d: "M2.6 2.4 V6.6 H6.2", fill: "none" })] }));
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Generated by scripts/sync-icon-artwork.mts from @deepseek-ai/dsh-client-ui-primitives
|
|
3
|
+
* (v0.1.7-rc.1, harness 46a7f68b09) — do not edit by hand. The 0.1.5 and rc.1 host lines
|
|
4
|
+
* share no icon export name, so the rc.1 artwork is inlined here: pure SVG,
|
|
5
|
+
* no host runtime identity, identical on every line. Re-run the generator
|
|
6
|
+
* after adding an icon import or bumping the harness checkout.
|
|
7
|
+
*
|
|
8
|
+
* @module @khorsheed/dsh-client-message-tools/client
|
|
5
9
|
*/
|
|
6
|
-
/**
|
|
7
|
-
export interface
|
|
8
|
-
/**
|
|
9
|
-
size?: number;
|
|
10
|
-
/**
|
|
11
|
-
className?: string;
|
|
10
|
+
/** Shared props for every product icon component (mirrors the upstream IconProps). */
|
|
11
|
+
export interface IconProps {
|
|
12
|
+
/** Square edge in px; defaults to the glyph's own drawn size. */
|
|
13
|
+
size?: number | undefined;
|
|
14
|
+
/** Extra class for layout placement; color rides currentColor. */
|
|
15
|
+
className?: string | undefined;
|
|
12
16
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* draws its own.
|
|
19
|
-
* @param props - size and className.
|
|
20
|
-
* @returns the icon element.
|
|
21
|
-
*/
|
|
22
|
-
export declare const IconUndoOutline16: ({ size, className }: LocalIconProps) => import("react").JSX.Element;
|
|
17
|
+
export declare const IconCheckOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
18
|
+
export declare const IconChevronDownOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
19
|
+
export declare const IconChevronRightOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
20
|
+
export declare const IconCopyOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
21
|
+
export declare const IconEditOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
23
22
|
//# sourceMappingURL=icons.d.ts.map
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* draws its own.
|
|
8
|
-
* @param props - size and className.
|
|
9
|
-
* @returns the icon element.
|
|
10
|
-
*/
|
|
11
|
-
export const IconUndoOutline16 = ({ size = 16, className }) => (_jsxs("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.4, strokeLinecap: "round", strokeLinejoin: "round", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M2.2 8.7 A5.8 5.8 0 1 0 5.9 3.3 L2.6 6.6" }), _jsx("path", { d: "M2.6 2.4 V6.6 H6.2", fill: "none" })] }));
|
|
2
|
+
export const IconCheckOutlineMedium = ({ size = 16, className }) => (_jsx("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: _jsx("path", { d: "M2.25 8.5L5.49732 11.7473C5.90519 12.1552 6.57263 12.1344 6.95426 11.7018L13.75 4", stroke: "currentColor" }) }));
|
|
3
|
+
export const IconChevronDownOutlineMedium = ({ size = 14, className }) => (_jsx("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: _jsx("path", { d: "M4 6L7.29289 9.29289C7.68342 9.68342 8.31658 9.68342 8.70711 9.29289L12 6", stroke: "currentColor" }) }));
|
|
4
|
+
export const IconChevronRightOutlineMedium = ({ size = 14, className }) => (_jsx("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: _jsx("path", { d: "M6 12L9.29289 8.70711C9.68342 8.31658 9.68342 7.68342 9.29289 7.29289L6 4", stroke: "currentColor" }) }));
|
|
5
|
+
export const IconCopyOutlineMedium = ({ size = 16, className }) => (_jsxs("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: [_jsx("rect", { x: "1.52075", y: "4.07373", width: "10.3932", height: "10.3932", rx: "2", stroke: "currentColor" }), _jsx("path", { d: "M11.9792 1.53296C13.36 1.53296 14.4792 2.65225 14.4792 4.03296V9.42847C14.4792 10.3756 13.9521 11.1987 13.1755 11.6228V10.3298C13.3652 10.0787 13.4792 9.7674 13.4792 9.42847V4.03296C13.4792 3.20453 12.8077 2.53296 11.9792 2.53296H6.58374C6.27966 2.53301 5.99684 2.6235 5.7605 2.77905H4.42358C4.85652 2.03463 5.66056 1.53304 6.58374 1.53296H11.9792Z", fill: "currentColor" })] }));
|
|
6
|
+
export const IconEditOutlineMedium = ({ size = 16, className }) => (_jsxs("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: [_jsx("path", { d: "M8.85596 2.69971H4.19971C3.37141 2.69971 2.69992 3.37146 2.69971 4.19971V11.8003C2.69992 12.6285 3.37141 13.3003 4.19971 13.3003H11.8003C12.6283 13.2999 13.3001 12.6283 13.3003 11.8003V7.89893H14.3003V11.8003C14.3001 13.1806 13.1806 14.2999 11.8003 14.3003H4.19971C2.81913 14.3003 1.69992 13.1808 1.69971 11.8003V4.19971C1.69992 2.81918 2.81913 1.69971 4.19971 1.69971H8.85596V2.69971Z", fill: "currentColor" }), _jsx("path", { d: "M7.7849 8.23878L13.888 2.13574", stroke: "currentColor" })] }));
|
|
@@ -179,7 +179,12 @@ export async function apply(ctx) {
|
|
|
179
179
|
},
|
|
180
180
|
selectModel: selection => directory === undefined
|
|
181
181
|
? Promise.resolve(false)
|
|
182
|
-
|
|
182
|
+
// Host 0.1.6-alpha.2 turned select() into a never-rejecting
|
|
183
|
+
// RemoteResult; 0.1.5 still rejects on failure. Read the settled
|
|
184
|
+
// shape: a value carrying `ok` is the new contract, anything else is
|
|
185
|
+
// the old void success, and a rejection only happens on the old line
|
|
186
|
+
// (the official consumer reads `!result.ok` the same way).
|
|
187
|
+
: directory.select(selection).then(result => (result && typeof result === 'object' && 'ok' in result ? result.ok : true), () => false),
|
|
183
188
|
hooks: {
|
|
184
189
|
modelDirectory: directory === undefined ? EMPTY_MODEL_DIRECTORY : directory.store,
|
|
185
190
|
},
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* renderers and the DOM hider.
|
|
10
10
|
*/
|
|
11
11
|
import type { ChatConversationViewNode } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
12
|
-
import type { ConversationNodeDefinition } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
12
|
+
import type { ConversationNodeDefinition, MessageImageSource } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
13
13
|
/** Chat node data of one withdrawal divider row. */
|
|
14
14
|
export interface WithdrawnDividerData {
|
|
15
15
|
/** Seq of the replacement event; the divider anchors here and the hidden span ends (exclusive). */
|
|
@@ -89,7 +89,7 @@ export declare const editedMessageDefinition: ConversationNodeDefinition<EditedM
|
|
|
89
89
|
export declare function foldHiddenRanges(nodes: readonly ChatConversationViewNode[]): number[];
|
|
90
90
|
/**
|
|
91
91
|
* The restore-row Definition: one single-event Context per message-tools
|
|
92
|
-
* user-message restore event (append-surface,
|
|
92
|
+
* user-message restore event (append-surface, producer-tagged), replaying the
|
|
93
93
|
* withdrawn message's content at the tail.
|
|
94
94
|
*/
|
|
95
95
|
export declare const restoredMessageDefinition: ConversationNodeDefinition<RestoredMessageData>;
|
|
@@ -105,7 +105,7 @@ export declare const restoredAssistantMessageDefinition: ConversationNodeDefinit
|
|
|
105
105
|
* Count the user-visible messages hidden by one withdrawn span. This mirrors
|
|
106
106
|
* {@link collectWithdrawnEntries}: context injections, tool calls, turn tails,
|
|
107
107
|
* and other UI chrome are not counted as “messages”, while restored/edited
|
|
108
|
-
* rows
|
|
108
|
+
* rows, reasoning-only assistant steps, and image-only user messages are.
|
|
109
109
|
* @param nodes - every materialized chat node.
|
|
110
110
|
* @param startSeq - first hidden seq (inclusive).
|
|
111
111
|
* @param endSeq - the divider's seq (exclusive).
|
|
@@ -116,16 +116,23 @@ export declare function countHiddenInSpan(nodes: readonly ChatConversationViewNo
|
|
|
116
116
|
export interface WithdrawnEntry {
|
|
117
117
|
/** Source node kind ('user' original or 'assistant-step' summary). */
|
|
118
118
|
readonly kind: 'user' | 'assistant';
|
|
119
|
-
/** The entry's text (full for user originals, a text join for assistant steps). */
|
|
119
|
+
/** The entry's text (full for user originals, a text join for assistant steps; empty for an image-only message). */
|
|
120
120
|
readonly text: string;
|
|
121
|
+
/**
|
|
122
|
+
* The entry's user-attached images, in source order — what the withdrawal
|
|
123
|
+
* hid alongside the text. Always empty for assistant entries: the replay is
|
|
124
|
+
* user originals plus assistant text, and assistant image blocks stay out
|
|
125
|
+
* of that vocabulary (see the module note on the replay).
|
|
126
|
+
*/
|
|
127
|
+
readonly images: readonly MessageImageSource[];
|
|
121
128
|
}
|
|
122
129
|
/**
|
|
123
|
-
* Collect the read-only replay of one withdrawn span: user originals
|
|
124
|
-
* assistant text, in anchor order. Other kinds
|
|
125
|
-
* and are skipped. Assistant-step data is
|
|
126
|
-
* `data.finalNode.blocks` once settled —
|
|
127
|
-
* not type-keyed like message content.
|
|
128
|
-
* event handlers.
|
|
130
|
+
* Collect the read-only replay of one withdrawn span: user originals (text
|
|
131
|
+
* and attached images alike) and assistant text, in anchor order. Other kinds
|
|
132
|
+
* carry no user-readable content and are skipped. Assistant-step data is
|
|
133
|
+
* kind-keyed (`data.blocks`, with `data.finalNode.blocks` once settled —
|
|
134
|
+
* `conversation-nodes/assistant.ts`), not type-keyed like message content.
|
|
135
|
+
* Reads the live node store — call from event handlers.
|
|
129
136
|
* @param nodes - every materialized chat node.
|
|
130
137
|
* @param startSeq - first hidden seq (inclusive).
|
|
131
138
|
* @param endSeq - the divider's seq (exclusive).
|