@khorsheed/dsh-client-message-tools 0.3.0 → 0.3.2

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.
@@ -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
- const _khorsheed_dsh_client_message_tools_messageTools_edit_parameter_0$schema = z.object({
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
- const _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema = z.union([z.object({
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
- const _khorsheed_dsh_client_message_tools_messageTools_restore_parameter_0$schema = z.object({
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
- const _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema = z.union([z.object({
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
- const _khorsheed_dsh_client_message_tools_messageTools_withdraw_parameter_0$schema = z.object({
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
- const _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema = z.union([z.object({
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
- schema: _khorsheed_dsh_client_message_tools_messageTools_edit_parameter_0$schema,
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
- schema: _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema,
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":135,"column":9},
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
- schema: _khorsheed_dsh_client_message_tools_messageTools_restore_parameter_0$schema,
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
- schema: _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema,
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":97,"column":9},
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
- schema: _khorsheed_dsh_client_message_tools_messageTools_withdraw_parameter_0$schema,
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
- schema: _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema,
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":63,"column":9},
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 { IconCheckOutline16, IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives';
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(IconChevronDownOutline14, {})] }), 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') }), [
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(IconCheckOutline16, {}) : 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) => {
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(IconCheckOutline16, {}) : null })] }, 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(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 { IconUndoOutline16 } from './icons.js';
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(IconUndoOutline16, {}), _jsx("span", { children: t('restored.assistant') })] }), _jsx(MarkdownText, { text: data.text, labels: labels })] }));
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, IconCheckOutline16, IconCopyOutline16, IconEditOutline16, JsonBlock, RiskConfirmation, Tooltip, writeClipboard, } from '@deepseek-ai/dsh-client-ui-primitives';
16
- import { IconUndoOutline16 } from './icons.js';
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(IconCheckOutline16, {}) : _jsx(IconCopyOutline16, {}) }) }));
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(IconEditOutline16, {}) }) }), _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(IconUndoOutline16, {}) }) })] }), 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
+ 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 + assistant text) with a「恢复到对话
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 + assistant text) with a「恢复到对话
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, IconChevronDownOutline14, IconChevronRightOutline14 } from '@deepseek-ai/dsh-client-ui-primitives';
16
- import { IconUndoOutline16 } from './icons.js';
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(IconChevronDownOutline14, {}) : _jsx(IconChevronRightOutline14, {}) }), _jsx("span", { className: css.title, children: t('withdrawn.divider', { count }) }), restored && (_jsxs("span", { className: css.badge, children: [_jsx(IconUndoOutline16, {}), 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') }), _jsx("div", { className: css.textRun, children: entry.text })] }, 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') })] })] }))] }));
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 { MESSAGE_TOOLS_PLUGIN, messageToolsOp } from '../marker.js';
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?.kind === 'plugin' && source.plugin === MESSAGE_TOOLS_PLUGIN
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().current;
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
- * 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.
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
- /** 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;
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
- * 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 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
- * 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 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" })] }));
@@ -15,8 +15,12 @@ const EMPTY_MODEL_DIRECTORY = {
15
15
  getSnapshot: () => EMPTY_MODEL_DIRECTORY_STATE,
16
16
  subscribe: () => () => { },
17
17
  };
18
+ // No `: ModelDirectoryState` annotation on purpose: the literal must satisfy both
19
+ // the 0.1.7-rc.1 shape (no `pending` yet — a fresh literal would trip the excess
20
+ // check) and rc.2's (which made `pending` required). A non-fresh binding is
21
+ // checked structurally, so both lines accept it.
18
22
  const EMPTY_MODEL_DIRECTORY_STATE = Object.freeze({
19
- current: null, routable: null, groups: [], failures: [], status: 'idle', error: null,
23
+ current: null, routable: null, groups: [], failures: [], status: 'idle', pending: null, error: null,
20
24
  });
21
25
  /**
22
26
  * Required services: slots, sessions, the remote channel, the scope-addressed
@@ -179,7 +183,12 @@ export async function apply(ctx) {
179
183
  },
180
184
  selectModel: selection => directory === undefined
181
185
  ? Promise.resolve(false)
182
- : directory.select(selection).then(() => true, () => false),
186
+ // Host 0.1.6-alpha.2 turned select() into a never-rejecting
187
+ // RemoteResult; 0.1.5 still rejects on failure. Read the settled
188
+ // shape: a value carrying `ok` is the new contract, anything else is
189
+ // the old void success, and a rejection only happens on the old line
190
+ // (the official consumer reads `!result.ok` the same way).
191
+ : directory.select(selection).then(result => (result && typeof result === 'object' && 'ok' in result ? result.ok : true), () => false),
183
192
  hooks: {
184
193
  modelDirectory: directory === undefined ? EMPTY_MODEL_DIRECTORY : directory.store,
185
194
  },
@@ -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, plugin-tagged), replaying the
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 and reasoning-only assistant steps are.
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 and
124
- * assistant text, in anchor order. Other kinds carry no user-readable text
125
- * and are skipped. Assistant-step data is kind-keyed (`data.blocks`, with
126
- * `data.finalNode.blocks` once settled — `conversation-nodes/assistant.ts`),
127
- * not type-keyed like message content. Reads the live node store — call from
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).