@usableapp/cardds 0.7.70 → 0.7.72

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 CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  > For: a project that consumes `@usableapp/cardds` and its AI — what changed between versions, written as before → after, so a screen can be brought up to date without reading the source. Newest first. A consumer never edits cardds; if something here leaves you without a way to do what you did before, report the gap.
4
4
 
5
+ ## 0.7.72 — 2026-10-08
6
+
7
+ ### Added
8
+ - **The catalog — for an AI session that builds with cardds** (Lh 2026-10-08) — the package now carries every component, its props and slots, and every variation's code and rendered HTML (`dist/catalog.json`), with one set of commands over it. Before → after: a session read `src/` and the gallery's stories to learn a component → it asks. `npx cardds list` (every name, by group) · `npx cardds get Menu [--html] [--all]` (props, slots, each variation's code) · `npx cardds find stamp picker` (the screens already drawn first, then components, then what they are used with — ask in English words) · `npx cardds scaffold [id] [--html [--css <href>]]` (a file to start a screen from: the React code, or a static HTML page that links `cardds.css`) · `npx cardds check` as before. Each takes `--json`.
9
+ - **`npx cardds mcp`** — the same five as an MCP server over stdio (`list_components`, `get_component`, `find_components`, `scaffold_screen`, `check_source`): `{"mcpServers":{"cardds":{"command":"npx","args":["cardds","mcp"]}}}`. It answers for the version the project has installed. README "For an AI session: the catalog".
10
+
11
+ ## 0.7.71 — 2026-10-08
12
+
13
+ ### Fixed
14
+ - **A popover `Menu` / `Dropdown` on an iPhone was as tall as the screen** (Lh 2026-10-08) — in Safari on iOS 26 an open menu ran from under its button to the foot of the screen, its rows spread out over that height. Before → after: three rows stood 706px tall → 163px, each row its own 48px. Nothing to change in an app: the same markup, the same props. Chrome and Android never showed it.
15
+
5
16
  ## 0.7.70 — 2026-10-08
6
17
 
7
18
  ### Added
package/README.md CHANGED
@@ -136,6 +136,28 @@ back here. It is deliberately thin — **this repo stays the source of truth**,
136
136
  so new components and rule changes go here, and the skill only gets updated
137
137
  if the contract itself changes.
138
138
 
139
+ ### For an AI session: the catalog
140
+
141
+ A session asked for a prototype need not read `src/`: the package ships a catalog (`dist/catalog.json`, built by
142
+ `npm run build`) of every component — props, slots — and every story with its code and its rendered HTML. Ask it first:
143
+
144
+ ```bash
145
+ npx cardds find <words…> # the screens already drawn, then components, then what is used with them (no match → every screen)
146
+ npx cardds scaffold <id> # a whole file from a screen: tsx (the code) or --html (a static document linking cardds.css)
147
+ npx cardds get <Name> [--html] # one component: props (a ReactNode prop is a slot) + variations with code
148
+ npx cardds list # every name and group, nothing more
149
+ npx cardds check <dir> # the rules, before a screen is called done
150
+ ```
151
+
152
+ All take `--json` (the function's result) and `--root <path>` (a cardds checkout: rebuilt first, so never stale). The same
153
+ five answers over MCP — `list_components` · `get_component` · `find_components` · `scaffold_screen` · `check_source`:
154
+
155
+ ```json
156
+ { "mcpServers": { "cardds": { "command": "npx", "args": ["cardds", "mcp"] } } }
157
+ ```
158
+
159
+ A scaffolded tsx may import `./_story` or `./_*Art`: story glue, not in the package — replace or drop it.
160
+
139
161
  ### React adapter — how a component is written
140
162
 
141
163
  `src/<group>/<Name>.tsx`, one export per class family; sub-parts are their own
package/bin/cardds.mjs CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  // `npx cardds init` — set a project up the ONE way (Lh 2026-09-29): the project's theme.css, the check script, the ESLint rules.
3
3
  // `npx cardds check [dir…]` — the rules of lint/rules.mjs over the project's source; exit 1 on an error.
4
+ // `npx cardds list | get <Name> | find <words…> | scaffold [id]` — the component catalog (lib/catalog.mjs) for an AI session; `npx cardds mcp` serves the same over MCP.
4
5
  import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from 'node:fs';
5
6
  import { dirname, join, relative } from 'node:path';
6
7
  import { fileURLToPath } from 'node:url';
7
8
  import { checkSource } from '../lint/rules.mjs';
9
+ import * as catalog from '../lib/catalog.mjs';
8
10
 
9
11
  const PKG = join(dirname(fileURLToPath(import.meta.url)), '..');
10
12
  const [cmd, ...args] = process.argv.slice(2);
@@ -68,5 +70,43 @@ import them, never copy their markup: the behaviour lives in the component. Gall
68
70
  return 0;
69
71
  }
70
72
 
71
- const code = cmd === 'check' ? check(args) : cmd === 'init' ? init() : (console.log('usage: cardds init | cardds check [dir…]'), cmd ? 2 : 0);
72
- process.exit(code);
73
+ const one = (text) => text.replace(/\s+/g, ' ').trim();
74
+ const indent = (text, by = ' ') => text.replace(/^(?=.)/gm, by);
75
+
76
+ // each catalog command: the function's result as compact text (--json prints the result itself)
77
+ const asText = {
78
+ list: (groups) => Object.entries(Object.groupBy(groups, (c) => c.group)).map(([g, cs]) => `${g}: ${cs.map((c) => c.name).join(' ')}`).join('\n'),
79
+ get: (c, { html }) => [
80
+ `${c.name} (${c.group})${c.element ? ` — forwards the <${c.element}> attributes` : ''}`,
81
+ one(c.description),
82
+ '\nprops',
83
+ ...c.props.map((p) => ` ${p.name}${p.optional ? '?' : ''}: ${p.type}${p.slot ? ' [slot]' : ''}${p.description ? ` — ${one(p.description)}` : ''}`),
84
+ `\nvariations (${c.variations.length})`,
85
+ ...c.variations.map((v) => `\n ${v.id} — ${one(v.description)}\n${indent(v.tsx)}${html ? `\n html:\n${indent(v.html, ' ')}` : ''}`),
86
+ ...(c.alsoUsedIn ? [`\nalso used in: ${c.alsoUsedIn.join(' ')}\n(--all prints their code too)`] : []),
87
+ ].join('\n'),
88
+ find: (r) => [
89
+ ...(r.fallback ? [`nothing matches "${r.query}" — ${r.hint}. Every screen already drawn:\n${r.results.map((x) => x.id).join(' ')}`] : r.results.map((x) => `${x.kind === 'screen' ? 'screen ' : 'component '}${x.id ?? x.name} — ${x.summary}`)),
90
+ ...(r.usedTogether?.length ? [`\nused together: ${r.usedTogether.map((u) => `${u.name} (${u.stories})`).join(' ')}`] : []),
91
+ ].join('\n'),
92
+ scaffold: (r) => typeof r === 'string' ? r.trimEnd() : r.map((x) => `${x.id} — ${x.summary}`).join('\n'),
93
+ };
94
+
95
+ function ask(cmd, argv) {
96
+ const flag = (name) => { const i = argv.indexOf(name); return i >= 0 && argv.splice(i, 1).length > 0; };
97
+ const at = argv.indexOf('--root');
98
+ const root = at >= 0 ? join(cwd, argv.splice(at, 2)[1] ?? '') : undefined;
99
+ const cssAt = argv.indexOf('--css'), css = cssAt >= 0 ? argv.splice(cssAt, 2)[1] : undefined;
100
+ const json = flag('--json'), html = flag('--html'), all = flag('--all');
101
+ if (cmd === 'mcp') return import('../lib/mcp.mjs').then((m) => m.serve({ root }));
102
+ const cat = catalog.load({ root });
103
+ const q = argv.join(' ');
104
+ const result = cmd === 'list' ? catalog.list(cat) : cmd === 'get' ? catalog.get(cat, q, { html, all }) : cmd === 'find' ? catalog.find(cat, q) : catalog.scaffold(cat, q || undefined, { format: html ? 'html' : 'tsx', css });
105
+ if (result.error) { console.error(`cardds ${cmd}: ${result.error}${result.closest ? ` — closest: ${result.closest.join(', ')}` : ''}`); return 1; }
106
+ console.log(json ? JSON.stringify(result) : asText[cmd](result, { html }));
107
+ return 0;
108
+ }
109
+
110
+ const code = cmd === 'check' ? check(args) : cmd === 'init' ? init() : ['list', 'get', 'find', 'scaffold', 'mcp'].includes(cmd) ? await ask(cmd, args)
111
+ : (console.log('usage: cardds init | check [dir…] | list | get <Name> [--html] | find <words…> | scaffold [id] [--html [--css <href>]] | mcp (list get find scaffold mcp: --root <path> --json)'), cmd ? 2 : 0);
112
+ process.exitCode = code; // not process.exit: a big answer piped to a reader is cut off by an exit before it is flushed
package/css/layover.css CHANGED
@@ -127,6 +127,7 @@
127
127
  --menu-pad: var(--sp-2); /* published: a row's radius is concentric with the panel's */
128
128
  padding: var(--menu-pad);
129
129
  min-width: 12rem;
130
+ align-content: start; /* rows keep their own height: WebKit stretches a popover grid's rows to the whole room under its anchor (iOS 26.5, 2026-10-08) */
130
131
  background: var(--menu-bg, var(--action-bg));
131
132
  color: var(--menu-ink, var(--action-ink));
132
133
  box-shadow: var(--menu-shadow, var(--shadow-float));
package/dist/cardds.css CHANGED
@@ -4823,6 +4823,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4823
4823
  --menu-pad: var(--sp-2); /* published: a row's radius is concentric with the panel's */
4824
4824
  padding: var(--menu-pad);
4825
4825
  min-width: 12rem;
4826
+ align-content: start; /* rows keep their own height: WebKit stretches a popover grid's rows to the whole room under its anchor (iOS 26.5, 2026-10-08) */
4826
4827
  background: var(--menu-bg, var(--action-bg));
4827
4828
  color: var(--menu-ink, var(--action-ink));
4828
4829
  box-shadow: var(--menu-shadow, var(--shadow-float));