dsh-better-display 0.1.1
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 +51 -0
- package/DESIGN.md +72 -0
- package/LICENSE +24 -0
- package/README.en.md +41 -0
- package/README.md +41 -0
- package/THIRD_PARTY_NOTICES.md +65 -0
- package/cordis.patch.yml +5 -0
- package/cordis.yml +4 -0
- package/dshx.yml +5 -0
- package/lib/client.js +49923 -0
- package/lib/client.js.map +1 -0
- package/lib/dsh-better-display.js +256 -0
- package/lib/types/client/Blocks.d.ts +45 -0
- package/lib/types/client/Blocks.d.ts.map +1 -0
- package/lib/types/client/Blocks.js +136 -0
- package/lib/types/client/Blocks.js.map +1 -0
- package/lib/types/client/ChoreographedFlow.d.ts +22 -0
- package/lib/types/client/ChoreographedFlow.d.ts.map +1 -0
- package/lib/types/client/ChoreographedFlow.js +170 -0
- package/lib/types/client/ChoreographedFlow.js.map +1 -0
- package/lib/types/client/ClosedProcessSummary.d.ts +9 -0
- package/lib/types/client/ClosedProcessSummary.d.ts.map +1 -0
- package/lib/types/client/ClosedProcessSummary.js +15 -0
- package/lib/types/client/ClosedProcessSummary.js.map +1 -0
- package/lib/types/client/LiveFold.d.ts +10 -0
- package/lib/types/client/LiveFold.d.ts.map +1 -0
- package/lib/types/client/LiveFold.js +39 -0
- package/lib/types/client/LiveFold.js.map +1 -0
- package/lib/types/client/McpAppFrame.d.ts +32 -0
- package/lib/types/client/McpAppFrame.d.ts.map +1 -0
- package/lib/types/client/McpAppFrame.js +193 -0
- package/lib/types/client/McpAppFrame.js.map +1 -0
- package/lib/types/client/Reader.d.ts +3 -0
- package/lib/types/client/Reader.d.ts.map +1 -0
- package/lib/types/client/Reader.js +537 -0
- package/lib/types/client/Reader.js.map +1 -0
- package/lib/types/client/ReasoningCard.d.ts +11 -0
- package/lib/types/client/ReasoningCard.d.ts.map +1 -0
- package/lib/types/client/ReasoningCard.js +301 -0
- package/lib/types/client/ReasoningCard.js.map +1 -0
- package/lib/types/client/StickyLane.d.ts +8 -0
- package/lib/types/client/StickyLane.d.ts.map +1 -0
- package/lib/types/client/StickyLane.js +28 -0
- package/lib/types/client/StickyLane.js.map +1 -0
- package/lib/types/client/TimelineRail.d.ts +10 -0
- package/lib/types/client/TimelineRail.d.ts.map +1 -0
- package/lib/types/client/TimelineRail.js +168 -0
- package/lib/types/client/TimelineRail.js.map +1 -0
- package/lib/types/client/ToolActivity.d.ts +17 -0
- package/lib/types/client/ToolActivity.d.ts.map +1 -0
- package/lib/types/client/ToolActivity.js +192 -0
- package/lib/types/client/ToolActivity.js.map +1 -0
- package/lib/types/client/TurnMetrics.d.ts +11 -0
- package/lib/types/client/TurnMetrics.d.ts.map +1 -0
- package/lib/types/client/TurnMetrics.js +44 -0
- package/lib/types/client/TurnMetrics.js.map +1 -0
- package/lib/types/client/WaitingStatus.d.ts +6 -0
- package/lib/types/client/WaitingStatus.d.ts.map +1 -0
- package/lib/types/client/WaitingStatus.js +19 -0
- package/lib/types/client/WaitingStatus.js.map +1 -0
- package/lib/types/client/conversation-scroll.d.ts +19 -0
- package/lib/types/client/conversation-scroll.d.ts.map +1 -0
- package/lib/types/client/conversation-scroll.js +25 -0
- package/lib/types/client/conversation-scroll.js.map +1 -0
- package/lib/types/client/deliverables.d.ts +24 -0
- package/lib/types/client/deliverables.d.ts.map +1 -0
- package/lib/types/client/deliverables.js +104 -0
- package/lib/types/client/deliverables.js.map +1 -0
- package/lib/types/client/entry-policy.d.ts +10 -0
- package/lib/types/client/entry-policy.d.ts.map +1 -0
- package/lib/types/client/entry-policy.js +24 -0
- package/lib/types/client/entry-policy.js.map +1 -0
- package/lib/types/client/entry.d.ts +4 -0
- package/lib/types/client/entry.d.ts.map +1 -0
- package/lib/types/client/entry.js +34 -0
- package/lib/types/client/entry.js.map +1 -0
- package/lib/types/client/fold-choreography.d.ts +28 -0
- package/lib/types/client/fold-choreography.d.ts.map +1 -0
- package/lib/types/client/fold-choreography.js +31 -0
- package/lib/types/client/fold-choreography.js.map +1 -0
- package/lib/types/client/index.d.ts +7 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +153 -0
- package/lib/types/client/index.js.map +1 -0
- package/lib/types/client/live-turn.d.ts +58 -0
- package/lib/types/client/live-turn.d.ts.map +1 -0
- package/lib/types/client/live-turn.js +144 -0
- package/lib/types/client/live-turn.js.map +1 -0
- package/lib/types/client/markdown/MarkdownText.d.ts +39 -0
- package/lib/types/client/markdown/MarkdownText.d.ts.map +1 -0
- package/lib/types/client/markdown/MarkdownText.js +171 -0
- package/lib/types/client/markdown/MarkdownText.js.map +1 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.d.ts +9 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.d.ts.map +1 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.js +73 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.js.map +1 -0
- package/lib/types/client/markdown/incremental.d.ts +69 -0
- package/lib/types/client/markdown/incremental.d.ts.map +1 -0
- package/lib/types/client/markdown/incremental.js +108 -0
- package/lib/types/client/markdown/incremental.js.map +1 -0
- package/lib/types/client/markdown/katex.d.ts +27 -0
- package/lib/types/client/markdown/katex.d.ts.map +1 -0
- package/lib/types/client/markdown/katex.js +88 -0
- package/lib/types/client/markdown/katex.js.map +1 -0
- package/lib/types/client/markdown/mathCompatibility.d.ts +11 -0
- package/lib/types/client/markdown/mathCompatibility.d.ts.map +1 -0
- package/lib/types/client/markdown/mathCompatibility.js +293 -0
- package/lib/types/client/markdown/mathCompatibility.js.map +1 -0
- package/lib/types/client/markdown/parse.d.ts +25 -0
- package/lib/types/client/markdown/parse.d.ts.map +1 -0
- package/lib/types/client/markdown/parse.js +42 -0
- package/lib/types/client/markdown/parse.js.map +1 -0
- package/lib/types/client/markdown/render.d.ts +119 -0
- package/lib/types/client/markdown/render.d.ts.map +1 -0
- package/lib/types/client/markdown/render.js +435 -0
- package/lib/types/client/markdown/render.js.map +1 -0
- package/lib/types/client/mcp-app.d.ts +50 -0
- package/lib/types/client/mcp-app.d.ts.map +1 -0
- package/lib/types/client/mcp-app.js +328 -0
- package/lib/types/client/mcp-app.js.map +1 -0
- package/lib/types/client/message-chrome.d.ts +19 -0
- package/lib/types/client/message-chrome.d.ts.map +1 -0
- package/lib/types/client/message-chrome.js +42 -0
- package/lib/types/client/message-chrome.js.map +1 -0
- package/lib/types/client/motion.d.ts +38 -0
- package/lib/types/client/motion.d.ts.map +1 -0
- package/lib/types/client/motion.js +367 -0
- package/lib/types/client/motion.js.map +1 -0
- package/lib/types/client/native/ContextBody.d.ts +130 -0
- package/lib/types/client/native/ContextBody.d.ts.map +1 -0
- package/lib/types/client/native/ContextBody.js +398 -0
- package/lib/types/client/native/ContextBody.js.map +1 -0
- package/lib/types/client/native/ContextInjectionRow.d.ts +26 -0
- package/lib/types/client/native/ContextInjectionRow.d.ts.map +1 -0
- package/lib/types/client/native/ContextInjectionRow.js +27 -0
- package/lib/types/client/native/ContextInjectionRow.js.map +1 -0
- package/lib/types/client/native/ReferenceIcon.d.ts +16 -0
- package/lib/types/client/native/ReferenceIcon.d.ts.map +1 -0
- package/lib/types/client/native/ReferenceIcon.js +16 -0
- package/lib/types/client/native/ReferenceIcon.js.map +1 -0
- package/lib/types/client/native/tool-call-model.d.ts +58 -0
- package/lib/types/client/native/tool-call-model.d.ts.map +1 -0
- package/lib/types/client/native/tool-call-model.js +219 -0
- package/lib/types/client/native/tool-call-model.js.map +1 -0
- package/lib/types/client/open-file.d.ts +24 -0
- package/lib/types/client/primitive-labels.d.ts +10 -0
- package/lib/types/client/primitive-labels.d.ts.map +1 -0
- package/lib/types/client/primitive-labels.js +37 -0
- package/lib/types/client/primitive-labels.js.map +1 -0
- package/lib/types/client/projection.d.ts +41 -0
- package/lib/types/client/projection.d.ts.map +1 -0
- package/lib/types/client/projection.js +112 -0
- package/lib/types/client/projection.js.map +1 -0
- package/lib/types/client/reasoning-follow.d.ts +6 -0
- package/lib/types/client/reasoning-follow.d.ts.map +1 -0
- package/lib/types/client/reasoning-follow.js +12 -0
- package/lib/types/client/reasoning-follow.js.map +1 -0
- package/lib/types/client/settings-copy.d.ts +6 -0
- package/lib/types/client/settings-slots.d.ts +16 -0
- package/lib/types/client/store.d.ts +17 -0
- package/lib/types/client/store.d.ts.map +1 -0
- package/lib/types/client/store.js +13 -0
- package/lib/types/client/store.js.map +1 -0
- package/lib/types/client/stream-buffer.d.ts +29 -0
- package/lib/types/client/stream-buffer.d.ts.map +1 -0
- package/lib/types/client/stream-buffer.js +113 -0
- package/lib/types/client/stream-buffer.js.map +1 -0
- package/lib/types/client/streaming.d.ts +18 -0
- package/lib/types/client/streaming.d.ts.map +1 -0
- package/lib/types/client/streaming.js +77 -0
- package/lib/types/client/streaming.js.map +1 -0
- package/lib/types/client/timeline.d.ts +26 -0
- package/lib/types/client/timeline.d.ts.map +1 -0
- package/lib/types/client/timeline.js +48 -0
- package/lib/types/client/timeline.js.map +1 -0
- package/lib/types/client/tool-activity.d.ts +52 -0
- package/lib/types/client/tool-activity.d.ts.map +1 -0
- package/lib/types/client/tool-activity.js +178 -0
- package/lib/types/client/tool-activity.js.map +1 -0
- package/lib/types/client/types.d.ts +66 -0
- package/lib/types/client/types.d.ts.map +1 -0
- package/lib/types/client/types.js +2 -0
- package/lib/types/client/types.js.map +1 -0
- package/lib/types/client/waiting-clock.d.ts +17 -0
- package/lib/types/client/waiting-clock.d.ts.map +1 -0
- package/lib/types/client/waiting-clock.js +24 -0
- package/lib/types/client/waiting-clock.js.map +1 -0
- package/lib/types/client/word-motion.d.ts +16 -0
- package/lib/types/client/word-motion.d.ts.map +1 -0
- package/lib/types/client/word-motion.js +89 -0
- package/lib/types/client/word-motion.js.map +1 -0
- package/lib/types/client/word-timeline.d.ts +28 -0
- package/lib/types/client/word-timeline.d.ts.map +1 -0
- package/lib/types/client/word-timeline.js +100 -0
- package/lib/types/client/word-timeline.js.map +1 -0
- package/lib/types/dsh-better-display.d.ts +17 -0
- package/lib/types/dsh-better-display.d.ts.map +1 -0
- package/lib/types/dsh-better-display.js +53 -0
- package/lib/types/dsh-better-display.js.map +1 -0
- package/package.json +121 -0
- package/scripts/build-motion-fixture.mjs +26 -0
- package/scripts/link-harness-dependencies.mjs +51 -0
- package/skills/generative-mcpapps/SKILL.md +47 -0
- package/skills/generative-mcpapps/examples/interactive_quiz.html +153 -0
- package/skills/generative-mcpapps/references/html_boilerplate.md +122 -0
- package/skills/generative-mcpapps/references/sep1865_protocol.md +54 -0
- package/src/client/Blocks.tsx +205 -0
- package/src/client/ChoreographedFlow.tsx +182 -0
- package/src/client/ClosedProcessSummary.tsx +19 -0
- package/src/client/LiveFold.tsx +43 -0
- package/src/client/McpAppFrame.module.css +210 -0
- package/src/client/McpAppFrame.tsx +317 -0
- package/src/client/Reader.module.css +329 -0
- package/src/client/Reader.tsx +814 -0
- package/src/client/ReasoningCard.tsx +285 -0
- package/src/client/SettingsSection.module.css +147 -0
- package/src/client/SettingsSection.tsx +117 -0
- package/src/client/StickyLane.tsx +24 -0
- package/src/client/TimelineRail.module.css +170 -0
- package/src/client/TimelineRail.tsx +262 -0
- package/src/client/ToolActivity.tsx +214 -0
- package/src/client/TurnMetrics.module.css +139 -0
- package/src/client/TurnMetrics.tsx +160 -0
- package/src/client/WaitingStatus.tsx +22 -0
- package/src/client/conversation-scroll.ts +28 -0
- package/src/client/deliverables.ts +122 -0
- package/src/client/entry-policy.ts +20 -0
- package/src/client/entry.tsx +40 -0
- package/src/client/fold-choreography.ts +38 -0
- package/src/client/index.tsx +198 -0
- package/src/client/live-turn.ts +155 -0
- package/src/client/markdown/MarkdownText.module.css +304 -0
- package/src/client/markdown/MarkdownText.tsx +187 -0
- package/src/client/markdown/cjkFriendlyStrong.ts +84 -0
- package/src/client/markdown/incremental.ts +131 -0
- package/src/client/markdown/katex.tsx +91 -0
- package/src/client/markdown/mathCompatibility.ts +350 -0
- package/src/client/markdown/parse.ts +45 -0
- package/src/client/markdown/render.tsx +630 -0
- package/src/client/markdown/upstream.json +14 -0
- package/src/client/mcp-app.ts +323 -0
- package/src/client/message-chrome.ts +47 -0
- package/src/client/motion.tsx +338 -0
- package/src/client/native/ContextBody.module.css +161 -0
- package/src/client/native/ContextBody.tsx +591 -0
- package/src/client/native/ContextInjectionRow.module.css +62 -0
- package/src/client/native/ContextInjectionRow.tsx +72 -0
- package/src/client/native/ReferenceIcon.tsx +35 -0
- package/src/client/native/tool-call-model.ts +248 -0
- package/src/client/native/upstream.json +41 -0
- package/src/client/open-file.ts +94 -0
- package/src/client/pending-submission.ts +49 -0
- package/src/client/primitive-labels.ts +53 -0
- package/src/client/projection.ts +118 -0
- package/src/client/reasoning-follow.ts +12 -0
- package/src/client/settings-copy.ts +53 -0
- package/src/client/settings-slots.ts +20 -0
- package/src/client/settings.tsx +73 -0
- package/src/client/skill-status.ts +78 -0
- package/src/client/store.ts +30 -0
- package/src/client/stream-buffer.ts +100 -0
- package/src/client/streaming.tsx +62 -0
- package/src/client/style.d.ts +4 -0
- package/src/client/timeline.ts +77 -0
- package/src/client/tool-activity.ts +152 -0
- package/src/client/types.ts +66 -0
- package/src/client/waiting-clock.ts +25 -0
- package/src/client/word-motion.tsx +91 -0
- package/src/client/word-timeline.ts +96 -0
- package/src/dsh-better-display.ts +108 -0
- package/src/skill-roots.ts +138 -0
- package/src/skill-status.ts +81 -0
- package/tests/browser-closed-gap.tsx +20 -0
- package/tests/browser-motion.tsx +65 -0
- package/tests/browser-sticky-lanes.tsx +22 -0
- package/tests/browser-waiting-clock.tsx +14 -0
- package/tests/chat-flow-hook.test.ts +66 -0
- package/tests/cloud-gate.test.ts +34 -0
- package/tests/conversation-scroll.test.ts +47 -0
- package/tests/deliverables.test.ts +128 -0
- package/tests/entry.test.ts +42 -0
- package/tests/fixtures/maid-composer-hide.html +31 -0
- package/tests/fold-choreography.test.ts +38 -0
- package/tests/history-word-regression.test.ts +16 -0
- package/tests/live-turn.test.ts +218 -0
- package/tests/mcp-apps.test.ts +157 -0
- package/tests/message-chrome.test.ts +29 -0
- package/tests/open-file.test.ts +103 -0
- package/tests/pending-submission.test.ts +45 -0
- package/tests/projection.test.ts +184 -0
- package/tests/reasoning-follow.test.ts +34 -0
- package/tests/settings-copy.test.ts +63 -0
- package/tests/skill-status.test.ts +137 -0
- package/tests/stock-install.test.ts +50 -0
- package/tests/stream-buffer.test.ts +97 -0
- package/tests/timeline.test.ts +59 -0
- package/tests/waiting-clock.test.ts +24 -0
- package/tests/word-timeline.test.ts +136 -0
- package/tsconfig.json +11 -0
- package/tsdown.config.ts +49 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type {} from '@deepseek-ai/dsh-api-session-controller/client';
|
|
3
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
4
|
+
import { resolveWorkspacePath } from '@deepseek-ai/dsh-util-workspace-path';
|
|
5
|
+
import * as workspacePathPkg from '@deepseek-ai/dsh-util-workspace-path';
|
|
6
|
+
import { dirname } from './deliverables.js';
|
|
7
|
+
import type {} from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
8
|
+
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
9
|
+
import { Reader } from './Reader.js';
|
|
10
|
+
import { createReaderStore } from './store.js';
|
|
11
|
+
import { installReaderEntry } from './entry.js';
|
|
12
|
+
import { installBetterDisplaySettings } from './settings.js';
|
|
13
|
+
import { fillComposerDom } from './mcp-app.js';
|
|
14
|
+
import { fileAddressFor, modeFromSnapshot, openDeliverableFile } from './open-file.js';
|
|
15
|
+
import type { ReaderInjected } from './types.js';
|
|
16
|
+
|
|
17
|
+
/** Structural face of the sanctioned per-session composer writer. */
|
|
18
|
+
interface ComposerShell {
|
|
19
|
+
setDraft: (text: string) => void;
|
|
20
|
+
}
|
|
21
|
+
interface ConversationFace {
|
|
22
|
+
input?: { shell?: (id: SessionId) => ComposerShell };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface SidebarRightFace {
|
|
26
|
+
openResource?: (address: string, options?: { params?: { line?: number } }) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const officialFileAddressFor = (workspacePathPkg as {
|
|
30
|
+
fileAddressFor?: typeof fileAddressFor;
|
|
31
|
+
}).fileAddressFor;
|
|
32
|
+
|
|
33
|
+
async function openWorkspacePath(
|
|
34
|
+
ctx: Context,
|
|
35
|
+
path: string,
|
|
36
|
+
): Promise<void> {
|
|
37
|
+
const remote = ctx.remote as unknown as { session?: { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } } | undefined;
|
|
38
|
+
const remoteSession = remote?.session
|
|
39
|
+
?? (ctx.get?.('remote.session') as unknown as { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } | undefined)
|
|
40
|
+
?? ((ctx.get?.('remote') as unknown as { session?: { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } })?.session);
|
|
41
|
+
if (remoteSession?.openWorkspacePath) {
|
|
42
|
+
const result = await remoteSession.openWorkspacePath({ path });
|
|
43
|
+
if (!result?.ok) {
|
|
44
|
+
console.warn('[dsh-better-display] openWorkspacePath failed:', result?.error?.message);
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
console.warn('[dsh-better-display] remote.session is not available');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type { ReaderBlockOwner } from './types.js';
|
|
52
|
+
export { McpAppFrame } from './McpAppFrame.js';
|
|
53
|
+
export const name = 'dsh-better-display-client';
|
|
54
|
+
export const inject = ['slots', 'sessions', 'conversation', 'remote', 'remote.session'];
|
|
55
|
+
|
|
56
|
+
export function apply(ctx: Context): void {
|
|
57
|
+
const store = createReaderStore();
|
|
58
|
+
// conversation.view is session-scoped, so session persist keys are
|
|
59
|
+
// `dsh.reader.v1.<sessionId>`. One root instance keeps the open-mode
|
|
60
|
+
// switch on the unsuffixed `dsh.reader.v1` key.
|
|
61
|
+
const prefs = store.create();
|
|
62
|
+
installBetterDisplaySettings(ctx, prefs);
|
|
63
|
+
ctx.slots.inject('conversation.view', function* () {
|
|
64
|
+
yield ctx.slots.register({
|
|
65
|
+
name: 'conversation.view',
|
|
66
|
+
id: 'reader',
|
|
67
|
+
order: -5,
|
|
68
|
+
label: () => '阅读',
|
|
69
|
+
locale: 'chat',
|
|
70
|
+
children: { 'dsh-better-display.block': { kind: 'chain', scope: 'session' } },
|
|
71
|
+
store,
|
|
72
|
+
inject: (sessionId: SessionId): ReaderInjected => {
|
|
73
|
+
const session = () => {
|
|
74
|
+
const current = ctx.sessions.binding(sessionId)?.session;
|
|
75
|
+
if (!current) throw new Error('阅读页对应的会话已关闭。');
|
|
76
|
+
return current;
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
openPrefs: prefs,
|
|
80
|
+
loadOlder: async () => { await session().loadOlder(); },
|
|
81
|
+
loadImage: async attachment => {
|
|
82
|
+
const receipt = await session().readAttachment(attachment.attachmentId);
|
|
83
|
+
if (!receipt.ok) throw new Error(receipt.error.message);
|
|
84
|
+
return { data: Uint8Array.from(receipt.value.data), mediaType: receipt.value.attachment.mediaType };
|
|
85
|
+
},
|
|
86
|
+
openFile: async (path: string) => {
|
|
87
|
+
try {
|
|
88
|
+
const cwd = ctx.sessions?.list?.getSnapshot?.()?.byId[sessionId]?.cwd;
|
|
89
|
+
const sidebar = (
|
|
90
|
+
ctx.get?.('sidebarRight')
|
|
91
|
+
?? (ctx as unknown as { sidebarRight?: SidebarRightFace }).sidebarRight
|
|
92
|
+
) as SidebarRightFace | undefined;
|
|
93
|
+
await openDeliverableFile({
|
|
94
|
+
path,
|
|
95
|
+
mode: modeFromSnapshot(prefs),
|
|
96
|
+
sessionId,
|
|
97
|
+
cwd,
|
|
98
|
+
resolveWorkspacePath,
|
|
99
|
+
openExternal: async (absolutePath) => { await openWorkspacePath(ctx, absolutePath); },
|
|
100
|
+
openSidebar: typeof sidebar?.openResource === 'function'
|
|
101
|
+
? (address) => { sidebar.openResource!(address); }
|
|
102
|
+
: undefined,
|
|
103
|
+
fileAddressFor: officialFileAddressFor ?? fileAddressFor,
|
|
104
|
+
warn: (message, extra) => { console.warn(message, extra); },
|
|
105
|
+
});
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.warn('[dsh-better-display] openFile error:', error);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
revealFile: async (path: string) => {
|
|
111
|
+
try {
|
|
112
|
+
const cwd = ctx.sessions?.list?.getSnapshot?.()?.byId[sessionId]?.cwd;
|
|
113
|
+
const targetPath = resolveWorkspacePath(cwd, path);
|
|
114
|
+
// 1. Try dedicated host endpoint for native file highlighting (open -R / explorer /select)
|
|
115
|
+
try {
|
|
116
|
+
const res = await fetch('/better-display/reveal', {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: { 'Content-Type': 'application/json' },
|
|
119
|
+
body: JSON.stringify({ path: targetPath }),
|
|
120
|
+
});
|
|
121
|
+
if (res.ok) {
|
|
122
|
+
const data = await res.json();
|
|
123
|
+
if (data.ok) return;
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
// Server endpoint not yet available, fallback to directory open
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 2. Fallback to official opener with parent directory
|
|
130
|
+
const parentDir = dirname(targetPath);
|
|
131
|
+
const remote = ctx.remote as unknown as { session?: { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } } | undefined;
|
|
132
|
+
const remoteSession = remote?.session
|
|
133
|
+
?? (ctx.get?.('remote.session') as unknown as { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } | undefined);
|
|
134
|
+
if (remoteSession?.openWorkspacePath) {
|
|
135
|
+
await remoteSession.openWorkspacePath({ path: parentDir });
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.warn('[dsh-better-display] revealFile error:', error);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
forkAt: (seq: number) => {
|
|
142
|
+
// A missing anchor would silently fork the whole session instead of
|
|
143
|
+
// the intended turn prefix, so refuse it loudly rather than guessing.
|
|
144
|
+
if (typeof seq !== 'number' || !Number.isFinite(seq)) {
|
|
145
|
+
console.warn('[dsh-better-display] fork refused: missing anchor seq');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const sessionsApi = ctx.sessions as unknown as {
|
|
150
|
+
fork: (arg: { sessionId: string; atSeq: number; increaseTitle: boolean }) => Promise<string>;
|
|
151
|
+
open: (sessionId: string) => void;
|
|
152
|
+
} | undefined;
|
|
153
|
+
if (sessionsApi?.fork) {
|
|
154
|
+
sessionsApi.fork({ sessionId, atSeq: seq, increaseTitle: true })
|
|
155
|
+
.then(childId => { sessionsApi.open?.(childId); })
|
|
156
|
+
.catch(err => { console.warn('[dsh-better-display] fork failed:', err); });
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.warn('[dsh-better-display] forkAt error:', error);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
loadThrough: async (seq: unknown) => {
|
|
163
|
+
try {
|
|
164
|
+
const current = session() as unknown as { loadThrough?: (seq: unknown) => Promise<void> };
|
|
165
|
+
if (typeof current?.loadThrough === 'function') {
|
|
166
|
+
await current.loadThrough(seq);
|
|
167
|
+
} else {
|
|
168
|
+
await session().loadOlder();
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.warn('[dsh-better-display] loadThrough error:', error);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
fillComposer: (text: string) => {
|
|
175
|
+
// Sanctioned path: the conversation input shell owns the Lexical
|
|
176
|
+
// editor, so setDraft lands in the draft store deterministically.
|
|
177
|
+
try {
|
|
178
|
+
const conversation = (ctx as unknown as { conversation?: ConversationFace }).conversation;
|
|
179
|
+
const shell = conversation?.input?.shell?.(sessionId);
|
|
180
|
+
if (shell && typeof shell.setDraft === 'function') {
|
|
181
|
+
shell.setDraft(text);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
// Fall through to the DOM path below.
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
return fillComposerDom(text);
|
|
189
|
+
} catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
}, Reader);
|
|
196
|
+
yield installReaderEntry(ctx);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { AssistantChatData, ChatConversationViewNode } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
2
|
+
import type { AssistantBlock } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
3
|
+
import { assistantSegments, hasVisibleBody } from './projection.js';
|
|
4
|
+
import type { TurnBoundary } from './projection.js';
|
|
5
|
+
import type { ReaderFlowEntry, ToolActivityEntry } from './tool-activity.js';
|
|
6
|
+
|
|
7
|
+
export type LiveStep =
|
|
8
|
+
| { kind: 'reasoning'; key: string; nodeKey: string; start: number; blocks: AssistantBlock[]; step: number }
|
|
9
|
+
| { kind: 'body'; key: string; nodeKey: string; start: number; blocks: AssistantBlock[]; step: number }
|
|
10
|
+
| { kind: 'tool'; key: string; entry: ToolActivityEntry }
|
|
11
|
+
| { kind: 'user'; key: string; nodeKey: string }
|
|
12
|
+
| { kind: 'other'; key: string; nodeKey: string };
|
|
13
|
+
|
|
14
|
+
export type LiveTurnItem =
|
|
15
|
+
| { kind: 'user'; key: string; step: Extract<LiveStep, { kind: 'user' }> }
|
|
16
|
+
| { kind: 'fold'; key: string; steps: readonly LiveStep[]; summary: string }
|
|
17
|
+
| { kind: 'open'; key: string; step: LiveStep };
|
|
18
|
+
|
|
19
|
+
export function liveFoldEnabled(boundary: TurnBoundary): boolean {
|
|
20
|
+
return boundary.status === 'open';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function foldSummary(steps: readonly LiveStep[]): string {
|
|
24
|
+
let reasoning = 0;
|
|
25
|
+
let body = 0;
|
|
26
|
+
let tool = 0;
|
|
27
|
+
let extra = 0;
|
|
28
|
+
for (const step of steps) {
|
|
29
|
+
if (step.kind === 'reasoning') reasoning += 1;
|
|
30
|
+
else if (step.kind === 'body') body += 1;
|
|
31
|
+
else if (step.kind === 'tool') tool += 1;
|
|
32
|
+
else if (step.kind !== 'user') extra += 1;
|
|
33
|
+
}
|
|
34
|
+
const parts: string[] = [];
|
|
35
|
+
if (reasoning) parts.push(`思考×${reasoning}`);
|
|
36
|
+
if (body) parts.push(`输出×${body}`);
|
|
37
|
+
if (tool) parts.push(`工具×${tool}`);
|
|
38
|
+
if (extra) parts.push(`记录×${extra}`);
|
|
39
|
+
return parts.join(' · ') || '此前步骤';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One chain: fold only when a new reasoning step has prior body/tool/reasoning. */
|
|
43
|
+
export function splitChain(chain: readonly LiveStep[]): { fold: readonly LiveStep[] | null; open: readonly LiveStep[] } {
|
|
44
|
+
const lastReasoning = chain.findLastIndex(step => step.kind === 'reasoning');
|
|
45
|
+
if (lastReasoning < 0) return { fold: null, open: chain };
|
|
46
|
+
const prior = chain.slice(0, lastReasoning);
|
|
47
|
+
if (!prior.length) return { fold: null, open: chain };
|
|
48
|
+
const trigger = prior.some(step => step.kind === 'reasoning' || step.kind === 'body' || step.kind === 'tool');
|
|
49
|
+
if (!trigger) return { fold: null, open: chain };
|
|
50
|
+
return { fold: prior, open: chain.slice(lastReasoning) };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function skipReasoning(part: { kind: string; blocks: AssistantBlock[] }): boolean {
|
|
54
|
+
return part.kind === 'reasoning' && !part.blocks.some(block => block.kind === 'reasoning' && block.text.trim() !== '');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function skipBody(part: { kind: string; blocks: AssistantBlock[] }): boolean {
|
|
58
|
+
return part.kind === 'body' && !hasVisibleBody(part.blocks);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function stepsFromAssistant(
|
|
62
|
+
nodeKey: string,
|
|
63
|
+
data: AssistantChatData,
|
|
64
|
+
toolsByCallId: Map<string, ToolActivityEntry>,
|
|
65
|
+
consumed: Set<string>,
|
|
66
|
+
): LiveStep[] {
|
|
67
|
+
const marks: { at: number; step: LiveStep }[] = [];
|
|
68
|
+
for (const part of assistantSegments(data.blocks)) {
|
|
69
|
+
if (skipReasoning(part) || skipBody(part)) continue;
|
|
70
|
+
marks.push({
|
|
71
|
+
at: part.start,
|
|
72
|
+
step: {
|
|
73
|
+
kind: part.kind,
|
|
74
|
+
key: `${nodeKey}:${part.kind}:${part.start}`,
|
|
75
|
+
nodeKey,
|
|
76
|
+
start: part.start,
|
|
77
|
+
blocks: part.blocks,
|
|
78
|
+
step: data.step,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
data.blocks.forEach((block, index) => {
|
|
83
|
+
if (block.kind !== 'tool-call' || !block.callId) return;
|
|
84
|
+
const tool = toolsByCallId.get(block.callId);
|
|
85
|
+
if (!tool || consumed.has(tool.callId)) return;
|
|
86
|
+
consumed.add(tool.callId);
|
|
87
|
+
marks.push({ at: index, step: { kind: 'tool', key: tool.key, entry: tool } });
|
|
88
|
+
});
|
|
89
|
+
marks.sort((left, right) => left.at - right.at || left.step.key.localeCompare(right.step.key));
|
|
90
|
+
return marks.map(mark => mark.step);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Expand readerFlow into source-ordered live steps using existing block boundaries. */
|
|
94
|
+
export function segmentLiveTurn(
|
|
95
|
+
flow: readonly ReaderFlowEntry[],
|
|
96
|
+
get: (key: string) => ChatConversationViewNode | undefined,
|
|
97
|
+
): LiveStep[] {
|
|
98
|
+
const steps: LiveStep[] = [];
|
|
99
|
+
const consumed = new Set<string>();
|
|
100
|
+
const toolsByCallId = new Map<string, ToolActivityEntry>();
|
|
101
|
+
for (const entry of flow) {
|
|
102
|
+
if (entry.kind === 'tool') toolsByCallId.set(entry.callId, entry);
|
|
103
|
+
}
|
|
104
|
+
for (const entry of flow) {
|
|
105
|
+
if (entry.kind === 'tool') {
|
|
106
|
+
if (!consumed.has(entry.callId)) {
|
|
107
|
+
steps.push({ kind: 'tool', key: entry.key, entry });
|
|
108
|
+
consumed.add(entry.callId);
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const node = get(entry.nodeKey);
|
|
113
|
+
if (!node || node.visibility === 'hidden' || node.kind === 'turn-tail') continue;
|
|
114
|
+
if (node.kind === 'user' || node.kind === 'steering') {
|
|
115
|
+
steps.push({ kind: 'user', key: entry.key, nodeKey: entry.nodeKey });
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (node.kind === 'assistant-step') {
|
|
119
|
+
steps.push(...stepsFromAssistant(entry.nodeKey, node.data as AssistantChatData, toolsByCallId, consumed));
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
steps.push({ kind: 'other', key: entry.key, nodeKey: entry.nodeKey });
|
|
123
|
+
}
|
|
124
|
+
return steps;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function presentLiveTurn(steps: readonly LiveStep[], boundary: TurnBoundary, autoFold = true): LiveTurnItem[] {
|
|
128
|
+
const live = autoFold && liveFoldEnabled(boundary);
|
|
129
|
+
const items: LiveTurnItem[] = [];
|
|
130
|
+
let chain: LiveStep[] = [];
|
|
131
|
+
const flush = () => {
|
|
132
|
+
if (!chain.length) return;
|
|
133
|
+
if (!live) {
|
|
134
|
+
for (const step of chain) items.push({ kind: 'open', key: step.key, step });
|
|
135
|
+
chain = [];
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const { fold, open } = splitChain(chain);
|
|
139
|
+
if (fold?.length) {
|
|
140
|
+
items.push({ kind: 'fold', key: `live-fold:${chain[0]!.key}`, steps: fold, summary: foldSummary(fold) });
|
|
141
|
+
}
|
|
142
|
+
for (const step of open) items.push({ kind: 'open', key: step.key, step });
|
|
143
|
+
chain = [];
|
|
144
|
+
};
|
|
145
|
+
for (const step of steps) {
|
|
146
|
+
if (step.kind === 'user') {
|
|
147
|
+
flush();
|
|
148
|
+
items.push({ kind: 'user', key: step.key, step });
|
|
149
|
+
} else {
|
|
150
|
+
chain.push(step);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
flush();
|
|
154
|
+
return items;
|
|
155
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/* Visual baseline: deepsuite `@deepseek/md` markdown.css, adapted to CSS
|
|
2
|
+
Modules. Cite pills, header anchors, and thinking-small variants are
|
|
3
|
+
intentionally absent (no matching DOM). Token names match that sheet. */
|
|
4
|
+
|
|
5
|
+
.markdown {
|
|
6
|
+
min-width: 0;
|
|
7
|
+
overflow-wrap: anywhere;
|
|
8
|
+
font: var(--dsw-font-markdown-base);
|
|
9
|
+
color: var(--dsw-alias-label-primary);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.markdown strong {
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.markdown h1 {
|
|
17
|
+
font: var(--dsw-font-markdown-h1);
|
|
18
|
+
margin: 32px 0 16px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.markdown h2 {
|
|
22
|
+
font: var(--dsw-font-markdown-h2);
|
|
23
|
+
margin: 32px 0 16px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.markdown h3 {
|
|
27
|
+
font: var(--dsw-font-markdown-h3);
|
|
28
|
+
margin: 32px 0 16px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.markdown h4 {
|
|
32
|
+
font: var(--dsw-font-markdown-h4);
|
|
33
|
+
margin: 16px 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.markdown :where(h5, h6) {
|
|
37
|
+
font: var(--dsw-font-markdown-base-strong);
|
|
38
|
+
margin: 16px 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.markdown :where(h1, h2, h3, h4, h5, h6) strong {
|
|
42
|
+
font-weight: inherit;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.markdown p {
|
|
46
|
+
margin: 16px 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Tighten h4–h6 against a following list (design: 8px gap). */
|
|
50
|
+
.markdown :where(h4, h5, h6) + :where(ul, ol) {
|
|
51
|
+
margin-top: 8px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.markdown :where(h4, h5, h6):has(+ :where(ul, ol)) {
|
|
55
|
+
margin-bottom: 8px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.markdown a {
|
|
59
|
+
/* deepsuite markdown.css uses brand-text (blue in newDesign); this sheet
|
|
60
|
+
keeps design-platform brand-text as near-black, so links use the blue
|
|
61
|
+
business-primary alias instead. */
|
|
62
|
+
color: var(--dsw-alias-state-business-primary);
|
|
63
|
+
transition: box-shadow var(--ds-transition-duration) var(--ds-ease-in-out);
|
|
64
|
+
position: relative;
|
|
65
|
+
text-decoration: none;
|
|
66
|
+
/* Transparent hit-area padding; literal zero-alpha only (no painted color). */
|
|
67
|
+
border-left: 3px solid rgb(255 255 255 / 0);
|
|
68
|
+
border-right: 3px solid rgb(255 255 255 / 0);
|
|
69
|
+
border-top: 2px solid rgb(255 255 255 / 0);
|
|
70
|
+
border-bottom: 2px solid rgb(255 255 255 / 0);
|
|
71
|
+
margin-left: -3px;
|
|
72
|
+
margin-right: -3px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.markdown a:hover,
|
|
76
|
+
.markdown a:focus {
|
|
77
|
+
outline: none;
|
|
78
|
+
text-decoration: underline var(--dsw-alias-state-business-primary);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.markdown a:focus-visible {
|
|
82
|
+
box-shadow: 0 0 0 2px var(--dsw-alias-state-business-primary);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.markdown :where(ul, ol) {
|
|
86
|
+
margin: 16px 0;
|
|
87
|
+
padding-left: 18px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.markdown li:not(:first-child) {
|
|
91
|
+
margin-top: 6px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.markdown li > :where(ul, ol) {
|
|
95
|
+
margin-top: 4px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.markdown li::marker {
|
|
99
|
+
line-height: 28px;
|
|
100
|
+
color: var(--dsw-alias-label-secondary);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Nested ol under ul/ol: markers inside (models sometimes emit this shape). */
|
|
104
|
+
.markdown :where(ul, ol) ol {
|
|
105
|
+
list-style-position: inside;
|
|
106
|
+
padding-left: 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.markdown :where(ul, ol) ol li p {
|
|
110
|
+
display: inline;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.markdown li > p {
|
|
114
|
+
margin: 8px 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.markdown li > *:first-child {
|
|
118
|
+
margin-top: 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Keep list-nested code-block vertical margins (design: +4px vs other last children). */
|
|
122
|
+
.markdown li > *:last-child:not(:global(.md-code-block)) {
|
|
123
|
+
margin-bottom: 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.markdown hr {
|
|
127
|
+
display: block;
|
|
128
|
+
border: none;
|
|
129
|
+
height: 1px;
|
|
130
|
+
margin: 32px 0;
|
|
131
|
+
background: var(--dsw-alias-border-l2);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.markdown blockquote {
|
|
135
|
+
border-left: 2px solid var(--dsw-alias-label-caption);
|
|
136
|
+
margin: 16px 0 0;
|
|
137
|
+
padding-left: 14px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.markdown pre {
|
|
141
|
+
margin: 16px 0;
|
|
142
|
+
font-family: var(--ds-font-family-code);
|
|
143
|
+
overflow: auto;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.markdown :not(pre) > code {
|
|
147
|
+
display: inline-flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
box-sizing: border-box;
|
|
150
|
+
font: var(--dsw-font-markdown-code);
|
|
151
|
+
font-family: var(--ds-font-family-code);
|
|
152
|
+
font-size: 0.875em !important;
|
|
153
|
+
background-color: var(--dsw-alias-markdown-inline-code);
|
|
154
|
+
border-radius: 6px;
|
|
155
|
+
padding: 0 5px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.markdown :where(h1, h2, h3, h4, h5, h6) code {
|
|
159
|
+
font: inherit;
|
|
160
|
+
font-family: var(--ds-font-family-code);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.markdown :global(.katex-display) {
|
|
164
|
+
max-width: 100%;
|
|
165
|
+
overflow-x: auto;
|
|
166
|
+
overflow-y: hidden;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.markdown input[type='checkbox'] {
|
|
170
|
+
margin: 0 8px 0 0;
|
|
171
|
+
accent-color: var(--dsw-alias-label-secondary);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.tableScroll {
|
|
175
|
+
max-width: 100%;
|
|
176
|
+
overflow-x: auto;
|
|
177
|
+
overscroll-behavior-x: contain;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Wide tables reveal their horizontal bar on hover (or keyboard focus)
|
|
181
|
+
instead of keeping it painted. Chromium never repaints state-conditioned
|
|
182
|
+
scrollbar STYLES (neither hover-conditioned `::-webkit-scrollbar*` rules
|
|
183
|
+
nor a :hover `scrollbar-color` change reaches the painted bar), so the
|
|
184
|
+
toggle is `overflow-x` itself — a layout change repaints reliably. The
|
|
185
|
+
resting padding matches the themed bar's height, so on an overflowing
|
|
186
|
+
table the appearing bar exactly replaces it and nothing below shifts.
|
|
187
|
+
Wheel and trackpad scrolling need the pointer over the table, which is
|
|
188
|
+
already the hover that re-enables `auto`. */
|
|
189
|
+
.tableScroll:global(.md-table-wide) {
|
|
190
|
+
overflow-x: hidden;
|
|
191
|
+
padding-bottom: var(--dsh-scrollbar-width, 8px);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.tableScroll:global(.md-table-wide):hover,
|
|
195
|
+
.tableScroll:global(.md-table-wide):focus-visible {
|
|
196
|
+
overflow-x: auto;
|
|
197
|
+
padding-bottom: 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Chromium keyboard-focuses scrollable containers by default; the ring uses
|
|
201
|
+
the sheet's link focus color. */
|
|
202
|
+
.tableScroll:focus-visible {
|
|
203
|
+
outline: none;
|
|
204
|
+
box-shadow: 0 0 0 2px var(--dsw-alias-state-business-primary);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Many-column tables (the renderer's `md-table-wide` hook) keep their
|
|
208
|
+
natural width and scroll inside the wrapper; a hosting layout may widen
|
|
209
|
+
the wrapper through the hook (deepsuite chat TableWrapper parity). */
|
|
210
|
+
.tableScroll table {
|
|
211
|
+
border-collapse: collapse;
|
|
212
|
+
width: max-content;
|
|
213
|
+
max-width: max-content;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Tables under four columns, and any table inside a blockquote, fill the
|
|
217
|
+
column instead: cells wrap down to their minimum readable width, and the
|
|
218
|
+
wrapper's scroll only remains for the below-floor case (deepsuite chat
|
|
219
|
+
parity: `.wrapper:not(:has(th:nth-child(4), td:nth-child(4)))`). */
|
|
220
|
+
.tableFill table {
|
|
221
|
+
width: 100%;
|
|
222
|
+
max-width: none;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.tableScroll th {
|
|
226
|
+
text-align: start;
|
|
227
|
+
padding: 10px 16px;
|
|
228
|
+
border-bottom: 1px solid var(--dsw-alias-border-l3);
|
|
229
|
+
border-top: none;
|
|
230
|
+
font: var(--dsw-font-markdown-table-head);
|
|
231
|
+
max-width: 320px;
|
|
232
|
+
max-width: min(30vw, 320px);
|
|
233
|
+
min-width: 100px;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.tableScroll td {
|
|
237
|
+
padding: 10px 16px;
|
|
238
|
+
border-bottom: 1px solid var(--dsw-alias-border-l2);
|
|
239
|
+
font: var(--dsw-font-markdown-table);
|
|
240
|
+
max-width: 320px;
|
|
241
|
+
max-width: min(30vw, 320px);
|
|
242
|
+
min-width: 100px;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.tableScroll th:first-child,
|
|
246
|
+
.tableScroll td:first-child {
|
|
247
|
+
padding-left: 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.tableScroll td:last-child {
|
|
251
|
+
padding-right: 0;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.tableScroll table code {
|
|
255
|
+
font-size: 13px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.markdown > *:first-child,
|
|
259
|
+
.markdown p:first-child {
|
|
260
|
+
margin-top: 0 !important;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.markdown > *:last-child,
|
|
264
|
+
.markdown p:last-child {
|
|
265
|
+
margin-bottom: 0 !important;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.imageAlt {
|
|
269
|
+
color: var(--dsw-alias-label-tertiary);
|
|
270
|
+
font-style: italic;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.image {
|
|
274
|
+
display: block;
|
|
275
|
+
width: auto;
|
|
276
|
+
max-width: 100%;
|
|
277
|
+
height: auto;
|
|
278
|
+
margin: 0;
|
|
279
|
+
border-radius: 8px;
|
|
280
|
+
background: var(--dsw-alias-bg-base);
|
|
281
|
+
object-fit: contain;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* Inline file mention: the same link language this sheet gives anchors (and
|
|
285
|
+
thereby URL-promoted inline code) — link-blue at rest, underline only on
|
|
286
|
+
hover/focus. An underline at rest reads badly inside the code chip, where
|
|
287
|
+
it collides with monospace descenders and the pill background. */
|
|
288
|
+
.fileMention {
|
|
289
|
+
margin: 0;
|
|
290
|
+
padding: 0;
|
|
291
|
+
border: none;
|
|
292
|
+
background: none;
|
|
293
|
+
font: inherit;
|
|
294
|
+
color: var(--dsw-alias-state-business-primary);
|
|
295
|
+
text-decoration: none;
|
|
296
|
+
cursor: pointer;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.fileMention:hover,
|
|
300
|
+
.fileMention:focus {
|
|
301
|
+
outline: none;
|
|
302
|
+
text-decoration: underline var(--dsw-alias-state-business-primary);
|
|
303
|
+
text-underline-offset: 3px;
|
|
304
|
+
}
|