tutuca 0.9.98 → 0.9.100

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/README.md CHANGED
@@ -7,7 +7,7 @@ Zero-dependency batteries included SPA framework.
7
7
  - **Fits in your head** (and the context window)
8
8
  - **View source friendly** — step through the whole stack
9
9
  - **As much HTML as possible, as little JS as needed**
10
- - ~177KB minified, ~40KB brotli compressed
10
+ - ~182KB minified, ~41KB brotli compressed
11
11
 
12
12
  ## Quick Start
13
13
 
@@ -95,6 +95,7 @@ tutuca help [command]
95
95
  | `lint <module> [name]` | Run lint checks — all, or one by name (exit 2 on errors) |
96
96
  | `render <module> [name] [--title t] [--view v]` | Render examples to HTML |
97
97
  | `test <module> [name] [--grep p] [--bail]` | Run `getTests()` (exit 4 on failures) |
98
+ | `storybook [dir]` | Serve a live storybook, auto-discovering co-located `*.dev.js` modules (`--port`, `--out`, `--dry-run`, `--no-margaui`, `--no-check`, `--no-tests`; no module path needed) |
98
99
  | `feedback [message]` | Append a feedback note (positional or stdin) to `~/.tutuca/feedback.jsonl` (no module path needed) |
99
100
  | `install-skill [--user\|--project] [--margaui-skill\|--immutable-skill\|--all] [--dot-agents] [--dry-run] [--force]` | Install bundled Claude Code skills (no module path needed) |
100
101
  | `agent-context` | Print a versioned JSON schema of the entire CLI surface (no module path needed) |
@@ -146,8 +147,9 @@ The invocation stays short even without wrapping, but common patterns:
146
147
 
147
148
  ## Use with Claude Code
148
149
 
149
- Tutuca ships an LLM-facing reference (`SKILL.md` + `core.md` / `cli.md` /
150
- `advanced.md` / `testing.md`) packaged as a [Claude Code skill](https://docs.claude.com/en/docs/claude-code/skills).
150
+ Tutuca ships an LLM-facing reference (`SKILL.md` plus topic files such as
151
+ `core.md`, `cli.md`, `advanced.md`, `testing.md`, `storybook.md`, and more)
152
+ packaged as a [Claude Code skill](https://docs.claude.com/en/docs/claude-code/skills).
151
153
  Once installed, Claude auto-loads it whenever a session touches tutuca
152
154
  components, views, macros, or the CLI.
153
155
 
@@ -9776,7 +9776,7 @@ class Renderer {
9776
9776
  return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
9777
9777
  }
9778
9778
  _rValComp(stack, val, comp, node, key, viewName) {
9779
- const cacheKey = `${viewName ?? stack.viewsId ?? ""}-${key}`;
9779
+ const cacheKey = `${viewName ?? ""}\x1F${stack.viewsId ?? ""}\x1F${key}`;
9780
9780
  const cachePath = [node, val];
9781
9781
  stack._pushDynBindValuesToArray(cachePath, comp);
9782
9782
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -9821,7 +9821,7 @@ class Renderer {
9821
9821
  const renderOne = (key, value, attrName) => {
9822
9822
  const cachePath = enricher ? [view, it, value] : [view, value];
9823
9823
  const binds = { key, value };
9824
- const cacheKey = `${nid}-${key}`;
9824
+ const cacheKey = `${stack.viewsId ?? ""}\x1F${nid}\x1F${key}`;
9825
9825
  if (enricher)
9826
9826
  enricher.call(it, binds, key, value, iterData);
9827
9827
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -11394,7 +11394,7 @@ var init_html_tokenizer = __esm(() => {
11394
11394
  });
11395
11395
 
11396
11396
  // tools/core/htmllinter-tables.js
11397
- var VOID_ELEMENTS, RAW_TEXT_ELEMENTS, RCDATA_ELEMENTS, SPECIAL_ELEMENTS, FORMATTING_ELEMENTS, DEFAULT_SCOPE_BOUNDARIES, MATHML_TEXT_INTEGRATION_POINT_NAMES, SVG_HTML_INTEGRATION_POINT_NAMES, SCOPE_LIST_ITEM, SCOPE_BUTTON, SCOPE_DEFAULT, SCOPE_TABLE, SCOPE_SELECT, STANDARD_SVG_CAMEL_ELEMENTS, STANDARD_SVG_CAMEL_ATTRS, MATHML_CAMEL_ATTRS, SVG_ATTR_LOWERCASE_TO_CAMEL, MATHML_ATTR_LOWERCASE_TO_CAMEL, FOREIGN_BREAKOUT_TAGS, MATHML_TEXT_INTEGRATION_POINTS, BLOCK_LEVEL_AUTO_CLOSE_P, SELECT_VALID_CHILDREN, SELECT_BREAKOUT_TAGS, MODES, NS, FRAGMENT_CONTEXT_MODES;
11397
+ var VOID_ELEMENTS, RAW_TEXT_ELEMENTS, SPECIAL_ELEMENTS, FORMATTING_ELEMENTS, DEFAULT_SCOPE_BOUNDARIES, MATHML_TEXT_INTEGRATION_POINT_NAMES, SVG_HTML_INTEGRATION_POINT_NAMES, SCOPE_LIST_ITEM, SCOPE_BUTTON, SCOPE_DEFAULT, SCOPE_TABLE, STANDARD_SVG_CAMEL_ELEMENTS, STANDARD_SVG_CAMEL_ATTRS, MATHML_CAMEL_ATTRS, SVG_ATTR_LOWERCASE_TO_CAMEL, MATHML_ATTR_LOWERCASE_TO_CAMEL, FOREIGN_BREAKOUT_TAGS, BLOCK_LEVEL_AUTO_CLOSE_P, SELECT_BREAKOUT_TAGS, MODES, NS, FRAGMENT_CONTEXT_MODES;
11398
11398
  var init_htmllinter_tables = __esm(() => {
11399
11399
  VOID_ELEMENTS = new Set([
11400
11400
  "area",
@@ -11421,7 +11421,6 @@ var init_htmllinter_tables = __esm(() => {
11421
11421
  "xmp",
11422
11422
  "plaintext"
11423
11423
  ]);
11424
- RCDATA_ELEMENTS = new Set(["textarea", "title"]);
11425
11424
  SPECIAL_ELEMENTS = new Set([
11426
11425
  "address",
11427
11426
  "applet",
@@ -11536,7 +11535,6 @@ var init_htmllinter_tables = __esm(() => {
11536
11535
  SCOPE_BUTTON = new Set([...DEFAULT_SCOPE_BOUNDARIES, "button"]);
11537
11536
  SCOPE_DEFAULT = DEFAULT_SCOPE_BOUNDARIES;
11538
11537
  SCOPE_TABLE = new Set(["html", "table", "template"]);
11539
- SCOPE_SELECT = new Set;
11540
11538
  STANDARD_SVG_CAMEL_ELEMENTS = new Set([
11541
11539
  "altGlyph",
11542
11540
  "altGlyphDef",
@@ -11688,7 +11686,6 @@ var init_htmllinter_tables = __esm(() => {
11688
11686
  "ul",
11689
11687
  "var"
11690
11688
  ]);
11691
- MATHML_TEXT_INTEGRATION_POINTS = new Set(["mi", "mo", "mn", "ms", "mtext"]);
11692
11689
  BLOCK_LEVEL_AUTO_CLOSE_P = new Set([
11693
11690
  "address",
11694
11691
  "article",
@@ -11731,7 +11728,6 @@ var init_htmllinter_tables = __esm(() => {
11731
11728
  "dd",
11732
11729
  "dt"
11733
11730
  ]);
11734
- SELECT_VALID_CHILDREN = new Set(["option", "optgroup", "hr", "script", "template"]);
11735
11731
  SELECT_BREAKOUT_TAGS = new Set(["input", "keygen", "textarea", "select"]);
11736
11732
  MODES = Object.freeze({
11737
11733
  inBody: "inBody",
@@ -16115,7 +16111,8 @@ function buildImports(base, { margaui }) {
16115
16111
  tutuca: dev,
16116
16112
  "tutuca/extra": dev,
16117
16113
  "tutuca/dev": dev,
16118
- "tutuca/storybook": `${base}/tutuca-storybook.js`
16114
+ "tutuca/storybook": `${base}/tutuca-storybook.js`,
16115
+ "tutuca/components": `${base}/tutuca-components.js`
16119
16116
  };
16120
16117
  if (margaui)
16121
16118
  imports.margaui = MARGAUI_CDN;
@@ -16141,19 +16138,29 @@ ${JSON.stringify({ imports }, null, 6)}
16141
16138
  </html>
16142
16139
  `;
16143
16140
  }
16144
- function renderBootstrap(devModuleUrls, { margaui, check }) {
16141
+ function renderBootstrap(devModuleUrls, { margaui, check, inspect: inspect3, noCache }) {
16145
16142
  const lines = ['import { mountStorybook } from "tutuca/storybook";'];
16146
16143
  if (margaui) {
16147
16144
  lines.push('import { compileClassesToStyleText } from "tutuca/extra";');
16148
16145
  lines.push('import { compile } from "margaui";');
16149
16146
  }
16147
+ if (inspect3) {
16148
+ lines.push('import { shadowCheckComponent, runTests, expect } from "tutuca/dev";');
16149
+ }
16150
16150
  if (check)
16151
16151
  lines.push('import { check } from "tutuca/dev";');
16152
16152
  devModuleUrls.forEach((url, i) => {
16153
16153
  lines.push(`import * as m${i} from ${JSON.stringify(url)};`);
16154
16154
  });
16155
16155
  const modules = devModuleUrls.map((_, i) => `m${i}`).join(", ");
16156
- const opts = margaui ? "{ compileCss: (app) => compileClassesToStyleText(app, compile) }" : "{}";
16156
+ const optParts = [];
16157
+ if (margaui)
16158
+ optParts.push("compileCss: (app) => compileClassesToStyleText(app, compile)");
16159
+ if (inspect3)
16160
+ optParts.push("dev: { shadowCheckComponent, runTests, expect }");
16161
+ if (noCache)
16162
+ optParts.push("noCache: true");
16163
+ const opts = optParts.length ? `{ ${optParts.join(", ")} }` : "{}";
16157
16164
  lines.push("");
16158
16165
  lines.push(`const app = await mountStorybook("#app", [${modules}], ${opts});`);
16159
16166
  if (check)
@@ -16267,7 +16274,9 @@ async function run4(argv, opts = {}) {
16267
16274
  out: { type: "string" },
16268
16275
  "no-margaui": { type: "boolean", default: false },
16269
16276
  "no-check": { type: "boolean", default: false },
16277
+ "no-inspect": { type: "boolean", default: false },
16270
16278
  "no-tests": { type: "boolean", default: false },
16279
+ "no-cache": { type: "boolean", default: false },
16271
16280
  "dry-run": { type: "boolean", default: false },
16272
16281
  help: { type: "boolean", short: "h", default: false }
16273
16282
  },
@@ -16275,7 +16284,8 @@ async function run4(argv, opts = {}) {
16275
16284
  });
16276
16285
  if (parsed.values.help) {
16277
16286
  process.stdout.write(`tutuca storybook [dir] [--port <n>] [--out <dir>] [--dry-run]
16278
- [--no-margaui] [--no-check] [--no-tests]
16287
+ [--no-margaui] [--no-check] [--no-inspect] [--no-tests]
16288
+ [--no-cache]
16279
16289
 
16280
16290
  Auto-discovers co-located *.dev.js modules (recursively, skipping
16281
16291
  node_modules/dotdirs) and serves a live storybook that mounts them via
@@ -16290,7 +16300,9 @@ async function run4(argv, opts = {}) {
16290
16300
  shown instead of serving; pass --json for structured output
16291
16301
  --no-margaui skip margaui styling (renders functional but unstyled)
16292
16302
  --no-check skip the in-browser check(app) dev validation
16303
+ --no-inspect skip the per-example Component/Instance/Data/Lint/Test tabs
16293
16304
  --no-tests skip running the modules' getTests() before serving
16305
+ --no-cache disable the render cache (NullDomCache); see every re-render
16294
16306
  `);
16295
16307
  return;
16296
16308
  }
@@ -16312,6 +16324,8 @@ async function run4(argv, opts = {}) {
16312
16324
  }
16313
16325
  const margaui = !parsed.values["no-margaui"];
16314
16326
  const check = !parsed.values["no-check"];
16327
+ const inspect3 = !parsed.values["no-inspect"];
16328
+ const noCache = parsed.values["no-cache"];
16315
16329
  const self = findSelf();
16316
16330
  if (parsed.values.out) {
16317
16331
  const outDir = resolve5(parsed.values.out);
@@ -16320,7 +16334,7 @@ async function run4(argv, opts = {}) {
16320
16334
  const imports2 = buildImports(base2, { margaui });
16321
16335
  const bootstrapName = "tutuca-storybook.bootstrap.js";
16322
16336
  writeFileSync(resolve5(outDir, "index.html"), renderIndexHtml(imports2, { margaui, bootstrapUrl: `./${bootstrapName}` }));
16323
- writeFileSync(resolve5(outDir, bootstrapName), renderBootstrap(devModuleUrls, { margaui, check }));
16337
+ writeFileSync(resolve5(outDir, bootstrapName), renderBootstrap(devModuleUrls, { margaui, check, inspect: inspect3, noCache }));
16324
16338
  process.stdout.write(`wrote static storybook → ${relative2(process.cwd(), outDir) || "."}/
16325
16339
  index.html + ${bootstrapName} (${devModuleUrls.length} dev modules, CDN import map)
16326
16340
  Host it from the project root so /*.dev.js paths resolve.
@@ -16336,7 +16350,7 @@ async function run4(argv, opts = {}) {
16336
16350
  const result = {
16337
16351
  projectDir,
16338
16352
  tutuca: { source, base: base2, version: self.version },
16339
- options: { margaui, check, runTests: !parsed.values["no-tests"] },
16353
+ options: { margaui, check, noCache, runTests: !parsed.values["no-tests"] },
16340
16354
  imports: imports2,
16341
16355
  modules,
16342
16356
  tests
@@ -16349,7 +16363,7 @@ async function run4(argv, opts = {}) {
16349
16363
  process.stdout.write(`tutuca storybook dry run (no server started)
16350
16364
  project: ${projectDir}
16351
16365
  tutuca runtime: ${source} (${base2}, version ${self.version})
16352
- margaui: ${margaui ? "on" : "off"}, in-browser check: ${check ? "on" : "off"}
16366
+ margaui: ${margaui ? "on" : "off"}, in-browser check: ${check ? "on" : "off"}, cache: ${noCache ? "off" : "on"}
16353
16367
  ${modules.length} dev module(s):
16354
16368
  `);
16355
16369
  for (const m of modules) {
@@ -16405,7 +16419,7 @@ async function run4(argv, opts = {}) {
16405
16419
  const { base, serveDist } = resolveTutucaBase(projectDir, self, false);
16406
16420
  const imports = buildImports(base, { margaui });
16407
16421
  const indexHtml = renderIndexHtml(imports, { margaui, bootstrapUrl: BOOTSTRAP_URL });
16408
- const bootstrapJs = renderBootstrap(devModuleUrls, { margaui, check });
16422
+ const bootstrapJs = renderBootstrap(devModuleUrls, { margaui, check, inspect: inspect3, noCache });
16409
16423
  const server = createServer((req, res) => {
16410
16424
  const path = req.url.split("?")[0];
16411
16425
  if (path === "/" || path === "/index.html") {