diff --git a/apps/web/tests/navigation-panes.e2e.ts b/apps/web/tests/navigation-panes.e2e.ts index 87e19a6915..d6fe3d0942 100644 --- a/apps/web/tests/navigation-panes.e2e.ts +++ b/apps/web/tests/navigation-panes.e2e.ts @@ -147,14 +147,21 @@ describe('web e2e: navigation & panes over a rich seeded session', () => { await expect.poll(() => page.locator('tr[data-turn-start="true"]').count(), { timeout: 15_000 }).toBe(2) await expect.poll(() => page.getByRole('columnheader').count(), { timeout: 10_000 }).toBe(0) await page.locator('tr[data-kind="tool"]').first().click() - await expect.poll(() => page.getByRole('complementary', { name: 'Event details' }).count(), { timeout: 10_000 }).toBe(1) + const details = page.getByRole('complementary', { name: 'Event details' }) + await expect.poll(() => details.count(), { timeout: 10_000 }).toBe(1) + await page.evaluate(() => { document.body.setAttribute('data-ds-dark-theme', '') }) + const darkSummarySurfaces = await details.getByRole('heading', { name: 'Payload' }).evaluate(heading => ({ + heading: getComputedStyle(heading).backgroundColor, + panel: getComputedStyle(heading.closest('[aria-label="Event details"]')!).backgroundColor, + })) + expect(darkSummarySurfaces.heading).toBe(darkSummarySurfaces.panel) + await page.evaluate(() => { document.body.removeAttribute('data-ds-dark-theme') }) await page.getByRole('tab', { name: 'Result' }).click() await expect.poll(() => page.getByText('NAVIGATION_OK', { exact: false }).count(), { timeout: 10_000 }).toBeGreaterThanOrEqual(1) const snapshot = (await captureStableAria(page, '[class*="viewArea"]', scaffold.workspaceCwd)) .split(SEED_ID).join('{{seededId}}') await compareOrRefreshGolden(TRAJECTORY_EXPECTED, snapshot, MODE) - await page.getByRole('complementary', { name: 'Event details' }) - .getByRole('button', { name: 'Close details' }).click() + await details.getByRole('button', { name: 'Close details' }).click() }, 60_000) it.skipIf(MODE === 'record')('focuses the ledger by dragging an overview interval', async () => { diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 7043de4911..1e8eb07210 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ { find: /^@deepseek-ai\/dsh-client-web-react$/, replacement: src('../../packages/client/web-react/src/index.ts') }, { find: /^@deepseek-ai\/dsh-client-ui-slots$/, replacement: src('../../packages/client/ui-slots/src/index.ts') }, { find: /^@deepseek-ai\/dsh-client-ui-primitives$/, replacement: src('../../packages/client/ui-primitives/src/index.ts') }, + { find: /^@deepseek-ai\/dsh-client-schema-form$/, replacement: src('../../packages/client/schema-form/src/index.ts') }, { find: /^@deepseek-ai\/dsh-client-modules\/client$/, replacement: src('../../packages/client/modules/src/client/index.ts') }, ], }, diff --git a/docs/module-graph.md b/docs/module-graph.md index eb2f7d887d..f0f58a18c1 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -316,6 +316,7 @@ flowchart TD pkg_client_ui_settings --> pkg_client_ui_primitives pkg_client_ui_settings --> pkg_client_ui_slots pkg_client_ui_settings --> pkg_invariants + pkg_client_ui_trajectory --> pkg_client_runtime pkg_client_ui_trajectory --> pkg_client_ui_primitives pkg_client_ui_trajectory --> pkg_invariants pkg_credentials --> pkg_brand @@ -1082,7 +1083,7 @@ flowchart TD | [`client-locale`](../packages/client/locale) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) | | [`client-test-runtime`](../packages/client/test-runtime) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`host-apiproxy`](../packages/host/apiproxy), [`invariants`](../packages/support/invariants) | | [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) | -| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`client-ui-primitives`](../packages/client/ui-primitives), [`invariants`](../packages/support/invariants) | +| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`invariants`](../packages/support/invariants) | | [`credentials`](../packages/credentials/credentials) | `credentials` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) | | [`helper`](../packages/sdk/helper) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess) | | [`telemetry`](../packages/sdk/telemetry) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) | diff --git a/packages/client/ui-trajectory/package.json b/packages/client/ui-trajectory/package.json index fc853d19d1..8da6559866 100644 --- a/packages/client/ui-trajectory/package.json +++ b/packages/client/ui-trajectory/package.json @@ -24,6 +24,7 @@ }, "dshClient": { "inject": [ + "@deepseek-ai/dsh-client-runtime", "@deepseek-ai/dsh-client-ui-conversation" ], "platform": "web" @@ -37,6 +38,7 @@ "diff": "^9.0.0" }, "peerDependencies": { + "@deepseek-ai/dsh-client-runtime": "^0.0.1", "@deepseek-ai/dsh-invariants": "^0.0.1", "@deepseek-ai/dsh-client-ui-primitives": "^0.0.1", "cordis": "^4.0.0-rc.7", diff --git a/packages/client/ui-trajectory/src/client/TrajectoryTable.module.css b/packages/client/ui-trajectory/src/client/TrajectoryTable.module.css index c7b7948845..86cc8dfd8b 100644 --- a/packages/client/ui-trajectory/src/client/TrajectoryTable.module.css +++ b/packages/client/ui-trajectory/src/client/TrajectoryTable.module.css @@ -321,6 +321,13 @@ width: 76px; } +.kindSlot :global([role='tooltip']) { + border: 1px solid var(--dsw-alias-border-l2); + background: var(--dsw-alias-bg-layer-2); + box-shadow: var(--dsw-shadow-lv2); + color: var(--dsw-alias-label-primary); +} + .content { padding-left: 4px !important; color: var(--dsw-alias-label-primary); @@ -828,6 +835,7 @@ flex: 1; min-height: 0; overflow: auto; + scrollbar-gutter: stable; } .detailBodySummary { @@ -989,7 +997,7 @@ margin: 0; padding: 0 0 3px 14px; color: var(--dsw-alias-label-secondary); - background: var(--dsw-alias-bg-base); + background: var(--dsw-alias-bg-layer-1); font: var(--dsw-font-xs-strong-13); user-select: none; } diff --git a/packages/client/ui-trajectory/src/client/TrajectoryTable.tsx b/packages/client/ui-trajectory/src/client/TrajectoryTable.tsx index 53db32efca..4f88bf0bca 100644 --- a/packages/client/ui-trajectory/src/client/TrajectoryTable.tsx +++ b/packages/client/ui-trajectory/src/client/TrajectoryTable.tsx @@ -1906,7 +1906,10 @@ export function TrajectoryTable({ - + (null) + const rootRef = useRef(null) + const trackRef = useRef(null) const [draft, setDraft] = useState(null) const [hover, setHover] = useState(null) const [viewport, setViewport] = useState(null) @@ -183,16 +191,48 @@ export const TrajectoryTimeline = memo(function TrajectoryTimeline({ } as CSSProperties const committed = model === null || range === null ? null - : rangeFraction(range, domainStart, domainDuration) + : rangeFraction(range, domainStart, domainDuration, model.start, model.end) const draftFraction = model === null || draft === null ? null - : rangeFraction(draft, domainStart, domainDuration) + : rangeFraction(draft, domainStart, domainDuration, model.start, model.end) const visibleRange = draftFraction ?? committed const activeRange = draft ?? range + useEffect(() => { + const root = rootRef.current + if (root === null) return + const onWheel = (event: globalThis.WheelEvent): void => { + event.preventDefault() + const track = trackRef.current + if (track === null || model === null) return + setAnimateViewport(false) + const rect = track.getBoundingClientRect() + const anchorFraction = + clampFraction((event.clientX - rect.left) / Math.max(1, rect.width)) + const nextDuration = Math.min( + fullDuration, + Math.max( + Math.min(mode === 'sequence' ? MINIMUM_ZOOM_OPERATIONS : 20, fullDuration), + domainDuration * Math.exp(event.deltaY * 0.0015), + ), + ) + if (nextDuration >= fullDuration * 0.999) { + setViewport(null) + return + } + const anchorTime = domainStart + anchorFraction * domainDuration + const nextStart = Math.min( + Math.max(anchorTime - anchorFraction * nextDuration, model.start), + model.end - nextDuration, + ) + setViewport({ start: nextStart, end: nextStart + nextDuration }) + } + root.addEventListener('wheel', onWheel, { passive: false }) + return () => { root.removeEventListener('wheel', onWheel) } + }, [domainDuration, domainStart, fullDuration, mode, model]) if (model === null) { return ( -
+
@@ -339,36 +379,12 @@ export const TrajectoryTimeline = memo(function TrajectoryTimeline({ setHover(null) } - const onWheel = (event: WheelEvent) => { - event.preventDefault() - setAnimateViewport(false) - const rect = event.currentTarget.getBoundingClientRect() - const anchorFraction = - clampFraction((event.clientX - rect.left) / Math.max(1, rect.width)) - const nextDuration = Math.min( - fullDuration, - Math.max( - Math.min(mode === 'sequence' ? MINIMUM_ZOOM_OPERATIONS : 20, fullDuration), - domainDuration * Math.exp(event.deltaY * 0.0015), - ), - ) - if (nextDuration >= fullDuration * 0.999) { - setViewport(null) - return - } - const anchorTime = domainStart + anchorFraction * domainDuration - const nextStart = Math.min( - Math.max(anchorTime - anchorFraction * nextDuration, model.start), - model.end - nextDuration, - ) - setViewport({ start: nextStart, end: nextStart + nextDuration }) - } - return ( -
+
{ event.preventDefault() setAnimateViewport(false) diff --git a/packages/client/ui-trajectory/src/client/TrajectoryToolbar.module.css b/packages/client/ui-trajectory/src/client/TrajectoryToolbar.module.css index 6e3dd27916..aa38650411 100644 --- a/packages/client/ui-trajectory/src/client/TrajectoryToolbar.module.css +++ b/packages/client/ui-trajectory/src/client/TrajectoryToolbar.module.css @@ -149,7 +149,7 @@ font: var(--dsw-font-xxs-12); } -.action:hover:not(:disabled) { +.action:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); } @@ -159,11 +159,6 @@ outline-offset: 1px; } -.action:disabled { - color: var(--dsw-alias-label-dimmed); - cursor: not-allowed; -} - .actionIcon { color: var(--dsw-alias-label-tertiary); font: 14px/14px var(--ds-font-family-code); diff --git a/packages/client/ui-trajectory/src/client/TrajectoryToolbar.tsx b/packages/client/ui-trajectory/src/client/TrajectoryToolbar.tsx index 09d213c36e..5db9f56e80 100644 --- a/packages/client/ui-trajectory/src/client/TrajectoryToolbar.tsx +++ b/packages/client/ui-trajectory/src/client/TrajectoryToolbar.tsx @@ -8,18 +8,14 @@ export interface TrajectoryToolbarProps { actualDuration: boolean /** Select recorded-duration or equal-width blocks. */ onActualDurationChange: (actualDuration: boolean) => void - /** Whether recorded timing retains idle gaps between user turns. */ + /** Whether recorded timing retains idle gaps between operations. */ actualTime: boolean /** Select complete wall-clock timing or idle-compressed timing. */ onActualTimeChange: (actualTime: boolean) => void - /** Number of turns containing more than one row. */ - collapsibleTurns: number /** Whether every collapsible turn is currently folded. */ allTurnsCollapsed: boolean /** Fold or expand every collapsible turn. */ onToggleAllTurns: () => void - /** Number of assistant messages followed by tool calls. */ - collapsibleAssistants: number /** Whether every collapsible assistant's tool calls are currently folded. */ allAssistantsCollapsed: boolean /** Fold or expand tool calls under every collapsible assistant. */ @@ -40,10 +36,8 @@ export function TrajectoryToolbar({ onActualDurationChange, actualTime, onActualTimeChange, - collapsibleTurns, allTurnsCollapsed, onToggleAllTurns, - collapsibleAssistants, allAssistantsCollapsed, onToggleAllAssistants, searchQuery, @@ -91,7 +85,6 @@ export function TrajectoryToolbar({ aria-label={allTurnsCollapsed ? 'Expand turns' : 'Collapse turns'} aria-pressed={allTurnsCollapsed} title={allTurnsCollapsed ? 'Expand turns' : 'Collapse turns'} - disabled={collapsibleTurns === 0} onClick={onToggleAllTurns} >