sdocs-dev 1.2.0 → 1.3.1

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.
@@ -11,75 +11,171 @@ 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': '--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',
14
+ '_sd_ctrl-color': '--md-color',
15
+ '_sd_ctrl-h-color': '--md-h-color',
16
+ '_sd_ctrl-h1-color': '--md-h1-color',
17
+ '_sd_ctrl-h2-color': '--md-h2-color',
18
+ '_sd_ctrl-h3-color': '--md-h3-color',
19
+ '_sd_ctrl-h4-color': '--md-h4-color',
20
+ '_sd_ctrl-p-color': '--md-p-color',
21
+ '_sd_ctrl-list-color': '--md-list-color',
22
+ // Block cascade
23
+ '_sd_ctrl-block-bg': '--md-block-bg',
24
+ '_sd_ctrl-block-text': '--md-block-text',
25
+ '_sd_ctrl-code-bg': ['--md-code-bg', '--md-pre-bg'],
26
+ '_sd_ctrl-code-color': '--md-code-color',
27
+ '_sd_ctrl-bq-bg': '--md-bq-bg',
28
+ '_sd_ctrl-bq-color': '--md-bq-color',
29
+ '_sd_ctrl-chart-bg': '--md-chart-bg',
30
+ '_sd_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': ['ctrl-h-color', 'ctrl-p-color'],
27
- 'ctrl-h-color': ['ctrl-h1-color', 'ctrl-h2-color', 'ctrl-h3-color', 'ctrl-h4-color'],
28
- 'ctrl-p-color': ['ctrl-list-color'],
35
+ '_sd_ctrl-color': ['_sd_ctrl-h-color', '_sd_ctrl-p-color'],
36
+ '_sd_ctrl-h-color': ['_sd_ctrl-h1-color', '_sd_ctrl-h2-color', '_sd_ctrl-h3-color', '_sd_ctrl-h4-color'],
37
+ '_sd_ctrl-p-color': ['_sd_ctrl-list-color'],
38
+ '_sd_ctrl-block-bg': ['_sd_ctrl-code-bg', '_sd_ctrl-bq-bg', '_sd_ctrl-chart-bg'],
39
+ '_sd_ctrl-block-text': ['_sd_ctrl-code-color', '_sd_ctrl-bq-color', '_sd_ctrl-chart-text'],
29
40
  };
30
41
 
31
42
  // Control ID → { cssVar, suffix?, compound? }
32
43
  // Maps every non-color control to its CSS variable and optional unit suffix
33
44
  const CTRL_CSS_MAP = {
34
- 'ctrl-bg-color': { cssVar: '--md-bg' },
35
- 'ctrl-font-family': { cssVar: '--md-font-family' },
36
- 'ctrl-base-size-num': { cssVar: '--md-base-size', suffix: 'px' },
37
- 'ctrl-line-height-num': { cssVar: '--md-line-height' },
38
- 'ctrl-h-font-family': { cssVar: '--md-h-font-family' },
39
- 'ctrl-h-scale-num': { cssVar: '--md-h-scale' },
40
- 'ctrl-h-mb-num': { cssVar: '--md-h-margin-bottom', suffix: 'em' },
41
- 'ctrl-h1-size-num': { cssVar: '--md-h1-size', suffix: 'em' },
42
- 'ctrl-h1-weight': { cssVar: '--md-h1-weight' },
43
- 'ctrl-h2-size-num': { cssVar: '--md-h2-size', suffix: 'em' },
44
- 'ctrl-h2-weight': { cssVar: '--md-h2-weight' },
45
- 'ctrl-h3-size-num': { cssVar: '--md-h3-size', suffix: 'em' },
46
- 'ctrl-h3-weight': { cssVar: '--md-h3-weight' },
47
- 'ctrl-h4-size-num': { cssVar: '--md-h4-size', suffix: 'em' },
48
- 'ctrl-h4-weight': { cssVar: '--md-h4-weight' },
49
- 'ctrl-p-lh-num': { cssVar: '--md-p-line-height' },
50
- 'ctrl-p-mb-num': { cssVar: '--md-p-margin', template: '0 0 {v}em' },
51
- 'ctrl-link-color': { cssVar: '--md-link-color' },
52
- 'ctrl-link-decoration': { cssVar: '--md-link-decoration' },
53
- '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
- 'ctrl-bq-border-color': { cssVar: '--md-bq-border', compound: 'bq-border' },
57
- 'ctrl-bq-bw-num': { cssVar: '--md-bq-border', compound: 'bq-border' },
58
- 'ctrl-bq-bg': { cssVar: '--md-bq-bg' },
59
- 'ctrl-bq-size-num': { cssVar: '--md-bq-size', suffix: 'em' },
60
- 'ctrl-bq-color': { cssVar: '--md-bq-color' },
61
- 'ctrl-list-spacing-num': { cssVar: '--md-list-spacing', suffix: 'em' },
62
- 'ctrl-list-indent-num': { cssVar: '--md-list-indent', suffix: 'em' },
45
+ '_sd_ctrl-bg-color': { cssVar: '--md-bg' },
46
+ '_sd_ctrl-font-family': { cssVar: '--md-font-family' },
47
+ '_sd_ctrl-base-size-num': { cssVar: '--md-base-size', suffix: 'px' },
48
+ '_sd_ctrl-line-height-num': { cssVar: '--md-line-height' },
49
+ '_sd_ctrl-h-font-family': { cssVar: '--md-h-font-family' },
50
+ '_sd_ctrl-h-scale-num': { cssVar: '--md-h-scale' },
51
+ '_sd_ctrl-h-mb-num': { cssVar: '--md-h-margin-bottom', suffix: 'em' },
52
+ '_sd_ctrl-h1-size-num': { cssVar: '--md-h1-size', suffix: 'em' },
53
+ '_sd_ctrl-h1-weight': { cssVar: '--md-h1-weight' },
54
+ '_sd_ctrl-h2-size-num': { cssVar: '--md-h2-size', suffix: 'em' },
55
+ '_sd_ctrl-h2-weight': { cssVar: '--md-h2-weight' },
56
+ '_sd_ctrl-h3-size-num': { cssVar: '--md-h3-size', suffix: 'em' },
57
+ '_sd_ctrl-h3-weight': { cssVar: '--md-h3-weight' },
58
+ '_sd_ctrl-h4-size-num': { cssVar: '--md-h4-size', suffix: 'em' },
59
+ '_sd_ctrl-h4-weight': { cssVar: '--md-h4-weight' },
60
+ '_sd_ctrl-p-lh-num': { cssVar: '--md-p-line-height' },
61
+ '_sd_ctrl-p-mb-num': { cssVar: '--md-p-margin', template: '0 0 {v}em' },
62
+ '_sd_ctrl-link-color': { cssVar: '--md-link-color' },
63
+ '_sd_ctrl-link-decoration': { cssVar: '--md-link-decoration' },
64
+ '_sd_ctrl-code-font': { cssVar: '--md-code-font' },
65
+ '_sd_ctrl-bq-border-color': { cssVar: '--md-bq-border', compound: 'bq-border' },
66
+ '_sd_ctrl-bq-bw-num': { cssVar: '--md-bq-border', compound: 'bq-border' },
67
+ '_sd_ctrl-bq-size-num': { cssVar: '--md-bq-size', suffix: 'em' },
68
+ '_sd_ctrl-list-spacing-num': { cssVar: '--md-list-spacing', suffix: 'em' },
69
+ '_sd_ctrl-list-indent-num': { cssVar: '--md-list-indent', suffix: 'em' },
70
+ '_sd_ctrl-chart-accent': { cssVar: '--md-chart-accent' },
71
+ '_sd_ctrl-chart-palette': { cssVar: '--md-chart-palette' },
63
72
  };
64
73
 
65
74
  // Range ↔ Number input pairs
66
75
  const RANGE_NUM_PAIRS = [
67
- ['ctrl-base-size-range', 'ctrl-base-size-num'],
68
- ['ctrl-line-height-range', 'ctrl-line-height-num'],
69
- ['ctrl-h-scale-range', 'ctrl-h-scale-num'],
70
- ['ctrl-h-mb-range', 'ctrl-h-mb-num'],
71
- ['ctrl-h1-size-range', 'ctrl-h1-size-num'],
72
- ['ctrl-h2-size-range', 'ctrl-h2-size-num'],
73
- ['ctrl-h3-size-range', 'ctrl-h3-size-num'],
74
- ['ctrl-h4-size-range', 'ctrl-h4-size-num'],
75
- ['ctrl-p-lh-range', 'ctrl-p-lh-num'],
76
- ['ctrl-p-mb-range', 'ctrl-p-mb-num'],
77
- ['ctrl-bq-bw-range', 'ctrl-bq-bw-num'],
78
- ['ctrl-bq-size-range', 'ctrl-bq-size-num'],
79
- ['ctrl-list-spacing-range', 'ctrl-list-spacing-num'],
80
- ['ctrl-list-indent-range', 'ctrl-list-indent-num'],
76
+ ['_sd_ctrl-base-size-range', '_sd_ctrl-base-size-num'],
77
+ ['_sd_ctrl-line-height-range', '_sd_ctrl-line-height-num'],
78
+ ['_sd_ctrl-h-scale-range', '_sd_ctrl-h-scale-num'],
79
+ ['_sd_ctrl-h-mb-range', '_sd_ctrl-h-mb-num'],
80
+ ['_sd_ctrl-h1-size-range', '_sd_ctrl-h1-size-num'],
81
+ ['_sd_ctrl-h2-size-range', '_sd_ctrl-h2-size-num'],
82
+ ['_sd_ctrl-h3-size-range', '_sd_ctrl-h3-size-num'],
83
+ ['_sd_ctrl-h4-size-range', '_sd_ctrl-h4-size-num'],
84
+ ['_sd_ctrl-p-lh-range', '_sd_ctrl-p-lh-num'],
85
+ ['_sd_ctrl-p-mb-range', '_sd_ctrl-p-mb-num'],
86
+ ['_sd_ctrl-bq-bw-range', '_sd_ctrl-bq-bw-num'],
87
+ ['_sd_ctrl-bq-size-range', '_sd_ctrl-bq-size-num'],
88
+ ['_sd_ctrl-list-spacing-range', '_sd_ctrl-list-spacing-num'],
89
+ ['_sd_ctrl-list-indent-range', '_sd_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
- return [{ cssVar: COLOR_VAR_MAP[ctrlId], value: value }];
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];
@@ -99,8 +199,8 @@ function controlToCssVars(ctrlId, value, allValues) {
99
199
 
100
200
  // Compound: bq-border combines color + width
101
201
  if (entry.compound === 'bq-border') {
102
- const col = allValues['ctrl-bq-border-color'] || '#2563EB';
103
- const w = allValues['ctrl-bq-bw-num'] || '3';
202
+ const col = allValues['_sd_ctrl-bq-border-color'] || '#2563EB';
203
+ const w = allValues['_sd_ctrl-bq-bw-num'] || '3';
104
204
  return [{ cssVar: '--md-bq-border', value: `${w}px solid ${col}` }];
105
205
  }
106
206
 
@@ -146,52 +246,67 @@ function collectStyles(values, overriddenColors) {
146
246
  const gn = id => parseFloat(values[id]) || 0;
147
247
 
148
248
  const styles = {
149
- fontFamily: gv('ctrl-font-family').replace(/['"]/g, '').split(',')[0].trim(),
150
- baseFontSize: gn('ctrl-base-size-num'),
151
- lineHeight: gn('ctrl-line-height-num'),
249
+ fontFamily: gv('_sd_ctrl-font-family').replace(/['"]/g, '').split(',')[0].trim(),
250
+ baseFontSize: gn('_sd_ctrl-base-size-num'),
251
+ lineHeight: gn('_sd_ctrl-line-height-num'),
152
252
  headers: {
153
- fontFamily: gv('ctrl-h-font-family').replace(/['"]/g, '').split(',')[0].trim(),
154
- scale: gn('ctrl-h-scale-num'),
155
- marginBottom: gn('ctrl-h-mb-num'),
253
+ fontFamily: gv('_sd_ctrl-h-font-family').replace(/['"]/g, '').split(',')[0].trim(),
254
+ scale: gn('_sd_ctrl-h-scale-num'),
255
+ marginBottom: gn('_sd_ctrl-h-mb-num'),
156
256
  },
157
- h1: { fontSize: gn('ctrl-h1-size-num'), fontWeight: parseInt(gv('ctrl-h1-weight')) || 0 },
158
- h2: { fontSize: gn('ctrl-h2-size-num'), fontWeight: parseInt(gv('ctrl-h2-weight')) || 0 },
159
- h3: { fontSize: gn('ctrl-h3-size-num'), fontWeight: parseInt(gv('ctrl-h3-weight')) || 0 },
160
- h4: { fontSize: gn('ctrl-h4-size-num'), fontWeight: parseInt(gv('ctrl-h4-weight')) || 0 },
257
+ h1: { fontSize: gn('_sd_ctrl-h1-size-num'), fontWeight: parseInt(gv('_sd_ctrl-h1-weight')) || 0 },
258
+ h2: { fontSize: gn('_sd_ctrl-h2-size-num'), fontWeight: parseInt(gv('_sd_ctrl-h2-weight')) || 0 },
259
+ h3: { fontSize: gn('_sd_ctrl-h3-size-num'), fontWeight: parseInt(gv('_sd_ctrl-h3-weight')) || 0 },
260
+ h4: { fontSize: gn('_sd_ctrl-h4-size-num'), fontWeight: parseInt(gv('_sd_ctrl-h4-weight')) || 0 },
161
261
  p: {
162
- lineHeight: gn('ctrl-p-lh-num'),
163
- marginBottom: gn('ctrl-p-mb-num'),
262
+ lineHeight: gn('_sd_ctrl-p-lh-num'),
263
+ marginBottom: gn('_sd_ctrl-p-mb-num'),
164
264
  },
165
- link: { color: gv('ctrl-link-color'), decoration: gv('ctrl-link-decoration') },
265
+ link: { color: gv('_sd_ctrl-link-color'), decoration: gv('_sd_ctrl-link-decoration') },
166
266
  code: {
167
- font: gv('ctrl-code-font').replace(/['"]/g, '').split(',')[0].trim(),
168
- background: gv('ctrl-code-bg'),
169
- color: gv('ctrl-code-color'),
267
+ font: gv('_sd_ctrl-code-font').replace(/['"]/g, '').split(',')[0].trim(),
170
268
  },
171
269
  blockquote: {
172
- borderColor: gv('ctrl-bq-border-color'),
173
- borderWidth: gn('ctrl-bq-bw-num'),
174
- background: gv('ctrl-bq-bg'),
175
- fontSize: gn('ctrl-bq-size-num'),
176
- color: gv('ctrl-bq-color'),
270
+ borderColor: gv('_sd_ctrl-bq-border-color'),
271
+ borderWidth: gn('_sd_ctrl-bq-bw-num'),
272
+ fontSize: gn('_sd_ctrl-bq-size-num'),
177
273
  },
178
274
  };
179
275
 
180
276
  styles.list = {
181
- spacing: gn('ctrl-list-spacing-num'),
182
- indent: gn('ctrl-list-indent-num'),
277
+ spacing: gn('_sd_ctrl-list-spacing-num'),
278
+ indent: gn('_sd_ctrl-list-indent-num'),
183
279
  };
184
280
 
185
281
  // Only emit colors that were explicitly overridden
186
- if (overriddenColors.has('ctrl-bg-color')) styles.background = gv('ctrl-bg-color');
187
- if (overriddenColors.has('ctrl-color')) styles.color = gv('ctrl-color');
188
- if (overriddenColors.has('ctrl-h-color')) styles.headers.color = gv('ctrl-h-color');
189
- if (overriddenColors.has('ctrl-h1-color')) styles.h1.color = gv('ctrl-h1-color');
190
- if (overriddenColors.has('ctrl-h2-color')) styles.h2.color = gv('ctrl-h2-color');
191
- if (overriddenColors.has('ctrl-h3-color')) styles.h3.color = gv('ctrl-h3-color');
192
- if (overriddenColors.has('ctrl-h4-color')) styles.h4.color = gv('ctrl-h4-color');
193
- if (overriddenColors.has('ctrl-p-color')) styles.p.color = gv('ctrl-p-color');
194
- if (overriddenColors.has('ctrl-list-color')) styles.list.color = gv('ctrl-list-color');
282
+ if (overriddenColors.has('_sd_ctrl-bg-color')) styles.background = gv('_sd_ctrl-bg-color');
283
+ if (overriddenColors.has('_sd_ctrl-color')) styles.color = gv('_sd_ctrl-color');
284
+ if (overriddenColors.has('_sd_ctrl-h-color')) styles.headers.color = gv('_sd_ctrl-h-color');
285
+ if (overriddenColors.has('_sd_ctrl-h1-color')) styles.h1.color = gv('_sd_ctrl-h1-color');
286
+ if (overriddenColors.has('_sd_ctrl-h2-color')) styles.h2.color = gv('_sd_ctrl-h2-color');
287
+ if (overriddenColors.has('_sd_ctrl-h3-color')) styles.h3.color = gv('_sd_ctrl-h3-color');
288
+ if (overriddenColors.has('_sd_ctrl-h4-color')) styles.h4.color = gv('_sd_ctrl-h4-color');
289
+ if (overriddenColors.has('_sd_ctrl-p-color')) styles.p.color = gv('_sd_ctrl-p-color');
290
+ if (overriddenColors.has('_sd_ctrl-list-color')) styles.list.color = gv('_sd_ctrl-list-color');
291
+
292
+ // Block cascade colors
293
+ if (overriddenColors.has('_sd_ctrl-block-bg') || overriddenColors.has('_sd_ctrl-block-text')) {
294
+ styles.blocks = {};
295
+ if (overriddenColors.has('_sd_ctrl-block-bg')) styles.blocks.background = gv('_sd_ctrl-block-bg');
296
+ if (overriddenColors.has('_sd_ctrl-block-text')) styles.blocks.color = gv('_sd_ctrl-block-text');
297
+ }
298
+ if (overriddenColors.has('_sd_ctrl-code-bg')) styles.code.background = gv('_sd_ctrl-code-bg');
299
+ if (overriddenColors.has('_sd_ctrl-code-color')) styles.code.color = gv('_sd_ctrl-code-color');
300
+ if (overriddenColors.has('_sd_ctrl-bq-bg')) styles.blockquote.background = gv('_sd_ctrl-bq-bg');
301
+ if (overriddenColors.has('_sd_ctrl-bq-color')) styles.blockquote.color = gv('_sd_ctrl-bq-color');
302
+
303
+ // Chart styles
304
+ var chartObj = {};
305
+ if (overriddenColors.has('_sd_ctrl-chart-accent')) chartObj.accent = gv('_sd_ctrl-chart-accent');
306
+ if (gv('_sd_ctrl-chart-palette') && gv('_sd_ctrl-chart-palette') !== 'monochrome') chartObj.palette = gv('_sd_ctrl-chart-palette');
307
+ if (overriddenColors.has('_sd_ctrl-chart-bg')) chartObj.background = gv('_sd_ctrl-chart-bg');
308
+ if (overriddenColors.has('_sd_ctrl-chart-text')) chartObj.textColor = gv('_sd_ctrl-chart-text');
309
+ if (Object.keys(chartObj).length) styles.chart = chartObj;
195
310
 
196
311
  return styles;
197
312
  }
@@ -207,70 +322,81 @@ function stylesToControls(styles) {
207
322
  const controls = {};
208
323
  const overridden = new Set();
209
324
 
210
- if (styles.fontFamily) controls['ctrl-font-family'] = styles.fontFamily;
211
- if (styles.baseFontSize) controls['ctrl-base-size-num'] = styles.baseFontSize;
212
- if (styles.lineHeight) controls['ctrl-line-height-num'] = styles.lineHeight;
325
+ if (styles.fontFamily) controls['_sd_ctrl-font-family'] = styles.fontFamily;
326
+ if (styles.baseFontSize) controls['_sd_ctrl-base-size-num'] = styles.baseFontSize;
327
+ if (styles.lineHeight) controls['_sd_ctrl-line-height-num'] = styles.lineHeight;
213
328
 
214
329
  if (styles.background) {
215
- controls['ctrl-bg-color'] = styles.background;
216
- overridden.add('ctrl-bg-color');
330
+ controls['_sd_ctrl-bg-color'] = styles.background;
331
+ overridden.add('_sd_ctrl-bg-color');
217
332
  }
218
333
  if (styles.color) {
219
- controls['ctrl-color'] = styles.color;
220
- overridden.add('ctrl-color');
334
+ controls['_sd_ctrl-color'] = styles.color;
335
+ overridden.add('_sd_ctrl-color');
221
336
  }
222
337
 
223
338
  const h = styles.headers || {};
224
- if (h.fontFamily) controls['ctrl-h-font-family'] = h.fontFamily;
225
- if (h.scale) controls['ctrl-h-scale-num'] = h.scale;
226
- if (h.marginBottom) controls['ctrl-h-mb-num'] = h.marginBottom;
339
+ if (h.fontFamily) controls['_sd_ctrl-h-font-family'] = h.fontFamily;
340
+ if (h.scale) controls['_sd_ctrl-h-scale-num'] = h.scale;
341
+ if (h.marginBottom) controls['_sd_ctrl-h-mb-num'] = h.marginBottom;
227
342
  if (h.color) {
228
- controls['ctrl-h-color'] = h.color;
229
- overridden.add('ctrl-h-color');
343
+ controls['_sd_ctrl-h-color'] = h.color;
344
+ overridden.add('_sd_ctrl-h-color');
230
345
  }
231
346
 
232
347
  ['h1', 'h2', 'h3', 'h4'].forEach(t => {
233
348
  const hs = styles[t] || {};
234
- if (hs.fontSize) controls[`ctrl-${t}-size-num`] = hs.fontSize;
235
- if (hs.fontWeight) controls[`ctrl-${t}-weight`] = String(hs.fontWeight);
349
+ if (hs.fontSize) controls[`_sd_ctrl-${t}-size-num`] = hs.fontSize;
350
+ if (hs.fontWeight) controls[`_sd_ctrl-${t}-weight`] = String(hs.fontWeight);
236
351
  if (hs.color) {
237
- controls[`ctrl-${t}-color`] = hs.color;
238
- overridden.add(`ctrl-${t}-color`);
352
+ controls[`_sd_ctrl-${t}-color`] = hs.color;
353
+ overridden.add(`_sd_ctrl-${t}-color`);
239
354
  }
240
355
  });
241
356
 
242
357
  const p = styles.p || {};
243
- if (p.lineHeight) controls['ctrl-p-lh-num'] = p.lineHeight;
244
- if (p.marginBottom) controls['ctrl-p-mb-num'] = p.marginBottom;
358
+ if (p.lineHeight) controls['_sd_ctrl-p-lh-num'] = p.lineHeight;
359
+ if (p.marginBottom) controls['_sd_ctrl-p-mb-num'] = p.marginBottom;
245
360
  if (p.color) {
246
- controls['ctrl-p-color'] = p.color;
247
- overridden.add('ctrl-p-color');
361
+ controls['_sd_ctrl-p-color'] = p.color;
362
+ overridden.add('_sd_ctrl-p-color');
248
363
  }
249
364
 
250
365
  const lk = styles.link || {};
251
- if (lk.color) controls['ctrl-link-color'] = lk.color;
252
- if (lk.decoration) controls['ctrl-link-decoration'] = lk.decoration;
366
+ if (lk.color) controls['_sd_ctrl-link-color'] = lk.color;
367
+ if (lk.decoration) controls['_sd_ctrl-link-decoration'] = lk.decoration;
368
+
369
+ // Blocks cascade parent
370
+ const bl = styles.blocks || {};
371
+ if (bl.background) { controls['_sd_ctrl-block-bg'] = bl.background; overridden.add('_sd_ctrl-block-bg'); }
372
+ if (bl.color) { controls['_sd_ctrl-block-text'] = bl.color; overridden.add('_sd_ctrl-block-text'); }
253
373
 
254
374
  const cd = styles.code || {};
255
- 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;
375
+ if (cd.font) controls['_sd_ctrl-code-font'] = cd.font;
376
+ if (cd.background) { controls['_sd_ctrl-code-bg'] = cd.background; overridden.add('_sd_ctrl-code-bg'); }
377
+ if (cd.color) { controls['_sd_ctrl-code-color'] = cd.color; overridden.add('_sd_ctrl-code-color'); }
258
378
 
259
379
  const bq = styles.blockquote || {};
260
- if (bq.borderColor) controls['ctrl-bq-border-color'] = bq.borderColor;
261
- if (bq.borderWidth) controls['ctrl-bq-bw-num'] = bq.borderWidth;
262
- if (bq.background) controls['ctrl-bq-bg'] = bq.background;
263
- if (bq.fontSize) controls['ctrl-bq-size-num'] = bq.fontSize;
264
- if (bq.color) controls['ctrl-bq-color'] = bq.color;
380
+ if (bq.borderColor) controls['_sd_ctrl-bq-border-color'] = bq.borderColor;
381
+ if (bq.borderWidth) controls['_sd_ctrl-bq-bw-num'] = bq.borderWidth;
382
+ if (bq.background) { controls['_sd_ctrl-bq-bg'] = bq.background; overridden.add('_sd_ctrl-bq-bg'); }
383
+ if (bq.fontSize) controls['_sd_ctrl-bq-size-num'] = bq.fontSize;
384
+ if (bq.color) { controls['_sd_ctrl-bq-color'] = bq.color; overridden.add('_sd_ctrl-bq-color'); }
265
385
 
266
386
  const ll = styles.list || {};
267
- if (ll.spacing) controls['ctrl-list-spacing-num'] = ll.spacing;
268
- if (ll.indent) controls['ctrl-list-indent-num'] = ll.indent;
387
+ if (ll.spacing) controls['_sd_ctrl-list-spacing-num'] = ll.spacing;
388
+ if (ll.indent) controls['_sd_ctrl-list-indent-num'] = ll.indent;
269
389
  if (ll.color) {
270
- controls['ctrl-list-color'] = ll.color;
271
- overridden.add('ctrl-list-color');
390
+ controls['_sd_ctrl-list-color'] = ll.color;
391
+ overridden.add('_sd_ctrl-list-color');
272
392
  }
273
393
 
394
+ const ch = styles.chart || {};
395
+ if (ch.accent) { controls['_sd_ctrl-chart-accent'] = ch.accent; overridden.add('_sd_ctrl-chart-accent'); }
396
+ if (ch.palette) controls['_sd_ctrl-chart-palette'] = ch.palette;
397
+ if (ch.background) { controls['_sd_ctrl-chart-bg'] = ch.background; overridden.add('_sd_ctrl-chart-bg'); }
398
+ if (ch.textColor) { controls['_sd_ctrl-chart-text'] = ch.textColor; overridden.add('_sd_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','ctrl-code-bg','ctrl-code-color','ctrl-bq-border-color','ctrl-bq-bg','ctrl-bq-color',
408
+ '_sd_ctrl-bg-color','_sd_ctrl-link-color',
409
+ '_sd_ctrl-bq-border-color',
410
+ '_sd_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
- * parseThemeColorBlock(block)
295
- * Extracts { colors: {ctrlId: val}, overridden: Set } from a light:/dark: sub-object.
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 parseThemeColorBlock(block) {
299
- if (!block) return { colors: {}, overridden: new Set() };
425
+ function parseDarkBlock(block) {
426
+ if (!block) return {};
300
427
  var colors = {};
301
- var overridden = new Set();
302
-
303
- // Background color
304
- if (block.background) {
305
- colors['ctrl-bg-color'] = block.background;
306
- overridden.add('ctrl-bg-color');
307
- }
308
428
 
309
- // Top-level color (cascade root)
310
- if (block.color) {
311
- colors['ctrl-color'] = block.color;
312
- overridden.add('ctrl-color');
313
- }
429
+ if (block.background) colors['_sd_ctrl-bg-color'] = block.background;
430
+ if (block.color) colors['_sd_ctrl-color'] = block.color;
314
431
 
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['_sd_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['_sd_ctrl-' + t + '-color'] = obj.color;
326
436
  });
327
- if (block.p && block.p.color) {
328
- colors['ctrl-p-color'] = block.p.color;
329
- overridden.add('ctrl-p-color');
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['_sd_ctrl-p-color'] = block.p.color;
438
+ if (block.list && block.list.color) colors['_sd_ctrl-list-color'] = block.list.color;
439
+ if (block.link && block.link.color) colors['_sd_ctrl-link-color'] = block.link.color;
335
440
 
336
- // Standalone colors
337
- if (block.link && block.link.color) {
338
- colors['ctrl-link-color'] = block.link.color;
339
- overridden.add('ctrl-link-color');
441
+ if (block.blocks) {
442
+ if (block.blocks.background) colors['_sd_ctrl-block-bg'] = block.blocks.background;
443
+ if (block.blocks.color) colors['_sd_ctrl-block-text'] = block.blocks.color;
340
444
  }
341
445
  if (block.code) {
342
- if (block.code.background) { colors['ctrl-code-bg'] = block.code.background; overridden.add('ctrl-code-bg'); }
343
- if (block.code.color) { colors['ctrl-code-color'] = block.code.color; overridden.add('ctrl-code-color'); }
446
+ if (block.code.background) colors['_sd_ctrl-code-bg'] = block.code.background;
447
+ if (block.code.color) colors['_sd_ctrl-code-color'] = block.code.color;
344
448
  }
345
449
  if (block.blockquote) {
346
- if (block.blockquote.borderColor) { colors['ctrl-bq-border-color'] = block.blockquote.borderColor; overridden.add('ctrl-bq-border-color'); }
347
- if (block.blockquote.background) { colors['ctrl-bq-bg'] = block.blockquote.background; overridden.add('ctrl-bq-bg'); }
348
- if (block.blockquote.color) { colors['ctrl-bq-color'] = block.blockquote.color; overridden.add('ctrl-bq-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'];
450
+ if (block.blockquote.borderColor) colors['_sd_ctrl-bq-border-color'] = block.blockquote.borderColor;
451
+ if (block.blockquote.background) colors['_sd_ctrl-bq-bg'] = block.blockquote.background;
452
+ if (block.blockquote.color) colors['_sd_ctrl-bq-color'] = block.blockquote.color;
368
453
  }
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'];
454
+ if (block.chart) {
455
+ if (block.chart.background) colors['_sd_ctrl-chart-bg'] = block.chart.background;
456
+ if (block.chart.textColor) colors['_sd_ctrl-chart-text'] = block.chart.textColor;
385
457
  }
386
458
 
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'];
403
- }
404
- if (overriddenSet.has('ctrl-bq-bg')) {
405
- if (!block.blockquote) block.blockquote = {};
406
- block.blockquote.background = colorValues['ctrl-bq-bg'];
407
- }
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
-
452
- styles.list = {
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.collectStylesDual = collectStylesDual;
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;