Commit Graph
48 Commits
Author SHA1 Message Date
bowenliang123 ef2cae84c3 refactor(client): split styles.css into per-component sheets
The single 576-line global sheet grew with every card (trend chart,
browser, file activity, agent graph...), so every component change
opened the same shared file. Cut it mechanically into 14 cohesive
sheets under src/client/styles/ — one per component surface plus a
base sheet (root/card chrome, the lc-gran toggle, list anatomy,
error states, the composer-hiding rule).

Rules are byte-identical to the previous single sheet; index.ts
imports them in the original section order so the cascade is
unchanged (base first — later sheets re-tint the shared gran chrome
at equal specificity). bundle-smoke.mjs now asserts one style tag
per sheet instead of the single dsh-context/styles.css tag.
2026-08-29 14:06:52 +08:00
bowenliang123 a53bace57c fix(scripts): count the assistant-actions slot in the bundle smoke 2026-08-29 10:46:48 +08:00
bowenliang123 de4d6f327a build: kill previous watch instance when starting pnpm watch
Track the tsdown watcher pid in .tmp/watch.pid; a new watch kills the
previous instance first and the pid file is cleaned up on exit.
2026-08-29 00:46:32 +08:00
bowenliang123 1dab8dcdbf build: track the 0.1.2 client module graph
Drop the deleted `@deepseek-ai/dsh-client-runtime` from dsh.client.inject
and remove the preloaded-client-externals channel that died with it; mirror
0.1.2's platform module table (+ dsh-client-store) in the client bundle's
purity gate; widen the dsh peer ranges to >=0.1.0-rc.7 so 0.1.2 prereleases
resolve; pin the devDependency types to the newest published surface
(0.1.1-rc.2); the bundle smoke's fake ctx now honors inject callbacks.
2026-08-28 22:32:31 +08:00
bowenliang123 02878dd6bd fix(scripts): drop the stale loadOlderHistory provide assertion from the bundle smoke 2026-08-26 13:11:10 +08:00
Bowen Liang f45bd7e8d2 chore(scripts): move diff-fold out of tests/ and drop its lib/ dependency
The session-log divergence check runs under bun against src/ directly,
tracking the working tree instead of the last build. Also add
scripts/bundle-smoke.mjs (pnpm run test:bundle): the one check that
must exercise the built bundle — module-loader handoff, CSS channel
tagging, registration disposal and HMR style-tag semantics in jsdom
with real React.
2026-08-26 00:57:58 +08:00
bowenliang123 d0b8f0daa0 chore(config): trim comments in build config, scripts and docs tooling
Compress the CSS-channel note in tsdown.config.ts, the vitest lane
explainer and the release workflow header; fix the install-hooks header
(git only consults .git/hooks when core.hooksPath is unset); slim the
social-preview generator and bundle-patch comments.
2026-08-24 17:16:40 +08:00
bowenliang123 dab80c6232 build: switch to tsup and declare harness runtime deps as peers
Replace the hand-rolled esbuild script (scripts/build.mjs) with a single
tsup config that already existed but never ran: tsup re-bundles the config
into a temp .mjs, so import.meta.url pointed at the temp dir and the
package.json read failed, silently leaving no entries. Read the manifest
from process.cwd() instead. The host half now keeps @deepseek-ai/dsh-session
external (resolving to the harness's single runtime instance) instead of
inlining it; the client half keeps the __ModuleLoader__.load wrap.

Align deps with the official dsh convention: runtime harness packages
(cordis, dsh-session, client-ui-primitives, react) move to peerDependencies
with dev mirrors (client-only ones optional); zod stays a dependency;
exact rc pins become caret ranges (^0.1.0-rc.8) so rc9/0.2 releases don't
break installs. Drop the esbuild devDependency (tsup brings its own) and
add tsup itself.
2026-08-20 16:09:16 +08:00
bowenliang123 85d7258c08 chore: define dsh function if not already available in register script 2026-08-19 10:29:13 +08:00
Bowen Liang 02c2d3765b chore: move the register command into a scripts/register.sh script 2026-08-18 21:23:12 +08:00
Bowen Liang 2dab549f98 fix: remove the husky pre-commit check to comply with DeepSeek Harness Desktop's plugin install verification requirements 2026-08-18 20:52:24 +08:00
Bowen Liang 36ae27765a build: silence commonjs-variable-in-esm warning for the harness-loader export
The client entry's `module.exports = { name, inject, apply }` is the
closure-factory contract every dsh.client package's `./client` export
must use — the harness's __ModuleLoader__ wraps the bundled CJS in a
factory that provides `var module = { exports: {} }` as a closure local
(see scripts/build.mjs:73-86), so the reference is legitimate. esbuild's
parser nonetheless flags it because the .ts source lives in a
"type": "module" package.

`logOverride: { 'commonjs-variable-in-esm': 'silent' }` suppresses that
one specific warning without touching any other esbuild signal — a future
maintainer adding a real CJS leak (e.g. require() inside the entry) would
still see a different warning. No source, test, or runtime behavior
changes; the bundled client.js still ends with the documented factory
shape that the harness's loader consumes.
2026-08-17 23:07:29 +08:00
bowenliang123 d6e89b415c feat(client): add Plugin info card beside Context stats
Side-by-side head row (3:2 flex, wraps when tight): plugin name, version,
and GitHub repo as whole-cell links (name/version -> GitHub/npm), with the
version/repo baked in from package.json via esbuild define. A once-per-page
npm registry check adds an 'Update!' badge + update-command tooltip on the
version cell whenever the registry release differs; failures stay silent.
Bilingual strings, cell-hover underline + brand border affordance.
2026-08-17 11:36:32 +08:00
bowenliang123 38dbd2ad92 refactor(ui): move the context tab onto the harness's React stack
All view components are now tsx JSX function components instead of
hand-rolled h() factories; the bundle externalizes the framework seed words
(react, @deepseek-ai/dsh-client-ui-primitives) so the loader module table
resolves the single shared framework instance at runtime.

Reuses shared ui-primitives where a clean fit exists (injection and
model-switch event glyphs -> IconPlusOutline16/IconBranchOutline16); the
bespoke data-viz chrome (charts, shared-hover tooltip) intentionally stays
custom but continues to draw the shared --dsw-alias-* tokens.

esbuild uses the classic JSX transform, so the emitted createElement trees
are isomorphic to the previous h() calls and the element-parsing tests, the
chart-render assertions, and the real-React repro all stay green unchanged.
Adds @deepseek-ai/dsh-client-ui-primitives as a type devDep and documents the
framework-reuse findings in docs/redesign.md §8.
2026-08-16 03:51:00 +08:00
bowenliang123 6bebd495e9 feat: route context timeline through the harness session-projection pipeline
Replace the custom /dsh-context RPC channel + 2s client polling with a native
contextTimeline session-projection unit: the host registers one pure
init/apply/view unit on ctx.sessionProjections (framework-driven, persisted by
the projection cache, streamed via session/projection push frames), and the
client reads the finished value through the framework standard
useProjection seat. Deletes the client stale-while-revalidate cache and the
local service type stubs; grows the modern shelf registration (locale) and
adds the published @deepseek-ai/* types as devDeps plus zod as the single
runtime dependency (wire schema, kept external in the host bundle).

Fixes tool-result node attribution to read the durable source.callId /
content toolCallId instead of the never-present block.callId (tool labels had
silently never rendered).

Retention is tightened to trim by whole turn-runs deterministically (bounded
state, no 1200<->1500 oscillation under incremental folding).

Tests now drive the projection unit directly (host) and feed the view through
a useProjection stub (client); all chart/render assertions preserved. Adds
docs/redesign.md (review + target architecture, phases 0-3 landed) and
docs/CLIENT_SURFACE.md (client-side audit).
2026-08-16 03:25:01 +08:00
bowenliang123 6cb6a96980 refactor: layered module split with first-screen optimizations
- shared/types.ts: single wire contract (type-only) used by both halves
- host/: services (contracts), pricing (token-meter), fold (incremental fold
  + retention), snapshot (build + event turn/step attribution), index (RPC)
- client/: i18n, styles, format, categories, viewkit (shared deps),
  cache (per-session stale-while-revalidate), components/* (one module per
  component), view (composition root), index (plugin entry)
- first-screen: initial render seeds from the session cache, polling pauses
  while the tab is hidden (resume + immediate fetch on visible), and a
  transient fetch error never blanks already-visible data
- build.mjs: bundle:true for both halves (react external in the client
  bundle); the client half is now covered by tsc --noEmit for the first time
2026-08-15 02:19:28 +08:00
bowenliang123 78db333048 feat(ui): hover the free window space in the composition bar 2026-08-15 01:59:48 +08:00
bowenliang123 b9853494d7 feat(ui): context stats board above the current composition 2026-08-15 01:56:25 +08:00
bowenliang123 ee0e3b2a4f feat(ui): history detail header shows the boundary event gap as a chip 2026-08-15 01:44:21 +08:00
bowenliang123 2e2debb81e feat(ui): boundary events show the gap as a step/turn range 2026-08-15 01:35:50 +08:00
bowenliang123 f93c7b40a3 feat(host): stamp each event with the request before and after it 2026-08-15 01:35:49 +08:00
bowenliang123 888bb23078 feat(ui): show the turn/step a context event contributed to 2026-08-15 01:23:48 +08:00
bowenliang123 d8f9324693 feat(host): attribute context events to the request that follows them 2026-08-15 01:23:48 +08:00
bowenliang123 9c0adb5dcc fix(ui): stop the edge-fade setState cascade that whites out the tab on granularity switch
The TrendChart layout effect has no deps and dispatched setEdges on every
render. Steady state survived via React's same-value eager bailout, but a
granularity switch genuinely flips the fades (narrow turn chart has no
overflow), and the programmatic scrollLeft assignment fires an async scroll
event whose dispatch leaves the fiber with pending lanes — the eager
bailout is skipped, every commit's effect enqueues yet another update, and
the queue grows without bound until React #185 (maximum update depth)
throws and the slot's error boundary blanks the tab.

Mirror the fades in a ref and never dispatch when the values are unchanged,
killing the cascade at the source instead of relying on bailout internals.

The real-React repro now emulates layout well enough to catch this class:
scrollWidth follows the bar count and programmatic scrolls dispatch an
async scroll event, so the old code crashes the test and the fix passes.
2026-08-15 01:15:33 +08:00
bowenliang123 2716066563 feat(ui): label turns as 'Turn {n}' with capitalized Step
Tooltips and detail headers now read 'Turn 44 · Step 5' (step mode) and
'Turn 44 · 5 steps' (turn mode) instead of the terse 'T44 · step 5';
Chinese uses the natural '第 44 轮 · 第 5 步'. The compact turn strip
blocks keep the short 'T44' label (14px columns cannot fit 'Turn').
2026-08-15 00:47:41 +08:00
bowenliang123 895ebbee1a feat(ui): granularity switches re-anchor the history at the newest bars
Returning from turn to step mode kept the stale left edge (the narrow
turn chart had clamped the scroll position), so the newest bars were off
screen. The layout effect now detects a granularity change and re-runs the
first-mount anchor — both directions land on the right edge. Plain
re-renders (polls) still respect a scrolled-away position.
2026-08-14 23:49:19 +08:00
bowenliang123 d09173ccd5 test: real-React + jsdom render smoke for the granularity toggle
Mounts the built client bundle with actual react/react-dom inside jsdom,
feeds a 460-request snapshot through the RPC, and toggles step -> turn ->
step — the flow reported to black-screen the tab. Renders cleanly, so the
black screen is not in the render path (layout-effect suspicion remains,
needs the real console error to pin down).
2026-08-14 23:44:49 +08:00
bowenliang123 4fee890b34 feat(ui): hovering a composition legend chip lights the segment and shows its tooltip
The tooltip is now derived from the shared hover key instead of internal
pointer state: hovering either a segment or its legend chip brightens the
same segment, highlights the chip, and shows the same tooltip centered on
the segment (percentage positioning, no measuring). Moving the pointer
within a segment no longer repositions the tooltip — the centered
placement is consistent for both directions.
2026-08-14 23:34:57 +08:00
bowenliang123 f8d66d08b2 fix(host): price assistant replies and tool results by their content
The TS port passed the event ENVELOPE (data) instead of the model-visible
message (data.message) for assistant/message and tool/result — every such
node was priced at a flat 4 tokens (role framing only), so tool-heavy and
assistant-rich sessions were massively undercounted in the Current
composition. This is why the overview (20.4k) disagreed with the real
usage. Mirrors dsh's deriveEventMessage exactly now; empty-content
assistant messages price 0; tools total uses dsh's whole-array price; the
overview additionally shows the last provider-reported actual prompt
alongside the estimate.
2026-08-14 23:27:04 +08:00
bowenliang123 72667cd5d3 fix(host): retain history by whole turns, not step count
The fold capped the timeline at a step count, so long sessions lost
older turns entirely in turn granularity. When the step bound is
exceeded, the timeline is now trimmed to the newest 300 WHOLE turn runs
(1500-step backstop for pathological many-step turns) — a turn is never
cut in half, and turn mode always shows the full recent turn range.
The harness session log itself has no event cap, so this was the only
limiter.
2026-08-14 23:06:16 +08:00
bowenliang123 45f28e3bde feat(ui): hovering the composition bar highlights the matching legend chip
The Current composition stacked bar and the legend below share one hover
state: hovering a segment brightens it and highlights its legend chip
(bold text + ring on the color dot), and hovering a chip highlights the
matching segment. The mini bar in the request detail is unaffected (no
legend there); the tooltip keeps working.
2026-08-14 23:01:06 +08:00
bowenliang123 44d58a96a7 fix(ui): turn bars use a uniform column width
Turn aggregates no longer stretch to their step span — every bar keeps
the fixed 14px column in both granularities, and the turn strip blocks
match (uniform in turn mode, step-spanning in step mode), so bars and
blocks stay aligned 1:1.
2026-08-14 22:56:27 +08:00
bowenliang123 d498eb54a9 feat(ui): message list shows timestamps (newest-first already, now locked in tests)
The Messages list renders newest-first by reversing the seq-ordered
surface; that ordering is now pinned by a test. Each node row additionally
shows the source event's timestamp when the host provides one — the host
now stamps every surface node with ev.time (no new data source, just
passing the existing event time through).
2026-08-14 22:54:15 +08:00
bowenliang123 98e47931de feat(ui): turn detail shows the step count and a last-step tag
A turn aggregate in the detail panel is now labeled 'T{t} · N steps'
with a 'last step' tag instead of a bare step number, making it clear the
breakdown shown is the turn's final step (the record the bar carries).
Tooltips on turn bars use the same step-count wording; step records are
unchanged.
2026-08-14 22:49:17 +08:00
bowenliang123 f4fe0460b8 fix(ui): turn-mode bars and the turn strip now align exactly
The strip's min-width clamp silently widened single-bar turn blocks
(14px -> 16px), shifting every following block out of column. Removed the
clamp, and turn-mode bars now span their steps' columns: bars and strip
blocks share the same span math, so they align by construction in both
granularities (step records carry stepCount, which also feeds the strip).
2026-08-14 22:48:51 +08:00
bowenliang123 d2d0510ea0 feat(ui): anchor the history chart at the newest bars by default
On first mount (and on session switch, via a sessionId key remount) the
chart scrolls to the right edge so the latest steps are visible
immediately; afterwards it only sticks to the end while the user is near
it, so scrolling back into earlier turns/steps is never fought. The edge
fades follow the scroll position as before. The render test's fake React
now keeps useRef stable across renders (like React), which is what the
effect needs to see the container.
2026-08-14 22:40:23 +08:00
bowenliang123 8f7e77d233 fix(ui): render the full history — earlier turns/steps were unreachable
The chart sliced to the newest 80 requests, so anything older was never
rendered. All requests the host provides (capped at 160) are now rendered
and reachable by horizontal scrolling; the scrollbar is thicker and
higher-contrast, edge fades appear whenever more history sits beyond the
viewport, and the hint mentions the sideways scroll.
2026-08-14 22:36:19 +08:00
bowenliang123 a18e4d1050 feat(ui): history granularity toggle — one bar per step or per turn
A Step/Turn segmented control in the trend card header switches the chart
granularity. Turn mode collapses each turn into a single bar showing its
LAST step's record (the context state when the turn finished); the turn
strip, hover linking, and detail panel all follow the aggregated bars.
2026-08-14 19:55:03 +08:00
bowenliang123 60aa6d43db feat(ui): render the turn strip as color blocks aligned to the steps
Each turn is now a colored block whose width spans exactly its bars'
columns (same grid math), so the partition lines up with the steps above.
Consecutive turns cycle a palette; the T-label sits on the block with a
tooltip fallback for narrow single-bar turns. Hover highlighting
(bidirectional with the bars) is unchanged.
2026-08-14 19:42:10 +08:00
bowenliang123 f13bdddbf3 feat(ui): turn strip under the history chart with hover highlighting
The turn tick row is now an interactive strip: hovering a turn block
highlights that turn's bars in the chart (tinted columns), and hovering a
bar highlights its turn block — both follow one shared hover-only state,
so nothing glows by default. Leaving the strip or the chart clears it.
2026-08-14 19:39:26 +08:00
bowenliang123 ed63b7f0d8 feat(ui): themed hover tooltip on the composition bar segments
The Current composition stacked bar (and the request-detail mini bar)
now shows a theme-native tooltip on hover per segment — category, token
count, and share of the total — positioned at the pointer and clamped
inside the bar. Replaces the native title attribute; the wrapper keeps
the tooltip outside the rounded/clipped stack.
2026-08-14 19:25:06 +08:00
bowenliang123 f8ac2f45cb feat(ui): hover a history bar to preview its breakdown below
Hovering a trend bar links the detail panel to that request immediately
(hover wins over the pinned selection, which takes over again when the
pointer leaves; both fall back to the newest request). The hovered bar
gets a dashed outline, and the chart clears the preview on mouse leave.
2026-08-14 19:24:28 +08:00
bowenliang123 0e81996a41 feat: port both halves to strict TypeScript; bump to 0.4.0
- src/host.ts: typed fold model (Snapshot/RequestRecord/SurfaceNode/
  ContextEventRecord), local drift-free service contracts for
  connection/sessions/sessionQuery via declare module augmentation
- src/client.cts: typed client bundle (React types via @types/react,
  locale/slots/connection contracts, typed component props); CJS-flavored
  so esbuild keeps require/module.exports despite type:module
- tsconfig.json: strict, noEmit, verbatimModuleSyntax (runtime-free of
  imports in the client bundle is enforced by the type system)
- scripts/build.mjs: esbuild transpile for both halves, loader wrap and
  smoke checks unchanged; npm run typecheck added to the test suite
- note: @deepseek-ai/* service type packages publish broken dependency
  chains on npm (dsh-paths 404), so local contracts replace them
2026-08-14 19:03:16 +08:00
bowenliang123 8085308997 feat(ui): fixed-width scrollable history chart with turn-range ticks
- bars use a fixed 14px column width, so sparse histories no longer
  stretch and dense histories scroll horizontally instead of compressing
- the chart sticks to the newest bars unless the user scrolls away
- a turn tick row under the bars groups consecutive requests of the same
  T into one labeled range (T1 spans its bars with a connector line)
- test-client now renders the view tree with a stateful fake React and
  asserts bar count, scroll container, and turn-range geometry
2026-08-14 18:02:29 +08:00
bowenliang123 446c220505 scripts: fix empty-array expansion under bash 3.2 set -u 2026-08-14 17:18:19 +08:00
bowenliang123 975d1f2179 scripts: support NPM_OTP for 2FA accounts in publish 2026-08-14 17:14:42 +08:00
bowenliang123 efb2fff4c2 scripts: keep npm cache in-repo for sandboxed environments 2026-08-14 17:12:51 +08:00
bowenliang123 cd7282ad53 refactor: package dsh-context as an installable dsh bundle
- src/host.js: ESM Cordis plugin; serve snapshots over the generic
  Connection RPC channel /dsh-context (ctx.connection.rpc) instead of the
  dynamic-runner harness.handle seat; lazily resolve sessions/sessionQuery.
- src/client.js: browser half in web-boot bundle shape (require('react'),
  inject connection/slots/locale, plugin-owned style tag, setInterval poll).
- package.json: dsh.bundle (cordis.patch.yml layer) + dsh.client (web UI)
  manifests, ./client export.
- cordis.patch.yml: inserts the dsh-context loader row.
- scripts/build.mjs: zero-dependency build of lib/index.js + lib/client.js
  with smoke checks; scripts/test-host.mjs + scripts/test-client.mjs:
  functional tests for both halves.
- scripts/publish.sh: npm publish driven by NPM_TOKEN, auto version bump.
2026-08-14 17:11:56 +08:00