dsh-fonttune 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.en.md +98 -111
- package/CHANGELOG.md +98 -111
- package/README.en.md +10 -7
- package/README.md +10 -7
- package/cordis.patch.yml +12 -2
- package/lib/client.js +346 -86
- package/lib/index.js +43 -1
- package/lib/shared.cjs +273 -84
- package/package.json +4 -5
package/CHANGELOG.en.md
CHANGED
|
@@ -2,179 +2,166 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to **dsh-fonttune** are documented here. Chinese version: [CHANGELOG.md](CHANGELOG.md).
|
|
4
4
|
|
|
5
|
-
## [0.2.
|
|
5
|
+
## [0.2.6] - 2026-09-24
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Opening "Font tune" still needed a second click before the settings appeared**: the official plugin page now treats opening that entry as the expansion gesture, while the card still carried a collapsible header of its own. The card now starts open on the entry page and on the package configuration page (the chevron still collapses it by hand).
|
|
9
|
+
- **Dragging the UI no longer stutters (the sidebar and the conversation-list resize both showed it)**: the injected stylesheet made the browser match nearly two thousand elements against every rule on each style recalculation (about 15,000 matches), and a drag recalculates once per frame — hence the jank. Each axis now matches only the elements it actually needs (about 1,400): over a 50-step drag the measured recalculation time dropped from **1.5 s to 0.5 s**, frames over 25 ms from **49 to 0–2**, and the frame interval is back to **17 ms** (zero long tasks). The families, sizes, weights, line heights and the code axis (ligatures and feature switches included) look and read exactly as before.
|
|
6
10
|
|
|
7
11
|
### Added
|
|
8
|
-
- **
|
|
9
|
-
- **The card can appear in any of the three seats the two lines declare**, and the host picks which to render: the rc line's `settings.plugin.item` (claimed by settings namespace), the alpha's `plugins.item` (an entry on the Plugins page — `id: fonttune`, label from the dictionary's `card.title`, summary from `card.description`, the whole card only on its detail page, which is **how DSH's own shell / agent-loop / subagent / web-search register themselves**), and `plugins.bundle.config` for an installed profile package (claimed by the npm name `dsh-fonttune`, and rendered on that package's detail page).
|
|
10
|
-
- **Claiming the settings namespace now waits, then matches by signature**: the alpha's `configForms.describe()` is asynchronous, and the old code guessed a candidate by name before it answered, so the guess could land on somebody else's namespace and every write was refused by the host (on a real machine: the card opens, and the first edit reports "The setting was not saved"). With no candidate the scope is now `null` and a pending stand-in (the whole `getSnapshot`/`subscribe`/`set`/`unset` surface) is adopted once the describe view arrives, recognized by a **schema that carries both `uiFollowsDialog` and `stackDialog`** (real log: `served namespaces []` → `matched by served schema: fonttune`).
|
|
11
|
-
- **The host side unwraps volatile references**: on the alpha every field of `ctx.fiber.config` is a cosmokit volatile reference (`{ get(), [Symbol.for("cosmokit.volatile.write")](v) }`, frozen), and reading it as a plain object yields nothing — on a real machine the card's **first render was a column of empty controls and the injected stylesheet was 0 bytes**. `plainConfigValue()` now unwraps it deeply, so `styleRow()` sees an ordinary object (measured: a 973 B rule set with `Georgia` / `520`).
|
|
12
|
-
- **`inject` narrowed to the services both lines have** (`slots` / `locale`): declaring one that a line does not have (say the rc-only `settingsScope`) **parks the whole package** (`pending (waiting for service: …)`) and the web never boots. The settings scope is looked up at runtime with `ctx.get()`, and a missing host layer degrades to browser-local storage.
|
|
13
|
-
- **Both shapes of a write result are understood**: the rc line's `mutate` resolves `undefined` even when it failed, while the alpha's `set`/`unset` resolve a boolean (`false` = the host refused). `followWrite` now reports a synchronous `false`, a thenable resolving `false`, and a rejection in that same note beside the preset row.
|
|
14
|
-
- **`test/host-line-probe.mjs`, a live probe that is independent of the host line.** It asserts only what must hold on every line — the page boots without errors, the plugin owns exactly **one** stylesheet element and it is connected, the settings really reach the document (with `DFP_EXPECT_FAMILY` / `DFP_EXPECT_WEIGHT` set it also asserts the computed values), the configuration card is reachable from the settings sheet and renders (`.dfp-card`, whichever seat that line declares) and a control on it really writes through to the host. It joined the read-only group of `npm run verify:browser`. `DFP_PROBE=1` makes the plugin log its diagnostics (which namespaces are served, which one was claimed, what state the seat is in); `DFP_DUMP=1` makes the probe print the Plugins page grouped as the host renders it.
|
|
12
|
+
- **DSH 0.1.7-rc.1 support**: its settings interface differs from the 0.1.5-rc.x one, and the plugin now works with whichever the host actually provides. The families, the interface weight, the code axis and the configuration card were all verified on that line (the card lives in the Plugins page's "Font tune" entry, and is reachable from the `dsh-fonttune` package row as well).
|
|
15
13
|
|
|
16
14
|
### Changed
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
|
|
15
|
+
- **The compatibility floor is now `0.1.5-rc.3`** (rc.1 / rc.2 are no longer claimed) and the compatibility table lists `0.1.7-rc.1`.
|
|
16
|
+
- **The loader row id this package ships is now `dsh-fonttune`** (the settings namespace and the package name): from DSH 0.1.7 on, settings are keyed by that id, and the first start imports the old `settings.yaml` by matching section names against it — with a different id the saved families and weights stay behind in the backup file and the plugin looks as if it did nothing. With the matching id, moving from 0.1.5-rc.x to 0.1.7 needs no re-selecting. (The same id also lets the package be installed through the profile's `dsh.profile.bundles`: the plugin manager then shows it as running, and the configuration page stays reachable.)
|
|
17
|
+
|
|
18
|
+
## [0.2.5] - 2026-09-23
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Installed as a link to the source directory, the card and the fonts disappeared together on 0.1.5-rc.x**: the plugin used the copy of the schema library in its own directory instead of the one DSH provides, and the newer copy marked the configuration in a way the rc line's settings controller cannot handle — the settings namespace was therefore never registered: no card in the settings sheet, an empty injected stylesheet, no typography change at all. It now always uses the copy DSH ships, so a linked install and a normal install behave identically.
|
|
22
|
+
|
|
23
|
+
## [0.2.4] - 2026-09-23
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **One package for both host lines**: it installs and works as-is on **DSH 0.1.5-rc.1 – rc.3** and on **0.1.7-alpha.1 – alpha.2**, with no version to pick. At runtime the plugin uses whichever settings interface the host offers (the rc line's and the alpha's are different); when neither is there it only injects the font and shows no card.
|
|
27
|
+
- **A configuration entry point on the alpha**: on 0.1.7-alpha.x the card lives in the sidebar's Plugins page, under the plugin entry "Font tune"; on rc.x it stays in Settings → Plugins → Plugin configuration.
|
|
20
28
|
|
|
21
|
-
###
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
### Fixed
|
|
30
|
+
- **On the alpha the card's first render was a column of empty controls**: it was reading the host's internal references instead of the values, so the injected stylesheet came out empty; the first frame now carries the right family and readouts.
|
|
31
|
+
- **On the alpha an edit reported "The setting was not saved"**: the plugin could write to a place that was not its own; it now writes only after confirming the configuration is its own.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
- The compatibility statement and the compatibility table in both READMEs are updated to 0.2.4, listing the five host lines that were actually tested (0.1.5-rc.1 / rc.2 / rc.3 and 0.1.7-alpha.1 / alpha.2) and where each line's settings live.
|
|
26
35
|
|
|
27
36
|
## [0.2.3] - 2026-09-22
|
|
28
37
|
|
|
29
38
|
### Fixed
|
|
30
|
-
- **A refused settings write
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
33
|
-
-
|
|
34
|
-
- **The per-theme "code font inside the dialog" rule missed its theme prefix** (dormant: per-theme is off today). `pushScopedCodeFamily` emitted its scoped rule without `body[data-ds-dark-theme]`, so with per-theme on the dark set would re-assert the **light** code family inside a dark dialog. It is prefixed like every other rule now, and a case asserts that every per-theme rule starts with the theme attribute.
|
|
35
|
-
- **`test/split-verify.mjs` was measuring a retired axis**: it still wrote `sizeOffset` (the interface size axis retired in 0.2.0) and expected the conversation chain to follow, so it rightly went red on a real machine. It now writes `sizeOffsetDialog` + `sizeOffsetCode`, computes its ratios from the measured baseline (the conversation offset rides DSH's own size, the code axis scales over the 16px reference — the two expectations differ), and adds a live check that the retired interface size axis moves nothing.
|
|
39
|
+
- **A refused settings write no longer looks like a successful one**: when the host rejects a save, the preset row says "The setting was not saved: <reason>" instead of silently snapping the control back to the stored value.
|
|
40
|
+
- **Export hands over the whole thing even without a clipboard**: the clipboard is tried first; on an origin that has none (the remote HTTP entry) the legacy copy path is used; only when both fail does the full JSON land in a fully selected read-only box, one copy shortcut away.
|
|
41
|
+
- **The font picker**: the four curated groups (monospace / CJK / Latin / generic) no longer vanish once local enumeration succeeds, so a curated family this machine does not have can still be found by name; a family is listed once; a refused font permission is no longer remembered forever — reopening the panel retries after twenty seconds.
|
|
42
|
+
- Every per-theme rule now carries its theme prefix (per-theme is not open yet; a latent bug fixed ahead of time).
|
|
36
43
|
|
|
37
44
|
### Changed
|
|
38
|
-
- **A smaller stylesheet
|
|
39
|
-
- **The retired size and line-height axes left the value surface**: `sizeOffset` and `lineHeight` (the interface's size and line height, which have emitted nothing since 0.2.0) stay in the schema and the document — old configurations still parse and `normalizeConfig` still carries them — but they are no longer part of a preset snapshot and no longer decide whether the dark set is a second set, so a light/dark pair that differs only there emits one stylesheet instead of two. "Reset every axis" **still clears them**, so a value nothing shows cannot hide in a document forever.
|
|
40
|
-
- **Dead code removed**: 31 dictionary keys nobody renders (both locales — the retired interface size/line-height copy, the per-theme editor, the old preset button row, the fixed preview label), one dead CSS rule (`.dfp-presetForm`) and seven dead exports (`MARKER`, `isShorthandToken`, `bodyTokenNames`, `uiLineTokenNames`, `deltaTokenNames`, `codeTokenNames`, `uiShorthandTokens`, `scaleFontShorthand`). `VALUE_FIELDS` / `RETIRED_FIELDS` / `DURABLE_FIELDS` now name the three ideas — value axes, stored-but-unrendered, every durable field — separately.
|
|
41
|
-
- **One entry point for the browser checks**: `npm run verify:browser` (`test/browser-verify.mjs`) runs the read-only `browser-probe` / `ui-walk` / `style-verify` by default, adds the four scripts that write the settings namespace under `--writers` (each snapshots the user layer and restores it before exiting), takes `--only a,b` for a subset, and prints a per-script time and exit-code summary. Two walk scripts were rewritten for the 0.2.x card: `slider-walk` expands the section it needs first (the card opens fully collapsed) and asserts "a drag writes nothing, a release writes, each axis lands on its own field"; `split-walk` writes a three-entry stack and asserts the simple-mode slots and remainder note, that a mode round-trip leaves the stack alone, and that picking a Western family in the real panel replaces exactly that slot. **A missing control is now a failure** — the old scripts printed "not found" and exited zero, which is a false green.
|
|
45
|
+
- **A smaller stylesheet and a cheaper background**: collapsing the exclusion lists took one real configuration's stylesheet from 2735 to 2440 bytes, and the size axis now compares three cheap signals before touching any stylesheet at all.
|
|
42
46
|
- Compatibility table gained 0.2.3.
|
|
43
47
|
|
|
44
|
-
### Tests
|
|
45
|
-
- `test/run.mjs` (86 checks): the picker's grouping rules (curated groups always present, no family twice, the local cap and sanitizing, the refusal-retry contract) and the polling gate with a counting stub (negative control included).
|
|
46
|
-
- `test/render-card.mjs` (21 checks): a second render after a state change keeping the hook order, both shapes of a refused write being reported, the export fallback handing over the whole JSON selected, reset clearing the retired axes, and a retired axis not being a value axis.
|
|
47
|
-
- Live (managed instance + headless Edge): WEIGHT OK (17 checks), SPLIT OK (9), SLIDER OK (9), SPLIT WALK OK (14), browser-probe and ui-walk green.
|
|
48
48
|
|
|
49
49
|
## [0.2.2] - 2026-09-16
|
|
50
50
|
|
|
51
51
|
### Fixed
|
|
52
|
-
- **
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
- **The release workflow never ran the tests**: `npm pack` does **not** trigger `prepublishOnly` (only `npm publish` does), so the tag-triggered release had always packed an untested artifact. It now installs and runs `npm run verify` before packing — deliberately **without** building first, because `test/artifacts.mjs` is exactly the check that the committed `lib/` is what the current `src/` produces, and a build step would mask that — and asserts that the tag equals the `package.json` version.
|
|
56
|
-
- **The CHANGELOG extraction matched version prefixes**: the release body used `index(line, "## " pat)`, so `0.2.1` also matched a `0.2.10` heading. It now matches `## <version> ` exactly and **fails the job** when the version has no section (it used to fall back to generated notes silently).
|
|
57
|
-
- **Two common CJK family names were not recognised**: `MS PGothic` and `MS PMincho` were treated as non-CJK (while `MS Gothic` and `MS Mincho` were fine), which feeds the simple mode's CJK slot. Fixed to `ms ?p?(?:gothic|mincho)`, the safe direction for a name heuristic.
|
|
58
|
-
- **`shiftTokens` guards zero**: a zero offset used to emit a real `calc((14px) + 0px)` declaration that disagrees with `isDormant` and `sameValueSet`. Unreachable today (the call sites guard), but a trap for the next caller.
|
|
59
|
-
- **`build.mjs --watch` watched files instead of the directory**: after an editor renamed a source, the process kept waiting on a dead inode and never rebuilt. It now watches `src/` and filters by name, and logs each successful rebuild.
|
|
52
|
+
- **In simple mode the CJK slot could not be picked or cleared**: choosing a family there, or clearing that chip, threw and lost the change (the western slot was fine); all three family sections now write their CJK slot properly.
|
|
53
|
+
- **`MS PGothic` / `MS PMincho` are recognised as CJK**: they used to be treated as western, which feeds simple mode's CJK slot.
|
|
54
|
+
- **A guard for unwired controls**: the offline checks now fire every control's callback (sliders, segmented controls, the family picker, the preset select, text inputs, every button), so a handler that was never connected fails immediately.
|
|
60
55
|
|
|
61
56
|
### Changed
|
|
62
|
-
- **
|
|
63
|
-
-
|
|
57
|
+
- **A release can no longer ship an untested package**: the tag-triggered build runs the full check before packing, and asserts that the packed artifact matches the sources and that the tag matches the version.
|
|
58
|
+
- Documentation: editing a source file requires a build first (the browser loads the built artifact), and the market entry only *states* its compatibility — there is no pre-install preflight.
|
|
59
|
+
|
|
64
60
|
|
|
65
61
|
## [0.2.1] - 2026-09-16
|
|
66
62
|
|
|
67
63
|
### Fixed
|
|
68
|
-
- **"
|
|
69
|
-
- **
|
|
70
|
-
- **The interface weight is back, and it
|
|
71
|
-
- **
|
|
72
|
-
-
|
|
73
|
-
|
|
64
|
+
- **After switching "interface follows conversation" off, the interface weight did not fall back and reset did nothing**: two copies of the injected stylesheet lived in the page, and the served one is only rebuilt on the next page load, so any change that *removed* a rule never took effect. There is only one copy now, and both switching the follow off and resetting apply immediately.
|
|
65
|
+
- **Going from following to not following no longer snaps the interface back to its own old value**: the value the interface was showing is stored as its own the moment the switch goes off, so nothing moves visually and the slider stays where it was; a conversation axis that was never set keeps DSH's own value.
|
|
66
|
+
- **The interface weight is back, and it really cannot touch the conversation**: the whole conversation body and code subtree is excluded from the interface weight rule — every interface text element moves, not a single conversation element does.
|
|
67
|
+
- **The conversation section no longer claims to "follow the interface" when unset** (the following has always been one-way: the interface follows the conversation).
|
|
68
|
+
- The previews inside the card are no longer dragged along by the interface weight: conversation, code and interface previews each match the real surface.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
- **The follow switch now covers font and weight together**: while it is on the interface uses the conversation's family and weight (its own two controls are hidden); with it off the interface uses its own, and neither side affects the other.
|
|
72
|
+
|
|
74
73
|
|
|
75
74
|
## [0.2.0] - 2026-09-16
|
|
76
75
|
|
|
77
76
|
### Added
|
|
78
|
-
- **Three
|
|
79
|
-
- **
|
|
80
|
-
- **
|
|
81
|
-
- **Code ligatures**:
|
|
82
|
-
- **Refuse synthetic
|
|
83
|
-
- **
|
|
84
|
-
- **
|
|
85
|
-
- **Card information architecture**: top to bottom the open card becomes an edit-mode row, a **preset bar** and three **accordion sections** — **conversation → interface → code** (the conversation leads because it owns every axis) — all collapsed by default, each title row carrying a summary with the chevron pinned to the far right, at most one open at a time; "refuse synthetic styles" is one or two switches, so it **no longer earns an accordion** and sits flat below the three sections. Expanding a section ends with that section's own **inline preview box** (rendering just that part; a floating variant was tried first and reverted after real-machine feedback). **Row format lines up with dsh-quick-toc**: every setting is a field row with 14px vertical padding (was 16px), every on/off control is the joined "one box split in two" segmented control (28px tall, was a 22px pill switch), and the control sits in the same row as its label, right-aligned (`.dfp-inline`, the same shape as quick-toc's `.dqt-pinline`) — so the two cards' rows and controls align.
|
|
86
|
-
- **The interface size / line-height / weight axes are RETIRED (measured)**: DSH exposes **no interface size hook** — the settings sheet, both sidebars and the workspace size their text with literals in upstream CSS (22 literals in settings-plugins, 18 in workspace, 6 in sidebar, **0** uses of the interface ladder tokens), and the ladder tokens (`--dsw-font-xs-13`, `--dsw-font-s-14`, …) are consumed only by 6 conversation-area packages (tool / trajectory / attachment / chat / conversation / cordis). The only official size hook is the conversation one. Scaling the ladder from these fields could not reach the settings sheet or the sidebars, while it reached the **conversation** through `--dsh-content-font-size` — the root cause of "I change the interface and the conversation moves". Weight is worse: it can only be expressed as a `body, body *` rule, which cannot avoid reaching the conversation either (narrowing it off the markdown subtree would also strip the theme's own heading weights). So **size, line height and weight belong to the conversation alone**, and the interface keeps the one axis that measurably works: its font (a page-wide `body` rule plus the theme variable). The `sizeOffset` / `lineHeight` / `weight` fields stay in the schema (old documents and presets keep parsing) but inject nothing at all.
|
|
87
|
-
- **Per-theme values ship out of this release**: light and dark share one value set — the top-bar per-theme switch and the light/dark editor are removed (the dark fields stay in the host schema but dormant, and rendering ignores them), to be re-evaluated in a later release.
|
|
88
|
-
|
|
89
|
-
### Fixed (real-machine walkthrough)
|
|
90
|
-
- **Preset messages no longer linger, and no longer move the layout**: a note like "Presets copied to the clipboard." used to sit in the card forever and, as a fresh line, push the sections below it down. It now **shares one row** with "With a preset selected, every change saves into it automatically": the message fades in on the **right** of that row and **fades out by itself after 2.6 s** (a 0.24 s opacity transition, then the text is cleared). The row is **always mounted** and exactly one line tall, and the message is clamped with `nowrap + ellipsis` — so appearing and disappearing never changes the row height and nothing below ever moves. Two identical actions in a row still work (each message carries an incrementing id, and the auto-hide timer restarts), and an over-long message (the clipboard-unavailable fallback, say) stays readable on hover through its `title`.
|
|
91
|
-
- **"I change the interface and the conversation moves"**: the interface size axis scaled the conversation's official content-size chain (`--dsh-content-font-size` / the secondary size) alongside the interface ladder, and every markdown size derives from that chain — so an interface edit really was a conversation edit. The interface axis is now explicitly barred from it (`bodyTokenNames` excludes `--dsh-content-font-*`), and the interface size axis itself is retired for the reason above.
|
|
92
|
-
- **The conversation size offset used to move headings but not paragraphs**: the old implementation only rewrote the delta tokens and literal sizes, while paragraphs take their size from `var(--dsh-content-font-size,14px)` — and `var()` inside a custom property resolves where the property is **declared**, so a deeper scoped override cannot reach it. The new implementation declares `--dsh-content-font-size` as "official value + offset" at the source, so paragraphs, headings and line heights all follow.
|
|
93
|
-
- **Dark overrides used to be shadowed by the light values**: the per-theme resolver merged the dark map over an **already-expanded** value set, so as soon as the follow rule changed direction the borrowed values could overwrite the dark ones. The dark overrides are now merged over the raw values and expanded afterwards, so a dark font can no longer be clobbered by a light value.
|
|
94
|
-
- **Weight readouts read 400**: an unset weight used to print "unset" while the slider sat at 400 and the hint said 400 keeps DSH's own — three statements that disagreed. An unset weight now reads **400** (conversation and code weights both).
|
|
95
|
-
- **The % vanished while dragging the line-height slider**: the pending readout had no unit and only gained one on release — the pending readout now keeps the `%` too (the size / code-height `px` prefixes already did).
|
|
96
|
-
- **The conversation section showed English "variable names"**: the dialog axis label keys (`dialog.*`, `size/line/weight.dialogLabel`…) were missing from the bilingual dictionaries, so `translate` fell back to the key names — both dictionaries are complete now.
|
|
97
|
-
- **The first control sat too far from the section title**: field rows carry 16px top padding on top of the section body — the first field of a section body is tightened to 10px.
|
|
98
|
-
- **The chevron left the far right edge when expanded**: with the summary gone nothing pushed it there — an expanded section renders a flexible spacer instead, so the arrow stays pinned in both states.
|
|
77
|
+
- **Three separate sets of typography**: **conversation** (body, tables, headings — family, size, line height and weight), **interface** (family only, following the conversation by default) and **code** (its own set, with ligatures and feature switches).
|
|
78
|
+
- **Conversation size**: −3 to +6 px on top of DSH's own conversation size, moving paragraphs, headings and line height together.
|
|
79
|
+
- **Conversation line height**: 100%–160%; the code line height is a separate additive offset.
|
|
80
|
+
- **Code ligatures**: default / on / off, plus a sanitized `font-feature-settings` field in advanced mode.
|
|
81
|
+
- **Refuse synthetic italics and bolds**: one switch in simple mode, two independent ones in advanced mode.
|
|
82
|
+
- **Presets**: a dropdown in the card with five defaults, applied on pick; once one is selected every edit is saved into it; rename, import and export sit beside it (import merges by name, at most 20, synced across browsers).
|
|
83
|
+
- **A rebuilt card**: edit mode, a preset bar, then collapsible conversation / interface / code sections that start collapsed, one open at a time, each ending in its own preview; row heights and controls match dsh-quick-toc.
|
|
99
84
|
|
|
100
85
|
### Changed
|
|
101
|
-
- **
|
|
102
|
-
-
|
|
86
|
+
- **The interface's size, line height and weight axes retired**: DSH has no interface-size interface (the settings page and sidebars use hard-coded sizes upstream), so those axes could not reach the interface and only disturbed the conversation. Size, line height and weight now belong to the conversation alone; the interface keeps the family, which does work. The old fields stay in the schema and keep parsing.
|
|
87
|
+
- Font families are delivered through the official theme layer, so a theme switch applies immediately.
|
|
88
|
+
- Light and dark share one set of values in this release; per-theme values are deferred.
|
|
89
|
+
|
|
90
|
+
### Fixed
|
|
91
|
+
- **"Changing the interface changed the conversation"**: the interface size axis used to scale the conversation's size chain too; it is isolated now.
|
|
92
|
+
- **The conversation size offset only moved headings, not paragraphs**: the offset is added once at the source, so paragraphs, headings and line height all follow.
|
|
93
|
+
- **In the dark theme a dark value could be overridden by a light one.**
|
|
94
|
+
- **The weight readout said "not set" while the slider sat at 400**: the readout is 400 now.
|
|
95
|
+
- **The `%` disappeared while dragging the line-height slider**: the pending readout carries its unit too.
|
|
96
|
+
- **The conversation section showed English variable names as labels**: both dictionaries are complete.
|
|
97
|
+
- The first field of an expanded section sat too far from the heading, and the expanded chevron was not pinned to the right.
|
|
98
|
+
|
|
103
99
|
|
|
104
100
|
## [0.1.6] - 2026-09-15
|
|
105
101
|
|
|
106
102
|
### Fixed
|
|
107
|
-
- **The
|
|
103
|
+
- **The card's expand chevron did not match the host's**: it was a text character — thinner than the host's arrows and off-centre when rotated. It is now the host's own inline chevron, rotating around its own centre.
|
|
104
|
+
|
|
108
105
|
|
|
109
106
|
## [0.1.5] - 2026-09-15
|
|
110
107
|
|
|
111
108
|
### Added
|
|
112
|
-
- **Code
|
|
109
|
+
- **Code weight**: weight split into independent "body weight" and "code weight" sliders (300–600 each, 400 meaning unchanged), so changing the body no longer drags the code along.
|
|
113
110
|
|
|
114
111
|
### Changed
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
|
|
112
|
+
- The weight slider moved into the family group it belongs to, with separate labels for the two.
|
|
113
|
+
- An unset code weight now writes DSH's own value back explicitly, so code does not follow the body weight after upgrading.
|
|
114
|
+
|
|
118
115
|
|
|
119
116
|
## [0.1.4] - 2026-09-15
|
|
120
117
|
|
|
121
118
|
### Added
|
|
122
|
-
- **Code
|
|
119
|
+
- **Code size**: size split into independent "body size" and "code size" sliders (−3 to +6 px each, 0 meaning unchanged), so changing the body no longer drags the code along.
|
|
123
120
|
|
|
124
121
|
### Changed
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
- **Compatibility table** now lists 0.1.4 as the latest version.
|
|
122
|
+
- The size slider moved under the family it belongs to, with separate labels; after upgrading the body behaves as before and code returns to DSH's own value.
|
|
123
|
+
|
|
128
124
|
|
|
129
125
|
## [0.1.3] - 2026-09-14
|
|
130
126
|
|
|
131
127
|
### Changed
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
|
|
128
|
+
- The switch row's label became "edit mode", and its two options went back to a joined segmented control with one divider between them.
|
|
129
|
+
- The card description now mentions the western/CJK split.
|
|
130
|
+
- The README dropped three implementation notes ("written on release", "no font flash on the first frame", "bilingual") — behaviour unchanged.
|
|
131
|
+
|
|
136
132
|
|
|
137
133
|
## [0.1.2] - 2026-09-14
|
|
138
134
|
|
|
139
135
|
### Changed
|
|
140
|
-
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
|
|
136
|
+
- The README was rewritten as features / install / usage / compatibility / development; design trade-offs and development pitfalls left it (both languages).
|
|
137
|
+
- The compatibility statement is narrower: only the latest patch of the 0.1.5 line is listed — older patches in the same major are compatible per semantic versioning and are no longer enumerated.
|
|
138
|
+
- The npm description was trimmed (published metadata cannot be edited, so this takes effect from 0.1.2), and the English card title became "Font tune", matching the package name.
|
|
139
|
+
|
|
144
140
|
|
|
145
141
|
## [0.1.1] - 2026-09-14
|
|
146
142
|
|
|
147
143
|
### Added
|
|
148
|
-
-
|
|
149
|
-
-
|
|
144
|
+
- An English changelog, `CHANGELOG.en.md`, shipped with the package.
|
|
145
|
+
- Pushing a `v*` tag now packs and creates/updates a Release automatically, including a version-less `dsh-fonttune.tgz` for the market entry to link to; the release body is the Chinese section for that version with the English one folded inside.
|
|
150
146
|
|
|
151
|
-
### Changed
|
|
152
|
-
- The stale offline-check count in the README and changelog was corrected (35 → 42). The running code is identical to 0.1.0.
|
|
153
147
|
|
|
154
148
|
## [0.1.0] - 2026-09-14
|
|
155
149
|
|
|
156
|
-
|
|
150
|
+
The first release: a two-half plugin. The browser half owns the settings card and the style injection; the host half registers the settings namespace and serves the saved declarations with the index, so the first frame already uses them.
|
|
157
151
|
|
|
158
152
|
### Added
|
|
159
|
-
- **
|
|
160
|
-
- **
|
|
161
|
-
- **
|
|
162
|
-
- **
|
|
163
|
-
- **
|
|
164
|
-
- **
|
|
165
|
-
- **
|
|
166
|
-
- **Global font weight** (300 ~ 600, any integer): applied through `body, body *`; 400 and "unset" both mean "leave DSH's own weight alone".
|
|
167
|
-
- **Commit-on-release sliders**: dragging the size-offset or weight slider only updates a local value and its readout — the write happens once on `pointerup` / `touchend` (window capture-level listeners) or on blur/keyup, so dragging never recomputes the size tokens and rewrites the settings document per step. **The local value also stays on screen until the host confirms it**: clearing it immediately would show the old committed number for one frame ("bounces back, then settles"); a repeated `pointerup` is guarded by an "already awaiting confirmation" flag so the same value is never written twice.
|
|
168
|
-
- **No first-paint font flash**: the host half listens on `webserver/index-inject` and injects the same declarations, so the first paint is already correct.
|
|
169
|
-
- **Bilingual copy** (Chinese/English) with a Chinese fallback; `queryLocalFonts` unavailability or refusal falls back to the built-in list silently.
|
|
170
|
-
- **Family-name sanitizing**: names are filtered through an allowlist and then quoted as a whole (letters/digits/space/`.` `,` `_` `-` only); the host schema adds a `^[^{};<>\\]*$` pattern and a length cap. A hand-crafted CSS injection cannot close a declaration or start a rule.
|
|
153
|
+
- **The plugin configuration card**: in Settings → Plugins → Plugin configuration, reading and writing DSH's own settings document.
|
|
154
|
+
- **Two font stacks**, one for the body and one for code, each a fallback list that may be empty (= leave DSH's fonts alone).
|
|
155
|
+
- **A font picker**: the built-in monospace / CJK / Latin / generic groups, the machine's installed families on Chromium, a "use <name>" entry for anything not listed, and every row rendered in its own family.
|
|
156
|
+
- **Western/CJK split (simple mode)**: the stack becomes a western slot and a CJK slot while every other entry and the order stay untouched; advanced mode is the full chip editor with drag reordering and a live preview.
|
|
157
|
+
- **A global size offset** (−3 to +6 px, stacking with DSH's own size slider) and **a global weight** (300–600, 400 meaning unchanged).
|
|
158
|
+
- **Sliders write on release**: nothing is written while dragging, and the readout holds until the host confirms.
|
|
159
|
+
- **Bilingual copy**; family names are sanitized so they cannot close a declaration or open a rule.
|
|
171
160
|
|
|
172
161
|
### Fixed
|
|
173
|
-
- **The card crashed on first render** (a release blocker):
|
|
174
|
-
- **Three
|
|
175
|
-
- **
|
|
176
|
-
- **
|
|
177
|
-
- **
|
|
178
|
-
|
|
179
|
-
- **The size-token regex missed `--dsh-content-font-size`** (the name has no `-font-` segment) and its `-secondary` variants.
|
|
180
|
-
- **Timers now go through `globalThis`** instead of assuming `window` carries them.
|
|
162
|
+
- **The card crashed on its first render** (a release blocker): the subscribe method was passed bare, losing `this`, so the card never appeared on the plugin configuration page.
|
|
163
|
+
- **Three problems in the size offset**: the body itself was not scaled, a refresh multiplied the plugin's own declarations as if they were the baseline, and derived sizes were scaled twice.
|
|
164
|
+
- **The family did not reach the conversation area or the sidebars** (those areas declare the font variable themselves).
|
|
165
|
+
- **The weight was snapped to the nearest hundred**: any integer now applies as chosen.
|
|
166
|
+
- **Family sanitizing was too permissive.**
|
|
167
|
+
|