sdocs-dev 1.6.2 → 1.13.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/bin/sdocs-bridge.js +974 -0
- package/bin/sdocs-dev.js +152 -2102
- package/bin/sdocs-icon-names.js +1965 -0
- package/lib/agent-block.js +245 -0
- package/lib/agent-files.js +162 -0
- package/lib/bridge-commands.js +171 -0
- package/lib/cells-transclude.js +114 -0
- package/lib/cells-verify.js +165 -0
- package/lib/commands.js +291 -0
- package/lib/constants.js +283 -0
- package/lib/help-text.js +2759 -0
- package/lib/io.js +177 -0
- package/lib/library-autostart.js +145 -0
- package/lib/library-commands.js +307 -0
- package/lib/library-ephemeral.js +111 -0
- package/lib/library-index.js +285 -0
- package/lib/library-paths.js +20 -0
- package/lib/library-scan.js +258 -0
- package/lib/library-server.js +400 -0
- package/lib/library-store.js +141 -0
- package/lib/router.js +52 -0
- package/lib/safe.js +200 -0
- package/lib/setup.js +351 -0
- package/lib/short-link.js +105 -0
- package/lib/styles.js +91 -0
- package/lib/update-check.js +163 -0
- package/lib/url.js +111 -0
- package/package.json +5 -16
- package/shared/sdocs-cells-formula.js +485 -0
- package/shared/sdocs-cells.js +389 -0
- package/shared/sdocs-contrast.js +196 -0
- package/shared/sdocs-form-block.js +605 -0
- package/shared/sdocs-library-tags.js +41 -0
- package/{public → shared}/sdocs-styles.js +134 -5
- package/README.md +0 -149
- /package/{public → shared}/sdocs-slugify.js +0 -0
- /package/{public → shared}/sdocs-yaml.js +0 -0
|
@@ -69,6 +69,11 @@ const CTRL_CSS_MAP = {
|
|
|
69
69
|
'_sd_ctrl-list-indent-num': { cssVar: '--md-list-indent', suffix: 'em' },
|
|
70
70
|
'_sd_ctrl-chart-accent': { cssVar: '--md-chart-accent' },
|
|
71
71
|
'_sd_ctrl-chart-palette': { cssVar: '--md-chart-palette' },
|
|
72
|
+
'_sd_ctrl-table-border': { cssVar: '--md-table-border' },
|
|
73
|
+
'_sd_ctrl-table-header-bg': { cssVar: '--md-table-header-bg' },
|
|
74
|
+
'_sd_ctrl-table-even-bg': { cssVar: '--md-table-even-bg' },
|
|
75
|
+
'_sd_ctrl-table-odd-bg': { cssVar: '--md-table-odd-bg' },
|
|
76
|
+
'_sd_ctrl-table-text': { cssVar: '--md-table-text' },
|
|
72
77
|
};
|
|
73
78
|
|
|
74
79
|
// Range ↔ Number input pairs
|
|
@@ -136,17 +141,42 @@ function hslToHex(h, s, l) {
|
|
|
136
141
|
* Keeps hue and saturation, mirrors lightness around 50%.
|
|
137
142
|
* Slightly biased: dark bgs get very dark (L≈10-20), light text gets bright (L≈80-90).
|
|
138
143
|
*/
|
|
144
|
+
// Color controls whose value is painted as TEXT rather than a fill. Their
|
|
145
|
+
// dark-mode counterpart must stay readable, so a very dark text color is
|
|
146
|
+
// lightened instead of preserved as an "intentional dark background".
|
|
147
|
+
var TEXT_COLOR_CONTROLS = {
|
|
148
|
+
'_sd_ctrl-color': 1, '_sd_ctrl-h-color': 1,
|
|
149
|
+
'_sd_ctrl-h1-color': 1, '_sd_ctrl-h2-color': 1,
|
|
150
|
+
'_sd_ctrl-h3-color': 1, '_sd_ctrl-h4-color': 1,
|
|
151
|
+
'_sd_ctrl-p-color': 1, '_sd_ctrl-list-color': 1,
|
|
152
|
+
'_sd_ctrl-link-color': 1, '_sd_ctrl-block-text': 1,
|
|
153
|
+
'_sd_ctrl-code-color': 1, '_sd_ctrl-bq-color': 1,
|
|
154
|
+
'_sd_ctrl-chart-text': 1, '_sd_ctrl-table-text': 1
|
|
155
|
+
};
|
|
156
|
+
|
|
139
157
|
/**
|
|
140
|
-
*
|
|
158
|
+
* colorControlRole(ctrlId) -> 'text' | 'background'
|
|
159
|
+
* Tells invertLightness whether a control's color is rendered as text.
|
|
160
|
+
*/
|
|
161
|
+
function colorControlRole(ctrlId) {
|
|
162
|
+
return TEXT_COLOR_CONTROLS[ctrlId] ? 'text' : 'background';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* invertLightness(hex, role)
|
|
141
167
|
* Generates a dark-theme counterpart for a light-theme color.
|
|
142
168
|
*
|
|
143
169
|
* Strategy: colors that look "right" in light mode get adapted for dark mode.
|
|
144
170
|
* - Very light colors (L>65): page/block backgrounds → make very dark
|
|
145
171
|
* - Very dark colors (L<20): already dark, likely intentional → keep as-is
|
|
172
|
+
* UNLESS role is 'text', in which case lighten it so it stays readable.
|
|
146
173
|
* - Dark-ish colors (20<L<45): body text, headings → make light
|
|
147
174
|
* - Mid-range (45-65): accent colors → moderate shift
|
|
175
|
+
*
|
|
176
|
+
* role: 'text' for colors painted as text (body, headings, links, code text),
|
|
177
|
+
* 'background' (default) for fills. Text colors are never kept dark.
|
|
148
178
|
*/
|
|
149
|
-
function invertLightness(hex) {
|
|
179
|
+
function invertLightness(hex, role) {
|
|
150
180
|
var hsl = hexToHsl(hex);
|
|
151
181
|
if (!hsl) return hex;
|
|
152
182
|
var h = hsl[0], s = hsl[1], l = hsl[2];
|
|
@@ -160,7 +190,7 @@ function invertLightness(hex) {
|
|
|
160
190
|
// Light background/accent → dark
|
|
161
191
|
invL = 12 + (100 - l) * 0.4; // L70→24, L65→26
|
|
162
192
|
invS = s * 0.75;
|
|
163
|
-
} else if (l < 20) {
|
|
193
|
+
} else if (l < 20 && role !== 'text') {
|
|
164
194
|
// Already very dark → keep as-is (intentional dark bg like code blocks)
|
|
165
195
|
return hex;
|
|
166
196
|
} else if (l < 40) {
|
|
@@ -308,6 +338,15 @@ function collectStyles(values, overriddenColors) {
|
|
|
308
338
|
if (overriddenColors.has('_sd_ctrl-chart-text')) chartObj.textColor = gv('_sd_ctrl-chart-text');
|
|
309
339
|
if (Object.keys(chartObj).length) styles.chart = chartObj;
|
|
310
340
|
|
|
341
|
+
// Table styles
|
|
342
|
+
var tableObj = {};
|
|
343
|
+
if (overriddenColors.has('_sd_ctrl-table-border')) tableObj.border = gv('_sd_ctrl-table-border');
|
|
344
|
+
if (overriddenColors.has('_sd_ctrl-table-header-bg')) tableObj.headerBackground = gv('_sd_ctrl-table-header-bg');
|
|
345
|
+
if (overriddenColors.has('_sd_ctrl-table-even-bg')) tableObj.evenBackground = gv('_sd_ctrl-table-even-bg');
|
|
346
|
+
if (overriddenColors.has('_sd_ctrl-table-odd-bg')) tableObj.oddBackground = gv('_sd_ctrl-table-odd-bg');
|
|
347
|
+
if (overriddenColors.has('_sd_ctrl-table-text')) tableObj.color = gv('_sd_ctrl-table-text');
|
|
348
|
+
if (Object.keys(tableObj).length) styles.table = tableObj;
|
|
349
|
+
|
|
311
350
|
return styles;
|
|
312
351
|
}
|
|
313
352
|
|
|
@@ -397,6 +436,13 @@ function stylesToControls(styles) {
|
|
|
397
436
|
if (ch.background) { controls['_sd_ctrl-chart-bg'] = ch.background; overridden.add('_sd_ctrl-chart-bg'); }
|
|
398
437
|
if (ch.textColor) { controls['_sd_ctrl-chart-text'] = ch.textColor; overridden.add('_sd_ctrl-chart-text'); }
|
|
399
438
|
|
|
439
|
+
const tb = styles.table || {};
|
|
440
|
+
if (tb.border) { controls['_sd_ctrl-table-border'] = tb.border; overridden.add('_sd_ctrl-table-border'); }
|
|
441
|
+
if (tb.headerBackground) { controls['_sd_ctrl-table-header-bg'] = tb.headerBackground; overridden.add('_sd_ctrl-table-header-bg'); }
|
|
442
|
+
if (tb.evenBackground) { controls['_sd_ctrl-table-even-bg'] = tb.evenBackground; overridden.add('_sd_ctrl-table-even-bg'); }
|
|
443
|
+
if (tb.oddBackground) { controls['_sd_ctrl-table-odd-bg'] = tb.oddBackground; overridden.add('_sd_ctrl-table-odd-bg'); }
|
|
444
|
+
if (tb.color) { controls['_sd_ctrl-table-text'] = tb.color; overridden.add('_sd_ctrl-table-text'); }
|
|
445
|
+
|
|
400
446
|
return { controls, overriddenColors: overridden };
|
|
401
447
|
}
|
|
402
448
|
|
|
@@ -408,6 +454,7 @@ var STANDALONE_COLOR_IDS = [
|
|
|
408
454
|
'_sd_ctrl-bg-color','_sd_ctrl-link-color',
|
|
409
455
|
'_sd_ctrl-bq-border-color',
|
|
410
456
|
'_sd_ctrl-chart-accent',
|
|
457
|
+
'_sd_ctrl-table-border','_sd_ctrl-table-header-bg','_sd_ctrl-table-even-bg','_sd_ctrl-table-odd-bg','_sd_ctrl-table-text',
|
|
411
458
|
];
|
|
412
459
|
|
|
413
460
|
var CASCADE_COLOR_IDS = Object.keys(COLOR_VAR_MAP);
|
|
@@ -455,6 +502,13 @@ function parseDarkBlock(block) {
|
|
|
455
502
|
if (block.chart.background) colors['_sd_ctrl-chart-bg'] = block.chart.background;
|
|
456
503
|
if (block.chart.textColor) colors['_sd_ctrl-chart-text'] = block.chart.textColor;
|
|
457
504
|
}
|
|
505
|
+
if (block.table) {
|
|
506
|
+
if (block.table.border) colors['_sd_ctrl-table-border'] = block.table.border;
|
|
507
|
+
if (block.table.headerBackground) colors['_sd_ctrl-table-header-bg'] = block.table.headerBackground;
|
|
508
|
+
if (block.table.evenBackground) colors['_sd_ctrl-table-even-bg'] = block.table.evenBackground;
|
|
509
|
+
if (block.table.oddBackground) colors['_sd_ctrl-table-odd-bg'] = block.table.oddBackground;
|
|
510
|
+
if (block.table.color) colors['_sd_ctrl-table-text'] = block.table.color;
|
|
511
|
+
}
|
|
458
512
|
|
|
459
513
|
return colors;
|
|
460
514
|
}
|
|
@@ -480,9 +534,9 @@ var STYLE_DEFAULTS = {
|
|
|
480
534
|
h3: { fontSize: 1.2, fontWeight: 600 },
|
|
481
535
|
h4: { fontSize: 1.0, fontWeight: 600 },
|
|
482
536
|
p: { lineHeight: 1.75, marginBottom: 1.1 },
|
|
483
|
-
link: { decoration: 'underline' },
|
|
537
|
+
link: { color: '#2563eb', decoration: 'underline' },
|
|
484
538
|
code: { font: 'JetBrains Mono' },
|
|
485
|
-
blockquote: { borderWidth: 3, fontSize: 1.0 },
|
|
539
|
+
blockquote: { borderColor: '#2563eb', borderWidth: 3, fontSize: 1.0 },
|
|
486
540
|
list: { spacing: 0.3, indent: 1.6 },
|
|
487
541
|
};
|
|
488
542
|
|
|
@@ -532,6 +586,78 @@ function stripStyleDefaults(styles) {
|
|
|
532
586
|
return result;
|
|
533
587
|
}
|
|
534
588
|
|
|
589
|
+
// ═══════════════════════════════════════════════════════
|
|
590
|
+
// STYLE REFERENCE RESOLVER ($path.to.prop → var(--md-*))
|
|
591
|
+
// ═══════════════════════════════════════════════════════
|
|
592
|
+
|
|
593
|
+
// Maps YAML-schema paths to the CSS custom property that carries their
|
|
594
|
+
// live value. Used by the slide shape DSL so an agent can write
|
|
595
|
+
// `fill=$h1.color` and have the slide pick up whatever the doc's h1
|
|
596
|
+
// color currently is, including dark-mode inversion. The value is
|
|
597
|
+
// resolved at CSS-paint time, not parse time — theme changes Just Work.
|
|
598
|
+
//
|
|
599
|
+
// Keep this list aligned with COLOR_VAR_MAP + CTRL_CSS_MAP above, plus
|
|
600
|
+
// a few convenience aliases (e.g. `headers.color` as well as `h.color`).
|
|
601
|
+
var STYLE_PATH_TO_VAR = {
|
|
602
|
+
// General
|
|
603
|
+
'background': '--md-bg',
|
|
604
|
+
'color': '--md-color',
|
|
605
|
+
'fontFamily': '--md-font-family',
|
|
606
|
+
|
|
607
|
+
// Headings
|
|
608
|
+
'h.color': '--md-h-color',
|
|
609
|
+
'headers.color': '--md-h-color',
|
|
610
|
+
'headers.fontFamily': '--md-h-font-family',
|
|
611
|
+
'h1.color': '--md-h1-color',
|
|
612
|
+
'h2.color': '--md-h2-color',
|
|
613
|
+
'h3.color': '--md-h3-color',
|
|
614
|
+
'h4.color': '--md-h4-color',
|
|
615
|
+
|
|
616
|
+
// Paragraph / list
|
|
617
|
+
'p.color': '--md-p-color',
|
|
618
|
+
'list.color': '--md-list-color',
|
|
619
|
+
|
|
620
|
+
// Link
|
|
621
|
+
'link.color': '--md-link-color',
|
|
622
|
+
|
|
623
|
+
// Blocks cascade (code / blockquote / chart parent)
|
|
624
|
+
'blocks.background': '--md-block-bg',
|
|
625
|
+
'blocks.color': '--md-block-text',
|
|
626
|
+
|
|
627
|
+
// Code
|
|
628
|
+
'code.background': '--md-code-bg',
|
|
629
|
+
'code.color': '--md-code-color',
|
|
630
|
+
'code.font': '--md-code-font',
|
|
631
|
+
|
|
632
|
+
// Blockquote
|
|
633
|
+
'blockquote.background': '--md-bq-bg',
|
|
634
|
+
'blockquote.color': '--md-bq-color',
|
|
635
|
+
'blockquote.borderColor':'--md-bq-border-color',
|
|
636
|
+
|
|
637
|
+
// Chart
|
|
638
|
+
'chart.accent': '--md-chart-accent',
|
|
639
|
+
'chart.background': '--md-chart-bg',
|
|
640
|
+
'chart.textColor': '--md-chart-text',
|
|
641
|
+
|
|
642
|
+
// Table
|
|
643
|
+
'table.border': '--md-table-border',
|
|
644
|
+
'table.headerBackground': '--md-table-header-bg',
|
|
645
|
+
'table.evenBackground': '--md-table-even-bg',
|
|
646
|
+
'table.oddBackground': '--md-table-odd-bg',
|
|
647
|
+
'table.color': '--md-table-text',
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
// Given a raw token (typically a shape attribute value), return the
|
|
651
|
+
// CSS var() expression to use in its place, or null if not a ref.
|
|
652
|
+
// Returns { value, error } so callers can surface unknown refs.
|
|
653
|
+
function resolveStyleRef(token) {
|
|
654
|
+
if (typeof token !== 'string' || token.charAt(0) !== '$') return null;
|
|
655
|
+
var key = token.slice(1);
|
|
656
|
+
var cssVar = STYLE_PATH_TO_VAR[key];
|
|
657
|
+
if (cssVar) return { value: 'var(' + cssVar + ')' };
|
|
658
|
+
return { error: 'unknown style reference "$' + key + '"' };
|
|
659
|
+
}
|
|
660
|
+
|
|
535
661
|
// ═══════════════════════════════════════════════════════
|
|
536
662
|
// EXPORTS
|
|
537
663
|
// ═══════════════════════════════════════════════════════
|
|
@@ -549,11 +675,14 @@ exports.hslToHex = hslToHex;
|
|
|
549
675
|
exports.controlToCssVars = controlToCssVars;
|
|
550
676
|
exports.cascadeColor = cascadeColor;
|
|
551
677
|
exports.invertLightness = invertLightness;
|
|
678
|
+
exports.colorControlRole = colorControlRole;
|
|
552
679
|
exports.collectStyles = collectStyles;
|
|
553
680
|
exports.parseDarkBlock = parseDarkBlock;
|
|
554
681
|
exports.stylesToControls = stylesToControls;
|
|
555
682
|
exports.STYLE_DEFAULTS = STYLE_DEFAULTS;
|
|
556
683
|
exports.stripStyleDefaults = stripStyleDefaults;
|
|
684
|
+
exports.STYLE_PATH_TO_VAR = STYLE_PATH_TO_VAR;
|
|
685
|
+
exports.resolveStyleRef = resolveStyleRef;
|
|
557
686
|
|
|
558
687
|
// UMD tail: in Node (tests) this writes to module.exports; in the browser
|
|
559
688
|
// it creates window.SDocStyles. We use this pattern instead of ES modules
|
package/README.md
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
# SDocs
|
|
2
|
-
|
|
3
|
-
**Read, style, and share markdown files — privately.**
|
|
4
|
-
|
|
5
|
-
SDocs is a lightweight, stateless markdown editor with live styling. Your entire document lives in the URL hash — nothing is ever sent to a server.
|
|
6
|
-
|
|
7
|
-
**[sdocs.dev](https://sdocs.dev)** | **[npm](https://www.npmjs.com/package/sdocs-dev)** | **[MIT License](LICENSE)**
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## What it does
|
|
12
|
-
|
|
13
|
-
- **Read** — open any `.md` file with clean, styled formatting
|
|
14
|
-
- **Style** — customize fonts, colors, sizes, and spacing via a visual panel or YAML front matter
|
|
15
|
-
- **Share** — compress a document into a URL and share it with anyone (no server, no account)
|
|
16
|
-
- **Export** — PDF, Word (.docx), raw `.md`, or styled `.md` with front matter
|
|
17
|
-
|
|
18
|
-
Everything runs client-side. The server is a small Node.js script that serves static files. The hosted instance at sdocs.dev counts anonymous visits (no IPs, no tracking IDs) which you can see at [sdocs.dev/analytics](https://sdocs.dev/analytics); self-hosted and CLI use never phone home.
|
|
19
|
-
|
|
20
|
-
## CLI
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
npm i -g sdocs-dev
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
sdoc README.md # open styled in browser
|
|
28
|
-
sdoc share report.md # copy shareable link to clipboard
|
|
29
|
-
sdoc share report.md --dark # link opens in dark theme
|
|
30
|
-
sdoc share report.md --section "Results" # deep-link to a heading
|
|
31
|
-
sdoc new # blank document in write mode
|
|
32
|
-
sdoc schema # print all style properties
|
|
33
|
-
cat notes.md | sdoc # pipe markdown to browser
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## How it works
|
|
37
|
-
|
|
38
|
-
Documents are compressed with [Brotli](https://en.wikipedia.org/wiki/Brotli) and encoded as [base64url](https://en.wikipedia.org/wiki/Base64#URL_applications) in the URL hash fragment. The hash fragment is never sent to the server by the browser — it stays entirely client-side.
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
https://sdocs.dev/#md={brotli compressed + base64url encoded .md}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Styles are stored as [YAML front matter](https://jekyllrb.com/docs/front-matter/) in the `.md` file using a `styles:` key. Default style values are omitted from URLs to keep them short. Run `sdoc schema` to see all available properties.
|
|
45
|
-
|
|
46
|
-
## Stack
|
|
47
|
-
|
|
48
|
-
- **Server**: `server.js` — pure Node.js `http` module
|
|
49
|
-
- **Frontend**: plain HTML, CSS, and JS in `public/` — no build step, no framework
|
|
50
|
-
- **Markdown parsing**: [marked](https://github.com/markedjs/marked) (the only runtime dependency)
|
|
51
|
-
- **Compression**: [brotli-wasm](https://github.com/nicolo-ribaudo/brotli-wasm) (WebAssembly, loaded in browser)
|
|
52
|
-
- **Tests**: `node test/run.js` — custom red/green harness using Node `assert`
|
|
53
|
-
- **Browser tests**: [Playwright](https://playwright.dev/) (Chromium)
|
|
54
|
-
|
|
55
|
-
## Contributing
|
|
56
|
-
|
|
57
|
-
### Setup
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
git clone https://github.com/espressoplease/SDocs.git
|
|
61
|
-
cd SDocs
|
|
62
|
-
npm install
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Run locally
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
node server.js # http://localhost:3000
|
|
69
|
-
PORT=8080 node server.js # custom port
|
|
70
|
-
SDOCS_DEV=1 node server.js # dev mode: no-cache headers, service worker disabled
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
`SDOCS_DEV=1` (or `NODE_ENV=development`) disables browser caching for CSS/JS and tells the app to unregister the service worker and clear its caches on load, so edits to frontend code are picked up instantly without hard-refreshing. Leave it unset in production.
|
|
74
|
-
|
|
75
|
-
### Run tests
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
node test/run.js # unit + integration tests
|
|
79
|
-
npx playwright test test/write-mode.spec.js # browser tests (needs Chromium)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Point the CLI at your local server
|
|
83
|
-
|
|
84
|
-
By default the CLI opens URLs on `https://sdocs.dev`. When developing, point it at your local instance:
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
# Per-command
|
|
88
|
-
sdoc README.md --url http://localhost:3000
|
|
89
|
-
sdoc share README.md --url http://localhost:3000
|
|
90
|
-
|
|
91
|
-
# Or set once for your session
|
|
92
|
-
export SDOCS_URL=http://localhost:3000
|
|
93
|
-
sdoc README.md
|
|
94
|
-
sdoc share README.md
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Project structure
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
server.js # Node.js static file server
|
|
101
|
-
bin/sdocs-dev.js # CLI entry point
|
|
102
|
-
public/
|
|
103
|
-
index.html # Single HTML file (markup only)
|
|
104
|
-
css/ # Modular CSS (tokens, layout, rendered, panel, mobile)
|
|
105
|
-
sdocs-yaml.js # YAML front matter parser (UMD, shared with Node)
|
|
106
|
-
sdocs-styles.js # Style data tables + logic (UMD, shared with tests)
|
|
107
|
-
sdocs-state.js # Shared mutable state namespace (window.SDocs)
|
|
108
|
-
sdocs-theme.js # Fonts, dark mode, theme toggle
|
|
109
|
-
sdocs-controls.js # CSS variable management, color cascade
|
|
110
|
-
sdocs-export.js # PDF/Word/MD export
|
|
111
|
-
sdocs-write.js # Write mode (contentEditable)
|
|
112
|
-
sdocs-app.js # Core app: render, sync, modes, compression, init
|
|
113
|
-
brotli-wasm-v1.js # Brotli WASM wrapper (IIFE)
|
|
114
|
-
brotli_wasm_bg.wasm # Brotli WebAssembly binary
|
|
115
|
-
vendor/marked.min.js # Markdown parser
|
|
116
|
-
sw.js # Service worker (stale-while-revalidate + version check)
|
|
117
|
-
sdoc.md # Landing page content
|
|
118
|
-
test/
|
|
119
|
-
run.js # Test runner entry point
|
|
120
|
-
runner.js # Shared test harness
|
|
121
|
-
test-yaml.js # YAML parser tests
|
|
122
|
-
test-styles.js # Style system tests (including stripStyleDefaults)
|
|
123
|
-
test-cli.js # CLI argument parsing + URL building tests
|
|
124
|
-
test-slugify.js # Slugify + heading dedup tests
|
|
125
|
-
test-base64.js # Base64 UTF-8 roundtrip tests
|
|
126
|
-
test-files.js # File existence + content assertions
|
|
127
|
-
test-http.js # HTTP server tests
|
|
128
|
-
write-mode.spec.js # Playwright write mode tests
|
|
129
|
-
sample.smd # Test fixture (styled markdown)
|
|
130
|
-
bench-compression.js # Compression benchmark (deflate vs brotli)
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### Architecture notes
|
|
134
|
-
|
|
135
|
-
The app is entirely stateless. All state lives in `window.SDocs` in the browser. There is no build step — all JS loads as plain `<script>` tags. Modules that need to run in both the browser and Node (for tests) use a UMD IIFE pattern.
|
|
136
|
-
|
|
137
|
-
Styles are driven by CSS custom properties on `#rendered`. Every control in the style panel maps to a `--md-*` CSS variable. When exporting, `collectStyles()` reads the current control values from the DOM.
|
|
138
|
-
|
|
139
|
-
### Guidelines
|
|
140
|
-
|
|
141
|
-
- No build step. All browser JS must work as plain `<script>` tags.
|
|
142
|
-
- One runtime dependency (`marked`). Add dependencies only when there's no reasonable alternative.
|
|
143
|
-
- Keep the server simple. It serves static files — no API routes, no database.
|
|
144
|
-
- Test everything that's testable without a browser in `test/run.js`. Use Playwright for browser-specific behavior.
|
|
145
|
-
- Style properties that match defaults should be omitted (see `stripStyleDefaults` in `sdocs-styles.js`).
|
|
146
|
-
|
|
147
|
-
## License
|
|
148
|
-
|
|
149
|
-
[MIT](LICENSE)
|
|
File without changes
|
|
File without changes
|