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 +5 -3
- package/dist/tutuca-cli.js +29 -15
- package/dist/tutuca-components.js +2444 -0
- package/dist/tutuca-dev.ext.js +18 -18
- package/dist/tutuca-dev.js +18 -18
- package/dist/tutuca-dev.min.js +2 -2
- package/dist/tutuca-extra.ext.js +2 -2
- package/dist/tutuca-extra.js +2 -2
- package/dist/tutuca-extra.min.js +1 -1
- package/dist/tutuca-storybook.js +154 -16
- package/dist/tutuca.ext.js +2 -2
- package/dist/tutuca.js +2 -2
- package/dist/tutuca.min.js +1 -1
- package/package.json +3 -1
- package/skill/tutuca/cli.md +10 -68
- package/skill/tutuca/core.md +25 -74
- package/skill/tutuca/semantics.md +4 -3
- package/skill/tutuca/testing.md +3 -6
- package/skill/tutuca-source/tutuca.ext.js +2 -2
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
|
-
- ~
|
|
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`
|
|
150
|
-
`
|
|
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
|
|
package/dist/tutuca-cli.js
CHANGED
|
@@ -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 ?? ""}
|
|
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}
|
|
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,
|
|
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
|
|
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") {
|