@usableapp/cardds 0.7.72 → 0.7.73
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 +17 -0
- package/README.md +5 -3
- package/bin/cardds.mjs +24 -8
- package/css/people.css +6 -0
- package/css/stamp.css +4 -0
- package/dist/actions/Chip.d.ts +7 -0
- package/dist/actions/Dropdown.d.ts +1 -0
- package/dist/actions/Fab.d.ts +3 -0
- package/dist/actions/IconBtnGroup.d.ts +1 -0
- package/dist/cardds.css +10 -0
- package/dist/cards/Card.d.ts +2 -0
- package/dist/cards/CardHead.d.ts +5 -0
- package/dist/cards/CardHead.js +5 -5
- package/dist/catalog.json +1 -1
- package/dist/choice/Calendar.d.ts +4 -2
- package/dist/choice/DayStrip.d.ts +1 -0
- package/dist/choice/Mood.d.ts +1 -0
- package/dist/choice/Slider.d.ts +2 -0
- package/dist/forms/Composer.d.ts +2 -0
- package/dist/forms/Field.d.ts +2 -0
- package/dist/forms/FileBtn.d.ts +1 -0
- package/dist/journey/NoteRow.d.ts +1 -0
- package/dist/journey/Route.d.ts +2 -0
- package/dist/layover/Callout.d.ts +4 -1
- package/dist/lists/Kv.d.ts +3 -0
- package/dist/lists/Row.d.ts +3 -1
- package/dist/media/Mosaic.d.ts +2 -0
- package/dist/media/Postcard.d.ts +4 -0
- package/dist/media/Tile.d.ts +6 -0
- package/dist/numbers/Bars.d.ts +1 -0
- package/dist/numbers/DotGrid.d.ts +1 -0
- package/dist/numbers/Picker.d.ts +1 -0
- package/dist/numbers/Track.d.ts +1 -0
- package/dist/people/Avatar.d.ts +7 -2
- package/dist/people/Avatar.js +4 -4
- package/dist/people/AvatarPick.d.ts +1 -0
- package/dist/post/PostLetter.d.ts +2 -1
- package/dist/scaffold/LayerHistory.d.ts +1 -0
- package/dist/scaffold/Screen.d.ts +1 -0
- package/dist/sheets/StackSheet.d.ts +2 -0
- package/dist/stamp/StampBook.d.ts +3 -1
- package/dist/stamp/StampBook.js +2 -2
- package/dist/templates/CallSheet.d.ts +2 -0
- package/dist/templates/Home.d.ts +2 -0
- package/dist/templates/Walkthrough.d.ts +5 -1
- package/lib/catalog.mjs +84 -21
- package/lib/mcp.mjs +23 -7
- package/lib/role.mjs +30 -0
- package/package.json +1 -1
package/lib/mcp.mjs
CHANGED
|
@@ -13,7 +13,7 @@ const bool = (description) => ({ type: 'boolean', description });
|
|
|
13
13
|
const TOOLS = [
|
|
14
14
|
{
|
|
15
15
|
name: 'list_components',
|
|
16
|
-
description: 'The names of every cardds component and the group it
|
|
16
|
+
description: 'The names of every cardds component and what each is FOR, nothing else. represents is what it stands for (person, group, time…) or, for a control that stands for no one thing, the KIND of data it carries (words, number, picture, signal); the word in parentheses after a name in the text is its represents. Every component shows information; shape says how much: "one" (one thing), "group-brief" (a group read at a glance or in a line), "group-detail" (a group read in detail), "—" (a helper function or structure, not something drawn). interact says whether the member can do something with it: false (it only shows or holds), or the kinds, any of pick, act, write, navigate — alone when shape is one (a Btn acts), as a group when shape is a group (a Segment picks among several). Two kinds mean it does both. group is the src folder, not what it is for. Call this once, first, in a session that will build a UI with cardds, to learn what exists; then get_component for the ones you use.',
|
|
17
17
|
inputSchema: { type: 'object', properties: {} },
|
|
18
18
|
run: (cat) => catalog.list(cat),
|
|
19
19
|
},
|
|
@@ -25,15 +25,26 @@ const TOOLS = [
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
name: 'find_components',
|
|
28
|
-
description:
|
|
29
|
-
inputSchema: {
|
|
30
|
-
|
|
28
|
+
description: (cat) => `Find what to build with when you do not know the component name. Ask BOTH ways and read both answers: by the AXES (translated from the request) AND by the English noun of the task ("stamp", "otp", "postcard") — the screen already drawn is often found by the noun. The axes are a closed vocabulary, so ask by them in any language: represents (what it stands for, or for a control the KIND of data it carries${cat ? `: ${catalog.vocabulary(cat).represents.join(' · ')}` : ' — the word in parentheses after a name in list_components'}), shape (one · group-brief · group-detail · group = either group shape), interact (the kinds the member can do with it: ${catalog.KINDS.join(' · ')}, or "none" = it only shows). "Show all the members" is represents [person] + shape group; adding interact [pick] narrows it to the ones the member chooses from. Whatever matches every axis you give comes first, each with its role so you see why; with axes and no words they are the whole answer. Words ("stamp", "otp pin", "bottom sheet") rank what matches only by words, in ENGLISH — the names and descriptions are English, translate first. Answers short: the screens cardds has already drawn (a screen is a composition: it counts when its parts answer the axes between them, one component per axis, and one that answers them all ranks higher), then components, then the components most often used together with them. Call it at the start of a task, before composing a screen from parts: if a screen like it exists, scaffold_screen gives its code. A query that matches nothing returns fallback: true, a hint and the number of screens drawn, not their ids: ask again in English words or by the axes, or call scaffold_screen with no id to list them.`,
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
represents: { type: 'array', items: { type: 'string' }, description: 'what it stands for, e.g. ["person"]; a component must hold every value you give' },
|
|
33
|
+
shape: { type: 'string', enum: ['one', 'group-brief', 'group-detail', 'group'], description: 'how many it shows and how much reading; group = group-brief or group-detail' },
|
|
34
|
+
interact: { anyOf: [{ type: 'array', items: { type: 'string' }, description: 'kinds, e.g. ["pick"]; the component must have every one' }, { type: 'string', enum: ['none'], description: 'only shows, nothing to do with it' }] },
|
|
35
|
+
query: str('English words describing the screen or the part; optional when axes are given'),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
run: (cat, a) => catalog.find(cat, a.query ?? '', { represents: a.represents, shape: a.shape, interact: a.interact === 'none' ? false : a.interact }),
|
|
31
39
|
},
|
|
32
40
|
{
|
|
33
41
|
name: 'scaffold_screen',
|
|
34
|
-
description: 'Start from a screen cardds has already drawn. With no id: the list of such screens, one line each. With an id (from find_components): a complete file to start from — format tsx (default) is the React code with its imports, edit its content for the task; format html is a static document (the look only, no behaviour) that links cardds.css, for a prototype without React. A tsx file may name story glue (from ./_story) that is not in the package: replace or drop it.',
|
|
42
|
+
description: 'Start from a screen cardds has already drawn. With no id: the list of such screens, one line each. With an id (from find_components): a complete file to start from — format tsx (default) is the React code with its imports, edit its content for the task; format html is a static document (the look only, no behaviour) that links cardds.css, for a prototype without React — when the screen has a sheet, menu, step or the like, the file begins with a comment saying the static picture is not the layout. A tsx file may name story glue (from ./_story) that is not in the package: replace or drop it.',
|
|
35
43
|
inputSchema: { type: 'object', properties: { id: str('a screen id such as StampScreens/MakeStamp; omit to list the screens'), format: { type: 'string', enum: ['tsx', 'html'], description: 'tsx (default) or html' }, css: str('html only: the href of cardds.css from where the file will sit; default node_modules/@usableapp/cardds/dist/cardds.css') } },
|
|
36
|
-
run: (cat, a) =>
|
|
44
|
+
run: (cat, a) => {
|
|
45
|
+
const r = catalog.scaffold(cat, a.id, { format: a.format, css: a.css });
|
|
46
|
+
return typeof r.html === 'string' ? `${r.warning ? `<!-- ${r.warning} -->\n` : ''}${r.html}` : r; // the warning first, where a caller reading the top of the file cannot miss it
|
|
47
|
+
},
|
|
37
48
|
},
|
|
38
49
|
{
|
|
39
50
|
name: 'check_source',
|
|
@@ -51,6 +62,11 @@ export function serve({ root, input = process.stdin, output = process.stdout } =
|
|
|
51
62
|
let cat;
|
|
52
63
|
const text = (r) => typeof r === 'string' ? r : JSON.stringify(r);
|
|
53
64
|
|
|
65
|
+
// the catalog, or nothing: listing the tools must not fail because it cannot be loaded (a call says why)
|
|
66
|
+
function loaded() {
|
|
67
|
+
try { return (cat ??= catalog.load({ root })); } catch { return undefined; }
|
|
68
|
+
}
|
|
69
|
+
|
|
54
70
|
function call(params) {
|
|
55
71
|
const tool = TOOLS.find((t) => t.name === params?.name);
|
|
56
72
|
if (!tool) return null;
|
|
@@ -69,7 +85,7 @@ export function serve({ root, input = process.stdin, output = process.stdout } =
|
|
|
69
85
|
switch (method) {
|
|
70
86
|
case 'initialize': return reply(id, { protocolVersion: params?.protocolVersion ?? '2025-06-18', capabilities: { tools: {} }, serverInfo: { name: 'cardds', version } });
|
|
71
87
|
case 'ping': return reply(id, {});
|
|
72
|
-
case 'tools/list': return reply(id, { tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })) });
|
|
88
|
+
case 'tools/list': return reply(id, { tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description: typeof description === 'function' ? description(loaded()) : description, inputSchema })) });
|
|
73
89
|
case 'tools/call': { const r = call(params); return r ? reply(id, r) : fail(id, -32602, `unknown tool: ${params?.name}`); }
|
|
74
90
|
default: return fail(id, -32601, `method not found: ${method}`);
|
|
75
91
|
}
|
package/lib/role.mjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// lib/role.mjs — the role table (docs/roles.md) read, and what a role says a component is FOR. Pure, no fs: the catalog (node), scripts/roles.mjs
|
|
2
|
+
// and the gallery's nav (browser) all read the table through these.
|
|
3
|
+
const AXES = ['represents', 'count', 'density', 'job'];
|
|
4
|
+
const values = (cell) => cell.trim().split(' · ').map((v) => v.trim()).filter(Boolean);
|
|
5
|
+
|
|
6
|
+
/** the text of docs/roles.md → `vocabulary` (each axis's closed values) and `rows` (one per component). Hand-written; the vocabulary is the
|
|
7
|
+
* table's own definition lines, so a value outside it is the test's to catch, not this reader's. */
|
|
8
|
+
export function parseRoles(doc) {
|
|
9
|
+
const vocabulary = Object.fromEntries([...doc.matchAll(/^- `([a-z]+)`: (.+)$/gm)].filter((m) => AXES.includes(m[1])).map((m) => [m[1], values(m[2])]));
|
|
10
|
+
const rows = [...doc.matchAll(/^\| (?!component )([A-Za-z]\w*) \| ([a-z]+) \|(.*)\|$/gm)].map((m) => {
|
|
11
|
+
const [represents, count, density, job, stands, note] = m[3].split('|').map((c) => c.trim());
|
|
12
|
+
return {
|
|
13
|
+
component: m[1],
|
|
14
|
+
group: m[2],
|
|
15
|
+
represents: values(represents), count: values(count), density: values(density), job: values(job),
|
|
16
|
+
standsInFor: stands ? stands.split(';').map((s) => s.trim().match(/^(\w+)(?: \((.*)\))?$/)).map((x) => ({ name: x[1], condition: x[2] ?? '' })) : [],
|
|
17
|
+
note: note.replace(/^\?\s*/, ''),
|
|
18
|
+
unsure: note.startsWith('?'),
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
return { vocabulary, rows };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// What a component is FOR, read from its role: every component shows information; what differs is how much it shows
|
|
25
|
+
// (`one` thing, a group in brief, a group in detail) and whether the member can do something with it (and what: pick, act, write, navigate).
|
|
26
|
+
export const SHAPES = ['one', 'group-brief', 'group-detail', '—'];
|
|
27
|
+
export const KINDS = ['pick', 'act', 'write', 'navigate'];
|
|
28
|
+
export const shapeOf = ({ count: [count], density: [density] }) =>
|
|
29
|
+
count === 'one' ? 'one' : count === 'few' || count === 'many' ? (density === 'detail' || density === 'full' ? 'group-detail' : 'group-brief') : '—'; // a holder with no density of its own claims no detail
|
|
30
|
+
export const interactOf = ({ job }) => job.some((j) => KINDS.includes(j)) ? job.filter((j) => KINDS.includes(j)) : false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usableapp/cardds",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.73",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "card-first mobile design system, React-first: the components in src/ are thin wrappers over the CSS contract (css/*.css stays the only truth); gallery/ shows every story live (npm run dev), tests/ measures the geometry.",
|