@zvndev/darkmown 0.19.0 → 1.0.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.
Files changed (69) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +115 -5
  4. package/docs/spec-alignment.md +16 -3
  5. package/package.json +22 -4
  6. package/site/_/base.skin +372 -0
  7. package/site/_/base.wd +4 -0
  8. package/site/_/footer.wd +14 -0
  9. package/site/_/metrics.json +13 -0
  10. package/site/_/nav.skin +35 -12
  11. package/site/_/nav.wd +8 -10
  12. package/site/_/products.json +10 -0
  13. package/site/pages/app.wd +1 -1
  14. package/site/pages/carousel/index.js +82 -0
  15. package/site/pages/carousel/index.skin +185 -0
  16. package/site/pages/carousel/index.wd +109 -0
  17. package/site/pages/carousel/slide-1.jpg +0 -0
  18. package/site/pages/carousel/slide-2.jpg +0 -0
  19. package/site/pages/carousel/slide-3.jpg +0 -0
  20. package/site/pages/carousel/slide-4.jpg +0 -0
  21. package/site/pages/carousel/slide-5.jpg +0 -0
  22. package/site/pages/compass/index.skin +247 -0
  23. package/site/pages/compass/index.wd +194 -0
  24. package/site/pages/docs/index.skin +48 -28
  25. package/site/pages/docs/index.wd +71 -5
  26. package/site/pages/folio/-style.skin +375 -0
  27. package/site/pages/folio/-style.wd +6 -0
  28. package/site/pages/folio/cart.skin +309 -0
  29. package/site/pages/folio/cart.wd +201 -0
  30. package/site/pages/folio/index.skin +24 -0
  31. package/site/pages/folio/index.wd +167 -0
  32. package/site/pages/forge/index.skin +676 -0
  33. package/site/pages/forge/index.wd +330 -0
  34. package/site/pages/index.skin +135 -144
  35. package/site/pages/index.wd +17 -5
  36. package/site/pages/ledger/index.skin +257 -0
  37. package/site/pages/ledger/index.wd +174 -0
  38. package/site/pages/media/clip-poster.jpg +0 -0
  39. package/site/pages/media/clip.mp4 +0 -0
  40. package/site/pages/media/index.skin +82 -0
  41. package/site/pages/media/index.wd +84 -0
  42. package/site/pages/media/tone.mp3 +0 -0
  43. package/site/pages/pulse/index.skin +434 -0
  44. package/site/pages/pulse/index.wd +178 -0
  45. package/site/pages/showcase/index.skin +130 -0
  46. package/site/pages/showcase/index.wd +168 -0
  47. package/src/builder.js +20 -14
  48. package/src/cli.js +216 -77
  49. package/src/compiler/body.js +457 -0
  50. package/src/compiler/context.js +160 -0
  51. package/src/compiler/directives.js +1345 -0
  52. package/src/compiler/format.js +244 -0
  53. package/src/compiler/frontmatter.js +117 -0
  54. package/src/compiler/includes.js +116 -0
  55. package/src/compiler/index.js +22 -0
  56. package/src/compiler/interpolation.js +223 -0
  57. package/src/compiler/loops.js +715 -0
  58. package/src/compiler/markdown.js +242 -0
  59. package/src/compiler/page.js +267 -0
  60. package/src/compiler/predicates.js +314 -0
  61. package/src/compiler.js +26 -2932
  62. package/src/dev.js +3 -1
  63. package/src/headers.js +8 -4
  64. package/src/index.js +22 -0
  65. package/src/router.js +3 -1
  66. package/src/runtime.js +168 -14
  67. package/src/scaffold.js +158 -131
  68. package/src/skin.js +11 -2
  69. package/site/_/og.png +0 -0
@@ -0,0 +1,130 @@
1
+ /* Showcase hub — layers on /base.wd. Gallery of the flagship apps. */
2
+
3
+ main.showcase
4
+ max-width 1080px
5
+
6
+ .sc-hero
7
+ padding 3.5rem 0 2.5rem
8
+ border-bottom 1px solid $line
9
+ margin-bottom 2.5rem
10
+
11
+ .sc-hero h1
12
+ max-width 18ch
13
+
14
+ .sc-hero > p
15
+ max-width 64ch
16
+ font-size 1.18rem
17
+ color $ink-soft
18
+ margin 1rem 0 1.75rem
19
+
20
+ .sc-cta
21
+ display flex
22
+ flex-wrap wrap
23
+
24
+ /* ── App gallery ────────────────────────────────────────────────── */
25
+
26
+ .sc-apps
27
+ display grid
28
+ grid-template-columns 1fr 1fr
29
+ gap 1.25rem
30
+ margin 1rem 0 3rem
31
+
32
+ @media (max-width: 760px)
33
+ .sc-apps
34
+ grid-template-columns 1fr
35
+
36
+ .sc-app
37
+ position relative
38
+ padding 1.6rem 1.6rem 1.4rem
39
+ border 1px solid $line
40
+ radius $radius-lg
41
+ overflow hidden
42
+ transition transform .14s ease, box-shadow .14s ease
43
+
44
+ .sc-app:hover
45
+ transform translateY(-3px)
46
+ box-shadow 0 18px 40px -22px rgba(28,24,20,.45)
47
+
48
+ /* A soft accent wash per app — its identity color. */
49
+ .sc-app::before
50
+ content ""
51
+ position absolute
52
+ inset 0 0 auto 0
53
+ height 4px
54
+
55
+ .sc-folio::before
56
+ background #b8462e
57
+
58
+ .sc-pulse::before
59
+ background #18b89a
60
+
61
+ .sc-forge::before
62
+ background #6d5ae6
63
+
64
+ .sc-compass::before
65
+ background #e0894a
66
+
67
+ .sc-ledger::before
68
+ background #c79a3a
69
+
70
+ .sc-emoji
71
+ font-size 2rem
72
+ line-height 1
73
+
74
+ .sc-app h3
75
+ margin .6rem 0 .5rem
76
+ font-size 1.45rem
77
+
78
+ .sc-app p
79
+ color $ink-soft
80
+ margin .4rem 0 1rem
81
+
82
+ .sc-open
83
+ display inline-block
84
+ margin-top .4rem
85
+ font-weight 600
86
+ text-decoration none
87
+ color $accent-deep
88
+
89
+ .sc-open:hover
90
+ text-decoration underline
91
+
92
+ /* ── Weight section ─────────────────────────────────────────────── */
93
+
94
+ .sc-weight
95
+ margin 1rem 0 3rem
96
+
97
+ .sc-scale
98
+ list-style none
99
+ padding 1.25rem 1.5rem
100
+ margin 1.25rem 0
101
+
102
+ .sc-scale li
103
+ padding .55rem 0
104
+ border-bottom 1px solid $line-soft
105
+ color $ink-soft
106
+
107
+ .sc-scale li:last-child
108
+ border-bottom 0
109
+
110
+ .sc-scale strong
111
+ color $ink
112
+
113
+ .sc-scale code
114
+ font-size 1em
115
+ font-weight 700
116
+ color $accent-deep
117
+ background $accent-soft
118
+
119
+ /* ── Mini demos ─────────────────────────────────────────────────── */
120
+
121
+ .sc-mini
122
+ padding 1.1rem 1.25rem
123
+
124
+ .sc-mini p
125
+ margin 0 0 .6rem
126
+ color $ink-soft
127
+
128
+ .sc-mini a
129
+ font-weight 600
130
+ text-decoration none
@@ -0,0 +1,168 @@
1
+ ---
2
+ title: Showcase — real apps built in Markdown
3
+ description: Five complete, beautiful apps — a storefront, a live dashboard, a pricing configurator, a product-finder quiz, and a sortable expense ledger — each one a readable Markdown file. See the app, then view its source.
4
+ image: https://darkmown.com/__wd/media/og-showcase.png
5
+ transitions: true
6
+ ---
7
+
8
+ @include /nav.wd
9
+ @include /base.wd
10
+
11
+ <main class="wide showcase">
12
+
13
+ <header class="sc-hero">
14
+ <span class="eyebrow">Built with Darkmown</span>
15
+
16
+ # Real apps. Written in Markdown.
17
+
18
+ Each of these is a complete, working app — a storefront with a cart, a live status dashboard, an interactive pricing builder, a guided quiz. Open one, use it, then **view its source**. It's a Markdown file you can read top to bottom.
19
+
20
+ <div class="sc-cta">
21
+ <a class="btn primary" href="/folio/">Open the storefront</a>
22
+ <a class="btn" href="/docs/">Read the docs</a>
23
+ </div>
24
+ </header>
25
+
26
+ <section class="sc-apps">
27
+
28
+ ::: card .sc-app .sc-folio
29
+ <span class="sc-emoji">🛍️</span>
30
+
31
+ ### Folio — boutique storefront
32
+
33
+ A multi-page shop where the cart **persists across pages, reloads, and browser tabs**, and navigation is instant. Live search, add-to-cart, checkout.
34
+
35
+ <span class="tag">static → reactive</span> <span class="tag">:store</span> <span class="tag">keyed @loop</span> <span class="tag">:form</span>
36
+
37
+ [Open Folio →](/folio/){.sc-open}
38
+ :::
39
+
40
+ ::: card .sc-app .sc-pulse
41
+ <span class="sc-emoji">📈</span>
42
+
43
+ ### Pulse — status dashboard
44
+
45
+ A service dashboard driven by `:fetch` — loading, error, and empty states, live refresh, sortable metrics, status badges. No hand-written JavaScript.
46
+
47
+ <span class="tag">:fetch lifecycle</span> <span class="tag">:computed</span> <span class="tag">.class when</span> <span class="tag">sort / where</span>
48
+
49
+ [Open Pulse →](/pulse/){.sc-open}
50
+ :::
51
+
52
+ ::: card .sc-app .sc-forge
53
+ <span class="sc-emoji">🧩</span>
54
+
55
+ ### Forge — plan configurator
56
+
57
+ Build your plan: toggle features, adjust seats, watch the price recompute instantly, get smart upsell hints, then check out — the classic "this needs React" interaction.
58
+
59
+ <span class="tag">:computed pricing</span> <span class="tag">:if logic</span> <span class="tag">toggles</span> <span class="tag">:form</span>
60
+
61
+ [Open Forge →](/forge/){.sc-open}
62
+ :::
63
+
64
+ ::: card .sc-app .sc-compass
65
+ <span class="sc-emoji">🧭</span>
66
+
67
+ ### Compass — product finder
68
+
69
+ A guided quiz that asks a few questions, scores your answers, and recommends a match — a branching state machine expressed entirely as `:if` steps.
70
+
71
+ <span class="tag">:if state machine</span> <span class="tag">:computed</span> <span class="tag">scored flow</span>
72
+
73
+ [Open Compass →](/compass/){.sc-open}
74
+ :::
75
+
76
+ ::: card .sc-app .sc-ledger
77
+ <span class="sc-emoji">📒</span>
78
+
79
+ ### Ledger — sortable expense table
80
+
81
+ A spreadsheet-grade data table: click a header to re-sort live, filter by merchant, watch running totals recompute, flip light/dark — formatted with `| money` and `| date`, totalled with `sum()`.
82
+
83
+ <span class="tag">format pipes</span> <span class="tag">aggregates</span> <span class="tag">reactive sort</span> <span class="tag">:theme</span>
84
+
85
+ [Open Ledger →](/ledger/){.sc-open}
86
+ :::
87
+
88
+ </section>
89
+
90
+ <section class="sc-weight">
91
+
92
+ ## What it costs
93
+
94
+ Darkmown's whole pitch is weight. A static page ships **zero** framework JavaScript — not "a little," none. The runtime loads only when a page actually declares reactivity, and it's one shared file, CI-enforced under 8 KB gzipped.
95
+
96
+ ::: card .sc-scale
97
+ - **Static Darkmown page** — `0 KB` framework JS. Forever.
98
+ - **Reactive Darkmown page** — one shared `~7.4 KB` gzipped runtime, no per-component bundles.
99
+ - **A typical SPA** — ships a framework runtime in the **~15–60 KB** gzipped range *before* your app code, on every page (Alpine ~15 KB, Vue ~18 KB runtime, React 19 ~59 KB).
100
+ :::
101
+
102
+ The two Darkmown numbers above are measured on every commit; the runtime budget fails CI if it crosses 8 KB. The comparison figures are representative min+gzip runtime sizes (June 2026) — the point isn't "smallest" (Preact is smaller), it's **nothing at all until you ask for it.**
103
+
104
+ </section>
105
+
106
+ <section class="sc-more">
107
+
108
+ ## The directive demos
109
+
110
+ Prefer to see each feature in isolation? These pages tour the building blocks the apps are made of:
111
+
112
+ <div class="grid auto">
113
+
114
+ ::: card .sc-mini
115
+ **Reactive** — state, keyed loops, scoped sections.
116
+
117
+ [Open →](/reactive/)
118
+ :::
119
+
120
+ ::: card .sc-mini
121
+ **Loops** — sort, paging, `@empty`, nesting.
122
+
123
+ [Open →](/loops/)
124
+ :::
125
+
126
+ ::: card .sc-mini
127
+ **Store** — durable, cross-tab global state.
128
+
129
+ [Open →](/store/)
130
+ :::
131
+
132
+ ::: card .sc-mini
133
+ **Theme** — zero-JS OS dark mode from a skin.
134
+
135
+ [Open →](/theme/)
136
+ :::
137
+
138
+ ::: card .sc-mini
139
+ **Swiper** — a draggable carousel via the escape hatch.
140
+
141
+ [Open →](/carousel/)
142
+ :::
143
+
144
+ ::: card .sc-mini
145
+ **Media** — `:video`, `:audio`, `:embed`, zero JS.
146
+
147
+ [Open →](/media/)
148
+ :::
149
+
150
+ ::: card .sc-mini
151
+ **Data & Forms** — fetch lifecycle, server round-trips.
152
+
153
+ [Open →](/data/)
154
+ :::
155
+
156
+ ::: card .sc-mini
157
+ **Plain Markdown** — a `.md` page, directives inert.
158
+
159
+ [Open →](/markdown/)
160
+ :::
161
+
162
+ </div>
163
+
164
+ </section>
165
+
166
+ </main>
167
+
168
+ @include /footer.wd
package/src/builder.js CHANGED
@@ -60,7 +60,9 @@ export function buildSite(cwd = process.cwd()) {
60
60
  file: path.relative(cwd, route.file).replaceAll(path.sep, "/"),
61
61
  assets: {
62
62
  skins: [...page.assets.skins],
63
- scripts: page.assets.runtime ? ["/__wd/runtime.js", ...page.assets.scripts] : [...page.assets.scripts],
63
+ scripts: page.assets.runtime
64
+ ? ["/__wd/runtime.js", ...page.assets.scripts]
65
+ : [...page.assets.scripts],
64
66
  runtime: page.assets.runtime
65
67
  }
66
68
  });
@@ -101,13 +103,15 @@ export function buildSite(cwd = process.cwd()) {
101
103
  * @returns {string}
102
104
  */
103
105
  export function stripRuntimeComments(source) {
104
- return source
105
- // JSDoc blocks — type annotations the browser never needs.
106
- .replace(/\/\*\*[\s\S]*?\*\//g, "")
107
- // Whole-line `//` comments — developer notes that don't belong in the download.
108
- .replace(/^[ \t]*\/\/.*$/gm, "")
109
- // Drop the now blank/whitespace-only lines the removals leave behind.
110
- .replace(/^[ \t]*\n/gm, "");
106
+ return (
107
+ source
108
+ // JSDoc blocks — type annotations the browser never needs.
109
+ .replace(/\/\*\*[\s\S]*?\*\//g, "")
110
+ // Whole-line `//` comments — developer notes that don't belong in the download.
111
+ .replace(/^[ \t]*\/\/.*$/gm, "")
112
+ // Drop the now blank/whitespace-only lines the removals leave behind.
113
+ .replace(/^[ \t]*\n/gm, "")
114
+ );
111
115
  }
112
116
 
113
117
  /**
@@ -126,10 +130,10 @@ export function stripRuntimeComments(source) {
126
130
  * @returns {string}
127
131
  */
128
132
  export function renderCloudflareHeaders(manifest) {
129
- const baseLines = Object.entries(BASE_SECURITY_HEADERS).map(([name, value]) => ` ${name}: ${value}`);
130
- const blocks = [
131
- ["/*", [...baseLines, ` Content-Security-Policy: ${REACTIVE_CSP}`]]
132
- ];
133
+ const baseLines = Object.entries(BASE_SECURITY_HEADERS).map(
134
+ ([name, value]) => ` ${name}: ${value}`
135
+ );
136
+ const blocks = [["/*", [...baseLines, ` Content-Security-Policy: ${REACTIVE_CSP}`]]];
133
137
 
134
138
  for (const entry of manifest) {
135
139
  if (entry.assets.runtime) continue; // reactive routes keep the catch-all (relaxed) CSP
@@ -169,7 +173,7 @@ function defaultNotFoundHtml() {
169
173
  "<body>",
170
174
  "<main>",
171
175
  "<h1>Page not found</h1>",
172
- "<p>That route does not exist. <a href=\"/\">Back to home</a>.</p>",
176
+ '<p>That route does not exist. <a href="/">Back to home</a>.</p>',
173
177
  "</main>",
174
178
  "</body>",
175
179
  "</html>",
@@ -243,7 +247,9 @@ function emitPageAssets(paths) {
243
247
  if (hasHiddenPathSegment(rel) || fs.lstatSync(file).isSymbolicLink()) continue;
244
248
  const out = path.join(paths.distRoot, rel);
245
249
  if (fs.existsSync(out)) {
246
- console.warn(`hint: page asset "${rel}" skipped — a built route already emits /${rel.replaceAll(path.sep, "/")}`);
250
+ console.warn(
251
+ `hint: page asset "${rel}" skipped — a built route already emits /${rel.replaceAll(path.sep, "/")}`
252
+ );
247
253
  continue;
248
254
  }
249
255
  fs.mkdirSync(path.dirname(out), { recursive: true });