tutuca 0.9.111 → 0.9.113
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/dist/tutuca-cli.js +55 -11
- package/dist/tutuca-dev.ext.js +9 -4
- package/dist/tutuca-dev.js +9 -4
- package/dist/tutuca-dev.min.js +2 -2
- package/dist/tutuca-extra.ext.js +9 -4
- package/dist/tutuca-extra.js +9 -4
- package/dist/tutuca-extra.min.js +2 -2
- package/dist/tutuca-storybook.js +106 -7
- package/dist/tutuca.ext.js +9 -4
- package/dist/tutuca.js +9 -4
- package/dist/tutuca.min.js +2 -2
- package/package.json +2 -2
- package/skill/tutuca/margaui.md +39 -0
- package/skill/tutuca/storybook.md +26 -0
- package/skill/tutuca/testing.md +8 -2
- package/skill/tutuca-source/tutuca.ext.js +9 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tutuca",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.113",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SPA framework with immutable state and virtual DOM; dependency-free browser bundle",
|
|
6
6
|
"main": "./dist/tutuca.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"sync-docs-storybook": "bun scripts/sync-docs-storybook.js",
|
|
35
35
|
"build-skill": "bun scripts/build-skill.js",
|
|
36
36
|
"clean-skill": "rm -rf skill",
|
|
37
|
-
"test": "bun test test/*.test.js",
|
|
37
|
+
"test": "bun run dist && bun run dist-ext && bun test test/*.test.js",
|
|
38
38
|
"test-watch": "bun test --watch test/*.test.js",
|
|
39
39
|
"format": "bunx @biomejs/biome format --write",
|
|
40
40
|
"lint": "bunx @biomejs/biome lint",
|
package/skill/tutuca/margaui.md
CHANGED
|
@@ -68,6 +68,45 @@ that reason):
|
|
|
68
68
|
Trade-off: no runtime network dependency and a frozen version, at the
|
|
69
69
|
cost of updating the vendored files by hand.
|
|
70
70
|
|
|
71
|
+
## Dark mode and the other palettes
|
|
72
|
+
|
|
73
|
+
A margaui theme is a block of CSS custom properties (`--color-*`, `--radius-*`,
|
|
74
|
+
…) under a `[data-theme="<name>"]` selector, and every class it compiles reads
|
|
75
|
+
those through `var(--color-*)`. So switching theme is **one attribute flip on
|
|
76
|
+
`<html>`** — the compiled stylesheet never changes:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
document.documentElement.dataset.theme = "dark";
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Two things to know, both of which bite if you assume otherwise:
|
|
83
|
+
|
|
84
|
+
- **Dark mode never turns itself on.** `dark.css` is keyed on
|
|
85
|
+
`[data-theme="dark"]` alone — there is no `prefers-color-scheme` fallback and
|
|
86
|
+
no `.dark` class. Link `theme.css` and do nothing else and the page is light
|
|
87
|
+
forever, on every machine. Following the OS is your job:
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
const dark = matchMedia("(prefers-color-scheme: dark)");
|
|
91
|
+
document.documentElement.dataset.theme = dark.matches ? "dark" : "light";
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- **`theme.css` is only light + dark.** It is literally
|
|
95
|
+
`@import"./light.css";@import"./dark.css";`. margaui ships ~33 more palettes
|
|
96
|
+
(dracula, nord, cyberpunk, …) as sibling files, each linked separately and
|
|
97
|
+
each cheap:
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<link rel="stylesheet" href="https://marianoguerra.github.io/margaui/themes/dracula.css" />
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Link it **after** `theme.css`: `light.css` claims plain `:root` as well as
|
|
104
|
+
`[data-theme=light]`, which ties on specificity with `[data-theme=dracula]`,
|
|
105
|
+
so a palette only wins by coming later in the cascade.
|
|
106
|
+
|
|
107
|
+
`tutuca storybook` does all of this for you — see the Themes section of
|
|
108
|
+
[storybook.md](./storybook.md).
|
|
109
|
+
|
|
71
110
|
## Wire it into tutuca
|
|
72
111
|
|
|
73
112
|
However you obtained `compile`, the integration is the same: register
|
|
@@ -215,6 +215,32 @@ version-pinned CDN. `--out` always pins the CDN so the artifact is portable —
|
|
|
215
215
|
host it from the project root so `/*.dev.js` paths resolve. See [cli.md](./cli.md)
|
|
216
216
|
for the exhaustive flag list and exit codes.
|
|
217
217
|
|
|
218
|
+
## Themes
|
|
219
|
+
|
|
220
|
+
The page starts in the theme the URL asks for (`?theme=dracula`), else the one
|
|
221
|
+
the OS prefers, and the sidebar has a switcher for margaui's palettes. Picking
|
|
222
|
+
one sets `?theme=`, so a themed storybook is shareable and survives a reload.
|
|
223
|
+
That OS step is on us, not margaui: its `dark.css` is keyed on
|
|
224
|
+
`[data-theme="dark"]` alone, with **no** `prefers-color-scheme` fallback, so a
|
|
225
|
+
page that only links `theme.css` renders light forever. See
|
|
226
|
+
[margaui.md](./margaui.md).
|
|
227
|
+
|
|
228
|
+
Embedding the storybook yourself? The switcher is opt-in through the `themes`
|
|
229
|
+
option, which — like `compileCss` — is injected, so the library still never
|
|
230
|
+
imports margaui:
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
mountStorybook("#app", modules, {
|
|
234
|
+
compileCss: (app) => compileClassesToStyleText(app, compile),
|
|
235
|
+
// The directory your theme.css lives in. Every other palette is a sibling
|
|
236
|
+
// file there; only the one selected is fetched, and only the first time.
|
|
237
|
+
themes: { baseUrl: "https://marianoguerra.github.io/margaui/themes/" },
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Omit `themes` (what `--no-margaui` does) and no switcher renders — there would
|
|
242
|
+
be no theme CSS to switch to.
|
|
243
|
+
|
|
218
244
|
## Footguns
|
|
219
245
|
|
|
220
246
|
- ⚠️ `value` must be a real instance (`Comp.make(...)`), not a plain object or
|
package/skill/tutuca/testing.md
CHANGED
|
@@ -157,8 +157,10 @@ you want to test them as a pipeline: filter + loop-data + enrichment
|
|
|
157
157
|
together produce a list of bindings the view sees. Use
|
|
158
158
|
`collectIterBindings` for that — a functional implementation only ships
|
|
159
159
|
in the dev build (`tutuca/dev`); the core `tutuca` build exports a no-op
|
|
160
|
-
stub
|
|
161
|
-
|
|
160
|
+
stub that returns `[]`. Both commands that run `getTests()` in the
|
|
161
|
+
terminal — `tutuca test` and `tutuca storybook` (including `--dry-run`) —
|
|
162
|
+
redirect the bare `tutuca` import to the dev build automatically, as does
|
|
163
|
+
the browser storybook's import map. So test modules can import it as below:
|
|
162
164
|
|
|
163
165
|
```js
|
|
164
166
|
import { collectIterBindings } from "tutuca";
|
|
@@ -180,6 +182,10 @@ const r = collectIterBindings(MyComp, c, c.items, {
|
|
|
180
182
|
- The `compInstance` is `this` for every handler. Pass
|
|
181
183
|
`MyComp.make({ field: ... })` so handlers that read `this.field` see
|
|
182
184
|
the value you want.
|
|
185
|
+
- The redirect uses Node's `module.register`, which is how the `tutuca`
|
|
186
|
+
bin runs. Under a Bun-driven CLI it degrades to the no-op stub — if you
|
|
187
|
+
see `collectIterBindings` return `[]`, import it from `tutuca/dev`
|
|
188
|
+
explicitly.
|
|
183
189
|
|
|
184
190
|
Example:
|
|
185
191
|
|
|
@@ -540,7 +540,6 @@ var PREDICATES = {
|
|
|
540
540
|
|
|
541
541
|
class ValParser {
|
|
542
542
|
constructor() {
|
|
543
|
-
this.bindValIt = new BindVal("it");
|
|
544
543
|
this.nullConstVal = new ConstVal(null);
|
|
545
544
|
}
|
|
546
545
|
const(v) {
|
|
@@ -2086,7 +2085,7 @@ function parseXOp(attrs, childs, opIdx, px) {
|
|
|
2086
2085
|
node = px.addNodeIf(RenderNode, parseXOpVal(name, value, px, vp.parseComponent), as);
|
|
2087
2086
|
break;
|
|
2088
2087
|
case "render-it":
|
|
2089
|
-
node = px.
|
|
2088
|
+
node = px.addNode(RenderItNode, as);
|
|
2090
2089
|
break;
|
|
2091
2090
|
case "render-each":
|
|
2092
2091
|
node = parseRenderEach(px, value, as, attrs);
|
|
@@ -2269,7 +2268,7 @@ function parseRenderEach(px, value, as, attrs) {
|
|
|
2269
2268
|
const seqVal = parseXOpVal("render-each", value, px, vp.parseSequence);
|
|
2270
2269
|
if (seqVal === null)
|
|
2271
2270
|
return null;
|
|
2272
|
-
const renderIt = px.
|
|
2271
|
+
const renderIt = px.addNode(RenderItNode, as);
|
|
2273
2272
|
const attrParser = getAttrParser(px);
|
|
2274
2273
|
const eachAttr = attrParser.eachAttr = attrParser.pushWrapper("each", value, seqVal);
|
|
2275
2274
|
const when = attrs.getNamedItem("@when") ?? attrs.getNamedItem("when");
|
|
@@ -2469,6 +2468,12 @@ class ParseContext {
|
|
|
2469
2468
|
}
|
|
2470
2469
|
return null;
|
|
2471
2470
|
}
|
|
2471
|
+
addNode(Class, extra) {
|
|
2472
|
+
const nodeId = this.nodes.length;
|
|
2473
|
+
const node = new Class(nodeId, null, extra);
|
|
2474
|
+
this.nodes.push(node);
|
|
2475
|
+
return node;
|
|
2476
|
+
}
|
|
2472
2477
|
registerEvents() {
|
|
2473
2478
|
const id = this.events.length;
|
|
2474
2479
|
const events = new NodeEvents(id);
|
|
@@ -2900,7 +2905,7 @@ class Stack {
|
|
|
2900
2905
|
return new Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
|
|
2901
2906
|
}
|
|
2902
2907
|
static root(comps, it, ctx) {
|
|
2903
|
-
const binds = [new BindFrame(it, {
|
|
2908
|
+
const binds = [new BindFrame(it, {}, true), null];
|
|
2904
2909
|
const dynBinds = [new ObjectFrame({}), null];
|
|
2905
2910
|
const views = ["main", null];
|
|
2906
2911
|
return new Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
|