dsh-fonttune 0.2.4 → 0.2.5

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 CHANGED
@@ -2,27 +2,23 @@
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] - 2026-09-23
6
+
7
+ ### Fixed
8
+ - **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.
9
+
5
10
  ## [0.2.4] - 2026-09-23
6
11
 
7
12
  ### Added
8
- - **One package installs on both host lines**: `0.1.5-rc.1` – `rc.3` (the host's `settings.installSection` plus the client's `settingsScope`) and `0.1.7-alpha.1` – `alpha.2` (the host's `settings.configure({auto:true}, ctx.fiber)` plus the client's `configForms`). The plugin looks at which dialect the running host offers, and **when neither is there it only applies its styles and registers no card** rather than parking the whole package. `package.json` now declares `engines.dsh` as the semver prerelease range `>=0.1.5-rc.1 <0.1.7-0 || >=0.1.7-alpha.1 <0.2.0-0` (a plain `>=0.1.5-rc.1` never matches `0.1.7-alpha.1`), and `dsh.compatibility.dshReleases` lists the five host lines that were actually exercised.
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.
13
+ - **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.
14
+ - **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.
15
15
 
16
- ### Changed
17
- - **No more `require` of host internals**: the alpha exports `IconPlusOutlineRegular` / `IconPlusOutlineMedium` and not the rc's `IconPlusOutline16`, so the `primitives` require is gone and the plus icon needed here is an inline SVG.
18
- - **What rc.3 actually changed, measured** (so a later release can decide whether it needs a branch): hashing all 728 js/css files under `lib/` + `dist/` across rc.2 and rc.3 leaves **exactly 2 files different** — `@deepseek-ai/cosmokit/lib/index.js` (13336 → 17160 B, gaining the volatile primitives `createVolatile` / `isVolatile` / `volatileEntries` / `updateVolatile` and `Symbol.for("cosmokit.volatile.write")`) and `dsh-client-ui-sidebar/lib/client.js` (a build stamp only, `0.1.5-rc.2-449af19` → `0.1.5-rc.3-a5380f3`). The internal `@deepseek-ai/*` dependencies moved to `^0.1.5-rc.3`, and `@deepseek-ai/schemastery` / `@deepseek-ai/cordis` went from `^` to exact pins (3.18.2 / 4.0.2). **No typography, theme or settings-API change**, so the rc behaviour needs no branch for rc.3. The rc line's own schemastery 3.18.2 has **no** `.volatile()` (the alpha's 3.18.4 does), so the volatile mark on the root schema has to be **added when supported and skipped when not**.
19
- - The compatibility table gained 0.2.4, and both READMEs describe how the runtime handles the two dialects.
16
+ ### Fixed
17
+ - **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.
18
+ - **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.
20
19
 
21
- ### Tests
22
- - `test/run.mjs` (91 checks): the card's slot allow-list gained `plugins.item` / `plugins.bundle.config`, and the case is now "registers the card in every seat a supported host may declare", asserting the three seats plus the `plugins.item` id, label and component shape (summary from `card.description`, the page returning an object).
23
- - `test/render-card.mjs` (23 checks): rendering resolves its seat as `settings.plugin.item` and falls back to `plugins.item`, so both lines' shapes are exercised.
24
- - Live (managed instance + headless Edge): `host-line-probe` passes on `0.1.5-rc.2`, `0.1.5-rc.3` and `0.1.7-alpha.2` (insert-style loading), and once more on alpha.2 loaded as a bundle. On the alpha the `codeLigatures: 2` driven from the card lands in `profiles/dfp/cordis.patch.yml`; on the rc line it lands in `settings.yaml`.
25
- - **CI caught a case the local dialect cannot show**: three schema cases in `test/run.mjs` read fields straight off `schema({…}).sans`. The release CI installs the newest schemastery (3.18.4, which has `.volatile()`), and a resolved field is then a **reference** rather than a value — all three were green locally (3.18.2) and red in CI. The host half now exports `plainConfigValue`, those cases read the config **the way the plugin reads it**, and each dialect's shape is asserted: with the modifier a field is a reference that has to be unwrapped, without it the field is the value. The devDependency moved to `^3.18.4` so local and CI exercise the same dialect (the rc line's unmarked shape is covered live on rc.2 / rc.3 by the probe).
20
+ ### Changed
21
+ - 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
22
 
27
23
  ## [0.2.3] - 2026-09-22
28
24
 
package/CHANGELOG.md CHANGED
@@ -2,27 +2,23 @@
2
2
 
3
3
  **dsh-fonttune** 的重要变更都记录在这里。英文版见 [CHANGELOG.en.md](CHANGELOG.en.md)。
4
4
 
5
+ ## [0.2.5] - 2026-09-23
6
+
7
+ ### 修复
8
+ - **以「链接到源码目录」的方式安装时,0.1.5-rc.x 上的配置卡片与字体一起消失**:插件会用自己目录里的 schema 库副本,而不是 DSH 自带的那一份;副本的较新版本给配置加了 rc 线宿主处理不了的标记,设置命名空间因此根本没有注册出去——设置页看不到卡片、注入的样式是空的、字体毫无变化。现在插件始终使用 DSH 自带的那一份,链接安装与正常安装在行为上完全一致。
9
+
5
10
  ## [0.2.4] - 2026-09-23
6
11
 
7
12
  ### 新增
8
- - **一个包同时装上两条宿主线**:`0.1.5-rc.1` – `rc.3`(宿主 `settings.installSection` + 客户端 `settingsScope` 那一套)与 `0.1.7-alpha.1` – `alpha.2`(宿主 `settings.configure({auto:true}, ctx.fiber)` + 客户端 `configForms` 那一套)。插件在运行时看宿主到底给了哪一套,**两条都不在就只装样式、不注册卡片**,不会把整包 park 住。`package.json` 的 `engines.dsh` 写成语义化预发布区间 `>=0.1.5-rc.1 <0.1.7-0 || >=0.1.7-alpha.1 <0.2.0-0`(写 `>=0.1.5-rc.1` 是匹配不到 `0.1.7-alpha.1` 的),`dsh.compatibility.dshReleases` 逐条列出五条实测过的宿主线。
9
- - **卡片在两条线的三个座位上都能出现**,由宿主自己决定渲染哪一个:rc 的 `settings.plugin.item`(按设置命名空间认领)、alpha 的 `plugins.item`(插件页的插件条目,`id: fonttune`、标签取字典里的 `card.title`、摘要用 `card.description`、详情页才渲染整张卡——**DSH 自己的 shell / agent-loop / subagent / web-search 也是这么登记的**)、以及给已装 profile 包用的 `plugins.bundle.config`(按 npm 包名 `dsh-fonttune` 认领,落在该包的详情页)。
10
- - **认领命名空间改成「先等、再按签名认」**:alpha 的 `configForms.describe()` 是异步的,以前抢在它应答前按名字猜候选,猜错就会落在别的命名空间上、每次写入都被宿主拒绝(真机表现:卡片能开、一改就报「设置没有保存成功」)。现在候选为空就返回 `null`,并挂一个「待定作用域」占位(`getSnapshot`/`subscribe`/`set`/`unset` 全套),等 describe 的视图到达后按 **schema 里同时含 `uiFollowsDialog` 与 `stackDialog`** 认出自己那一个再接管(真机日志:`served namespaces []` → `matched by served schema: fonttune`)。
11
- - **宿主侧拆掉 volatile 引用**:alpha 的 `ctx.fiber.config` 里每个字段都是 cosmokit 的 volatile 引用(`{ get(), [Symbol.for("cosmokit.volatile.write")](v) }`,且被冻结),当普通对象读只会拿到空值——真机表现是**卡片第一次渲染是一整排空控件、注入的样式表 0 字节**。现在统一走 `plainConfigValue()` 深拆,`styleRow()` 拿到的是普通对象(实测样式行 973 B、`Georgia` / `520`)。
12
- - **`inject` 收窄到两条线都有的服务**(`slots` / `locale`):声明一个某条线上不存在的服务(例如 rc 才有的 `settingsScope`)会把**整包 park 住**(`pending (waiting for service: …)`),Web 直接起不来。设置作用域改为运行时用 `ctx.get()` 免声明查找,找不到就按「没有宿主层」降级。
13
- - **写设置的结果两种形状都认**:rc 的 `mutate` 即使失败也 resolve `undefined`,alpha 的 `set`/`unset` resolve 布尔(`false` = 宿主拒绝)。`followWrite` 现在把同步 `false`、resolve 成 `false` 的 thenable、以及 rejection 三种都报到预设行右侧那句提示里。
14
- - **`test/host-line-probe.mjs`:与宿主线无关的真机探针**。只断言两条线都必须成立的事——页面无报错、插件只维护**一份**样式表且已接入文档、设置真的作用到文档(设了 `DFP_EXPECT_FAMILY` / `DFP_EXPECT_WEIGHT` 就顺带断言计算样式)、设置页里能找到并渲染出 `.dfp-card`(哪条线的座位都行)、以及从卡片上改一个值真的写进宿主。已并入 `npm run verify:browser` 的只读组。设 `DFP_PROBE=1` 时插件会往控制台打诊断行(服务了哪些命名空间、认领了哪个、座位当前状态),`DFP_DUMP=1` 时探针额外按分组打印插件页的条目。
13
+ - **同一个安装包同时支持两条宿主线**:DSH **0.1.5-rc.1 – rc.3** 与 **0.1.7-alpha.1 – alpha.2** 上都能直接安装使用,不需要挑版本。插件在运行时按宿主实际提供的那一套设置接口工作(rc 线一套、alpha 线一套),两条都不在时只注入字体、不显示卡片。
14
+ - **alpha 上的配置入口**:在 0.1.7-alpha.x 上,卡片出现在侧栏「插件」页的插件条目「字体增强」里;rc.x 上仍在 设置 → 插件 → 插件配置。
15
15
 
16
- ### 变更
17
- - **不再 require 宿主内部原语**:alpha 只导出 `IconPlusOutlineRegular` / `IconPlusOutlineMedium`(没有 rc 的 `IconPlusOutline16`),所以 `primitives` 的 require 整个去掉,用到的加号改成内联 SVG。
18
- - **实测清楚 rc.3 到底改了什么**(供后续判断要不要为它开分支):把 rc.2 与 rc.3 的 `lib/` + `dist/` 共 728 个 js/css 逐个哈希比对,**只有 2 个文件不同**——`@deepseek-ai/cosmokit/lib/index.js`(13336 → 17160 B,新增 volatile 原语 `createVolatile` / `isVolatile` / `volatileEntries` / `updateVolatile` 与 `Symbol.for("cosmokit.volatile.write")`)和 `dsh-client-ui-sidebar/lib/client.js`(只差构建戳 `0.1.5-rc.2-449af19` → `0.1.5-rc.3-a5380f3`);依赖上内部包抬到 `^0.1.5-rc.3`,`@deepseek-ai/schemastery` / `@deepseek-ai/cordis` 从 `^` 改成精确钉版(3.18.2 / 4.0.2)。**排版、主题、设置 API 一处没动**,面向 rc 的行为在 rc.3 上不需要新分支。rc 线自带的 schemastery 3.18.2 **没有** `.volatile()`(alpha 的 3.18.4 才有),所以根 schema 上的 volatile 标记必须**有则加、无则跳**。
19
- - 兼容表补 0.2.4(中英两份 README 都改了运行时双方言的处理说明)。
16
+ ### 修复
17
+ - **alpha 上卡片第一次打开是一排空控件**:此前读到的是宿主的内部引用而不是配置值,注入的样式因此为空;现在首帧就是正确的字体与读数。
18
+ - **alpha 上改设置提示「设置没有保存成功」**:插件有几率把改动写到不属于自己的位置;现在只在确认是自己的配置之后才写入。
20
19
 
21
- ### 测试
22
- - `test/run.mjs`(91 项):卡片槽位白名单加入 `plugins.item` / `plugins.bundle.config`,那条用例改成「每个受支持宿主可能派发的座位都注册」,断言三个座位与 `plugins.item` 的 id / 标签 / 组件形态(摘要取 `card.description`,页面交出一个对象)。
23
- - `test/render-card.mjs`(23 项):渲染取值改为「`settings.plugin.item` 不在就取 `plugins.item`」,两条线的座位各跑一遍。
24
- - 真机(受管实例 + 无头 Edge):`0.1.5-rc.2`、`0.1.5-rc.3`、`0.1.7-alpha.2` 各跑一次 `host-line-probe` 通过(插入式装载);alpha.2 另跑一次 bundle 式装载通过。alpha 上确认从卡片改的 `codeLigatures: 2` 真的写进 `profiles/dfp/cordis.patch.yml`,rc 上确认落在 `settings.yaml`。
25
- - **CI 抓到一条本地测不出来的用例**:`test/run.mjs` 里三条 schema 用例直接读 `schema({…}).sans` 这类字段。发布 CI 装的是最新的 schemastery(3.18.4,带 `.volatile()`),于是解析出来的字段是**引用**而不是值,三条在本地(3.18.2)全绿、在 CI 全红。现在宿主半把 `plainConfigValue` 导出,这三条按**插件读配置的同一方式**取值,并断言两种方言各自的形态:有修饰符时字段是引用、拆开后才是值;没有修饰符时字段本身就是值。devDependency 同时抬到 `^3.18.4`,让本地与 CI 跑同一个方言(rc 线那套无修饰符的形态由真机探针在 rc.2 / rc.3 上覆盖)。
20
+ ### 变更
21
+ - 兼容声明与中英 README 的兼容表更新到 0.2.4,逐条列出五条实测过的宿主线(0.1.5-rc.1 / rc.2 / rc.3、0.1.7-alpha.1 / alpha.2),并写清两条线各自的设置入口。
26
22
 
27
23
  ## [0.2.3] - 2026-09-22
28
24
 
package/lib/index.js CHANGED
@@ -20,12 +20,54 @@
20
20
  *
21
21
  * @module dsh-fonttune
22
22
  */
23
- import z from "@deepseek-ai/schemastery";
23
+ import { createRequire } from "node:module";
24
+ import { dirname, sep } from "node:path";
25
+ import { fileURLToPath } from "node:url";
24
26
  // Default import, not named: `shared.cjs` is a CommonJS module, and Node's
25
27
  // CJS-to-ESM interop only guarantees `module.exports` as the default binding
26
28
  // (statically-detected named exports would break this half at boot).
27
29
  import shared from "./shared.cjs";
28
30
 
31
+ /**
32
+ * The schema library, taken from the HOST whenever the host can be reached.
33
+ *
34
+ * `@deepseek-ai/schemastery` is an optional peer dependency — DSH supplies the
35
+ * schema library, this package never ships its own copy into a profile. Node's
36
+ * bare-specifier resolution does not know that: when this package is linked or
37
+ * junctioned into a profile (the development setup) the specifier resolves
38
+ * inside this package's own `node_modules`, i.e. to the devDependency. That is
39
+ * a SECOND instance of the library, and — worse — a newer one than the running
40
+ * host may carry: its `.volatile()` marking lands on a schema the rc line's
41
+ * settings controller cannot digest, so the namespace is never served (no card
42
+ * in the settings sheet, an empty first-frame stylesheet). Measured on
43
+ * 0.1.5-rc.3: linked → nothing, installed → works.
44
+ *
45
+ * Resolving from the host first keeps one instance and one dialect, which is
46
+ * exactly what an installed package gets. `process.argv[1]` is the host's own
47
+ * entry (its `node_modules` holds the authoritative copy); anything resolving
48
+ * back inside THIS package is skipped, so the offline suites — which have no
49
+ * host — still fall through to the devDependency.
50
+ * @returns {object} the schemastery export.
51
+ */
52
+ function loadSchemastery() {
53
+ const own = dirname(dirname(fileURLToPath(import.meta.url))) + sep;
54
+ const bases = [process.argv[1], fileURLToPath(import.meta.url)];
55
+ for (const base of bases) {
56
+ if (typeof base !== "string" || base === "") continue;
57
+ try {
58
+ const from = createRequire(base);
59
+ if (!from.resolve("@deepseek-ai/schemastery").startsWith(own)) {
60
+ return from("@deepseek-ai/schemastery");
61
+ }
62
+ } catch {
63
+ // Not resolvable from that anchor: try the next one.
64
+ }
65
+ }
66
+ return createRequire(import.meta.url)("@deepseek-ai/schemastery");
67
+ }
68
+
69
+ const z = loadSchemastery();
70
+
29
71
  const {
30
72
  buildFontCss,
31
73
  CODE_LINE_HEIGHT_FIELD,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-fonttune",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Font plugin for the DeepSeek Harness Web GUI: the conversation gets its own font, size, line height and weight, the interface font and weight follow it, code keeps its own axis with ligature control, and whole setups save as presets.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",