@khorsheed/dsh-client-message-tools 0.3.3 → 0.3.5
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 +15 -0
- package/README.en.md +29 -63
- package/README.i18n.yaml +3 -3
- package/README.md +27 -61
- package/lib/client.js +15305 -965
- package/lib/typert.host.js +82 -35
- package/lib/typert.remote-client.d.ts +7 -2
- package/lib/typert.remote-client.js +76 -34
- package/lib/types/client/UserMessageView.d.ts +9 -6
- package/lib/types/client/UserMessageView.js +50 -11
- package/lib/types/client/WithdrawnDividerView.d.ts +5 -2
- package/lib/types/client/WithdrawnDividerView.js +7 -7
- package/lib/types/client/chat-hook.js +3 -0
- package/lib/types/client/dom-hider.d.ts +28 -5
- package/lib/types/client/dom-hider.js +70 -3
- package/lib/types/client/fold.d.ts +23 -0
- package/lib/types/client/fold.js +44 -0
- package/lib/types/client/icons.d.ts +1 -1
- package/lib/types/client/index.js +1 -0
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/locales.js +4 -0
- package/lib/types/client/message-images.d.ts +30 -0
- package/lib/types/client/message-images.js +17 -0
- package/lib/types/client/slots.d.ts +20 -3
- package/package.json +15 -17
- package/lib/invariant.js +0 -27
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -25
|
@@ -4,20 +4,25 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
4
4
|
* the plugin's own 'message-tools-edited' and 'message-tools-restored' rows):
|
|
5
5
|
* a visual clone of the official bubble chrome — ui-conversation ships no
|
|
6
6
|
* public components, so the structure is replicated against ui-primitives
|
|
7
|
-
* platform modules
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* platform modules — plus the copy/edit/withdraw action row and the
|
|
8
|
+
* 「已编辑」/「已恢复」labels. Attached images render through the host's
|
|
9
|
+
* gallery surface (see message-images.ts): the pre-0.2.1 `renderMessageImages`
|
|
10
|
+
* owner prop when the host still passes it, otherwise the root-scoped
|
|
11
|
+
* `conversation.images.content` factory. User messages inside a withdrawn
|
|
12
|
+
* span render nothing; the withdrawal divider marks the spot. A long message
|
|
13
|
+
* folds behind a fade; a small chevron seated in the bubble's bottom-right
|
|
14
|
+
* corner expands / collapses it (see fold.ts).
|
|
12
15
|
*/
|
|
13
|
-
import { memo, useEffect, useRef, useState } from 'react';
|
|
16
|
+
import { Fragment, memo, useEffect, useRef, useState } from 'react';
|
|
14
17
|
import { shallowEqual } from '@deepseek-ai/dsh-client-store';
|
|
15
18
|
import { Button, JsonBlock, RiskConfirmation, Tooltip, writeClipboard } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
16
|
-
import { IconCheckOutlineMedium, IconCopyOutlineMedium, IconEditOutlineMedium } from './icons.js';
|
|
19
|
+
import { IconCheckOutlineMedium, IconChevronDownOutlineMedium, IconCopyOutlineMedium, IconEditOutlineMedium } from './icons.js';
|
|
17
20
|
import { IconUndoOutlineMedium } from './icons-local.js';
|
|
18
21
|
import { foldHiddenRanges, isSeqHidden } from './withdrawn-node.js';
|
|
19
22
|
import { chatHookOf } from './chat-hook.js';
|
|
23
|
+
import { renderImageGallery } from './message-images.js';
|
|
20
24
|
import { withdrawAndBackfill } from './withdraw-backfill.js';
|
|
25
|
+
import { foldable, foldChoices, selectNewestUserSeq } from './fold.js';
|
|
21
26
|
import { ModelChip } from './ModelChip.js';
|
|
22
27
|
import css from './UserMessageView.module.css';
|
|
23
28
|
/** Selector identity kept module-level so uSES memoization holds. */
|
|
@@ -41,8 +46,30 @@ function contentParts(content) {
|
|
|
41
46
|
}
|
|
42
47
|
return { text: texts.join(''), images, rest };
|
|
43
48
|
}
|
|
44
|
-
/**
|
|
49
|
+
/** The host's canonical session mention, `@[title](dsh-session:…)` (the @ menu's session chip, sent as text). */
|
|
50
|
+
const SESSION_MENTION = /@\[((?:\\.|[^\\\]\n])+)\]\(dsh-session:[^)\s]+\)/gu;
|
|
51
|
+
/**
|
|
52
|
+
* A session mention shows as a chip with its title (the host's own user-text
|
|
53
|
+
* rule does the same); the rest of the text keeps the token projection.
|
|
54
|
+
*/
|
|
45
55
|
function projectUserText(text) {
|
|
56
|
+
const parts = [];
|
|
57
|
+
let cursor = 0;
|
|
58
|
+
for (const match of text.matchAll(SESSION_MENTION)) {
|
|
59
|
+
if (match.index > cursor)
|
|
60
|
+
parts.push(_jsx(Fragment, { children: projectTokens(text.slice(cursor, match.index)) }, `t${cursor}`));
|
|
61
|
+
const label = match[1].replace(/\\(.)/gu, '$1');
|
|
62
|
+
parts.push(_jsxs("span", { className: css.refChip, "data-ref-chip": "session", title: label, children: ["@", label] }, `s${match.index}`));
|
|
63
|
+
cursor = match.index + match[0].length;
|
|
64
|
+
}
|
|
65
|
+
if (parts.length === 0)
|
|
66
|
+
return projectTokens(text);
|
|
67
|
+
if (cursor < text.length)
|
|
68
|
+
parts.push(_jsx(Fragment, { children: projectTokens(text.slice(cursor)) }, `t${cursor}`));
|
|
69
|
+
return _jsx(_Fragment, { children: parts });
|
|
70
|
+
}
|
|
71
|
+
/** Plaintext `/name` and `@name` word-boundary tokens decorate as chips; the logged text stays the truth. */
|
|
72
|
+
function projectTokens(text) {
|
|
46
73
|
const re = /(^|\s)([/@][\w-]+)(?=\s|$)/g;
|
|
47
74
|
const parts = [];
|
|
48
75
|
let cursor = 0;
|
|
@@ -134,10 +161,14 @@ function InlineEditor({ initial, busy, t, onSave, onCancel, models }) {
|
|
|
134
161
|
} }), _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') })] })] }));
|
|
135
162
|
}
|
|
136
163
|
/** The shadowed user-message view: bubble plus copy/edit/withdraw actions. */
|
|
137
|
-
export const UserMessageView = memo(function UserMessageView({ node, renderMessageImages, t, editMessage, withdrawMessage, backfillDraft, useModelDirectory, modelsAvailable, loadModels, selectModel, ...standard }) {
|
|
164
|
+
export const UserMessageView = memo(function UserMessageView({ node, renderMessageImages, loadImage, renderFactorySlot, t, editMessage, withdrawMessage, backfillDraft, foldScope, useModelDirectory, modelsAvailable, loadModels, selectModel, ...standard }) {
|
|
138
165
|
// Chat data lives in the `useChat` session standard prop on host 0.1.2
|
|
139
166
|
// (the helper degrades to the frozen empty snapshot without it).
|
|
140
|
-
const
|
|
167
|
+
const useChat = chatHookOf(standard);
|
|
168
|
+
const ranges = useChat(selectHiddenRanges, shallowEqual);
|
|
169
|
+
const newestUserSeq = useChat(selectNewestUserSeq);
|
|
170
|
+
// The human's own choice for this message (remembered), else the default.
|
|
171
|
+
const [chosen, setChosen] = useState(() => foldChoices.read(foldScope)[String(node.data.seq)]);
|
|
141
172
|
const [editing, setEditing] = useState(false);
|
|
142
173
|
const [confirming, setConfirming] = useState(false);
|
|
143
174
|
const [acknowledged, setAcknowledged] = useState(false);
|
|
@@ -149,6 +180,14 @@ export const UserMessageView = memo(function UserMessageView({ node, renderMessa
|
|
|
149
180
|
const { text, images, rest } = contentParts(data.content);
|
|
150
181
|
const truncated = (total) => t('json.truncated', { total });
|
|
151
182
|
const showBubble = text !== '' || rest.length > 0;
|
|
183
|
+
const collapsible = foldable(text);
|
|
184
|
+
// The newest user message reads unfolded until the next one arrives.
|
|
185
|
+
const expanded = chosen ?? data.seq >= newestUserSeq;
|
|
186
|
+
const clamped = collapsible && !expanded;
|
|
187
|
+
const toggle = (next) => {
|
|
188
|
+
setChosen(next);
|
|
189
|
+
foldChoices.write(foldScope, data.seq, next);
|
|
190
|
+
};
|
|
152
191
|
const saveEdit = (newText) => {
|
|
153
192
|
setBusy(true);
|
|
154
193
|
setFailed(null);
|
|
@@ -175,5 +214,5 @@ export const UserMessageView = memo(function UserMessageView({ node, renderMessa
|
|
|
175
214
|
? { useModelDirectory, loadModels, selectModel, t }
|
|
176
215
|
: undefined }), failed === 'edit' && _jsx("div", { className: css.actionError, role: "status", children: t('error.edit') })] }) }));
|
|
177
216
|
}
|
|
178
|
-
return (_jsxs("div", { className: css.userRow, "data-time-hover-root": true, children: [_jsxs("div", { className: css.userStack, children: [node.kind === 'message-tools-edited' && (_jsx("div", { className: css.editedLabel, children: t('edited.badge') })), node.kind === 'message-tools-restored' && (_jsx("div", { className: css.editedLabel, children: t('withdrawn.restored') })), images.length > 0 ? renderMessageImages
|
|
217
|
+
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 ? renderImageGallery(renderMessageImages, renderFactorySlot, loadImage, { images, align: 'end' }) : null, showBubble && (_jsxs("div", { className: clamped ? `${css.bubble} ${css.clamped}` : css.bubble, children: [projectUserText(text), rest.map((block, index) => (_jsx(JsonBlock, { label: t('message.extraBlock'), payload: block, truncatedLabel: truncated }, index))), clamped && _jsx("div", { className: css.fade, "aria-hidden": true, onClick: () => { toggle(true); } }), collapsible && (_jsx("div", { className: css.foldBar, children: _jsx(Tooltip, { label: expanded ? t('fold.collapse') : t('fold.expand'), side: "bottom", children: _jsx("button", { type: "button", className: css.foldToggle, "aria-label": expanded ? t('fold.collapse') : t('fold.expand'), "aria-expanded": expanded, onClick: () => { toggle(!expanded); }, children: _jsx(IconChevronDownOutlineMedium, {}) }) }) }))] }))] }), _jsxs("div", { className: css.actions, children: [_jsx(CopyButton, { text: text, t: t }), _jsx(Tooltip, { label: t('edit'), side: "bottom", children: _jsx("button", { type: "button", className: css.action, "aria-label": t('edit'), onClick: () => { setFailed(null); setEditing(true); }, children: _jsx(IconEditOutlineMedium, {}) }) }), _jsx(Tooltip, { label: t('action.withdraw'), side: "bottom", children: _jsx("button", { type: "button", className: css.action, "aria-label": t('action.withdraw'), onClick: () => { setFailed(null); setAcknowledged(false); setConfirming(true); }, children: _jsx(IconUndoOutlineMedium, {}) }) })] }), failed === 'withdraw' && _jsx("div", { className: css.actionError, role: "status", children: t('error.withdraw') }), confirming && (_jsx(RiskConfirmation, { open: true, title: t('withdraw.title'), description: t('withdraw.description'), acknowledgeLabel: t('withdraw.acknowledge'), cancelLabel: t('cancel'), closeLabel: t('close'), confirmLabel: t('withdraw.confirm'), acknowledged: acknowledged, disabled: busy, onAcknowledgedChange: setAcknowledged, onCancel: () => { setConfirming(false); setAcknowledged(false); }, onConfirm: confirmWithdraw }))] }));
|
|
179
218
|
});
|
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
* restoration is impossible — and the divider carries a「已恢复」badge while
|
|
10
10
|
* a live restore row cites the span (withdrawing the restored rows clears
|
|
11
11
|
* the badge and re-enables the action). The「重新编辑」action was removed
|
|
12
|
-
* once withdrawals backfill the draft automatically.
|
|
12
|
+
* once withdrawals backfill the draft automatically. Replayed images render
|
|
13
|
+
* through the host's gallery surface (see message-images.ts): the pre-0.2.1
|
|
14
|
+
* `renderMessageImages` owner prop when the host still passes it, otherwise
|
|
15
|
+
* the root-scoped `conversation.images.content` factory.
|
|
13
16
|
*/
|
|
14
17
|
import { type ReactNode } from 'react';
|
|
15
18
|
import type { WithdrawnDividerViewProps } from './slots.ts';
|
|
16
19
|
/** The withdrawal divider: collapsed marker row plus the expandable replay. */
|
|
17
|
-
export declare function WithdrawnDividerView({ node, t, restoreMessage, renderMessageImages, ...standard }: WithdrawnDividerViewProps): ReactNode;
|
|
20
|
+
export declare function WithdrawnDividerView({ node, t, restoreMessage, renderMessageImages, loadImage, renderFactorySlot, ...standard }: WithdrawnDividerViewProps): ReactNode;
|
|
18
21
|
//# sourceMappingURL=WithdrawnDividerView.d.ts.map
|
|
@@ -10,7 +10,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
* restoration is impossible — and the divider carries a「已恢复」badge while
|
|
11
11
|
* a live restore row cites the span (withdrawing the restored rows clears
|
|
12
12
|
* the badge and re-enables the action). The「重新编辑」action was removed
|
|
13
|
-
* once withdrawals backfill the draft automatically.
|
|
13
|
+
* once withdrawals backfill the draft automatically. Replayed images render
|
|
14
|
+
* through the host's gallery surface (see message-images.ts): the pre-0.2.1
|
|
15
|
+
* `renderMessageImages` owner prop when the host still passes it, otherwise
|
|
16
|
+
* the root-scoped `conversation.images.content` factory.
|
|
14
17
|
*/
|
|
15
18
|
import { useState } from 'react';
|
|
16
19
|
import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
@@ -18,14 +21,11 @@ import { IconChevronDownOutlineMedium, IconChevronRightOutlineMedium } from './i
|
|
|
18
21
|
import { IconUndoOutlineMedium } from './icons-local.js';
|
|
19
22
|
import { collectWithdrawnEntries, countHiddenInSpan, foldHiddenRanges, hasRestoreForSpan, isRestoreSuperseded, } from './withdrawn-node.js';
|
|
20
23
|
import { chatHookOf } from './chat-hook.js';
|
|
24
|
+
import { renderImageGallery } from './message-images.js';
|
|
21
25
|
import css from './WithdrawnDividerView.module.css';
|
|
22
26
|
/** The withdrawal divider: collapsed marker row plus the expandable replay. */
|
|
23
|
-
export function WithdrawnDividerView({ node, t, restoreMessage, renderMessageImages, ...standard }) {
|
|
27
|
+
export function WithdrawnDividerView({ node, t, restoreMessage, renderMessageImages, loadImage, renderFactorySlot, ...standard }) {
|
|
24
28
|
const data = node.data;
|
|
25
|
-
// The gallery closure is a required owner prop of every `conversation.chat.node`
|
|
26
|
-
// renderer; widening it to `| undefined` keeps the degrade path explicit — a
|
|
27
|
-
// composition without the attachment UI replays text only instead of throwing.
|
|
28
|
-
const renderImages = renderMessageImages;
|
|
29
29
|
const [expanded, setExpanded] = useState(false);
|
|
30
30
|
const [entries, setEntries] = useState(null);
|
|
31
31
|
const [busy, setBusy] = useState(false);
|
|
@@ -65,5 +65,5 @@ export function WithdrawnDividerView({ node, t, restoreMessage, renderMessageIma
|
|
|
65
65
|
.catch(() => { setFailed(true); })
|
|
66
66
|
.finally(() => { setBusy(false); });
|
|
67
67
|
};
|
|
68
|
-
return (_jsxs("div", { className: css.row, children: [_jsxs("button", { type: "button", className: css.marker, "aria-expanded": expanded, "aria-label": t('withdrawn.expand'), onClick: toggle, children: [_jsx("span", { className: css.leading, "aria-hidden": true, children: expanded ? _jsx(IconChevronDownOutlineMedium, {}) : _jsx(IconChevronRightOutlineMedium, {}) }), _jsx("span", { className: css.title, children: t('withdrawn.divider', { count }) }), restored && (_jsxs("span", { className: css.badge, children: [_jsx(IconUndoOutlineMedium, {}), t('withdrawn.restored')] }))] }), expanded && (_jsxs("div", { className: css.body, children: [entries === null || entries.length === 0 ? (_jsx("div", { className: css.empty, children: t('withdrawn.empty') })) : (entries.map((entry, index) => (_jsxs("div", { className: entry.kind === 'user' ? css.entryUser : css.entryAssistant, children: [_jsx("div", { className: css.entryKind, children: entry.kind === 'user' ? t('withdrawn.entryUser') : t('withdrawn.entryAssistant') }), entry.text !== '' && _jsx("div", { className: css.textRun, children: entry.text }),
|
|
68
|
+
return (_jsxs("div", { className: css.row, children: [_jsxs("button", { type: "button", className: css.marker, "aria-expanded": expanded, "aria-label": t('withdrawn.expand'), onClick: toggle, children: [_jsx("span", { className: css.leading, "aria-hidden": true, children: expanded ? _jsx(IconChevronDownOutlineMedium, {}) : _jsx(IconChevronRightOutlineMedium, {}) }), _jsx("span", { className: css.title, children: t('withdrawn.divider', { count }) }), restored && (_jsxs("span", { className: css.badge, children: [_jsx(IconUndoOutlineMedium, {}), t('withdrawn.restored')] }))] }), expanded && (_jsxs("div", { className: css.body, children: [entries === null || entries.length === 0 ? (_jsx("div", { className: css.empty, children: t('withdrawn.empty') })) : (entries.map((entry, index) => (_jsxs("div", { className: entry.kind === 'user' ? css.entryUser : css.entryAssistant, children: [_jsx("div", { className: css.entryKind, children: entry.kind === 'user' ? t('withdrawn.entryUser') : t('withdrawn.entryAssistant') }), entry.text !== '' && _jsx("div", { className: css.textRun, children: entry.text }), entry.images.length > 0 && (_jsx("div", { className: css.entryImages, children: renderImageGallery(renderMessageImages, renderFactorySlot, loadImage, { images: entry.images, align: 'end' }) }))] }, index)))), _jsxs("div", { className: css.restoreRow, children: [_jsx(Button, { variant: "outline", disabled: busy || restored || superseded, onClick: restore, children: t('withdrawn.restore') }), failed && _jsx("span", { className: css.restoreFailed, role: "status", children: t('withdrawn.restoreFailed') })] })] }))] }));
|
|
69
69
|
}
|
|
@@ -9,6 +9,9 @@ const EMPTY_CHAT = {
|
|
|
9
9
|
nodes: {
|
|
10
10
|
get: () => undefined,
|
|
11
11
|
source: () => ({ getSnapshot: () => undefined, subscribe: () => () => { } }),
|
|
12
|
+
// Host 0.2.1 added this required store face; the frozen empty snapshot
|
|
13
|
+
// reports every node as not belonging to the latest exchange.
|
|
14
|
+
bottomSource: () => ({ getSnapshot: () => false, subscribe: () => () => { } }),
|
|
12
15
|
turnDataSource: () => ({ getSnapshot: () => [], subscribe: () => () => { } }),
|
|
13
16
|
processSource: () => ({ getSnapshot: () => undefined, subscribe: () => () => { } }),
|
|
14
17
|
values: () => [],
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
* DOM hider: hide EVERY chat row whose node anchor falls inside a withdrawn
|
|
3
3
|
* span — all node kinds, not just user messages — through a dynamic
|
|
4
4
|
* stylesheet keyed on the row's `data-chat-flow-key` attribute
|
|
5
|
-
* (ChatNodeSeat.tsx).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* (ChatNodeSeat.tsx). Host 0.1.7-alpha.1's conversation groups wrap a turn's
|
|
6
|
+
* process rows in a Group seat whose CONTAINER carries the group key as its
|
|
7
|
+
* flow key (ChatGroupSeat.tsx): member rules hide the rows inside, but the
|
|
8
|
+
* container itself (the「已调用工具」fold header) would show through, so the
|
|
9
|
+
* rule set also covers fully-hidden groups' container keys plus the composite
|
|
10
|
+
* `["<nodeKey>","<groupPart>"]` row keys the plain node-key rules never
|
|
11
|
+
* reach. The projection itself offers no suppression seam (see README), so
|
|
12
|
+
* this works at the DOM layer. The attribute is an undocumented surface: the
|
|
13
|
+
* first time a non-empty rule set is computed, the hider probes for it (after
|
|
14
|
+
* a frame, so React has committed). A page restored onto a tab
|
|
9
15
|
* where the chat rows are not mounted yet (the trace tab, or the chat area
|
|
10
16
|
* still mounting) makes that first probe miss, so a miss does NOT disable
|
|
11
17
|
* the hider outright: it enters a bounded retry — a MutationObserver waits
|
|
@@ -20,6 +26,7 @@
|
|
|
20
26
|
*/
|
|
21
27
|
import type { Context } from '@deepseek-ai/cordis';
|
|
22
28
|
import type { ChatConversationViewNode } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
29
|
+
import type { ConversationGroupedView } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
23
30
|
/**
|
|
24
31
|
* Flow keys of every chat node inside a hidden span. The withdrawal divider
|
|
25
32
|
* itself (`message-tools-withdrawn`, anchored at the span's exclusive end) is
|
|
@@ -34,6 +41,21 @@ import type { ChatConversationViewNode } from '@deepseek-ai/dsh-client-ui-chat/c
|
|
|
34
41
|
* @returns the flow keys to hide, in node order.
|
|
35
42
|
*/
|
|
36
43
|
export declare function hiddenFlowKeys(nodes: readonly ChatConversationViewNode[], ranges?: readonly number[]): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Flow keys the grouped rendering (host 0.1.7-alpha.1's conversation groups)
|
|
46
|
+
* adds on top of {@link hiddenFlowKeys}. A Group seat's container row carries
|
|
47
|
+
* the GROUP key as its `data-chat-flow-key`, so member rules leave the fold
|
|
48
|
+
* header visible: hide the container when every member's node key is hidden
|
|
49
|
+
* (a group straddling a span boundary keeps its header for the visible
|
|
50
|
+
* members). A member's composite groupPart row (`["<nodeKey>","<part>"]`,
|
|
51
|
+
* ChatNodeSeat — e.g. the reasoning pane) is never reached by the plain
|
|
52
|
+
* node-key rule either, so hidden members contribute those keys on top,
|
|
53
|
+
* whether or not their group hides as a whole.
|
|
54
|
+
* @param view - the chat target's grouped reader (undefined on pre-group hosts).
|
|
55
|
+
* @param hiddenNodeKeys - node keys the plain rules already hide.
|
|
56
|
+
* @returns additional flow keys to hide, in root-entry order.
|
|
57
|
+
*/
|
|
58
|
+
export declare function hiddenGroupFlowKeys(view: ConversationGroupedView<unknown> | undefined, hiddenNodeKeys: ReadonlySet<string>): string[];
|
|
37
59
|
/**
|
|
38
60
|
* Render the display rules for one rule set.
|
|
39
61
|
* @param keys - flow keys to hide.
|
|
@@ -53,7 +75,8 @@ export interface DomHiderOptions {
|
|
|
53
75
|
}
|
|
54
76
|
/**
|
|
55
77
|
* Install the hider: one shared style element, rebound whenever the selected
|
|
56
|
-
* session changes, rewritten only when the span set
|
|
78
|
+
* session changes, rewritten only when the span set, the node count, or the
|
|
79
|
+
* hidden group keys move.
|
|
57
80
|
* @param ctx - client root context (reads the sessions service).
|
|
58
81
|
* @param options - probe retry tuning.
|
|
59
82
|
* @returns disposer removing the stylesheet and every subscription.
|
|
@@ -49,6 +49,64 @@ export function hiddenFlowKeys(nodes, ranges) {
|
|
|
49
49
|
}
|
|
50
50
|
return keys;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Flow keys the grouped rendering (host 0.1.7-alpha.1's conversation groups)
|
|
54
|
+
* adds on top of {@link hiddenFlowKeys}. A Group seat's container row carries
|
|
55
|
+
* the GROUP key as its `data-chat-flow-key`, so member rules leave the fold
|
|
56
|
+
* header visible: hide the container when every member's node key is hidden
|
|
57
|
+
* (a group straddling a span boundary keeps its header for the visible
|
|
58
|
+
* members). A member's composite groupPart row (`["<nodeKey>","<part>"]`,
|
|
59
|
+
* ChatNodeSeat — e.g. the reasoning pane) is never reached by the plain
|
|
60
|
+
* node-key rule either, so hidden members contribute those keys on top,
|
|
61
|
+
* whether or not their group hides as a whole.
|
|
62
|
+
* @param view - the chat target's grouped reader (undefined on pre-group hosts).
|
|
63
|
+
* @param hiddenNodeKeys - node keys the plain rules already hide.
|
|
64
|
+
* @returns additional flow keys to hide, in root-entry order.
|
|
65
|
+
*/
|
|
66
|
+
export function hiddenGroupFlowKeys(view, hiddenNodeKeys) {
|
|
67
|
+
if (view === undefined || hiddenNodeKeys.size === 0)
|
|
68
|
+
return [];
|
|
69
|
+
const keys = [];
|
|
70
|
+
for (const entry of view.entries) {
|
|
71
|
+
if (entry.kind !== 'group')
|
|
72
|
+
continue;
|
|
73
|
+
const members = view.groupSource(entry.key).getSnapshot()?.members ?? [];
|
|
74
|
+
if (members.length === 0)
|
|
75
|
+
continue;
|
|
76
|
+
let hidden = 0;
|
|
77
|
+
for (const member of members) {
|
|
78
|
+
if (!hiddenNodeKeys.has(member.key))
|
|
79
|
+
continue;
|
|
80
|
+
hidden += 1;
|
|
81
|
+
if (member.groupPart !== undefined && member.groupPart !== 'response') {
|
|
82
|
+
keys.push(JSON.stringify([member.key, member.groupPart]));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (hidden === members.length)
|
|
86
|
+
keys.push(entry.key);
|
|
87
|
+
}
|
|
88
|
+
return keys;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The chat target's grouped reader on hosts with conversation groups
|
|
92
|
+
* (0.1.7-alpha.1); earlier hosts carry no `views.grouped` and degrade to
|
|
93
|
+
* undefined — their ungrouped rows are covered by the plain node-key rules.
|
|
94
|
+
* @param ctx - client root context.
|
|
95
|
+
* @param sessionId - owning session.
|
|
96
|
+
* @returns the grouped reader, or undefined when the host or session lacks it.
|
|
97
|
+
*/
|
|
98
|
+
function groupedChatViewOf(ctx, sessionId) {
|
|
99
|
+
const uiConversation = ctx.get('uiConversation');
|
|
100
|
+
if (uiConversation === undefined)
|
|
101
|
+
return undefined;
|
|
102
|
+
try {
|
|
103
|
+
const views = uiConversation.binding(sessionId).snapshot.getSnapshot().views;
|
|
104
|
+
return typeof views.grouped === 'function' ? views.grouped('chat') : undefined;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return undefined; // unknown session — the caller's undefined branch
|
|
108
|
+
}
|
|
109
|
+
}
|
|
52
110
|
/**
|
|
53
111
|
* Render the display rules for one rule set.
|
|
54
112
|
* @param keys - flow keys to hide.
|
|
@@ -61,7 +119,8 @@ export function renderHiderRules(keys) {
|
|
|
61
119
|
}
|
|
62
120
|
/**
|
|
63
121
|
* Install the hider: one shared style element, rebound whenever the selected
|
|
64
|
-
* session changes, rewritten only when the span set
|
|
122
|
+
* session changes, rewritten only when the span set, the node count, or the
|
|
123
|
+
* hidden group keys move.
|
|
65
124
|
* @param ctx - client root context (reads the sessions service).
|
|
66
125
|
* @param options - probe retry tuning.
|
|
67
126
|
* @returns disposer removing the stylesheet and every subscription.
|
|
@@ -89,11 +148,19 @@ export function installDomHider(ctx, options = {}) {
|
|
|
89
148
|
latest = chat;
|
|
90
149
|
const nodes = chat.nodes.values();
|
|
91
150
|
const ranges = foldHiddenRanges(nodes);
|
|
92
|
-
const
|
|
151
|
+
const nodeKeys = hiddenFlowKeys(nodes, ranges);
|
|
152
|
+
// Group membership only moves with node changes, and the assembler runs
|
|
153
|
+
// group definitions inside the same publish this snapshot reports, so the
|
|
154
|
+
// grouped reader is current here; its keys join the signature so a
|
|
155
|
+
// grouping-only delta still rewrites the rules.
|
|
156
|
+
const groupKeys = activeSession === undefined
|
|
157
|
+
? []
|
|
158
|
+
: hiddenGroupFlowKeys(groupedChatViewOf(ctx, activeSession), new Set(nodeKeys));
|
|
159
|
+
const nextSignature = `${ranges.join(',')}:${nodes.length}:${groupKeys.join(',')}`;
|
|
93
160
|
if (nextSignature === signature)
|
|
94
161
|
return;
|
|
95
162
|
signature = nextSignature;
|
|
96
|
-
style.textContent = renderHiderRules(
|
|
163
|
+
style.textContent = renderHiderRules([...nodeKeys, ...groupKeys]);
|
|
97
164
|
};
|
|
98
165
|
const probeFound = () => document.querySelector('[data-chat-flow-key]') !== null;
|
|
99
166
|
const stopRetry = () => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Long user messages fold: a message past {@link FOLD_CHARS} characters or
|
|
3
|
+
* {@link FOLD_LINES} lines starts clamped, except the session's newest user
|
|
4
|
+
* message (what the human just sent stays readable while it is answered; it
|
|
5
|
+
* folds once the next one arrives). The human's own expand/collapse wins and
|
|
6
|
+
* is remembered per message in browser storage — a per-viewer convenience:
|
|
7
|
+
* unreadable storage only means the default applies.
|
|
8
|
+
*/
|
|
9
|
+
import type { ChatSlice } from './chat-hook.ts';
|
|
10
|
+
/** Characters past which a message folds (roughly ten rendered lines). */
|
|
11
|
+
export declare const FOLD_CHARS = 600;
|
|
12
|
+
/** Lines past which a message folds even when short in characters. */
|
|
13
|
+
export declare const FOLD_LINES = 12;
|
|
14
|
+
/** Whether a message's text is long enough to fold. */
|
|
15
|
+
export declare function foldable(text: string): boolean;
|
|
16
|
+
/** Selector: the newest user message's seq (-1 when none). Module-level so uSES memoization holds. */
|
|
17
|
+
export declare function selectNewestUserSeq(chat: ChatSlice): number;
|
|
18
|
+
/** The human's remembered expand/collapse choices, by message seq. */
|
|
19
|
+
export declare const foldChoices: {
|
|
20
|
+
read(sessionId: string): Record<string, boolean>;
|
|
21
|
+
write(sessionId: string, seq: number, expanded: boolean): void;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=fold.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Characters past which a message folds (roughly ten rendered lines). */
|
|
2
|
+
export const FOLD_CHARS = 600;
|
|
3
|
+
/** Lines past which a message folds even when short in characters. */
|
|
4
|
+
export const FOLD_LINES = 12;
|
|
5
|
+
/** The kinds that render as the human's own message bubble. */
|
|
6
|
+
const USER_KINDS = new Set(['user', 'steering', 'message-tools-edited', 'message-tools-restored']);
|
|
7
|
+
/** Whether a message's text is long enough to fold. */
|
|
8
|
+
export function foldable(text) {
|
|
9
|
+
return text.length > FOLD_CHARS || text.split('\n').length > FOLD_LINES;
|
|
10
|
+
}
|
|
11
|
+
/** Selector: the newest user message's seq (-1 when none). Module-level so uSES memoization holds. */
|
|
12
|
+
export function selectNewestUserSeq(chat) {
|
|
13
|
+
let newest = -1;
|
|
14
|
+
for (const node of chat.nodes.values()) {
|
|
15
|
+
const seq = node.data?.seq;
|
|
16
|
+
if (USER_KINDS.has(node.kind) && typeof seq === 'number' && seq > newest)
|
|
17
|
+
newest = seq;
|
|
18
|
+
}
|
|
19
|
+
return newest;
|
|
20
|
+
}
|
|
21
|
+
const KEY_PREFIX = 'dsh-message-tools:fold:';
|
|
22
|
+
/** Choices kept per session (newest messages win): bounds the stored map. */
|
|
23
|
+
const KEEP = 200;
|
|
24
|
+
/** The human's remembered expand/collapse choices, by message seq. */
|
|
25
|
+
export const foldChoices = {
|
|
26
|
+
read(sessionId) {
|
|
27
|
+
try {
|
|
28
|
+
const raw = globalThis.localStorage?.getItem(KEY_PREFIX + sessionId);
|
|
29
|
+
const parsed = raw == null ? undefined : JSON.parse(raw);
|
|
30
|
+
return typeof parsed === 'object' && parsed !== null ? parsed : {};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
write(sessionId, seq, expanded) {
|
|
37
|
+
try {
|
|
38
|
+
const choices = { ...foldChoices.read(sessionId), [String(seq)]: expanded };
|
|
39
|
+
const kept = Object.keys(choices).map(Number).sort((a, b) => b - a).slice(0, KEEP);
|
|
40
|
+
globalThis.localStorage?.setItem(KEY_PREFIX + sessionId, JSON.stringify(Object.fromEntries(kept.map(key => [key, choices[key]]))));
|
|
41
|
+
}
|
|
42
|
+
catch { /* a convenience: never fail the view */ }
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generated by scripts/sync-icon-artwork.mts from @deepseek-ai/dsh-client-ui-primitives
|
|
3
|
-
* (v0.1
|
|
3
|
+
* (v0.2.1-rc.1, harness 1600d8a1e1) — do not edit by hand. The 0.1.5 and rc.1 host lines
|
|
4
4
|
* share no icon export name, so the rc.1 artwork is inlined here: pure SVG,
|
|
5
5
|
* no host runtime identity, identical on every line. Re-run the generator
|
|
6
6
|
* after adding an icon import or bumping the harness checkout.
|
|
@@ -176,6 +176,7 @@ export async function apply(ctx) {
|
|
|
176
176
|
withdraw: () => withdraw(targetSeq),
|
|
177
177
|
}, ctx.sessions.binding(sessionId)?.session.getSnapshot().running === true),
|
|
178
178
|
backfillDraft: (text) => { backfill(sessionId, text); },
|
|
179
|
+
foldScope: sessionId,
|
|
179
180
|
modelsAvailable: directory !== undefined,
|
|
180
181
|
loadModels: () => {
|
|
181
182
|
// Load failures surface on the directory store itself.
|
|
@@ -21,6 +21,8 @@ export declare const zh: {
|
|
|
21
21
|
'withdrawn.restoreFailed': string;
|
|
22
22
|
'error.edit': string;
|
|
23
23
|
'edited.badge': string;
|
|
24
|
+
'fold.expand': string;
|
|
25
|
+
'fold.collapse': string;
|
|
24
26
|
'error.withdraw': string;
|
|
25
27
|
'model.aria': string;
|
|
26
28
|
'model.menuAria': string;
|
|
@@ -65,6 +67,8 @@ export declare const en: {
|
|
|
65
67
|
'withdrawn.restoreFailed': string;
|
|
66
68
|
'error.edit': string;
|
|
67
69
|
'edited.badge': string;
|
|
70
|
+
'fold.expand': string;
|
|
71
|
+
'fold.collapse': string;
|
|
68
72
|
'error.withdraw': string;
|
|
69
73
|
'model.aria': string;
|
|
70
74
|
'model.menuAria': string;
|
|
@@ -21,6 +21,8 @@ export const zh = {
|
|
|
21
21
|
'withdrawn.restoreFailed': '恢复失败,请重试',
|
|
22
22
|
'error.edit': '编辑失败,请重试',
|
|
23
23
|
'edited.badge': '已编辑',
|
|
24
|
+
'fold.expand': '展开全部',
|
|
25
|
+
'fold.collapse': '收起',
|
|
24
26
|
'error.withdraw': '撤回失败,请重试',
|
|
25
27
|
'model.aria': '切换模型,当前 {model}',
|
|
26
28
|
'model.menuAria': '模型与推理等级',
|
|
@@ -57,6 +59,8 @@ export const en = {
|
|
|
57
59
|
'withdrawn.restoreFailed': 'Could not restore; please retry',
|
|
58
60
|
'error.edit': 'Could not apply the edit; please retry',
|
|
59
61
|
'edited.badge': 'Edited',
|
|
62
|
+
'fold.expand': 'Show all',
|
|
63
|
+
'fold.collapse': 'Collapse',
|
|
60
64
|
'error.withdraw': 'Could not withdraw the message; please retry',
|
|
61
65
|
'model.aria': 'Switch model, current {model}',
|
|
62
66
|
'model.menuAria': 'Model and reasoning effort',
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-host message-image gallery dispatch shared by the two chat-node views.
|
|
3
|
+
* Hosts before 0.2.1-alpha.3 pass a `renderMessageImages` owner prop with the
|
|
4
|
+
* session-authorized loader bound inside; 0.2.1-alpha.3 removed that prop when
|
|
5
|
+
* the images slot split into `conversation.chat.user.images` /
|
|
6
|
+
* `conversation.chat.message.images`, with the official gallery now behind the
|
|
7
|
+
* root-scoped `conversation.images.content` factory (ui-attachment) reached
|
|
8
|
+
* through the framework `renderFactorySlot` seat. The factory outlet renders
|
|
9
|
+
* nothing while the factory is unregistered, which matches the official
|
|
10
|
+
* omit-on-absence behavior of the unfilled images slots.
|
|
11
|
+
*/
|
|
12
|
+
import type { ReactNode } from 'react';
|
|
13
|
+
import type { MessageImageLoader, MessageImagesOwnerProps } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
14
|
+
import type { RenderFactorySlot } from '@deepseek-ai/dsh-client-ui-slots';
|
|
15
|
+
/** The gallery input one view owns: the owner props minus the loader. */
|
|
16
|
+
export type MessageImagesInput = Omit<MessageImagesOwnerProps, 'loadImage'>;
|
|
17
|
+
/**
|
|
18
|
+
* Render one image group through the host's gallery surface. The legacy owner
|
|
19
|
+
* prop wins when present: on hosts that still pass it (before 0.2.1-alpha.3)
|
|
20
|
+
* the factory either has no seat (0.1.5) or is never registered, while the
|
|
21
|
+
* prop always carries a working gallery. On 0.2.1-alpha.3+ the prop is absent
|
|
22
|
+
* and the factory renders the official gallery.
|
|
23
|
+
* @param legacy - the pre-0.2.1 owner-prop closure, when the host passes it.
|
|
24
|
+
* @param renderFactorySlot - the framework factory seat.
|
|
25
|
+
* @param loadImage - the session-authorized loader from `ChatNodeOwnerProps`.
|
|
26
|
+
* @param input - image sources and layout hints.
|
|
27
|
+
* @returns the gallery occurrence, or null when the composition has none.
|
|
28
|
+
*/
|
|
29
|
+
export declare function renderImageGallery(legacy: ((owner: MessageImagesInput) => ReactNode) | undefined, renderFactorySlot: RenderFactorySlot, loadImage: MessageImageLoader, input: MessageImagesInput): ReactNode;
|
|
30
|
+
//# sourceMappingURL=message-images.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render one image group through the host's gallery surface. The legacy owner
|
|
3
|
+
* prop wins when present: on hosts that still pass it (before 0.2.1-alpha.3)
|
|
4
|
+
* the factory either has no seat (0.1.5) or is never registered, while the
|
|
5
|
+
* prop always carries a working gallery. On 0.2.1-alpha.3+ the prop is absent
|
|
6
|
+
* and the factory renders the official gallery.
|
|
7
|
+
* @param legacy - the pre-0.2.1 owner-prop closure, when the host passes it.
|
|
8
|
+
* @param renderFactorySlot - the framework factory seat.
|
|
9
|
+
* @param loadImage - the session-authorized loader from `ChatNodeOwnerProps`.
|
|
10
|
+
* @param input - image sources and layout hints.
|
|
11
|
+
* @returns the gallery occurrence, or null when the composition has none.
|
|
12
|
+
*/
|
|
13
|
+
export function renderImageGallery(legacy, renderFactorySlot, loadImage, input) {
|
|
14
|
+
if (legacy !== undefined)
|
|
15
|
+
return legacy(input);
|
|
16
|
+
return renderFactorySlot('conversation.images.content', { ...input, loadImage });
|
|
17
|
+
}
|
|
@@ -2,9 +2,24 @@
|
|
|
2
2
|
* Slot-facing types of the message-tools client half: the injected action
|
|
3
3
|
* face and the composed props of its two `conversation.chat.node` entries.
|
|
4
4
|
*/
|
|
5
|
+
import type { ReactNode } from 'react';
|
|
5
6
|
import type { ModelSelection } from '@deepseek-ai/dsh-api-remotes/client';
|
|
6
7
|
import type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client';
|
|
7
|
-
import type {
|
|
8
|
+
import type { MessageImagesOwnerProps } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
9
|
+
import type { HostObservable, InjectFace, PropsLocale, PropsRenderFactories, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
10
|
+
/**
|
|
11
|
+
* The pre-0.2.1 image-gallery owner prop. Hosts before 0.2.1-alpha.3 pass a
|
|
12
|
+
* `renderMessageImages` closure in `ChatNodeOwnerProps` (the session-authorized
|
|
13
|
+
* loader is bound inside it); 0.2.1-alpha.3 removed the prop when the images
|
|
14
|
+
* slot split into `conversation.chat.user.images` /
|
|
15
|
+
* `conversation.chat.message.images` plus the root-scoped
|
|
16
|
+
* `conversation.images.content` factory. The views prefer this prop when the
|
|
17
|
+
* host still passes it and otherwise render through the factory seat
|
|
18
|
+
* (`PropsRenderFactories`), so one build serves both host lines.
|
|
19
|
+
*/
|
|
20
|
+
export interface LegacyMessageImagesProp {
|
|
21
|
+
readonly renderMessageImages?: ((owner: Omit<MessageImagesOwnerProps, 'loadImage'>) => ReactNode) | undefined;
|
|
22
|
+
}
|
|
8
23
|
/** Injected action face of the shadowed user-message entry. */
|
|
9
24
|
export interface MessageToolsInjected {
|
|
10
25
|
hooks: {
|
|
@@ -31,14 +46,16 @@ export interface MessageToolsInjected {
|
|
|
31
46
|
* the same path.
|
|
32
47
|
*/
|
|
33
48
|
backfillDraft: (text: string) => void;
|
|
49
|
+
/** The session id the fold choices are remembered under. */
|
|
50
|
+
foldScope: string;
|
|
34
51
|
}
|
|
35
52
|
/** Full props of the shadowed 'user'/'steering'/'message-tools-edited'/'message-tools-restored' renderer (all keys share it). */
|
|
36
|
-
export type UserMessageViewProps = PropsRuntime<'conversation.chat.node', 'user' | 'steering' | 'message-tools-edited' | 'message-tools-restored'> & InjectFace<MessageToolsInjected> & PropsLocale<'message-tools'
|
|
53
|
+
export type UserMessageViewProps = PropsRuntime<'conversation.chat.node', 'user' | 'steering' | 'message-tools-edited' | 'message-tools-restored'> & PropsRenderFactories & InjectFace<MessageToolsInjected> & PropsLocale<'message-tools'> & LegacyMessageImagesProp;
|
|
37
54
|
/** Injected action face of the withdrawal divider entry. */
|
|
38
55
|
export interface WithdrawnDividerInjected {
|
|
39
56
|
/** Restore the span's withdrawn user message as a fresh tail message. */
|
|
40
57
|
restoreMessage: (targetSeq: number) => Promise<void>;
|
|
41
58
|
}
|
|
42
59
|
/** Full props of the withdrawal divider renderer. */
|
|
43
|
-
export type WithdrawnDividerViewProps = PropsRuntime<'conversation.chat.node', 'message-tools-withdrawn'> & InjectFace<WithdrawnDividerInjected> & PropsLocale<'message-tools'
|
|
60
|
+
export type WithdrawnDividerViewProps = PropsRuntime<'conversation.chat.node', 'message-tools-withdrawn'> & PropsRenderFactories & InjectFace<WithdrawnDividerInjected> & PropsLocale<'message-tools'> & LegacyMessageImagesProp;
|
|
44
61
|
//# sourceMappingURL=slots.d.ts.map
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"cordis",
|
|
8
8
|
"messages"
|
|
9
9
|
],
|
|
10
|
-
"version": "0.3.
|
|
10
|
+
"version": "0.3.5",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"main": "lib/index.js",
|
|
13
13
|
"types": "lib/types/index.d.ts",
|
|
@@ -16,10 +16,6 @@
|
|
|
16
16
|
"types": "./lib/types/index.d.ts",
|
|
17
17
|
"default": "./lib/index.js"
|
|
18
18
|
},
|
|
19
|
-
"./invariant": {
|
|
20
|
-
"types": "./lib/types/invariant.d.ts",
|
|
21
|
-
"default": "./lib/invariant.js"
|
|
22
|
-
},
|
|
23
19
|
"./client": {
|
|
24
20
|
"types": "./lib/types/client/index.d.ts",
|
|
25
21
|
"default": "./lib/client.js"
|
|
@@ -55,7 +51,7 @@
|
|
|
55
51
|
},
|
|
56
52
|
"compat": {
|
|
57
53
|
"minHost": "0.1.5-rc.1",
|
|
58
|
-
"verifiedHost": "0.1
|
|
54
|
+
"verifiedHost": "0.2.1-rc.1"
|
|
59
55
|
}
|
|
60
56
|
},
|
|
61
57
|
"license": "MIT",
|
|
@@ -64,16 +60,15 @@
|
|
|
64
60
|
},
|
|
65
61
|
"peerDependencies": {
|
|
66
62
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
67
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
68
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
69
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
70
|
-
"@deepseek-ai/dsh-client-ui-model-selection": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
71
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
72
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
73
|
-
"@deepseek-ai/dsh-
|
|
74
|
-
"@deepseek-ai/dsh-
|
|
75
|
-
"@deepseek-ai/dsh-
|
|
76
|
-
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-model-selection": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
77
72
|
"react": "^18.2.0"
|
|
78
73
|
},
|
|
79
74
|
"files": [
|
|
@@ -86,5 +81,8 @@
|
|
|
86
81
|
"LICENSE",
|
|
87
82
|
"locale/*.json",
|
|
88
83
|
"CHANGELOG.md"
|
|
89
|
-
]
|
|
84
|
+
],
|
|
85
|
+
"engines": {
|
|
86
|
+
"dsh": ">=0.1.5-rc.1 <0.3.0-0"
|
|
87
|
+
}
|
|
90
88
|
}
|