The themed WebKit scrollbar skin keeps a wide table's horizontal bar
permanently painted. Chromium never repaints state-conditioned scrollbar
styles (hover-conditioned ::-webkit-scrollbar* rules and :hover
scrollbar-color changes both compute but never reach the painted bar,
measured headed and headless), so the hover reveal toggles overflow-x
itself: hidden at rest with a padding-bottom matching the themed bar
height, auto on hover or keyboard focus with the padding released — the
appearing bar exactly replaces the padding and nothing below shifts.
Resting hidden overflow drops Chromium's implicit scroller focusability,
so wide wrappers carry an explicit tabindex for arrow-key scrolling.
Markdown tables were always rendered at natural width, so anything wider
than the 748px message column could only be read through horizontal
scrolling. Following the deepsuite chat TableWrapper treatment: tables
under four columns (and any table inside a blockquote) now fill the column
and wrap cell text down to the cells' minimum readable width, while
four-or-more-column tables keep their natural width behind the wrapper's
horizontal scroll and carry the stable md-table-wide hook. The chat
transcript widens hooked tables past the message column with a pure-CSS
container-query breakout (100cqw against ChatView's scroll container
standing in for deepsuite's JS-measured list width), keeping the table
content aligned with the message column and clamping to neutral when the
transcript is narrower than the column.
The message-feedback note editor previously expanded inline inside the
assistant message's shared IconActions row, overflowing the row at every
viewport and pushing the branch action and the clock out of the column.
Rework it into a portable popover: the note editor is a fixed-position panel
portaled to document.body and anchored to the note trigger, so the row keeps
its single 28px line whether the editor is open or not and the panel escapes
the column's overflow clip.
The like/dislike buttons and the note trigger stay in the row unchanged; the
trigger toggles the popover (textarea + Save/Cancel + note-save failure) which
auto-focuses the input and closes on Escape or an outside click, returning
focus to the trigger. Rating/list-load failures surface in the row, note-save
failures inside the popover. Reuse the Menu portal surface tokens and add
@types/react-dom so the createPortal usage typechecks.
Layout e2e sweeps six viewports and pins that opening the editor leaves the
row's overflow, line count, and out-of-column items unchanged, and that the
panel is outside the column, within the viewport, and anchored to its trigger.
Unit tests cover the popover's portal-to-body, toggle, Escape/outside-click
dismissal, and unmount early-returns.
react, react-dom, shiki, katex, clsx, the micromark and mdast families and nine
more reach only browser artifacts, which resolve nothing on a user's machine.
Moving 79 declarations out of dependencies and non-optional peerDependencies
drops 103 tarballs and 6.05 MB from an install of the published CLI.
The jobs list and the Cordis panel carried identical outside-pointerdown
close effects, which the duplication gate rejects; both now use
useDismissOnOutsidePointer from ui-primitives.
Refs #2526
Every release member now declares publishConfig.access: public, so the scope no
longer mixes levels: the 221 packages/*/* and apps/* manifests join the vendored
framework and the native packages.
check-workspace-constraints drops the per-sequence expectation and holds every
release member to public, which is what stops a member from drifting back.
Access is a property of the package, not of a version: the dsh packages already
published as restricted become world-readable at their next publication.
The grep, glob, and web_search tool rows all rendered as "Search" with
the same magnifier icon, so a transcript full of local searches was
indistinguishable from web searches. Grep and glob now carry their own
command-named titles through TOOL_TITLES, and the web_search row wears
a new globe glyph (IconGlobeOutline14) while keeping its "Search" title.
A test file under packages/client now says which face it covers:
`*.client.spec.{ts,tsx}` and its `*.client.{ts,tsx}` helpers belong to the
Client aggregate, `*.host.spec.ts` to the host aggregate. The carrier's four
node-half specs take the Host suffix.
The two suffixes are mutually exclusive, so each aggregate excludes the
other's and both keep one broad test glob: `exclude` wins over `include`, and
`packages/client/**` no longer has to be excluded wholesale from the host
program with per-file `files` entries carved back out of it. A Host-face spec
that reaches only Host source therefore needs no cross-face project
reference, which the split-project rule rejects.
vitest still discovers every file through `**/*.spec.{ts,tsx}`.
The toast sits 120px from the viewport top and centers over its anchor —
the composer card, so it centers on the chat column rather than the window;
a rejected model selection (e.g. picking a text-only model while the
session holds images) announces through the same banner while the in-menu
strip with Retry stays the catalog-load surface. The attachment rail
consumes every wheel tick with a vertical component: a diagonal pan keeps
its horizontal intent and nothing scrolls the conversation behind the
composer.
Body-portal the lightbox and toast so transformed ancestors cannot trap
their fixed positioning (a lightbox opened from a chat message covered only
the chat column); make the toast pointer-transparent; observe the rail
element's own size instead of window resizes; consume vertical wheel ticks
exclusively via a non-passive listener with LINE/PAGE delta normalization;
keep the start position when the rail mounts over an existing draft; honor
prefers-reduced-motion for the toast, remove-control, and paging; retry
loads through the guarded load effect; note the deliberate promptError
re-announce; pin the intake toast in the assembled snapshot; sync the
superseded multimodal note and package docs.
Single-click original preview in the composer rail and chat history; remove
control inside the thumbnail, revealed on hover/focus (always on touch);
hidden-scrollbar rail overflow paged by edge arrows with wheel panning and
end-reveal on add; image-intake rejections and prompt failures announce as a
transient top-center toast instead of inline strips.
The attachment atoms move to a new zero-cordis package
@deepseek-ai/dsh-client-ui-attachment (rail, message gallery, lightbox),
seeded as a platform module; the toast is a ui-primitives atom. Strings
arrive as label props bridged from the conversation dictionary.