
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
dsh-ui-font
Advanced tools
DeepSeek Harness Web GUI plugin: set the UI and code font families from Settings → General (--dsw-font-family, --dsw-font-mono, --ds-font-family-code)
A DeepSeek Harness Web GUI plugin that sets the UI and code font families from Settings → General.
The DSH Web GUI exposes the colour scheme and the conversation font size in Settings → General, but not the font family. dsh-client-ui-theme ships base.css with two hardcoded stacks:
| Token | Used by | Stock value |
|---|---|---|
--dsw-font-family | body text and UI chrome | -apple-system, BlinkMacSystemFont, "Segoe UI", ... |
--dsw-font-mono, --ds-font-family-code | code blocks and monospace UI | "SF Mono", "JetBrains Mono", "Fira Code", Consolas, ... |
This plugin adds two rows to that settings page, stores the chosen stacks in its own profile entry, and applies them without touching any file inside the DSH installation (an npm update cannot revert them).
dsh plugin --profile web add dsh-ui-font
dsh web
dsh plugin installs the package into the profile and appends dependency packages that declare dsh.bundle to dsh.profile.bundles automatically, so no manual patch editing is required.
A running dsh web composes dsh.profile.bundles once at startup and only watches the user patch layer (cordis.patch.yml), so restart it after installing — a browser refresh alone will not load the plugin.
Uninstall with dsh plugin --profile web remove dsh-ui-font.
Open Settings → General. Each row lists the saved families as ordered chips, shows a sample line set in that stack, and carries an Add font button:
| Row | Setting | Applies to |
|---|---|---|
| Body font | sans | body text and UI chrome |
| Code font | mono | code blocks and monospaced text |
Add font opens a searchable list of the families installed on this machine, read with the browser's queryLocalFonts(), and each entry is rendered in its own face. Chrome asks for font access the first time; entries already in the stack carry a check mark, and picking one appends it to the end. The panel stays open so several families can be added in a row. When the browser cannot list local fonts, or you decline the prompt, the list falls back to a short set of common families and says so. A search term that matches nothing in the list offers Add "<name>", so any family the browser can resolve stays reachable.
Each chip carries three buttons: move earlier, move later, and remove. The order is the fallback order, so keep a CJK-capable family after the Latin one. Every change saves immediately, and an unchanged stack writes nothing.
The stored value is still a plain CSS font-family list, so a hand edit works too. Braces, semicolons, and angle brackets are sanitised away, because the value is interpolated into a stylesheet. The two fields are declared volatile() in the plugin's schema, which is what makes them editable; a write lands in this plugin's entry inside the active profile patch, and only the fields you change are written:
# $DSH_HOME/profiles/<profile>/cordis.patch.yml
- id: ui-font
name: dsh-ui-font
config:
sans: '"JetBrains Mono", "BIZ UDPGothic", "Noto Sans JP", sans-serif'
The entry id doubles as the settings namespace the browser half reads, so it must stay ui-font. Nothing is stored at all until you change a field, and the schema defaults cover a config that omits one:
| Setting | Shipped default |
|---|---|
sans | "JetBrains Mono", "BIZ UDPGothic", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif |
mono | "JetBrains Mono", "SF Mono", "Fira Code", Consolas, "Liberation Mono", monospace |
JetBrains Mono carries no CJK glyphs, so the shipped sans falls back to BIZ UDPGothic and Noto Sans JP for Japanese text. Keep at least one CJK-capable family after the Latin font, or Japanese falls back to whatever the browser picks.
A profile can also set the plugin's Loader config:, which becomes the base layer under the saved values — see the commented example in cordis.patch.yml.
| Path | Role |
|---|---|
src/index.ts | Host half: declares the two font stacks as volatile config and injects the saved stacks into the served index |
src/client.tsx | Browser half: applies the stylesheet and registers the two settings rows, including the picker |
src/shared.ts | Values shared by both halves: namespace, defaults, sanitising, stack parsing and formatting, and the stylesheet builder |
build.ts | esbuild through tsx on Node.js: host half as ESM, browser half as CJS wrapped in the module-loader format |
cordis.patch.yml | Inserts the ui-font row into the profile tree |
quality.json, knip.ts | Quality gate: the engines quality-check runs, all bundled checker rules as errors, warning failures, no baseline exemptions, and the Knip entry points |
lib/ | Generated by pnpm run build and by prepack; not tracked, so it exists only locally and inside the published tarball |
tests/host.test.ts, tests/client.test.ts, tests/shared.test.ts | Contract tests that run against the built lib/ |
.volatile(), so the settings domain projects them as an editable entry keyed by the profile row id (ui-font). Values are live references: apply() reads config.sans.get() / config.mono.get(), so a write is visible without re-resolving the config.settings.configure({ auto: false }, ctx.fiber) inside ctx.inject(["settings"], ...), because the browser half ships its own row UI and an auto-generated page would duplicate it.{ kind: "style" } row on webserver/index-inject. Rows are collected on every index render and placed directly after <head>, so the saved stacks are in effect for the first paint, before the browser half has loaded.inject: ["slots", "locale", "configForms"], reads the entry with configForms.get("ui-font"), and keeps one style[data-plugin-css="dsh-ui-font/font-family.css"] tag in sync with the snapshot. Only one authority writes the tokens: both halves emit the same :root,body{... !important} declarations, and the tag the browser half appends later wins on equal specificity while the host row covers the window before the client loads.settings.general.item slot with id: "dsh-ui-font-sans" / "dsh-ui-font-mono" and order: 70 / 71, so they sit after the built-in appearance and font-size rows. The browser half ships en, zh, and ja dictionaries; other locales fall back to English.formatFontStack() before every save, so what the settings file holds stays a round-trippable CSS list. The picker reads installed families once per row mount with globalThis.queryLocalFonts(); a missing API, an empty result, or a rejected permission request falls back to a bundled list rather than leaving the row unusable.!important is required because the ui-layout presenter writes the resolved tokens onto document.body as inline custom properties, which outrank a plain :root declaration for every descendant.--dsw-font-mono is not declared by the stock sheets; some components read it through var(--dsw-font-mono, ui-monospace, monospace), so the override claims it too.
Use Node.js 22.12.0 or newer and pnpm 10.34.6, as declared in package.json. CI uses Node.js 24.
The pnpm.onlyBuiltDependencies setting in package.json allows only esbuild's dependency build script. Bun 1.4.2 is required only to execute the upstream quality-check CLI; dependency installation, builds, and tests use pnpm and Node.js. CI installs Bun solely for that quality gate.
pnpm install --frozen-lockfile
pnpm run check # tsc --noEmit through @yuu1111/tsconfig/base.json
pnpm run lint # biome check .
pnpm run format # biome check --write --unsafe .
pnpm run check:quality # biome, tsc, knip, and the code, comment, document, and TSDoc checkers
pnpm run test # builds lib/, then runs Vitest against it
pnpm run build # regenerates lib/
The browser half may only require() modules the shell already seeds — react, react/jsx-runtime, @deepseek-ai/dsh-client-store, and @deepseek-ai/dsh-client-ui-primitives. build.ts fails the build when any other specifier shows up, and pins the production JSX runtime because the shell does not seed react/jsx-dev-runtime.
To try an edit in a live profile without publishing, install this checkout by path and rerun pnpm run build after each change:
dsh plugin --profile web add "link:$((Resolve-Path .\dsh-ui-font).Path)"
Bump version, commit, then publish a GitHub Release for the matching tag (v1.1.0 ↔ 1.1.0). .github/workflows/release.yml builds the tarball in a contents: read job, verifies the tag against package.json, and publishes it from a separate job with npm trusted publishing (--provenance), so no long-lived npm token is stored.
0.2.0-rc.2 (@deepseek-ai/dsh-client-ui-settings 0.2.0-rc.2), Windows 11 / Chromiumsemver.satisfies(runtime, range, { includePrerelease: true }) test dsh-app-boot applies to every @deepseek-ai/dsh-* peerpnpm run test: module id, exported plugin face, the configForms entry read, the settings form policy, the volatile value read, index style row, stylesheet updates, row registration, stack parsing and formatting, the picker paths (installed list, fallback, refused access, free-text add), chip removal and reorderingpnpm run check:quality: Biome, TypeScript, Knip, and the code, comment, document, and TSDoc checkersMIT
FAQs
DeepSeek Harness Web GUI plugin: set the UI and code font families from Settings → General (--dsw-font-family, --dsw-font-mono, --ds-font-family-code)
The npm package dsh-ui-font receives a total of 42 weekly downloads. As such, dsh-ui-font popularity was classified as not popular.
We found that dsh-ui-font demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.