@zvndev/darkmown 0.4.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/CHANGELOG.md +34 -0
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/docs/cli.md +51 -0
- package/docs/spec-alignment.md +60 -0
- package/package.json +50 -0
- package/site/_/feature-card.wd +7 -0
- package/site/_/features.json +14 -0
- package/site/_/logo.svg +1 -0
- package/site/_/nav.skin +84 -0
- package/site/_/nav.wd +8 -0
- package/site/_/quotes.json +6 -0
- package/site/_/sprint-loop.wd +11 -0
- package/site/_/team.json +7 -0
- package/site/pages/-draft.wd +3 -0
- package/site/pages/data.js +8 -0
- package/site/pages/data.skin +98 -0
- package/site/pages/data.wd +110 -0
- package/site/pages/docs/-relative-note.wd +5 -0
- package/site/pages/docs/.secret.wd +3 -0
- package/site/pages/docs/index.skin +39 -0
- package/site/pages/docs/index.wd +125 -0
- package/site/pages/index.js +10 -0
- package/site/pages/index.skin +134 -0
- package/site/pages/index.wd +64 -0
- package/site/pages/markdown.md +36 -0
- package/site/pages/markdown.skin +63 -0
- package/site/pages/reactive.skin +62 -0
- package/site/pages/reactive.wd +68 -0
- package/src/builder.js +87 -0
- package/src/cli.js +156 -0
- package/src/compiler.js +907 -0
- package/src/config.js +19 -0
- package/src/dev.js +30 -0
- package/src/router.js +51 -0
- package/src/runtime.js +222 -0
- package/src/scaffold.js +95 -0
- package/src/skin.js +68 -0
- package/src/statics.js +31 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Data, forms, and persistence
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
@include /nav.wd
|
|
6
|
+
|
|
7
|
+
<main>
|
|
8
|
+
|
|
9
|
+
# Data, forms, and persistence
|
|
10
|
+
|
|
11
|
+
This page demos the Tier 1.5 surface: declarative fetch, forms that write state, persistent state, and the JavaScript escape hatch.
|
|
12
|
+
|
|
13
|
+
## Fetched data drives reactive loops
|
|
14
|
+
|
|
15
|
+
`:fetch team from "/__wd/data/team.json"` declares state and fills it from the network. Until it lands, the page shows the fallback branch:
|
|
16
|
+
|
|
17
|
+
:fetch team from "/__wd/data/team.json"
|
|
18
|
+
|
|
19
|
+
:if team
|
|
20
|
+
@loop team into member
|
|
21
|
+
::: card .member-card
|
|
22
|
+
**{ member.name }** — { member.role }
|
|
23
|
+
:if member.lead
|
|
24
|
+
Team lead ★
|
|
25
|
+
:else
|
|
26
|
+
Crew
|
|
27
|
+
:endif
|
|
28
|
+
:::
|
|
29
|
+
@endloop
|
|
30
|
+
:else
|
|
31
|
+
Loading the team…
|
|
32
|
+
:endif
|
|
33
|
+
|
|
34
|
+
That loop also proves `:if` over loop items: the lead badge is decided per row.
|
|
35
|
+
|
|
36
|
+
## Forms write state, no backend required
|
|
37
|
+
|
|
38
|
+
`:form into profile` captures the submit straight into state. With `action="/url"` instead, Darkmown emits a plain native form and stays out of the way entirely:
|
|
39
|
+
|
|
40
|
+
:form into profile
|
|
41
|
+
:input name placeholder="Your name" required
|
|
42
|
+
:input quest placeholder="Your quest"
|
|
43
|
+
:submit "Save profile"
|
|
44
|
+
:endform
|
|
45
|
+
|
|
46
|
+
:if profile
|
|
47
|
+
Hello **{ profile.name }** — good luck with { profile.quest }.
|
|
48
|
+
:else
|
|
49
|
+
Submit the form and this sentence reacts. Nothing leaves the page.
|
|
50
|
+
:endif
|
|
51
|
+
|
|
52
|
+
## Server round-trips, adapter style
|
|
53
|
+
|
|
54
|
+
Darkmown does not own your backend — point a form at any endpoint. With JS, the submit becomes a fetch and the JSON reply lands in state; without JS, the same markup is a plain native POST:
|
|
55
|
+
|
|
56
|
+
:form action="/__wd/echo" into reply
|
|
57
|
+
:input message placeholder="Say something to the server"
|
|
58
|
+
:submit "Send to server"
|
|
59
|
+
:endform
|
|
60
|
+
|
|
61
|
+
:if reply
|
|
62
|
+
The server answered with { reply.received.message } at { reply.at }.
|
|
63
|
+
:else
|
|
64
|
+
The reply will appear here.
|
|
65
|
+
:endif
|
|
66
|
+
|
|
67
|
+
:if reply_error
|
|
68
|
+
The request failed: { reply_error }
|
|
69
|
+
:endif
|
|
70
|
+
|
|
71
|
+
In `darkmown dev` the echo endpoint is built into the dev server; on darkmown.com it is a real serverless function behind the same URL. Either way, Darkmown itself stays static — sessions ride on `:fetch` plus ordinary cookies against your real API.
|
|
72
|
+
|
|
73
|
+
## Persistent state survives reload
|
|
74
|
+
|
|
75
|
+
`:state items = [] persist` keeps this section in localStorage. Add a few, reload the page, and the count holds:
|
|
76
|
+
|
|
77
|
+
::: section #cart
|
|
78
|
+
:state items = [] persist
|
|
79
|
+
:computed total = items.length * 4
|
|
80
|
+
|
|
81
|
+
The cart holds { items.length } item(s) worth ${ total }.
|
|
82
|
+
|
|
83
|
+
:button "Add sticker" -> items += {"id": "sticker", "name": "Sticker"}
|
|
84
|
+
:button "Add poster" -> items += {"id": "poster", "name": "Poster"}
|
|
85
|
+
:button "Empty cart" -> items = []
|
|
86
|
+
:::
|
|
87
|
+
|
|
88
|
+
`:computed total = items.length * 4` derives state from state — the price updates with every cart change, persistence included.
|
|
89
|
+
|
|
90
|
+
## The escape hatch
|
|
91
|
+
|
|
92
|
+
Colocated `data.js` uses the `window.wd` API (`wd.get`, `wd.set`) for anything directives can't say. Section state is addressed as `cart:items`:
|
|
93
|
+
|
|
94
|
+
<button class="pulse-button" data-wd-double>Double the cart via window.wd</button>
|
|
95
|
+
|
|
96
|
+
## Lazy loading
|
|
97
|
+
|
|
98
|
+
The list below uses `when=visible` — the request only fires once this section scrolls into view. Watch the network tab:
|
|
99
|
+
|
|
100
|
+
:fetch quotes from "/__wd/data/quotes.json" when=visible
|
|
101
|
+
|
|
102
|
+
:if quotes
|
|
103
|
+
@loop quotes into quote
|
|
104
|
+
- { quote }
|
|
105
|
+
@endloop
|
|
106
|
+
:else
|
|
107
|
+
Scroll me into view and I will load.
|
|
108
|
+
:endif
|
|
109
|
+
|
|
110
|
+
</main>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
tokens
|
|
2
|
+
ink #171717
|
|
3
|
+
paper #fbfaf7
|
|
4
|
+
panel #ffffff
|
|
5
|
+
line #dedbd3
|
|
6
|
+
accent #16645a
|
|
7
|
+
muted #666158
|
|
8
|
+
radius 8px
|
|
9
|
+
|
|
10
|
+
page
|
|
11
|
+
margin 0
|
|
12
|
+
font ui-serif, Georgia, Cambria, serif
|
|
13
|
+
color $ink
|
|
14
|
+
bg $paper
|
|
15
|
+
line-height 1.7
|
|
16
|
+
|
|
17
|
+
main
|
|
18
|
+
max-width 820px
|
|
19
|
+
margin 0 auto
|
|
20
|
+
padding 3rem 2rem 5rem
|
|
21
|
+
|
|
22
|
+
h1
|
|
23
|
+
font-size 3rem
|
|
24
|
+
line-height 1
|
|
25
|
+
margin 1rem 0
|
|
26
|
+
|
|
27
|
+
h2
|
|
28
|
+
margin-top 2.4rem
|
|
29
|
+
padding-top 1.2rem
|
|
30
|
+
border-top 1px solid $line
|
|
31
|
+
|
|
32
|
+
a
|
|
33
|
+
color $accent
|
|
34
|
+
|
|
35
|
+
.feature-card
|
|
36
|
+
border 1px solid $line
|
|
37
|
+
bg $panel
|
|
38
|
+
padding 1rem
|
|
39
|
+
radius $radius
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Darkmown docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
@include /nav.wd
|
|
6
|
+
|
|
7
|
+
<main>
|
|
8
|
+
|
|
9
|
+
# Docs
|
|
10
|
+
|
|
11
|
+
Darkmown is a Markdown-native framework for mostly-static sites with tiny reactive islands. Pages are plain files: `.md` stays pure CommonMark, and `.wd` ("whateverdown") is the same Markdown plus first-party directives.
|
|
12
|
+
|
|
13
|
+
@include ./-relative-note.wd
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npx @zvndev/darkmown init my-site
|
|
19
|
+
cd my-site
|
|
20
|
+
npm install
|
|
21
|
+
npm run dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or add Darkmown to an existing project with `npm install -D @zvndev/darkmown`. The package is scoped; the command it installs is plain `darkmown`.
|
|
25
|
+
|
|
26
|
+
## The two formats
|
|
27
|
+
|
|
28
|
+
- `.md` is strict CommonMark. Directives, includes, and `{ bindings }` stay plain text, and the build prints a hint if it spots `.wd` syntax in a `.md` file.
|
|
29
|
+
- `.wd` is Markdown plus directives. Renaming a file from `.md` to `.wd` is the upgrade path — nothing else changes.
|
|
30
|
+
|
|
31
|
+
## Routing rules
|
|
32
|
+
|
|
33
|
+
- `site/pages/index.wd` becomes `/`.
|
|
34
|
+
- `site/pages/docs/index.wd` becomes `/docs/`.
|
|
35
|
+
- `.secret.wd`, `-draft.wd`, and hidden folders do not become pages.
|
|
36
|
+
- `site/_/` is the include shelf, never a route.
|
|
37
|
+
|
|
38
|
+
## Includes
|
|
39
|
+
|
|
40
|
+
- `@include /nav.wd` resolves from `site/_`.
|
|
41
|
+
- `@include ./-relative-note.wd` resolves beside the current page.
|
|
42
|
+
- `@include /card.wd with title="Hello" count=3` passes values into the include.
|
|
43
|
+
- `with title={ feature.title }` passes a value already in scope — including whole objects.
|
|
44
|
+
- Includes inside a loop inherit the loop value automatically.
|
|
45
|
+
|
|
46
|
+
## Loops
|
|
47
|
+
|
|
48
|
+
`@loop <things> into <thing>` is the only loop. The body is normal Markdown (or more directives) and repeats once per row:
|
|
49
|
+
|
|
50
|
+
```wd
|
|
51
|
+
@loop /features.json into card
|
|
52
|
+
@include /feature-card.wd
|
|
53
|
+
@endloop
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The source decides how the loop behaves:
|
|
57
|
+
|
|
58
|
+
- A JSON file path (`/features.json`, `./data.json`) unrolls at build time — pure static HTML.
|
|
59
|
+
- An in-scope value (an include argument or an outer loop value) also unrolls at build time.
|
|
60
|
+
- A `:state` list compiles to a reactive region the runtime patches by key.
|
|
61
|
+
|
|
62
|
+
Loops nest, and `{ card.title }` style dotted paths reach into each row.
|
|
63
|
+
|
|
64
|
+
## Interpolation
|
|
65
|
+
|
|
66
|
+
One syntax everywhere: `{ name }` or `{ name.path }`.
|
|
67
|
+
|
|
68
|
+
- If the name is a static value in scope (include argument, loop value), it is resolved at build time.
|
|
69
|
+
- If the name is declared `:state`, it becomes a live binding.
|
|
70
|
+
- Otherwise the text is left exactly as written — braces in prose never break a page or pull in the runtime.
|
|
71
|
+
|
|
72
|
+
## Sections
|
|
73
|
+
|
|
74
|
+
`::: section #id .class` opens a container and `:::` closes it. Sections scope state: a `:state` declared inside a section belongs to that section, so two sections can both declare `count` without colliding. Bindings and buttons resolve to the nearest scope.
|
|
75
|
+
|
|
76
|
+
## Reactive directives
|
|
77
|
+
|
|
78
|
+
Reactive pages opt into `/__wd/runtime.js` (under 1 KB gzipped). Static pages do not.
|
|
79
|
+
|
|
80
|
+
```wd
|
|
81
|
+
:state count = 0
|
|
82
|
+
|
|
83
|
+
The count is { count }.
|
|
84
|
+
|
|
85
|
+
:button "Increment" -> count++
|
|
86
|
+
|
|
87
|
+
:if count
|
|
88
|
+
Count has changed.
|
|
89
|
+
:else
|
|
90
|
+
Count is still zero.
|
|
91
|
+
:endif
|
|
92
|
+
|
|
93
|
+
:state todos = [{"id": 1, "title": "Route pages"}]
|
|
94
|
+
|
|
95
|
+
@loop todos into todo
|
|
96
|
+
- { todo.title }
|
|
97
|
+
@endloop
|
|
98
|
+
|
|
99
|
+
:button "Add" -> todos += {"id": 2, "title": "Live compile"}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Directive actions are intentionally narrow and compile-time checked. Arbitrary JavaScript belongs in a colocated `.js` file.
|
|
103
|
+
|
|
104
|
+
## Data, forms, and persistence
|
|
105
|
+
|
|
106
|
+
- `:fetch name from "url"` declares state and fills it from JSON over the network. Shelf `.json` files are served at `/__wd/data/`.
|
|
107
|
+
- `:form into name` captures submits straight into state — no backend. `:form action="/url"` emits a plain native form with zero JS instead.
|
|
108
|
+
- `:input field placeholder="…" required` and `:submit "Label"` build the form body.
|
|
109
|
+
- `:state cart = [] persist` survives reloads via localStorage.
|
|
110
|
+
- `:if item.path` works inside reactive loops for per-row branches.
|
|
111
|
+
- Reactive pages expose `window.wd` (`get`, `set`, `state`, `render`) so colocated `.js` can do anything directives can't. Section state is addressed as `sectionId:name`.
|
|
112
|
+
|
|
113
|
+
See it all live on the [Data & Forms page](/data/).
|
|
114
|
+
|
|
115
|
+
## Colocation
|
|
116
|
+
|
|
117
|
+
- A matching `.skin` file attaches CSS to the page (indentation-based, compiles to real CSS).
|
|
118
|
+
- A matching `.js` file attaches page behavior.
|
|
119
|
+
- Both work for included fragments too, by basename.
|
|
120
|
+
|
|
121
|
+
## Spec status
|
|
122
|
+
|
|
123
|
+
The implementation is faithful to the original core thesis: Markdown-first authoring, no component ceremony, zero runtime on static pages, and tiny direct-DOM reactivity only when declared. `:fetch`, `:form`, `:session`, `:cart`, and server-driven fragments remain roadmap work — see `docs/spec-alignment.md` in the package for the full audit.
|
|
124
|
+
|
|
125
|
+
</main>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const button = document.querySelector("[data-wd-demo]");
|
|
2
|
+
|
|
3
|
+
if (button) {
|
|
4
|
+
button.addEventListener("click", () => {
|
|
5
|
+
button.classList.toggle("is-on");
|
|
6
|
+
button.textContent = button.classList.contains("is-on")
|
|
7
|
+
? "Colocated index.js is active"
|
|
8
|
+
: "Script is waiting";
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
tokens
|
|
2
|
+
ink #171717
|
|
3
|
+
paper #fbfaf7
|
|
4
|
+
panel #ffffff
|
|
5
|
+
line #dedbd3
|
|
6
|
+
accent #16645a
|
|
7
|
+
warm #a83f2a
|
|
8
|
+
muted #666158
|
|
9
|
+
gap 1rem
|
|
10
|
+
radius 8px
|
|
11
|
+
|
|
12
|
+
page
|
|
13
|
+
margin 0
|
|
14
|
+
font ui-serif, Georgia, Cambria, serif
|
|
15
|
+
color $ink
|
|
16
|
+
bg $paper
|
|
17
|
+
line-height 1.6
|
|
18
|
+
padding 0 0 4rem
|
|
19
|
+
|
|
20
|
+
.hero
|
|
21
|
+
min-height 60vh
|
|
22
|
+
display flex
|
|
23
|
+
flex-direction column
|
|
24
|
+
align-items flex-start
|
|
25
|
+
justify-content center
|
|
26
|
+
border-bottom 1px solid $line
|
|
27
|
+
margin-bottom 2rem
|
|
28
|
+
|
|
29
|
+
.hero > p:not(.eyebrow)
|
|
30
|
+
max-width 70ch
|
|
31
|
+
|
|
32
|
+
.eyebrow
|
|
33
|
+
color $accent
|
|
34
|
+
text-transform uppercase
|
|
35
|
+
letter-spacing .08em
|
|
36
|
+
font-size .78rem
|
|
37
|
+
margin 0
|
|
38
|
+
|
|
39
|
+
main
|
|
40
|
+
max-width 920px
|
|
41
|
+
margin 0 auto
|
|
42
|
+
padding 1rem 2rem 2rem
|
|
43
|
+
|
|
44
|
+
h1
|
|
45
|
+
font-size clamp(2.4rem, 6vw, 5rem)
|
|
46
|
+
line-height .95
|
|
47
|
+
margin 2rem 0 1rem
|
|
48
|
+
max-width 10ch
|
|
49
|
+
|
|
50
|
+
h2
|
|
51
|
+
margin-top 2rem
|
|
52
|
+
border-bottom 1px solid $line
|
|
53
|
+
padding-bottom .35rem
|
|
54
|
+
|
|
55
|
+
a
|
|
56
|
+
color $accent
|
|
57
|
+
|
|
58
|
+
.btn
|
|
59
|
+
display inline-block
|
|
60
|
+
margin .5rem .5rem .5rem 0
|
|
61
|
+
padding .75rem 1rem
|
|
62
|
+
border 1px solid $line
|
|
63
|
+
radius $radius
|
|
64
|
+
bg $panel
|
|
65
|
+
text-decoration none
|
|
66
|
+
|
|
67
|
+
.btn.primary
|
|
68
|
+
bg $ink
|
|
69
|
+
color white
|
|
70
|
+
border 1px solid $ink
|
|
71
|
+
|
|
72
|
+
.note
|
|
73
|
+
border 1px solid $line
|
|
74
|
+
bg $panel
|
|
75
|
+
padding 1rem
|
|
76
|
+
radius $radius
|
|
77
|
+
|
|
78
|
+
.try-card
|
|
79
|
+
display inline-flex
|
|
80
|
+
align-items center
|
|
81
|
+
gap .65rem
|
|
82
|
+
margin .35rem .5rem .35rem 0
|
|
83
|
+
padding .75rem 1rem
|
|
84
|
+
border 1px solid $line
|
|
85
|
+
radius $radius
|
|
86
|
+
bg $panel
|
|
87
|
+
text-decoration none
|
|
88
|
+
|
|
89
|
+
.try-card span
|
|
90
|
+
color white
|
|
91
|
+
bg $accent
|
|
92
|
+
padding .1rem .45rem
|
|
93
|
+
radius 999px
|
|
94
|
+
|
|
95
|
+
.feature-card
|
|
96
|
+
display grid
|
|
97
|
+
gap .35rem
|
|
98
|
+
margin 1rem 0
|
|
99
|
+
padding 1rem
|
|
100
|
+
bg $panel
|
|
101
|
+
border 1px solid $line
|
|
102
|
+
radius $radius
|
|
103
|
+
|
|
104
|
+
.feature-card strong
|
|
105
|
+
color $warm
|
|
106
|
+
|
|
107
|
+
.pulse-button
|
|
108
|
+
border 0
|
|
109
|
+
bg $ink
|
|
110
|
+
color white
|
|
111
|
+
padding .85rem 1rem
|
|
112
|
+
radius $radius
|
|
113
|
+
font inherit
|
|
114
|
+
cursor pointer
|
|
115
|
+
|
|
116
|
+
.pulse-button.is-on
|
|
117
|
+
bg $warm
|
|
118
|
+
|
|
119
|
+
.sprint-board
|
|
120
|
+
display grid
|
|
121
|
+
grid-template-columns repeat(auto-fit, minmax(130px, 1fr))
|
|
122
|
+
gap $gap
|
|
123
|
+
margin 1rem 0
|
|
124
|
+
|
|
125
|
+
.sprint-board article
|
|
126
|
+
bg $panel
|
|
127
|
+
border 1px solid $line
|
|
128
|
+
padding 1rem
|
|
129
|
+
radius $radius
|
|
130
|
+
|
|
131
|
+
.sprint-board span
|
|
132
|
+
display block
|
|
133
|
+
color $accent
|
|
134
|
+
font-size .9rem
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Darkmown
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
@include /nav.wd
|
|
6
|
+
|
|
7
|
+
<main>
|
|
8
|
+
|
|
9
|
+
<section class="hero">
|
|
10
|
+
<p class="eyebrow">Markdown-native framework</p>
|
|
11
|
+
|
|
12
|
+
# Darkmown
|
|
13
|
+
|
|
14
|
+
Markdown pages that compile into fast, mostly-static websites, with tiny reactive islands only where the document asks for them.
|
|
15
|
+
|
|
16
|
+
<a class="btn primary" href="/docs/">Read the docs</a>
|
|
17
|
+
<a class="btn" href="/reactive/">Try reactivity</a>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
:note "Two formats, one rule: .md stays plain CommonMark forever, and renaming to .wd is what unlocks directives, includes, loops, and state."
|
|
21
|
+
|
|
22
|
+
## What to try
|
|
23
|
+
|
|
24
|
+
:try "Open the docs route, a .wd page full of includes" href="/docs/"
|
|
25
|
+
:try "Open a plain .md page and see directives stay inert" href="/markdown/"
|
|
26
|
+
:try "Open the reactive page: state, keyed loops, scoped sections" href="/reactive/"
|
|
27
|
+
:try "Open the data page: :fetch, forms into state, persistence" href="/data/"
|
|
28
|
+
:try "Open a hidden route and confirm it 404s" href="/-draft/"
|
|
29
|
+
|
|
30
|
+
## Loops that read like prose
|
|
31
|
+
|
|
32
|
+
`@loop` is the only loop. Point it at a JSON file, an in-scope value, or a `:state` list, and the body repeats once per row. Includes inside the loop inherit the loop value:
|
|
33
|
+
|
|
34
|
+
```wd
|
|
35
|
+
@loop /features.json into card
|
|
36
|
+
@include /feature-card.wd
|
|
37
|
+
@endloop
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
@loop /features.json into card
|
|
41
|
+
@include /feature-card.wd
|
|
42
|
+
@endloop
|
|
43
|
+
|
|
44
|
+
## Colocated behavior
|
|
45
|
+
|
|
46
|
+
This button is powered by `site/pages/index.js`, discovered by matching the page basename:
|
|
47
|
+
|
|
48
|
+
<button class="pulse-button" data-wd-demo>Script is waiting</button>
|
|
49
|
+
|
|
50
|
+
## Deploy in one click
|
|
51
|
+
|
|
52
|
+
This repo doubles as a template — `npm run build` emits a plain static `dist/`, so it deploys anywhere that serves files:
|
|
53
|
+
|
|
54
|
+
<a class="btn primary" href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FZVN-DEV%2Fdarkmown">Deploy to Vercel</a>
|
|
55
|
+
<a class="btn" href="https://deploy.workers.cloudflare.com/?url=https%3A%2F%2Fgithub.com%2FZVN-DEV%2Fdarkmown">Deploy to Cloudflare</a>
|
|
56
|
+
<a class="btn" href="https://github.com/ZVN-DEV/darkmown">Star on GitHub</a>
|
|
57
|
+
|
|
58
|
+
## Sprint board
|
|
59
|
+
|
|
60
|
+
:sprint min=4 max=6 roles="PM, Development, Research, Testing, Review"
|
|
61
|
+
|
|
62
|
+
@include /sprint-loop.wd
|
|
63
|
+
|
|
64
|
+
</main>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Plain Markdown
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Plain Markdown stays plain
|
|
6
|
+
|
|
7
|
+
This page is `markdown.md`. Darkmown renders it with a real CommonMark parser and adds nothing — no runtime, no directives, no surprises.
|
|
8
|
+
|
|
9
|
+
## Everything CommonMark works
|
|
10
|
+
|
|
11
|
+
1. Ordered lists
|
|
12
|
+
2. With proper numbering
|
|
13
|
+
1. And nesting
|
|
14
|
+
|
|
15
|
+
> Blockquotes render correctly, including **inline emphasis**.
|
|
16
|
+
|
|
17
|
+
| Feature | Status |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| Tables | Working |
|
|
20
|
+
| Ordered lists | Working |
|
|
21
|
+
| Blockquotes | Working |
|
|
22
|
+
| Images | Working |
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
## Directives stay inert here
|
|
27
|
+
|
|
28
|
+
The next line is `.wd` syntax, left as plain text on purpose:
|
|
29
|
+
|
|
30
|
+
:state count = 0
|
|
31
|
+
|
|
32
|
+
And `{ count }` braces in prose are just braces: { count }
|
|
33
|
+
|
|
34
|
+
Rename this file to `markdown.wd` and all of it comes alive. That is the whole upgrade path.
|
|
35
|
+
|
|
36
|
+
[Back home](/)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
tokens
|
|
2
|
+
ink #171717
|
|
3
|
+
paper #fbfaf7
|
|
4
|
+
panel #ffffff
|
|
5
|
+
line #dedbd3
|
|
6
|
+
accent #16645a
|
|
7
|
+
radius 8px
|
|
8
|
+
|
|
9
|
+
html
|
|
10
|
+
bg $paper
|
|
11
|
+
|
|
12
|
+
page
|
|
13
|
+
max-width 760px
|
|
14
|
+
margin 0 auto
|
|
15
|
+
padding 3rem 2rem 5rem
|
|
16
|
+
font ui-serif, Georgia, Cambria, serif
|
|
17
|
+
color $ink
|
|
18
|
+
bg $paper
|
|
19
|
+
line-height 1.7
|
|
20
|
+
|
|
21
|
+
h1
|
|
22
|
+
font-size 3rem
|
|
23
|
+
line-height 1
|
|
24
|
+
margin 2rem 0 1rem
|
|
25
|
+
|
|
26
|
+
h2
|
|
27
|
+
margin-top 2.4rem
|
|
28
|
+
padding-top 1.2rem
|
|
29
|
+
border-top 1px solid $line
|
|
30
|
+
|
|
31
|
+
a
|
|
32
|
+
color $accent
|
|
33
|
+
|
|
34
|
+
code
|
|
35
|
+
bg $panel
|
|
36
|
+
border 1px solid $line
|
|
37
|
+
padding .08rem .28rem
|
|
38
|
+
radius 5px
|
|
39
|
+
|
|
40
|
+
blockquote
|
|
41
|
+
margin 1rem 0
|
|
42
|
+
padding .5rem 1.2rem
|
|
43
|
+
border-left 3px solid $accent
|
|
44
|
+
bg $panel
|
|
45
|
+
radius $radius
|
|
46
|
+
|
|
47
|
+
table
|
|
48
|
+
border-collapse collapse
|
|
49
|
+
bg $panel
|
|
50
|
+
|
|
51
|
+
th
|
|
52
|
+
text-align left
|
|
53
|
+
border 1px solid $line
|
|
54
|
+
padding .5rem .9rem
|
|
55
|
+
bg #f1ede4
|
|
56
|
+
|
|
57
|
+
td
|
|
58
|
+
border 1px solid $line
|
|
59
|
+
padding .5rem .9rem
|
|
60
|
+
|
|
61
|
+
img
|
|
62
|
+
width 64px
|
|
63
|
+
height 64px
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
tokens
|
|
2
|
+
ink #171717
|
|
3
|
+
paper #faf8f3
|
|
4
|
+
panel #ffffff
|
|
5
|
+
line #dedbd3
|
|
6
|
+
accent #16645a
|
|
7
|
+
warm #a83f2a
|
|
8
|
+
radius 8px
|
|
9
|
+
|
|
10
|
+
page
|
|
11
|
+
margin 0
|
|
12
|
+
font ui-serif, Georgia, Cambria, serif
|
|
13
|
+
color $ink
|
|
14
|
+
bg $paper
|
|
15
|
+
line-height 1.7
|
|
16
|
+
|
|
17
|
+
main
|
|
18
|
+
max-width 820px
|
|
19
|
+
margin 0 auto
|
|
20
|
+
padding 3rem 2rem 5rem
|
|
21
|
+
|
|
22
|
+
h1
|
|
23
|
+
font-size 3rem
|
|
24
|
+
line-height 1
|
|
25
|
+
|
|
26
|
+
h2
|
|
27
|
+
margin-top 2rem
|
|
28
|
+
border-top 1px solid $line
|
|
29
|
+
padding-top 1.2rem
|
|
30
|
+
|
|
31
|
+
button
|
|
32
|
+
border 1px solid $ink
|
|
33
|
+
bg $ink
|
|
34
|
+
color white
|
|
35
|
+
padding .72rem 1rem
|
|
36
|
+
margin .3rem .5rem .3rem 0
|
|
37
|
+
radius $radius
|
|
38
|
+
font inherit
|
|
39
|
+
cursor pointer
|
|
40
|
+
|
|
41
|
+
button:hover
|
|
42
|
+
bg $accent
|
|
43
|
+
border-color $accent
|
|
44
|
+
|
|
45
|
+
a
|
|
46
|
+
color $accent
|
|
47
|
+
|
|
48
|
+
ul
|
|
49
|
+
border 1px solid $line
|
|
50
|
+
bg $panel
|
|
51
|
+
padding 1rem 1rem 1rem 2rem
|
|
52
|
+
radius $radius
|
|
53
|
+
|
|
54
|
+
.tally-card
|
|
55
|
+
border 1px solid $line
|
|
56
|
+
bg $panel
|
|
57
|
+
padding .25rem 1.2rem 1.2rem
|
|
58
|
+
margin 1rem 0
|
|
59
|
+
radius $radius
|
|
60
|
+
|
|
61
|
+
.tally-card h3
|
|
62
|
+
color $warm
|