@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.
- package/CHANGELOG.md +8 -0
- package/README.en.md +15 -11
- package/README.i18n.yaml +2 -2
- package/README.md +35 -31
- package/lib/client.js +276 -131
- package/lib/client.js.map +1 -1
- package/lib/index.js +16 -15
- package/lib/tsconfig.client.tsbuildinfo +1 -1
- package/lib/tsconfig.host.tsbuildinfo +1 -1
- package/lib/typert.host.js +3 -3
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +3 -3
- package/lib/types/client/RestoredMessageView.d.ts +1 -1
- package/lib/types/client/RestoredMessageView.d.ts.map +1 -1
- package/lib/types/client/RestoredMessageView.js +20 -1
- package/lib/types/client/UserMessageView.d.ts +1 -1
- package/lib/types/client/UserMessageView.d.ts.map +1 -1
- package/lib/types/client/UserMessageView.js +9 -6
- package/lib/types/client/WithdrawnDividerView.d.ts +1 -1
- package/lib/types/client/WithdrawnDividerView.d.ts.map +1 -1
- package/lib/types/client/WithdrawnDividerView.js +11 -7
- package/lib/types/client/chat-hook.d.ts +36 -0
- package/lib/types/client/chat-hook.d.ts.map +1 -0
- package/lib/types/client/chat-hook.js +51 -0
- package/lib/types/client/dom-hider.d.ts +3 -2
- package/lib/types/client/dom-hider.d.ts.map +1 -1
- package/lib/types/client/dom-hider.js +20 -13
- package/lib/types/client/edit-in-place.d.ts +2 -1
- package/lib/types/client/edit-in-place.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +6 -4
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +42 -8
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +8 -0
- package/lib/types/client/slots.d.ts.map +1 -1
- package/lib/types/client/withdrawn-node.d.ts +3 -2
- package/lib/types/client/withdrawn-node.d.ts.map +1 -1
- package/lib/types/client/withdrawn-node.js +43 -52
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +10 -8
- package/lib/types/withdraw.d.ts.map +1 -1
- package/lib/types/withdraw.js +10 -6
- package/package.json +19 -18
package/lib/typert.host.js
CHANGED
|
@@ -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":
|
|
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":
|
|
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":
|
|
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":";;;;;;;;;
|
|
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":
|
|
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":
|
|
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":
|
|
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
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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;
|
|
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-
|
|
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(
|
|
23
|
-
return foldHiddenRanges(
|
|
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,
|
|
133
|
-
|
|
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,
|
|
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;
|
|
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,
|
|
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 =
|
|
30
|
-
const count =
|
|
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 =
|
|
36
|
-
const nodes =
|
|
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 =
|
|
40
|
-
const nodes =
|
|
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 {
|
|
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:
|
|
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,
|
|
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 = (
|
|
81
|
-
latest =
|
|
82
|
-
const nodes =
|
|
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 = (
|
|
145
|
-
latest =
|
|
145
|
+
const onSnapshot = (chat) => {
|
|
146
|
+
latest = chat;
|
|
146
147
|
if (disabled)
|
|
147
148
|
return;
|
|
148
149
|
if (probed) {
|
|
149
|
-
applyRules(
|
|
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(
|
|
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
|
-
|
|
180
|
-
|
|
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
|
-
|
|
183
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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,
|
|
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:
|
|
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,
|
|
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,
|
|
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', '
|
|
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
|
|
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
|
-
|
|
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
|
|
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;
|
|
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"}
|