dsh-fonttune 0.1.0
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.md +41 -0
- package/LICENSE +21 -0
- package/README.en.md +78 -0
- package/README.md +108 -0
- package/cordis.patch.yml +20 -0
- package/lib/client.js +2455 -0
- package/lib/index.js +124 -0
- package/lib/shared.cjs +663 -0
- package/package.json +66 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-fonttune — host half.
|
|
3
|
+
*
|
|
4
|
+
* Two jobs, both small:
|
|
5
|
+
*
|
|
6
|
+
* 1. Register the `dsh-fonttune` settings namespace so the durable values live
|
|
7
|
+
* in the Host user-settings document next to every other preference. The
|
|
8
|
+
* plugin's own composition entry is the base layer, so a value the user
|
|
9
|
+
* clears falls back to the profile's config rather than to nothing.
|
|
10
|
+
* 2. Contribute a `<style>` row to the served index. The browser half applies
|
|
11
|
+
* the same declarations once it activates, but that is after the shell has
|
|
12
|
+
* already painted; injecting here is what keeps the first frame from
|
|
13
|
+
* flashing DSH's default typography.
|
|
14
|
+
*
|
|
15
|
+
* @module dsh-fonttune
|
|
16
|
+
*/
|
|
17
|
+
import z from "@deepseek-ai/schemastery";
|
|
18
|
+
// Default import, not named: `shared.cjs` is a CommonJS module, and Node's
|
|
19
|
+
// CJS-to-ESM interop only guarantees `module.exports` as the default binding
|
|
20
|
+
// (staticky-detected named exports would break this half at boot).
|
|
21
|
+
import shared from "./shared.cjs";
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
buildFontCss,
|
|
25
|
+
FALLBACK_TOKENS,
|
|
26
|
+
MONO_FIELD,
|
|
27
|
+
NAMESPACE,
|
|
28
|
+
normalizeConfig,
|
|
29
|
+
SANS_FIELD,
|
|
30
|
+
SIZE_FIELD,
|
|
31
|
+
SIZE_MAX,
|
|
32
|
+
SIZE_MIN,
|
|
33
|
+
WEIGHT_FIELD,
|
|
34
|
+
WEIGHT_MAX,
|
|
35
|
+
WEIGHT_MIN,
|
|
36
|
+
WEIGHT_UNSET,
|
|
37
|
+
} = shared;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Characters a declaration cannot survive: they would close the declaration,
|
|
41
|
+
* the rule, or the `<style>` element the row is rendered into.
|
|
42
|
+
*/
|
|
43
|
+
const SAFE_STACK = /^[^{};<>\\]*$/;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Stack length accepted by the durable schema, in characters.
|
|
47
|
+
*/
|
|
48
|
+
const MAX_STACK = 200;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The durable settings section.
|
|
52
|
+
*
|
|
53
|
+
* Every field defaults to "leave DSH alone": an empty stack injects no family
|
|
54
|
+
* rule, a zero offset injects no size rule, and a zero weight injects no
|
|
55
|
+
* weight rule. Installing the plugin therefore changes nothing until the user
|
|
56
|
+
* asks for something.
|
|
57
|
+
*/
|
|
58
|
+
export const Config = z.object({
|
|
59
|
+
[SANS_FIELD]: z
|
|
60
|
+
.string()
|
|
61
|
+
.max(MAX_STACK)
|
|
62
|
+
.pattern(SAFE_STACK)
|
|
63
|
+
.default("")
|
|
64
|
+
.description("CSS font-family list for the UI and conversation text"),
|
|
65
|
+
[MONO_FIELD]: z
|
|
66
|
+
.string()
|
|
67
|
+
.max(MAX_STACK)
|
|
68
|
+
.pattern(SAFE_STACK)
|
|
69
|
+
.default("")
|
|
70
|
+
.description("CSS font-family list for code and monospaced text"),
|
|
71
|
+
[SIZE_FIELD]: z
|
|
72
|
+
.number()
|
|
73
|
+
.min(SIZE_MIN)
|
|
74
|
+
.max(SIZE_MAX)
|
|
75
|
+
.default(0)
|
|
76
|
+
.description(
|
|
77
|
+
`Global font-size offset in px (${SIZE_MIN}..${SIZE_MAX}, 0 keeps DSH's own sizes)`
|
|
78
|
+
),
|
|
79
|
+
[WEIGHT_FIELD]: z
|
|
80
|
+
.number()
|
|
81
|
+
.min(WEIGHT_UNSET)
|
|
82
|
+
.max(WEIGHT_MAX)
|
|
83
|
+
.default(WEIGHT_UNSET)
|
|
84
|
+
.description(
|
|
85
|
+
`Global font weight (${WEIGHT_MIN}..${WEIGHT_MAX}, ${WEIGHT_UNSET} keeps DSH's own weights)`
|
|
86
|
+
),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* One row of the structured index injection table.
|
|
91
|
+
* @typedef {{kind: "style", text: string}} StyleRow
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The style row carrying one configuration.
|
|
96
|
+
* @param {unknown} resolved - the current settings section.
|
|
97
|
+
* @returns {StyleRow} the row the web server renders into `<head>`.
|
|
98
|
+
*/
|
|
99
|
+
function styleRow(resolved) {
|
|
100
|
+
const text = buildFontCss(normalizeConfig(resolved), FALLBACK_TOKENS);
|
|
101
|
+
return { kind: "style", text };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Register the settings section and keep the index in sync with it.
|
|
106
|
+
* @param {object} ctx - host cordis context.
|
|
107
|
+
* @param {unknown} config - the plugin entry's composition config (base layer).
|
|
108
|
+
*/
|
|
109
|
+
export function apply(ctx, config) {
|
|
110
|
+
let current = () => config;
|
|
111
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
112
|
+
settingsCtx.settings.installSection(ctx, NAMESPACE, Config, config ?? {}, {
|
|
113
|
+
setSource: (source) => {
|
|
114
|
+
current = source;
|
|
115
|
+
},
|
|
116
|
+
// The row table is rebuilt on every index render and every worker boot
|
|
117
|
+
// payload, so each read is already fresh; nothing to invalidate here.
|
|
118
|
+
onChange: () => {},
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
ctx.on("webserver/index-inject", (table) => {
|
|
122
|
+
table.push(styleRow(current()));
|
|
123
|
+
});
|
|
124
|
+
}
|