tutuca 0.9.82 → 0.9.84

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tutuca",
3
- "version": "0.9.82",
3
+ "version": "0.9.84",
4
4
  "type": "module",
5
5
  "description": "Zero-dependency SPA framework with immutable state and virtual DOM",
6
6
  "main": "./dist/tutuca.js",
@@ -13,6 +13,7 @@
13
13
  "./ext": "./dist/tutuca.ext.js",
14
14
  "./extra-ext": "./dist/tutuca-extra.ext.js",
15
15
  "./dev-ext": "./dist/tutuca-dev.ext.js",
16
+ "./storybook": "./dist/tutuca-storybook.js",
16
17
  "./immutable": "./dist/immutable.js",
17
18
  "./chai": "./dist/chai.js",
18
19
  "./package.json": "./package.json"
@@ -40,7 +41,9 @@
40
41
  "stresstest": "bun scripts/stresstest.js",
41
42
  "smoke-test": "bun tools/tutuca.js lint ./test/todo.js && bun tools/tutuca.js render ./test/todo.js && bun tools/tutuca.js test ./test/todo.js && bun tools/tutuca.js lint ./test/json.js && bun tools/tutuca.js render ./test/json.js"
42
43
  },
43
- "sideEffects": false,
44
+ "sideEffects": [
45
+ "./deps/chai.js"
46
+ ],
44
47
  "files": [
45
48
  "dist/tutuca.js",
46
49
  "dist/tutuca.min.js",
@@ -52,6 +55,7 @@
52
55
  "dist/tutuca.ext.js",
53
56
  "dist/tutuca-extra.ext.js",
54
57
  "dist/tutuca-dev.ext.js",
58
+ "dist/tutuca-storybook.js",
55
59
  "dist/immutable.js",
56
60
  "dist/chai.js",
57
61
  "skill"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tutuca
3
- description: Use when authoring or reviewing tutuca modules — `component({...})` definitions, `html`...`` views, `@`-directives, `input` / `bubble` / `receive` / `response` / `alter` handlers, macros, or `getTests` exports — or when running the `tutuca` CLI (`lint` / `test` / `render` / `docs`). Covers the post-edit `tutuca <module> lint` → `test` → `render --title "<example>"` verification recipe.
3
+ description: Use when authoring or reviewing tutuca modules — `component({...})` definitions, `html`...`` views, `@`-directives, `input` / `bubble` / `receive` / `response` / `alter` handlers, macros, or `getTests` exports — or when running the `tutuca` CLI (`lint` / `test` / `render` / `show`). Covers the post-edit `tutuca lint <module>` → `test` → `render --title "<example>"` verification recipe.
4
4
  ---
5
5
 
6
6
  <!-- Source of truth: docs/skill/. The skill/tutuca/ copy is generated by scripts/build-skill.js — edit docs/skill/, not skill/. -->
@@ -9,7 +9,7 @@ description: Use when authoring or reviewing tutuca modules — `component({...}
9
9
 
10
10
  Tutuca is an immutable-state SPA framework. See [core.md](./core.md)
11
11
  for the framework primer and the post-edit verification recipe
12
- (`tutuca <module> lint` → `test` → `render --title "<example>"`).
12
+ (`tutuca lint <module>` → `test` → `render --title "<example>"`).
13
13
 
14
14
  ## Companion skills
15
15
 
@@ -165,8 +165,8 @@ import { SEQ_INFO } from "tutuca";
165
165
  class MyClass {
166
166
  // ...
167
167
  }
168
- MyClass.prototype[SEQ_INFO] = (seq, visit) => {
169
- for (const [k, v] of seq.entries()) visit(k, v, "data-sk");
168
+ MyClass.prototype[SEQ_INFO] = (seq, visit, start, end) => {
169
+ for (const [k, v] of seq.entries()) visit(k, v, "sk");
170
170
  };
171
171
  ```
172
172
 
@@ -175,8 +175,18 @@ is shared across module graphs (source vs. bundled tutuca). The
175
175
  renderer reads `seq[SEQ_INFO]` directly (no `.constructor` lookup),
176
176
  which is why the walker goes on the prototype, not as a static.
177
177
 
178
- The third arg to `visit` is the data attribute used for stable-key
179
- diffing (typically `"data-sk"` for "sequence key").
178
+ The third arg to `visit` must be `"sk"` (keyed entries) or `"si"`
179
+ (positional indexes): it is the meta key event-path reconstruction
180
+ reads to resolve an event back to its entry — any other value means
181
+ `@key` and keyed steps silently stop resolving inside `@each`.
182
+
183
+ `start`/`end` are an optional `[start, end)` slice produced by
184
+ `@loop-with` (Array.slice semantics). Walkers may ignore them, in
185
+ which case `@loop-with` ranges simply don't apply to that type.
186
+
187
+ See `docs/examples/custom-collection.js` for a complete worked
188
+ example: an immutable keyed list whose walker supports slicing and
189
+ whose entries resolve `@key` in event handlers.
180
190
 
181
191
  ## Tailwind / MargaUI Class Compilation (extra build)
182
192
 
@@ -42,6 +42,7 @@ Use `--module=<path>` if the path conflicts with positional parsing.
42
42
  | `test <module> [name]` | Run tests defined by `getTests({ describe, test, expect })`. Filter by component name, `--grep <pattern>`, or `--bail`. Exits **4** on any failure |
43
43
  | `help [cmd]` | Show usage. No module path needed |
44
44
  | `feedback [message]` | Append a feedback note (positional or stdin) to `~/.tutuca/feedback.jsonl`. No module path needed |
45
+ | `install-skill [name]` | Copy a bundled skill (`tutuca`, `margaui`, `immutable-js`, or `--all`) into `.claude/skills/`. No module path needed |
45
46
  | `agent-context` | Print a versioned JSON schema of every command, flag, exit code, and error code. No module path needed |
46
47
 
47
48
  ## Global flags
@@ -119,7 +119,7 @@ A compact worked version of the first four (`method`, `bubble`, `send`/`receive`
119
119
  → [advanced.md](./advanced.md) "Pitfall: `@if.class` payloads are invisible to
120
120
  the scanner", and the worked decoy view in `personal-site.js`.
121
121
 
122
- - **Do close the loop after every change** with `tutuca <module> lint` → `test` →
122
+ - **Do close the loop after every change** with `tutuca lint <module>` → `test` →
123
123
  `render`. → [core.md](./core.md) "Verifying changes"
124
124
 
125
125
  ## Smells & refactors
@@ -977,6 +977,17 @@ export function getExamples() {
977
977
  export function getTests({ describe, test, expect }) { /*...*/ } // optional — see cli.md
978
978
  ```
979
979
 
980
+ Best practice: have `getComponents()` return **every** component the module
981
+ defines — child and helper components included — and give each one at least
982
+ one item in `getExamples()`. A component left out of `getComponents()` is
983
+ invisible to `tutuca lint`/`render`/`test`, so it silently loses linting and
984
+ render coverage. If your components already live behind a differently named
985
+ export, alias it instead of teaching tools a new name:
986
+
987
+ ```js
988
+ export { allMyComponents as getComponents } from "./app.js";
989
+ ```
990
+
980
991
  ## See also
981
992
 
982
993
  - [component-design.md](./component-design.md) — design judgment for shaping a
@@ -36,6 +36,7 @@ task.
36
36
 
37
37
  - [Bind text and attributes](bind-text-and-attributes.md) — `@text`, `:attr`, `$'…'` templates, scope enrichment.
38
38
  - [Handle events](handle-events.md) — `@on.<event>`, handler args, modifiers, custom events.
39
+ - [Read a picked file](file-input.md) — `@on.change="… event"` and reading `event.target.files`.
39
40
 
40
41
  ## Component communication
41
42
 
@@ -0,0 +1,39 @@
1
+ # Read a picked file
2
+
3
+ **Problem:** let the user pick a file and show its metadata.
4
+
5
+ ```js
6
+ component({
7
+ name: "FilePicker",
8
+ fields: { name: "", size: 0, type: "", hasFile: false },
9
+ input: {
10
+ // `event` is the raw DOM event; the File is on event.target.files
11
+ onPickFile(event) {
12
+ const file = event.target.files?.[0];
13
+ if (!file) return this.setHasFile(false);
14
+ return this.setName(file.name)
15
+ .setSize(file.size)
16
+ .setType(file.type)
17
+ .setHasFile(true);
18
+ },
19
+ },
20
+ view: html`<section>
21
+ <input type="file" @on.change="onPickFile event" />
22
+ <p @hide=".hasFile">No file selected yet.</p>
23
+ <dl @show=".hasFile">
24
+ <dt>Name</dt><dd @text=".name"></dd>
25
+ <dt>Size</dt><dd @text=".size"></dd>
26
+ <dt>Type</dt><dd @text=".type"></dd>
27
+ </dl>
28
+ </section>`,
29
+ });
30
+ ```
31
+
32
+ Pass `event` (not `value`) to the handler: for a file input `value` is just the
33
+ fake `C:\fakepath\…` string, while `event.target.files` holds the real `File`
34
+ objects. The metadata (`name`/`size`/`type`/`lastModified`) is available
35
+ synchronously; the *contents* are not — read those with the async `File` API
36
+ (`file.text()`, `file.arrayBuffer()`) and feed the result back in through a
37
+ `request`/`response` or a follow-up `send`. Flatten what you need into fields so
38
+ the view can bind each piece; gate the summary on a `hasFile` flag with
39
+ `@show`/`@hide`.