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
package/public/css/mobile.css
CHANGED
|
@@ -26,6 +26,13 @@
|
|
|
26
26
|
#write { padding: 24px 20px 40px; max-width: 100%; }
|
|
27
27
|
#raw { padding: 20px 16px; min-height: calc(100dvh - 36px); font-size: 16px; }
|
|
28
28
|
|
|
29
|
+
/* ── File-info card: inset 20px from viewport edges ── */
|
|
30
|
+
#file-info-card {
|
|
31
|
+
max-width: none;
|
|
32
|
+
margin: 16px 20px 0;
|
|
33
|
+
padding: 8px 12px;
|
|
34
|
+
}
|
|
35
|
+
|
|
29
36
|
/* ── Write toolbar: sticky below left-toolbar ── */
|
|
30
37
|
body.write-mode #write-toolbar { position: sticky; top: 36px; z-index: 98; justify-content: flex-start; }
|
|
31
38
|
|
package/public/css/panel.css
CHANGED
package/public/css/rendered.css
CHANGED
|
@@ -104,6 +104,7 @@
|
|
|
104
104
|
color: var(--md-link-color);
|
|
105
105
|
text-decoration: var(--md-link-decoration);
|
|
106
106
|
text-underline-offset: 2px;
|
|
107
|
+
cursor: pointer;
|
|
107
108
|
}
|
|
108
109
|
:is(#rendered, #write) a:hover { opacity: .8; }
|
|
109
110
|
|
|
@@ -129,7 +130,15 @@
|
|
|
129
130
|
#rendered .section-toggle.open {
|
|
130
131
|
transform: rotate(90deg);
|
|
131
132
|
}
|
|
132
|
-
#rendered .section-toggle:hover
|
|
133
|
+
#rendered .section-toggle:hover,
|
|
134
|
+
#rendered h1:hover > .section-toggle,
|
|
135
|
+
#rendered .md-section > h2:hover > .section-toggle,
|
|
136
|
+
#rendered .md-section > h3:hover > .section-toggle,
|
|
137
|
+
#rendered .md-section > h4:hover > .section-toggle,
|
|
138
|
+
#rendered > h1:hover ~ .md-section .section-toggle,
|
|
139
|
+
#rendered .md-section > h2:hover ~ .md-section-body .section-toggle,
|
|
140
|
+
#rendered .md-section > h3:hover ~ .md-section-body .section-toggle,
|
|
141
|
+
#rendered .md-section > h4:hover ~ .md-section-body .section-toggle {
|
|
133
142
|
color: var(--text-2);
|
|
134
143
|
}
|
|
135
144
|
#rendered .md-section-body:not(.open):not(:has(.md-section-body.open)) > :not(.md-section) {
|
|
@@ -254,6 +263,81 @@
|
|
|
254
263
|
white-space: pre;
|
|
255
264
|
word-break: normal;
|
|
256
265
|
}
|
|
266
|
+
/* ── Charts ── */
|
|
267
|
+
.sdoc-chart {
|
|
268
|
+
margin: 1.2em 0;
|
|
269
|
+
padding: 16px;
|
|
270
|
+
border-radius: var(--radius, 8px);
|
|
271
|
+
background: var(--md-chart-bg, var(--md-block-bg, #F5F2EE));
|
|
272
|
+
max-width: 100%;
|
|
273
|
+
position: relative;
|
|
274
|
+
}
|
|
275
|
+
.sdoc-chart canvas { max-height: 400px; }
|
|
276
|
+
.sdoc-chart-error { outline: 2px dashed #ef4444; }
|
|
277
|
+
|
|
278
|
+
/* Chart menu */
|
|
279
|
+
.chart-menu-btn {
|
|
280
|
+
position: absolute; top: 6px; right: 6px;
|
|
281
|
+
background: var(--md-chart-bg, var(--md-block-bg, #F5F2EE));
|
|
282
|
+
border: 1px solid var(--md-copy-btn-border, rgba(0,0,0,0.1));
|
|
283
|
+
border-radius: 4px;
|
|
284
|
+
padding: 3px 5px;
|
|
285
|
+
cursor: pointer; opacity: 0.7;
|
|
286
|
+
transition: opacity .15s;
|
|
287
|
+
color: var(--md-chart-text, var(--text-3, #999));
|
|
288
|
+
font-size: 12px; line-height: 1;
|
|
289
|
+
display: flex; align-items: center; justify-content: center;
|
|
290
|
+
z-index: 2;
|
|
291
|
+
}
|
|
292
|
+
.chart-menu-btn:hover { opacity: 1; background: var(--md-copy-btn-hover, rgba(0,0,0,0.05)); }
|
|
293
|
+
|
|
294
|
+
.chart-menu {
|
|
295
|
+
display: none; position: absolute; top: 36px; right: 8px;
|
|
296
|
+
min-width: 160px; width: max-content; background: var(--bg-surface, #fff);
|
|
297
|
+
border: 1px solid var(--border, #e0dcd7);
|
|
298
|
+
border-radius: var(--radius, 8px);
|
|
299
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.12);
|
|
300
|
+
padding: 6px 0; z-index: 10; font-size: 13px; color: var(--text, #333);
|
|
301
|
+
}
|
|
302
|
+
.chart-menu.open { display: block; }
|
|
303
|
+
|
|
304
|
+
.chart-menu-item {
|
|
305
|
+
display: block; width: 100%; padding: 6px 14px;
|
|
306
|
+
border: none; background: none; text-align: left;
|
|
307
|
+
cursor: pointer; font-size: 13px; color: var(--text, #333);
|
|
308
|
+
}
|
|
309
|
+
.chart-menu-item:hover { background: var(--bg-hover, #f5f3f0); }
|
|
310
|
+
|
|
311
|
+
.chart-menu-sep { height: 1px; background: var(--border, #e0dcd7); margin: 4px 0; }
|
|
312
|
+
|
|
313
|
+
.chart-menu-toggle {
|
|
314
|
+
display: flex; align-items: center; gap: 6px;
|
|
315
|
+
padding: 5px 14px; cursor: pointer; font-size: 13px; color: var(--text-2, #666);
|
|
316
|
+
}
|
|
317
|
+
.chart-menu-toggle:hover { background: var(--bg-hover, #f5f3f0); }
|
|
318
|
+
.chart-menu-toggle input { margin: 0; }
|
|
319
|
+
|
|
320
|
+
.chart-menu-types { display: flex; gap: 3px; padding: 4px 10px; flex-wrap: wrap; }
|
|
321
|
+
.chart-type-btn {
|
|
322
|
+
padding: 3px 10px; border: 1px solid var(--border, #e0dcd7);
|
|
323
|
+
background: none; border-radius: var(--radius-sm, 4px);
|
|
324
|
+
font-size: 11px; cursor: pointer; color: var(--text-2, #666);
|
|
325
|
+
white-space: nowrap;
|
|
326
|
+
}
|
|
327
|
+
.chart-type-btn:hover { background: var(--bg-hover, #f5f3f0); }
|
|
328
|
+
.chart-type-btn.active {
|
|
329
|
+
background: var(--bg-input, #fff); color: var(--text, #333);
|
|
330
|
+
border-color: var(--border-strong, #c0bbb5); box-shadow: 0 1px 2px rgba(0,0,0,0.06);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.chart-menu-input {
|
|
334
|
+
display: block; width: calc(100% - 28px); margin: 4px 14px;
|
|
335
|
+
padding: 4px 8px; border: 1px solid var(--border, #e0dcd7);
|
|
336
|
+
border-radius: var(--radius-sm, 4px); font-size: 12px;
|
|
337
|
+
background: var(--bg-input, #fff); color: var(--text, #333);
|
|
338
|
+
}
|
|
339
|
+
.chart-menu-input:focus { outline: none; border-color: var(--accent, #3b82f6); }
|
|
340
|
+
|
|
257
341
|
:is(#rendered, #write) blockquote {
|
|
258
342
|
border-left: var(--md-bq-border);
|
|
259
343
|
font-size: var(--md-bq-size);
|
|
@@ -293,3 +377,102 @@
|
|
|
293
377
|
}
|
|
294
378
|
:is(#rendered, #write) tr:nth-child(even) td { background: var(--md-table-even-bg); }
|
|
295
379
|
:is(#rendered, #write) img { max-width: 100%; border-radius: var(--radius-md); }
|
|
380
|
+
|
|
381
|
+
/* ── File-info card (shown when front matter has `file:`) ─────────── */
|
|
382
|
+
/* Inherits the block cascade: --md-fic-bg / --md-fic-text fall back to
|
|
383
|
+
--md-block-bg / --md-block-text so the card blends in with code blocks,
|
|
384
|
+
blockquotes, and charts. Override per-doc via `styles.fileinfo` in YAML. */
|
|
385
|
+
#file-info-card[hidden] { display: none; }
|
|
386
|
+
#file-info-card {
|
|
387
|
+
--fic-bg: var(--md-fic-bg, var(--md-block-bg, var(--bg)));
|
|
388
|
+
--fic-text: var(--md-fic-text, var(--md-block-text, var(--text)));
|
|
389
|
+
--fic-muted: color-mix(in srgb, var(--fic-text) 60%, transparent);
|
|
390
|
+
--fic-border: color-mix(in srgb, var(--fic-text) 15%, transparent);
|
|
391
|
+
--fic-hover: color-mix(in srgb, var(--fic-text) 8%, transparent);
|
|
392
|
+
|
|
393
|
+
max-width: 680px;
|
|
394
|
+
margin: 24px auto 0;
|
|
395
|
+
padding: 10px 16px;
|
|
396
|
+
background: var(--fic-bg);
|
|
397
|
+
border: 1px solid var(--fic-border);
|
|
398
|
+
border-radius: var(--radius-md);
|
|
399
|
+
box-shadow: var(--shadow-sm);
|
|
400
|
+
font-family: var(--font-ui);
|
|
401
|
+
font-size: 13px;
|
|
402
|
+
color: var(--fic-text);
|
|
403
|
+
display: flex;
|
|
404
|
+
flex-direction: column;
|
|
405
|
+
gap: 6px;
|
|
406
|
+
}
|
|
407
|
+
#file-info-card .fic-rows { display: flex; flex-direction: column; gap: 4px; }
|
|
408
|
+
#file-info-card .fic-row {
|
|
409
|
+
display: flex;
|
|
410
|
+
align-items: center;
|
|
411
|
+
gap: 8px;
|
|
412
|
+
padding: 2px 0;
|
|
413
|
+
border-radius: var(--radius-sm);
|
|
414
|
+
}
|
|
415
|
+
#file-info-card .fic-label {
|
|
416
|
+
color: var(--fic-muted);
|
|
417
|
+
font-size: 11px;
|
|
418
|
+
font-weight: 600;
|
|
419
|
+
text-transform: uppercase;
|
|
420
|
+
letter-spacing: 0.04em;
|
|
421
|
+
min-width: 52px;
|
|
422
|
+
}
|
|
423
|
+
#file-info-card .fic-value {
|
|
424
|
+
flex: 1;
|
|
425
|
+
min-width: 0;
|
|
426
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
427
|
+
font-size: 12.5px;
|
|
428
|
+
color: var(--fic-text);
|
|
429
|
+
overflow-wrap: anywhere;
|
|
430
|
+
word-break: break-word;
|
|
431
|
+
}
|
|
432
|
+
#file-info-card .fic-local-pill {
|
|
433
|
+
font-size: 10px;
|
|
434
|
+
font-weight: 600;
|
|
435
|
+
text-transform: uppercase;
|
|
436
|
+
letter-spacing: 0.04em;
|
|
437
|
+
color: var(--fic-muted);
|
|
438
|
+
background: var(--fic-hover);
|
|
439
|
+
border: 1px solid var(--fic-border);
|
|
440
|
+
border-radius: 999px;
|
|
441
|
+
padding: 2px 7px;
|
|
442
|
+
}
|
|
443
|
+
#file-info-card .fic-copy,
|
|
444
|
+
#file-info-card .fic-action {
|
|
445
|
+
display: inline-flex;
|
|
446
|
+
align-items: center;
|
|
447
|
+
gap: 6px;
|
|
448
|
+
background: transparent;
|
|
449
|
+
border: 1px solid var(--fic-border);
|
|
450
|
+
color: var(--fic-text);
|
|
451
|
+
border-radius: var(--radius-sm);
|
|
452
|
+
padding: 4px 8px;
|
|
453
|
+
font-family: var(--font-ui);
|
|
454
|
+
font-size: 12px;
|
|
455
|
+
cursor: pointer;
|
|
456
|
+
transition: background .1s, border-color .1s, color .1s;
|
|
457
|
+
}
|
|
458
|
+
#file-info-card .fic-copy { padding: 3px 6px; }
|
|
459
|
+
#file-info-card .fic-copy:hover,
|
|
460
|
+
#file-info-card .fic-action:hover {
|
|
461
|
+
background: var(--fic-hover);
|
|
462
|
+
color: var(--fic-text);
|
|
463
|
+
}
|
|
464
|
+
#file-info-card .fic-footer {
|
|
465
|
+
display: flex;
|
|
466
|
+
align-items: center;
|
|
467
|
+
gap: 10px;
|
|
468
|
+
margin-top: 2px;
|
|
469
|
+
}
|
|
470
|
+
#file-info-card .fic-privacy-note {
|
|
471
|
+
margin-left: auto;
|
|
472
|
+
display: inline-flex;
|
|
473
|
+
align-items: center;
|
|
474
|
+
gap: 6px;
|
|
475
|
+
font-size: 11px;
|
|
476
|
+
color: var(--fic-muted);
|
|
477
|
+
}
|
|
478
|
+
#file-info-card .fic-privacy-note[hidden] { display: none; }
|
package/public/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<link rel="stylesheet" href="/public/css/panel.css">
|
|
12
12
|
<link rel="stylesheet" href="/public/css/write.css">
|
|
13
13
|
<link rel="stylesheet" href="/public/css/mobile.css">
|
|
14
|
-
<script>
|
|
14
|
+
<script nonce="__CSP_NONCE__">
|
|
15
15
|
(function(){
|
|
16
16
|
var t = localStorage.getItem('sdocs-theme');
|
|
17
17
|
if (!t) t = window.matchMedia('(prefers-color-scheme:dark)').matches ? 'dark' : 'light';
|
|
@@ -46,22 +46,14 @@
|
|
|
46
46
|
<button class="btn" id="btn-export" title="Export">
|
|
47
47
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
|
48
48
|
</button>
|
|
49
|
-
<span class="write-tb-sep"></span>
|
|
50
49
|
<button class="btn" id="btn-new" title="New document">
|
|
51
50
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11.35 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v5.35"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M14 19h6"/><path d="M17 16v6"/></svg>
|
|
52
51
|
</button>
|
|
52
|
+
<span class="write-tb-sep"></span>
|
|
53
|
+
<button class="btn" id="btn-theme" title="Toggle dark mode" aria-label="Toggle dark mode">
|
|
54
|
+
<svg id="icon-theme" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" stroke-width="2"/></svg>
|
|
55
|
+
</button>
|
|
53
56
|
</div>
|
|
54
|
-
<button id="btn-expand-all" class="toolbar-icon-btn" title="Expand all sections" aria-label="Expand all sections">
|
|
55
|
-
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M15 6V11C15 13.21 13.21 15 11 15H6C5.26 15 4.62 14.6 4.27 14H11C12.65 14 14 12.65 14 11V4.27C14.6 4.62 15 5.26 15 6ZM11 13H4C2.897 13 2 12.103 2 11V4C2 2.897 2.897 2 4 2H11C12.103 2 13 2.897 13 4V11C13 12.103 12.103 13 11 13ZM4 12H11C11.551 12 12 11.552 12 11V4C12 3.449 11.551 3 11 3H4C3.449 3 3 3.449 3 4V11C3 11.552 3.449 12 4 12ZM9.5 7H8V5.5C8 5.224 7.776 5 7.5 5C7.224 5 7 5.224 7 5.5V7H5.5C5.224 7 5 7.224 5 7.5C5 7.776 5.224 8 5.5 8H7V9.5C7 9.776 7.224 10 7.5 10C7.776 10 8 9.776 8 9.5V8H9.5C9.776 8 10 7.776 10 7.5C10 7.224 9.776 7 9.5 7Z"/></svg>
|
|
56
|
-
</button>
|
|
57
|
-
<button id="btn-collapse-all" class="toolbar-icon-btn" title="Collapse all sections" aria-label="Collapse all sections">
|
|
58
|
-
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M14 4.27051C14.5999 4.62053 15 5.26009 15 6V11C15 13.21 13.21 15 11 15H6C5.26009 15 4.62053 14.5999 4.27051 14H11C12.65 14 14 12.65 14 11V4.27051Z"/><path d="M9.5 7C9.776 7 10 7.224 10 7.5C10 7.776 9.776 8 9.5 8H5.5C5.224 8 5 7.776 5 7.5C5 7.224 5.224 7 5.5 7H9.5Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M11 2C12.103 2 13 2.897 13 4V11C13 12.103 12.103 13 11 13H4C2.897 13 2 12.103 2 11V4C2 2.897 2.897 2 4 2H11ZM4 3C3.449 3 3 3.449 3 4V11C3 11.552 3.449 12 4 12H11C11.551 12 12 11.552 12 11V4C12 3.449 11.551 3 11 3H4Z"/></svg>
|
|
59
|
-
</button>
|
|
60
|
-
<button id="btn-theme" title="Toggle dark mode" aria-label="Toggle dark mode">
|
|
61
|
-
<svg id="icon-theme" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
62
|
-
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
|
|
63
|
-
</svg>
|
|
64
|
-
</button>
|
|
65
57
|
<span id="drop-hint">
|
|
66
58
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M6 1v7M3.5 5.5L6 8l2.5-2.5M2 10h8" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
67
59
|
Drop .md to load
|
|
@@ -105,7 +97,17 @@
|
|
|
105
97
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M12 4v16"/><line x1="4" y1="20" x2="20" y2="4" stroke-width="1.5" opacity="0.5"/></svg>
|
|
106
98
|
</button>
|
|
107
99
|
</div>
|
|
108
|
-
<div id="content-area">
|
|
100
|
+
<div id="content-area" tabindex="0">
|
|
101
|
+
<div id="file-info-card" hidden>
|
|
102
|
+
<div class="fic-rows"></div>
|
|
103
|
+
<div class="fic-footer">
|
|
104
|
+
<button class="fic-action" id="fic-copy-all" title="Copy the full markdown (body + front matter)">
|
|
105
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
106
|
+
<span>Copy whole file</span>
|
|
107
|
+
</button>
|
|
108
|
+
<span class="fic-privacy-note" hidden><span class="fic-local-pill">local only</span> rows aren't included in shared sdocs</span>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
109
111
|
<div id="rendered"></div>
|
|
110
112
|
<div id="write" contenteditable="true" spellcheck="true"></div>
|
|
111
113
|
<textarea id="raw" spellcheck="false"></textarea>
|
|
@@ -218,37 +220,87 @@
|
|
|
218
220
|
</div>
|
|
219
221
|
|
|
220
222
|
<div class="sub-section">
|
|
221
|
-
<div class="sub-header" data-target="sub-colors-
|
|
222
|
-
<span class="arrow">▶</span>
|
|
223
|
+
<div class="sub-header" data-target="sub-colors-blocks">
|
|
224
|
+
<span class="arrow">▶</span> Blocks
|
|
223
225
|
</div>
|
|
224
|
-
<div class="sub-body" id="sub-colors-
|
|
226
|
+
<div class="sub-body" id="sub-colors-blocks">
|
|
227
|
+
<p style="font-size:11px; color:var(--text-3); margin:0 0 8px; line-height:1.5;">Default for code blocks, blockquotes, and charts. Override individually below.</p>
|
|
225
228
|
<div class="control-row">
|
|
226
229
|
<label>Background</label>
|
|
227
|
-
<div class="color-pair"><input type="color" id="ctrl-
|
|
230
|
+
<div class="color-pair"><input type="color" id="ctrl-block-bg" value="#f4f1ed"><button class="reset-btn" id="reset-block-bg" title="Reset to default">↺</button></div>
|
|
228
231
|
</div>
|
|
229
232
|
<div class="control-row">
|
|
230
233
|
<label>Text</label>
|
|
231
|
-
<div class="color-pair"><input type="color" id="ctrl-
|
|
234
|
+
<div class="color-pair"><input type="color" id="ctrl-block-text" value="#6b6560"><button class="reset-btn" id="reset-block-text" title="Reset to default">↺</button></div>
|
|
232
235
|
</div>
|
|
233
|
-
</div>
|
|
234
|
-
</div>
|
|
235
236
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
237
|
+
<div class="sub-section">
|
|
238
|
+
<div class="sub-header" data-target="sub-colors-code">
|
|
239
|
+
<span class="arrow">▶</span> Code
|
|
240
|
+
</div>
|
|
241
|
+
<div class="sub-body" id="sub-colors-code">
|
|
242
|
+
<div class="control-row">
|
|
243
|
+
<label>Background</label>
|
|
244
|
+
<div class="color-pair"><input type="color" id="ctrl-code-bg" value="#f4f1ed"><button class="reset-btn" id="reset-code-bg" title="Follow block background">↺</button></div>
|
|
245
|
+
</div>
|
|
246
|
+
<div class="control-row">
|
|
247
|
+
<label>Text</label>
|
|
248
|
+
<div class="color-pair"><input type="color" id="ctrl-code-color" value="#6b21a8"><button class="reset-btn" id="reset-code-color" title="Follow block text">↺</button></div>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
244
251
|
</div>
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
<div class="
|
|
252
|
+
|
|
253
|
+
<div class="sub-section">
|
|
254
|
+
<div class="sub-header" data-target="sub-colors-bq">
|
|
255
|
+
<span class="arrow">▶</span> Blockquote
|
|
256
|
+
</div>
|
|
257
|
+
<div class="sub-body" id="sub-colors-bq">
|
|
258
|
+
<div class="control-row">
|
|
259
|
+
<label>Border</label>
|
|
260
|
+
<div class="color-pair"><input type="color" id="ctrl-bq-border-color" value="#2563eb"><button class="reset-btn" id="reset-bq-border-color" title="Reset to default">↺</button></div>
|
|
261
|
+
</div>
|
|
262
|
+
<div class="control-row">
|
|
263
|
+
<label>Background</label>
|
|
264
|
+
<div class="color-pair"><input type="color" id="ctrl-bq-bg" value="#f7f5f2"><button class="reset-btn" id="reset-bq-bg" title="Follow block background">↺</button></div>
|
|
265
|
+
</div>
|
|
266
|
+
<div class="control-row">
|
|
267
|
+
<label>Text</label>
|
|
268
|
+
<div class="color-pair"><input type="color" id="ctrl-bq-color" value="#6b6560"><button class="reset-btn" id="reset-bq-color" title="Follow block text">↺</button></div>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
248
271
|
</div>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
<div class="
|
|
272
|
+
|
|
273
|
+
<div class="sub-section">
|
|
274
|
+
<div class="sub-header" data-target="sub-colors-charts">
|
|
275
|
+
<span class="arrow">▶</span> Charts
|
|
276
|
+
</div>
|
|
277
|
+
<div class="sub-body" id="sub-colors-charts">
|
|
278
|
+
<div class="control-row">
|
|
279
|
+
<label>Background</label>
|
|
280
|
+
<div class="color-pair"><input type="color" id="ctrl-chart-bg" value="#f4f1ed"><button class="reset-btn" id="reset-chart-bg" title="Follow block background">↺</button></div>
|
|
281
|
+
</div>
|
|
282
|
+
<div class="control-row">
|
|
283
|
+
<label>Text</label>
|
|
284
|
+
<div class="color-pair"><input type="color" id="ctrl-chart-text" value="#78716c"><button class="reset-btn" id="reset-chart-text" title="Follow block text">↺</button></div>
|
|
285
|
+
</div>
|
|
286
|
+
<div class="control-row">
|
|
287
|
+
<label>Accent</label>
|
|
288
|
+
<div class="color-pair"><input type="color" id="ctrl-chart-accent" value="#3b82f6"><button class="reset-btn" id="reset-chart-accent" title="Reset to default palette">↺</button></div>
|
|
289
|
+
</div>
|
|
290
|
+
<div class="control-row">
|
|
291
|
+
<label>Palette</label>
|
|
292
|
+
<select id="ctrl-chart-palette">
|
|
293
|
+
<option value="monochrome" selected>Monochrome</option>
|
|
294
|
+
<option value="complementary">Complementary</option>
|
|
295
|
+
<option value="analogous">Analogous</option>
|
|
296
|
+
<option value="triadic">Triadic</option>
|
|
297
|
+
<option value="pastel">Pastel</option>
|
|
298
|
+
<option value="warm">Warm</option>
|
|
299
|
+
<option value="cool">Cool</option>
|
|
300
|
+
<option value="earth">Earth</option>
|
|
301
|
+
</select>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
252
304
|
</div>
|
|
253
305
|
</div>
|
|
254
306
|
</div>
|
|
@@ -567,28 +619,70 @@
|
|
|
567
619
|
|
|
568
620
|
<!-- Status bar -->
|
|
569
621
|
<div id="statusbar">
|
|
570
|
-
<span
|
|
571
|
-
<span
|
|
572
|
-
|
|
573
|
-
|
|
622
|
+
<span id="status-text"></span>
|
|
623
|
+
<span style="margin-left:auto; font-size:10.5px; color:var(--text-2); display:flex; align-items:center; gap:4px; flex-wrap:wrap; justify-content:flex-end">
|
|
624
|
+
<span>100% private (<a href="https://sdocs.dev/#sec=privacy" target="_blank" rel="noopener" style="color:var(--text-2); text-decoration:underline">learn why</a>)</span>
|
|
625
|
+
· CLI for you & your agents: <span id="cli-install-wrap" style="display:inline-flex; align-items:center; gap:3px; cursor:pointer" title="Click to copy install command"><code id="cli-install-cmd" style="background:var(--bg-surface); border:1px solid var(--md-link-color, #2563eb); border-radius:3px; padding:1px 5px; font-size:10px; color:var(--md-link-color, #2563eb)">npm i -g sdocs-dev</code><button id="cli-copy-btn" style="background:none; border:none; cursor:pointer; color:var(--md-link-color, #2563eb); padding:0; vertical-align:middle; opacity:0.5; transition:opacity .15s"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button></span>
|
|
626
|
+
· <a href="https://github.com/JoshInLisbon/SDocs" target="_blank" rel="noopener" style="color:var(--text-2); text-decoration:underline">GitHub</a>
|
|
574
627
|
</span>
|
|
575
628
|
</div>
|
|
629
|
+
<style>
|
|
630
|
+
#cli-install-wrap:hover #cli-copy-btn { opacity: 1 !important; }
|
|
631
|
+
</style>
|
|
632
|
+
<script nonce="__CSP_NONCE__">
|
|
633
|
+
(function () {
|
|
634
|
+
var COPY_ICON = '<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
|
|
635
|
+
var CHECK_ICON = '<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>';
|
|
636
|
+
var wrap = document.getElementById('cli-install-wrap');
|
|
637
|
+
var btn = document.getElementById('cli-copy-btn');
|
|
638
|
+
if (wrap && btn) wrap.addEventListener('click', function () {
|
|
639
|
+
navigator.clipboard.writeText('npm i -g sdocs-dev').then(function () {
|
|
640
|
+
btn.innerHTML = CHECK_ICON;
|
|
641
|
+
setTimeout(function () { btn.innerHTML = COPY_ICON; }, 1500);
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
})();
|
|
645
|
+
</script>
|
|
576
646
|
|
|
577
|
-
<script src="/public/brotli-wasm.js"></script>
|
|
647
|
+
<script src="/public/brotli-wasm-v1.js"></script>
|
|
578
648
|
<script src="/public/vendor/marked.min.js"></script>
|
|
649
|
+
<script src="/public/vendor/purify.min.js"></script>
|
|
579
650
|
<script src="/public/sdocs-yaml.js"></script>
|
|
580
651
|
<script src="/public/sdocs-styles.js"></script>
|
|
581
652
|
<script src="/public/sdocs-state.js"></script>
|
|
653
|
+
<script src="/public/sdocs-slugify.js"></script>
|
|
582
654
|
<script src="/public/sdocs-theme.js"></script>
|
|
583
655
|
<script src="/public/sdocs-controls.js"></script>
|
|
584
656
|
<script src="/public/sdocs-export.js"></script>
|
|
585
657
|
<script src="/public/sdocs-write.js"></script>
|
|
658
|
+
<script src="/public/sdocs-charts.js"></script>
|
|
586
659
|
<script src="/public/sdocs-app.js"></script>
|
|
587
|
-
<script>
|
|
660
|
+
<script nonce="__CSP_NONCE__">
|
|
588
661
|
var APP_VERSION = '__APP_VERSION__';
|
|
589
|
-
|
|
662
|
+
var SDOCS_DEV = '__SDOCS_DEV__' === '1';
|
|
663
|
+
// Cohort: store the ISO week of first visit for retention tracking.
|
|
664
|
+
// Not a unique ID — groups all visitors who first came the same week.
|
|
665
|
+
if (!localStorage.getItem('sdocs_cohort')) {
|
|
666
|
+
try {
|
|
667
|
+
var _d = new Date(), _u = new Date(Date.UTC(_d.getFullYear(), _d.getMonth(), _d.getDate()));
|
|
668
|
+
var _dn = _u.getUTCDay() || 7;
|
|
669
|
+
_u.setUTCDate(_u.getUTCDate() + 4 - _dn);
|
|
670
|
+
var _ys = new Date(Date.UTC(_u.getUTCFullYear(), 0, 1));
|
|
671
|
+
var _wn = Math.ceil((((_u - _ys) / 86400000) + 1) / 7);
|
|
672
|
+
localStorage.setItem('sdocs_cohort', _u.getUTCFullYear() + '-W' + String(_wn).padStart(2, '0'));
|
|
673
|
+
} catch (e) { /* private browsing or storage disabled */ }
|
|
674
|
+
}
|
|
675
|
+
if (SDOCS_DEV && 'serviceWorker' in navigator) {
|
|
676
|
+
// Dev mode: unregister any existing SW and clear caches so edits are picked up instantly.
|
|
677
|
+
navigator.serviceWorker.getRegistrations().then(function (regs) {
|
|
678
|
+
regs.forEach(function (r) { r.unregister(); });
|
|
679
|
+
});
|
|
680
|
+
if (window.caches) caches.keys().then(function (ks) { ks.forEach(function (k) { caches.delete(k); }); });
|
|
681
|
+
} else if ('serviceWorker' in navigator) {
|
|
682
|
+
var _cohort = '';
|
|
683
|
+
try { _cohort = localStorage.getItem('sdocs_cohort') || ''; } catch (e) {}
|
|
590
684
|
navigator.serviceWorker.register('/sw.js').then(function (reg) {
|
|
591
|
-
var msg = { type: 'check-update', version: APP_VERSION };
|
|
685
|
+
var msg = { type: 'check-update', version: APP_VERSION, cohort: _cohort };
|
|
592
686
|
if (reg.active) reg.active.postMessage(msg);
|
|
593
687
|
navigator.serviceWorker.addEventListener('controllerchange', function () {
|
|
594
688
|
if (navigator.serviceWorker.controller) navigator.serviceWorker.controller.postMessage(msg);
|