dsh-better-display 0.3.9 → 0.3.10
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/README.en.md +1 -1
- package/README.md +1 -1
- package/compat/harness-rc2.json +17 -13
- package/docs/official-rendering-bridge.md +1 -1
- package/lib/client.js +406 -403
- package/lib/client.js.map +1 -1
- package/lib/dsh-better-display.js +9 -18
- package/lib/types/client/OfficialContent.js +6 -7
- package/lib/types/client/OfficialContent.js.map +1 -1
- package/lib/types/client/Reader.d.ts.map +1 -1
- package/lib/types/client/Reader.js +2 -6
- package/lib/types/client/Reader.js.map +1 -1
- package/lib/types/client/official-slots.d.ts +15 -3
- package/lib/types/client/official-slots.d.ts.map +1 -1
- package/lib/types/client/official-slots.js +37 -4
- package/lib/types/client/official-slots.js.map +1 -1
- package/lib/types/client/primitive-labels.d.ts.map +1 -1
- package/lib/types/client/primitive-labels.js +1 -0
- package/lib/types/client/primitive-labels.js.map +1 -1
- package/package.json +20 -19
- package/scripts/check-harness-compat.mjs +4 -2
- package/scripts/link-harness-dependencies.mjs +48 -11
- package/src/client/OfficialContent.tsx +7 -7
- package/src/client/Reader.tsx +2 -6
- package/src/client/official-slots.tsx +38 -5
- package/src/client/primitive-labels.ts +1 -0
- package/tests/browser-official-bridge.tsx +2 -1
- package/tests/fixtures/reader-user-bubble.tsx +37 -15
- package/tests/reader-user-bubble.test.ts +1 -0
- package/tests/stock-install.test.ts +10 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primitive-labels.js","sourceRoot":"","sources":["../../../src/client/primitive-labels.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"primitive-labels.js","sourceRoot":"","sources":["../../../src/client/primitive-labels.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-better-display",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "A calmer reading view for DeepSeek Harness: native process details, live reasoning, and clean final answers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,30 +56,30 @@
|
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@deepseek-ai/cordis": "~4.0.4",
|
|
59
|
-
"@deepseek-ai/dsh-api-session-controller": ">=0.2.
|
|
60
|
-
"@deepseek-ai/dsh-attachment": ">=0.2.
|
|
61
|
-
"@deepseek-ai/dsh-client-store": ">=0.2.
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-chat": ">=0.2.
|
|
63
|
-
"@deepseek-ai/dsh-client-ui-conversation": ">=0.2.
|
|
64
|
-
"@deepseek-ai/dsh-client-ui-primitives": ">=0.2.
|
|
65
|
-
"@deepseek-ai/dsh-client-ui-renderer": ">=0.2.
|
|
66
|
-
"@deepseek-ai/dsh-client-ui-session": ">=0.2.
|
|
67
|
-
"@deepseek-ai/dsh-client-ui-slots": ">=0.2.
|
|
68
|
-
"@deepseek-ai/dsh-client-ui-tool": ">=0.2.
|
|
69
|
-
"@deepseek-ai/dsh-session": ">=0.2.
|
|
70
|
-
"@deepseek-ai/dsh-util-workspace-path": ">=0.2.
|
|
59
|
+
"@deepseek-ai/dsh-api-session-controller": ">=0.2.1-rc.1 <0.2.2",
|
|
60
|
+
"@deepseek-ai/dsh-attachment": ">=0.2.1-rc.1 <0.2.2",
|
|
61
|
+
"@deepseek-ai/dsh-client-store": ">=0.2.1-rc.1 <0.2.2",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-chat": ">=0.2.1-rc.1 <0.2.2",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-conversation": ">=0.2.1-rc.1 <0.2.2",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-primitives": ">=0.2.1-rc.1 <0.2.2",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.2.1-rc.1 <0.2.2",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-session": ">=0.2.1-rc.1 <0.2.2",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.2.1-rc.1 <0.2.2",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-tool": ">=0.2.1-rc.1 <0.2.2",
|
|
69
|
+
"@deepseek-ai/dsh-session": ">=0.2.1-rc.1 <0.2.2",
|
|
70
|
+
"@deepseek-ai/dsh-util-workspace-path": ">=0.2.1-rc.1 <0.2.2",
|
|
71
71
|
"react": "^18.2.0"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@deepseek-ai/dsh-api-remotes": "0.2.
|
|
75
|
-
"@deepseek-ai/dsh-api-session-controller": "0.2.
|
|
76
|
-
"@deepseek-ai/dsh-client-connection": "0.2.
|
|
77
|
-
"@deepseek-ai/dsh-session-turn-outline": "0.2.
|
|
74
|
+
"@deepseek-ai/dsh-api-remotes": "0.2.1-rc.5",
|
|
75
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.1-rc.5",
|
|
76
|
+
"@deepseek-ai/dsh-client-connection": "0.2.1-rc.5",
|
|
77
|
+
"@deepseek-ai/dsh-session-turn-outline": "0.2.1-rc.5",
|
|
78
78
|
"@types/mdast": "^4.0.4",
|
|
79
79
|
"@types/node": "^22.20.0",
|
|
80
80
|
"@types/react": "~18.3.1",
|
|
81
81
|
"lightningcss": "^1.33.0",
|
|
82
|
-
"tsdown": "^0.
|
|
82
|
+
"tsdown": "^0.23.2",
|
|
83
83
|
"tsx": "^4.22.4",
|
|
84
84
|
"typescript": "^6.0.3",
|
|
85
85
|
"semver": "^7.7.2",
|
|
@@ -131,7 +131,8 @@
|
|
|
131
131
|
"docs"
|
|
132
132
|
],
|
|
133
133
|
"engines": {
|
|
134
|
-
"node": "^22.19.0 || >=24.0.0"
|
|
134
|
+
"node": "^22.19.0 || >=24.0.0",
|
|
135
|
+
"dsh": ">=0.2.1-rc.1 <0.2.2"
|
|
135
136
|
},
|
|
136
137
|
"repository": {
|
|
137
138
|
"type": "git",
|
|
@@ -51,8 +51,10 @@ for (const path of execFileSync('rg', ['--files', 'packages/client', '-g', '*.ts
|
|
|
51
51
|
visit(file);
|
|
52
52
|
}
|
|
53
53
|
registrations.sort();
|
|
54
|
+
const harnessCommit = execFileSync('git', ['-C', harness, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim();
|
|
54
55
|
const current = {
|
|
55
56
|
harnessVersion: JSON.parse(readFileSync(join(harness, 'packages/client/ui-chat/package.json'), 'utf8')).version,
|
|
57
|
+
harnessCommit,
|
|
56
58
|
hashes, registrations,
|
|
57
59
|
};
|
|
58
60
|
if (process.argv.includes('--record')) {
|
|
@@ -63,8 +65,8 @@ if (process.argv.includes('--record')) {
|
|
|
63
65
|
const changed = files.filter(file => current.hashes[file] !== baseline.hashes[file]);
|
|
64
66
|
const added = registrations.filter(row => !baseline.registrations.includes(row));
|
|
65
67
|
const removed = baseline.registrations.filter(row => !registrations.includes(row));
|
|
66
|
-
if (current.harnessVersion !== baseline.harnessVersion || changed.length || added.length || removed.length) {
|
|
67
|
-
console.error(JSON.stringify({ compatible: 'review-required', target: current.harnessVersion, changedContractsOrForkSources: changed, addedRegistrations: added, removedRegistrations: removed }, null, 2));
|
|
68
|
+
if (current.harnessVersion !== baseline.harnessVersion || current.harnessCommit !== baseline.harnessCommit || changed.length || added.length || removed.length) {
|
|
69
|
+
console.error(JSON.stringify({ compatible: 'review-required', target: current.harnessVersion, commit: current.harnessCommit, baselineCommit: baseline.harnessCommit, changedContractsOrForkSources: changed, addedRegistrations: added, removedRegistrations: removed }, null, 2));
|
|
68
70
|
console.error('Review the differences and run the official bridge/visual tests. --record alone is not compatibility proof.');
|
|
69
71
|
process.exitCode = 1;
|
|
70
72
|
} else console.log(`Harness compatibility baseline passed (${current.harnessVersion}; ${registrations.length} official registrations).`);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
1
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, symlinkSync, unlinkSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
|
|
5
|
-
const root = resolve(process.argv[2] || '');
|
|
6
|
-
if (!
|
|
5
|
+
const root = resolve(process.argv[2] || process.env.DSH_REPO || '');
|
|
6
|
+
if (!root || !existsSync(join(root, 'tools/dshx/src/client-build.js'))) throw new Error('Pass a prepared Harness checkout, or set DSH_REPO.');
|
|
7
7
|
const local = resolve('node_modules');
|
|
8
8
|
mkdirSync(local, { recursive: true });
|
|
9
9
|
const shared = {
|
|
@@ -24,23 +24,60 @@ const shared = {
|
|
|
24
24
|
'@deepseek-ai/dsh-session': 'packages/core/session',
|
|
25
25
|
'@deepseek-ai/dsh-util-workspace-path': 'packages/util/workspace-path',
|
|
26
26
|
};
|
|
27
|
-
function link(name, target) {
|
|
27
|
+
function link(name, target, replace = false) {
|
|
28
28
|
if (!existsSync(target)) throw new Error(`Missing Harness dependency target: ${name} (${target})`);
|
|
29
29
|
const destination = join(local, name);
|
|
30
30
|
mkdirSync(dirname(destination), { recursive: true });
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return;
|
|
31
|
+
const stat = lstatSync(destination, { throwIfNoEntry: false });
|
|
32
|
+
if (stat) {
|
|
33
|
+
if (!replace && !stat.isSymbolicLink() && existsSync(destination)) return;
|
|
34
|
+
if (existsSync(destination) && realpathSync(destination) === realpathSync(target)) return;
|
|
35
|
+
rmSync(destination, { recursive: true, force: true });
|
|
34
36
|
}
|
|
35
37
|
symlinkSync(target, destination);
|
|
36
38
|
}
|
|
37
|
-
|
|
39
|
+
function packageDir(resolver, name) {
|
|
40
|
+
try { return dirname(resolver.resolve(`${name}/package.json`)); }
|
|
41
|
+
catch { /* pnpm may leave the package only in the store. */ }
|
|
42
|
+
const store = join(root, 'node_modules/.pnpm');
|
|
43
|
+
const prefix = name.startsWith('@') ? `${name.slice(1).replace('/', '+')}@` : `${name}@`;
|
|
44
|
+
const scoped = name.startsWith('@');
|
|
45
|
+
const match = existsSync(store) ? readdirSync(store).find(entry => entry.startsWith(prefix) || (scoped && entry.startsWith(name.replace('/', '+') + '@'))) : undefined;
|
|
46
|
+
if (!match) throw new Error(`Missing Harness dependency: ${name}`);
|
|
47
|
+
const nested = join(store, match, 'node_modules', name);
|
|
48
|
+
if (!existsSync(join(nested, 'package.json'))) throw new Error(`Missing Harness dependency: ${name} (${nested})`);
|
|
49
|
+
return nested;
|
|
50
|
+
}
|
|
51
|
+
function declaredTypesExist(pkgDir) {
|
|
52
|
+
const manifest = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf8'));
|
|
53
|
+
const types = typeof manifest.types === 'string'
|
|
54
|
+
? manifest.types
|
|
55
|
+
: manifest.exports?.['.']?.types;
|
|
56
|
+
return typeof types !== 'string' || existsSync(join(pkgDir, types));
|
|
57
|
+
}
|
|
58
|
+
function builtPackage(name) {
|
|
59
|
+
const runtime = process.env.DSH_RUNTIME || join(dirname(root), 'runtime-rc5');
|
|
60
|
+
const store = join(runtime, 'node_modules/.pnpm');
|
|
61
|
+
if (!existsSync(store)) return undefined;
|
|
62
|
+
const slug = `${name.replace('/', '+')}@`;
|
|
63
|
+
const match = readdirSync(store).find(entry => entry.startsWith(slug));
|
|
64
|
+
if (!match) return undefined;
|
|
65
|
+
const pkgDir = join(store, match, 'node_modules', name);
|
|
66
|
+
return existsSync(join(pkgDir, 'package.json')) && declaredTypesExist(pkgDir) ? pkgDir : undefined;
|
|
67
|
+
}
|
|
68
|
+
for (const [name, path] of Object.entries(shared)) {
|
|
69
|
+
const source = join(root, path);
|
|
70
|
+
const target = (name === '@deepseek-ai/cordis' || !declaredTypesExist(source)) ? (builtPackage(name) ?? source) : source;
|
|
71
|
+
link(name, target, true);
|
|
72
|
+
}
|
|
38
73
|
const require = createRequire(join(root, 'packages/client/ui-conversation/package.json'));
|
|
39
74
|
const rootRequire = createRequire(join(root, 'package.json'));
|
|
40
|
-
for (const name of ['react', '@types/react']) link(name,
|
|
41
|
-
for (const name of ['@types/node', 'typescript', 'tsdown', 'tsx']) link(name,
|
|
75
|
+
for (const name of ['react', '@types/react']) link(name, packageDir(require, name));
|
|
76
|
+
for (const name of ['@types/node', 'typescript', 'tsdown', 'tsx']) link(name, packageDir(rootRequire, name));
|
|
42
77
|
for (const name of ['@types/mdast', 'clsx', 'katex', 'mdast-util-from-markdown', 'mdast-util-gfm', 'mdast-util-math', 'micromark-core-commonmark', 'micromark-extension-gfm', 'micromark-extension-math', 'micromark-factory-space', 'micromark-util-character', 'micromark-util-classify-character', 'micromark-util-sanitize-uri', 'micromark-util-symbol', 'micromark-util-types']) {
|
|
43
|
-
|
|
78
|
+
const target = join(root, 'packages/client/ui-primitives/node_modules', name);
|
|
79
|
+
if (!existsSync(target)) continue;
|
|
80
|
+
link(name, target);
|
|
44
81
|
}
|
|
45
82
|
mkdirSync(join(local, '.bin'), { recursive: true });
|
|
46
83
|
for (const name of ['typescript', 'tsdown']) {
|
|
@@ -3,7 +3,7 @@ import type { AssistantActionOwnerProps, ChatConversationViewNode, ChatNode, Cha
|
|
|
3
3
|
import { createSnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
4
4
|
import type { AssistantBlock, ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
5
5
|
type MessageId = AssistantActionOwnerProps['messageId'];
|
|
6
|
-
import { OFFICIAL_SEATS } from './official-slots.js';
|
|
6
|
+
import { OFFICIAL_SEATS, readerGroupAction } from './official-slots.js';
|
|
7
7
|
import type { BlockRenderProps } from './types.js';
|
|
8
8
|
import { chatSeatProps } from './chat-seat.js';
|
|
9
9
|
import { Blocks } from './Blocks.js';
|
|
@@ -42,17 +42,17 @@ function chatNodeOwner(render: BlockRenderProps, official: NonNullable<BlockRend
|
|
|
42
42
|
return {
|
|
43
43
|
cwd: render.cwd,
|
|
44
44
|
openFile: render.openFile ?? (() => {}),
|
|
45
|
-
openSkill: () => {},
|
|
46
45
|
forkAt: render.forkAt ?? (() => {}),
|
|
47
46
|
inspectCall: callId => official.openView('trajectory', callId),
|
|
48
47
|
loadImage: official.officialImageLoader,
|
|
49
|
-
renderMessageImages: images => official.renderSlot(OFFICIAL_SEATS.images, {
|
|
50
|
-
...images, loadImage: official.officialImageLoader,
|
|
51
|
-
}),
|
|
52
48
|
fileMentions: official.officialFileMentions,
|
|
53
49
|
};
|
|
54
50
|
}
|
|
55
51
|
|
|
52
|
+
function nodeHookContext(turnData: unknown, disclosureReset: ReturnType<typeof createSnapshotStore<number>>) {
|
|
53
|
+
return { turnData, disclosureReset, useGroupAction: readerGroupAction };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
56
|
export function OfficialNode({ node, fallback, ...render }: BlockRenderProps & { node: ChatConversationViewNode; fallback: ReactNode }) {
|
|
57
57
|
const official = render.official;
|
|
58
58
|
const [disclosureReset] = useState(() => createSnapshotStore(0));
|
|
@@ -61,7 +61,7 @@ export function OfficialNode({ node, fallback, ...render }: BlockRenderProps & {
|
|
|
61
61
|
// The runtime node domain is open; the public SlotMap enumerates the known kinds.
|
|
62
62
|
const renderNode = official.renderSlot as unknown as (key: string, owner: object, options: object) => ReactNode;
|
|
63
63
|
return <div data-reader-official-node={node.kind} {...(node.kind === 'user' || node.kind === 'steering' ? chatSeatProps(node.kind, node.key) : {})}>
|
|
64
|
-
{renderNode(OFFICIAL_SEATS.nodes, { ...chatNodeOwner(render, official), node }, { entryKey: node.kind, hookContext:
|
|
64
|
+
{renderNode(OFFICIAL_SEATS.nodes, { ...chatNodeOwner(render, official), node }, { entryKey: node.kind, hookContext: nodeHookContext(turn?.data, disclosureReset), fallback })}
|
|
65
65
|
</div>;
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -95,7 +95,7 @@ export function OfficialAssistantBody({
|
|
|
95
95
|
// slice is what a replacement that ignores groupPart actually receives.
|
|
96
96
|
return renderNode(OFFICIAL_SEATS.nodes, { ...chatNodeOwner(render, official), node: view, groupPart: 'response' }, {
|
|
97
97
|
entryKey: 'assistant-step',
|
|
98
|
-
hookContext:
|
|
98
|
+
hookContext: nodeHookContext(turn?.data, disclosureReset),
|
|
99
99
|
fallback,
|
|
100
100
|
});
|
|
101
101
|
}
|
package/src/client/Reader.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import { BlockBoundary, Blocks, contentBlocks, UserMessageActions } from './Bloc
|
|
|
9
9
|
import { AssistantNode } from './AssistantNode.js';
|
|
10
10
|
import { ToolActivity, ToolMedia } from './ToolActivity.js';
|
|
11
11
|
import { OfficialNode, OfficialTail } from './OfficialContent.js';
|
|
12
|
-
import { OFFICIAL_SEATS } from './official-slots.js';
|
|
12
|
+
import { OFFICIAL_SEATS, readerGroupAction } from './official-slots.js';
|
|
13
13
|
import { preparingLabel, readerFlow } from './tool-activity.js';
|
|
14
14
|
import { Disclosure, ProcessFragment, RetiringTurns, StatusText, useMotionAllowed, usePinnedSelection, useReadingScroll } from './motion.js';
|
|
15
15
|
import { StreamMotionContext } from './streaming.js';
|
|
@@ -198,17 +198,13 @@ const MainNode = memo(function MainNode({ useChat, nodeKey, boundary, pinned, pr
|
|
|
198
198
|
const owner: ChatNodeOwnerProps & Pick<ReaderProps, 'sessionId'> = {
|
|
199
199
|
cwd: render.cwd,
|
|
200
200
|
openFile: render.openFile ?? (() => {}),
|
|
201
|
-
openSkill: () => {},
|
|
202
201
|
forkAt: render.forkAt ?? (() => {}),
|
|
203
202
|
inspectCall: callId => official.openView('trajectory', callId),
|
|
204
203
|
loadImage: official.officialImageLoader,
|
|
205
|
-
renderMessageImages: images => official.renderSlot(OFFICIAL_SEATS.images, {
|
|
206
|
-
...images, loadImage: official.officialImageLoader,
|
|
207
|
-
}),
|
|
208
204
|
fileMentions: official.officialFileMentions,
|
|
209
205
|
sessionId: render.sessionId,
|
|
210
206
|
};
|
|
211
|
-
const options = { hookContext: { turnData: turn?.data, disclosureReset }, fallback };
|
|
207
|
+
const options = { hookContext: { turnData: turn?.data, disclosureReset, useGroupAction: readerGroupAction }, fallback };
|
|
212
208
|
rendered = node.kind === 'user'
|
|
213
209
|
? official.renderSlot(OFFICIAL_SEATS.nodes, { ...owner, node }, { ...options, entryKey: 'user' })
|
|
214
210
|
: official.renderSlot(OFFICIAL_SEATS.nodes, { ...owner, node }, { ...options, entryKey: 'steering' });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { createElement, memo, useMemo, type ComponentType, type ReactNode } from 'react';
|
|
1
|
+
import { createElement, memo, useEffect, useLayoutEffect, useMemo, useRef, type ComponentType, type ReactNode, type RefObject } from 'react';
|
|
2
2
|
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { UseGroupAction } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
3
4
|
import type { SlotEntryDef, SlotMap, SlotSpec, StoredEntry } from '@deepseek-ai/dsh-client-ui-slots';
|
|
4
5
|
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
5
6
|
|
|
@@ -18,7 +19,8 @@ export const OFFICIAL_SLOTS = {
|
|
|
18
19
|
tools: 'tool.call.toolview',
|
|
19
20
|
tail: 'conversation.chat.turnTail',
|
|
20
21
|
nodes: 'conversation.chat.node',
|
|
21
|
-
|
|
22
|
+
messageImages: 'conversation.chat.message.images',
|
|
23
|
+
userImages: 'conversation.chat.user.images',
|
|
22
24
|
} as const;
|
|
23
25
|
export type OfficialFamily = keyof typeof OFFICIAL_SLOTS;
|
|
24
26
|
export const OFFICIAL_SEATS = {
|
|
@@ -26,7 +28,8 @@ export const OFFICIAL_SEATS = {
|
|
|
26
28
|
tools: 'dsh-better-display.official.tools/tool.call.toolview',
|
|
27
29
|
tail: 'dsh-better-display.official.tail/conversation.chat.turnTail',
|
|
28
30
|
nodes: 'dsh-better-display.official.nodes/conversation.chat.node',
|
|
29
|
-
|
|
31
|
+
messageImages: 'dsh-better-display.official.messageImages/conversation.chat.message.images',
|
|
32
|
+
userImages: 'dsh-better-display.official.userImages/conversation.chat.user.images',
|
|
30
33
|
} as const;
|
|
31
34
|
export type OfficialSeat = typeof OFFICIAL_SEATS[OfficialFamily];
|
|
32
35
|
export const officialSeat = (family: OfficialFamily, source: string = OFFICIAL_SLOTS[family]) =>
|
|
@@ -52,10 +55,39 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
52
55
|
'dsh-better-display.official.tools/tool.call.toolview': SlotMap['tool.call.toolview'];
|
|
53
56
|
'dsh-better-display.official.tail/conversation.chat.turnTail': SlotMap['conversation.chat.turnTail'];
|
|
54
57
|
'dsh-better-display.official.nodes/conversation.chat.node': SlotMap['conversation.chat.node'];
|
|
55
|
-
'dsh-better-display.official.
|
|
58
|
+
'dsh-better-display.official.messageImages/conversation.chat.message.images': SlotMap['conversation.chat.message.images'];
|
|
59
|
+
'dsh-better-display.official.userImages/conversation.chat.user.images': SlotMap['conversation.chat.user.images'];
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Searchable hide for official nodes drawn outside the Chat flow viewport.
|
|
65
|
+
* The flow's own `useGroupAction` is not in scope here; this keeps the same
|
|
66
|
+
* `hidden="until-found"` contract so browser find can still reveal the row.
|
|
67
|
+
*/
|
|
68
|
+
export function useReaderGroupAction(hidden: boolean, reveal: () => void): RefObject<HTMLDivElement> {
|
|
69
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
70
|
+
useLayoutEffect(() => {
|
|
71
|
+
const element = ref.current;
|
|
72
|
+
if (element === null) return;
|
|
73
|
+
if (hidden && element.contains(element.ownerDocument.activeElement)) {
|
|
74
|
+
reveal();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (hidden) element.setAttribute('hidden', 'until-found');
|
|
78
|
+
else element.removeAttribute('hidden');
|
|
79
|
+
}, [hidden, reveal]);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const element = ref.current;
|
|
82
|
+
if (element === null) return;
|
|
83
|
+
element.addEventListener('beforematch', reveal);
|
|
84
|
+
return () => { element.removeEventListener('beforematch', reveal); };
|
|
85
|
+
}, [reveal]);
|
|
86
|
+
return ref;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const readerGroupAction: UseGroupAction = useReaderGroupAction;
|
|
90
|
+
|
|
59
91
|
// Reader draws these rows itself. Everything else, including future kinds, is
|
|
60
92
|
// mirrored so the official renderer still runs inside the reading view.
|
|
61
93
|
// 'user' and 'steering' stay mirrored (message-recall buttons and the like).
|
|
@@ -83,7 +115,8 @@ const EXPECTED: Record<OfficialFamily, Spec> = {
|
|
|
83
115
|
tools: { kind: 'keyed', scope: 'session' },
|
|
84
116
|
tail: { kind: 'list', scope: 'session' },
|
|
85
117
|
nodes: { kind: 'keyed', scope: 'session' },
|
|
86
|
-
|
|
118
|
+
messageImages: { kind: 'single', scope: 'session' },
|
|
119
|
+
userImages: { kind: 'single', scope: 'session' },
|
|
87
120
|
};
|
|
88
121
|
|
|
89
122
|
/**
|
|
@@ -21,6 +21,7 @@ export const readBlockLabels: ReadBlockLabels = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
export const terminalBlockLabels: TerminalBlockLabels = {
|
|
24
|
+
commandLine: line => `命令第 ${line} 行`,
|
|
24
25
|
signal: signal => `信号 ${signal}`, exitCode: code => `退出码 ${code}`, noExitCode: '无退出码',
|
|
25
26
|
running: '执行中', failed: '失败', done: '已完成', copy: '复制', copied: '已复制',
|
|
26
27
|
noOutput: '没有输出', collapseAria: '收起命令输出', collapse: '收起',
|
|
@@ -78,7 +78,8 @@ slots.register({ name: 'root', children: {
|
|
|
78
78
|
'tool.call.toolview': { kind: 'keyed', scope: 'session' },
|
|
79
79
|
'conversation.chat.turnTail': { kind: 'list', scope: 'session' },
|
|
80
80
|
'conversation.chat.node': { kind: 'keyed', scope: 'session', inject: { hooks: { turnData: (_s: any, value: any) => () => value } } },
|
|
81
|
-
'conversation.message.images': { kind: 'single', scope: 'session' },
|
|
81
|
+
'conversation.chat.message.images': { kind: 'single', scope: 'session' },
|
|
82
|
+
'conversation.chat.user.images': { kind: 'single', scope: 'session' },
|
|
82
83
|
'conversation.input.overlay': { kind: 'list', scope: 'session' },
|
|
83
84
|
'fixture.reader': { kind: 'single', scope: 'session' },
|
|
84
85
|
} }, ({ SessionProvider, renderSlot }: any) => <SessionProvider>
|
|
@@ -4,6 +4,7 @@ import { flushSync } from 'react-dom';
|
|
|
4
4
|
import { SlotRegistry } from '@fixture/registry';
|
|
5
5
|
import { createSlotRenderer } from '@fixture/renderer';
|
|
6
6
|
import { UserMessageNodeView } from '@fixture/user-message';
|
|
7
|
+
import { UserContent } from '@fixture/user-content';
|
|
7
8
|
import { Reader } from '../../src/client/Reader.js';
|
|
8
9
|
import { createReaderStore } from '../../src/client/store.js';
|
|
9
10
|
import { OFFICIAL_SEATS, OFFICIAL_SLOTS, installOfficialSlots, officialChildren } from '../../src/client/official-slots.js';
|
|
@@ -81,16 +82,20 @@ function NativeColumn({ renderSlot, kind, text }: {
|
|
|
81
82
|
}) {
|
|
82
83
|
const node = messageNode(kind, text);
|
|
83
84
|
const owner = {
|
|
84
|
-
cwd: '/fixture', openFile: () => {},
|
|
85
|
+
cwd: '/fixture', openFile: () => {}, forkAt: () => {},
|
|
85
86
|
inspectCall: () => {}, loadImage: async () => null,
|
|
86
|
-
|
|
87
|
+
fileMentions: () => undefined, node,
|
|
87
88
|
};
|
|
88
89
|
return (
|
|
89
90
|
<div data-native={kind} style={{ width: '100%', maxWidth: 'var(--dsh-chat-content-width, 748px)', display: 'flex', flexDirection: 'column' }}>
|
|
90
91
|
<div style={{ minWidth: 0 }}>
|
|
91
92
|
{renderSlot(OFFICIAL_SLOTS.nodes, owner, {
|
|
92
93
|
entryKey: kind,
|
|
93
|
-
hookContext: {
|
|
94
|
+
hookContext: {
|
|
95
|
+
turnData: node.location.turn.data,
|
|
96
|
+
disclosureReset: { getSnapshot: () => 0, subscribe: () => () => {}, set: () => {} },
|
|
97
|
+
useGroupAction: () => ({ current: null }),
|
|
98
|
+
},
|
|
94
99
|
fallback: null,
|
|
95
100
|
})}
|
|
96
101
|
</div>
|
|
@@ -98,22 +103,33 @@ function NativeColumn({ renderSlot, kind, text }: {
|
|
|
98
103
|
);
|
|
99
104
|
}
|
|
100
105
|
|
|
106
|
+
function paddedTextBox(scope: ParentNode | null): Element | null {
|
|
107
|
+
if (!scope) return null;
|
|
108
|
+
const walker = document.createTreeWalker(scope, NodeFilter.SHOW_TEXT);
|
|
109
|
+
let node: Node | null;
|
|
110
|
+
while ((node = walker.nextNode())) {
|
|
111
|
+
if (!node.textContent?.trim()) continue;
|
|
112
|
+
const parent = node.parentElement;
|
|
113
|
+
if (!parent || parent.closest('button')) continue;
|
|
114
|
+
let element: HTMLElement | null = parent;
|
|
115
|
+
while (element && element !== scope) {
|
|
116
|
+
if (parseFloat(getComputedStyle(element).paddingTop) > 0) return element;
|
|
117
|
+
element = element.parentElement;
|
|
118
|
+
}
|
|
119
|
+
return parent;
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
101
124
|
function boxOf(root: ParentNode | null, kind: 'user' | 'steering' | 'pending') {
|
|
102
125
|
const scope = kind === 'pending'
|
|
103
126
|
? root?.querySelector('[data-reader-pending-submission]')
|
|
104
127
|
: root?.querySelector(`[data-reader-key="${kind}-key"]`);
|
|
105
|
-
|
|
106
|
-
if (kind === 'pending') {
|
|
107
|
-
return [...scope.children].find(element => element instanceof HTMLElement && element.querySelector('button') === null) ?? null;
|
|
108
|
-
}
|
|
109
|
-
const slot = scope.querySelector('[data-slot]');
|
|
110
|
-
return slot?.firstElementChild?.firstElementChild?.firstElementChild ?? null;
|
|
128
|
+
return paddedTextBox(scope ?? null);
|
|
111
129
|
}
|
|
112
130
|
|
|
113
131
|
function nativeBox(frame: ParentNode, kind: 'user' | 'steering') {
|
|
114
|
-
|
|
115
|
-
const slot = column?.querySelector('[data-slot]');
|
|
116
|
-
return slot?.firstElementChild?.firstElementChild?.firstElementChild ?? null;
|
|
132
|
+
return paddedTextBox(frame.querySelector(`[data-native="${kind}"]`));
|
|
117
133
|
}
|
|
118
134
|
|
|
119
135
|
function measure(element: Element | null) {
|
|
@@ -146,16 +162,15 @@ async function verify() {
|
|
|
146
162
|
{...props as never}
|
|
147
163
|
t={(props.t as ((key: string) => string) | undefined) ?? ((key: string) => key)}
|
|
148
164
|
openFile={(props.openFile as (() => void) | undefined) ?? (() => {})}
|
|
149
|
-
openSkill={(props.openSkill as (() => void) | undefined) ?? (() => {})}
|
|
150
|
-
renderMessageImages={(props.renderMessageImages as (() => null) | undefined) ?? (() => null)}
|
|
151
165
|
/>
|
|
152
166
|
);
|
|
153
167
|
slots.register({ name: 'root', children: {
|
|
154
168
|
'conversation.chat.node': { kind: 'keyed', scope: 'session', inject: { hooks: {
|
|
155
169
|
turnData: (_: unknown, context: { turnData?: unknown }) => () => context.turnData,
|
|
156
170
|
disclosure: (_: unknown, context: { disclosureReset?: { getSnapshot(): number } }) => () => context.disclosureReset,
|
|
171
|
+
groupAction: (_: unknown, context: { useGroupAction?: unknown }) => context.useGroupAction,
|
|
157
172
|
} } },
|
|
158
|
-
'conversation.message.images': { kind: 'single', scope: 'session' },
|
|
173
|
+
'conversation.chat.message.images': { kind: 'single', scope: 'session' },
|
|
159
174
|
'fixture.reader': { kind: 'single', scope: 'session' },
|
|
160
175
|
} }, ({ SessionProvider, renderSlot }: any) => (
|
|
161
176
|
<SessionProvider>
|
|
@@ -182,6 +197,13 @@ async function verify() {
|
|
|
182
197
|
))}
|
|
183
198
|
</>
|
|
184
199
|
));
|
|
200
|
+
slots.registerFactory({
|
|
201
|
+
name: 'conversation.chat.user.content', scope: 'session', locale: 'chat',
|
|
202
|
+
children: { 'conversation.chat.user.images': { kind: 'single', scope: 'session' } },
|
|
203
|
+
}, UserContent);
|
|
204
|
+
slots.registerFactory({
|
|
205
|
+
name: 'conversation.user.text', scope: 'session', locale: 'chat',
|
|
206
|
+
}, ({ text }: { text: string }) => <>{text}</>);
|
|
185
207
|
slots.register({ name: 'conversation.chat.node', key: 'user', locale: 'chat' }, HostUser);
|
|
186
208
|
slots.register({ name: 'conversation.chat.node', key: 'steering', locale: 'chat' }, HostUser);
|
|
187
209
|
const stopMirror = installOfficialSlots(ctx);
|
|
@@ -48,6 +48,7 @@ test('reader user and steering bubbles match the host column cap', { skip, timeo
|
|
|
48
48
|
'@fixture/registry': join(harness, 'packages/client/ui-renderer/src/client/registry.ts'),
|
|
49
49
|
'@fixture/renderer': join(harness, 'packages/client/ui-renderer/src/client/scoped-slots.tsx'),
|
|
50
50
|
'@fixture/user-message': join(harness, 'packages/client/ui-chat/src/client/chat/MessageItem.tsx'),
|
|
51
|
+
'@fixture/user-content': join(harness, 'packages/client/ui-chat/src/client/chat/UserContent.tsx'),
|
|
51
52
|
'@deepseek-ai/dsh-client-ui-primitives': join(harness, 'packages/client/ui-primitives/src/index.ts'),
|
|
52
53
|
'@deepseek-ai/dsh-client-ui-slots': join(harness, 'packages/client/ui-slots/src/index.ts'),
|
|
53
54
|
'@deepseek-ai/dsh-client-store': join(harness, 'packages/client/store/src/index.ts'),
|
|
@@ -16,19 +16,23 @@ const pkg = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8')) as {
|
|
|
16
16
|
peerDependencies: Record<string, string>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
const HARNESS_PEER = '>=0.2.
|
|
19
|
+
const HARNESS_PEER = '>=0.2.1-rc.1 <0.2.2';
|
|
20
20
|
|
|
21
|
-
test('Harness 0.2.
|
|
21
|
+
test('Harness 0.2.1 peer range accepts rc.5 and rejects the 0.2.0 series', () => {
|
|
22
22
|
const peers = Object.entries(pkg.peerDependencies).filter(([name]) => name.startsWith('@deepseek-ai/dsh-'));
|
|
23
23
|
assert.ok(peers.length >= 12);
|
|
24
24
|
for (const [name, range] of peers) {
|
|
25
25
|
assert.equal(range, HARNESS_PEER, name);
|
|
26
|
-
assert.equal(semver.satisfies('0.2.
|
|
27
|
-
assert.equal(semver.satisfies('0.2.
|
|
28
|
-
assert.equal(semver.satisfies('0.2.
|
|
29
|
-
assert.equal(semver.satisfies('0.2.
|
|
26
|
+
assert.equal(semver.satisfies('0.2.1-rc.5', range), true, name);
|
|
27
|
+
assert.equal(semver.satisfies('0.2.1-rc.1', range), true, name);
|
|
28
|
+
assert.equal(semver.satisfies('0.2.1', range), true, name);
|
|
29
|
+
assert.equal(semver.satisfies('0.2.1-alpha.1', range), false, name);
|
|
30
|
+
assert.equal(semver.satisfies('0.2.0-rc.2', range), false, name);
|
|
31
|
+
assert.equal(semver.satisfies('0.2.0', range), false, name);
|
|
30
32
|
assert.equal(semver.satisfies('0.1.7-rc.2', range), false, name);
|
|
31
33
|
}
|
|
34
|
+
const manifest = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8')) as { engines?: { dsh?: string } };
|
|
35
|
+
assert.equal(manifest.engines?.dsh, HARNESS_PEER);
|
|
32
36
|
});
|
|
33
37
|
|
|
34
38
|
test('declares dsh.bundle.patch so official add joins the profile layer stack', () => {
|