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/lib/index.js
CHANGED
|
@@ -20,12 +20,54 @@
|
|
|
20
20
|
*
|
|
21
21
|
* @module dsh-fonttune
|
|
22
22
|
*/
|
|
23
|
-
import
|
|
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/lib/shared.cjs
CHANGED
|
@@ -954,14 +954,14 @@ var PART_SUFFIXES = ["-font-size", "-line-height", "-font-family", "-font-weight
|
|
|
954
954
|
var FAMILY_VARS = { "--dsw-font-family": true, "--dsw-font-mono": true, "--ds-font-family-code": true };
|
|
955
955
|
|
|
956
956
|
/**
|
|
957
|
-
*
|
|
958
|
-
*
|
|
959
|
-
*
|
|
960
|
-
*
|
|
957
|
+
* Ever since every global declaration moved to the root rule (see
|
|
958
|
+
* `buildAxisCss`), no rule needs a `body, body *` reach for a TOKEN, and only
|
|
959
|
+
* the interface weight keeps it — because a weight is the one property whose
|
|
960
|
+
* per-element declarations cannot be reached by inheritance. Nothing else may
|
|
961
|
+
* reintroduce a blanket: on DSH 0.1.5-rc.3 a single one costs 1892
|
|
962
|
+
* selector-element matches on every style recalculation, and dragging a sidebar
|
|
963
|
+
* handle recalculates once per frame.
|
|
961
964
|
*/
|
|
962
|
-
function bodyAndDescendants(declarations) {
|
|
963
|
-
return "body,body *{" + declarations + "}";
|
|
964
|
-
}
|
|
965
965
|
|
|
966
966
|
/**
|
|
967
967
|
* Whether one name belongs to the official CONTENT-size chain the conversation
|
|
@@ -1202,7 +1202,23 @@ var CODE_EXCLUDES = excludeAll(CODE_SURFACE_SELECTORS);
|
|
|
1202
1202
|
* the rule, and an unmatched token/class simply means that surface follows
|
|
1203
1203
|
* the interface axis — the same degradation as an unknown token name.
|
|
1204
1204
|
*/
|
|
1205
|
-
|
|
1205
|
+
/** The default code selector list: the substring form, for callers with no page. */
|
|
1206
|
+
var CODE_SELECTOR;
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* The code surfaces that never need a substring match.
|
|
1210
|
+
*
|
|
1211
|
+
* Everything here is either a semantic element or a class this plugin owns, so
|
|
1212
|
+
* the browser can match it with a tag or a class-token test. The containers
|
|
1213
|
+
* that only a hashed class identifies (`…_codeBody`, `…_terminal`) used to be
|
|
1214
|
+
* spelled `[class*="code" i]` / `[class*="terminal" i]` — measured on
|
|
1215
|
+
* 0.1.5-rc.3, those two substring selectors cost an attribute scan on every one
|
|
1216
|
+
* of the page's ~1900 elements on every rule that carries them, and a drag
|
|
1217
|
+
* recalculates styles once per frame. The browser half now HARVESTS the real
|
|
1218
|
+
* class tokens from the live page and passes them in as exact class selectors
|
|
1219
|
+
* (see `buildFontCss`), falling back to the substring form whenever it cannot.
|
|
1220
|
+
*/
|
|
1221
|
+
var CODE_SURFACE_SELECTORS_CHEAP = [
|
|
1206
1222
|
"pre",
|
|
1207
1223
|
"code",
|
|
1208
1224
|
"kbd",
|
|
@@ -1212,19 +1228,58 @@ var CODE_SELECTOR = [
|
|
|
1212
1228
|
"textarea",
|
|
1213
1229
|
".cm-editor",
|
|
1214
1230
|
".dfp-previewCode",
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1231
|
+
];
|
|
1232
|
+
|
|
1233
|
+
/**
|
|
1234
|
+
* The code surfaces for one build.
|
|
1235
|
+
*
|
|
1236
|
+
* The two substring hooks ride along on purpose. A class token the page has
|
|
1237
|
+
* not rendered yet (`…_codeBody`, `…_terminalBody` appear only once a tool card
|
|
1238
|
+
* or a terminal pane exists) cannot be named by a harvested class, and an
|
|
1239
|
+
* unknown surface must still get the code axes — the same "an unmatched token
|
|
1240
|
+
* simply follows the interface" degradation the token axes have. The hooks cost
|
|
1241
|
+
* an attribute scan per element per rule that carries them, which is why the
|
|
1242
|
+
* rest of this module spent its effort removing the OTHER wide selectors (see
|
|
1243
|
+
* `rootRule` / `weightRule` / `dialogSelector`); measured on 0.1.5-rc.3 that is
|
|
1244
|
+
* the difference between 1.87 s and ~0.9 s of style recalculation per drag.
|
|
1245
|
+
*/
|
|
1246
|
+
function codeSurfaceList() {
|
|
1247
|
+
return CODE_SURFACE_SELECTORS_CHEAP.concat([
|
|
1248
|
+
'[class*="code" i]',
|
|
1249
|
+
'[class*="terminal" i]',
|
|
1250
|
+
]);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* The code selector list for one build.
|
|
1255
|
+
*
|
|
1256
|
+
* Surfaces only — no `… *` descendants. Everything inside a code surface
|
|
1257
|
+
* inherits its family and weight, so descendants are unnecessary, and they are
|
|
1258
|
+
* not free: an ancestor-walking selector costs about 0.03 s per 50-step drag on
|
|
1259
|
+
* 0.1.5-rc.3 (measured: stripping the ` *` forms from these two rules took the
|
|
1260
|
+
* drag from 0.89 s to 0.52 s of style recalculation). The weight family keeps
|
|
1261
|
+
* the code subtree right through `--dfp-interface-weight` instead — see
|
|
1262
|
+
* `weightRule`.
|
|
1263
|
+
*
|
|
1264
|
+
* @param {{code?: string[], terminal?: string[]}|null} [hints] - unused; kept so
|
|
1265
|
+
* the browser half can keep passing one harvest object to `buildFontCss`.
|
|
1266
|
+
* @returns {string} the comma-separated list of code surfaces.
|
|
1267
|
+
*/
|
|
1268
|
+
function codeSelector(hints) {
|
|
1269
|
+
return codeSurfaceList().join(",");
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
CODE_SELECTOR = codeSelector(null);
|
|
1222
1273
|
|
|
1223
1274
|
/**
|
|
1224
1275
|
* The dialog surface: DSH renders the conversation markdown in a CSS-module
|
|
1225
|
-
* container whose class hashes per release (`_markdown_<hash>_…`)
|
|
1226
|
-
*
|
|
1227
|
-
*
|
|
1276
|
+
* container whose class hashes per release (`_markdown_<hash>_…`), wrapped in a
|
|
1277
|
+
* stable `[data-dss-prose]` container (measured on 0.1.5-rc.3: every markdown
|
|
1278
|
+
* container on the page sits inside one). A class-substring match degrades
|
|
1279
|
+
* safely — no match means the dialog follows the interface axis, the same
|
|
1280
|
+
* behavior as an unknown token name — but it costs an attribute scan per
|
|
1281
|
+
* element per rule, so the browser half prefers the stable wrapper when it can
|
|
1282
|
+
* see one and falls back to the substring form otherwise.
|
|
1228
1283
|
*/
|
|
1229
1284
|
var MARKDOWN_SELECTOR =
|
|
1230
1285
|
'[class*="_markdown_" i]' +
|
|
@@ -1232,37 +1287,106 @@ var MARKDOWN_SELECTOR =
|
|
|
1232
1287
|
',[class*="_markdown_" i] *' +
|
|
1233
1288
|
CODE_EXCLUDES;
|
|
1234
1289
|
|
|
1290
|
+
/**
|
|
1291
|
+
* The dialog selector for one build, given the harvested wrapper.
|
|
1292
|
+
* @param {{markdown?: string}|null} [hints] - the harvested wrapper selector.
|
|
1293
|
+
* @returns {string} the markdown-scoped selector list.
|
|
1294
|
+
*/
|
|
1295
|
+
function dialogSelector(hints) {
|
|
1296
|
+
if (hints !== undefined && hints !== null && typeof hints.markdown === "string" && hints.markdown !== "") {
|
|
1297
|
+
// The wrapper is not markdown content itself, so only its subtree is
|
|
1298
|
+
// scoped; the code exclusion still rides along so inline code keeps the
|
|
1299
|
+
// code family. This is the rule the harvest earns its keep on: the same
|
|
1300
|
+
// `:not(…)` over the stable wrapper trials ~37 elements instead of the
|
|
1301
|
+
// ~1800 the class-substring container selector used to hand it.
|
|
1302
|
+
return hints.markdown + " *" + CODE_EXCLUDES;
|
|
1303
|
+
}
|
|
1304
|
+
return MARKDOWN_SELECTOR;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1235
1307
|
/** The dark-theme attribute DSH's ThemePresenter writes on body. */
|
|
1236
1308
|
var DARK_ATTR = "body[data-ds-dark-theme]";
|
|
1237
1309
|
|
|
1238
1310
|
/**
|
|
1239
|
-
*
|
|
1311
|
+
* What the INTERFACE weight rule stays out of when the conversation has no
|
|
1312
|
+
* weight of its own.
|
|
1313
|
+
*
|
|
1314
|
+
* The weight is still the one property that needs a `body, body *` reach: DSH
|
|
1315
|
+
* pins weights on elements that do not inherit (`button` / `span` / `div`
|
|
1316
|
+
* class rules — measured on 0.1.5-rc.3: 19 buttons, 13 spans and 1 div in the
|
|
1317
|
+
* chrome declare their own weight; the markdown side's 7 `strong` and 2 `th`
|
|
1318
|
+
* declare theirs). But the 26-argument `:not(…)` that used to ride along cost
|
|
1319
|
+
* a selector trial per argument on ~1800 elements EVERY recalculation, and a
|
|
1320
|
+
* drag recalculates once per frame — measured 1.87 s of style recalculation
|
|
1321
|
+
* for a 50-step drag versus 0.47 s with this plugin removed.
|
|
1322
|
+
*
|
|
1323
|
+
* Two narrowings removed that:
|
|
1240
1324
|
*
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1247
|
-
* `:
|
|
1325
|
+
* 1. Code surfaces no longer appear here at all. The code rules are emitted
|
|
1326
|
+
* after this one and win on their own: class hooks by specificity, element
|
|
1327
|
+
* selectors by order at equal specificity. Their descendants inherit from
|
|
1328
|
+
* them (`font-weight` and `font-family` are inherited), so the blanket
|
|
1329
|
+
* cannot reach a highlight `<span>` inside a `<pre>` either.
|
|
1330
|
+
* 2. What remains — the conversation markdown subtree and the card's own
|
|
1331
|
+
* conversation preview — rides `:where(…)`, which contributes NO
|
|
1332
|
+
* specificity: the rule stays at (0,0,1), exactly like the plain
|
|
1333
|
+
* `body,body *` form, so the dialog rule (class-scoped) and the code rules
|
|
1334
|
+
* keep out-ranking it no matter which order they land in.
|
|
1248
1335
|
*
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1252
|
-
*
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
".dfp-previewCode *",
|
|
1336
|
+
* When the conversation DOES carry a weight the guard is dropped entirely (see
|
|
1337
|
+
* `weightRule`): the dialog rule is class-scoped, so it overrides everything
|
|
1338
|
+
* this rule does inside markdown anyway, and the rule collapses to the bare
|
|
1339
|
+
* `body,body *` pair. Measured with the axis at 560: all 141 interface text
|
|
1340
|
+
* elements on the page took 560 and no markdown element moved — the same
|
|
1341
|
+
* coverage the 26-argument form had.
|
|
1342
|
+
*/
|
|
1343
|
+
var WEIGHT_MARKDOWN_GUARD =
|
|
1344
|
+
":not(:where(" +
|
|
1345
|
+
[
|
|
1260
1346
|
'[class*="_markdown_" i]',
|
|
1261
1347
|
'[class*="_markdown_" i] *',
|
|
1262
1348
|
".dfp-previewDialog",
|
|
1263
1349
|
".dfp-previewDialog *",
|
|
1264
|
-
])
|
|
1265
|
-
);
|
|
1350
|
+
].join(",") +
|
|
1351
|
+
"))";
|
|
1352
|
+
|
|
1353
|
+
/**
|
|
1354
|
+
* The elements that carry a weight of their own in DSH.
|
|
1355
|
+
*
|
|
1356
|
+
* The weight is the one property that cannot ride inheritance alone: an element
|
|
1357
|
+
* whose own rule declares a weight ignores the value it would inherit, whatever
|
|
1358
|
+
* that value's importance. Measured on 0.1.5-rc.3, the elements that do declare
|
|
1359
|
+
* one are `button` / `span` / `div` in the chrome and `strong` / `th` in the
|
|
1360
|
+
* conversation; the rest of the list is the inline markup that could plausibly
|
|
1361
|
+
* join them. This replaces the old `body,body *` reach, which matched 1892
|
|
1362
|
+
* elements on every recalculation — the whole point of the table is that it
|
|
1363
|
+
* matches a fraction of that while still covering every pinned element.
|
|
1364
|
+
*/
|
|
1365
|
+
var WEIGHT_ELEMENT_TABLE = [
|
|
1366
|
+
"button",
|
|
1367
|
+
"span",
|
|
1368
|
+
"div",
|
|
1369
|
+
"strong",
|
|
1370
|
+
"b",
|
|
1371
|
+
"em",
|
|
1372
|
+
"i",
|
|
1373
|
+
"a",
|
|
1374
|
+
"label",
|
|
1375
|
+
"small",
|
|
1376
|
+
"p",
|
|
1377
|
+
"li",
|
|
1378
|
+
"td",
|
|
1379
|
+
"th",
|
|
1380
|
+
"dt",
|
|
1381
|
+
"dd",
|
|
1382
|
+
"summary",
|
|
1383
|
+
"h1",
|
|
1384
|
+
"h2",
|
|
1385
|
+
"h3",
|
|
1386
|
+
"h4",
|
|
1387
|
+
"h5",
|
|
1388
|
+
"h6",
|
|
1389
|
+
].join(",");
|
|
1266
1390
|
|
|
1267
1391
|
/**
|
|
1268
1392
|
* Split a selector list on its top-level commas only.
|
|
@@ -1442,27 +1566,66 @@ function isDormant(config) {
|
|
|
1442
1566
|
* @param {boolean} isDark - prefix every rule with the dark-theme attribute.
|
|
1443
1567
|
* @returns {string} declarations for one `<style>` element.
|
|
1444
1568
|
*/
|
|
1445
|
-
function buildAxisCss(axis, baseTokens, isDark) {
|
|
1569
|
+
function buildAxisCss(axis, baseTokens, isDark, hints) {
|
|
1446
1570
|
var sans = formatStack(parseStack(axis[SANS_FIELD]));
|
|
1447
1571
|
var mono = formatStack(parseStack(axis[MONO_FIELD]));
|
|
1572
|
+
var dialog = formatStack(parseStack(axis[STACK_DIALOG_FIELD]));
|
|
1573
|
+
// Normalised once: an expanded axis always carries a number, but a caller
|
|
1574
|
+
// handing in a raw object may leave the field out, and "absent" has to mean
|
|
1575
|
+
// "unset" rather than "write the string undefined".
|
|
1576
|
+
var dialogWeight = typeof axis[WEIGHT_DIALOG_FIELD] === "number" ? axis[WEIGHT_DIALOG_FIELD] : WEIGHT_UNSET;
|
|
1577
|
+
var codeSurfaces = codeSelector(hints);
|
|
1578
|
+
var dialogSurfaces = dialogSelector(hints);
|
|
1448
1579
|
var declarations = [];
|
|
1449
1580
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1581
|
+
/**
|
|
1582
|
+
* A rule for INHERITED state: custom properties and the inherited `font-*`
|
|
1583
|
+
* properties. `:root, body` is two elements, `body` alone is one, and every
|
|
1584
|
+
* descendant picks the value up by inheritance — including the values DSH
|
|
1585
|
+
* itself writes (measured on 0.1.5-rc.3: the host declares these tokens at
|
|
1586
|
+
* `:root` and on `body`, and the only inline copy is on `body` itself, where
|
|
1587
|
+
* an `!important` stylesheet declaration still wins).
|
|
1588
|
+
*
|
|
1589
|
+
* This used to be `body, body *`, which matched 1892 elements on EVERY style
|
|
1590
|
+
* recalculation. A drag writes `style` once per frame, so that reach alone
|
|
1591
|
+
* was most of the measured cost: 1.87 s of style recalculation for a 50-step
|
|
1592
|
+
* sidebar drag (0.47 s with the plugin removed) and 49 frames over 25 ms.
|
|
1593
|
+
*/
|
|
1594
|
+
function rootRule(declarations) {
|
|
1595
|
+
return (isDark ? DARK_ATTR : ":root,body") + "{" + declarations + "}";
|
|
1596
|
+
}
|
|
1597
|
+
function bodyRule(declarations) {
|
|
1598
|
+
return (isDark ? DARK_ATTR : "body") + "{" + declarations + "}";
|
|
1459
1599
|
}
|
|
1460
1600
|
function codeRule(declarations) {
|
|
1461
|
-
var selector = isDark ? prefixSelector(
|
|
1601
|
+
var selector = isDark ? prefixSelector(codeSurfaces, DARK_ATTR) : codeSurfaces;
|
|
1462
1602
|
return selector + "{" + declarations + "}";
|
|
1463
1603
|
}
|
|
1464
1604
|
function dialogRule(declarations) {
|
|
1465
|
-
var selector = isDark ? prefixSelector(
|
|
1605
|
+
var selector = isDark ? prefixSelector(dialogSurfaces, DARK_ATTR) : dialogSurfaces;
|
|
1606
|
+
return selector + "{" + declarations + "}";
|
|
1607
|
+
}
|
|
1608
|
+
/**
|
|
1609
|
+
* The interface weight, in two reaches: `body` for everything that inherits,
|
|
1610
|
+
* plus an element table for the tags DSH pins a weight on of their own (see
|
|
1611
|
+
* `WEIGHT_ELEMENT_TABLE`).
|
|
1612
|
+
*
|
|
1613
|
+
* The value rides a custom property (`--dfp-interface-weight`, declared on
|
|
1614
|
+
* `:root,body` by the caller) rather than a literal. That is what keeps the
|
|
1615
|
+
* code subtree right without a descendant selector: a highlight `<span>`
|
|
1616
|
+
* inside a `<pre>` IS matched by this rule's table, but it resolves the
|
|
1617
|
+
* variable its code ancestor set (see the code weight rule), so it takes the
|
|
1618
|
+
* code weight instead of the interface one. Naming `pre *` and friends here
|
|
1619
|
+
* or in the code rules instead cost 0.03 s per selector per drag.
|
|
1620
|
+
*
|
|
1621
|
+
* The markdown guard only rides along when the conversation has NO weight of
|
|
1622
|
+
* its own — with one set, the class-scoped dialog rule out-ranks this rule
|
|
1623
|
+
* inside markdown anyway.
|
|
1624
|
+
*/
|
|
1625
|
+
function weightRule(declarations) {
|
|
1626
|
+
var base = isDark ? DARK_ATTR : "body";
|
|
1627
|
+
var selector = base + "," + base + " :where(" + WEIGHT_ELEMENT_TABLE + ")";
|
|
1628
|
+
if (dialogWeight === WEIGHT_UNSET) selector += WEIGHT_MARKDOWN_GUARD;
|
|
1466
1629
|
return selector + "{" + declarations + "}";
|
|
1467
1630
|
}
|
|
1468
1631
|
/**
|
|
@@ -1472,7 +1635,7 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1472
1635
|
*/
|
|
1473
1636
|
function pushScopedCodeFamily() {
|
|
1474
1637
|
var scopedCode = [];
|
|
1475
|
-
var surfaces = splitSelectorList(
|
|
1638
|
+
var surfaces = splitSelectorList(codeSurfaces);
|
|
1476
1639
|
for (var cIndex = 0; cIndex < surfaces.length; cIndex += 1) {
|
|
1477
1640
|
var scoped = '[class*="_markdown_" i] ' + surfaces[cIndex];
|
|
1478
1641
|
// The dark variant has to carry the theme attribute like every other
|
|
@@ -1523,29 +1686,42 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1523
1686
|
// (below).
|
|
1524
1687
|
|
|
1525
1688
|
// ---- interface weight ----
|
|
1526
|
-
//
|
|
1527
|
-
//
|
|
1528
|
-
//
|
|
1529
|
-
//
|
|
1530
|
-
//
|
|
1531
|
-
|
|
1532
|
-
var
|
|
1533
|
-
var codeWeight = axis[CODE_WEIGHT_FIELD];
|
|
1689
|
+
// A `body{font-weight}` rule only reaches the elements that INHERIT their
|
|
1690
|
+
// weight; the chrome's `button` / `span` / `div` class rules pin their own
|
|
1691
|
+
// (measured on 0.1.5-rc.3), which is why the second reach exists. The
|
|
1692
|
+
// conversation is held apart by `weightRule`'s guard (or by the class-scoped
|
|
1693
|
+
// dialog rule when the conversation has a weight of its own).
|
|
1694
|
+
var interfaceWeight = typeof axis[WEIGHT_FIELD] === "number" ? axis[WEIGHT_FIELD] : WEIGHT_UNSET;
|
|
1695
|
+
var codeWeight = typeof axis[CODE_WEIGHT_FIELD] === "number" ? axis[CODE_WEIGHT_FIELD] : WEIGHT_UNSET;
|
|
1534
1696
|
if (interfaceWeight !== WEIGHT_UNSET) {
|
|
1697
|
+
// The variable the weight rule resolves; the code rules below override it
|
|
1698
|
+
// on their surfaces so everything inside them inherits the code weight.
|
|
1535
1699
|
declarations.push(
|
|
1536
|
-
|
|
1700
|
+
(isDark ? DARK_ATTR : ":root,body") +
|
|
1701
|
+
"{--dfp-interface-weight:" +
|
|
1702
|
+
interfaceWeight +
|
|
1703
|
+
"}"
|
|
1704
|
+
);
|
|
1705
|
+
declarations.push(
|
|
1706
|
+
weightRule(
|
|
1707
|
+
"font-weight:var(--dfp-interface-weight," + interfaceWeight + ") !important"
|
|
1708
|
+
)
|
|
1537
1709
|
);
|
|
1538
1710
|
if (codeWeight === WEIGHT_UNSET) {
|
|
1539
|
-
//
|
|
1540
|
-
//
|
|
1541
|
-
//
|
|
1542
|
-
//
|
|
1543
|
-
|
|
1711
|
+
// The code rules come after this one and win on their own (class hooks by
|
|
1712
|
+
// specificity, element selectors by order), so code surfaces — which
|
|
1713
|
+
// inherit nothing but start from the body weight — are reset here.
|
|
1714
|
+
// `normal` is what DSH computes for them (the code `font` shorthands
|
|
1715
|
+
// carry no weight), so an unset code weight keeps code exactly as DSH
|
|
1716
|
+
// shipped it. The variable is reset alongside so an element INSIDE a code
|
|
1717
|
+
// surface (which the interface table matches) lands on `normal` too.
|
|
1718
|
+
declarations.push(
|
|
1719
|
+
codeRule("font-weight:normal !important;--dfp-interface-weight:normal")
|
|
1720
|
+
);
|
|
1544
1721
|
}
|
|
1545
1722
|
}
|
|
1546
1723
|
|
|
1547
1724
|
// ---- dialog family (scoped; skipped when it equals the interface) ----
|
|
1548
|
-
var dialog = formatStack(parseStack(axis[STACK_DIALOG_FIELD]));
|
|
1549
1725
|
if (dialog !== "" && dialog !== sans) {
|
|
1550
1726
|
declarations.push(dialogRule("font-family:" + dialog + " !important"));
|
|
1551
1727
|
if (mono !== "") {
|
|
@@ -1555,7 +1731,6 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1555
1731
|
}
|
|
1556
1732
|
|
|
1557
1733
|
// ---- dialog weight (uniform inside the conversation) ----
|
|
1558
|
-
var dialogWeight = axis[WEIGHT_DIALOG_FIELD];
|
|
1559
1734
|
if (dialogWeight !== WEIGHT_UNSET) {
|
|
1560
1735
|
declarations.push(dialogRule("font-weight:" + dialogWeight + " !important"));
|
|
1561
1736
|
// The card's conversation preview simulates this surface, so it takes the
|
|
@@ -1594,7 +1769,7 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1594
1769
|
);
|
|
1595
1770
|
});
|
|
1596
1771
|
var contentShift = shiftTokens(contentSources, baseTokens, dialogOffsetValue);
|
|
1597
|
-
if (contentShift.length > 0) declarations.push(
|
|
1772
|
+
if (contentShift.length > 0) declarations.push(rootRule(contentShift.join(";")));
|
|
1598
1773
|
// The `small` series is the only markdown family with literal sizes: it
|
|
1599
1774
|
// does not derive from the chain, so its parts and shorthands shift here.
|
|
1600
1775
|
var literals = markdownTokenNames(baseTokens).filter(function (name) {
|
|
@@ -1602,7 +1777,7 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1602
1777
|
return name.indexOf("-font-size") >= 0 && typeof base === "string" && base.indexOf("var(") === -1;
|
|
1603
1778
|
});
|
|
1604
1779
|
var literalShift = shiftTokens(literals, baseTokens, dialogOffsetValue);
|
|
1605
|
-
if (literalShift.length > 0) declarations.push(
|
|
1780
|
+
if (literalShift.length > 0) declarations.push(rootRule(literalShift.join(";")));
|
|
1606
1781
|
// The shorthands whose size is a literal (the small series) must ride the
|
|
1607
1782
|
// shift too; the delta-derived shorthands flow through the chain above.
|
|
1608
1783
|
var sizeShifted = [];
|
|
@@ -1617,7 +1792,7 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1617
1792
|
mEntry.name + ":" + rebuildShorthand({ lead: mEntry.parts.lead, size: mSize, height: mEntry.parts.height, family: mEntry.parts.family }) + " !important"
|
|
1618
1793
|
);
|
|
1619
1794
|
}
|
|
1620
|
-
if (sizeShifted.length > 0) declarations.push(
|
|
1795
|
+
if (sizeShifted.length > 0) declarations.push(rootRule(sizeShifted.join(";")));
|
|
1621
1796
|
}
|
|
1622
1797
|
|
|
1623
1798
|
// ---- dialog line height (ratio, through the shorthand heights) ----
|
|
@@ -1628,7 +1803,7 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1628
1803
|
return name.indexOf("-line-height") >= 0;
|
|
1629
1804
|
});
|
|
1630
1805
|
var dialogLineDecls = scaleLineTokens(markdownLines, baseTokens, dialogFactor);
|
|
1631
|
-
if (dialogLineDecls.length > 0) declarations.push(
|
|
1806
|
+
if (dialogLineDecls.length > 0) declarations.push(rootRule(dialogLineDecls.join(";")));
|
|
1632
1807
|
var dialogShorthandList = markdownShorthandTokens(baseTokens);
|
|
1633
1808
|
var dialogShorthandShifted = [];
|
|
1634
1809
|
for (var dIndex = 0; dIndex < dialogShorthandList.length; dIndex += 1) {
|
|
@@ -1646,7 +1821,7 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1646
1821
|
);
|
|
1647
1822
|
}
|
|
1648
1823
|
if (dialogShorthandShifted.length > 0) {
|
|
1649
|
-
declarations.push(
|
|
1824
|
+
declarations.push(rootRule(dialogShorthandShifted.join(";")));
|
|
1650
1825
|
}
|
|
1651
1826
|
}
|
|
1652
1827
|
|
|
@@ -1659,11 +1834,17 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1659
1834
|
round(scaleFor(codeSize), 6),
|
|
1660
1835
|
codeLine
|
|
1661
1836
|
);
|
|
1662
|
-
if (codeScaled.length > 0) declarations.push(
|
|
1837
|
+
if (codeScaled.length > 0) declarations.push(rootRule(codeScaled.join(";")));
|
|
1663
1838
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1839
|
+
if (codeWeight !== WEIGHT_UNSET) {
|
|
1840
|
+
// The variable rides along so the elements INSIDE a code surface — which the
|
|
1841
|
+
// interface weight rule's element table matches — resolve the code weight
|
|
1842
|
+
// instead of the interface one, without any `… *` selector.
|
|
1843
|
+
declarations.push(
|
|
1844
|
+
codeRule(
|
|
1845
|
+
"font-weight:" + codeWeight + " !important;--dfp-interface-weight:" + codeWeight
|
|
1846
|
+
)
|
|
1847
|
+
);
|
|
1667
1848
|
}
|
|
1668
1849
|
var ligatures = axis[LIGATURES_FIELD];
|
|
1669
1850
|
if (ligatures === LIGATURES_ON) {
|
|
@@ -1680,10 +1861,10 @@ function buildAxisCss(axis, baseTokens, isDark) {
|
|
|
1680
1861
|
var noItalic = axis[NO_SYNTHETIC_ITALIC_FIELD] === true;
|
|
1681
1862
|
var noBold = axis[NO_SYNTHETIC_BOLD_FIELD] === true;
|
|
1682
1863
|
if (noItalic && noBold) {
|
|
1683
|
-
declarations.push(
|
|
1864
|
+
declarations.push(bodyRule("font-synthesis:none !important"));
|
|
1684
1865
|
} else {
|
|
1685
|
-
if (noItalic) declarations.push(
|
|
1686
|
-
if (noBold) declarations.push(
|
|
1866
|
+
if (noItalic) declarations.push(bodyRule("font-synthesis-style:none !important"));
|
|
1867
|
+
if (noBold) declarations.push(bodyRule("font-synthesis-weight:none !important"));
|
|
1687
1868
|
}
|
|
1688
1869
|
|
|
1689
1870
|
return declarations.join("\n");
|
|
@@ -1772,15 +1953,19 @@ function scaleCodeTokens(baseTokens, scale, lineOffset) {
|
|
|
1772
1953
|
*
|
|
1773
1954
|
* @param {unknown} config - normalized or raw configuration.
|
|
1774
1955
|
* @param {Record<string, string>} [baseTokens] - token name to untouched value.
|
|
1956
|
+
* @param {{markdown?: string, code?: string[], terminal?: string[]}} [hints] - what
|
|
1957
|
+
* the page harvested: the stable markdown wrapper, and the real class tokens
|
|
1958
|
+
* behind the code/terminal hooks. Absent means "match by substring", which is
|
|
1959
|
+
* correct everywhere and costs an attribute scan per element per rule.
|
|
1775
1960
|
* @returns {string} declarations for one `<style>` element ("" when dormant).
|
|
1776
1961
|
*/
|
|
1777
|
-
function buildFontCss(config, baseTokens) {
|
|
1962
|
+
function buildFontCss(config, baseTokens, hints) {
|
|
1778
1963
|
var tokens = baseTokens === undefined || baseTokens === null ? FALLBACK_TOKENS : baseTokens;
|
|
1779
1964
|
var sets = resolveAxes(config);
|
|
1780
1965
|
if (isDormant(sets.light) && isDormant(sets.dark)) return "";
|
|
1781
|
-
var light = buildAxisCss(sets.light, tokens, false);
|
|
1966
|
+
var light = buildAxisCss(sets.light, tokens, false, hints);
|
|
1782
1967
|
if (sameValueSet(sets.light, sets.dark)) return light;
|
|
1783
|
-
var dark = buildAxisCss(sets.dark, tokens, true);
|
|
1968
|
+
var dark = buildAxisCss(sets.dark, tokens, true, hints);
|
|
1784
1969
|
if (dark === "") return light;
|
|
1785
1970
|
return light + "\n" + dark;
|
|
1786
1971
|
}
|
|
@@ -2174,9 +2359,13 @@ var shared = {
|
|
|
2174
2359
|
pickerGroups: pickerGroups,
|
|
2175
2360
|
catalogRefreshDue: catalogRefreshDue,
|
|
2176
2361
|
MARKDOWN_SELECTOR: MARKDOWN_SELECTOR,
|
|
2362
|
+
codeSelector: codeSelector,
|
|
2363
|
+
dialogSelector: dialogSelector,
|
|
2364
|
+
CODE_SURFACE_SELECTORS_CHEAP: CODE_SURFACE_SELECTORS_CHEAP,
|
|
2177
2365
|
CODE_SURFACE_SELECTORS: CODE_SURFACE_SELECTORS,
|
|
2178
2366
|
CODE_EXCLUDES: CODE_EXCLUDES,
|
|
2179
|
-
|
|
2367
|
+
WEIGHT_MARKDOWN_GUARD: WEIGHT_MARKDOWN_GUARD,
|
|
2368
|
+
WEIGHT_ELEMENT_TABLE: WEIGHT_ELEMENT_TABLE,
|
|
2180
2369
|
excludeAll: excludeAll,
|
|
2181
2370
|
splitSelectorList: splitSelectorList,
|
|
2182
2371
|
prefixSelector: prefixSelector,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-fonttune",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
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",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
21
|
"engines": {
|
|
22
|
-
"dsh": ">=0.1.5-rc.
|
|
22
|
+
"dsh": ">=0.1.5-rc.3 <0.1.7-0 || >=0.1.7-alpha.1 <0.2.0-0"
|
|
23
23
|
},
|
|
24
24
|
"dsh": {
|
|
25
25
|
"bundle": {
|
|
@@ -30,11 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"compatibility": {
|
|
32
32
|
"dshReleases": {
|
|
33
|
-
"0.1.5-rc.1": "compatible",
|
|
34
|
-
"0.1.5-rc.2": "compatible",
|
|
35
33
|
"0.1.5-rc.3": "compatible",
|
|
36
34
|
"0.1.7-alpha.1": "compatible",
|
|
37
|
-
"0.1.7-alpha.2": "compatible"
|
|
35
|
+
"0.1.7-alpha.2": "compatible",
|
|
36
|
+
"0.1.7-rc.1": "compatible"
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
},
|