dsh-fonttune 0.4.0 → 0.4.2
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 +16 -0
- package/CHANGELOG.md +16 -0
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/lib/client.js +187 -245
- package/lib/shared.cjs +0 -44
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +6 -2
package/CHANGELOG.en.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to **dsh-fonttune** are documented here. Chinese version: [CHANGELOG.md](CHANGELOG.md).
|
|
4
4
|
|
|
5
|
+
## [0.4.2] - 2026-10-12
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- **DSH 0.2.1-alpha.1 and 0.2.1-alpha.2 join the compatibility table**: both were verified live — the settings card, the floating panel's tweaks and their writes, and the plugin's page all behave as documented.
|
|
9
|
+
|
|
10
|
+
## [0.4.1] - 2026-10-04
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Panel weight values falling back after an adjustment**: on a freshly installed machine, reaching the target weight notch snapped straight back and a second try lost it too; the first adjustment now sticks.
|
|
14
|
+
- **Panel size and line-height values falling back after an adjustment**: the page followed for a moment and then returned to its old value, which made the line height look ineffective; an adjustment now applies at once and stays.
|
|
15
|
+
- **The unit next to a panel value vanishing while the slider moved**: it blinked away mid-drag and came back on release; it now stays put.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- **The plugin no longer appears in the Plugins page's official list**: it is not an official plugin, so that entry is gone; the whole settings card lives on the installed side now, and the panel's settings button opens that page directly.
|
|
19
|
+
- **The plugin's name and blurb on the Plugins page are Chinese**: the name reads 「字体增强(dsh-fonttune)」 and the blurb matches the settings card's wording.
|
|
20
|
+
|
|
5
21
|
## [0.4.0] - 2026-10-01
|
|
6
22
|
|
|
7
23
|
### Added
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
**dsh-fonttune** 的重要变更都记录在这里。英文版见 [CHANGELOG.en.md](CHANGELOG.en.md)。
|
|
4
4
|
|
|
5
|
+
## [0.4.2] - 2026-10-12
|
|
6
|
+
|
|
7
|
+
### 变更
|
|
8
|
+
- **兼容对照表列出 DSH 0.2.1-alpha.1 与 0.2.1-alpha.2**:两个新预发布版实测通过,设置卡片、悬浮面板调参与写入、插件页卡片均正常。
|
|
9
|
+
|
|
10
|
+
## [0.4.1] - 2026-10-04
|
|
11
|
+
|
|
12
|
+
### 修复
|
|
13
|
+
- **面板调整字重后数值跳回**:在新安装的环境里,字重调到目标档位后会立刻退回原处,再调一次也留不住;现在第一次调整就能稳定保存。
|
|
14
|
+
- **面板调整字号与行高后数值跳回**:页面刚跟着变化就退回原值,行高因此看着没有作用;现在调整即时生效并保存。
|
|
15
|
+
- **面板数值的单位在调整时短暂消失**:滑块移动过程中单位会不见又出现,现在始终保留。
|
|
16
|
+
|
|
17
|
+
### 变更
|
|
18
|
+
- **插件从插件页的官方列表移出**:本插件不是官方插件,官方栏不再有它的条目;完整设置改由「已安装」栏目的插件页承载,面板上的设置按钮直达该页。
|
|
19
|
+
- **插件页上的插件名称与简介改为中文**:名称显示为「字体增强(dsh-fonttune)」,简介与设置卡片同一句中文说明。
|
|
20
|
+
|
|
5
21
|
## [0.4.0] - 2026-10-01
|
|
6
22
|
|
|
7
23
|
### 新增
|
package/README.en.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> [中文](README.md) | **English**
|
|
4
4
|
|
|
5
|
-
**Font plugin for DeepSeek Harness (DSH)**: the **conversation** (markdown prose) gets full typographic control — font, size, line height, weight; the **interface** (settings sheet, sidebars, workspace, buttons) takes a font and a weight and **follows the conversation by default**; **code** stays its own axis with ligature and feature switches; and whole setups can be saved as **presets**. Everything lives in
|
|
5
|
+
**Font plugin for DeepSeek Harness (DSH)**: the **conversation** (markdown prose) gets full typographic control — font, size, line height, weight; the **interface** (settings sheet, sidebars, workspace, buttons) takes a font and a weight and **follows the conversation by default**; **code** stays its own axis with ligature and feature switches; and whole setups can be saved as **presets**. Everything lives in this plugin's native card (collapsed sections with summaries) and applies immediately: on the 0.2 line it is on the Plugins page under **Installed** (open this plugin's card), on the rc line under **Settings → Plugins → Plugin configuration**.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
| Plugin version | Supported DSH versions |
|
|
30
30
|
| --- | --- |
|
|
31
|
-
| **0.4.x** (latest, 0.4.
|
|
31
|
+
| **0.4.x** (latest, 0.4.1) | 0.1.7-alpha.1, 0.1.7-alpha.2, 0.1.7-rc.1, 0.1.7-rc.2, 0.2.0-rc.1, 0.2.0-rc.2, 0.2.1-alpha.1, 0.2.1-alpha.2 |
|
|
32
32
|
| 0.3.x (0.3.6) | 0.1.7-alpha.1, 0.1.7-alpha.2, 0.1.7-rc.1, 0.1.7-rc.2, 0.2.0-rc.1, 0.2.0-rc.2 |
|
|
33
33
|
| 0.2.x (0.2.8) | 0.1.5-rc.3, 0.1.7-alpha.1, 0.1.7-alpha.2, 0.1.7-rc.1, 0.1.7-rc.2 |
|
|
34
34
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> [English](README.en.md) | **中文**
|
|
4
4
|
|
|
5
|
-
**DeepSeek Harness(DSH)字体插件**:**对话**(会话 Markdown
|
|
5
|
+
**DeepSeek Harness(DSH)字体插件**:**对话**(会话 Markdown 正文)拥有完整的排印控制——字体族、字号、行高、字重;**界面**(设置页、侧栏、工作区、按钮)提供字体与字重并**默认跟随对话**;**代码**独立成轴,另有连字与特性开关;整套配置可存为**预设方案**。配置入口是**原生卡片**(分区收起 + 摘要),改完即时生效:0.2 线在「插件」页的**已安装**栏目里(点开本插件的卡片),rc 线在 设置 → 插件 → 插件配置。
|
|
6
6
|
|
|
7
7
|
## 功能
|
|
8
8
|
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
| 插件版本 | 支持的 DSH 版本 |
|
|
30
30
|
| --- | --- |
|
|
31
|
-
| **0.4.x**(最新,0.4.
|
|
31
|
+
| **0.4.x**(最新,0.4.1) | 0.1.7-alpha.1、0.1.7-alpha.2、0.1.7-rc.1、0.1.7-rc.2、0.2.0-rc.1、0.2.0-rc.2、0.2.1-alpha.1、0.2.1-alpha.2 |
|
|
32
32
|
| 0.3.x(0.3.6) | 0.1.7-alpha.1、0.1.7-alpha.2、0.1.7-rc.1、0.1.7-rc.2、0.2.0-rc.1、0.2.0-rc.2 |
|
|
33
33
|
| 0.2.x(0.2.8) | 0.1.5-rc.3、0.1.7-alpha.1、0.1.7-alpha.2、0.1.7-rc.1、0.1.7-rc.2 |
|
|
34
34
|
|
package/lib/client.js
CHANGED
|
@@ -743,48 +743,6 @@ function isPluginEntry(node) {
|
|
|
743
743
|
/** The package name the host's own navigation service opens for this plugin. */
|
|
744
744
|
var PLUGIN_BUNDLE_NAME = "dsh-fonttune";
|
|
745
745
|
|
|
746
|
-
/** The main panel id the Plugins page lives in (what `layout.selectPanel` takes). */
|
|
747
|
-
var PLUGIN_PANEL_ID = "plugins";
|
|
748
|
-
|
|
749
|
-
/**
|
|
750
|
-
* Whether a node sits inside this plugin's own floating panel.
|
|
751
|
-
*
|
|
752
|
-
* The panel can carry the plugin's own name, and a lookup that mistakes it for
|
|
753
|
-
* a host entry would click ourselves.
|
|
754
|
-
* @param {object} node - the candidate element.
|
|
755
|
-
* @returns {boolean} true when the node is ours, not the host's.
|
|
756
|
-
*/
|
|
757
|
-
function isOwnPanelNode(node) {
|
|
758
|
-
if (node === null || node === undefined) return true;
|
|
759
|
-
try {
|
|
760
|
-
return typeof node.closest === "function" && node.closest(".dfp-floatHost") !== null;
|
|
761
|
-
} catch (error) {
|
|
762
|
-
return false;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
/**
|
|
767
|
-
* THIS plugin's own card on the host's Plugins page, or null.
|
|
768
|
-
*
|
|
769
|
-
* The Plugins page publishes every entry as `data-plugin-item="<id>"` and this
|
|
770
|
-
* plugin's registration id is `fonttune`: the attribute is host-published data,
|
|
771
|
-
* so the lookup survives a language change and can never match a sentence that
|
|
772
|
-
* merely names the plugin.
|
|
773
|
-
* @param {object} doc - the document to search (a stub is fine).
|
|
774
|
-
* @returns {object|null} the card element, or null.
|
|
775
|
-
*/
|
|
776
|
-
function pluginEntryCard(doc) {
|
|
777
|
-
if (doc === null || doc === undefined || typeof doc.querySelector !== "function") return null;
|
|
778
|
-
var card = null;
|
|
779
|
-
try {
|
|
780
|
-
card = doc.querySelector("[" + PLUGIN_ITEM_ATTR + '="' + PLUGIN_ITEM_VALUE + '"]');
|
|
781
|
-
} catch (error) {
|
|
782
|
-
return null;
|
|
783
|
-
}
|
|
784
|
-
if (card === null || card === undefined) return null;
|
|
785
|
-
return isOwnPanelNode(card) ? null : card;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
746
|
/**
|
|
789
747
|
* The element that actually opens a plugin's page from its entry card.
|
|
790
748
|
*
|
|
@@ -4247,8 +4205,6 @@ var shared = {
|
|
|
4247
4205
|
PLUGIN_ITEM_ATTR: PLUGIN_ITEM_ATTR,
|
|
4248
4206
|
PLUGIN_ITEM_VALUE: PLUGIN_ITEM_VALUE,
|
|
4249
4207
|
PLUGIN_BUNDLE_NAME: PLUGIN_BUNDLE_NAME,
|
|
4250
|
-
PLUGIN_PANEL_ID: PLUGIN_PANEL_ID,
|
|
4251
|
-
pluginEntryCard: pluginEntryCard,
|
|
4252
4208
|
pluginClickTarget: pluginClickTarget,
|
|
4253
4209
|
// The client's own click step needs this: the bundle keeps this module's scope
|
|
4254
4210
|
// separate from the client's, so a bare name here is a ReferenceError there.
|
|
@@ -5558,18 +5514,103 @@ function writeSingleShot(scope, presetList, activeName, changes, options) {
|
|
|
5558
5514
|
paintLocalMove(field, changes[field]);
|
|
5559
5515
|
}
|
|
5560
5516
|
if (fields.length === 0) return;
|
|
5517
|
+
// The weight fields live on an OFFSET scale; a document written before that
|
|
5518
|
+
// scale stores absolute weights. The card adopts the scale on its first
|
|
5519
|
+
// weight write (converted values first, the marker last) and every writer has
|
|
5520
|
+
// to do the same: without the marker a written offset reads back as one of
|
|
5521
|
+
// those absolute weights and is converted away — the "+2 falls straight
|
|
5522
|
+
// back" a fresh document showed. The marker is painted locally too, so the
|
|
5523
|
+
// rows and the page agree with the write from the first frame.
|
|
5524
|
+
var raw = {};
|
|
5525
|
+
try {
|
|
5526
|
+
var snap = scope && typeof scope.getSnapshot === "function" ? scope.getSnapshot() : null;
|
|
5527
|
+
raw = snap !== null && snap !== undefined && snap.value !== null && typeof snap.value === "object" ? snap.value : {};
|
|
5528
|
+
} catch (error) {
|
|
5529
|
+
raw = {};
|
|
5530
|
+
}
|
|
5531
|
+
var touchesWeight = false;
|
|
5532
|
+
for (var weightIndex = 0; weightIndex < shared.WEIGHT_FIELDS.length; weightIndex += 1) {
|
|
5533
|
+
if (fields.indexOf(shared.WEIGHT_FIELDS[weightIndex]) >= 0) {
|
|
5534
|
+
touchesWeight = true;
|
|
5535
|
+
break;
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
var adopting = touchesWeight && raw[WEIGHT_OFFSETS_FIELD] !== true;
|
|
5539
|
+
// The preset snapshot is decided UP FRONT: it tells the adoption below
|
|
5540
|
+
// whether it still has to convert the snapshots itself, and it is the one op
|
|
5541
|
+
// a batch can be refused over.
|
|
5542
|
+
var mirrorOp = null;
|
|
5543
|
+
if (settings.mirror !== false) {
|
|
5544
|
+
var snapshot = shared.mirrorPresetSnapshot(presetList, activeName, changes);
|
|
5545
|
+
if (snapshot !== null) mirrorOp = { op: snapshot.op, path: snapshot.path, value: snapshot.value };
|
|
5546
|
+
}
|
|
5561
5547
|
var ops = [];
|
|
5548
|
+
if (adopting) {
|
|
5549
|
+
for (var adoptIndex = 0; adoptIndex < shared.WEIGHT_FIELDS.length; adoptIndex += 1) {
|
|
5550
|
+
var adoptName = shared.WEIGHT_FIELDS[adoptIndex];
|
|
5551
|
+
if (fields.indexOf(adoptName) >= 0) continue;
|
|
5552
|
+
var storedWeight = Number(raw[adoptName]);
|
|
5553
|
+
if (!isFinite(storedWeight) || storedWeight < shared.WEIGHT_MIN || storedWeight > shared.WEIGHT_MAX) continue;
|
|
5554
|
+
var convertedWeight = storedWeight - shared.WEIGHT_BASE;
|
|
5555
|
+
paintLocalMove(adoptName, convertedWeight);
|
|
5556
|
+
ops.push({ op: shared.PENDING_SET, path: [adoptName], value: convertedWeight });
|
|
5557
|
+
}
|
|
5558
|
+
paintLocalMove(WEIGHT_OFFSETS_FIELD, true);
|
|
5559
|
+
// The snapshots are converted from their STORED form, like the card does:
|
|
5560
|
+
// the normalized list is already converted and would report no change. A
|
|
5561
|
+
// mirrored write carries that converted list anyway; with no mirror to do
|
|
5562
|
+
// it, this is the only chance to keep the snapshots on the new scale.
|
|
5563
|
+
if (mirrorOp === null) {
|
|
5564
|
+
var rawPresets = raw[PRESETS_FIELD];
|
|
5565
|
+
if (typeof rawPresets === "string" && rawPresets.trim() !== "") {
|
|
5566
|
+
var parsedPresets = null;
|
|
5567
|
+
try {
|
|
5568
|
+
parsedPresets = JSON.parse(rawPresets);
|
|
5569
|
+
} catch (parseError) {
|
|
5570
|
+
parsedPresets = null;
|
|
5571
|
+
}
|
|
5572
|
+
var migratedPresets = shared.migratePresetWeights(parsedPresets);
|
|
5573
|
+
if (migratedPresets.changed) {
|
|
5574
|
+
ops.push({ op: shared.PENDING_SET, path: [PRESETS_FIELD], value: JSON.stringify(migratedPresets.presets) });
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5562
5579
|
for (var index = 0; index < fields.length; index += 1) {
|
|
5563
5580
|
var name = fields[index];
|
|
5564
5581
|
if (changes[name] === undefined) ops.push({ op: shared.PENDING_UNSET, path: [name] });
|
|
5565
5582
|
else ops.push({ op: shared.PENDING_SET, path: [name], value: changes[name] });
|
|
5566
5583
|
}
|
|
5567
|
-
if (
|
|
5568
|
-
|
|
5569
|
-
|
|
5584
|
+
if (mirrorOp !== null) ops.push(mirrorOp);
|
|
5585
|
+
if (adopting) {
|
|
5586
|
+
// Marker last: a marked document is never left holding an unconverted
|
|
5587
|
+
// absolute weight, and a stylesheet rebuild between the ops still renders
|
|
5588
|
+
// exactly as before.
|
|
5589
|
+
ops.push({ op: shared.PENDING_SET, path: [WEIGHT_OFFSETS_FIELD], value: true });
|
|
5570
5590
|
}
|
|
5591
|
+
var retried = false;
|
|
5571
5592
|
var fail = function (message) {
|
|
5593
|
+
// The axis write is the product; the preset snapshot is a nicety. A batch
|
|
5594
|
+
// the host refuses wholesale gets exactly ONE more try without the
|
|
5595
|
+
// snapshot: landing the value beats keeping the mirror. A second refusal
|
|
5596
|
+
// has nothing but the value in it, so it is the value the host declined.
|
|
5597
|
+
if (!retried && mirrorOp !== null) {
|
|
5598
|
+
retried = true;
|
|
5599
|
+
var lean = ops.filter(function (op) {
|
|
5600
|
+
return op !== mirrorOp;
|
|
5601
|
+
});
|
|
5602
|
+
probeLog("write-retry-without-mirror", { fields: fields.slice() });
|
|
5603
|
+
send(lean);
|
|
5604
|
+
return;
|
|
5605
|
+
}
|
|
5606
|
+
probeLog("write-failed", { message: message, fields: fields.slice(), adopting: adopting === true });
|
|
5572
5607
|
for (var at = 0; at < fields.length; at += 1) clearLocalMove(fields[at]);
|
|
5608
|
+
if (adopting) {
|
|
5609
|
+
for (var unadopt = 0; unadopt < shared.WEIGHT_FIELDS.length; unadopt += 1) {
|
|
5610
|
+
clearLocalMove(shared.WEIGHT_FIELDS[unadopt]);
|
|
5611
|
+
}
|
|
5612
|
+
clearLocalMove(WEIGHT_OFFSETS_FIELD);
|
|
5613
|
+
}
|
|
5573
5614
|
if (typeof settings.onError === "function") settings.onError(message);
|
|
5574
5615
|
};
|
|
5575
5616
|
var verdict = function (applied, error) {
|
|
@@ -5588,13 +5629,57 @@ function writeSingleShot(scope, presetList, activeName, changes, options) {
|
|
|
5588
5629
|
return op.path[0] + "=" + String(op.value).slice(0, 24);
|
|
5589
5630
|
}),
|
|
5590
5631
|
});
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5632
|
+
/**
|
|
5633
|
+
* Hand one batch to the host: the batch call where there is one, and one
|
|
5634
|
+
* field per call where there is not.
|
|
5635
|
+
*
|
|
5636
|
+
* @param {object[]} batch - the ops to land, in order.
|
|
5637
|
+
* @returns {void}
|
|
5638
|
+
*/
|
|
5639
|
+
var send = function (batch) {
|
|
5640
|
+
try {
|
|
5641
|
+
if (scope && typeof scope.mutate === "function" && (typeof scope.canMutate !== "function" || scope.canMutate())) {
|
|
5642
|
+
var call = scope.mutate(batch);
|
|
5643
|
+
if (call && typeof call.then === "function") {
|
|
5644
|
+
call.then(
|
|
5645
|
+
function (accepted) {
|
|
5646
|
+
verdict(accepted !== false, accepted);
|
|
5647
|
+
},
|
|
5648
|
+
function (error) {
|
|
5649
|
+
verdict(false, error);
|
|
5650
|
+
}
|
|
5651
|
+
);
|
|
5652
|
+
return;
|
|
5653
|
+
}
|
|
5654
|
+
verdict(call !== false, call);
|
|
5655
|
+
return;
|
|
5656
|
+
}
|
|
5657
|
+
} catch (error) {
|
|
5658
|
+
fail(error && error.message ? String(error.message).slice(0, 160) : String(error).slice(0, 160));
|
|
5659
|
+
return;
|
|
5660
|
+
}
|
|
5661
|
+
// A host without the batch call takes one field per call; the last verdict
|
|
5662
|
+
// is the write's.
|
|
5663
|
+
var at = 0;
|
|
5664
|
+
var step = function () {
|
|
5665
|
+
if (at >= batch.length) return;
|
|
5666
|
+
var op = batch[at];
|
|
5667
|
+
at += 1;
|
|
5668
|
+
var single = null;
|
|
5669
|
+
try {
|
|
5670
|
+
single =
|
|
5671
|
+
op.op === shared.PENDING_UNSET
|
|
5672
|
+
? scope.unset(op.path[0])
|
|
5673
|
+
: scope.set(op.path[0], op.value);
|
|
5674
|
+
} catch (error) {
|
|
5675
|
+
fail(error && error.message ? String(error.message).slice(0, 160) : String(error).slice(0, 160));
|
|
5676
|
+
return;
|
|
5677
|
+
}
|
|
5678
|
+
if (single && typeof single.then === "function") {
|
|
5679
|
+
single.then(
|
|
5596
5680
|
function (accepted) {
|
|
5597
|
-
|
|
5681
|
+
if (accepted === false) verdict(false, accepted);
|
|
5682
|
+
else step();
|
|
5598
5683
|
},
|
|
5599
5684
|
function (error) {
|
|
5600
5685
|
verdict(false, error);
|
|
@@ -5602,45 +5687,11 @@ function writeSingleShot(scope, presetList, activeName, changes, options) {
|
|
|
5602
5687
|
);
|
|
5603
5688
|
return;
|
|
5604
5689
|
}
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
}
|
|
5608
|
-
} catch (error) {
|
|
5609
|
-
fail(error && error.message ? String(error.message).slice(0, 160) : String(error).slice(0, 160));
|
|
5610
|
-
return;
|
|
5611
|
-
}
|
|
5612
|
-
// A host without the batch call takes one field per call; the last verdict
|
|
5613
|
-
// is the write's.
|
|
5614
|
-
var at = 0;
|
|
5615
|
-
var step = function () {
|
|
5616
|
-
if (at >= ops.length) return;
|
|
5617
|
-
var op = ops[at];
|
|
5618
|
-
at += 1;
|
|
5619
|
-
var single = null;
|
|
5620
|
-
try {
|
|
5621
|
-
single =
|
|
5622
|
-
op.op === shared.PENDING_UNSET
|
|
5623
|
-
? scope.unset(op.path[0])
|
|
5624
|
-
: scope.set(op.path[0], op.value);
|
|
5625
|
-
} catch (error) {
|
|
5626
|
-
fail(error && error.message ? String(error.message).slice(0, 160) : String(error).slice(0, 160));
|
|
5627
|
-
return;
|
|
5628
|
-
}
|
|
5629
|
-
if (single && typeof single.then === "function") {
|
|
5630
|
-
single.then(
|
|
5631
|
-
function (accepted) {
|
|
5632
|
-
if (accepted === false) verdict(false, accepted);
|
|
5633
|
-
else step();
|
|
5634
|
-
},
|
|
5635
|
-
function (error) {
|
|
5636
|
-
verdict(false, error);
|
|
5637
|
-
}
|
|
5638
|
-
);
|
|
5639
|
-
return;
|
|
5640
|
-
}
|
|
5690
|
+
step();
|
|
5691
|
+
};
|
|
5641
5692
|
step();
|
|
5642
5693
|
};
|
|
5643
|
-
|
|
5694
|
+
send(ops);
|
|
5644
5695
|
}
|
|
5645
5696
|
|
|
5646
5697
|
/**
|
|
@@ -7217,32 +7268,6 @@ function SwitchField(props) {
|
|
|
7217
7268
|
* the card
|
|
7218
7269
|
* ------------------------------------------------------------------ */
|
|
7219
7270
|
|
|
7220
|
-
/**
|
|
7221
|
-
* The card as DSH 0.1.7-alpha.x's Plugins page renders it.
|
|
7222
|
-
*
|
|
7223
|
-
* That page contributes one entry per plugin (`plugins.item`) and asks that one
|
|
7224
|
-
* component for two different views: a one-line `summary` for the card in the
|
|
7225
|
-
* list, and the `page` body once the entry is opened. The summary is answered
|
|
7226
|
-
* with the card's own description, and every other view is the card itself, so
|
|
7227
|
-
* the same UI serves the rc line's configuration cell and the alpha's plugin
|
|
7228
|
-
* page without a second implementation.
|
|
7229
|
-
* @param {object} props - the view asked for plus the injected face.
|
|
7230
|
-
* @returns {object|string} the card, or the one-line summary.
|
|
7231
|
-
*/
|
|
7232
|
-
function FontCardPage(props) {
|
|
7233
|
-
var t = typeof props.t === "function" ? props.t : cardT || fallbackTranslate;
|
|
7234
|
-
if (props.view === "summary") return t("card.description");
|
|
7235
|
-
// Every other view is the card as the content of a section the reader has
|
|
7236
|
-
// already opened, so it starts open: the row/entry they clicked IS the
|
|
7237
|
-
// expansion gesture, and asking for a second click on the card's own header
|
|
7238
|
-
// was the one thing left to remove on the official plugin page.
|
|
7239
|
-
return h(FontCard, {
|
|
7240
|
-
scope: props.scope || cardScope || MEMORY_SCOPE,
|
|
7241
|
-
t: t,
|
|
7242
|
-
defaultOpen: props.view !== "row",
|
|
7243
|
-
});
|
|
7244
|
-
}
|
|
7245
|
-
|
|
7246
7271
|
/**
|
|
7247
7272
|
* Render the plugin's card: the settings section dispatches this component
|
|
7248
7273
|
* under the namespace key, and the host has to serve that namespace for it to
|
|
@@ -7265,13 +7290,11 @@ function FontCard(props) {
|
|
|
7265
7290
|
// values `apply` bound are the fallback.
|
|
7266
7291
|
var t = typeof props.t === "function" ? props.t : cardT || fallbackTranslate;
|
|
7267
7292
|
var scope = props.scope || cardScope || MEMORY_SCOPE;
|
|
7268
|
-
// The
|
|
7269
|
-
// configuration cell
|
|
7270
|
-
//
|
|
7271
|
-
//
|
|
7272
|
-
//
|
|
7273
|
-
// otherwise (`defaultOpen: false`), and `FontCardPage` answers a one-line
|
|
7274
|
-
// summary request with its description instead of the card.
|
|
7293
|
+
// The per-package page on the Plugins list's INSTALLED side and the rc line's
|
|
7294
|
+
// configuration cell both render this card as the whole body of a section the
|
|
7295
|
+
// reader has ALREADY opened — the host's own row is the expansion gesture
|
|
7296
|
+
// there, so starting collapsed asked for a second click. The card therefore
|
|
7297
|
+
// starts open unless the seat says otherwise (`defaultOpen: false`).
|
|
7275
7298
|
var [open, setOpen] = useState(props.defaultOpen !== false);
|
|
7276
7299
|
var [view, setView] = useState(readViewMode);
|
|
7277
7300
|
var [expanded, setExpanded] = useState(null);
|
|
@@ -9430,115 +9453,38 @@ function mountFloatPanel(scope, t, hostCtx) {
|
|
|
9430
9453
|
}
|
|
9431
9454
|
};
|
|
9432
9455
|
|
|
9433
|
-
var ENTRY_CLICK_TRIES = 12;
|
|
9434
|
-
var ENTRY_CLICK_DELAY_MS = 150;
|
|
9435
|
-
|
|
9436
|
-
/**
|
|
9437
|
-
* Click THIS plugin's entry on the Plugins page once the page has mounted it.
|
|
9438
|
-
*
|
|
9439
|
-
* The click lands on the entry card's title button, which is the host's own
|
|
9440
|
-
* open gesture; the card itself is a list item with no handler.
|
|
9441
|
-
* @param {string} tag - a short tag for the diagnostic line.
|
|
9442
|
-
* @param {Function} done - settled with whether the click landed.
|
|
9443
|
-
*/
|
|
9444
|
-
var clickPluginEntry = function (tag, done) {
|
|
9445
|
-
var tries = 0;
|
|
9446
|
-
var step = function () {
|
|
9447
|
-
tries += 1;
|
|
9448
|
-
var target = shared.pluginClickTarget(shared.pluginEntryCard(document));
|
|
9449
|
-
if (target !== null) {
|
|
9450
|
-
var landed = shared.dispatchSynthetic(target, "click");
|
|
9451
|
-
probeLog("settings-open", { reason: tag, kind: "panel-entry", taken: landed === true });
|
|
9452
|
-
done(landed === true);
|
|
9453
|
-
return;
|
|
9454
|
-
}
|
|
9455
|
-
if (tries >= ENTRY_CLICK_TRIES) {
|
|
9456
|
-
probeLog("settings-open", { reason: tag, kind: "panel-entry", taken: false });
|
|
9457
|
-
done(false);
|
|
9458
|
-
return;
|
|
9459
|
-
}
|
|
9460
|
-
globalThis.setTimeout(step, ENTRY_CLICK_DELAY_MS);
|
|
9461
|
-
};
|
|
9462
|
-
step();
|
|
9463
|
-
};
|
|
9464
|
-
|
|
9465
9456
|
/**
|
|
9466
9457
|
* Reach this plugin's own configuration page through the HOST's navigation.
|
|
9467
9458
|
*
|
|
9468
|
-
* The
|
|
9469
|
-
*
|
|
9470
|
-
*
|
|
9471
|
-
*
|
|
9459
|
+
* The plugin manager publishes `pluginNavigation` for cross-plugin jumps
|
|
9460
|
+
* straight to a package's own page — the per-package page on the INSTALLED
|
|
9461
|
+
* side of the Plugins list, which carries this plugin's whole card. It is the
|
|
9462
|
+
* same service a shipped DSH feature calls for exactly this gesture, and it
|
|
9463
|
+
* comes before any DOM lookup: it cannot mistake text in a conversation for a
|
|
9464
|
+
* navigation entry. The lookup does not DEPEND on the service (see
|
|
9472
9465
|
* `getService`): a required dependency would park this whole package on a
|
|
9473
|
-
* host line that ships
|
|
9474
|
-
*
|
|
9475
|
-
* Order: open the Plugins panel and click this plugin's entry (its own page,
|
|
9476
|
-
* titled with its name); failing the click, the navigation service's package
|
|
9477
|
-
* page; failing both services, the caller keeps the legacy DOM chain.
|
|
9466
|
+
* host line that ships none. Failing it, the caller keeps the legacy DOM
|
|
9467
|
+
* chain.
|
|
9478
9468
|
* @param {string} tag - a short tag for the diagnostic line.
|
|
9479
|
-
* @returns {boolean} whether a host route was taken
|
|
9469
|
+
* @returns {boolean} whether a host route was taken.
|
|
9480
9470
|
*/
|
|
9481
9471
|
var openPluginPage = function (tag) {
|
|
9482
9472
|
var nav = serviceOf("pluginNavigation");
|
|
9483
|
-
var layout = serviceOf("layout");
|
|
9484
9473
|
if (globalThis.__DFP_PROBE__ === true) {
|
|
9485
|
-
var raw = function (name) {
|
|
9486
|
-
var out = {};
|
|
9487
|
-
try {
|
|
9488
|
-
var viaGet = serviceOf(name);
|
|
9489
|
-
out.get = viaGet === null || viaGet === undefined ? String(viaGet) : typeof viaGet;
|
|
9490
|
-
} catch (error) {
|
|
9491
|
-
out.get = "THREW " + String(error && error.message ? error.message : error).slice(0, 80);
|
|
9492
|
-
}
|
|
9493
|
-
try {
|
|
9494
|
-
var direct = hostCtx === null || hostCtx === undefined ? undefined : hostCtx[name];
|
|
9495
|
-
out.direct = direct === null || direct === undefined ? String(direct) : typeof direct;
|
|
9496
|
-
} catch (error) {
|
|
9497
|
-
out.direct = "THREW " + String(error && error.message ? error.message : error).slice(0, 80);
|
|
9498
|
-
}
|
|
9499
|
-
return out;
|
|
9500
|
-
};
|
|
9501
9474
|
probeLog("settings-routes", {
|
|
9502
|
-
nav:
|
|
9503
|
-
|
|
9504
|
-
slots: raw("slots"),
|
|
9505
|
-
canNav: nav !== null && nav !== undefined && typeof nav.openBundle === "function",
|
|
9506
|
-
canLayout: layout !== null && layout !== undefined && typeof layout.selectPanel === "function",
|
|
9475
|
+
nav: nav === null || nav === undefined ? String(nav) : typeof nav,
|
|
9476
|
+
canBundle: nav !== null && nav !== undefined && typeof nav.openBundle === "function",
|
|
9507
9477
|
});
|
|
9508
9478
|
}
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
layout.selectPanel(shared.PLUGIN_PANEL_ID);
|
|
9514
|
-
} catch (error) {
|
|
9515
|
-
canPanel = false;
|
|
9516
|
-
}
|
|
9517
|
-
}
|
|
9518
|
-
if (canPanel) {
|
|
9519
|
-
clickPluginEntry(tag, function (clicked) {
|
|
9520
|
-
if (clicked || !canBundle) return;
|
|
9521
|
-
try {
|
|
9522
|
-
nav.openBundle(shared.PLUGIN_BUNDLE_NAME);
|
|
9523
|
-
probeLog("settings-open", { reason: tag, kind: "plugin-navigation", taken: true });
|
|
9524
|
-
} catch (error) {
|
|
9525
|
-
warnSettingsMissing(tag, "the plugin navigation service refused the package");
|
|
9526
|
-
}
|
|
9527
|
-
});
|
|
9528
|
-
probeLog("settings-open", { reason: tag, kind: "panel-select", taken: true });
|
|
9479
|
+
if (nav === null || nav === undefined || typeof nav.openBundle !== "function") return false;
|
|
9480
|
+
try {
|
|
9481
|
+
nav.openBundle(shared.PLUGIN_BUNDLE_NAME);
|
|
9482
|
+
probeLog("settings-open", { reason: tag, kind: "plugin-navigation", taken: true });
|
|
9529
9483
|
return true;
|
|
9484
|
+
} catch (error) {
|
|
9485
|
+
warnSettingsMissing(tag, "the plugin navigation service refused the package");
|
|
9486
|
+
return false;
|
|
9530
9487
|
}
|
|
9531
|
-
if (canBundle) {
|
|
9532
|
-
try {
|
|
9533
|
-
nav.openBundle(shared.PLUGIN_BUNDLE_NAME);
|
|
9534
|
-
probeLog("settings-open", { reason: tag, kind: "plugin-navigation", taken: true });
|
|
9535
|
-
return true;
|
|
9536
|
-
} catch (error) {
|
|
9537
|
-
warnSettingsMissing(tag, "the plugin navigation service refused the package");
|
|
9538
|
-
return false;
|
|
9539
|
-
}
|
|
9540
|
-
}
|
|
9541
|
-
return false;
|
|
9542
9488
|
};
|
|
9543
9489
|
|
|
9544
9490
|
/**
|
|
@@ -9818,12 +9764,12 @@ function mountFloatPanel(scope, t, hostCtx) {
|
|
|
9818
9764
|
: DEFAULT_PRESET_NAMES.map(function (name) {
|
|
9819
9765
|
return { name: name, values: {}, savedAt: 0 };
|
|
9820
9766
|
});
|
|
9767
|
+
// No selection means NO auto-save, exactly like the card: a preset is only
|
|
9768
|
+
// written into when the reader picked one. Falling back to the first
|
|
9769
|
+
// built-in here made every panel tweak write the WHOLE preset list as well
|
|
9770
|
+
// — a second op in the same batch that a document with no presets has no
|
|
9771
|
+
// business receiving, and one more thing a host can refuse the batch over.
|
|
9821
9772
|
var activeName = config[ACTIVE_PRESET_FIELD];
|
|
9822
|
-
var known = false;
|
|
9823
|
-
for (var index = 0; index < list.length; index += 1) {
|
|
9824
|
-
if (list[index].name === activeName) known = true;
|
|
9825
|
-
}
|
|
9826
|
-
if (!known) activeName = list[0].name;
|
|
9827
9773
|
return { config: config, presetList: list, activeName: activeName, writable: snap.writable === true };
|
|
9828
9774
|
};
|
|
9829
9775
|
|
|
@@ -10573,11 +10519,11 @@ function mountFloatPanel(scope, t, hostCtx) {
|
|
|
10573
10519
|
render();
|
|
10574
10520
|
};
|
|
10575
10521
|
|
|
10576
|
-
var sliderRow = function (body, labelText,
|
|
10522
|
+
var sliderRow = function (body, labelText, format, min, max, step, value, onRelease) {
|
|
10577
10523
|
var row = el("div", "dfp-floatRow");
|
|
10578
10524
|
var label = el("div", "dfp-floatLabel");
|
|
10579
10525
|
label.appendChild(el("span", null, labelText));
|
|
10580
|
-
var shown = el("span", "dfp-floatValue",
|
|
10526
|
+
var shown = el("span", "dfp-floatValue", format(value));
|
|
10581
10527
|
label.appendChild(shown);
|
|
10582
10528
|
row.appendChild(label);
|
|
10583
10529
|
var input = el("input", "dfp-floatSlider");
|
|
@@ -10588,7 +10534,9 @@ function mountFloatPanel(scope, t, hostCtx) {
|
|
|
10588
10534
|
input.value = String(value);
|
|
10589
10535
|
input.setAttribute("aria-label", labelText);
|
|
10590
10536
|
input.addEventListener("input", function () {
|
|
10591
|
-
|
|
10537
|
+
// The resting formatter, every time: a raw number here is what made the
|
|
10538
|
+
// unit blink away the moment the slider moved.
|
|
10539
|
+
shown.textContent = format(Number(input.value));
|
|
10592
10540
|
});
|
|
10593
10541
|
input.addEventListener("change", function () {
|
|
10594
10542
|
onRelease(Number(input.value));
|
|
@@ -11031,7 +10979,9 @@ function mountFloatPanel(scope, t, hostCtx) {
|
|
|
11031
10979
|
sliderRow(
|
|
11032
10980
|
card,
|
|
11033
10981
|
t("size.dialogLabel"),
|
|
11034
|
-
|
|
10982
|
+
function (next) {
|
|
10983
|
+
return (next > 0 ? "+" + next : String(next)) + " " + t("size.unit");
|
|
10984
|
+
},
|
|
11035
10985
|
SIZE_MIN,
|
|
11036
10986
|
SIZE_MAX,
|
|
11037
10987
|
1,
|
|
@@ -11045,7 +10995,9 @@ function mountFloatPanel(scope, t, hostCtx) {
|
|
|
11045
10995
|
sliderRow(
|
|
11046
10996
|
card,
|
|
11047
10997
|
t("line.dialogLabel"),
|
|
11048
|
-
|
|
10998
|
+
function (next) {
|
|
10999
|
+
return String(next) + " " + t("line.unit");
|
|
11000
|
+
},
|
|
11049
11001
|
LINE_HEIGHT_MIN,
|
|
11050
11002
|
LINE_HEIGHT_MAX,
|
|
11051
11003
|
5,
|
|
@@ -11818,18 +11770,19 @@ function apply(ctx) {
|
|
|
11818
11770
|
* Offer the card in one slot seat.
|
|
11819
11771
|
*
|
|
11820
11772
|
* Which seat a host declares is a line difference, not a preference, and the
|
|
11821
|
-
*
|
|
11773
|
+
* two are not alternatives to pick from — each is simply inert where a host
|
|
11822
11774
|
* never declares it:
|
|
11823
11775
|
*
|
|
11824
11776
|
* * `settings.plugin.item` — up to 0.1.5-rc.x, a keyed cell under
|
|
11825
11777
|
* Settings → Plugins → Plugin configuration (keyed by the namespace);
|
|
11826
|
-
* * `plugins.
|
|
11827
|
-
* official list, which is how EVERY plugin page is contributed there (the
|
|
11828
|
-
* built-in settings pages do the same), and the seat that needs no
|
|
11829
|
-
* installed package behind it;
|
|
11830
|
-
* * `plugins.bundle.config` — the alpha's per-package page, keyed by the npm
|
|
11778
|
+
* * `plugins.bundle.config` — the per-package page, keyed by the npm
|
|
11831
11779
|
* package name, for a profile that installed this plugin as a bundle.
|
|
11832
11780
|
*
|
|
11781
|
+
* The `plugins.item` entry is deliberately NOT offered: it renders in the
|
|
11782
|
+
* Plugins page's OFFICIAL list, and this is not an official plugin. Its page
|
|
11783
|
+
* belongs on the installed side, where the per-package seat above already
|
|
11784
|
+
* carries the whole card.
|
|
11785
|
+
*
|
|
11833
11786
|
* @param {string} seat - the slot name.
|
|
11834
11787
|
* @param {object} options - the registration options that seat looks up.
|
|
11835
11788
|
* @param {Function} [component] - the component to register; the card itself
|
|
@@ -11855,17 +11808,6 @@ function apply(ctx) {
|
|
|
11855
11808
|
if (scope !== MEMORY_SCOPE) {
|
|
11856
11809
|
offerCard("settings.plugin.item", { key: NAMESPACE });
|
|
11857
11810
|
offerCard("plugins.bundle.config", { key: "dsh-fonttune" });
|
|
11858
|
-
offerCard(
|
|
11859
|
-
"plugins.item",
|
|
11860
|
-
{
|
|
11861
|
-
id: "fonttune",
|
|
11862
|
-
order: 40,
|
|
11863
|
-
label: function () {
|
|
11864
|
-
return t("card.title");
|
|
11865
|
-
},
|
|
11866
|
-
},
|
|
11867
|
-
FontCardPage
|
|
11868
|
-
);
|
|
11869
11811
|
}
|
|
11870
11812
|
}
|
|
11871
11813
|
|
package/lib/shared.cjs
CHANGED
|
@@ -734,48 +734,6 @@ function isPluginEntry(node) {
|
|
|
734
734
|
/** The package name the host's own navigation service opens for this plugin. */
|
|
735
735
|
var PLUGIN_BUNDLE_NAME = "dsh-fonttune";
|
|
736
736
|
|
|
737
|
-
/** The main panel id the Plugins page lives in (what `layout.selectPanel` takes). */
|
|
738
|
-
var PLUGIN_PANEL_ID = "plugins";
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* Whether a node sits inside this plugin's own floating panel.
|
|
742
|
-
*
|
|
743
|
-
* The panel can carry the plugin's own name, and a lookup that mistakes it for
|
|
744
|
-
* a host entry would click ourselves.
|
|
745
|
-
* @param {object} node - the candidate element.
|
|
746
|
-
* @returns {boolean} true when the node is ours, not the host's.
|
|
747
|
-
*/
|
|
748
|
-
function isOwnPanelNode(node) {
|
|
749
|
-
if (node === null || node === undefined) return true;
|
|
750
|
-
try {
|
|
751
|
-
return typeof node.closest === "function" && node.closest(".dfp-floatHost") !== null;
|
|
752
|
-
} catch (error) {
|
|
753
|
-
return false;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* THIS plugin's own card on the host's Plugins page, or null.
|
|
759
|
-
*
|
|
760
|
-
* The Plugins page publishes every entry as `data-plugin-item="<id>"` and this
|
|
761
|
-
* plugin's registration id is `fonttune`: the attribute is host-published data,
|
|
762
|
-
* so the lookup survives a language change and can never match a sentence that
|
|
763
|
-
* merely names the plugin.
|
|
764
|
-
* @param {object} doc - the document to search (a stub is fine).
|
|
765
|
-
* @returns {object|null} the card element, or null.
|
|
766
|
-
*/
|
|
767
|
-
function pluginEntryCard(doc) {
|
|
768
|
-
if (doc === null || doc === undefined || typeof doc.querySelector !== "function") return null;
|
|
769
|
-
var card = null;
|
|
770
|
-
try {
|
|
771
|
-
card = doc.querySelector("[" + PLUGIN_ITEM_ATTR + '="' + PLUGIN_ITEM_VALUE + '"]');
|
|
772
|
-
} catch (error) {
|
|
773
|
-
return null;
|
|
774
|
-
}
|
|
775
|
-
if (card === null || card === undefined) return null;
|
|
776
|
-
return isOwnPanelNode(card) ? null : card;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
737
|
/**
|
|
780
738
|
* The element that actually opens a plugin's page from its entry card.
|
|
781
739
|
*
|
|
@@ -4238,8 +4196,6 @@ var shared = {
|
|
|
4238
4196
|
PLUGIN_ITEM_ATTR: PLUGIN_ITEM_ATTR,
|
|
4239
4197
|
PLUGIN_ITEM_VALUE: PLUGIN_ITEM_VALUE,
|
|
4240
4198
|
PLUGIN_BUNDLE_NAME: PLUGIN_BUNDLE_NAME,
|
|
4241
|
-
PLUGIN_PANEL_ID: PLUGIN_PANEL_ID,
|
|
4242
|
-
pluginEntryCard: pluginEntryCard,
|
|
4243
4199
|
pluginClickTarget: pluginClickTarget,
|
|
4244
4200
|
// The client's own click step needs this: the bundle keeps this module's scope
|
|
4245
4201
|
// separate from the client's, so a bare name here is a ReferenceError there.
|
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-fonttune",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./lib/index.js",
|
|
9
9
|
"./client": "./lib/client.js",
|
|
10
|
+
"./locale/*": "./locale/*",
|
|
10
11
|
"./package.json": "./package.json"
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
13
14
|
"lib",
|
|
15
|
+
"locale",
|
|
14
16
|
"cordis.patch.yml",
|
|
15
17
|
"README.md",
|
|
16
18
|
"README.en.md",
|
|
@@ -35,7 +37,9 @@
|
|
|
35
37
|
"0.1.7-rc.1": "compatible",
|
|
36
38
|
"0.1.7-rc.2": "compatible",
|
|
37
39
|
"0.2.0-rc.1": "compatible",
|
|
38
|
-
"0.2.0-rc.2": "compatible"
|
|
40
|
+
"0.2.0-rc.2": "compatible",
|
|
41
|
+
"0.2.1-alpha.1": "compatible",
|
|
42
|
+
"0.2.1-alpha.2": "compatible"
|
|
39
43
|
}
|
|
40
44
|
}
|
|
41
45
|
},
|