dsh-fonttune 0.1.6 → 0.2.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.en.md +29 -0
- package/CHANGELOG.md +29 -0
- package/README.en.md +23 -14
- package/README.md +22 -13
- package/lib/client.js +2855 -525
- package/lib/index.js +143 -27
- package/lib/shared.cjs +1361 -265
- package/package.json +4 -4
package/lib/index.js
CHANGED
|
@@ -10,32 +10,43 @@
|
|
|
10
10
|
* 2. Contribute a `<style>` row to the served index. The browser half applies
|
|
11
11
|
* the same declarations once it activates, but that is after the shell has
|
|
12
12
|
* already painted; injecting here is what keeps the first frame from
|
|
13
|
-
* flashing DSH's default typography.
|
|
13
|
+
* flashing DSH's default typography. When `perTheme` is on, the row carries
|
|
14
|
+
* the dark theme's rules prefixed with the dark-theme attribute, so the
|
|
15
|
+
* first paint of each theme already uses that theme's own values.
|
|
14
16
|
*
|
|
15
17
|
* @module dsh-fonttune
|
|
16
18
|
*/
|
|
17
19
|
import z from "@deepseek-ai/schemastery";
|
|
18
20
|
// Default import, not named: `shared.cjs` is a CommonJS module, and Node's
|
|
19
21
|
// CJS-to-ESM interop only guarantees `module.exports` as the default binding
|
|
20
|
-
// (
|
|
22
|
+
// (statically-detected named exports would break this half at boot).
|
|
21
23
|
import shared from "./shared.cjs";
|
|
22
24
|
|
|
23
25
|
const {
|
|
24
26
|
buildFontCss,
|
|
27
|
+
CODE_LINE_HEIGHT_FIELD,
|
|
25
28
|
CODE_SIZE_FIELD,
|
|
26
29
|
CODE_WEIGHT_FIELD,
|
|
30
|
+
DARK_VALUES_FIELD,
|
|
27
31
|
FALLBACK_TOKENS,
|
|
32
|
+
FEATURES_FIELD,
|
|
33
|
+
LIGATURES_FIELD,
|
|
34
|
+
LINE_HEIGHT_DIALOG_FIELD,
|
|
35
|
+
LINE_HEIGHT_FIELD,
|
|
36
|
+
MAX_FEATURES_LENGTH,
|
|
37
|
+
MAX_STACK_LENGTH,
|
|
28
38
|
MONO_FIELD,
|
|
29
39
|
NAMESPACE,
|
|
30
40
|
normalizeConfig,
|
|
41
|
+
NO_SYNTHETIC_BOLD_FIELD,
|
|
42
|
+
NO_SYNTHETIC_ITALIC_FIELD,
|
|
43
|
+
PER_THEME_FIELD,
|
|
31
44
|
SANS_FIELD,
|
|
45
|
+
SIZE_DIALOG_FIELD,
|
|
32
46
|
SIZE_FIELD,
|
|
33
|
-
|
|
34
|
-
|
|
47
|
+
STACK_DIALOG_FIELD,
|
|
48
|
+
WEIGHT_DIALOG_FIELD,
|
|
35
49
|
WEIGHT_FIELD,
|
|
36
|
-
WEIGHT_MAX,
|
|
37
|
-
WEIGHT_MIN,
|
|
38
|
-
WEIGHT_UNSET,
|
|
39
50
|
} = shared;
|
|
40
51
|
|
|
41
52
|
/**
|
|
@@ -49,14 +60,22 @@ const SAFE_STACK = /^[^{};<>\\]*$/;
|
|
|
49
60
|
*/
|
|
50
61
|
const MAX_STACK = 200;
|
|
51
62
|
|
|
63
|
+
/**
|
|
64
|
+
* A `font-feature-settings` value may only carry feature tags, on/off/number
|
|
65
|
+
* and the entry separators — anything else cannot appear in a valid value.
|
|
66
|
+
*/
|
|
67
|
+
const SAFE_FEATURES = /^[a-zA-Z0-9"' ,]*$/;
|
|
68
|
+
|
|
52
69
|
/**
|
|
53
70
|
* The durable settings section.
|
|
54
71
|
*
|
|
55
72
|
* Every field defaults to "leave DSH alone": an empty stack injects no family
|
|
56
|
-
* rule, a zero offset injects no size rule
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
73
|
+
* rule, a zero offset injects no size rule, a ratio of 100 injects no
|
|
74
|
+
* line-height rule, and a zero weight injects no weight rule (an unset code
|
|
75
|
+
* weight still writes the rule that keeps code out of the interface/dialog
|
|
76
|
+
* weights). A dialog field left at its neutral value follows the interface
|
|
77
|
+
* value. Installing the plugin therefore changes nothing until the user asks
|
|
78
|
+
* for something.
|
|
60
79
|
*/
|
|
61
80
|
export const Config = z.object({
|
|
62
81
|
[SANS_FIELD]: z
|
|
@@ -64,7 +83,15 @@ export const Config = z.object({
|
|
|
64
83
|
.max(MAX_STACK)
|
|
65
84
|
.pattern(SAFE_STACK)
|
|
66
85
|
.default("")
|
|
67
|
-
.description("CSS font-family list for the
|
|
86
|
+
.description("CSS font-family list for the interface text"),
|
|
87
|
+
[STACK_DIALOG_FIELD]: z
|
|
88
|
+
.string()
|
|
89
|
+
.max(MAX_STACK)
|
|
90
|
+
.pattern(SAFE_STACK)
|
|
91
|
+
.default("")
|
|
92
|
+
.description(
|
|
93
|
+
"CSS font-family list for the conversation markdown; empty follows the interface stack"
|
|
94
|
+
),
|
|
68
95
|
[MONO_FIELD]: z
|
|
69
96
|
.string()
|
|
70
97
|
.max(MAX_STACK)
|
|
@@ -73,36 +100,121 @@ export const Config = z.object({
|
|
|
73
100
|
.description("CSS font-family list for code and monospaced text"),
|
|
74
101
|
[SIZE_FIELD]: z
|
|
75
102
|
.number()
|
|
76
|
-
.min(SIZE_MIN)
|
|
77
|
-
.max(SIZE_MAX)
|
|
103
|
+
.min(shared.SIZE_MIN)
|
|
104
|
+
.max(shared.SIZE_MAX)
|
|
78
105
|
.default(0)
|
|
79
106
|
.description(
|
|
80
|
-
`
|
|
107
|
+
`Interface font-size offset in px (${shared.SIZE_MIN}..${shared.SIZE_MAX}, 0 keeps DSH's own sizes)`
|
|
108
|
+
),
|
|
109
|
+
[SIZE_DIALOG_FIELD]: z
|
|
110
|
+
.number()
|
|
111
|
+
.min(shared.SIZE_MIN)
|
|
112
|
+
.max(shared.SIZE_MAX)
|
|
113
|
+
.default(0)
|
|
114
|
+
.description(
|
|
115
|
+
"Conversation font-size offset in px; 0 follows the interface offset"
|
|
81
116
|
),
|
|
82
117
|
[CODE_SIZE_FIELD]: z
|
|
83
118
|
.number()
|
|
84
|
-
.min(SIZE_MIN)
|
|
85
|
-
.max(SIZE_MAX)
|
|
119
|
+
.min(shared.SIZE_MIN)
|
|
120
|
+
.max(shared.SIZE_MAX)
|
|
86
121
|
.default(0)
|
|
87
122
|
.description(
|
|
88
|
-
`Code font-size offset in px (${SIZE_MIN}..${SIZE_MAX}, 0 keeps DSH's own sizes; independent of the
|
|
123
|
+
`Code font-size offset in px (${shared.SIZE_MIN}..${shared.SIZE_MAX}, 0 keeps DSH's own sizes; independent of the interface offset)`
|
|
89
124
|
),
|
|
90
125
|
[WEIGHT_FIELD]: z
|
|
91
126
|
.number()
|
|
92
|
-
.min(WEIGHT_UNSET)
|
|
93
|
-
.max(WEIGHT_MAX)
|
|
94
|
-
.default(WEIGHT_UNSET)
|
|
127
|
+
.min(shared.WEIGHT_UNSET)
|
|
128
|
+
.max(shared.WEIGHT_MAX)
|
|
129
|
+
.default(shared.WEIGHT_UNSET)
|
|
95
130
|
.description(
|
|
96
|
-
`
|
|
131
|
+
`Interface font weight (${shared.WEIGHT_MIN}..${shared.WEIGHT_MAX}, ${shared.WEIGHT_UNSET} keeps DSH's own weights; code is not affected)`
|
|
132
|
+
),
|
|
133
|
+
[WEIGHT_DIALOG_FIELD]: z
|
|
134
|
+
.number()
|
|
135
|
+
.min(shared.WEIGHT_UNSET)
|
|
136
|
+
.max(shared.WEIGHT_MAX)
|
|
137
|
+
.default(shared.WEIGHT_UNSET)
|
|
138
|
+
.description(
|
|
139
|
+
"Conversation font weight; 0 follows the interface weight"
|
|
97
140
|
),
|
|
98
141
|
[CODE_WEIGHT_FIELD]: z
|
|
99
142
|
.number()
|
|
100
|
-
.min(WEIGHT_UNSET)
|
|
101
|
-
.max(WEIGHT_MAX)
|
|
102
|
-
.default(WEIGHT_UNSET)
|
|
143
|
+
.min(shared.WEIGHT_UNSET)
|
|
144
|
+
.max(shared.WEIGHT_MAX)
|
|
145
|
+
.default(shared.WEIGHT_UNSET)
|
|
146
|
+
.description(
|
|
147
|
+
`Code font weight (${shared.WEIGHT_MIN}..${shared.WEIGHT_MAX}, ${shared.WEIGHT_UNSET} keeps DSH's own weights; independent of the interface weight)`
|
|
148
|
+
),
|
|
149
|
+
[LINE_HEIGHT_FIELD]: z
|
|
150
|
+
.number()
|
|
151
|
+
.min(shared.LINE_HEIGHT_MIN)
|
|
152
|
+
.max(shared.LINE_HEIGHT_MAX)
|
|
153
|
+
.default(shared.LINE_HEIGHT_MIN)
|
|
154
|
+
.description(
|
|
155
|
+
"Interface line-height ratio in percent (100 keeps DSH's own line heights)"
|
|
156
|
+
),
|
|
157
|
+
[LINE_HEIGHT_DIALOG_FIELD]: z
|
|
158
|
+
.number()
|
|
159
|
+
.min(shared.LINE_HEIGHT_MIN)
|
|
160
|
+
.max(shared.LINE_HEIGHT_MAX)
|
|
161
|
+
.default(shared.LINE_HEIGHT_MIN)
|
|
162
|
+
.description(
|
|
163
|
+
"Conversation line-height ratio in percent; 100 follows the interface ratio"
|
|
164
|
+
),
|
|
165
|
+
[CODE_LINE_HEIGHT_FIELD]: z
|
|
166
|
+
.number()
|
|
167
|
+
.min(shared.CODE_LINE_HEIGHT_MIN)
|
|
168
|
+
.max(shared.CODE_LINE_HEIGHT_MAX)
|
|
169
|
+
.default(0)
|
|
170
|
+
.description(
|
|
171
|
+
"Code line-height offset in px (0 keeps DSH's own line heights)"
|
|
172
|
+
),
|
|
173
|
+
[LIGATURES_FIELD]: z
|
|
174
|
+
.number()
|
|
175
|
+
.min(0)
|
|
176
|
+
.max(2)
|
|
177
|
+
.default(0)
|
|
178
|
+
.description("Code ligatures: 0 DSH default, 1 forced on, 2 forced off"),
|
|
179
|
+
[FEATURES_FIELD]: z
|
|
180
|
+
.string()
|
|
181
|
+
.max(MAX_FEATURES_LENGTH)
|
|
182
|
+
.pattern(SAFE_FEATURES)
|
|
183
|
+
.default("")
|
|
184
|
+
.description("Advanced font-feature-settings value for code"),
|
|
185
|
+
[NO_SYNTHETIC_ITALIC_FIELD]: z
|
|
186
|
+
.boolean()
|
|
187
|
+
.default(false)
|
|
188
|
+
.description("Refuse synthetic italic (stops CJK text from being tilted)"),
|
|
189
|
+
[NO_SYNTHETIC_BOLD_FIELD]: z
|
|
190
|
+
.boolean()
|
|
191
|
+
.default(false)
|
|
192
|
+
.description("Refuse synthetic bold (for faces without a real bold)"),
|
|
193
|
+
[shared.UI_FOLLOWS_FIELD]: z
|
|
194
|
+
.boolean()
|
|
195
|
+
.default(true)
|
|
103
196
|
.description(
|
|
104
|
-
|
|
197
|
+
"The interface follows the conversation: its family and weight take the conversation's values (off = the interface keeps its own)"
|
|
105
198
|
),
|
|
199
|
+
[PER_THEME_FIELD]: z
|
|
200
|
+
.boolean()
|
|
201
|
+
.default(false)
|
|
202
|
+
.description("Keep a separate value set for the dark theme"),
|
|
203
|
+
[DARK_VALUES_FIELD]: z
|
|
204
|
+
.string()
|
|
205
|
+
.max(8000)
|
|
206
|
+
.default("{}")
|
|
207
|
+
.description("Dark theme sparse overrides (JSON map of axis field to value)"),
|
|
208
|
+
[shared.PRESETS_FIELD]: z
|
|
209
|
+
.string()
|
|
210
|
+
.max(20000)
|
|
211
|
+
.default("[]")
|
|
212
|
+
.description("Saved presets (JSON array of {name, values, savedAt})"),
|
|
213
|
+
[shared.ACTIVE_PRESET_FIELD]: z
|
|
214
|
+
.string()
|
|
215
|
+
.max(shared.MAX_PRESET_NAME)
|
|
216
|
+
.default("")
|
|
217
|
+
.description("Name of the preset the card currently edits (edits auto-save into it)"),
|
|
106
218
|
});
|
|
107
219
|
|
|
108
220
|
/**
|
|
@@ -116,7 +228,11 @@ export const Config = z.object({
|
|
|
116
228
|
* @returns {StyleRow} the row the web server renders into `<head>`.
|
|
117
229
|
*/
|
|
118
230
|
function styleRow(resolved) {
|
|
119
|
-
const
|
|
231
|
+
const cfg = normalizeConfig(resolved);
|
|
232
|
+
// 0.2.0 ships the per-theme editor out; light and dark stay one value set
|
|
233
|
+
// (the dark fields remain in the schema, dormant, for a future release).
|
|
234
|
+
cfg[PER_THEME_FIELD] = false;
|
|
235
|
+
const text = buildFontCss(cfg, FALLBACK_TOKENS);
|
|
120
236
|
return { kind: "style", text };
|
|
121
237
|
}
|
|
122
238
|
|