sdocs-dev 1.2.0 → 1.3.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/README.md +149 -0
- package/bin/sdocs-dev.js +282 -38
- package/package.json +6 -3
- package/public/{brotli-wasm.js → brotli-wasm-v1.js} +5 -6
- package/public/css/layout.css +3 -4
- package/public/css/mobile.css +7 -0
- package/public/css/panel.css +1 -1
- package/public/css/rendered.css +184 -1
- package/public/index.html +137 -43
- package/public/sdoc.md +542 -0
- package/public/sdocs-app.js +222 -63
- package/public/sdocs-charts.js +905 -0
- package/public/sdocs-controls.js +135 -53
- package/public/sdocs-export.js +939 -61
- package/public/sdocs-slugify.js +12 -0
- package/public/sdocs-state.js +14 -0
- package/public/sdocs-styles.js +180 -254
- package/public/sdocs-theme.js +21 -19
- package/public/sdocs-write.js +4 -1
- package/public/sw.js +5 -3
- package/public/vendor/purify.min.js +3 -0
- package/server.js +51 -3
- package/public/default.md +0 -255
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// sdocs-slugify.js — Slugify heading text into URL-safe IDs
|
|
2
|
+
// Shared by sdocs-app.js (browser) and test/test-slugify.js (Node)
|
|
3
|
+
(function (exports) {
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
function slugify(text) {
|
|
7
|
+
return text.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
exports.slugify = slugify;
|
|
11
|
+
|
|
12
|
+
})(typeof module !== 'undefined' && module.exports ? module.exports : (window.SDocSlugify = {}));
|
package/public/sdocs-state.js
CHANGED
|
@@ -8,6 +8,14 @@ window.SDocs = {
|
|
|
8
8
|
currentBody: '',
|
|
9
9
|
currentMeta: {},
|
|
10
10
|
|
|
11
|
+
// Runtime-only metadata (path, fullPath) passed via &local= URL param.
|
|
12
|
+
// Stripped from the URL on load so it isn't in anything the user copies/shares.
|
|
13
|
+
localMeta: {},
|
|
14
|
+
|
|
15
|
+
// Per-doc overrides for the file-info card. Set from `styles.fileinfo` in
|
|
16
|
+
// YAML front matter; emitted back via collectStyles so they round-trip.
|
|
17
|
+
fileinfoStyles: {},
|
|
18
|
+
|
|
11
19
|
// Per-theme color state
|
|
12
20
|
activeTheme: 'light',
|
|
13
21
|
themeColors: { light: {}, dark: {} },
|
|
@@ -51,6 +59,12 @@ SDocs.contentAreaEl = document.getElementById('content-area');
|
|
|
51
59
|
SDocs.setStyleVar = function(cssVar, value) {
|
|
52
60
|
SDocs.renderedEl.style.setProperty(cssVar, value);
|
|
53
61
|
if (SDocs.writeEl) SDocs.writeEl.style.setProperty(cssVar, value);
|
|
62
|
+
// File-info card is a sibling of #rendered, so it doesn't inherit vars set
|
|
63
|
+
// on #rendered. Mirror the relevant block-cascade vars to it directly.
|
|
64
|
+
var ficEl = document.getElementById('file-info-card');
|
|
65
|
+
if (ficEl && /^--md-(fic-|block-)/.test(cssVar)) {
|
|
66
|
+
ficEl.style.setProperty(cssVar, value);
|
|
67
|
+
}
|
|
54
68
|
if (cssVar === '--md-bg') {
|
|
55
69
|
SDocs.contentAreaEl.style.setProperty('background-color', value);
|
|
56
70
|
}
|
package/public/sdocs-styles.js
CHANGED
|
@@ -11,21 +11,32 @@ const COLOR_DEFAULT = '#1c1917';
|
|
|
11
11
|
|
|
12
12
|
// Control ID → CSS variable name (color controls only)
|
|
13
13
|
const COLOR_VAR_MAP = {
|
|
14
|
-
'ctrl-color':
|
|
15
|
-
'ctrl-h-color':
|
|
16
|
-
'ctrl-h1-color':
|
|
17
|
-
'ctrl-h2-color':
|
|
18
|
-
'ctrl-h3-color':
|
|
19
|
-
'ctrl-h4-color':
|
|
20
|
-
'ctrl-p-color':
|
|
21
|
-
'ctrl-list-color':
|
|
14
|
+
'ctrl-color': '--md-color',
|
|
15
|
+
'ctrl-h-color': '--md-h-color',
|
|
16
|
+
'ctrl-h1-color': '--md-h1-color',
|
|
17
|
+
'ctrl-h2-color': '--md-h2-color',
|
|
18
|
+
'ctrl-h3-color': '--md-h3-color',
|
|
19
|
+
'ctrl-h4-color': '--md-h4-color',
|
|
20
|
+
'ctrl-p-color': '--md-p-color',
|
|
21
|
+
'ctrl-list-color': '--md-list-color',
|
|
22
|
+
// Block cascade
|
|
23
|
+
'ctrl-block-bg': '--md-block-bg',
|
|
24
|
+
'ctrl-block-text': '--md-block-text',
|
|
25
|
+
'ctrl-code-bg': ['--md-code-bg', '--md-pre-bg'],
|
|
26
|
+
'ctrl-code-color': '--md-code-color',
|
|
27
|
+
'ctrl-bq-bg': '--md-bq-bg',
|
|
28
|
+
'ctrl-bq-color': '--md-bq-color',
|
|
29
|
+
'ctrl-chart-bg': '--md-chart-bg',
|
|
30
|
+
'ctrl-chart-text': '--md-chart-text',
|
|
22
31
|
};
|
|
23
32
|
|
|
24
33
|
// Cascade tree: parent → direct children
|
|
25
34
|
const COLOR_CASCADE = {
|
|
26
|
-
'ctrl-color':
|
|
27
|
-
'ctrl-h-color':
|
|
28
|
-
'ctrl-p-color':
|
|
35
|
+
'ctrl-color': ['ctrl-h-color', 'ctrl-p-color'],
|
|
36
|
+
'ctrl-h-color': ['ctrl-h1-color', 'ctrl-h2-color', 'ctrl-h3-color', 'ctrl-h4-color'],
|
|
37
|
+
'ctrl-p-color': ['ctrl-list-color'],
|
|
38
|
+
'ctrl-block-bg': ['ctrl-code-bg', 'ctrl-bq-bg', 'ctrl-chart-bg'],
|
|
39
|
+
'ctrl-block-text': ['ctrl-code-color', 'ctrl-bq-color', 'ctrl-chart-text'],
|
|
29
40
|
};
|
|
30
41
|
|
|
31
42
|
// Control ID → { cssVar, suffix?, compound? }
|
|
@@ -51,15 +62,13 @@ const CTRL_CSS_MAP = {
|
|
|
51
62
|
'ctrl-link-color': { cssVar: '--md-link-color' },
|
|
52
63
|
'ctrl-link-decoration': { cssVar: '--md-link-decoration' },
|
|
53
64
|
'ctrl-code-font': { cssVar: '--md-code-font' },
|
|
54
|
-
'ctrl-code-bg': { cssVar: ['--md-code-bg', '--md-pre-bg'] },
|
|
55
|
-
'ctrl-code-color': { cssVar: '--md-code-color' },
|
|
56
65
|
'ctrl-bq-border-color': { cssVar: '--md-bq-border', compound: 'bq-border' },
|
|
57
66
|
'ctrl-bq-bw-num': { cssVar: '--md-bq-border', compound: 'bq-border' },
|
|
58
|
-
'ctrl-bq-bg': { cssVar: '--md-bq-bg' },
|
|
59
67
|
'ctrl-bq-size-num': { cssVar: '--md-bq-size', suffix: 'em' },
|
|
60
|
-
'ctrl-bq-color': { cssVar: '--md-bq-color' },
|
|
61
68
|
'ctrl-list-spacing-num': { cssVar: '--md-list-spacing', suffix: 'em' },
|
|
62
69
|
'ctrl-list-indent-num': { cssVar: '--md-list-indent', suffix: 'em' },
|
|
70
|
+
'ctrl-chart-accent': { cssVar: '--md-chart-accent' },
|
|
71
|
+
'ctrl-chart-palette': { cssVar: '--md-chart-palette' },
|
|
63
72
|
};
|
|
64
73
|
|
|
65
74
|
// Range ↔ Number input pairs
|
|
@@ -80,6 +89,93 @@ const RANGE_NUM_PAIRS = [
|
|
|
80
89
|
['ctrl-list-indent-range', 'ctrl-list-indent-num'],
|
|
81
90
|
];
|
|
82
91
|
|
|
92
|
+
// ═══════════════════════════════════════════════════════
|
|
93
|
+
// HSL COLOR UTILITIES
|
|
94
|
+
// ═══════════════════════════════════════════════════════
|
|
95
|
+
|
|
96
|
+
function hexToHsl(hex) {
|
|
97
|
+
if (!hex || hex.charAt(0) !== '#') return null;
|
|
98
|
+
var r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
99
|
+
var g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
100
|
+
var b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
101
|
+
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
102
|
+
var h, s, l = (max + min) / 2;
|
|
103
|
+
if (max === min) { h = s = 0; }
|
|
104
|
+
else {
|
|
105
|
+
var d = max - min;
|
|
106
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
107
|
+
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
108
|
+
else if (max === g) h = ((b - r) / d + 2) / 6;
|
|
109
|
+
else h = ((r - g) / d + 4) / 6;
|
|
110
|
+
}
|
|
111
|
+
return [h * 360, s * 100, l * 100];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function hslToHex(h, s, l) {
|
|
115
|
+
h = ((h % 360) + 360) % 360;
|
|
116
|
+
s = Math.max(0, Math.min(100, s)) / 100;
|
|
117
|
+
l = Math.max(0, Math.min(100, l)) / 100;
|
|
118
|
+
var c = (1 - Math.abs(2 * l - 1)) * s;
|
|
119
|
+
var x = c * (1 - Math.abs((h / 60) % 2 - 1));
|
|
120
|
+
var m = l - c / 2;
|
|
121
|
+
var r, g, b;
|
|
122
|
+
if (h < 60) { r = c; g = x; b = 0; }
|
|
123
|
+
else if (h < 120) { r = x; g = c; b = 0; }
|
|
124
|
+
else if (h < 180) { r = 0; g = c; b = x; }
|
|
125
|
+
else if (h < 240) { r = 0; g = x; b = c; }
|
|
126
|
+
else if (h < 300) { r = x; g = 0; b = c; }
|
|
127
|
+
else { r = c; g = 0; b = x; }
|
|
128
|
+
var toH = function(v) { var h = Math.round((v + m) * 255).toString(16); return h.length < 2 ? '0' + h : h; };
|
|
129
|
+
return '#' + toH(r) + toH(g) + toH(b);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* invertLightness(hex)
|
|
134
|
+
* Inverts a color's lightness for the opposite theme.
|
|
135
|
+
* Light colors (L>50) become dark, dark colors become light.
|
|
136
|
+
* Keeps hue and saturation, mirrors lightness around 50%.
|
|
137
|
+
* Slightly biased: dark bgs get very dark (L≈10-20), light text gets bright (L≈80-90).
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* invertLightness(hex)
|
|
141
|
+
* Generates a dark-theme counterpart for a light-theme color.
|
|
142
|
+
*
|
|
143
|
+
* Strategy: colors that look "right" in light mode get adapted for dark mode.
|
|
144
|
+
* - Very light colors (L>65): page/block backgrounds → make very dark
|
|
145
|
+
* - Very dark colors (L<20): already dark, likely intentional → keep as-is
|
|
146
|
+
* - Dark-ish colors (20<L<45): body text, headings → make light
|
|
147
|
+
* - Mid-range (45-65): accent colors → moderate shift
|
|
148
|
+
*/
|
|
149
|
+
function invertLightness(hex) {
|
|
150
|
+
var hsl = hexToHsl(hex);
|
|
151
|
+
if (!hsl) return hex;
|
|
152
|
+
var h = hsl[0], s = hsl[1], l = hsl[2];
|
|
153
|
+
var invL, invS;
|
|
154
|
+
|
|
155
|
+
if (l > 80) {
|
|
156
|
+
// Very light background → very dark
|
|
157
|
+
invL = 10 + (100 - l) * 0.3; // L95→11, L85→14, L80→16
|
|
158
|
+
invS = s * 0.7;
|
|
159
|
+
} else if (l > 65) {
|
|
160
|
+
// Light background/accent → dark
|
|
161
|
+
invL = 12 + (100 - l) * 0.4; // L70→24, L65→26
|
|
162
|
+
invS = s * 0.75;
|
|
163
|
+
} else if (l < 20) {
|
|
164
|
+
// Already very dark → keep as-is (intentional dark bg like code blocks)
|
|
165
|
+
return hex;
|
|
166
|
+
} else if (l < 40) {
|
|
167
|
+
// Dark text → make light for dark theme readability
|
|
168
|
+
invL = 65 + (40 - l) * 0.7; // L10→86, L20→79, L35→68
|
|
169
|
+
invS = s * 0.8;
|
|
170
|
+
} else {
|
|
171
|
+
// Mid-range accent → moderate inversion
|
|
172
|
+
invL = 100 - l;
|
|
173
|
+
invS = s * 0.85;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return hslToHex(h, Math.max(0, invS), Math.max(0, Math.min(100, invL)));
|
|
177
|
+
}
|
|
178
|
+
|
|
83
179
|
// ═══════════════════════════════════════════════════════
|
|
84
180
|
// PURE FUNCTIONS
|
|
85
181
|
// ═══════════════════════════════════════════════════════
|
|
@@ -91,7 +187,11 @@ const RANGE_NUM_PAIRS = [
|
|
|
91
187
|
function controlToCssVars(ctrlId, value, allValues) {
|
|
92
188
|
// Color controls go through the cascade system, not CTRL_CSS_MAP
|
|
93
189
|
if (COLOR_VAR_MAP[ctrlId]) {
|
|
94
|
-
|
|
190
|
+
var varName = COLOR_VAR_MAP[ctrlId];
|
|
191
|
+
if (Array.isArray(varName)) {
|
|
192
|
+
return varName.map(function(cv) { return { cssVar: cv, value: value }; });
|
|
193
|
+
}
|
|
194
|
+
return [{ cssVar: varName, value: value }];
|
|
95
195
|
}
|
|
96
196
|
|
|
97
197
|
const entry = CTRL_CSS_MAP[ctrlId];
|
|
@@ -165,15 +265,11 @@ function collectStyles(values, overriddenColors) {
|
|
|
165
265
|
link: { color: gv('ctrl-link-color'), decoration: gv('ctrl-link-decoration') },
|
|
166
266
|
code: {
|
|
167
267
|
font: gv('ctrl-code-font').replace(/['"]/g, '').split(',')[0].trim(),
|
|
168
|
-
background: gv('ctrl-code-bg'),
|
|
169
|
-
color: gv('ctrl-code-color'),
|
|
170
268
|
},
|
|
171
269
|
blockquote: {
|
|
172
270
|
borderColor: gv('ctrl-bq-border-color'),
|
|
173
271
|
borderWidth: gn('ctrl-bq-bw-num'),
|
|
174
|
-
background: gv('ctrl-bq-bg'),
|
|
175
272
|
fontSize: gn('ctrl-bq-size-num'),
|
|
176
|
-
color: gv('ctrl-bq-color'),
|
|
177
273
|
},
|
|
178
274
|
};
|
|
179
275
|
|
|
@@ -193,6 +289,25 @@ function collectStyles(values, overriddenColors) {
|
|
|
193
289
|
if (overriddenColors.has('ctrl-p-color')) styles.p.color = gv('ctrl-p-color');
|
|
194
290
|
if (overriddenColors.has('ctrl-list-color')) styles.list.color = gv('ctrl-list-color');
|
|
195
291
|
|
|
292
|
+
// Block cascade colors
|
|
293
|
+
if (overriddenColors.has('ctrl-block-bg') || overriddenColors.has('ctrl-block-text')) {
|
|
294
|
+
styles.blocks = {};
|
|
295
|
+
if (overriddenColors.has('ctrl-block-bg')) styles.blocks.background = gv('ctrl-block-bg');
|
|
296
|
+
if (overriddenColors.has('ctrl-block-text')) styles.blocks.color = gv('ctrl-block-text');
|
|
297
|
+
}
|
|
298
|
+
if (overriddenColors.has('ctrl-code-bg')) styles.code.background = gv('ctrl-code-bg');
|
|
299
|
+
if (overriddenColors.has('ctrl-code-color')) styles.code.color = gv('ctrl-code-color');
|
|
300
|
+
if (overriddenColors.has('ctrl-bq-bg')) styles.blockquote.background = gv('ctrl-bq-bg');
|
|
301
|
+
if (overriddenColors.has('ctrl-bq-color')) styles.blockquote.color = gv('ctrl-bq-color');
|
|
302
|
+
|
|
303
|
+
// Chart styles
|
|
304
|
+
var chartObj = {};
|
|
305
|
+
if (overriddenColors.has('ctrl-chart-accent')) chartObj.accent = gv('ctrl-chart-accent');
|
|
306
|
+
if (gv('ctrl-chart-palette') && gv('ctrl-chart-palette') !== 'monochrome') chartObj.palette = gv('ctrl-chart-palette');
|
|
307
|
+
if (overriddenColors.has('ctrl-chart-bg')) chartObj.background = gv('ctrl-chart-bg');
|
|
308
|
+
if (overriddenColors.has('ctrl-chart-text')) chartObj.textColor = gv('ctrl-chart-text');
|
|
309
|
+
if (Object.keys(chartObj).length) styles.chart = chartObj;
|
|
310
|
+
|
|
196
311
|
return styles;
|
|
197
312
|
}
|
|
198
313
|
|
|
@@ -251,17 +366,22 @@ function stylesToControls(styles) {
|
|
|
251
366
|
if (lk.color) controls['ctrl-link-color'] = lk.color;
|
|
252
367
|
if (lk.decoration) controls['ctrl-link-decoration'] = lk.decoration;
|
|
253
368
|
|
|
369
|
+
// Blocks cascade parent
|
|
370
|
+
const bl = styles.blocks || {};
|
|
371
|
+
if (bl.background) { controls['ctrl-block-bg'] = bl.background; overridden.add('ctrl-block-bg'); }
|
|
372
|
+
if (bl.color) { controls['ctrl-block-text'] = bl.color; overridden.add('ctrl-block-text'); }
|
|
373
|
+
|
|
254
374
|
const cd = styles.code || {};
|
|
255
375
|
if (cd.font) controls['ctrl-code-font'] = cd.font;
|
|
256
|
-
if (cd.background) controls['ctrl-code-bg'] = cd.background;
|
|
257
|
-
if (cd.color) controls['ctrl-code-color'] = cd.color;
|
|
376
|
+
if (cd.background) { controls['ctrl-code-bg'] = cd.background; overridden.add('ctrl-code-bg'); }
|
|
377
|
+
if (cd.color) { controls['ctrl-code-color'] = cd.color; overridden.add('ctrl-code-color'); }
|
|
258
378
|
|
|
259
379
|
const bq = styles.blockquote || {};
|
|
260
380
|
if (bq.borderColor) controls['ctrl-bq-border-color'] = bq.borderColor;
|
|
261
381
|
if (bq.borderWidth) controls['ctrl-bq-bw-num'] = bq.borderWidth;
|
|
262
|
-
if (bq.background) controls['ctrl-bq-bg'] = bq.background;
|
|
382
|
+
if (bq.background) { controls['ctrl-bq-bg'] = bq.background; overridden.add('ctrl-bq-bg'); }
|
|
263
383
|
if (bq.fontSize) controls['ctrl-bq-size-num'] = bq.fontSize;
|
|
264
|
-
if (bq.color) controls['ctrl-bq-color'] = bq.color;
|
|
384
|
+
if (bq.color) { controls['ctrl-bq-color'] = bq.color; overridden.add('ctrl-bq-color'); }
|
|
265
385
|
|
|
266
386
|
const ll = styles.list || {};
|
|
267
387
|
if (ll.spacing) controls['ctrl-list-spacing-num'] = ll.spacing;
|
|
@@ -271,6 +391,12 @@ function stylesToControls(styles) {
|
|
|
271
391
|
overridden.add('ctrl-list-color');
|
|
272
392
|
}
|
|
273
393
|
|
|
394
|
+
const ch = styles.chart || {};
|
|
395
|
+
if (ch.accent) { controls['ctrl-chart-accent'] = ch.accent; overridden.add('ctrl-chart-accent'); }
|
|
396
|
+
if (ch.palette) controls['ctrl-chart-palette'] = ch.palette;
|
|
397
|
+
if (ch.background) { controls['ctrl-chart-bg'] = ch.background; overridden.add('ctrl-chart-bg'); }
|
|
398
|
+
if (ch.textColor) { controls['ctrl-chart-text'] = ch.textColor; overridden.add('ctrl-chart-text'); }
|
|
399
|
+
|
|
274
400
|
return { controls, overriddenColors: overridden };
|
|
275
401
|
}
|
|
276
402
|
|
|
@@ -279,7 +405,9 @@ function stylesToControls(styles) {
|
|
|
279
405
|
// ═══════════════════════════════════════════════════════
|
|
280
406
|
|
|
281
407
|
var STANDALONE_COLOR_IDS = [
|
|
282
|
-
'ctrl-bg-color','ctrl-link-color',
|
|
408
|
+
'ctrl-bg-color','ctrl-link-color',
|
|
409
|
+
'ctrl-bq-border-color',
|
|
410
|
+
'ctrl-chart-accent',
|
|
283
411
|
];
|
|
284
412
|
|
|
285
413
|
var CASCADE_COLOR_IDS = Object.keys(COLOR_VAR_MAP);
|
|
@@ -291,249 +419,46 @@ var ALL_COLOR_IDS = CASCADE_COLOR_IDS.concat(STANDALONE_COLOR_IDS);
|
|
|
291
419
|
// ═══════════════════════════════════════════════════════
|
|
292
420
|
|
|
293
421
|
/**
|
|
294
|
-
*
|
|
295
|
-
* Extracts {
|
|
296
|
-
* The block is an object like { color: "#1a1a2e", h1: { color: "#c0392b" }, link: { color: "#2563eb" } }
|
|
422
|
+
* parseDarkBlock(block)
|
|
423
|
+
* Extracts { ctrlId: colorValue } from a dark: sub-object in YAML front matter.
|
|
297
424
|
*/
|
|
298
|
-
function
|
|
299
|
-
if (!block) return {
|
|
425
|
+
function parseDarkBlock(block) {
|
|
426
|
+
if (!block) return {};
|
|
300
427
|
var colors = {};
|
|
301
|
-
var overridden = new Set();
|
|
302
428
|
|
|
303
|
-
|
|
304
|
-
if (block.
|
|
305
|
-
colors['ctrl-bg-color'] = block.background;
|
|
306
|
-
overridden.add('ctrl-bg-color');
|
|
307
|
-
}
|
|
429
|
+
if (block.background) colors['ctrl-bg-color'] = block.background;
|
|
430
|
+
if (block.color) colors['ctrl-color'] = block.color;
|
|
308
431
|
|
|
309
|
-
|
|
310
|
-
if (block.color) {
|
|
311
|
-
colors['ctrl-color'] = block.color;
|
|
312
|
-
overridden.add('ctrl-color');
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Cascade colors nested in sub-objects
|
|
316
|
-
if (block.headers && block.headers.color) {
|
|
317
|
-
colors['ctrl-h-color'] = block.headers.color;
|
|
318
|
-
overridden.add('ctrl-h-color');
|
|
319
|
-
}
|
|
432
|
+
if (block.headers && block.headers.color) colors['ctrl-h-color'] = block.headers.color;
|
|
320
433
|
['h1','h2','h3','h4'].forEach(function(t) {
|
|
321
434
|
var obj = block[t];
|
|
322
|
-
if (obj && obj.color)
|
|
323
|
-
colors['ctrl-' + t + '-color'] = obj.color;
|
|
324
|
-
overridden.add('ctrl-' + t + '-color');
|
|
325
|
-
}
|
|
435
|
+
if (obj && obj.color) colors['ctrl-' + t + '-color'] = obj.color;
|
|
326
436
|
});
|
|
327
|
-
if (block.p && block.p.color)
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
if (block.list && block.list.color) {
|
|
332
|
-
colors['ctrl-list-color'] = block.list.color;
|
|
333
|
-
overridden.add('ctrl-list-color');
|
|
334
|
-
}
|
|
437
|
+
if (block.p && block.p.color) colors['ctrl-p-color'] = block.p.color;
|
|
438
|
+
if (block.list && block.list.color) colors['ctrl-list-color'] = block.list.color;
|
|
439
|
+
if (block.link && block.link.color) colors['ctrl-link-color'] = block.link.color;
|
|
335
440
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
colors['ctrl-
|
|
339
|
-
overridden.add('ctrl-link-color');
|
|
441
|
+
if (block.blocks) {
|
|
442
|
+
if (block.blocks.background) colors['ctrl-block-bg'] = block.blocks.background;
|
|
443
|
+
if (block.blocks.color) colors['ctrl-block-text'] = block.blocks.color;
|
|
340
444
|
}
|
|
341
445
|
if (block.code) {
|
|
342
|
-
if (block.code.background)
|
|
343
|
-
if (block.code.color)
|
|
446
|
+
if (block.code.background) colors['ctrl-code-bg'] = block.code.background;
|
|
447
|
+
if (block.code.color) colors['ctrl-code-color'] = block.code.color;
|
|
344
448
|
}
|
|
345
449
|
if (block.blockquote) {
|
|
346
|
-
if (block.blockquote.borderColor)
|
|
347
|
-
if (block.blockquote.background)
|
|
348
|
-
if (block.blockquote.color)
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return { colors: colors, overridden: overridden };
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* collectThemeColors(colorValues, overriddenSet)
|
|
356
|
-
* Builds the YAML sub-object for one theme (only emits overridden colors).
|
|
357
|
-
*/
|
|
358
|
-
function collectThemeColors(colorValues, overriddenSet) {
|
|
359
|
-
var block = {};
|
|
360
|
-
if (overriddenSet.has('ctrl-bg-color')) block.background = colorValues['ctrl-bg-color'];
|
|
361
|
-
if (overriddenSet.has('ctrl-color')) block.color = colorValues['ctrl-color'];
|
|
362
|
-
|
|
363
|
-
// Heading colors
|
|
364
|
-
var hasHColor = overriddenSet.has('ctrl-h-color');
|
|
365
|
-
if (hasHColor) {
|
|
366
|
-
if (!block.headers) block.headers = {};
|
|
367
|
-
block.headers.color = colorValues['ctrl-h-color'];
|
|
368
|
-
}
|
|
369
|
-
['h1','h2','h3','h4'].forEach(function(t) {
|
|
370
|
-
var id = 'ctrl-' + t + '-color';
|
|
371
|
-
if (overriddenSet.has(id)) {
|
|
372
|
-
if (!block[t]) block[t] = {};
|
|
373
|
-
block[t].color = colorValues[id];
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
// Paragraph and list
|
|
378
|
-
if (overriddenSet.has('ctrl-p-color')) {
|
|
379
|
-
if (!block.p) block.p = {};
|
|
380
|
-
block.p.color = colorValues['ctrl-p-color'];
|
|
381
|
-
}
|
|
382
|
-
if (overriddenSet.has('ctrl-list-color')) {
|
|
383
|
-
if (!block.list) block.list = {};
|
|
384
|
-
block.list.color = colorValues['ctrl-list-color'];
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Standalone colors
|
|
388
|
-
if (overriddenSet.has('ctrl-link-color')) {
|
|
389
|
-
if (!block.link) block.link = {};
|
|
390
|
-
block.link.color = colorValues['ctrl-link-color'];
|
|
391
|
-
}
|
|
392
|
-
if (overriddenSet.has('ctrl-code-bg')) {
|
|
393
|
-
if (!block.code) block.code = {};
|
|
394
|
-
block.code.background = colorValues['ctrl-code-bg'];
|
|
395
|
-
}
|
|
396
|
-
if (overriddenSet.has('ctrl-code-color')) {
|
|
397
|
-
if (!block.code) block.code = {};
|
|
398
|
-
block.code.color = colorValues['ctrl-code-color'];
|
|
399
|
-
}
|
|
400
|
-
if (overriddenSet.has('ctrl-bq-border-color')) {
|
|
401
|
-
if (!block.blockquote) block.blockquote = {};
|
|
402
|
-
block.blockquote.borderColor = colorValues['ctrl-bq-border-color'];
|
|
450
|
+
if (block.blockquote.borderColor) colors['ctrl-bq-border-color'] = block.blockquote.borderColor;
|
|
451
|
+
if (block.blockquote.background) colors['ctrl-bq-bg'] = block.blockquote.background;
|
|
452
|
+
if (block.blockquote.color) colors['ctrl-bq-color'] = block.blockquote.color;
|
|
403
453
|
}
|
|
404
|
-
if (
|
|
405
|
-
if (
|
|
406
|
-
block.
|
|
454
|
+
if (block.chart) {
|
|
455
|
+
if (block.chart.background) colors['ctrl-chart-bg'] = block.chart.background;
|
|
456
|
+
if (block.chart.textColor) colors['ctrl-chart-text'] = block.chart.textColor;
|
|
407
457
|
}
|
|
408
|
-
if (overriddenSet.has('ctrl-bq-color')) {
|
|
409
|
-
if (!block.blockquote) block.blockquote = {};
|
|
410
|
-
block.blockquote.color = colorValues['ctrl-bq-color'];
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return block;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* collectStylesDual(values, lightOverridden, darkOverridden, lightColors, darkColors)
|
|
418
|
-
* Builds full styles object with light:/dark: keys for colors, shared non-color props at top level.
|
|
419
|
-
*/
|
|
420
|
-
function collectStylesDual(values, lightOverridden, darkOverridden, lightColors, darkColors) {
|
|
421
|
-
var gv = function(id) { return values[id] || ''; };
|
|
422
|
-
var gn = function(id) { return parseFloat(values[id]) || 0; };
|
|
423
|
-
|
|
424
|
-
var styles = {
|
|
425
|
-
fontFamily: gv('ctrl-font-family').replace(/['"]/g, '').split(',')[0].trim(),
|
|
426
|
-
baseFontSize: gn('ctrl-base-size-num'),
|
|
427
|
-
lineHeight: gn('ctrl-line-height-num'),
|
|
428
|
-
headers: {
|
|
429
|
-
fontFamily: gv('ctrl-h-font-family').replace(/['"]/g, '').split(',')[0].trim(),
|
|
430
|
-
scale: gn('ctrl-h-scale-num'),
|
|
431
|
-
marginBottom: gn('ctrl-h-mb-num'),
|
|
432
|
-
},
|
|
433
|
-
h1: { fontSize: gn('ctrl-h1-size-num'), fontWeight: parseInt(gv('ctrl-h1-weight')) || 0 },
|
|
434
|
-
h2: { fontSize: gn('ctrl-h2-size-num'), fontWeight: parseInt(gv('ctrl-h2-weight')) || 0 },
|
|
435
|
-
h3: { fontSize: gn('ctrl-h3-size-num'), fontWeight: parseInt(gv('ctrl-h3-weight')) || 0 },
|
|
436
|
-
h4: { fontSize: gn('ctrl-h4-size-num'), fontWeight: parseInt(gv('ctrl-h4-weight')) || 0 },
|
|
437
|
-
p: {
|
|
438
|
-
lineHeight: gn('ctrl-p-lh-num'),
|
|
439
|
-
marginBottom: gn('ctrl-p-mb-num'),
|
|
440
|
-
},
|
|
441
|
-
link: { decoration: gv('ctrl-link-decoration') },
|
|
442
|
-
code: {
|
|
443
|
-
font: gv('ctrl-code-font').replace(/['"]/g, '').split(',')[0].trim(),
|
|
444
|
-
},
|
|
445
|
-
blockquote: {
|
|
446
|
-
borderWidth: gn('ctrl-bq-bw-num'),
|
|
447
|
-
fontSize: gn('ctrl-bq-size-num'),
|
|
448
|
-
},
|
|
449
|
-
|
|
450
|
-
};
|
|
451
458
|
|
|
452
|
-
|
|
453
|
-
spacing: gn('ctrl-list-spacing-num'),
|
|
454
|
-
indent: gn('ctrl-list-indent-num'),
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
// Build theme color blocks
|
|
458
|
-
var lightBlock = collectThemeColors(lightColors, lightOverridden);
|
|
459
|
-
var darkBlock = collectThemeColors(darkColors, darkOverridden);
|
|
460
|
-
|
|
461
|
-
// Only emit light/dark blocks if they have content
|
|
462
|
-
if (Object.keys(lightBlock).length > 0) styles.light = lightBlock;
|
|
463
|
-
if (Object.keys(darkBlock).length > 0) styles.dark = darkBlock;
|
|
464
|
-
|
|
465
|
-
return styles;
|
|
459
|
+
return colors;
|
|
466
460
|
}
|
|
467
461
|
|
|
468
|
-
/**
|
|
469
|
-
* stylesToControls(styles) — extended version
|
|
470
|
-
* Detects styles.light/styles.dark and returns per-theme data when present.
|
|
471
|
-
*/
|
|
472
|
-
var _origStylesToControls = stylesToControls;
|
|
473
|
-
|
|
474
|
-
stylesToControls = function(styles) {
|
|
475
|
-
if (!styles) return { controls: {}, overriddenColors: new Set(), hasThemeColors: false };
|
|
476
|
-
|
|
477
|
-
var hasThemeColors = !!(styles.light || styles.dark);
|
|
478
|
-
|
|
479
|
-
if (!hasThemeColors) {
|
|
480
|
-
var result = _origStylesToControls(styles);
|
|
481
|
-
result.hasThemeColors = false;
|
|
482
|
-
return result;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
// Parse non-color controls from top level (same as original, but skip colors)
|
|
486
|
-
var controls = {};
|
|
487
|
-
var overridden = new Set();
|
|
488
|
-
|
|
489
|
-
if (styles.fontFamily) controls['ctrl-font-family'] = styles.fontFamily;
|
|
490
|
-
if (styles.baseFontSize) controls['ctrl-base-size-num'] = styles.baseFontSize;
|
|
491
|
-
if (styles.lineHeight) controls['ctrl-line-height-num'] = styles.lineHeight;
|
|
492
|
-
|
|
493
|
-
var h = styles.headers || {};
|
|
494
|
-
if (h.fontFamily) controls['ctrl-h-font-family'] = h.fontFamily;
|
|
495
|
-
if (h.scale) controls['ctrl-h-scale-num'] = h.scale;
|
|
496
|
-
if (h.marginBottom) controls['ctrl-h-mb-num'] = h.marginBottom;
|
|
497
|
-
|
|
498
|
-
['h1', 'h2', 'h3', 'h4'].forEach(function(t) {
|
|
499
|
-
var hs = styles[t] || {};
|
|
500
|
-
if (hs.fontSize) controls['ctrl-' + t + '-size-num'] = hs.fontSize;
|
|
501
|
-
if (hs.fontWeight) controls['ctrl-' + t + '-weight'] = String(hs.fontWeight);
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
var p = styles.p || {};
|
|
505
|
-
if (p.lineHeight) controls['ctrl-p-lh-num'] = p.lineHeight;
|
|
506
|
-
if (p.marginBottom) controls['ctrl-p-mb-num'] = p.marginBottom;
|
|
507
|
-
|
|
508
|
-
var lk = styles.link || {};
|
|
509
|
-
if (lk.decoration) controls['ctrl-link-decoration'] = lk.decoration;
|
|
510
|
-
|
|
511
|
-
var cd = styles.code || {};
|
|
512
|
-
if (cd.font) controls['ctrl-code-font'] = cd.font;
|
|
513
|
-
|
|
514
|
-
var bq = styles.blockquote || {};
|
|
515
|
-
if (bq.borderWidth) controls['ctrl-bq-bw-num'] = bq.borderWidth;
|
|
516
|
-
if (bq.fontSize) controls['ctrl-bq-size-num'] = bq.fontSize;
|
|
517
|
-
|
|
518
|
-
var ll = styles.list || {};
|
|
519
|
-
if (ll.spacing) controls['ctrl-list-spacing-num'] = ll.spacing;
|
|
520
|
-
if (ll.indent) controls['ctrl-list-indent-num'] = ll.indent;
|
|
521
|
-
|
|
522
|
-
// Parse per-theme color blocks
|
|
523
|
-
var lightParsed = parseThemeColorBlock(styles.light);
|
|
524
|
-
var darkParsed = parseThemeColorBlock(styles.dark);
|
|
525
|
-
|
|
526
|
-
return {
|
|
527
|
-
controls: controls,
|
|
528
|
-
overriddenColors: overridden,
|
|
529
|
-
hasThemeColors: true,
|
|
530
|
-
lightColors: lightParsed.colors,
|
|
531
|
-
lightOverridden: lightParsed.overridden,
|
|
532
|
-
darkColors: darkParsed.colors,
|
|
533
|
-
darkOverridden: darkParsed.overridden,
|
|
534
|
-
};
|
|
535
|
-
};
|
|
536
|
-
|
|
537
462
|
// ═══════════════════════════════════════════════════════
|
|
538
463
|
// STRIP STYLE DEFAULTS (for shorter URLs)
|
|
539
464
|
// ═══════════════════════════════════════════════════════
|
|
@@ -619,12 +544,13 @@ exports.RANGE_NUM_PAIRS = RANGE_NUM_PAIRS;
|
|
|
619
544
|
exports.STANDALONE_COLOR_IDS = STANDALONE_COLOR_IDS;
|
|
620
545
|
exports.ALL_COLOR_IDS = ALL_COLOR_IDS;
|
|
621
546
|
|
|
547
|
+
exports.hexToHsl = hexToHsl;
|
|
548
|
+
exports.hslToHex = hslToHex;
|
|
622
549
|
exports.controlToCssVars = controlToCssVars;
|
|
623
550
|
exports.cascadeColor = cascadeColor;
|
|
551
|
+
exports.invertLightness = invertLightness;
|
|
624
552
|
exports.collectStyles = collectStyles;
|
|
625
|
-
exports.
|
|
626
|
-
exports.collectThemeColors = collectThemeColors;
|
|
627
|
-
exports.parseThemeColorBlock = parseThemeColorBlock;
|
|
553
|
+
exports.parseDarkBlock = parseDarkBlock;
|
|
628
554
|
exports.stylesToControls = stylesToControls;
|
|
629
555
|
exports.STYLE_DEFAULTS = STYLE_DEFAULTS;
|
|
630
556
|
exports.stripStyleDefaults = stripStyleDefaults;
|
package/public/sdocs-theme.js
CHANGED
|
@@ -89,7 +89,7 @@ function getPreferredTheme() {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
const SUN_ICON = '<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>';
|
|
92
|
-
const MOON_ICON = '<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>';
|
|
92
|
+
const MOON_ICON = '<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" stroke-width="1.5"/>';
|
|
93
93
|
|
|
94
94
|
function updateThemeIcon(theme) {
|
|
95
95
|
const icon = document.getElementById('icon-theme');
|
|
@@ -120,32 +120,35 @@ function loadThemeColors(theme) {
|
|
|
120
120
|
var overridden = S.themeOverridden[theme];
|
|
121
121
|
var defaults = getThemeDefaultsFor(theme);
|
|
122
122
|
|
|
123
|
-
// Cascade
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
// Cascade roots: text color and block bg/text
|
|
124
|
+
var cascadeRoots = [
|
|
125
|
+
['ctrl-color', defaults.colorDefault],
|
|
126
|
+
['ctrl-block-bg', defaults.codeBg],
|
|
127
|
+
['ctrl-block-text', defaults.bqColor],
|
|
128
|
+
];
|
|
129
|
+
cascadeRoots.forEach(function(pair) {
|
|
130
|
+
var id = pair[0], def = pair[1];
|
|
131
|
+
if (overridden.has(id)) {
|
|
132
|
+
S.setColorValue(id, colors[id], true);
|
|
133
|
+
} else {
|
|
134
|
+
S.setColorValue(id, def, false);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
129
137
|
|
|
130
|
-
// Then cascade children that are overridden
|
|
138
|
+
// Then cascade children that are overridden (overrides the parent cascade)
|
|
131
139
|
var cascadeIds = Object.keys(SDocStyles.COLOR_VAR_MAP);
|
|
132
140
|
cascadeIds.forEach(function(ctrlId) {
|
|
133
|
-
if (ctrlId === 'ctrl-color') return;
|
|
141
|
+
if (ctrlId === 'ctrl-color' || ctrlId === 'ctrl-block-bg' || ctrlId === 'ctrl-block-text') return;
|
|
134
142
|
if (overridden.has(ctrlId)) {
|
|
135
143
|
S.setColorValue(ctrlId, colors[ctrlId], true);
|
|
136
144
|
}
|
|
137
|
-
// Non-overridden cascade children are already set by cascade from parent
|
|
138
145
|
});
|
|
139
146
|
|
|
140
|
-
// Standalone colors
|
|
147
|
+
// Standalone colors (not in cascade)
|
|
141
148
|
var standaloneMap = {
|
|
142
149
|
'ctrl-bg-color': defaults.bgColor,
|
|
143
150
|
'ctrl-link-color': defaults.linkColor,
|
|
144
|
-
'ctrl-code-bg': defaults.codeBg,
|
|
145
|
-
'ctrl-code-color': defaults.codeColor,
|
|
146
151
|
'ctrl-bq-border-color': defaults.bqBorderColor,
|
|
147
|
-
'ctrl-bq-bg': defaults.bqBg,
|
|
148
|
-
'ctrl-bq-color': defaults.bqColor,
|
|
149
152
|
};
|
|
150
153
|
for (var ctrlId in standaloneMap) {
|
|
151
154
|
var el = document.getElementById(ctrlId);
|
|
@@ -217,11 +220,10 @@ function getStandaloneDefault(ctrlId) {
|
|
|
217
220
|
const map = {
|
|
218
221
|
'ctrl-bg-color': d.bgColor,
|
|
219
222
|
'ctrl-link-color': d.linkColor,
|
|
220
|
-
'ctrl-code-bg': d.codeBg,
|
|
221
|
-
'ctrl-code-color': d.codeColor,
|
|
222
223
|
'ctrl-bq-border-color': d.bqBorderColor,
|
|
223
|
-
'ctrl-
|
|
224
|
-
'ctrl-
|
|
224
|
+
'ctrl-block-bg': d.codeBg,
|
|
225
|
+
'ctrl-block-text': d.bqColor,
|
|
226
|
+
'ctrl-chart-accent': '#3b82f6',
|
|
225
227
|
};
|
|
226
228
|
return map[ctrlId];
|
|
227
229
|
}
|