Escape the .pdf extension in the op-target regex (the unescaped /.pdf$/
matched any path ending in pdf) and fold the copy-pasted toggle button
into PaneToggle; record the sixth deliverable in the design doc.
- redaction useMemo: one redactText pass per field (the outcome carries
both text and hit; was a hit-scan plus a mask pass), and the banner
condition drops the redundant redactionOn check (hit implies on)
- three copy-pasted header toggles -> PaneToggle; the md reading body
-> MdReadingView (absorbing the mermaid branch + codeLabels)
- redaction preference persisted under the repo's dsh-sidebar:v1: prefix
- render toggle limited to .html/.htm (the editor html viewer's ext set;
.xhtml went beyond any claim)
- tsx/jsx share TS_WORDS/JS_WORDS; CSS word list loses its duplicate
entries and its nonexistent true/false constants
- parseReadContent/parseReadLines share the contentLines() envelope core
(content extraction + Showing-lines filter)
Path layer: patterns now match with a right boundary (must not continue
into [a-z0-9]) and list only store-plural forms (credentials/secrets/
passwords, not tokens), so tokenizer.ts / dev.environment.ts /
style.keys.ts / api-tokens.md are no longer masked whole while
.env.local / credentials.json / mytoken.yaml still are.
Content layer: drop the bare key/auth/token/pass/bearer field names —
they label far more ordinary content (keyboard keys, CSS custom
properties, lexical tokens) than secrets; qualified names stay and
Bearer schemes remain covered by BEARER_RE.
Adds the over-masking regression cases; fixes the spec's source import
extension to .ts (repo convention).
.htmlFrame hardcoded #fff -> var(--dsw-alias-bg-base) (mirrors the editor's
.editorHtml); .redactBanner referenced --dsw-alias-state-warning-primary,
which does not exist in the DSW vocabulary (it is state-warn-*), so the
var() always fell through to a hardcoded #b8860b — use the real token with
no literal fallback.
Every .md preview renders through MarkdownDocument (htmlInfo is ungated),
but its markdown segments passed the raw source into MarkdownText, whose
chat-security stance drops non-http(s) image destinations — so local
markdown-syntax images (, reference-style definitions)
always fell back to alt text. Only HTML <img src> tags were rewritten
(postProcessSanitized).
Apply rewriteLocalImageUrls to each markdown run (segment + appended
reference definitions) inside MarkdownDocument's prepared memo, through
the same media trust fence the sanitized HTML leaves already use.
Idempotent for already-absolute media URLs; remote destinations
untouched. Also correct the stale "non-null only for HTML documents"
comment on TextEditor's htmlInfo.
.html/.htm/.xhtml op previews (read / write / edit, non-error) gain a
Render/Raw toggle in the preview pane head: the render view loads the
SAVED file through the same /sidebar/html route the editor's html viewer
uses, so relative assets (./style.css, img/x.png) resolve inside the
route and a segmented read still renders the whole document (the route
serves the file, not the op snapshot — the file-trace-style srcDoc
approach would render a cut-off document whose scripts never run).
The frame is ALWAYS sandboxed (HTML_IFRAME_SANDBOX, moved to the shared
html-preview.ts module — opaque origin, no allow-same-origin, no
top-navigation) plus the route's CSP sandbox header as
defense-in-depth; unlike the editor viewer this surface has no
no-sandbox escape hatch. The contract is pinned by new sandbox-spec
tests on the extracted HtmlRenderPreview component and the pane toggle
(off by default; absent for non-html and error targets).
Locales: zh/en + all 19 shipped third-language overrides (key-set-equal
gate green). Typecheck + eslint clean; sandbox/redact/diff-rows/frontmatter
suites 36/36 green. The full-suite host-side failures (fs-operations /
pty smoke / git / install-powershell) are pre-existing on the base
branch (verified via stash) and unrelated.
- DiffPane.tsx: gate the redacted view on the computed `hit` (the unused-var
lint was the memo returning redactionHit: true unconditionally; now an
unredacted payload short-circuits like the toggle-off path, and only an
actual secret hit builds the masked copy)
- MarkdownHtml.tsx: drop the three dead imports (ComponentType,
lazyChunkComponent, MermaidMarkdownProps) left by the mermaid-lazy merge
Typecheck clean; eslint clean on both files; 39 targeted tests pass.
The changes-tab op previews replay read/write/edit payloads into the DOM
(diff rows, read rows, markdown reading mode, error text) — when the model
reads a credentials file, that plaintext must not land on screen or in
inspectable DOM. New display-only redaction layer, on by default with a
one-click pane toggle (persisted in localStorage):
- path layer: credential-shaped filenames (.env / *secret* / *credential* /
*token* / *api-key* / private keys...) mask whole files line-wise
- content layer: ordinary files mask only secret-shaped spans — secret
field assignments keep the field name (api_key: [REDACTED]), plus bare
token prefixes (sk- / AKIA / ghp_ / xox / ya29.), Bearer headers, and
PEM private-key block headers
- redacted payloads flow through the same viewOp shape into diff rows,
read rows, markdown reading mode, and error text
Known edge (documented in README): an unquoted mermaid node label
containing a redacted secret breaks the diagram and falls back to source;
workaround is quoting the label. Session data is untouched — this is
display-only and not a boundary against same-context malicious plugins.
Locales: zh/en dictionaries + all 19 shipped third-language overrides
(key-set-equal gate green). 9 new unit tests; typecheck clean; targeted
suites 40/40 green. The fs-operations failure is pre-existing on the base
branch (environment-dependent, verified via stash) and unrelated.
The host-anchor fallback diagnostic strip inlined hardcoded colors
(#f2a1a1 on #1b1b22). Route them through var() chains like the existing
SideChatView.module.css fallback pattern: text/border read
--dsw-alias-state-error-primary (the codebase's canonical error color,
23 existing usages) and the background reads
--dsw-alias-bg-layer-3,var(--dsw-alias-bg-base,...) (elevated surface,
then page base). The old hexes ride as the chain tails, so with no skin
tokens on the page the bar renders byte-identical to before, while any
--dsw-alias-* skin re-themes it (guide §12: no hardcoded colors). The
guide does not cover this strip and tests/theme.spec.ts asserts
effectiveTokenValue semantics only, so neither needs a follow-up.
Catalog entry names (15 tab + 4 viewer) and the dsh-desktop shell preset
desc were raw hardcoded zh strings rendered straight into the add-plugin
modals and the title-bar scheme dropdown. Mirror the existing desc
pattern exactly: PluginEntry.name / ShellPreset.desc widen to the
i18n-friendly string-or-() =>-string union, the catalogs carry lazy
t('pluginXxxName') / t('presetDshDesktopDesc') lookups, and the
add-plugin modal (render, search filter, aria-labels) plus the scheme
SelectMenu resolve them like every other copy. The plain-string arm of
the union stays as the raw-name fallback, and t() itself never yields
empty text (missing keys render the key), so a dict gap can never blank
a name.
20 new dictionary keys added to zh/en (locales.ts), ja (locales-ja.ts),
and all 18 third-language dictionaries — tests/locales.spec.ts enforces
key-set equality against zh for every shipped dict (precedent: 9cc642b),
so the 'leave other locales-* untouched' shortcut would fail the hard
parity gate; real translations are provided instead.
Spec follow-ups (behavior change: names/descs now render through t()):
- plugin-list.spec.ts: name non-empty assertion resolves the union.
- add-plugin-modal.spec.tsx: sentinel/office name assertions resolve
from the catalog (en locale in tests) instead of pinning zh literals.
- shell-presets.spec.ts: desc non-empty assertion resolves the union.
The viewport-corner toggle cluster sits at top:3 riding the right panel's
tab strip while expanded, and recenters to top:14 over DSH's session
header row when collapsed — an 11px snap on every expand/collapse. Ride
the panel slide's own duration/easing (--ds-transition-duration-slow +
--ds-ease-in-out) so both moves stay in lockstep; prefers-reduced-motion
already clears .toggleCluster transitions.
Graduate from the alpha track to the stable channel:
- Baseline: CI mount pin, dsh.plugin.json engines floor, and every
@deepseek-ai/* peer floor / devDependencies pin move to 0.1.2-rc.1
(published on the `next` dist-tag). alpha.5 -> rc.1 is a pure version
bump in the harness (all package.json "version" lines, zero source
delta), so no code adaptation; every host contract was re-verified
unchanged and the real-host mount smoke passes.
- dsh-client-locale resumed publishing; its trailing-pin exception is
gone — peers and devDeps align at 0.1.2-rc.1 (pnpm peers check clean).
- Version: fold the unpublished v0.18.1-alpha.0 / v0.19.0-alpha.0 bumps
into the v0.18.0 stable release; SIDEBAR_SERVICE_VERSION follows
package.json (asserted by tests/service.spec.ts).
- Docs: README/README_EN badges + channel notes + a full "since v0.17.1"
changelog entry (the two folded alpha numbers are annotated), AGENTS.md
§2/§3 baseline wording, external-plugin-guide version line, and the
e2e-host-protocol header's re-verified list gains 0.1.2-rc.1.
The explorer @ button appended plain text (@folder/file.ts), which DSH's
composer only decorates as a folder ref (@folder/) — the file name stayed
outside the link. Insert FILE references through DSH's own structured
insert-reference event instead, minting one chip for the whole reference
(displayed as @basename, serialized as the full @path). Directories keep the
plain @dir/ mention so folder completion still works.
The markdown preview container re-mounts on every mode flip (its scrollTop
lives on that DOM element), and the save-then-switch-to-preview reload
(#215) rebuilds the whole TextEditor instance - so after editing and
saving, the preview returned to the top, and entering edit mode opened the
editor at the top instead of where the reader was.
- Module-level per-file (session + path) preview scroll memory, seeded on
component mount and re-applied in a before-paint layout effect on
[mode, mdText]; a restoring guard keeps the collapsed-reload clamp from
polluting the remembered position.
- Preview -> edit handoff: throttle-capture the block text at the preview
viewport top, reverse-map it to a source line (linesOfSelection) and
scroll the editor there, with a proportional fallback when the text match
is ambiguous (ratio === 1, i.e. the reader is exactly at the bottom, also
lands the editor at the bottom).
- The editor jump writes scrollDOM.scrollTop directly instead of using
CodeMirror's scrollIntoView: that API walks every scrollable ancestor -
and even the window when the browser is zoomed (visualViewport <
innerHeight) - to reveal the target in the sidebar, which dragged the
whole sidebar/page up when the reader was at the very end of the
document. A plain scrollTop write on the editor scroller can never
scroll anything outside the editor.
Closes#466
Three preview (render) bugs where DSH's chat-optimized markdown primitives
leak into the file-preview scroll world:
- CodeBlock banner: DSH's bannerWrap is position:sticky (top:0, z-index:6) so a
block keeps its copy action reachable inside a per-message scroll container.
In the preview every fence shares one .editorMd scroll container, so the
banner pins to the viewport top until the whole block scrolls past and reads
as a floating header. Neutralize stickiness for non-mermaid fences in the
preview (mermaid chrome replaces the banner and is excluded via
data-mermaid-processed).
- TOC popover z-index: .tocBar was z-index:3, below the code banner's 6, so the
outline panel painted beneath a sticky fence header. Raise the bar to 7.
- TOC dismissal: only the toggle re-click closed the popover. Add a document
pointerdown listener that closes on an outside press (blank preview space),
exempting the button and panel so the toggle still flips.
Adds md-toc unit coverage for outside-click dismiss and toggle-preserving
behavior; records the deviations in the TOC design doc.
shift fix) — but as a BARE declaration. Engines without clip support
(Safari < 16, older Chromium) drop an unsupported declaration at parse
time, which leaves the host `overflow: visible`: no clipping at all,
and the historical "collapsed panels extend the document's scrollable
area, whole page draggable" regression returns on those engines.
Declare `overflow: hidden` BEFORE `overflow: clip`: clip-aware engines
apply the later clip (identical behavior to #453), while old engines
fall back to hidden and keep clipping (with its pre-clip
scrollability — the same state they had before #453, strictly better
than visible). The cascade order is the contract.
Also extends the rule comment (the fix guards the whole
scroll-into-view class — focus moving into off-viewport regions,
nested iframes/workbenches claiming focus while loading — not just the
reveal case) and adds tests/panel-host-css.spec.ts guarding the
cascade (hidden first, clip last), the fixed viewport layer, and that
the degraded rule never reintroduces overflow. Contract documented in
the integration guide §12 (root anchor bullet).