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.
@@ -1,272 +0,0 @@
1
- // sdocs-controls.js — CSS variable management and control wiring
2
- (function () {
3
- 'use strict';
4
-
5
- var S = SDocs;
6
-
7
- // ── CSS var updates ──────────────────────────────────
8
-
9
- function applyCtrl(id) {
10
- var el = document.getElementById(id);
11
- if (!el) return;
12
- var v = el.value;
13
-
14
- if (id === 'ctrl-font-family' || id === 'ctrl-h-font-family') {
15
- var name = v.replace(/['"]/g,'').split(',')[0].trim();
16
- if (S.GOOGLE_FONTS.includes(name)) S.loadGoogleFont(name);
17
- }
18
-
19
- var allVals = readAllControlValues();
20
- SDocStyles.controlToCssVars(id, v, allVals)
21
- .forEach(function(a) { S.setStyleVar(a.cssVar, a.value); });
22
- S.syncAll('controls');
23
- }
24
-
25
- function readAllControlValues() {
26
- var vals = {};
27
- Object.keys(SDocStyles.CTRL_CSS_MAP).forEach(function(id) {
28
- var el = document.getElementById(id);
29
- if (el) vals[id] = el.value;
30
- });
31
- Object.keys(SDocStyles.COLOR_VAR_MAP).forEach(function(id) {
32
- var el = document.getElementById(id);
33
- if (el) vals[id] = el.value;
34
- });
35
- return vals;
36
- }
37
-
38
- function linkRangeNum(rangeId, numId) {
39
- var r = document.getElementById(rangeId);
40
- var n = document.getElementById(numId);
41
- r.addEventListener('input', function() { n.value = r.value; applyCtrl(numId); });
42
- n.addEventListener('input', function() { r.value = n.value; applyCtrl(numId); });
43
- }
44
-
45
- SDocStyles.RANGE_NUM_PAIRS.forEach(function(pair) { linkRangeNum(pair[0], pair[1]); });
46
-
47
- var STANDALONE_COLOR_IDS = new Set(SDocStyles.STANDALONE_COLOR_IDS);
48
-
49
- [
50
- 'ctrl-font-family','ctrl-h-font-family',
51
- 'ctrl-h1-weight','ctrl-h2-weight','ctrl-h3-weight','ctrl-h4-weight',
52
- 'ctrl-bg-color','ctrl-link-color','ctrl-link-decoration',
53
- 'ctrl-code-font','ctrl-code-bg','ctrl-code-color',
54
- 'ctrl-bq-border-color','ctrl-bq-bg','ctrl-bq-color',
55
- ].forEach(function(id) {
56
- var handler = function() { if (STANDALONE_COLOR_IDS.has(id)) S.overriddenColors.add(id); applyCtrl(id); };
57
- document.getElementById(id).addEventListener('input', handler);
58
- document.getElementById(id).addEventListener('change', handler);
59
- });
60
-
61
- // ── Color cascade ──────────────────────────────────
62
-
63
- var COLOR_VAR = SDocStyles.COLOR_VAR_MAP;
64
- var COLOR_CHILDREN = SDocStyles.COLOR_CASCADE;
65
-
66
- function setColorValue(ctrlId, value, userAction) {
67
- if (userAction) S.overriddenColors.add(ctrlId);
68
- S.setStyleVar(COLOR_VAR[ctrlId], value);
69
- var ctrl = document.getElementById(ctrlId);
70
- if (ctrl) ctrl.value = value;
71
- var children = COLOR_CHILDREN[ctrlId] || [];
72
- for (var i = 0; i < children.length; i++) {
73
- if (!S.overriddenColors.has(children[i])) {
74
- setColorValue(children[i], value, false);
75
- }
76
- }
77
- }
78
-
79
- function resetColorValue(ctrlId) {
80
- S.overriddenColors.delete(ctrlId);
81
- setColorValue(ctrlId, S.getColorDefault(), false);
82
- }
83
-
84
- Object.keys(COLOR_VAR).forEach(function(ctrlId) {
85
- var el = document.getElementById(ctrlId);
86
- if (!el) return;
87
- el.addEventListener('input', function() { setColorValue(ctrlId, el.value, true); S.syncAll('controls'); });
88
- el.addEventListener('change', function() { setColorValue(ctrlId, el.value, true); S.syncAll('controls'); });
89
- });
90
-
91
- document.getElementById('reset-color').addEventListener('click', function() { S.overriddenColors.delete('ctrl-color'); setColorValue('ctrl-color', S.getColorDefault(), false); S.syncAll('controls'); });
92
- document.getElementById('reset-h-color').addEventListener('click', function() { resetColorValue('ctrl-h-color'); S.syncAll('controls'); });
93
- document.getElementById('reset-h1-color').addEventListener('click', function() { resetColorValue('ctrl-h1-color'); S.syncAll('controls'); });
94
- document.getElementById('reset-h2-color').addEventListener('click', function() { resetColorValue('ctrl-h2-color'); S.syncAll('controls'); });
95
- document.getElementById('reset-h3-color').addEventListener('click', function() { resetColorValue('ctrl-h3-color'); S.syncAll('controls'); });
96
- document.getElementById('reset-h4-color').addEventListener('click', function() { resetColorValue('ctrl-h4-color'); S.syncAll('controls'); });
97
- document.getElementById('reset-p-color').addEventListener('click', function() { resetColorValue('ctrl-p-color'); S.syncAll('controls'); });
98
- document.getElementById('reset-list-color').addEventListener('click', function() { resetColorValue('ctrl-list-color'); S.syncAll('controls'); });
99
-
100
- ['ctrl-bg-color','ctrl-link-color','ctrl-code-bg','ctrl-code-color','ctrl-bq-border-color','ctrl-bq-bg','ctrl-bq-color'].forEach(function(ctrlId) {
101
- var btnId = 'reset-' + ctrlId.replace('ctrl-', '');
102
- document.getElementById(btnId).addEventListener('click', function() {
103
- var defaultVal = S.getStandaloneDefault(ctrlId);
104
- var el = document.getElementById(ctrlId);
105
- S.overriddenColors.delete(ctrlId);
106
- el.value = defaultVal;
107
- var assignments = SDocStyles.controlToCssVars(ctrlId, defaultVal, readAllControlValues());
108
- assignments.forEach(function(a) { S.setStyleVar(a.cssVar, a.value); });
109
- S.syncAll('controls');
110
- });
111
- });
112
-
113
- // ── Apply styles from meta → controls ──────────────────
114
-
115
- function setCtrl(id, val) {
116
- if (val === undefined || val === null) return;
117
- var el = document.getElementById(id);
118
- if (!el) return;
119
- el.value = val;
120
- var rangeId = id.replace(/-num$/, '-range');
121
- var rng = document.getElementById(rangeId);
122
- if (rng) rng.value = val;
123
- applyCtrl(id);
124
- }
125
-
126
- function applyStylesFromMeta(s) {
127
- if (!s) return;
128
- // Suppress syncAll during batch updates to avoid capturing partial state
129
- var wasSyncing = S._syncing;
130
- S._syncing = true;
131
-
132
- var result = SDocStyles.stylesToControls(s);
133
- var controls = result.controls;
134
-
135
- // Font family selects need special handling to match bare names against <option> values
136
- [['fontFamily', 'ctrl-font-family'], ['headers.fontFamily', 'ctrl-h-font-family']].forEach(function(pair) {
137
- var styleKey = pair[0], ctrlId = pair[1];
138
- var fontName = styleKey === 'fontFamily' ? s.fontFamily : (s.headers || {}).fontFamily;
139
- if (fontName) {
140
- var sel = document.getElementById(ctrlId);
141
- var match = [].slice.call(sel.options).find(function(o) {
142
- return o.value.replace(/['"]/g,'').split(',')[0].trim() === fontName ||
143
- o.textContent === fontName;
144
- });
145
- if (match) { sel.value = match.value; applyCtrl(ctrlId); }
146
- }
147
- });
148
-
149
- Object.keys(controls).forEach(function(id) {
150
- if (id === 'ctrl-font-family' || id === 'ctrl-h-font-family') return;
151
- if (SDocStyles.COLOR_VAR_MAP[id]) return;
152
- setCtrl(id, controls[id]);
153
- });
154
-
155
- if (result.hasThemeColors) {
156
- // Per-theme color mode: populate both theme states
157
- S.themeColors.light = result.lightColors || {};
158
- S.themeColors.dark = result.darkColors || {};
159
- S.themeOverridden.light = result.lightOverridden || new Set();
160
- S.themeOverridden.dark = result.darkOverridden || new Set();
161
-
162
- // Load the active theme's colors into controls
163
- S._syncing = wasSyncing;
164
- S.loadThemeColors(S.activeTheme);
165
- return;
166
- }
167
-
168
- // Legacy single-theme path: top-level colors go to light theme only,
169
- // then loadThemeColors applies the correct theme's colors.
170
- var lightOverridden = S.themeOverridden.light;
171
- var lightColors = S.themeColors.light;
172
- var newOverridden = result.overriddenColors;
173
-
174
- lightOverridden.clear();
175
- S.themeOverridden.dark.clear();
176
-
177
- newOverridden.forEach(function(id) {
178
- lightOverridden.add(id);
179
- lightColors[id] = controls[id];
180
- });
181
-
182
- // Also store standalone colors from legacy format into light theme
183
- var legacyStandalone = [];
184
- if (s.background) legacyStandalone.push(['ctrl-bg-color', s.background]);
185
- if (s.link && s.link.color) legacyStandalone.push(['ctrl-link-color', s.link.color]);
186
- if (s.code) {
187
- if (s.code.background) legacyStandalone.push(['ctrl-code-bg', s.code.background]);
188
- if (s.code.color) legacyStandalone.push(['ctrl-code-color', s.code.color]);
189
- }
190
- if (s.blockquote) {
191
- if (s.blockquote.borderColor) legacyStandalone.push(['ctrl-bq-border-color', s.blockquote.borderColor]);
192
- if (s.blockquote.background) legacyStandalone.push(['ctrl-bq-bg', s.blockquote.background]);
193
- if (s.blockquote.color) legacyStandalone.push(['ctrl-bq-color', s.blockquote.color]);
194
- }
195
- legacyStandalone.forEach(function(pair) {
196
- lightOverridden.add(pair[0]);
197
- lightColors[pair[0]] = pair[1];
198
- });
199
-
200
- // Now apply the active theme's colors (light overrides or dark defaults)
201
- S._syncing = wasSyncing;
202
- S.loadThemeColors(S.activeTheme);
203
- }
204
-
205
- function collectStyles() {
206
- S.saveCurrentThemeColors();
207
- var lightHas = S.themeOverridden.light.size > 0;
208
- var darkHas = S.themeOverridden.dark.size > 0;
209
- if (lightHas || darkHas) {
210
- return SDocStyles.collectStylesDual(
211
- readAllControlValues(),
212
- S.themeOverridden.light, S.themeOverridden.dark,
213
- S.themeColors.light, S.themeColors.dark
214
- );
215
- }
216
- return SDocStyles.collectStyles(readAllControlValues(), S.overriddenColors);
217
- }
218
-
219
- // ── Reset all styles ──────────────────────────────────
220
-
221
- function resetAllStyles() {
222
- // Clear both theme states
223
- S.themeOverridden.light.clear();
224
- S.themeOverridden.dark.clear();
225
- S.themeColors.light = {};
226
- S.themeColors.dark = {};
227
-
228
- setColorValue('ctrl-color', S.getColorDefault(), false);
229
- // Set standalone color controls to theme-appropriate defaults
230
- STANDALONE_COLOR_IDS.forEach(function(ctrlId) {
231
- var el = document.getElementById(ctrlId);
232
- if (el) el.value = S.getStandaloneDefault(ctrlId);
233
- });
234
- document.querySelectorAll('#right input, #right select').forEach(function(el) {
235
- if (STANDALONE_COLOR_IDS.has(el.id)) return; // already set above
236
- if (el.type === 'range' || el.type === 'number') el.value = el.defaultValue;
237
- else if (el.tagName === 'SELECT') el.selectedIndex = [].slice.call(el.options).findIndex(function(o) { return o.defaultSelected; });
238
- else if (el.type === 'color') el.value = el.defaultValue;
239
- });
240
- ['ctrl-bg-color','ctrl-font-family','ctrl-base-size-num','ctrl-line-height-num',
241
- 'ctrl-h-font-family','ctrl-h-scale-num','ctrl-h-mb-num',
242
- 'ctrl-h1-size-num','ctrl-h1-weight','ctrl-h2-size-num','ctrl-h2-weight',
243
- 'ctrl-h3-size-num','ctrl-h3-weight','ctrl-h4-size-num','ctrl-h4-weight',
244
- 'ctrl-p-lh-num','ctrl-p-mb-num',
245
- 'ctrl-link-color','ctrl-link-decoration',
246
- 'ctrl-code-font','ctrl-code-bg','ctrl-code-color',
247
- 'ctrl-bq-border-color','ctrl-bq-bg','ctrl-bq-bw-num','ctrl-bq-size-num','ctrl-bq-color',
248
- 'ctrl-list-spacing-num','ctrl-list-indent-num',
249
- ].forEach(function(id) { applyCtrl(id); });
250
- }
251
-
252
- // ── Init: set colors to theme-appropriate defaults ──────
253
-
254
- if (document.documentElement.dataset.theme === 'dark') {
255
- STANDALONE_COLOR_IDS.forEach(function(ctrlId) {
256
- var el = document.getElementById(ctrlId);
257
- if (el) el.value = S.getStandaloneDefault(ctrlId);
258
- });
259
- }
260
-
261
- setColorValue('ctrl-color', S.getColorDefault(), false);
262
-
263
- // ── Register on SDocs for cross-module access ──────────
264
-
265
- S.setColorValue = setColorValue;
266
- S.readAllControlValues = readAllControlValues;
267
- S.collectStyles = collectStyles;
268
- S.applyStylesFromMeta = applyStylesFromMeta;
269
- S.resetAllStyles = resetAllStyles;
270
- S.STANDALONE_COLOR_IDS = STANDALONE_COLOR_IDS;
271
-
272
- })();
@@ -1,212 +0,0 @@
1
- // sdocs-export.js — Export pipeline: CSS, HTML, PDF, Word, save-default
2
- (function () {
3
- 'use strict';
4
-
5
- var S = SDocs;
6
-
7
- function cv(name) {
8
- return (S.renderedEl.style.getPropertyValue(name) ||
9
- getComputedStyle(S.renderedEl).getPropertyValue(name)).trim();
10
- }
11
-
12
- function buildExportCSS() {
13
- var bgColor = cv('--md-bg') || '#ffffff';
14
- var fontFamily = cv('--md-font-family') || "'Inter', sans-serif";
15
- var baseSize = cv('--md-base-size') || '16px';
16
- var lineHeight = cv('--md-line-height') || '1.75';
17
- var color = cv('--md-color') || '#1c1917';
18
-
19
- var hFontFamily = cv('--md-h-font-family') || 'inherit';
20
- var hScale = parseFloat(cv('--md-h-scale') || '1');
21
- var hMB = cv('--md-h-margin-bottom')|| '0.4em';
22
- var hColor = cv('--md-h-color') || '#0f0d0c';
23
-
24
- var h1Size = 'calc(' + (cv('--md-h1-size') || '2.1em') + ' * ' + hScale + ')';
25
- var h1Color = cv('--md-h1-color') || hColor;
26
- var h1Weight = cv('--md-h1-weight') || '700';
27
- var h2Size = 'calc(' + (cv('--md-h2-size') || '1.55em') + ' * ' + hScale + ')';
28
- var h2Color = cv('--md-h2-color') || hColor;
29
- var h2Weight = cv('--md-h2-weight') || '600';
30
- var h3Size = 'calc(' + (cv('--md-h3-size') || '1.2em') + ' * ' + hScale + ')';
31
- var h3Color = cv('--md-h3-color') || hColor;
32
- var h3Weight = cv('--md-h3-weight') || '600';
33
- var h4Size = 'calc(' + (cv('--md-h4-size') || '1.0em') + ' * ' + hScale + ')';
34
- var h4Color = cv('--md-h4-color') || hColor;
35
- var h4Weight = cv('--md-h4-weight') || '600';
36
-
37
- var pColor = cv('--md-p-color') || '#3c3733';
38
- var pLH = cv('--md-p-line-height')|| lineHeight;
39
- var pMargin = cv('--md-p-margin') || '0 0 1.1em';
40
-
41
- var linkColor = cv('--md-link-color') || '#2563eb';
42
- var linkDec = cv('--md-link-decoration') || 'underline';
43
-
44
- var codeBG = cv('--md-code-bg') || '#f4f1ed';
45
- var codeColor = cv('--md-code-color') || '#6b21a8';
46
- var codeFont = cv('--md-code-font') || "'JetBrains Mono', monospace";
47
- var preBG = cv('--md-pre-bg') || codeBG;
48
-
49
- var bqBorder = cv('--md-bq-border') || '3px solid #2563eb';
50
- var bqBg = cv('--md-bq-bg') || '#f7f5f2';
51
- var bqColor = cv('--md-bq-color') || '#6b6560';
52
- var bqPad = cv('--md-bq-padding') || '0.5em 1em';
53
- var bqMargin = cv('--md-bq-margin') || '1.2em 0';
54
-
55
- return '\nbody {\n font-family: ' + fontFamily + ';\n font-size: ' + baseSize + ';\n color: ' + color + ';\n line-height: ' + lineHeight + ';\n background-color: ' + bgColor + ';\n max-width: 720px;\n margin: 0 auto;\n padding: 40px 48px 60px;\n -webkit-font-smoothing: antialiased;\n}\nh1 { font-family: ' + hFontFamily + '; font-size: ' + h1Size + '; color: ' + h1Color + '; font-weight: ' + h1Weight + '; letter-spacing: -0.02em; line-height: 1.2; margin: 0 0 ' + hMB + '; }\nh2 { font-family: ' + hFontFamily + '; font-size: ' + h2Size + '; color: ' + h2Color + '; font-weight: ' + h2Weight + '; letter-spacing: -0.015em; line-height: 1.3; margin: 1.4em 0 ' + hMB + '; padding-bottom: 0.3em; border-bottom: 1px solid #ede8e2; }\nh3 { font-family: ' + hFontFamily + '; font-size: ' + h3Size + '; color: ' + h3Color + '; font-weight: ' + h3Weight + '; letter-spacing: -0.01em; line-height: 1.4; margin: 1.2em 0 ' + hMB + '; }\nh4 { font-family: ' + hFontFamily + '; font-size: ' + h4Size + '; color: ' + h4Color + '; font-weight: ' + h4Weight + '; line-height: 1.5; margin: 1em 0 ' + hMB + '; }\np { color: ' + pColor + '; line-height: ' + pLH + '; margin: ' + pMargin + '; }\na { color: ' + linkColor + '; text-decoration: ' + linkDec + '; text-underline-offset: 2px; }\ncode { background: ' + codeBG + '; color: ' + codeColor + '; padding: 0.15em 0.45em; border-radius: 4px; font-family: ' + codeFont + '; font-size: 0.85em; }\npre { background: ' + preBG + '; padding: 1.1em 1.25em; border-radius: 8px; overflow-x: auto; margin: 1.2em 0; border: 1px solid #e7e2db; }\npre code { background: none; padding: 0; color: #3c3733; font-size: 0.88em; }\nblockquote { border-left: ' + bqBorder + '; color: ' + bqColor + '; padding: ' + bqPad + '; margin: ' + bqMargin + '; background: ' + bqBg + '; border-radius: 0 6px 6px 0; }\nblockquote p { margin: 0; color: inherit; }\nul, ol { padding-left: 1.6em; margin: 0.5em 0 1.1em; }\nli { margin-bottom: 0.3em; }\nhr { border: none; border-top: 1px solid #ede8e2; margin: 2em 0; }\ntable { border-collapse: collapse; width: 100%; margin: 1.2em 0; font-size: 0.92em; }\nth, td { border: 1px solid #e2ddd6; padding: 7px 12px; text-align: left; }\nth { background: #f4f1ed; font-weight: 600; }\ntr:nth-child(even) td { background: #fafaf8; }\nimg { max-width: 100%; border-radius: 8px; }\n';
56
- }
57
-
58
- function buildExportHTML() {
59
- var fontName = document.getElementById('ctrl-font-family').value.replace(/['"]/g,'').split(',')[0].trim();
60
- var fontLink = S.GOOGLE_FONTS.includes(fontName)
61
- ? '<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=' + encodeURIComponent(fontName) + ':wght@400;500;600;700&display=swap">'
62
- : '';
63
- var title = (S.currentMeta.title || 'Document').replace(/</g,'&lt;');
64
- var clone = S.renderedEl.cloneNode(true);
65
- clone.querySelectorAll('.section-toggle').forEach(function(el) { el.remove(); });
66
- clone.querySelectorAll('.md-section-body').forEach(function(el) {
67
- while (el.firstChild) el.parentNode.insertBefore(el.firstChild, el);
68
- el.remove();
69
- });
70
- clone.querySelectorAll('.md-section').forEach(function(el) {
71
- while (el.firstChild) el.parentNode.insertBefore(el.firstChild, el);
72
- el.remove();
73
- });
74
- return '<!DOCTYPE html>\n<html>\n<head>\n<meta charset="UTF-8">\n<title>' + title + '</title>\n' + fontLink + '\n<style>' + buildExportCSS() + '</style>\n</head>\n<body>\n' +
75
- clone.innerHTML
76
- .replace(/<button class="copy-btn"[^]*?<\/button>/g, '')
77
- .replace(/<button class="header-copy-btn"[^]*?<\/button>/g, '')
78
- .replace(/<div class="pre-wrapper">([\s\S]*?)<\/div>/g, '$1')
79
- .replace(/<a class="header-anchor"[^]*?<\/a>/g, '') +
80
- '\n</body>\n</html>';
81
- }
82
-
83
- function exportPDF() {
84
- S.setStatus('Opening print dialog\u2026');
85
- var html = buildExportHTML();
86
- var win = window.open('', '_blank', 'width=900,height=700');
87
- win.document.open();
88
- win.document.write(html + '<script>\n document.fonts.ready.then(() => {\n window.focus();\n window.print();\n });\n <\/script>');
89
- win.document.close();
90
- S.setStatus('PDF print dialog opened');
91
- }
92
-
93
- var htmlDocxLoaded = false;
94
-
95
- function loadHtmlDocx() {
96
- return new Promise(function(resolve, reject) {
97
- if (htmlDocxLoaded) { resolve(); return; }
98
- var s = document.createElement('script');
99
- s.src = 'https://cdn.jsdelivr.net/npm/html-docx-js@0.3.1/dist/html-docx.js';
100
- s.onload = function() { htmlDocxLoaded = true; resolve(); };
101
- s.onerror = function() { reject(new Error('Could not load html-docx-js')); };
102
- document.head.appendChild(s);
103
- });
104
- }
105
-
106
- async function exportWord() {
107
- S.setStatus('Generating Word document\u2026');
108
- try {
109
- await loadHtmlDocx();
110
- var html = buildExportHTML();
111
- var blob = window.htmlDocx.asBlob(html, {
112
- orientation: 'portrait',
113
- margins: { top: 720, right: 900, bottom: 720, left: 900 },
114
- });
115
- var a = document.createElement('a');
116
- a.href = URL.createObjectURL(blob);
117
- a.download = (S.currentMeta.title || 'document').replace(/[^a-z0-9_-]/gi,'_') + '.docx';
118
- a.click();
119
- URL.revokeObjectURL(a.href);
120
- S.setStatus('Exported Word .docx');
121
- } catch (e) {
122
- S.setStatus('Word export failed: ' + e.message);
123
- console.error(e);
124
- }
125
- }
126
-
127
- function download(filename, content) {
128
- var a = document.createElement('a');
129
- a.href = URL.createObjectURL(new Blob([content], { type: 'text/plain' }));
130
- a.download = filename;
131
- a.click();
132
- URL.revokeObjectURL(a.href);
133
- }
134
-
135
- // ── Export panel handlers ──────────────────────────────
136
-
137
- document.getElementById('exp-pdf').addEventListener('click', exportPDF);
138
- document.getElementById('exp-word').addEventListener('click', exportWord);
139
-
140
- document.getElementById('exp-raw').addEventListener('click', function() {
141
- download('document.md', S.currentBody);
142
- S.setStatus('Exported raw .md');
143
- });
144
-
145
- document.getElementById('exp-styled').addEventListener('click', function() {
146
- var meta = Object.assign({}, S.currentMeta, { styles: S.collectStyles() });
147
- var fm = SDocYaml.serializeFrontMatter(meta);
148
- download('document.md', fm + '\n' + S.currentBody);
149
- S.setStatus('Exported styled .md with YAML front matter');
150
- });
151
-
152
- // ── Save as default styles ──────────────────────────────
153
-
154
- function formatStyleValue(k, v) {
155
- if (typeof v !== 'object' || v === null) {
156
- return k + ': ' + JSON.stringify(v);
157
- }
158
- // light/dark theme blocks: serialize as nested block (not inline)
159
- if (k === 'light' || k === 'dark') {
160
- var blockLines = [k + ':'];
161
- Object.keys(v).forEach(function(sk) {
162
- var sv = v[sk];
163
- if (typeof sv === 'object' && sv !== null) {
164
- var inner = Object.keys(sv).map(function(a) { return a + ': ' + JSON.stringify(sv[a]); }).join(', ');
165
- blockLines.push(' ' + sk + ': { ' + inner + ' }');
166
- } else {
167
- blockLines.push(' ' + sk + ': ' + JSON.stringify(sv));
168
- }
169
- });
170
- return blockLines.join('\n');
171
- }
172
- // Default: inline object
173
- var inner = Object.keys(v).map(function(a) { return a + ': ' + JSON.stringify(v[a]); }).join(', ');
174
- return k + ': { ' + inner + ' }';
175
- }
176
-
177
- function buildStylesYaml() {
178
- var styles = S.collectStyles();
179
- var lines = [];
180
- Object.keys(styles).forEach(function(k) {
181
- lines.push(formatStyleValue(k, styles[k]));
182
- });
183
- return lines.join('\n');
184
- }
185
-
186
- var saveDefaultCmd = '';
187
-
188
- function refreshSaveDefaultPreview() {
189
- var yaml = buildStylesYaml();
190
- saveDefaultCmd = "mkdir -p ~/.sdocs && cat > ~/.sdocs/styles.yaml << 'SDOCS'\n" + yaml + '\nSDOCS';
191
- document.getElementById('save-default-display').textContent = saveDefaultCmd;
192
- }
193
-
194
- document.querySelector('[data-target="body-save-default"]').addEventListener('click', function() {
195
- // Delay so the panel-body open class is toggled first
196
- setTimeout(refreshSaveDefaultPreview, 0);
197
- });
198
-
199
- document.getElementById('btn-copy-default').addEventListener('click', async function() {
200
- refreshSaveDefaultPreview();
201
- try {
202
- await navigator.clipboard.writeText(saveDefaultCmd);
203
- var msg = document.getElementById('save-default-msg');
204
- msg.textContent = 'Copied! Paste in your terminal to save defaults.';
205
- msg.style.display = 'block';
206
- setTimeout(function() { msg.style.display = 'none'; }, 4000);
207
- } catch (e) {
208
- S.setStatus('Could not copy to clipboard');
209
- }
210
- });
211
-
212
- })();
@@ -1,59 +0,0 @@
1
- // sdocs-state.js — Shared mutable state namespace
2
- // All modules read/write through window.SDocs
3
- (function () {
4
- 'use strict';
5
-
6
- window.SDocs = {
7
- // Document state
8
- currentBody: '',
9
- currentMeta: {},
10
-
11
- // Per-theme color state
12
- activeTheme: 'light',
13
- themeColors: { light: {}, dark: {} },
14
- themeOverridden: { light: new Set(), dark: new Set() },
15
-
16
- // DOM references
17
- renderedEl: document.getElementById('rendered'),
18
- rawEl: document.getElementById('raw'),
19
- writeEl: null,
20
-
21
- // Mode tracking
22
- currentMode: 'read',
23
-
24
- // Sync flags
25
- _syncing: false,
26
- _isDefaultState: true,
27
- _hashTimer: null,
28
- _rawSyncTimer: null,
29
- _writeSyncTimer: null,
30
-
31
- // Cross-module functions (registered by defining module)
32
- // Theme: toggleTheme, getThemeDefaults, getColorDefault, getStandaloneDefault,
33
- // loadGoogleFont, updateDefaultColors, GOOGLE_FONTS,
34
- // switchThemeAndUpdate, saveCurrentThemeColors, loadThemeColors
35
- // Controls: setColorValue, readAllControlValues, collectStyles,
36
- // applyStylesFromMeta, resetAllStyles, STANDALONE_COLOR_IDS
37
- // App: syncAll, setStatus, setMode, render, loadText
38
- // Write: enterWriteMode, exitWriteMode
39
- };
40
-
41
- // Backwards compat: S.overriddenColors delegates to active theme's set
42
- Object.defineProperty(SDocs, 'overriddenColors', {
43
- get: function() { return SDocs.themeOverridden[SDocs.activeTheme]; },
44
- set: function(v) { SDocs.themeOverridden[SDocs.activeTheme] = v; },
45
- enumerable: true,
46
- configurable: true,
47
- });
48
-
49
- SDocs.contentAreaEl = document.getElementById('content-area');
50
-
51
- SDocs.setStyleVar = function(cssVar, value) {
52
- SDocs.renderedEl.style.setProperty(cssVar, value);
53
- if (SDocs.writeEl) SDocs.writeEl.style.setProperty(cssVar, value);
54
- if (cssVar === '--md-bg') {
55
- SDocs.contentAreaEl.style.setProperty('background-color', value);
56
- }
57
- };
58
-
59
- })();