@khorsheed/dsh-client-message-tools 0.1.0 → 0.2.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.en.md +15 -11
  3. package/README.i18n.yaml +2 -2
  4. package/README.md +35 -31
  5. package/lib/client.js +276 -131
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.js +16 -15
  8. package/lib/tsconfig.client.tsbuildinfo +1 -1
  9. package/lib/tsconfig.host.tsbuildinfo +1 -1
  10. package/lib/typert.host.js +3 -3
  11. package/lib/typert.remote-client.d.ts.map +1 -1
  12. package/lib/typert.remote-client.js +3 -3
  13. package/lib/types/client/RestoredMessageView.d.ts +1 -1
  14. package/lib/types/client/RestoredMessageView.d.ts.map +1 -1
  15. package/lib/types/client/RestoredMessageView.js +20 -1
  16. package/lib/types/client/UserMessageView.d.ts +1 -1
  17. package/lib/types/client/UserMessageView.d.ts.map +1 -1
  18. package/lib/types/client/UserMessageView.js +9 -6
  19. package/lib/types/client/WithdrawnDividerView.d.ts +1 -1
  20. package/lib/types/client/WithdrawnDividerView.d.ts.map +1 -1
  21. package/lib/types/client/WithdrawnDividerView.js +11 -7
  22. package/lib/types/client/chat-hook.d.ts +36 -0
  23. package/lib/types/client/chat-hook.d.ts.map +1 -0
  24. package/lib/types/client/chat-hook.js +51 -0
  25. package/lib/types/client/dom-hider.d.ts +3 -2
  26. package/lib/types/client/dom-hider.d.ts.map +1 -1
  27. package/lib/types/client/dom-hider.js +20 -13
  28. package/lib/types/client/edit-in-place.d.ts +2 -1
  29. package/lib/types/client/edit-in-place.d.ts.map +1 -1
  30. package/lib/types/client/index.d.ts +6 -4
  31. package/lib/types/client/index.d.ts.map +1 -1
  32. package/lib/types/client/index.js +42 -8
  33. package/lib/types/client/locales.d.ts +8 -0
  34. package/lib/types/client/locales.d.ts.map +1 -1
  35. package/lib/types/client/locales.js +8 -0
  36. package/lib/types/client/slots.d.ts.map +1 -1
  37. package/lib/types/client/withdrawn-node.d.ts +3 -2
  38. package/lib/types/client/withdrawn-node.d.ts.map +1 -1
  39. package/lib/types/client/withdrawn-node.js +43 -52
  40. package/lib/types/index.d.ts.map +1 -1
  41. package/lib/types/index.js +10 -8
  42. package/lib/types/withdraw.d.ts.map +1 -1
  43. package/lib/types/withdraw.js +10 -6
  44. package/package.json +19 -18
@@ -99,7 +99,7 @@ export const TYPERT = {
99
99
  typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsEditResult',
100
100
  schema: _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema,
101
101
  },
102
- sourceLocation: {"file":"packages/message-tools/src/index.ts","line":133,"column":9},
102
+ sourceLocation: {"file":"packages/message-tools/src/index.ts","line":135,"column":9},
103
103
  },
104
104
  {
105
105
  id: '@khorsheed/dsh-client-message-tools#messageTools/restore',
@@ -124,7 +124,7 @@ export const TYPERT = {
124
124
  typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsRestoreResult',
125
125
  schema: _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema,
126
126
  },
127
- sourceLocation: {"file":"packages/message-tools/src/index.ts","line":95,"column":9},
127
+ sourceLocation: {"file":"packages/message-tools/src/index.ts","line":97,"column":9},
128
128
  },
129
129
  {
130
130
  id: '@khorsheed/dsh-client-message-tools#messageTools/withdraw',
@@ -149,7 +149,7 @@ export const TYPERT = {
149
149
  typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsWithdrawResult',
150
150
  schema: _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema,
151
151
  },
152
- sourceLocation: {"file":"packages/message-tools/src/index.ts","line":61,"column":9},
152
+ sourceLocation: {"file":"packages/message-tools/src/index.ts","line":63,"column":9},
153
153
  },
154
154
  ],
155
155
  model: {
@@ -1 +1 @@
1
- {"version":3,"file":"typert.remote-client.d.ts","names":["edit","restore","withdraw"],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":";;;;;;;;;IAoIQA,I;IAtCAC,O;IAlCAC,Q;;;IAwEAF,mB;IAtCAC,sB;IAlCAC,uB","ignoreList":[]}
1
+ {"version":3,"file":"typert.remote-client.d.ts","names":["edit","restore","withdraw"],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":";;;;;;;;;IAsIQA,I;IAtCAC,O;IAlCAC,Q;;;IAwEAF,mB;IAtCAC,sB;IAlCAC,uB","ignoreList":[]}
@@ -96,7 +96,7 @@ export const TYPERT_REMOTE = {
96
96
  typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsEditResult',
97
97
  schema: _khorsheed_dsh_client_message_tools_messageTools_edit_result$schema,
98
98
  },
99
- sourceLocation: {"file":"packages/message-tools/src/index.ts","line":133,"column":9},
99
+ sourceLocation: {"file":"packages/message-tools/src/index.ts","line":135,"column":9},
100
100
  },
101
101
  {
102
102
  id: '@khorsheed/dsh-client-message-tools#messageTools/restore',
@@ -121,7 +121,7 @@ export const TYPERT_REMOTE = {
121
121
  typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsRestoreResult',
122
122
  schema: _khorsheed_dsh_client_message_tools_messageTools_restore_result$schema,
123
123
  },
124
- sourceLocation: {"file":"packages/message-tools/src/index.ts","line":95,"column":9},
124
+ sourceLocation: {"file":"packages/message-tools/src/index.ts","line":97,"column":9},
125
125
  },
126
126
  {
127
127
  id: '@khorsheed/dsh-client-message-tools#messageTools/withdraw',
@@ -146,7 +146,7 @@ export const TYPERT_REMOTE = {
146
146
  typeSymbol: '@khorsheed/dsh-client-message-tools/types#MessageToolsWithdrawResult',
147
147
  schema: _khorsheed_dsh_client_message_tools_messageTools_withdraw_result$schema,
148
148
  },
149
- sourceLocation: {"file":"packages/message-tools/src/index.ts","line":61,"column":9},
149
+ sourceLocation: {"file":"packages/message-tools/src/index.ts","line":63,"column":9},
150
150
  },
151
151
  ],
152
152
  }
@@ -8,7 +8,7 @@
8
8
  * marker. The model-facing frame text is not displayed (stripped in the
9
9
  * Definition). No action row: the group's user bubbles already carry copy.
10
10
  */
11
- import type { ReactNode } from 'react';
11
+ import { type ReactNode } from 'react';
12
12
  import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
13
13
  /** Full props of the restored-assistant renderer. */
14
14
  export type RestoredMessageViewProps = PropsRuntime<'conversation.chat.node', 'message-tools-restored-assistant'> & PropsLocale<'message-tools'>;
@@ -1 +1 @@
1
- {"version":3,"file":"RestoredMessageView.d.ts","sourceRoot":"","sources":["../../../src/client/RestoredMessageView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAKjF,qDAAqD;AACrD,MAAM,MAAM,wBAAwB,GAClC,YAAY,CAAC,wBAAwB,EAAE,kCAAkC,CAAC,GACxE,WAAW,CAAC,eAAe,CAAC,CAAA;AAEhC,4FAA4F;AAC5F,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,wBAAwB,GAAG,SAAS,CAWpF"}
1
+ {"version":3,"file":"RestoredMessageView.d.ts","sourceRoot":"","sources":["../../../src/client/RestoredMessageView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAI/C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAKjF,qDAAqD;AACrD,MAAM,MAAM,wBAAwB,GAClC,YAAY,CAAC,wBAAwB,EAAE,kCAAkC,CAAC,GACxE,WAAW,CAAC,eAAe,CAAC,CAAA;AAUhC,4FAA4F;AAC5F,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,wBAAwB,GAAG,SAAS,CAYpF"}
@@ -1,9 +1,28 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Restored-assistant line ('message-tools-restored-assistant' keyed
4
+ * renderer): one withdrawn assistant reply's text replayed at the
5
+ * conversation tail by a restore. The body renders through the official
6
+ * `MarkdownText` (the same public renderer behind ui-conversation's
7
+ * AssistantMarkdown), so typography, code blocks, and lists match a native
8
+ * assistant reply; the「已恢复 · 助手回复」caption stays as a small tertiary
9
+ * marker. The model-facing frame text is not displayed (stripped in the
10
+ * Definition). No action row: the group's user bubbles already carry copy.
11
+ */
12
+ import { useMemo } from 'react';
2
13
  import { MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives';
3
14
  import { IconUndoOutline16 } from './icons.js';
4
15
  import css from './RestoredMessageView.module.css';
16
+ /** Localized Markdown chrome (code-fence copy buttons, footnotes) over the message-tools namespace. */
17
+ function markdownLabels(t) {
18
+ return {
19
+ code: { copyLabel: t('markdown.copy'), copiedLabel: t('markdown.copied') },
20
+ footnotes: t('markdown.footnotes'),
21
+ };
22
+ }
5
23
  /** The restored assistant line: caption plus the reply body in official markdown chrome. */
6
24
  export function RestoredMessageView({ node, t }) {
7
25
  const data = node.data;
8
- 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 })] }));
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 })] }));
9
28
  }
@@ -12,5 +12,5 @@
12
12
  import { type ReactNode } from 'react';
13
13
  import type { UserMessageViewProps } from './slots.ts';
14
14
  /** The shadowed user-message view: bubble plus copy/edit/withdraw actions. */
15
- export declare const UserMessageView: import("react").MemoExoticComponent<({ node, renderMessageImages, t, useSession, editMessage, withdrawMessage, backfillDraft, useModelDirectory, modelsAvailable, loadModels, selectModel, }: UserMessageViewProps) => ReactNode>;
15
+ export declare const UserMessageView: import("react").MemoExoticComponent<({ node, renderMessageImages, t, editMessage, withdrawMessage, backfillDraft, useModelDirectory, modelsAvailable, loadModels, selectModel, ...standard }: UserMessageViewProps) => ReactNode>;
16
16
  //# sourceMappingURL=UserMessageView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserMessageView.d.ts","sourceRoot":"","sources":["../../../src/client/UserMessageView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAqC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAUzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAwJtD,8EAA8E;AAC9E,eAAO,MAAM,eAAe,gMAGzB,oBAAoB,KAAG,SAAS,CAoHjC,CAAA"}
1
+ {"version":3,"file":"UserMessageView.d.ts","sourceRoot":"","sources":["../../../src/client/UserMessageView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAqC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAYzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAwJtD,8EAA8E;AAC9E,eAAO,MAAM,eAAe,gMAIzB,oBAAoB,KAAG,SAAS,CAuHjC,CAAA"}
@@ -11,16 +11,17 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
11
11
  * marks the spot.
12
12
  */
13
13
  import { memo, useEffect, useRef, useState } from 'react';
14
- import { shallowEqual } from '@deepseek-ai/dsh-client-runtime/client';
14
+ import { shallowEqual } from '@deepseek-ai/dsh-client-store';
15
15
  import { Button, IconCheckOutline16, IconCopyOutline16, IconEditOutline16, JsonBlock, MessageText, RiskConfirmation, Tooltip, writeClipboard, } from '@deepseek-ai/dsh-client-ui-primitives';
16
16
  import { IconUndoOutline16 } from './icons.js';
17
17
  import { foldHiddenRanges, isSeqHidden } from './withdrawn-node.js';
18
+ import { chatHookOf } from './chat-hook.js';
18
19
  import { withdrawAndBackfill } from './withdraw-backfill.js';
19
20
  import { ModelChip } from './ModelChip.js';
20
21
  import css from './UserMessageView.module.css';
21
22
  /** Selector identity kept module-level so uSES memoization holds. */
22
- function selectHiddenRanges(snapshot) {
23
- return foldHiddenRanges(snapshot.chat.nodes.values());
23
+ function selectHiddenRanges(chat) {
24
+ return foldHiddenRanges(chat.nodes.values());
24
25
  }
25
26
  /** Split user content into joined text, images, and leftover blocks. */
26
27
  function contentParts(content) {
@@ -129,8 +130,10 @@ function InlineEditor({ initial, busy, t, onSave, onCancel, models }) {
129
130
  } }), _jsxs("div", { className: css.editorActions, children: [_jsx("div", { className: css.editorTrailing, children: models !== undefined && _jsx(ModelChip, { ...models }) }), _jsx(Button, { variant: "outline", disabled: busy, onClick: onCancel, children: t('cancel') }), _jsx(Button, { variant: "primary", disabled: busy || draft.trim() === '', onClick: save, children: t('editor.save') })] })] }));
130
131
  }
131
132
  /** The shadowed user-message view: bubble plus copy/edit/withdraw actions. */
132
- export const UserMessageView = memo(function UserMessageView({ node, renderMessageImages, t, useSession, editMessage, withdrawMessage, backfillDraft, useModelDirectory, modelsAvailable, loadModels, selectModel, }) {
133
- const ranges = useSession(selectHiddenRanges, shallowEqual);
133
+ export const UserMessageView = memo(function UserMessageView({ node, renderMessageImages, t, editMessage, withdrawMessage, backfillDraft, useModelDirectory, modelsAvailable, loadModels, selectModel, ...standard }) {
134
+ // Chat data lives in the `useChat` session standard prop on host 0.1.2
135
+ // (the helper degrades to the frozen empty snapshot without it).
136
+ const ranges = chatHookOf(standard)(selectHiddenRanges, shallowEqual);
134
137
  const [editing, setEditing] = useState(false);
135
138
  const [confirming, setConfirming] = useState(false);
136
139
  const [acknowledged, setAcknowledged] = useState(false);
@@ -168,5 +171,5 @@ export const UserMessageView = memo(function UserMessageView({ node, renderMessa
168
171
  ? { useModelDirectory, loadModels, selectModel, t }
169
172
  : undefined }), failed === 'edit' && _jsx("div", { className: css.actionError, role: "status", children: t('error.edit') })] }) }));
170
173
  }
171
- 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'), confirmLabel: t('withdraw.confirm'), acknowledged: acknowledged, disabled: busy, onAcknowledgedChange: setAcknowledged, onCancel: () => { setConfirming(false); setAcknowledged(false); }, onConfirm: confirmWithdraw }))] }));
174
+ 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 }))] }));
172
175
  });
@@ -13,5 +13,5 @@
13
13
  import { type ReactNode } from 'react';
14
14
  import type { WithdrawnDividerViewProps } from './slots.ts';
15
15
  /** The withdrawal divider: collapsed marker row plus the expandable replay. */
16
- export declare function WithdrawnDividerView({ node, t, useSession, restoreMessage, }: WithdrawnDividerViewProps): ReactNode;
16
+ export declare function WithdrawnDividerView({ node, t, restoreMessage, ...standard }: WithdrawnDividerViewProps): ReactNode;
17
17
  //# sourceMappingURL=WithdrawnDividerView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WithdrawnDividerView.d.ts","sourceRoot":"","sources":["../../../src/client/WithdrawnDividerView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAOhD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAG3D,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,GACpC,EAAE,yBAAyB,GAAG,SAAS,CA0FvC"}
1
+ {"version":3,"file":"WithdrawnDividerView.d.ts","sourceRoot":"","sources":["../../../src/client/WithdrawnDividerView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAQhD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAG3D,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EAAE,CAAC,EAAE,cAAc,EACvB,GAAG,QAAQ,EACZ,EAAE,yBAAyB,GAAG,SAAS,CA6FvC"}
@@ -15,29 +15,33 @@ import { useState } from 'react';
15
15
  import { Button, IconChevronDownOutline14, IconChevronRightOutline14, MessageText } from '@deepseek-ai/dsh-client-ui-primitives';
16
16
  import { IconUndoOutline16 } from './icons.js';
17
17
  import { collectWithdrawnEntries, countHiddenInSpan, foldHiddenRanges, hasRestoreForSpan, isRestoreSuperseded, } from './withdrawn-node.js';
18
+ import { chatHookOf } from './chat-hook.js';
18
19
  import css from './WithdrawnDividerView.module.css';
19
20
  /** The withdrawal divider: collapsed marker row plus the expandable replay. */
20
- export function WithdrawnDividerView({ node, t, useSession, restoreMessage, }) {
21
+ export function WithdrawnDividerView({ node, t, restoreMessage, ...standard }) {
21
22
  const data = node.data;
22
23
  const [expanded, setExpanded] = useState(false);
23
24
  const [entries, setEntries] = useState(null);
24
25
  const [busy, setBusy] = useState(false);
25
26
  const [failed, setFailed] = useState(false);
27
+ // Chat data lives in the `useChat` session standard prop on host 0.1.2
28
+ // (the helper degrades to the frozen empty snapshot without it).
29
+ const useChatSlice = chatHookOf(standard);
26
30
  // The node store is a stable live reader: the count/badge selectors read
27
31
  // through it, and the expand click folds the replay from the live snapshot
28
32
  // (event handlers may read live snapshots; render code subscribes).
29
- const nodesStore = useSession(snapshot => snapshot.chat.nodes);
30
- const count = useSession(snapshot => countHiddenInSpan(snapshot.chat.nodes.values(), data.hiddenStartSeq, data.seq));
33
+ const nodesStore = useChatSlice(chat => chat.nodes);
34
+ const count = useChatSlice(chat => countHiddenInSpan(chat.nodes.values(), data.hiddenStartSeq, data.seq));
31
35
  // The badge tracks a LIVE restore row: withdrawing the restored rows again
32
36
  // clears it. A superseded divider still shows its historical marker, but its
33
37
  // restore action is disabled because the later re-withdrawal divider is now
34
38
  // the active restore point for the same logical content.
35
- const restored = useSession((snapshot) => {
36
- const nodes = snapshot.chat.nodes.values();
39
+ const restored = useChatSlice((chat) => {
40
+ const nodes = chat.nodes.values();
37
41
  return hasRestoreForSpan(nodes, data.hiddenStartSeq, foldHiddenRanges(nodes));
38
42
  });
39
- const superseded = useSession((snapshot) => {
40
- const nodes = snapshot.chat.nodes.values();
43
+ const superseded = useChatSlice((chat) => {
44
+ const nodes = chat.nodes.values();
41
45
  return isRestoreSuperseded(nodes, data.hiddenStartSeq, foldHiddenRanges(nodes));
42
46
  });
43
47
  const toggle = () => {
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Chat snapshot access on host 0.1.2: the chat snapshot lives OUTSIDE the
3
+ * Session snapshot — components read it through the `useChat` session
4
+ * standard prop (ui-chat), the apply world through the conversation binding's
5
+ * chat target (`ctx.uiConversation.binding(sessionId).target('chat')`). The
6
+ * target snapshot is `undefined` until the first subscriber activates the
7
+ * chat view; callers treat that as "no chat yet", never as an error.
8
+ */
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
11
+ import type { ChatSnapshot, UseChat } from '@deepseek-ai/dsh-client-ui-chat/client';
12
+ import type { HostObservable, SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
13
+ /** The chat slice the plugin's selectors read. */
14
+ export type ChatSlice = ChatSnapshot;
15
+ /** Selector hook over the chat slice. */
16
+ export type ChatSliceHook = SnapshotSelectorHook<ChatSlice>;
17
+ /**
18
+ * Pick the live chat hook from the entry's composed props (`useChat` is a
19
+ * session standard prop on 0.1.2), degrading to the frozen empty snapshot
20
+ * when the framework composed none.
21
+ * @param props - the entry's composed props.
22
+ * @returns the chat selector hook.
23
+ */
24
+ export declare function chatHookOf(props: {
25
+ useChat?: UseChat;
26
+ }): ChatSliceHook;
27
+ /**
28
+ * Resolve the apply-world chat source for one session.
29
+ * @param ctx - client root context.
30
+ * @param sessionId - owning session.
31
+ * @returns the conversation binding's chat target (its snapshot is undefined
32
+ * until the first subscription activates the view), or undefined when the
33
+ * uiConversation service or the session binding is absent.
34
+ */
35
+ export declare function chatSourceOf(ctx: Context, sessionId: SessionId): HostObservable<ChatSlice | undefined> | undefined;
36
+ //# sourceMappingURL=chat-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-hook.d.ts","sourceRoot":"","sources":["../../../src/client/chat-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,wCAAwC,CAAA;AACnF,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAK5F,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,YAAY,CAAA;AAEpC,yCAAyC;AACzC,MAAM,MAAM,aAAa,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;AAsB3D;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,aAAa,CAItE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,cAAc,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,SAAS,CAQlH"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Frozen empty chat snapshot for the degraded render path: a composition
3
+ * without ui-chat never mounts the `conversation.chat.node` slot at all, so
4
+ * this only guards a stray render — hook call order stays stable and every
5
+ * selector reads the empty state.
6
+ */
7
+ const EMPTY_CHAT = {
8
+ order: [],
9
+ nodes: {
10
+ get: () => undefined,
11
+ source: () => ({ getSnapshot: () => undefined, subscribe: () => () => { } }),
12
+ processSource: () => ({ getSnapshot: () => undefined, subscribe: () => () => { } }),
13
+ values: () => [],
14
+ },
15
+ locations: { getTurn: () => [], getStep: () => [] },
16
+ navigation: { items: () => [] },
17
+ timeline: { turnOrder: [], turns: new Map() },
18
+ legacy: { nodes: [], turnTimings: new Map(), turnEnds: new Map(), partial: null, runningCalls: [] },
19
+ };
20
+ /**
21
+ * Pick the live chat hook from the entry's composed props (`useChat` is a
22
+ * session standard prop on 0.1.2), degrading to the frozen empty snapshot
23
+ * when the framework composed none.
24
+ * @param props - the entry's composed props.
25
+ * @returns the chat selector hook.
26
+ */
27
+ export function chatHookOf(props) {
28
+ const direct = props.useChat;
29
+ if (direct !== undefined)
30
+ return direct;
31
+ return selector => selector(EMPTY_CHAT);
32
+ }
33
+ /**
34
+ * Resolve the apply-world chat source for one session.
35
+ * @param ctx - client root context.
36
+ * @param sessionId - owning session.
37
+ * @returns the conversation binding's chat target (its snapshot is undefined
38
+ * until the first subscription activates the view), or undefined when the
39
+ * uiConversation service or the session binding is absent.
40
+ */
41
+ export function chatSourceOf(ctx, sessionId) {
42
+ const uiConversation = ctx.get('uiConversation');
43
+ if (uiConversation === undefined)
44
+ return undefined;
45
+ try {
46
+ return uiConversation.binding(sessionId).target('chat');
47
+ }
48
+ catch {
49
+ return undefined; // unknown session — the caller's undefined branch
50
+ }
51
+ }
@@ -18,7 +18,8 @@
18
18
  * session only: flow keys are session-scoped (seq-like ids collide across
19
19
  * sessions), and only one chat view is mounted at a time.
20
20
  */
21
- import type { ChatConversationViewNode, ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
21
+ import type { Context } from '@deepseek-ai/cordis';
22
+ import type { ChatConversationViewNode } from '@deepseek-ai/dsh-client-ui-chat/client';
22
23
  /**
23
24
  * Flow keys of every chat node inside a hidden span. The withdrawal divider
24
25
  * itself (`message-tools-withdrawn`, anchored at the span's exclusive end) is
@@ -57,5 +58,5 @@ export interface DomHiderOptions {
57
58
  * @param options - probe retry tuning.
58
59
  * @returns disposer removing the stylesheet and every subscription.
59
60
  */
60
- export declare function installDomHider(ctx: ClientContext, options?: DomHiderOptions): () => void;
61
+ export declare function installDomHider(ctx: Context, options?: DomHiderOptions): () => void;
61
62
  //# sourceMappingURL=dom-hider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dom-hider.d.ts","sourceRoot":"","sources":["../../../src/client/dom-hider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,wBAAwB,EAAE,aAAa,EAAmC,MAAM,wCAAwC,CAAA;AAItI;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,wBAAwB,EAAE,EAAE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CA2B/G;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAIhE;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,IAAI,CAsI7F"}
1
+ {"version":3,"file":"dom-hider.d.ts","sourceRoot":"","sources":["../../../src/client/dom-hider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AAKtF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,wBAAwB,EAAE,EAAE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CA2B/G;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAIhE;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,IAAI,CA2IvF"}
@@ -1,3 +1,4 @@
1
+ import { chatSourceOf } from './chat-hook.js';
1
2
  import { MESSAGE_TOOLS_PLUGIN, messageToolsOp } from '../marker.js';
2
3
  import { foldHiddenRanges, isSeqHidden } from './withdrawn-node.js';
3
4
  /**
@@ -77,9 +78,9 @@ export function installDomHider(ctx, options = {}) {
77
78
  let retryObserver;
78
79
  let retryTimer;
79
80
  let latest;
80
- const applyRules = (snapshot) => {
81
- latest = snapshot;
82
- const nodes = snapshot.chat.nodes.values();
81
+ const applyRules = (chat) => {
82
+ latest = chat;
83
+ const nodes = chat.nodes.values();
83
84
  const ranges = foldHiddenRanges(nodes);
84
85
  const nextSignature = `${ranges.join(',')}:${nodes.length}`;
85
86
  if (nextSignature === signature)
@@ -141,17 +142,17 @@ export function installDomHider(ctx, options = {}) {
141
142
  else
142
143
  failProbe(); }, probeRetryWindowMs);
143
144
  };
144
- const onSnapshot = (snapshot) => {
145
- latest = snapshot;
145
+ const onSnapshot = (chat) => {
146
+ latest = chat;
146
147
  if (disabled)
147
148
  return;
148
149
  if (probed) {
149
- applyRules(snapshot);
150
+ applyRules(chat);
150
151
  return;
151
152
  }
152
153
  // Probe on the first non-empty rule set, one frame out so React has
153
154
  // committed the rows the rules target.
154
- if (hiddenFlowKeys(snapshot.chat.nodes.values()).length === 0)
155
+ if (hiddenFlowKeys(chat.nodes.values()).length === 0)
155
156
  return;
156
157
  if (probeScheduled || retrying)
157
158
  return;
@@ -176,18 +177,24 @@ export function installDomHider(ctx, options = {}) {
176
177
  style.textContent = '';
177
178
  if (current === undefined)
178
179
  return;
179
- const binding = ctx.sessions.binding(current);
180
- if (binding === undefined)
180
+ // Chat data lives in the uiConversation binding's chat target (host
181
+ // 0.1.2's split — see chat-hook.ts); its snapshot is undefined until the
182
+ // first subscription activates the view.
183
+ const source = chatSourceOf(ctx, current);
184
+ if (source === undefined)
181
185
  return;
182
- onSnapshot(binding.session.getSnapshot());
183
- stopSession = binding.session.subscribe(() => { onSnapshot(binding.session.getSnapshot()); });
186
+ const publish = () => {
187
+ const chat = source.getSnapshot();
188
+ if (chat !== undefined)
189
+ onSnapshot(chat);
190
+ };
191
+ publish();
192
+ stopSession = source.subscribe(publish);
184
193
  };
185
194
  const stopList = ctx.sessions.list.subscribe(bindCurrent);
186
- const stopProvide = ctx.sessions.currentProvideInfo.subscribe(bindCurrent);
187
195
  bindCurrent();
188
196
  return () => {
189
197
  stopList();
190
- stopProvide();
191
198
  stopSession?.();
192
199
  stopRetry();
193
200
  style.remove();
@@ -51,7 +51,8 @@ export interface WithdrawInPlaceSteps {
51
51
  export declare function withdrawInPlace(steps: WithdrawInPlaceSteps, running: boolean): Promise<void>;
52
52
  /**
53
53
  * The minimal slice of the client conversation snapshot the settle probe
54
- * reads (structurally satisfied by `ConversationSnapshot`, so the unit tests
54
+ * reads (structurally satisfied by the ui-chat snapshot's timeline slice, so
55
+ * the unit tests stub only these fields).
55
56
  * stub only these fields).
56
57
  */
57
58
  export interface TurnSettleSnapshot {
@@ -1 +1 @@
1
- {"version":3,"file":"edit-in-place.d.ts","sourceRoot":"","sources":["../../../src/client/edit-in-place.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAM1F;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAMlG;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,SAAS,OAAO,EAAE,CAAA;IAChC,IAAI,EAAE;QACJ,QAAQ,EAAE;YACR,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;YAC5B,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE;gBAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;aAAE,CAAC,CAAA;SAC/E,CAAA;KACF,CAAA;CACF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAOjE;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,gCAAgC;IAChC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,kBAAkB,GAAG,SAAS,EAC9C,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAcf"}
1
+ {"version":3,"file":"edit-in-place.d.ts","sourceRoot":"","sources":["../../../src/client/edit-in-place.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAM1F;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAMlG;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,SAAS,OAAO,EAAE,CAAA;IAChC,IAAI,EAAE;QACJ,QAAQ,EAAE;YACR,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;YAC5B,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE;gBAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;aAAE,CAAC,CAAA;SAC/E,CAAA;KACF,CAAA;CACF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAOjE;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,gCAAgC;IAChC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,kBAAkB,GAAG,SAAS,EAC9C,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -13,19 +13,21 @@
13
13
  * adds.
14
14
  * @module @khorsheed/dsh-client-message-tools/client
15
15
  */
16
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
16
+ import type { Context } from '@deepseek-ai/cordis';
17
17
  import type { TypertRemoteNamespaceMap } from '@deepseek-ai/dsh-typert-protocol';
18
18
  /** The messageTools Remote namespace, as mounted by this plugin. */
19
19
  export type MessageToolsRemote = TypertRemoteNamespaceMap['messageTools'];
20
20
  /**
21
21
  * Required services: slots, sessions, the remote channel, the scope-addressed
22
- * conversation service, conversation node registration, and the copy.
22
+ * conversation service, and the copy.
23
23
  * `remote.messageTools` is deliberately NOT an inject: this plugin both
24
24
  * mounts the namespace (through `$mount` below) and consumes it, and the
25
25
  * Cordis property proxy only resolves services declared in `inject` or
26
26
  * provided by an ancestor fiber. Declaring it would deadlock the loader; the
27
27
  * namespace is read back from the global store with `ctx.get` after the
28
- * awaited mount.
28
+ * awaited mount. The event-Definition registry (`uiConversation.events`) is
29
+ * likewise NOT a static inject: the deferred ctx.inject arm keeps the
30
+ * registration independent of ui-conversation's own mount order.
29
31
  */
30
32
  export declare const inject: string[];
31
33
  /**
@@ -35,5 +37,5 @@ export declare const inject: string[];
35
37
  * @param ctx - client root context.
36
38
  * @returns disposer unwinding the mounted Remote namespace.
37
39
  */
38
- export declare function apply(ctx: ClientContext): Promise<() => Promise<void>>;
40
+ export declare function apply(ctx: Context): Promise<() => Promise<void>>;
39
41
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,wCAAwC,CAAA;AAatF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAA;AAahF,oEAAoE;AACpE,MAAM,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,cAAc,CAAC,CAAA;AAczE;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,UAAkF,CAAA;AAErG;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAoJ5E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAmBlD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAA;AAchF,oEAAoE;AACpE,MAAM,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,cAAc,CAAC,CAAA;AAczE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,UAA4D,CAAA;AAqB/E;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CA+JtE"}
@@ -1,6 +1,7 @@
1
1
  import messageToolsRemote from '@khorsheed/dsh-client-message-tools/remote';
2
2
  import { installDomHider } from './dom-hider.js';
3
3
  import { mergedDraft } from './backfill.js';
4
+ import { chatSourceOf } from './chat-hook.js';
4
5
  import { editInPlace, waitForTurnSettled, withdrawInPlace } from './edit-in-place.js';
5
6
  import { en, zh } from './locales.js';
6
7
  import { editedMessageDefinition, restoredAssistantMessageDefinition, restoredMessageDefinition, withdrawnDividerDefinition, } from './withdrawn-node.js';
@@ -19,15 +20,35 @@ const EMPTY_MODEL_DIRECTORY_STATE = Object.freeze({
19
20
  });
20
21
  /**
21
22
  * Required services: slots, sessions, the remote channel, the scope-addressed
22
- * conversation service, conversation node registration, and the copy.
23
+ * conversation service, and the copy.
23
24
  * `remote.messageTools` is deliberately NOT an inject: this plugin both
24
25
  * mounts the namespace (through `$mount` below) and consumes it, and the
25
26
  * Cordis property proxy only resolves services declared in `inject` or
26
27
  * provided by an ancestor fiber. Declaring it would deadlock the loader; the
27
28
  * namespace is read back from the global store with `ctx.get` after the
28
- * awaited mount.
29
+ * awaited mount. The event-Definition registry (`uiConversation.events`) is
30
+ * likewise NOT a static inject: the deferred ctx.inject arm keeps the
31
+ * registration independent of ui-conversation's own mount order.
29
32
  */
30
- export const inject = ['slots', 'sessions', 'remote', 'conversation', 'conversationEvents', 'locale'];
33
+ export const inject = ['slots', 'sessions', 'remote', 'conversation', 'locale'];
34
+ /**
35
+ * Register the four message-tools event Definitions against ui-conversation's
36
+ * event registry (host 0.1.2 folded the standalone registry service into
37
+ * `uiConversation.events`, upstream commit be531688f3). The registry is live
38
+ * (a registration re-assembles mounted views), so the deferred arm needs no
39
+ * ordering against ui-conversation's own apply; a composition without the
40
+ * service never fires it and simply has no chat projection at all.
41
+ * @param ctx - client root context.
42
+ */
43
+ function registerEventDefinitions(ctx) {
44
+ ctx.inject(['uiConversation'], (lctx) => {
45
+ const registry = lctx.uiConversation.events;
46
+ registry.register(withdrawnDividerDefinition);
47
+ registry.register(restoredMessageDefinition);
48
+ registry.register(restoredAssistantMessageDefinition);
49
+ registry.register(editedMessageDefinition);
50
+ });
51
+ }
31
52
  /**
32
53
  * Client plugin body: mount the Remote, register the dictionaries, the
33
54
  * withdrawal divider Definition, and the two `conversation.chat.node` entries
@@ -72,10 +93,7 @@ export async function apply(ctx) {
72
93
  };
73
94
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'message-tools: dictionaries');
74
95
  const t = ctx.locale.bind(NS);
75
- ctx.conversationEvents.register(withdrawnDividerDefinition);
76
- ctx.conversationEvents.register(restoredMessageDefinition);
77
- ctx.conversationEvents.register(restoredAssistantMessageDefinition);
78
- ctx.conversationEvents.register(editedMessageDefinition);
96
+ registerEventDefinitions(ctx);
79
97
  ctx.effect(() => installDomHider(ctx), 'message-tools: dom hider');
80
98
  ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({
81
99
  name: 'conversation.chat.node',
@@ -125,7 +143,23 @@ export async function apply(ctx) {
125
143
  // turn/end last); a replacement's span must cover all of it, so wait
126
144
  // for the turn to actually close. Bounded: a turn that never settles
127
145
  // rejects instead of letting the edit/withdraw race the stragglers.
128
- const waitIdle = () => waitForTurnSettled(() => ctx.sessions.binding(sessionId)?.session.getSnapshot());
146
+ // `running` rides the Session snapshot; the chat slice (timeline, and
147
+ // runningCalls under `legacy`) rides the conversation binding's chat
148
+ // target (host 0.1.2's split, see chat-hook.ts).
149
+ const settleSnapshot = () => {
150
+ const session = ctx.sessions.binding(sessionId)?.session.getSnapshot();
151
+ if (session === undefined)
152
+ return undefined;
153
+ const chat = chatSourceOf(ctx, sessionId)?.getSnapshot();
154
+ if (chat === undefined)
155
+ return undefined;
156
+ return {
157
+ running: session.running,
158
+ runningCalls: chat.legacy.runningCalls,
159
+ chat,
160
+ };
161
+ };
162
+ const waitIdle = () => waitForTurnSettled(settleSnapshot);
129
163
  return {
130
164
  editMessage: (targetSeq, text) => editInPlace({
131
165
  cancel: () => conversation.cancel(),
@@ -29,6 +29,10 @@ export declare const zh: {
29
29
  'model.empty': string;
30
30
  'model.selectFailed': string;
31
31
  'message.extraBlock': string;
32
+ close: string;
33
+ 'markdown.copy': string;
34
+ 'markdown.copied': string;
35
+ 'markdown.footnotes': string;
32
36
  'json.truncated': string;
33
37
  };
34
38
  /** The message-tools namespace key union. */
@@ -69,6 +73,10 @@ export declare const en: {
69
73
  'model.empty': string;
70
74
  'model.selectFailed': string;
71
75
  'message.extraBlock': string;
76
+ close: string;
77
+ 'markdown.copy': string;
78
+ 'markdown.copied': string;
79
+ 'markdown.footnotes': string;
72
80
  'json.truncated': string;
73
81
  };
74
82
  //# sourceMappingURL=locales.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BmB,CAAA;AAElC,6CAA6C;AAC7C,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,EAAE,CAAA;AAE7C,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,qDAAqD;QACrD,eAAe,EAAE,eAAe,CAAA;KACjC;CACF;AAED,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8B4B,CAAA"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCmB,CAAA;AAElC,6CAA6C;AAC7C,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,EAAE,CAAA;AAE7C,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,qDAAqD;QACrD,eAAe,EAAE,eAAe,CAAA;KACjC;CACF;AAED,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkC4B,CAAA"}
@@ -29,6 +29,10 @@ export const zh = {
29
29
  'model.empty': '暂无可用模型',
30
30
  'model.selectFailed': '模型切换失败',
31
31
  'message.extraBlock': '附加内容块',
32
+ 'close': '关闭',
33
+ 'markdown.copy': '复制',
34
+ 'markdown.copied': '已复制',
35
+ 'markdown.footnotes': '脚注',
32
36
  'json.truncated': '… 已截断,共 {total} 字符',
33
37
  };
34
38
  /** English dictionary, checked complete against the zh key set. */
@@ -61,5 +65,9 @@ export const en = {
61
65
  'model.empty': 'No models available',
62
66
  'model.selectFailed': 'Could not switch model',
63
67
  'message.extraBlock': 'Extra content block',
68
+ 'close': 'Close',
69
+ 'markdown.copy': 'Copy',
70
+ 'markdown.copied': 'Copied',
71
+ 'markdown.footnotes': 'Footnotes',
64
72
  'json.truncated': '… truncated, {total} characters total',
65
73
  };
@@ -1 +1 @@
1
- {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAO7G,+DAA+D;AAC/D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE;QACL,mIAAmI;QACnI,cAAc,EAAE,cAAc,CAAC,mBAAmB,CAAC,CAAA;KACpD,CAAA;IACD,yEAAyE;IACzE,eAAe,EAAE,OAAO,CAAA;IACxB,wFAAwF;IACxF,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,6EAA6E;IAC7E,WAAW,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5D;;;OAGG;IACH,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,yDAAyD;IACzD,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD;;;;;OAKG;IACH,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACtC;AAED,iIAAiI;AACjI,MAAM,MAAM,oBAAoB,GAC9B,YAAY,CAAC,wBAAwB,EAAE,MAAM,GAAG,UAAU,GAAG,sBAAsB,GAAG,wBAAwB,CAAC,GAC7G,UAAU,CAAC,oBAAoB,CAAC,GAChC,WAAW,CAAC,eAAe,CAAC,CAAA;AAEhC,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,yEAAyE;IACzE,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrD;AAED,qDAAqD;AACrD,MAAM,MAAM,yBAAyB,GACnC,YAAY,CAAC,wBAAwB,EAAE,yBAAyB,CAAC,GAC/D,UAAU,CAAC,wBAAwB,CAAC,GACpC,WAAW,CAAC,eAAe,CAAC,CAAA"}
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAQ7G,+DAA+D;AAC/D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE;QACL,mIAAmI;QACnI,cAAc,EAAE,cAAc,CAAC,mBAAmB,CAAC,CAAA;KACpD,CAAA;IACD,yEAAyE;IACzE,eAAe,EAAE,OAAO,CAAA;IACxB,wFAAwF;IACxF,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,6EAA6E;IAC7E,WAAW,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5D;;;OAGG;IACH,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,yDAAyD;IACzD,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD;;;;;OAKG;IACH,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACtC;AAED,iIAAiI;AACjI,MAAM,MAAM,oBAAoB,GAC9B,YAAY,CAAC,wBAAwB,EAAE,MAAM,GAAG,UAAU,GAAG,sBAAsB,GAAG,wBAAwB,CAAC,GAC7G,UAAU,CAAC,oBAAoB,CAAC,GAChC,WAAW,CAAC,eAAe,CAAC,CAAA;AAEhC,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,yEAAyE;IACzE,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrD;AAED,qDAAqD;AACrD,MAAM,MAAM,yBAAyB,GACnC,YAAY,CAAC,wBAAwB,EAAE,yBAAyB,CAAC,GAC/D,UAAU,CAAC,wBAAwB,CAAC,GACpC,WAAW,CAAC,eAAe,CAAC,CAAA"}