makersclaw 0.7.0 → 0.8.0

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.
Files changed (85) hide show
  1. package/README.md +8 -1
  2. package/package.json +1 -1
  3. package/r/annotate.json +4 -4
  4. package/r/approval-request.json +2 -2
  5. package/r/audio-player.json +2 -2
  6. package/r/auth-form.json +2 -2
  7. package/r/board.json +2 -2
  8. package/r/calendar-view.json +3 -3
  9. package/r/cart.json +2 -2
  10. package/r/chart.json +5 -5
  11. package/r/chat.json +3 -3
  12. package/r/code-editor.json +2 -2
  13. package/r/comment-box.json +3 -3
  14. package/r/contact-section.json +2 -2
  15. package/r/core.json +7 -1
  16. package/r/csv-import.json +3 -3
  17. package/r/data-table.json +2 -2
  18. package/r/diagram.json +2 -2
  19. package/r/doc-editor.json +2 -2
  20. package/r/doc-page.json +3 -3
  21. package/r/email-render.json +2 -2
  22. package/r/faq.json +2 -2
  23. package/r/file-upload.json +2 -2
  24. package/r/flow-canvas.json +3 -3
  25. package/r/footer.json +3 -3
  26. package/r/gantt.json +3 -3
  27. package/r/job-panel.json +2 -2
  28. package/r/json-editor.json +3 -3
  29. package/r/map.json +2 -2
  30. package/r/menubar.json +2 -2
  31. package/r/mind-map.json +4 -4
  32. package/r/navigation-menu.json +2 -2
  33. package/r/order-detail.json +2 -2
  34. package/r/org-chart.json +4 -4
  35. package/r/page-builder.json +3 -3
  36. package/r/payment-methods.json +2 -2
  37. package/r/pdf-document.json +2 -2
  38. package/r/pdf-viewer.json +3 -3
  39. package/r/product-detail.json +2 -2
  40. package/r/product-grid.json +3 -3
  41. package/r/record-form.json +2 -2
  42. package/r/record-notes.json +4 -4
  43. package/r/registry.json +126 -121
  44. package/r/search-dialog.json +2 -2
  45. package/r/settings-list.json +2 -2
  46. package/r/setup-checklist.json +2 -2
  47. package/r/share-dialog.json +2 -2
  48. package/r/sidebar.json +3 -3
  49. package/r/signature-pad.json +2 -2
  50. package/r/sketch-canvas.json +3 -3
  51. package/r/spreadsheet.json +2 -2
  52. package/r/stat-cards.json +3 -3
  53. package/r/task-list.json +3 -3
  54. package/r/task-model.json +2 -2
  55. package/r/testimonials.json +2 -2
  56. package/r/transaction-list.json +2 -2
  57. package/r/whiteboard.json +4 -4
  58. package/registry/blocks/annotate/README.md +14 -11
  59. package/registry/blocks/chart/cartesian.jsx +1 -1
  60. package/registry/blocks/chart/core.jsx +33 -23
  61. package/registry/blocks/chart/pie.jsx +1 -1
  62. package/registry/blocks/chat/README.md +4 -1
  63. package/registry/blocks/chat/demo.jsx +2 -2
  64. package/registry/blocks/flow-canvas/README.md +15 -8
  65. package/registry/blocks/footer/README.md +3 -2
  66. package/registry/blocks/mind-map/README.md +4 -2
  67. package/registry/blocks/org-chart/README.md +4 -2
  68. package/registry/blocks/page-builder/README.md +1 -1
  69. package/registry/blocks/page-builder/demo.jsx +3 -3
  70. package/registry/blocks/pdf-viewer/README.md +4 -1
  71. package/registry/blocks/sidebar/README.md +23 -13
  72. package/registry/blocks/sketch-canvas/README.md +15 -9
  73. package/registry/blocks/whiteboard/README.md +20 -14
  74. package/src/cli.mjs +2 -2
  75. package/src/install.mjs +23 -6
  76. package/src/registry.mjs +34 -3
  77. package/templates/next/files/README.md +291 -11
  78. package/templates/next/files/app/shell.jsx +54 -37
  79. package/templates/next/files/check/run.mjs +76 -4
  80. package/templates/next/files/components/ui/index.js +1 -0
  81. package/templates/next/files/components/ui/layout.jsx +307 -0
  82. package/templates/next/files/lib/claw.js +4 -1
  83. package/templates/next/files/lib/hooks.js +71 -2
  84. package/templates/next/files/lib/platform.js +93 -4
  85. package/templates/next/template.json +10 -1
package/r/footer.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Footer",
6
6
  "description": "A public site's footer: brand and tagline, link columns or one row of links, social buttons in the kit's brand marks (X, LinkedIn, GitHub, YouTube, Instagram, Facebook), and the legal line with its links; optionally a newsletter sign-up with consent (validates, saves through an async onSubscribe returning { ok }, shows the failure), a call to action, a large brand statement, language and currency selects, and a version badge. Simple, columns or centred.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -25,7 +25,7 @@
25
25
  "path": "registry/blocks/footer/README.md",
26
26
  "type": "registry:file",
27
27
  "target": "@components/makersclaw/blocks/footer/README.md",
28
- "content": "# Footer\n\nA public site's footer. It holds the brand and a line about it, link\ncolumns (or one row of links), social buttons drawn with the kit's brand\nmarks, and the legal line with its links. Optional parts: a newsletter\nsign-up (with a consent box), a call to action, a large brand statement\nor quote, language and currency selects, and a version badge.\n\nThree layouts:\n- **columns** (default) — the brand, tagline and socials beside the link\n columns (and an inline sign-up), then the legal row;\n- **simple** — one row: brand, links, socials; then the legal row;\n- **centred** — everything stacked in the middle.\n\nThe sign-up checks the email (and the consent, when asked) in place,\nthen calls `newsletter.onSubscribe(email, { consent })`, which is async\nand returns `{ ok }`. While it runs the button spins. `{ ok: false,\nerror }` (or a throw) shows the error and keeps the email. `{ ok: true }`\nshows \"You're subscribed\". It is a full-width section: it pads itself\nand centres its content.\n\n```jsx\nimport { Footer } from \"../components/blocks/footer\"; // a Makersclaw app (\"./blocks/footer\" from components/)\nimport { Footer } from \"@/components/makersclaw/blocks/footer\"; // a shadcn project\n```\n\nIt is a client component: render it from a `\"use client\"` component (a\nlayout is the usual place).\n\n## The data\n\n```js\n// a link, anywhere: columns, links, legalLinks\n{ label: \"Pricing\", href: \"/pricing\", external?: true } // external opens a new tab\n\n// columns\n[{ title: \"Product\", links: [link, …] }, …] // up to 5 across; 2 on a phone\n\n// socials\n[{ network: \"x\", href: \"https://x.com/fieldline\", label? }, …]\n```\n\n`network` is one of `x`, `linkedin`, `github`, `youtube`, `instagram`,\n`facebook`, `google`, `apple`, `microsoft`, `email`, `rss`. Anything\nelse needs an `icon` (a kit Icon name) and a `label`. Each is an icon\nbutton, named to a screen reader (\"X\", \"LinkedIn\"), opening a new tab.\n\n## Props\n\n- `layout` — `columns` (default) · `simple` · `centered`.\n- `brand` — a node: your logo, linked home if you like.\n- `tagline` — a line under the brand (columns and centred).\n- `columns` — link columns (columns layout).\n- `links` — one row of links (simple and centred; under the socials in\n columns).\n- `socials` — as above.\n- `newsletter` — `{ onSubscribe, title?, body?, label?, placeholder?,\n consent?, look?, doneTitle?, doneBody? }`. `onSubscribe(email, {\n consent })` is async and returns `{ ok: true }` or `{ ok: false,\n error? }`; without it there is no sign-up. `consent` is the checkbox's\n label (a string or a node with a link), and must be ticked. `look`:\n `inline` (default: a column beside the links) · `card` (a full-width\n card under them). `label` is the button, default \"Subscribe\".\n- `cta` — `{ title?, body?, actions: [{ label, href?, onClick?, icon?,\n variant? }] }`: \"Sign in\", \"Get the app\". A band above the legal row;\n the first action is primary unless it says otherwise.\n- `statement` — a large line at the top: a string, or `{ text, by }` for\n a quote.\n- `locales` — `[{ id, label, options: [{ value, label }], value?,\n defaultValue?, onChange? }]`: language, currency, country selects in\n the legal row. Uncontrolled unless you pass `value`.\n- `version` — \"v4.2.0\": a code badge in the legal row.\n- `legal` — the copyright line: \"© 2026 Fieldline Ltd.\". Pass the year;\n the block does not read the clock.\n- `legalLinks` — privacy, terms, cookies.\n- `onNavigate(link, event)` — called when any link or a `cta` action with\n an `href` is followed, before navigation; `event.preventDefault()`\n stops it.\n- `label` — what a screen reader calls the footer and its link row.\n Default \"Site\".\n- `className` — the footer's spacing. Never colour.\n\n## Example\n\n```jsx\n// components/SiteFooter.jsx\n\"use client\";\nimport { Icon } from \"./ui\";\nimport { Footer } from \"./blocks/footer\";\nimport { insertRow } from \"../lib/actions\";\n\nexport default function SiteFooter() {\n return (\n <Footer\n brand={<span className=\"inline-flex items-center gap-2\"><Icon name=\"zap\" /> Fieldline</span>}\n tagline=\"The pipeline small sales teams actually keep up to date.\"\n columns={[\n { title: \"Product\", links: [{ label: \"Pricing\", href: \"/pricing\" }, { label: \"Changelog\", href: \"/changelog\" }] },\n { title: \"Company\", links: [{ label: \"About\", href: \"/about\" }, { label: \"Careers\", href: \"/careers\" }] },\n ]}\n socials={[{ network: \"x\", href: \"https://x.com/fieldline\" }, { network: \"linkedin\", href: \"https://linkedin.com/company/fieldline\" }]}\n newsletter={{\n body: \"One email a month.\",\n consent: \"I agree to the privacy policy.\",\n onSubscribe: (email) => insertRow({ source: \"subscribers\", values: { email } }), // { ok, id } or { ok: false, error }\n }}\n legal=\"© 2026 Fieldline Ltd. All rights reserved.\"\n legalLinks={[{ label: \"Privacy\", href: \"/privacy\" }, { label: \"Terms\", href: \"/terms\" }]}\n />\n );\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\n## What it will not do\n\n- Change the language or currency by itself: `locales[].onChange` is\n yours.\n- Stick to the bottom of a short page: that is your layout's\n `min-h-dvh` flex column.\n- Paint itself in the primary colour: the look is the workspace\n theme's.\n- Draw store badges: \"Get the app\" is two buttons with the Apple and\n Google marks.\n\nA footer it cannot be is your own component in `components/`, built\nfrom the kit — not an edit to this block. Its files are fixed: `npm run\ncheck` refuses a change, and `makersclaw add footer` restores them.\n\n## Credits\n\nLayouts ported from Creative Tim UI's footer blocks\n(https://github.com/creativetimofficial/ui, MIT), rewritten on the\nMakersclaw kit.\n"
28
+ "content": "# Footer\n\nA public site's footer. It holds the brand and a line about it, link\ncolumns (or one row of links), social buttons drawn with the kit's brand\nmarks, and the legal line with its links. Optional parts: a newsletter\nsign-up (with a consent box), a call to action, a large brand statement\nor quote, language and currency selects, and a version badge.\n\nThree layouts:\n- **columns** (default) — the brand, tagline and socials beside the link\n columns (and an inline sign-up), then the legal row;\n- **simple** — one row: brand, links, socials; then the legal row;\n- **centred** — everything stacked in the middle.\n\nThe sign-up checks the email (and the consent, when asked) in place,\nthen calls `newsletter.onSubscribe(email, { consent })`, which is async\nand returns `{ ok }`. While it runs the button spins. `{ ok: false,\nerror }` (or a throw) shows the error and keeps the email. `{ ok: true }`\nshows \"You're subscribed\". It is a full-width section: it pads itself\nand centres its content.\n\n```jsx\nimport { Footer } from \"../components/blocks/footer\"; // a Makersclaw app (\"./blocks/footer\" from components/)\nimport { Footer } from \"@/components/makersclaw/blocks/footer\"; // a shadcn project\n```\n\nIt is a client component: render it from a `\"use client\"` component (a\nlayout is the usual place).\n\n## The data\n\n```js\n// a link, anywhere: columns, links, legalLinks\n{ label: \"Pricing\", href: \"/pricing\", external?: true } // external opens a new tab\n\n// columns\n[{ title: \"Product\", links: [link, …] }, …] // up to 5 across; 2 on a phone\n\n// socials\n[{ network: \"x\", href: \"https://x.com/fieldline\", label? }, …]\n```\n\n`network` is one of `x`, `linkedin`, `github`, `youtube`, `instagram`,\n`facebook`, `google`, `apple`, `microsoft`, `email`, `rss`. Anything\nelse needs an `icon` (a kit Icon name) and a `label`. Each is an icon\nbutton, named to a screen reader (\"X\", \"LinkedIn\"), opening a new tab.\n\n## Props\n\n- `layout` — `columns` (default) · `simple` · `centered`.\n- `brand` — a node: your logo, linked home if you like.\n- `tagline` — a line under the brand (columns and centred).\n- `columns` — link columns (columns layout).\n- `links` — one row of links (simple and centred; under the socials in\n columns).\n- `socials` — as above.\n- `newsletter` — `{ onSubscribe, title?, body?, label?, placeholder?,\n consent?, look?, doneTitle?, doneBody? }`. `onSubscribe(email, {\n consent })` is async and returns `{ ok: true }` or `{ ok: false,\n error? }`; without it there is no sign-up. `consent` is the checkbox's\n label (a string or a node with a link), and must be ticked. `look`:\n `inline` (default: a column beside the links) · `card` (a full-width\n card under them). `label` is the button, default \"Subscribe\".\n- `cta` — `{ title?, body?, actions: [{ label, href?, onClick?, icon?,\n variant? }] }`: \"Sign in\", \"Get the app\". A band above the legal row;\n the first action is primary unless it says otherwise.\n- `statement` — a large line at the top: a string, or `{ text, by }` for\n a quote.\n- `locales` — `[{ id, label, options: [{ value, label }], value?,\n defaultValue?, onChange? }]`: language, currency, country selects in\n the legal row. Uncontrolled unless you pass `value`.\n- `version` — \"v4.2.0\": a code badge in the legal row.\n- `legal` — the copyright line: \"© 2026 Fieldline Ltd.\". Pass the year;\n the block does not read the clock.\n- `legalLinks` — privacy, terms, cookies.\n- `onNavigate(link, event)` — called when any link or a `cta` action with\n an `href` is followed, before navigation; `event.preventDefault()`\n stops it.\n- `label` — what a screen reader calls the footer and its link row.\n Default \"Site\".\n- `className` — the footer's spacing. Never colour.\n\n## Example\n\n```jsx\n// components/SiteFooter.jsx\n\"use client\";\nimport { Icon } from \"./ui\";\nimport { Footer } from \"./blocks/footer\";\nimport { insertRow } from \"../lib/actions\";\n\nexport default function SiteFooter() {\n return (\n <Footer\n brand={<span className=\"inline-flex items-center gap-2\"><Icon name=\"zap\" /> Fieldline</span>}\n tagline=\"The pipeline small sales teams actually keep up to date.\"\n columns={[\n { title: \"Product\", links: [{ label: \"Pricing\", href: \"/pricing\" }, { label: \"Changelog\", href: \"/changelog\" }] },\n { title: \"Company\", links: [{ label: \"About\", href: \"/about\" }, { label: \"Careers\", href: \"/careers\" }] },\n ]}\n socials={[{ network: \"x\", href: \"https://x.com/fieldline\" }, { network: \"linkedin\", href: \"https://linkedin.com/company/fieldline\" }]}\n newsletter={{\n body: \"One email a month.\",\n consent: \"I agree to the privacy policy.\",\n onSubscribe: (email) => insertRow({ source: \"subscribers\", values: { email } }), // { ok, id } or { ok: false, error }\n }}\n legal=\"© 2026 Fieldline Ltd. All rights reserved.\"\n legalLinks={[{ label: \"Privacy\", href: \"/privacy\" }, { label: \"Terms\", href: \"/terms\" }]}\n />\n );\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\n## What it will not do\n\n- Change the language or currency by itself: `locales[].onChange` is\n yours.\n- Stick to the bottom of a short page: that is your layout's. The kit's\n `AppLayout` content is a flex column, so a `flex-1` above the footer\n pushes it down on a short page and lets it follow a long one.\n- Paint itself in the primary colour: the look is the workspace\n theme's.\n- Draw store badges: \"Get the app\" is two buttons with the Apple and\n Google marks.\n\nA footer it cannot be is your own component in `components/`, built\nfrom the kit — not an edit to this block. Its files are fixed: `npm run\ncheck` refuses a change, and `makersclaw add footer` restores them.\n\n## Credits\n\nLayouts ported from Creative Tim UI's footer blocks\n(https://github.com/creativetimofficial/ui, MIT), rewritten on the\nMakersclaw kit.\n"
29
29
  }
30
30
  ],
31
31
  "categories": [
@@ -45,7 +45,7 @@
45
45
  "docs": "Installed to components/makersclaw/blocks/footer. Import: import { Footer } from \"@/components/makersclaw/blocks/footer\". Props and an example: components/makersclaw/blocks/footer/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
46
46
  "meta": {
47
47
  "makersclaw": {
48
- "version": "0.7.0",
48
+ "version": "0.8.0",
49
49
  "useWhen": "The bottom of every page of a public site: a marketing site, a docs hub, a shop, a customer portal. Not for an app's own status bar or a toolbar pinned to the bottom of a screen."
50
50
  }
51
51
  }
package/r/gantt.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "title": "Gantt",
6
6
  "description": "The Gantt view of work, like Linear's timeline or TeamGantt: a task list on the left (groups that fold, subtasks indented under a summary bar) beside a timeline with a two-tier date header, bars coloured by project or status with their progress, milestones as diamonds, dependency arrows (red when the dates disagree), a today line and weekend shading. Drag a bar to move it, an edge to change its start or end, the dot past its end onto another bar to link them; tasks waiting on a moved one slide along while you drag and the finish date shows what the move cost. Zoom day, week, month or quarter; highlight the critical path; click an empty row to schedule an undated task; quick-add per group; arrow keys move a focused bar; search, filters, group and sort from the shared toolbar. One async onChange saves every change; failures roll back with a notice.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/task-model.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/task-model.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -63,7 +63,7 @@
63
63
  "docs": "Installed to components/makersclaw/blocks/gantt. Import: import { Gantt, ZOOMS, ganttRange, headerTiers, planMove, afterBlockersPatch, isMilestone, summarySpan, arrowPath } from \"@/components/makersclaw/blocks/gantt\". Props and an example: components/makersclaw/blocks/gantt/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
64
64
  "meta": {
65
65
  "makersclaw": {
66
- "version": "0.7.0",
66
+ "version": "0.8.0",
67
67
  "useWhen": "A screen that plans dated work and shows what waits on what: a launch plan, a project schedule, a content calendar with lead times, an office move, a release. Not for an undated to-do list (task-list), stage columns (board), or days on a month grid (calendar)."
68
68
  }
69
69
  }
package/r/job-panel.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "JobPanel",
6
6
  "description": "The panel that follows work the founder asked the app's agent for (useJob): each step's label as it runs, Stop, \"Needs your answer\" with Answer while the agent waits on the founder, \"Queued · after …\" while it waits its turn, then done or the reason it stopped — and nothing on screen when no job runs. variant=\"generator\" wraps it in a prompt, the surface's options, Generate and a results shelf, with no model, provider or API key of its own.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -52,7 +52,7 @@
52
52
  "docs": "Installed to components/makersclaw/blocks/job-panel. Import: import { JobPanel } from \"@/components/makersclaw/blocks/job-panel\". Props and an example: components/makersclaw/blocks/job-panel/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
53
53
  "meta": {
54
54
  "makersclaw": {
55
- "version": "0.7.0",
55
+ "version": "0.8.0",
56
56
  "useWhen": "A surface has a verb that asks the agent to make or find something (\"Generate a launch plan\", \"Find 20 leads\", \"Plan next sprint\") and the founder should watch it happen where the result will land. variant=\"generator\" when the screen IS the place to ask: a prompt and a few options, the results under them (product photos, ad copy, names)."
57
57
  }
58
58
  }
@@ -5,8 +5,8 @@
5
5
  "title": "JsonEditor",
6
6
  "description": "A JSON editor in the workspace theme with two views of one value: a Tree of folding rows (edit a value or rename a key in place, add keys and items, change a type, copy a path, delete with Undo, arrow keys throughout) and Code (the code-editor block in JSON mode). Checked as it changes: the status line says what is wrong in plain words and which line, and clicking it goes there. A Format button, copy as pretty or one-line JSON, a friendly empty state with starters, and an optional Save that is optimistic and rolls back with a notice when it fails.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/code-editor.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/code-editor.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -59,7 +59,7 @@
59
59
  "docs": "Installed to components/makersclaw/blocks/json-editor. Import: import { JsonEditor, JsonTree, typeOf, pathLabel, getAt, setAt, removeAt, insertAt, renameKey, flattenTree, expandedTo, summary, preview } from \"@/components/makersclaw/blocks/json-editor\". Props and an example: components/makersclaw/blocks/json-editor/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
60
60
  "meta": {
61
61
  "makersclaw": {
62
- "version": "0.7.0",
62
+ "version": "0.8.0",
63
63
  "useWhen": "A screen edits or inspects a JSON value: an integration's settings, a webhook payload, a record's metadata or custom fields column (jsonb), an API response, feature flags. Not for editing SQL or other code (code-editor), a form of known fields (record-form), or a table of rows (data-table)."
64
64
  }
65
65
  }
package/r/map.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "MapView",
6
6
  "description": "An interactive map (MapLibre) in the workspace theme: places from rows as markers in token tones or your own kit content, clustered when they crowd, a popup with kit parts, a searchable list beside the map that shares one selection and hover with it, fit-to-all, zoom controls, draggable markers with an optimistic save, and tinted areas for territories. Light and dark tiles follow the workspace scheme (OpenFreeMap by default, no key); `styleUrl` swaps them. Helpers for bounds, clustering and distances run anywhere.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "maplibre-gl@6.11.1",
@@ -82,7 +82,7 @@
82
82
  "docs": "Installed to components/makersclaw/blocks/map. Import: import { MapView, useMapView, MAP_STYLES, DEFAULT_STYLE, TONES, readPosition, boundsOf, inBounds, clusterPoints, zoomToReveal, distanceKm, formatDistance, sortByDistance } from \"@/components/makersclaw/blocks/map\". Props and an example: components/makersclaw/blocks/map/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
83
83
  "meta": {
84
84
  "makersclaw": {
85
- "version": "0.7.0",
85
+ "version": "0.8.0",
86
86
  "useWhen": "A screen shows records that have a place: customers, stores, offices, field visits, deliveries, events, territories. The location-map and store-locator blocks and the Map view of work-views are built on it. Not for a chart of numbers by region (chart) or a hand-drawn diagram (sketch-canvas, flow-canvas)."
87
87
  }
88
88
  }
package/r/menubar.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Menubar",
6
6
  "description": "A desktop app's menu bar — File, Edit, View, Help — each a menu of actions with shortcuts, checkboxes, radio groups and submenus; click to open, hover to switch, arrow keys along the bar; one menu button on a phone.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -45,7 +45,7 @@
45
45
  "docs": "Installed to components/makersclaw/blocks/menubar. Import: import { Menubar } from \"@/components/makersclaw/blocks/menubar\". Props and an example: components/makersclaw/blocks/menubar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
46
46
  "meta": {
47
47
  "makersclaw": {
48
- "version": "0.7.0",
48
+ "version": "0.8.0",
49
49
  "useWhen": "A document-style tool with many commands grouped by kind: an editor, a canvas, a spreadsheet, an admin console that mimics a desktop app. Not for a site's navigation (a navigation menu) or one record's actions (a menu on the row)."
50
50
  }
51
51
  }
package/r/mind-map.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "title": "MindMap",
6
6
  "description": "A mind map you can build without the mouse, on flow-canvas: a centre topic with main branches balanced on both sides, each branch a tidy auto-laid-out tree in its own chart hue. Tab adds a subtopic, Enter the next topic, typing renames, arrows walk the map, ⌘↑/⌘↓ fold a branch (a folded one shows how many it hides), ⌥↑/⌥↓ reorder, Shift+Tab moves up a level; drag a topic onto another to move its branch. Undo/redo, a colour per branch, an Outline view, copy a branch as an outline and paste an outline as topics, starter templates. The map is plain JSON (a tree of topics); saves are async callbacks answering { ok }.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/flow-canvas.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/flow-canvas.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -38,7 +38,7 @@
38
38
  "path": "registry/blocks/mind-map/README.md",
39
39
  "type": "registry:file",
40
40
  "target": "@components/makersclaw/blocks/mind-map/README.md",
41
- "content": "# MindMap\n\nA mind map you can build without touching the mouse. One centre topic,\nmain branches balanced on both sides of it, each branch a tidy tree in its\nown colour. **Tab** adds a subtopic, **Enter** the next topic, typing\nrenames, the arrows walk the map. The layout is automatic: topics never\nneed dragging into place, and the map re-balances as it grows.\n\nA bar on top holds undo and redo, the save state, the selected topic's\nactions (add, colour, fold, delete) and a Map / Outline switch. The\nOutline view is the map as a nested list — the reading of it for a screen\nreader, and a quick way to jump to a topic.\n\nIt is built on `flow-canvas` (which draws it, pans and zooms): this block\nowns the tree, the layout and the keys.\n\nA map opens fitted to its view, but never smaller than its words can be\nread: 79%, where its smallest words (14px) draw at the kit's smallest\ntext size (11px). A map too big for that — most maps on a phone — opens\nat 79% centred on its centre topic; ⇧1 still fits the whole of it.\n\n```jsx\nimport { MindMap } from \"../components/blocks/mind-map\"; // a Makersclaw app (\"./blocks/mind-map\" from components/)\nimport { MindMap } from \"@/components/makersclaw/blocks/mind-map\"; // a shadcn project\n```\n\n`MindMap` is a client component: render it from a `\"use client\"`\ncomponent. `makersclaw add mind-map` adds `flow-canvas` with it (and its\nnpm package, `@xyflow/react`: run `npm install` after). The tree helpers\n(`mindMapToText`, `outlineToMindMap`, `layoutMindMap`…) are plain\nfunctions and work on the server too.\n\n## The data\n\nA map is one tree of topics. Store it as it comes out of `onChange` (one\n`jsonb` column), and keep `mindMapToText(map)` beside it for search and\nagents.\n\n```js\n{\n id: \"root\", text: \"Relay 2.0 launch\",\n children: [\n { id: \"why\", text: \"Why now\", side: \"right\", children: [\n { id: \"why-1\", text: \"Churn up 3 pts\" },\n ] },\n { id: \"risks\", text: \"Risks\", side: \"left\", color: \"chart-4\", collapsed: true, children: [ … ] },\n ],\n}\n```\n\n- `id` — any string, unique in the map. `text` — the words (a newline is\n allowed). `children` — the subtopics, in order.\n- `collapsed: true` — the branch is folded; the map shows how many topics\n it hides.\n- `color` — a colour **token**, never a colour value: `\"chart-2\"` …\n `\"chart-5\"`, `\"chart-1\"` (the theme's own), `\"warning\"`, `\"success\"`,\n `\"info\"`, `\"destructive\"`. Subtopics inherit their branch's. A main\n branch with none takes the next of `colors`.\n- `side` — `\"left\"` or `\"right\"`, main branches only. Missing, the map\n splits the branches to balance the two sides; the first change pins\n them, so adding a branch never flips the others across the centre.\n- Anything else on a topic (`note`, your own keys) is kept as it is.\n\n`normalizeMap(anything)` cleans what it is given: missing or repeated ids,\n`title`/`label` for `text`, `topics`/`items` for `children`, an outline\nstring, or nothing (a fresh map). The component runs it on `value`.\n\n## Props\n\n- `value` — the map, controlled. A new `value` that is not what the map\n last sent through `onChange` replaces it (loading another record); that\n replacement can be undone, and when it is another map (a new centre, or\n other main branches) the view fits it, as it fits a first one. Pass\n back exactly what `onChange` gave you.\n- `defaultValue` — the starting map, uncontrolled. With neither, a centre\n topic called \"Central topic\" and the first-run card.\n- `onChange(map)` — the whole tree after a change settles (debounced by\n `changeDelay`; typing is saved as it goes). Selection, folding in\n read-only mode, panning and zooming never call it.\n- `onSave(map)` — `async`, returns `{ ok: true }` or `{ ok: false,\n error? }` (a throw counts as failed). Called right after `onChange`. The\n bar shows \"Saving…\", \"Saved\", or the error with Retry. A failure keeps\n the map as it is on screen (nothing typed is thrown away); Retry sends\n it again.\n- `changeDelay` — ms of quiet before `onChange` / `onSave` (default 400).\n- `readOnly` — `false` (default). `true`: no editing; the arrows still\n walk the map, Space folds and unfolds (not saved), the Outline view and\n copying work.\n- `colors` — the branch palette, token names (default `BRANCH_COLORS`:\n chart-2, 3, 4, 5, 1). The colour picker offers these plus warning,\n success, info and destructive.\n- `templates` — what the first-run card offers: `[{ id, label, icon?,\n description?, map }]`, `map` a tree or a function returning one (default\n `mindMapTemplates()`: Project plan, Brainstorm, Meeting notes). `[]` for\n none.\n- `placeholder` — what an empty topic shows while it is typed (default\n \"New topic\").\n- `height` — px or any CSS length, default `600`. A height class in\n `className` (`h-dvh`, `h-full`, `flex-1`) replaces it.\n- `label` — the map's accessible name (default \"Mind map\").\n- `onSelectionChange(topic | null)` — the selected topic changed (a side\n panel with its notes).\n- `className` — layout around it (margin, width, height). Never colour.\n\n## Keyboard\n\nWith a topic selected (click one, or press an arrow):\n\n| Key | Does |\n| --- | --- |\n| Tab | add a subtopic and type it |\n| Enter / Shift+Enter | add the next topic below / above |\n| Space, F2, or just type | rename |\n| Delete / Backspace | delete the topic and its branch (a toast offers Undo) |\n| ← → ↑ ↓ | walk the map: outwards to a child, inwards to the parent, up and down the column |\n| ⌥↑ / ⌥↓ | move the topic up or down among its siblings |\n| Shift+Tab | move the topic up a level |\n| ⌘↑ / ⌘↓ | fold / unfold the branch |\n| ⌘D | duplicate the branch |\n| ⌘C / ⌘X / ⌘V | copy or cut a branch as an outline; paste an outline (from notes, a doc, an agent) as subtopics |\n| ⌘Z / ⇧⌘Z | undo / redo |\n| Home | the centre |\n| Esc | clear the selection |\n| ⇧1 | fit the whole map to the screen, however small |\n| ? | the shortcuts sheet |\n\nWhile typing a topic: **Enter** finishes it and starts the next one (on\nan empty one it steps out a level, so a run of topics never needs the\nmouse), **Tab** finishes it and starts a subtopic (on an empty one it\nmoves in a level), **Backspace** on an empty topic removes it and goes\nback to the one before, **Esc** finishes. Shift+Enter is a new line.\n\nWith the mouse: click selects, double-click renames, the **+** at a\ntopic's end adds a subtopic, the chip at a branch's end folds it (folded,\nit shows the count it hides), and dragging a topic onto another moves its\nbranch there. A plain drag on the empty canvas pans; pinch or ⌘-scroll\nzooms. On a touch screen a drag always pans; tap selects, double-tap\nrenames, and the bar's buttons add.\n\n## The helpers\n\nPlain functions over a tree; each returns a new tree and leaves the old\none as it was.\n\n- Read: `findTopic(map, id)`, `pathTo(map, id)` → `[root, …, topic]`,\n `parentOf(map, id)`, `countTopics(topic)` (everything under it).\n- Change: `updateTopic(map, id, patch)`, `insertTopic(map, parentId,\n topic, index?)`, `insertSibling(map, id, topic, { before })`,\n `removeTopic(map, id)`, `moveTopic(map, id, parentId, index?)` (refused\n into its own branch), `shiftTopic(map, id, ±1)`, `indentTopic`,\n `outdentTopic`, `duplicateTopic(map, id)` → `{ root, id }`,\n `setAllCollapsed(map, on)`, `createTopic(text)`.\n- Text: `mindMapToText(map)` (an indented outline, for an agent or search),\n `mindMapToMarkdown(map)` (a heading and a nested list),\n `outlineToMindMap(text)` and `outlineToTopics(text)` (bullets, numbers,\n headings, tabs or spaces — what an agent writes).\n- Layout: `layoutMindMap(map, { measure?, colors? })` → `{ items, bounds }`\n (every visible topic's box, side, depth, colour and the line to its\n parent), `mindMapToBoard(map)` (the same as a flow-canvas board).\n- `mindMapTemplates()`, `BRANCH_COLORS`.\n\n## Example\n\n```jsx\n// surfaces/plan-map.jsx — client: one plan's map\n\"use client\";\nimport { useState } from \"react\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useRow } from \"../lib/data\";\nimport { updateRow } from \"../lib/actions\";\nimport { MindMap, mindMapToText } from \"../components/blocks/mind-map\";\n\nexport default function PlanMapSurface() {\n const id = useSearchParams().get(\"row\");\n const { row: plan, loading } = useRow({ source: \"plans\", id });\n if (loading || !plan) return null;\n return <PlanMap key={plan.id} plan={plan} />;\n}\n\nfunction PlanMap({ plan }) {\n const [map, setMap] = useState(plan.map); // jsonb, may be null; read once per plan\n return (\n <MindMap\n value={map ?? undefined}\n defaultValue={{ text: plan.name }}\n onChange={setMap}\n onSave={(m) => updateRow({ source: \"plans\", id: plan.id, patch: { map: m, map_text: mindMapToText(m) } })}\n label={`${plan.name} map`}\n className=\"h-[calc(100dvh-8rem)]\"\n />\n );\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\nAn agent's outline, straight onto a map:\n\n```jsx\nconst map = outlineToMindMap(`# Hiring plan\n- Roles\n - Founding engineer\n - Designer\n- Sourcing\n - Referrals\n - Communities`);\n<MindMap defaultValue={map} onSave={save} />\n```\n\n## What it will not do\n\n- Free placement: topics sit where the layout puts them (drag moves a\n branch to another topic, not to a spot). For free boards use\n `flow-canvas` or `whiteboard`.\n- Cross-links between branches, images or icons on topics.\n- Collaborate live: saves are last-write-wins.\n- Export an image.\n- Render the map on the server: the server shows it as text until the\n canvas mounts. For a read-only page that must be rich in the HTML,\n render `mindMapToMarkdown` yourself.\n\nA map it cannot be is your own component in `components/` — not an edit\nto this block. Its files are fixed: `npm run check` refuses a change, and\n`makersclaw add mind-map` restores them.\n"
41
+ "content": "# MindMap\n\nA mind map you can build without touching the mouse. One centre topic,\nmain branches balanced on both sides of it, each branch a tidy tree in its\nown colour. **Tab** adds a subtopic, **Enter** the next topic, typing\nrenames, the arrows walk the map. The layout is automatic: topics never\nneed dragging into place, and the map re-balances as it grows.\n\nA bar on top holds undo and redo, the save state, the selected topic's\nactions (add, colour, fold, delete) and a Map / Outline switch. The\nOutline view is the map as a nested list — the reading of it for a screen\nreader, and a quick way to jump to a topic.\n\nIt is built on `flow-canvas` (which draws it, pans and zooms): this block\nowns the tree, the layout and the keys.\n\nA map opens fitted to its view, but never smaller than its words can be\nread: 79%, where its smallest words (14px) draw at the kit's smallest\ntext size (11px). A map too big for that — most maps on a phone — opens\nat 79% centred on its centre topic; ⇧1 still fits the whole of it.\n\n```jsx\nimport { MindMap } from \"../components/blocks/mind-map\"; // a Makersclaw app (\"./blocks/mind-map\" from components/)\nimport { MindMap } from \"@/components/makersclaw/blocks/mind-map\"; // a shadcn project\n```\n\n`MindMap` is a client component: render it from a `\"use client\"`\ncomponent. `makersclaw add mind-map` adds `flow-canvas` with it (and its\nnpm package, `@xyflow/react`: run `npm install` after). The tree helpers\n(`mindMapToText`, `outlineToMindMap`, `layoutMindMap`…) are plain\nfunctions and work on the server too.\n\n## The data\n\nA map is one tree of topics. Store it as it comes out of `onChange` (one\n`jsonb` column), and keep `mindMapToText(map)` beside it for search and\nagents.\n\n```js\n{\n id: \"root\", text: \"Relay 2.0 launch\",\n children: [\n { id: \"why\", text: \"Why now\", side: \"right\", children: [\n { id: \"why-1\", text: \"Churn up 3 pts\" },\n ] },\n { id: \"risks\", text: \"Risks\", side: \"left\", color: \"chart-4\", collapsed: true, children: [ … ] },\n ],\n}\n```\n\n- `id` — any string, unique in the map. `text` — the words (a newline is\n allowed). `children` — the subtopics, in order.\n- `collapsed: true` — the branch is folded; the map shows how many topics\n it hides.\n- `color` — a colour **token**, never a colour value: `\"chart-2\"` …\n `\"chart-5\"`, `\"chart-1\"` (the theme's own), `\"warning\"`, `\"success\"`,\n `\"info\"`, `\"destructive\"`. Subtopics inherit their branch's. A main\n branch with none takes the next of `colors`.\n- `side` — `\"left\"` or `\"right\"`, main branches only. Missing, the map\n splits the branches to balance the two sides; the first change pins\n them, so adding a branch never flips the others across the centre.\n- Anything else on a topic (`note`, your own keys) is kept as it is.\n\n`normalizeMap(anything)` cleans what it is given: missing or repeated ids,\n`title`/`label` for `text`, `topics`/`items` for `children`, an outline\nstring, or nothing (a fresh map). The component runs it on `value`.\n\n## Props\n\n- `value` — the map, controlled. A new `value` that is not what the map\n last sent through `onChange` replaces it (loading another record); that\n replacement can be undone, and when it is another map (a new centre, or\n other main branches) the view fits it, as it fits a first one. Pass\n back exactly what `onChange` gave you.\n- `defaultValue` — the starting map, uncontrolled. With neither, a centre\n topic called \"Central topic\" and the first-run card.\n- `onChange(map)` — the whole tree after a change settles (debounced by\n `changeDelay`; typing is saved as it goes). Selection, folding in\n read-only mode, panning and zooming never call it.\n- `onSave(map)` — `async`, returns `{ ok: true }` or `{ ok: false,\n error? }` (a throw counts as failed). Called right after `onChange`. The\n bar shows \"Saving…\", \"Saved\", or the error with Retry. A failure keeps\n the map as it is on screen (nothing typed is thrown away); Retry sends\n it again.\n- `changeDelay` — ms of quiet before `onChange` / `onSave` (default 400).\n- `readOnly` — `false` (default). `true`: no editing; the arrows still\n walk the map, Space folds and unfolds (not saved), the Outline view and\n copying work.\n- `colors` — the branch palette, token names (default `BRANCH_COLORS`:\n chart-2, 3, 4, 5, 1). The colour picker offers these plus warning,\n success, info and destructive.\n- `templates` — what the first-run card offers: `[{ id, label, icon?,\n description?, map }]`, `map` a tree or a function returning one (default\n `mindMapTemplates()`: Project plan, Brainstorm, Meeting notes). `[]` for\n none.\n- `placeholder` — what an empty topic shows while it is typed (default\n \"New topic\").\n- `height` — px or any CSS length, default `600`. A height class in\n `className` (`h-full`, `min-h-0 flex-1`) replaces it: `min-h-0 flex-1`\n fills what the surface has left (the Example). Never `h-dvh` or a `vh`\n height, which `npm run check` refuses.\n- `label` — the map's accessible name (default \"Mind map\").\n- `onSelectionChange(topic | null)` — the selected topic changed (a side\n panel with its notes).\n- `className` — layout around it (margin, width, height). Never colour.\n\n## Keyboard\n\nWith a topic selected (click one, or press an arrow):\n\n| Key | Does |\n| --- | --- |\n| Tab | add a subtopic and type it |\n| Enter / Shift+Enter | add the next topic below / above |\n| Space, F2, or just type | rename |\n| Delete / Backspace | delete the topic and its branch (a toast offers Undo) |\n| ← → ↑ ↓ | walk the map: outwards to a child, inwards to the parent, up and down the column |\n| ⌥↑ / ⌥↓ | move the topic up or down among its siblings |\n| Shift+Tab | move the topic up a level |\n| ⌘↑ / ⌘↓ | fold / unfold the branch |\n| ⌘D | duplicate the branch |\n| ⌘C / ⌘X / ⌘V | copy or cut a branch as an outline; paste an outline (from notes, a doc, an agent) as subtopics |\n| ⌘Z / ⇧⌘Z | undo / redo |\n| Home | the centre |\n| Esc | clear the selection |\n| ⇧1 | fit the whole map to the screen, however small |\n| ? | the shortcuts sheet |\n\nWhile typing a topic: **Enter** finishes it and starts the next one (on\nan empty one it steps out a level, so a run of topics never needs the\nmouse), **Tab** finishes it and starts a subtopic (on an empty one it\nmoves in a level), **Backspace** on an empty topic removes it and goes\nback to the one before, **Esc** finishes. Shift+Enter is a new line.\n\nWith the mouse: click selects, double-click renames, the **+** at a\ntopic's end adds a subtopic, the chip at a branch's end folds it (folded,\nit shows the count it hides), and dragging a topic onto another moves its\nbranch there. A plain drag on the empty canvas pans; pinch or ⌘-scroll\nzooms. On a touch screen a drag always pans; tap selects, double-tap\nrenames, and the bar's buttons add.\n\n## The helpers\n\nPlain functions over a tree; each returns a new tree and leaves the old\none as it was.\n\n- Read: `findTopic(map, id)`, `pathTo(map, id)` → `[root, …, topic]`,\n `parentOf(map, id)`, `countTopics(topic)` (everything under it).\n- Change: `updateTopic(map, id, patch)`, `insertTopic(map, parentId,\n topic, index?)`, `insertSibling(map, id, topic, { before })`,\n `removeTopic(map, id)`, `moveTopic(map, id, parentId, index?)` (refused\n into its own branch), `shiftTopic(map, id, ±1)`, `indentTopic`,\n `outdentTopic`, `duplicateTopic(map, id)` → `{ root, id }`,\n `setAllCollapsed(map, on)`, `createTopic(text)`.\n- Text: `mindMapToText(map)` (an indented outline, for an agent or search),\n `mindMapToMarkdown(map)` (a heading and a nested list),\n `outlineToMindMap(text)` and `outlineToTopics(text)` (bullets, numbers,\n headings, tabs or spaces — what an agent writes).\n- Layout: `layoutMindMap(map, { measure?, colors? })` → `{ items, bounds }`\n (every visible topic's box, side, depth, colour and the line to its\n parent), `mindMapToBoard(map)` (the same as a flow-canvas board).\n- `mindMapTemplates()`, `BRANCH_COLORS`.\n\n## Example\n\n```jsx\n// surfaces/plan-map.jsx — client: one plan's map\n\"use client\";\nimport { useState } from \"react\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useRow } from \"../lib/data\";\nimport { updateRow } from \"../lib/actions\";\nimport { MindMap, mindMapToText } from \"../components/blocks/mind-map\";\n\nexport default function PlanMapSurface() {\n const id = useSearchParams().get(\"row\");\n const { row: plan, loading } = useRow({ source: \"plans\", id });\n if (loading || !plan) return null;\n return <PlanMap key={plan.id} plan={plan} />;\n}\n\nfunction PlanMap({ plan }) {\n const [map, setMap] = useState(plan.map); // jsonb, may be null; read once per plan\n return (\n <MindMap\n value={map ?? undefined}\n defaultValue={{ text: plan.name }}\n onChange={setMap}\n onSave={(m) => updateRow({ source: \"plans\", id: plan.id, patch: { map: m, map_text: mindMapToText(m) } })}\n label={`${plan.name} map`}\n className=\"min-h-0 flex-1\"\n />\n );\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\nAn agent's outline, straight onto a map:\n\n```jsx\nconst map = outlineToMindMap(`# Hiring plan\n- Roles\n - Founding engineer\n - Designer\n- Sourcing\n - Referrals\n - Communities`);\n<MindMap defaultValue={map} onSave={save} />\n```\n\n## What it will not do\n\n- Free placement: topics sit where the layout puts them (drag moves a\n branch to another topic, not to a spot). For free boards use\n `flow-canvas` or `whiteboard`.\n- Cross-links between branches, images or icons on topics.\n- Collaborate live: saves are last-write-wins.\n- Export an image.\n- Render the map on the server: the server shows it as text until the\n canvas mounts. For a read-only page that must be rich in the HTML,\n render `mindMapToMarkdown` yourself.\n\nA map it cannot be is your own component in `components/` — not an edit\nto this block. Its files are fixed: `npm run check` refuses a change, and\n`makersclaw add mind-map` restores them.\n"
42
42
  }
43
43
  ],
44
44
  "categories": [
@@ -60,7 +60,7 @@
60
60
  "docs": "Installed to components/makersclaw/blocks/mind-map. Import: import { MindMap, BRANCH_COLORS, normalizeMap, createTopic, findTopic, updateTopic, insertTopic, removeTopic, moveTopic, layoutMindMap, mindMapToBoard, mindMapToText, mindMapToMarkdown, outlineToMindMap, outlineToTopics, mindMapTemplates } from \"@/components/makersclaw/blocks/mind-map\". Props and an example: components/makersclaw/blocks/mind-map/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
61
61
  "meta": {
62
62
  "makersclaw": {
63
- "version": "0.7.0",
63
+ "version": "0.8.0",
64
64
  "useWhen": "A screen for thinking in a tree: brainstorming around one idea, planning a project, breaking a goal down, notes from a meeting, a topic outline an agent drafted. Not for free-form boards with stickies and arrows (flow-canvas, whiteboard), a people hierarchy (org-chart), or a process with steps (process-map)."
65
65
  }
66
66
  }
@@ -5,7 +5,7 @@
5
5
  "title": "Navigation Menu",
6
6
  "description": "A site's top nav: plain links, and triggers that open a mega-menu panel of links (icon, title, one-line description) with an optional featured card; hover with intent, click, arrow keys and Escape; a menu button and a sheet of collapsible groups on a phone.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -45,7 +45,7 @@
45
45
  "docs": "Installed to components/makersclaw/blocks/navigation-menu. Import: import { NavigationMenu } from \"@/components/makersclaw/blocks/navigation-menu\". Props and an example: components/makersclaw/blocks/navigation-menu/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
46
46
  "meta": {
47
47
  "makersclaw": {
48
- "version": "0.7.0",
48
+ "version": "0.8.0",
49
49
  "useWhen": "The header of a public-facing site or a product with many destinations: a marketing site, a docs hub, a customer portal. Not for an app's own sidebar or a row of tabs that switch views on one page."
50
50
  }
51
51
  }
@@ -5,7 +5,7 @@
5
5
  "title": "OrderDetail",
6
6
  "description": "One placed order on a page: its number, status and date, the items, the delivery address and contact, a timeline from placed to delivered (Steps), and the totals, with Track order and Invoice actions. Loading, error and not-found states built in.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -47,7 +47,7 @@
47
47
  "docs": "Installed to components/makersclaw/blocks/order-detail. Import: import { OrderDetail } from \"@/components/makersclaw/blocks/order-detail\". Props and an example: components/makersclaw/blocks/order-detail/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
48
48
  "meta": {
49
49
  "makersclaw": {
50
- "version": "0.7.0",
50
+ "version": "0.8.0",
51
51
  "useWhen": "A buyer or an operator opens one order: \"where is my order\", an order confirmation page, a support view of a purchase. Not for a list of orders (data-table) or for a basket before paying (cart)."
52
52
  }
53
53
  }
package/r/org-chart.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "title": "OrgChart",
6
6
  "description": "An org chart on flow-canvas: people laid out top down from a flat list (each names a manager), managers centred over their teams, a team with no managers in it as a compact column, every line drawn for you. Teams fold past the first levels behind a chip that says how many are hidden; search finds a person, unfolds the way to them, centres them and lights their chain of managers; a card opens a details sheet (reports to, direct reports, team size). Colour strips and a legend by department. Optional: drag a card onto another to change someone's manager (async { ok }, optimistic, with Undo). Keyboard throughout.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/flow-canvas.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/flow-canvas.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -38,7 +38,7 @@
38
38
  "path": "registry/blocks/org-chart/README.md",
39
39
  "type": "registry:file",
40
40
  "target": "@components/makersclaw/blocks/org-chart/README.md",
41
- "content": "# OrgChart\n\nWho reports to whom, top down. Give it people as a flat list — each row\nnames its manager — and it draws the tree: managers centred over their\nteams, a team with no managers in it as a compact column (twelve cards in\na row read worse than a list), every line in place.\n\nTeams past the first levels start folded; the chip under a manager says\nhow many report to them and, folded, how many are hidden. **Find a\nperson** (or press `/`) unfolds the way down to them, centres them and\nlights their chain of managers. A click selects a card; a second click\n(or Enter) opens a details sheet: who they report to, their direct\nreports, the team's size. Cards carry a colour strip by department, with\na legend on top.\n\nWith `onChangeManager`, drag a card onto another to move that person to a\nnew manager — shown at once, saved through your callback, undone if it\nfails, with an Undo in the toast.\n\nIt is built on `flow-canvas` (which draws it, pans and zooms): this block\nbuilds the tree, lays it out and owns the keys.\n\n```jsx\nimport { OrgChart } from \"../components/blocks/org-chart\"; // a Makersclaw app (\"./blocks/org-chart\" from components/)\nimport { OrgChart } from \"@/components/makersclaw/blocks/org-chart\"; // a shadcn project\n```\n\n`OrgChart` is a client component: render it from a `\"use client\"`\ncomponent. `makersclaw add org-chart` adds `flow-canvas` with it (and its\nnpm package, `@xyflow/react`: run `npm install` after). The helpers\n(`buildOrg`, `orgToText`…) are plain functions and work on the server too.\n\n## The data\n\nOne row per person, as a table holds them:\n\n```js\n[\n { id: \"p1\", name: \"Amara Okafor\", title: \"Chief Executive Officer\", department: \"Leadership\" },\n { id: \"p2\", name: \"Daniel Reyes\", title: \"VP Engineering\", department: \"Engineering\", managerId: \"p1\", avatar: \"https://…\", email: \"daniel@…\", location: \"Lisbon\" },\n]\n```\n\n- `id` — required, unique. `name`, `title` — the card's two lines.\n- `managerId` (or the field named by `managerKey`) — the manager's `id`.\n Missing, or naming someone not in the list: the person is the top of a\n tree (several tops sit side by side). A loop of managers is broken, and\n never hangs the chart.\n- `avatar` — an image URL; without one the card shows initials.\n- `department` (or the field named by `groupBy`) — the card's colour strip\n and the legend. Colours are the theme's chart hues, given in the order\n groups first appear.\n- `email`, `location` — shown in the details sheet. Any other field is\n kept and handed back to your callbacks.\n\n## Props\n\n- `people` — required, the rows above. An empty list shows a friendly\n empty state.\n- `managerKey` — the field naming the manager (default `\"managerId\"`).\n- `groupBy` — the field to colour by (default `\"department\"`), or\n `(person) => group`; `null` for no colours or legend.\n- `defaultDepth` — how many levels show before teams fold (default `3`:\n the top, their reports, and theirs).\n- `onChangeManager(person, managerId)` — `async`, return `{ ok: true }` or\n `{ ok: false, error? }` (a throw counts as failed). Turns on dragging a\n card onto another (with a mouse; on touch a drag always pans). The move\n shows at once; a failure puts the person back and says why. Update\n `people` when it succeeds (the chart keeps the move until you do).\n- `onOpenPerson(person)` — open your own view of a person (a record page,\n your own Sheet) instead of the built-in details sheet.\n- `renderDetails(person)` — more for the built-in details sheet, under the\n reports: kit parts (their open deals, a \"Message\" button).\n- `onSelectionChange(person | null)` — the selected card changed.\n- `height` — px or any CSS length, default `640`. A height class in\n `className` (`h-dvh`, `h-full`, `flex-1`) replaces it.\n- `label` — the chart's accessible name (default \"Org chart\").\n- `className` — layout around it (margin, width, height). Never colour.\n\n## Keyboard and pointer\n\n- `/` finds a person. Arrows walk the chart: ↑ to the manager, ↓ into the\n team (unfolding it), ← → along the team (and across to the next team\n at the end of a row); in a column ↑ ↓ go up and down it.\n- Space folds or unfolds the selected person's team; Enter opens their\n details; Home goes to the top; Esc clears the selection; ⇧1 fits the\n chart to the screen.\n- A plain drag on the canvas pans, pinch or ⌘-scroll zooms, the chip under\n a manager folds their team. The zoom bar (bottom right) has fit, zoom\n and a list view for screen readers.\n\n## The helpers\n\n- `buildOrg(people, { managerKey })` → `{ roots, byId }`: each node\n `{ id, person, parentId, depth, children, total }` (`total`: everyone\n under them).\n- `chainOf(byId, id)` → the chain of managers, top first, ending with them.\n- `orgToText(people, { managerKey })` — one person a line, indented under\n their manager, \"Daniel Reyes — VP Engineering (10)\": for an agent or a\n search column.\n- `layoutOrg(roots, { collapsed(node), stack = true })` → `{ items,\n bounds }`: every visible card's box and the line up to its manager.\n- `groupHues(people, key)` → Map(group → hue 1..5), the colours the chart\n uses (for a Legend or Badge elsewhere).\n\n## Example\n\n```jsx\n// surfaces/team.jsx — client: the chart, read and rearranged\n\"use client\";\nimport { useRows, whenLanded } from \"../lib/data\";\nimport { updateRow } from \"../lib/actions\";\nimport { OrgChart } from \"../components/blocks/org-chart\";\n\nexport default function TeamChart() {\n const { rows: people, loading } = useRows({ source: \"employees\" });\n if (loading) return null;\n return (\n <OrgChart\n people={people}\n managerKey=\"manager_id\"\n onChangeManager={async (person, managerId) => {\n const res = await updateRow({ source: \"employees\", id: person.id, patch: { manager_id: managerId } });\n if (res.ok) await whenLanded(); // the people are read again: the chart draws the new manager\n return res;\n }}\n className=\"h-[calc(100dvh-8rem)]\"\n />\n );\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\n## What it will not do\n\n- Show a person under two managers (dotted lines, matrix orgs): one\n manager each.\n- Edit a person's details: open your own form from `onOpenPerson` or put\n a button in `renderDetails`.\n- Export an image or print.\n- Render on the server: the server shows the people as text until the\n canvas mounts. For a read-only page that must be rich in the HTML,\n render `orgToText` yourself.\n\nA chart it cannot be is your own component in `components/` — not an edit\nto this block. Its files are fixed: `npm run check` refuses a change, and\n`makersclaw add org-chart` restores them.\n"
41
+ "content": "# OrgChart\n\nWho reports to whom, top down. Give it people as a flat list — each row\nnames its manager — and it draws the tree: managers centred over their\nteams, a team with no managers in it as a compact column (twelve cards in\na row read worse than a list), every line in place.\n\nTeams past the first levels start folded; the chip under a manager says\nhow many report to them and, folded, how many are hidden. **Find a\nperson** (or press `/`) unfolds the way down to them, centres them and\nlights their chain of managers. A click selects a card; a second click\n(or Enter) opens a details sheet: who they report to, their direct\nreports, the team's size. Cards carry a colour strip by department, with\na legend on top.\n\nWith `onChangeManager`, drag a card onto another to move that person to a\nnew manager — shown at once, saved through your callback, undone if it\nfails, with an Undo in the toast.\n\nIt is built on `flow-canvas` (which draws it, pans and zooms): this block\nbuilds the tree, lays it out and owns the keys.\n\n```jsx\nimport { OrgChart } from \"../components/blocks/org-chart\"; // a Makersclaw app (\"./blocks/org-chart\" from components/)\nimport { OrgChart } from \"@/components/makersclaw/blocks/org-chart\"; // a shadcn project\n```\n\n`OrgChart` is a client component: render it from a `\"use client\"`\ncomponent. `makersclaw add org-chart` adds `flow-canvas` with it (and its\nnpm package, `@xyflow/react`: run `npm install` after). The helpers\n(`buildOrg`, `orgToText`…) are plain functions and work on the server too.\n\n## The data\n\nOne row per person, as a table holds them:\n\n```js\n[\n { id: \"p1\", name: \"Amara Okafor\", title: \"Chief Executive Officer\", department: \"Leadership\" },\n { id: \"p2\", name: \"Daniel Reyes\", title: \"VP Engineering\", department: \"Engineering\", managerId: \"p1\", avatar: \"https://…\", email: \"daniel@…\", location: \"Lisbon\" },\n]\n```\n\n- `id` — required, unique. `name`, `title` — the card's two lines.\n- `managerId` (or the field named by `managerKey`) — the manager's `id`.\n Missing, or naming someone not in the list: the person is the top of a\n tree (several tops sit side by side). A loop of managers is broken, and\n never hangs the chart.\n- `avatar` — an image URL; without one the card shows initials.\n- `department` (or the field named by `groupBy`) — the card's colour strip\n and the legend. Colours are the theme's chart hues, given in the order\n groups first appear.\n- `email`, `location` — shown in the details sheet. Any other field is\n kept and handed back to your callbacks.\n\n## Props\n\n- `people` — required, the rows above. An empty list shows a friendly\n empty state.\n- `managerKey` — the field naming the manager (default `\"managerId\"`).\n- `groupBy` — the field to colour by (default `\"department\"`), or\n `(person) => group`; `null` for no colours or legend.\n- `defaultDepth` — how many levels show before teams fold (default `3`:\n the top, their reports, and theirs).\n- `onChangeManager(person, managerId)` — `async`, return `{ ok: true }` or\n `{ ok: false, error? }` (a throw counts as failed). Turns on dragging a\n card onto another (with a mouse; on touch a drag always pans). The move\n shows at once; a failure puts the person back and says why. Update\n `people` when it succeeds (the chart keeps the move until you do).\n- `onOpenPerson(person)` — open your own view of a person (a record page,\n your own Sheet) instead of the built-in details sheet.\n- `renderDetails(person)` — more for the built-in details sheet, under the\n reports: kit parts (their open deals, a \"Message\" button).\n- `onSelectionChange(person | null)` — the selected card changed.\n- `height` — px or any CSS length, default `640`. A height class in\n `className` (`h-full`, `min-h-0 flex-1`) replaces it: `min-h-0 flex-1`\n fills what the surface has left (the Example). Never `h-dvh` or a `vh`\n height, which `npm run check` refuses.\n- `label` — the chart's accessible name (default \"Org chart\").\n- `className` — layout around it (margin, width, height). Never colour.\n\n## Keyboard and pointer\n\n- `/` finds a person. Arrows walk the chart: ↑ to the manager, ↓ into the\n team (unfolding it), ← → along the team (and across to the next team\n at the end of a row); in a column ↑ ↓ go up and down it.\n- Space folds or unfolds the selected person's team; Enter opens their\n details; Home goes to the top; Esc clears the selection; ⇧1 fits the\n chart to the screen.\n- A plain drag on the canvas pans, pinch or ⌘-scroll zooms, the chip under\n a manager folds their team. The zoom bar (bottom right) has fit, zoom\n and a list view for screen readers.\n\n## The helpers\n\n- `buildOrg(people, { managerKey })` → `{ roots, byId }`: each node\n `{ id, person, parentId, depth, children, total }` (`total`: everyone\n under them).\n- `chainOf(byId, id)` → the chain of managers, top first, ending with them.\n- `orgToText(people, { managerKey })` — one person a line, indented under\n their manager, \"Daniel Reyes — VP Engineering (10)\": for an agent or a\n search column.\n- `layoutOrg(roots, { collapsed(node), stack = true })` → `{ items,\n bounds }`: every visible card's box and the line up to its manager.\n- `groupHues(people, key)` → Map(group → hue 1..5), the colours the chart\n uses (for a Legend or Badge elsewhere).\n\n## Example\n\n```jsx\n// surfaces/team.jsx — client: the chart, read and rearranged\n\"use client\";\nimport { useRows, whenLanded } from \"../lib/data\";\nimport { updateRow } from \"../lib/actions\";\nimport { OrgChart } from \"../components/blocks/org-chart\";\n\nexport default function TeamChart() {\n const { rows: people, loading } = useRows({ source: \"employees\" });\n if (loading) return null;\n return (\n <OrgChart\n people={people}\n managerKey=\"manager_id\"\n onChangeManager={async (person, managerId) => {\n const res = await updateRow({ source: \"employees\", id: person.id, patch: { manager_id: managerId } });\n if (res.ok) await whenLanded(); // the people are read again: the chart draws the new manager\n return res;\n }}\n className=\"min-h-0 flex-1\"\n />\n );\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\n## What it will not do\n\n- Show a person under two managers (dotted lines, matrix orgs): one\n manager each.\n- Edit a person's details: open your own form from `onOpenPerson` or put\n a button in `renderDetails`.\n- Export an image or print.\n- Render on the server: the server shows the people as text until the\n canvas mounts. For a read-only page that must be rich in the HTML,\n render `orgToText` yourself.\n\nA chart it cannot be is your own component in `components/` — not an edit\nto this block. Its files are fixed: `npm run check` refuses a change, and\n`makersclaw add org-chart` restores them.\n"
42
42
  }
43
43
  ],
44
44
  "categories": [
@@ -59,7 +59,7 @@
59
59
  "docs": "Installed to components/makersclaw/blocks/org-chart. Import: import { OrgChart, buildOrg, layoutOrg, chainOf, orgToText, groupHues } from \"@/components/makersclaw/blocks/org-chart\". Props and an example: components/makersclaw/blocks/org-chart/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
60
60
  "meta": {
61
61
  "makersclaw": {
62
- "version": "0.7.0",
62
+ "version": "0.8.0",
63
63
  "useWhen": "A screen that shows who reports to whom: a team or company directory's chart, an account's buying committee as a hierarchy, a reporting line to check before a reorg. Not for a free-form map of people and notes (account-map, flow-canvas), a plain list of people (data-table), or a tree of ideas (mind-map)."
64
64
  }
65
65
  }
@@ -5,7 +5,7 @@
5
5
  "title": "PageBuilder",
6
6
  "description": "A visual page editor (GrapesJS core) in the workspace theme: our own clean panels instead of GrapesJS's chrome — an Add panel of section and basic blocks (click or drag), a Layers tree, a Design panel limited to theme-token choices (colours, type, spacing, width, corners, borders), a device toggle (desktop, tablet, phone), undo/redo, preview, and a friendly empty state with starter templates. Pages style with CSS variables, so a saved page repaints with the theme. Saves out as { html, css, project }; PageView shows a saved page in an app.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "grapesjs@0.22.16",
@@ -76,7 +76,7 @@
76
76
  "path": "registry/blocks/page-builder/README.md",
77
77
  "type": "registry:file",
78
78
  "target": "@components/makersclaw/blocks/page-builder/README.md",
79
- "content": "# PageBuilder\n\nA visual page editor in the workspace theme, calm like Webflow's canvas\nwithout its cockpit. The page sits in the middle at desktop, tablet or\nphone width. On the left, **Add** lists blocks as small wireframes —\nsections (hero, features, logos, quote, pricing, questions, call to action,\nfooter) and basics (heading, text, button, image, two columns, list,\ndivider, space). Click one to add it below the selection, or drag it onto\nthe page. **Layers** shows the page as a tree: select, fold, hide while\nediting. On the right, **Design** changes the selected element's look,\nlimited to the theme's choices: text colour, background, text size,\nweight, typeface, alignment, spacing, width, corners and border. It also\nedits a link's address or a picture's source and description, and moves,\nduplicates or deletes the element. The top bar has the save state, the\ndevice toggle, undo and redo, Preview and Save. A new page opens on a\nfriendly empty state with starter templates. On a narrow screen the\npanels move to a bottom bar and open as sheets.\n\nIt is [GrapesJS](https://github.com/GrapesJS/grapesjs) (BSD-3-Clause, the\nfree core — never the commercial Studio SDK) with its React wrapper\n[@grapesjs/react](https://github.com/GrapesJS/react) (MIT). GrapesJS does\nthe page model, the canvas, selection, drag and drop, text editing, undo\nand the export. The panels, the top bar, the empty state and the Design\nchoices are ours, built from the kit. GrapesJS's own panels, style manager,\ntrait manager, asset modal and code viewer are never created. The blocks\nbuilt on this one — `landing-builder` first — import it, never `grapesjs`\ndirectly.\n\n```jsx\nimport { PageBuilder, PageView } from \"../components/blocks/page-builder\"; // a Makersclaw app (\"./blocks/page-builder\" from components/)\nimport { PageBuilder, PageView } from \"@/components/makersclaw/blocks/page-builder\"; // a shadcn project\n```\n\n`PageBuilder` is a client component: render it from a `\"use client\"`\ncomponent. `PageView` and the helpers (`pageDocument`, `cleanHtml`,\n`cleanCss`, `isPageEmpty`, `projectSections`…) work on the server too.\n\n`makersclaw add page-builder` adds `grapesjs` and `@grapesjs/react`\n(pinned, exact) to package.json: run `npm install` after. They load with\nthe page, with no `import()`. The editor is created after the first render.\nUntil then, and on the server, the frame shows a skeleton.\n\n## The data\n\nThe builder hands out a page as three things. You get it from `onSave`,\nfrom `onChange`, and from the handle's `getPage()`:\n\n```js\n{\n html: \"<section class=\\\"pb-section pb-center\\\" …>…</section>…\", // the body's content, cleaned\n css: \".pb-section{padding:88px 24px;background-color:var(--color-background);…}#i4kd{…}\",\n project: { pages: [ … ], styles: [ … ], assets: [], … }, // GrapesJS project JSON\n}\n```\n\n- **Store `project`** (a `jsonb` column). It is what reopens the page for\n editing: pass it back as `page={{ project }}`.\n- **Show `html` + `css`** with `PageView`, or publish them. They are the\n source of truth for what visitors see. They are derived from `project`\n at save time, so store them next to it instead of recomputing them.\n- `page={{ html, css }}` also opens, as a fresh project. This is how a\n template, an import or an agent-written page starts.\n- The HTML is cleaned: no `<script>`, no `on*` handlers, no `javascript:`\n links. The CSS has no `body`, `html` or `*` rules. Only rules the page\n uses are exported.\n\n### Pages keep the theme\n\n**A page never holds a colour. It names theme tokens, so it repaints when\nthe theme changes.** Every Design choice writes a CSS variable:\n`var(--color-primary)`, `var(--color-muted-foreground)`,\n`var(--theme-font-display)`, `var(--theme-radius)`. These are the same\nvariables the app's Tailwind utilities read. The built-in blocks' CSS\n(`BASE_CSS`, `pb-` classes) uses them too. Sizes and spacing are plain\n`rem`/`px` steps.\n\n- **In the editor**, the canvas is an iframe that cannot see the app's\n stylesheet. The builder resolves each token where it sits (its light or\n dark scheme and any `[data-theme]` scope) and writes the values into the\n frame. It does this again when the theme changes. It also copies the\n app's `@font-face` rules, so the page uses the theme's typefaces.\n- **In the app**, `PageView` shows the page. The tokens resolve to the\n workspace theme. Tailwind emits a theme variable only when some source\n names it, so this block names every token a page may use\n (`KEEP_TOKENS`). Installing the block is enough.\n- **Outside the app** (a download, a plain file host),\n `standaloneDocument(page, { root })` returns a full HTML document with\n the tokens resolved and inlined. `pageDocument(page, { tokens })` is the\n same function for the server, if you pass it the `:root{…}` string\n yourself.\n\n## Props\n\n| Prop | Default | |\n| --- | --- | --- |\n| `page` | empty | The page to open: `{ project }` or `{ html, css }`. Read once, when the editor is created. To open another page, change the component's `key` or call the handle's `setPage`. |\n| `onSave` | — | `async (page) => ({ ok, error? })`. Called by Save and ⌘/Ctrl+S with `{ html, css, project }`. On `{ ok: false }`, the page stays unsaved and a toast shows your `error` with \"Try again\". The top bar says \"Not saved\". |\n| `onChange` | — | `(page) => void`, 500 ms after the last edit. Use it for autosave or a live preview elsewhere. |\n| `title` | `\"Untitled page\"` | The name in the top bar. |\n| `blocks` | `[]` | Your own blocks, added after the built-ins. See \"Your own sections\". |\n| `builtIns` | `true` | Which built-in blocks to offer: `true` (sections and basics), `\"sections\"`, `\"basics\"` or `false`. |\n| `templates` | `STARTER_TEMPLATES` | Whole pages offered in the empty state and in \"Start from a template\". Pass `[]` for none. |\n| `styles` | `STYLE_GROUPS` | The Design panel's choices. See \"The Design choices\". |\n| `onUploadImage` | — | `async (file) => ({ ok, url, error? })`. When given, a picture's field gets an Upload button. Without it, people paste an address. |\n| `actions` | — | Extra top-bar controls before Save, such as landing-builder's Publish. Use kit `Button`s at `size=\"sm\"`. |\n| `saveLabel` | `\"Save\"` | The Save button's words. |\n| `builderRef` | — | A ref from `usePageBuilder()`. It receives the handle. |\n| `height` | `720` | The builder's height: a number (px) or any CSS length (`\"min(760px, 88dvh)\"`). It fills its width. |\n| `className` | — | Added to the frame. |\n\n### The handle\n\n```jsx\nconst builder = usePageBuilder();\n<PageBuilder builderRef={builder} … />\nbuilder.current?.getPage(); // { html, css, project }\n```\n\n`getPage()` · `save()` (runs `onSave`, returns its result) ·\n`addBlock(id)` · `loadTemplate(idOrTemplate)` · `setPage({ html, css } |\n{ project })` · `undo()` · `redo()` · `setDevice(\"desktop\" | \"tablet\" |\n\"mobile\")` · `preview(on = true)` · `editor` (the GrapesJS editor, for what\nthis block does not wrap; changes made through it are yours to keep\ntoken-only).\n\n## Your own sections\n\nThis is how `landing-builder` builds on this block. A block is plain data:\n\n```js\nconst HERO_WAITLIST = {\n id: \"hero-waitlist\", // unique across built-ins and yours\n label: \"Waitlist hero\", // the tile's name\n category: \"Hero\", // the Add panel's group heading\n thumb: \"split\", // a wireframe from THUMB_NAMES\n description: \"Headline, one line and an email button.\", // the tile's tooltip\n level: \"section\", // \"section\": added between top-level sections ·\n // \"element\": added inside the selected box\n // (default: \"section\" when category is \"Sections\")\n html: `<section class=\"lb-hero\" data-gjs-name=\"Waitlist hero\">…</section>`,\n css: `.lb-hero{padding:96px 24px;background-color:var(--color-muted);color:var(--color-foreground)}`,\n};\n<PageBuilder blocks={[HERO_WAITLIST]} builtIns=\"basics\" templates={LANDING_TEMPLATES} />\n```\n\n- `html` is the markup, or use `content`, a GrapesJS component definition,\n in its place. `data-gjs-name` names the element in Layers. Any\n `data-gjs-*` attribute sets that GrapesJS property: for example,\n `data-gjs-draggable=\"false\"` or `data-gjs-removable=\"false\"` lock a part.\n- `css` is added to the page once, when the editor opens. **Name only\n tokens for colour** (`var(--color-…)`, from `COLOR_TOKENS`) and\n `--theme-font-*` / `--theme-radius` for faces and corners. Then your\n sections repaint with the theme. Prefix your classes (`lb-`), and reuse\n the built-in `pb-` classes (`pb-button`, `pb-h2`, `pb-lead`,\n `pb-container`…) when they fit. Add an `@media (max-width: 768px)` rule\n for the phone layout. The canvas's phone width is a real narrow frame,\n so media queries work there.\n- Several blocks may share one `css` string. The same rule added twice is\n one rule.\n\nA **template** is a whole page:\n`{ id, label, description?, thumbs?: [\"hero\", \"features\", \"cta\"], html, css? }`\nor `{ id, label, project }`. `thumbs` draws its card, up to four sections\ntall. Picking a template replaces the page. A toast offers Undo when there\nwas something to replace.\n\n`BlockThumb` and `PageThumb` draw the same wireframes elsewhere, for\nexample in a landing-builder gallery of templates.\n\n## The Design choices\n\n`STYLE_GROUPS` is a list of groups. Each group is one row of choices:\n\n```js\n{\n id: \"fill\", label: \"Background\", kind: \"swatch\" | \"segmented\" | \"select\",\n for: [\"link\", \"box\"], // which elements show it: text · link · image · box (all if left out)\n match: \"background-color\", // the property read back to show the current choice\n options: [\n { value: \"\", label: \"Default\", css: { \"background-color\": \"\" } }, // \"\" = reset: the block's own CSS applies\n { value: \"primary\", label: \"Primary\", css: { \"background-color\": \"var(--color-primary)\" } },\n ],\n}\n```\n\nPicking an option writes its `css` onto that element only (an `#id`\nrule). Any property another option of the group sets is cleared. \"Reset\"\nbeside a group removes the group's properties. \"Clear this element's\nstyle\" removes all of them. To offer fewer or different choices, pass\nyour own list, filtered from or modelled on `STYLE_GROUPS`. A swatch\noption's `value` is the token name it paints (`primary` →\n`var(--color-primary)`). `groupsFor`, `currentOption` and `patchFor` are\nthe helpers the panel uses.\n\n## Mouse, keyboard and touch\n\n- Click an element to select it. Double-click text to edit the words\n (bold, italic and link in the small toolbar). Double-click a picture to\n jump to its field. Drag the selected element's handle to move it.\n- ⌘/Ctrl+S saves. ⌘/Ctrl+Z undoes, ⇧⌘Z redoes. Backspace or Delete\n removes the selection (with Undo in a toast when done from the panel).\n Esc leaves Preview.\n- The block tiles, the layer tree (↑ ↓ to move, ← → to fold), the swatches\n (arrow keys) and every control are reachable by keyboard. A tile adds\n on Enter.\n- On a phone, tap a tile to add it. Dragging is for a mouse.\n\n## Example\n\n```jsx\n// surfaces/landing.jsx — client: edit one page\n\"use client\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useRow } from \"../lib/data\";\nimport { updateRow } from \"../lib/actions\";\nimport { PageBuilder, PageView } from \"../components/blocks/page-builder\";\n\nexport default function LandingEditor() {\n const id = useSearchParams().get(\"row\");\n const { row, loading } = useRow({ source: \"pages\", id });\n if (loading || !row) return null;\n return (\n <PageBuilder\n key={row.id}\n title={row.title}\n page={row.project ? { project: row.project } : { html: row.html, css: row.css }}\n onSave={async ({ html, css, project }) => {\n const res = await updateRow({ source: \"pages\", id: row.id, patch: { html, css, project } });\n return res.ok ? { ok: true } : { ok: false, error: \"The page was not saved. Check your connection and try again.\" };\n }}\n />\n );\n}\n\n// The finished page, for a surface of its own that reads the row with the same useRow:\nexport function LandingPage({ row }) {\n return <PageView page={row} label={row.title} />;\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\n## What it will not do\n\n- No live multiplayer. Two people editing the same page is last save wins.\n- No free colour picker, hex field or arbitrary CSS. The Design panel\n offers the theme's choices only, by design. A block's own `css` is where\n a fixed design lives.\n- Styles apply at every width. The canvas's device toggle previews the\n page's own media queries. It does not keep per-device overrides.\n- No forms that submit, and no scripts. The HTML is cleaned of them. A\n signup form belongs to the app (a block beside `PageView`), not to the\n page.\n- No asset library. Pictures are an address, or an upload through\n `onUploadImage`.\n- No publishing, domains or SEO fields. Those belong to `landing-builder`\n and the app.\n\n## Credits\n\nGrapesJS © Artur Arseniev, BSD-3-Clause. @grapesjs/react © Artur\nArseniev, MIT. Both are used unmodified, as npm dependencies. The block\nships none of their code. GrapesJS's stylesheet is imported from the\npackage and repainted with the workspace's tokens.\n"
79
+ "content": "# PageBuilder\n\nA visual page editor in the workspace theme, calm like Webflow's canvas\nwithout its cockpit. The page sits in the middle at desktop, tablet or\nphone width. On the left, **Add** lists blocks as small wireframes —\nsections (hero, features, logos, quote, pricing, questions, call to action,\nfooter) and basics (heading, text, button, image, two columns, list,\ndivider, space). Click one to add it below the selection, or drag it onto\nthe page. **Layers** shows the page as a tree: select, fold, hide while\nediting. On the right, **Design** changes the selected element's look,\nlimited to the theme's choices: text colour, background, text size,\nweight, typeface, alignment, spacing, width, corners and border. It also\nedits a link's address or a picture's source and description, and moves,\nduplicates or deletes the element. The top bar has the save state, the\ndevice toggle, undo and redo, Preview and Save. A new page opens on a\nfriendly empty state with starter templates. On a narrow screen the\npanels move to a bottom bar and open as sheets.\n\nIt is [GrapesJS](https://github.com/GrapesJS/grapesjs) (BSD-3-Clause, the\nfree core — never the commercial Studio SDK) with its React wrapper\n[@grapesjs/react](https://github.com/GrapesJS/react) (MIT). GrapesJS does\nthe page model, the canvas, selection, drag and drop, text editing, undo\nand the export. The panels, the top bar, the empty state and the Design\nchoices are ours, built from the kit. GrapesJS's own panels, style manager,\ntrait manager, asset modal and code viewer are never created. The blocks\nbuilt on this one — `landing-builder` first — import it, never `grapesjs`\ndirectly.\n\n```jsx\nimport { PageBuilder, PageView } from \"../components/blocks/page-builder\"; // a Makersclaw app (\"./blocks/page-builder\" from components/)\nimport { PageBuilder, PageView } from \"@/components/makersclaw/blocks/page-builder\"; // a shadcn project\n```\n\n`PageBuilder` is a client component: render it from a `\"use client\"`\ncomponent. `PageView` and the helpers (`pageDocument`, `cleanHtml`,\n`cleanCss`, `isPageEmpty`, `projectSections`…) work on the server too.\n\n`makersclaw add page-builder` adds `grapesjs` and `@grapesjs/react`\n(pinned, exact) to package.json: run `npm install` after. They load with\nthe page, with no `import()`. The editor is created after the first render.\nUntil then, and on the server, the frame shows a skeleton.\n\n## The data\n\nThe builder hands out a page as three things. You get it from `onSave`,\nfrom `onChange`, and from the handle's `getPage()`:\n\n```js\n{\n html: \"<section class=\\\"pb-section pb-center\\\" …>…</section>…\", // the body's content, cleaned\n css: \".pb-section{padding:88px 24px;background-color:var(--color-background);…}#i4kd{…}\",\n project: { pages: [ … ], styles: [ … ], assets: [], … }, // GrapesJS project JSON\n}\n```\n\n- **Store `project`** (a `jsonb` column). It is what reopens the page for\n editing: pass it back as `page={{ project }}`.\n- **Show `html` + `css`** with `PageView`, or publish them. They are the\n source of truth for what visitors see. They are derived from `project`\n at save time, so store them next to it instead of recomputing them.\n- `page={{ html, css }}` also opens, as a fresh project. This is how a\n template, an import or an agent-written page starts.\n- The HTML is cleaned: no `<script>`, no `on*` handlers, no `javascript:`\n links. The CSS has no `body`, `html` or `*` rules. Only rules the page\n uses are exported.\n\n### Pages keep the theme\n\n**A page never holds a colour. It names theme tokens, so it repaints when\nthe theme changes.** Every Design choice writes a CSS variable:\n`var(--color-primary)`, `var(--color-muted-foreground)`,\n`var(--theme-font-display)`, `var(--theme-radius)`. These are the same\nvariables the app's Tailwind utilities read. The built-in blocks' CSS\n(`BASE_CSS`, `pb-` classes) uses them too. Sizes and spacing are plain\n`rem`/`px` steps.\n\n- **In the editor**, the canvas is an iframe that cannot see the app's\n stylesheet. The builder resolves each token where it sits (its light or\n dark scheme and any `[data-theme]` scope) and writes the values into the\n frame. It does this again when the theme changes. It also copies the\n app's `@font-face` rules, so the page uses the theme's typefaces.\n- **In the app**, `PageView` shows the page. The tokens resolve to the\n workspace theme. Tailwind emits a theme variable only when some source\n names it, so this block names every token a page may use\n (`KEEP_TOKENS`). Installing the block is enough.\n- **Outside the app** (a download, a plain file host),\n `standaloneDocument(page, { root })` returns a full HTML document with\n the tokens resolved and inlined. `pageDocument(page, { tokens })` is the\n same function for the server, if you pass it the `:root{…}` string\n yourself.\n\n## Props\n\n| Prop | Default | |\n| --- | --- | --- |\n| `page` | empty | The page to open: `{ project }` or `{ html, css }`. Read once, when the editor is created. To open another page, change the component's `key` or call the handle's `setPage`. |\n| `onSave` | — | `async (page) => ({ ok, error? })`. Called by Save and ⌘/Ctrl+S with `{ html, css, project }`. On `{ ok: false }`, the page stays unsaved and a toast shows your `error` with \"Try again\". The top bar says \"Not saved\". |\n| `onChange` | — | `(page) => void`, 500 ms after the last edit. Use it for autosave or a live preview elsewhere. |\n| `title` | `\"Untitled page\"` | The name in the top bar. |\n| `blocks` | `[]` | Your own blocks, added after the built-ins. See \"Your own sections\". |\n| `builtIns` | `true` | Which built-in blocks to offer: `true` (sections and basics), `\"sections\"`, `\"basics\"` or `false`. |\n| `templates` | `STARTER_TEMPLATES` | Whole pages offered in the empty state and in \"Start from a template\". Pass `[]` for none. |\n| `styles` | `STYLE_GROUPS` | The Design panel's choices. See \"The Design choices\". |\n| `onUploadImage` | — | `async (file) => ({ ok, url, error? })`. When given, a picture's field gets an Upload button. Without it, people paste an address. |\n| `actions` | — | Extra top-bar controls before Save, such as landing-builder's Publish. Use kit `Button`s at `size=\"sm\"`. |\n| `saveLabel` | `\"Save\"` | The Save button's words. |\n| `builderRef` | — | A ref from `usePageBuilder()`. It receives the handle. |\n| `height` | `720` | The builder's height: a number (px) or any CSS length (`\"100%\"` in a sized box, such as the kit's `AppLayout contentMode=\"fill\"`; never a `vh` length, which `npm run check` refuses). It fills its width. |\n| `className` | — | Added to the frame. |\n\n### The handle\n\n```jsx\nconst builder = usePageBuilder();\n<PageBuilder builderRef={builder} … />\nbuilder.current?.getPage(); // { html, css, project }\n```\n\n`getPage()` · `save()` (runs `onSave`, returns its result) ·\n`addBlock(id)` · `loadTemplate(idOrTemplate)` · `setPage({ html, css } |\n{ project })` · `undo()` · `redo()` · `setDevice(\"desktop\" | \"tablet\" |\n\"mobile\")` · `preview(on = true)` · `editor` (the GrapesJS editor, for what\nthis block does not wrap; changes made through it are yours to keep\ntoken-only).\n\n## Your own sections\n\nThis is how `landing-builder` builds on this block. A block is plain data:\n\n```js\nconst HERO_WAITLIST = {\n id: \"hero-waitlist\", // unique across built-ins and yours\n label: \"Waitlist hero\", // the tile's name\n category: \"Hero\", // the Add panel's group heading\n thumb: \"split\", // a wireframe from THUMB_NAMES\n description: \"Headline, one line and an email button.\", // the tile's tooltip\n level: \"section\", // \"section\": added between top-level sections ·\n // \"element\": added inside the selected box\n // (default: \"section\" when category is \"Sections\")\n html: `<section class=\"lb-hero\" data-gjs-name=\"Waitlist hero\">…</section>`,\n css: `.lb-hero{padding:96px 24px;background-color:var(--color-muted);color:var(--color-foreground)}`,\n};\n<PageBuilder blocks={[HERO_WAITLIST]} builtIns=\"basics\" templates={LANDING_TEMPLATES} />\n```\n\n- `html` is the markup, or use `content`, a GrapesJS component definition,\n in its place. `data-gjs-name` names the element in Layers. Any\n `data-gjs-*` attribute sets that GrapesJS property: for example,\n `data-gjs-draggable=\"false\"` or `data-gjs-removable=\"false\"` lock a part.\n- `css` is added to the page once, when the editor opens. **Name only\n tokens for colour** (`var(--color-…)`, from `COLOR_TOKENS`) and\n `--theme-font-*` / `--theme-radius` for faces and corners. Then your\n sections repaint with the theme. Prefix your classes (`lb-`), and reuse\n the built-in `pb-` classes (`pb-button`, `pb-h2`, `pb-lead`,\n `pb-container`…) when they fit. Add an `@media (max-width: 768px)` rule\n for the phone layout. The canvas's phone width is a real narrow frame,\n so media queries work there.\n- Several blocks may share one `css` string. The same rule added twice is\n one rule.\n\nA **template** is a whole page:\n`{ id, label, description?, thumbs?: [\"hero\", \"features\", \"cta\"], html, css? }`\nor `{ id, label, project }`. `thumbs` draws its card, up to four sections\ntall. Picking a template replaces the page. A toast offers Undo when there\nwas something to replace.\n\n`BlockThumb` and `PageThumb` draw the same wireframes elsewhere, for\nexample in a landing-builder gallery of templates.\n\n## The Design choices\n\n`STYLE_GROUPS` is a list of groups. Each group is one row of choices:\n\n```js\n{\n id: \"fill\", label: \"Background\", kind: \"swatch\" | \"segmented\" | \"select\",\n for: [\"link\", \"box\"], // which elements show it: text · link · image · box (all if left out)\n match: \"background-color\", // the property read back to show the current choice\n options: [\n { value: \"\", label: \"Default\", css: { \"background-color\": \"\" } }, // \"\" = reset: the block's own CSS applies\n { value: \"primary\", label: \"Primary\", css: { \"background-color\": \"var(--color-primary)\" } },\n ],\n}\n```\n\nPicking an option writes its `css` onto that element only (an `#id`\nrule). Any property another option of the group sets is cleared. \"Reset\"\nbeside a group removes the group's properties. \"Clear this element's\nstyle\" removes all of them. To offer fewer or different choices, pass\nyour own list, filtered from or modelled on `STYLE_GROUPS`. A swatch\noption's `value` is the token name it paints (`primary` →\n`var(--color-primary)`). `groupsFor`, `currentOption` and `patchFor` are\nthe helpers the panel uses.\n\n## Mouse, keyboard and touch\n\n- Click an element to select it. Double-click text to edit the words\n (bold, italic and link in the small toolbar). Double-click a picture to\n jump to its field. Drag the selected element's handle to move it.\n- ⌘/Ctrl+S saves. ⌘/Ctrl+Z undoes, ⇧⌘Z redoes. Backspace or Delete\n removes the selection (with Undo in a toast when done from the panel).\n Esc leaves Preview.\n- The block tiles, the layer tree (↑ ↓ to move, ← → to fold), the swatches\n (arrow keys) and every control are reachable by keyboard. A tile adds\n on Enter.\n- On a phone, tap a tile to add it. Dragging is for a mouse.\n\n## Example\n\n```jsx\n// surfaces/landing.jsx — client: edit one page\n\"use client\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useRow } from \"../lib/data\";\nimport { updateRow } from \"../lib/actions\";\nimport { PageBuilder, PageView } from \"../components/blocks/page-builder\";\n\nexport default function LandingEditor() {\n const id = useSearchParams().get(\"row\");\n const { row, loading } = useRow({ source: \"pages\", id });\n if (loading || !row) return null;\n return (\n <PageBuilder\n key={row.id}\n title={row.title}\n page={row.project ? { project: row.project } : { html: row.html, css: row.css }}\n onSave={async ({ html, css, project }) => {\n const res = await updateRow({ source: \"pages\", id: row.id, patch: { html, css, project } });\n return res.ok ? { ok: true } : { ok: false, error: \"The page was not saved. Check your connection and try again.\" };\n }}\n />\n );\n}\n\n// The finished page, for a surface of its own that reads the row with the same useRow:\nexport function LandingPage({ row }) {\n return <PageView page={row} label={row.title} />;\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\n## What it will not do\n\n- No live multiplayer. Two people editing the same page is last save wins.\n- No free colour picker, hex field or arbitrary CSS. The Design panel\n offers the theme's choices only, by design. A block's own `css` is where\n a fixed design lives.\n- Styles apply at every width. The canvas's device toggle previews the\n page's own media queries. It does not keep per-device overrides.\n- No forms that submit, and no scripts. The HTML is cleaned of them. A\n signup form belongs to the app (a block beside `PageView`), not to the\n page.\n- No asset library. Pictures are an address, or an upload through\n `onUploadImage`.\n- No publishing, domains or SEO fields. Those belong to `landing-builder`\n and the app.\n\n## Credits\n\nGrapesJS © Artur Arseniev, BSD-3-Clause. @grapesjs/react © Artur\nArseniev, MIT. Both are used unmodified, as npm dependencies. The block\nships none of their code. GrapesJS's stylesheet is imported from the\npackage and repainted with the workspace's tokens.\n"
80
80
  }
81
81
  ],
82
82
  "categories": [
@@ -98,7 +98,7 @@
98
98
  "docs": "Installed to components/makersclaw/blocks/page-builder. Import: import { PageBuilder, usePageBuilder, readPage, DEVICES, PageView, standaloneDocument, BlockThumb, PageThumb, THUMB_NAMES, BASE_CSS, SECTION_BLOCKS, BASIC_BLOCKS, STARTER_TEMPLATES, STYLE_GROUPS, COLOR_TOKENS, groupsFor, currentOption, patchFor, tokenCss, fontFaceCss, fontLinks, PAGE_BASE_CSS, emptyPage, isPageEmpty, cleanHtml, cleanCss, normalizeBlock, templatePage, pageDocument, projectSections } from \"@/components/makersclaw/blocks/page-builder\". Props and an example: components/makersclaw/blocks/page-builder/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
99
99
  "meta": {
100
100
  "makersclaw": {
101
- "version": "0.7.0",
101
+ "version": "0.8.0",
102
102
  "useWhen": "A screen where someone builds or edits a web page visually: a landing page, a waitlist or launch page, a simple site section, an announcement page. landing-builder is built on it (section templates, publishing). Not for rich text in a document (doc-editor), an email body (newsletter-builder), a form of fields (record-form) or a freeform drawing (sketch-canvas)."
103
103
  }
104
104
  }
@@ -5,7 +5,7 @@
5
5
  "title": "PaymentMethods",
6
6
  "description": "A customer's SAVED payment methods as cards: brand, last four digits, expiry (expired and expiring soon flagged) and a Default badge; Make default, Remove behind a Confirm, and Add, which hands off to the payment provider (Paddle). Never a card-number field. Empty, loading and error states built in.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -44,7 +44,7 @@
44
44
  "docs": "Installed to components/makersclaw/blocks/payment-methods. Import: import { PaymentMethods } from \"@/components/makersclaw/blocks/payment-methods\". Props and an example: components/makersclaw/blocks/payment-methods/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
45
45
  "meta": {
46
46
  "makersclaw": {
47
- "version": "0.7.0",
47
+ "version": "0.8.0",
48
48
  "useWhen": "A billing or account page where a customer manages the cards and wallets on file. Not for taking a payment (cart's checkout step hands off to Paddle) or for a list of charges (transaction-list)."
49
49
  }
50
50
  }
@@ -5,7 +5,7 @@
5
5
  "title": "PdfDocument",
6
6
  "description": "Make PDFs in the workspace theme (@react-pdf/renderer): a small set of document primitives (headings, text, label/value fields, tables with repeating headers, totals, callouts, badges, rows and columns) and a clean ready-made quote / estimate / invoice layout, rendered in the browser to a Blob with renderPdf(doc). The theme's colours are read from the page when the PDF is made (a PDF cannot read CSS variables): white paper, near-black ink and the theme's primary as the one accent. Also a live-preview hook (usePdf), a Download PDF button, and the quote sums and money formatting as plain functions.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "@react-pdf/renderer@4.9.0"
@@ -72,7 +72,7 @@
72
72
  "docs": "Installed to components/makersclaw/blocks/pdf-document. Import: import { renderPdf, usePdf, PdfDownloadButton, downloadBlob, registerPdfFont, PdfDocument, PdfStack, PdfRow, PdfCol, PdfText, PdfHeading, PdfLabel, PdfField, PdfDivider, PdfCallout, PdfBadge, PdfTable, PdfTotals, PdfLines, PdfView, PdfRawText, PdfImage, PdfLink, PdfThemeProvider, usePdfTheme, PDF_TYPE, QuoteDocument, quoteTotals, lineAmount, round2, formatMoney, formatQuantity, formatPdfDate, resolvePdfTheme, buildPdfTheme, DEFAULT_PDF_THEME } from \"@/components/makersclaw/blocks/pdf-document\". Props and an example: components/makersclaw/blocks/pdf-document/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
73
73
  "meta": {
74
74
  "makersclaw": {
75
- "version": "0.7.0",
75
+ "version": "0.8.0",
76
76
  "useWhen": "A screen produces a document someone downloads, prints, emails or signs: a quote, an invoice, an estimate, a receipt, a statement, a certificate, a one-page report. The quote-builder and proposal blocks print through it. Not for showing an existing PDF on screen (pdf-viewer) or for a web page people read in the app (doc-page)."
77
77
  }
78
78
  }
package/r/pdf-viewer.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "PdfViewer",
6
6
  "description": "A PDF on screen, in the workspace theme (react-pdf + pdf.js): pages in one smooth scrolling column (only the pages near the window are drawn, so long files stay fast), a thumbnail rail, a page box, zoom by steps, fit width, fit page and Ctrl/⌘ + wheel or pinch, find in the text with every match marked, and highlights with notes: select text, choose Highlight or Add note, and it saves through onHighlight; notes are edited in a side list, colours are theme tokens, delete has Undo. Opens a URL, a Blob or bytes; password-protected files ask for the password; a narrow viewer turns its rails into sheets. The pdf.js worker ships with the app (no public/ file, no CDN).",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "react-pdf@11.0.0",
@@ -58,7 +58,7 @@
58
58
  "path": "registry/blocks/pdf-viewer/README.md",
59
59
  "type": "registry:file",
60
60
  "target": "@components/makersclaw/blocks/pdf-viewer/README.md",
61
- "content": "# PdfViewer\n\nA PDF on screen, in the workspace theme. The pages scroll in one column;\na rail of thumbnails sits on the left and a page box in the toolbar\njumps anywhere. Zoom by steps, fit the width, fit the page, or hold\nCtrl/⌘ and scroll (a trackpad pinch works too) — the line under the\npointer stays put. ⌘F finds words in the text: every match is marked on\nthe page, Enter walks them.\n\nPeople can highlight. Select a sentence and a small bar offers\n**Highlight**, **Add note** and **Copy**. A highlight shows as a marker\nstroke in a theme colour; its note sits in the Highlights list beside\nthe pages, where it is edited, recoloured or deleted (with Undo). Every\nchange shows at once and saves through your callback; when a save fails,\nthe change goes back and a toast says why.\n\nA narrow viewer (under 900px wide — a phone, a side column) turns the\npage rail and the Highlights list into sheets. Long files stay fast:\nonly the pages near the window are drawn. Password-protected files ask\nfor their password. A file that will not open says why and offers Try\nagain.\n\nIt is [React-PDF](https://github.com/wojtekmaj/react-pdf) (MIT) over\nMozilla's [PDF.js](https://github.com/mozilla/pdf.js) (Apache-2.0)\nunderneath: they parse and draw the pages and the selectable text. The\ntoolbar, rails, search, highlights and states are ours, in the kit. The\nblocks built on this one (proposal-accept, a contract review screen…)\nimport it, never `react-pdf` or `pdfjs-dist`.\n\n```jsx\nimport { PdfViewer } from \"../components/blocks/pdf-viewer\"; // a Makersclaw app (\"./blocks/pdf-viewer\" from components/)\nimport { PdfViewer } from \"@/components/makersclaw/blocks/pdf-viewer\"; // a shadcn project\n```\n\n`PdfViewer` is a client component: render it from a `\"use client\"`\ncomponent. The helpers (`findMatches`, `toPageRects`, `sortHighlights`…)\nare plain functions and work on the server.\n\n## The file, and the worker\n\n`file` is a URL (same origin, or a server that allows CORS), a `Blob` /\n`File` (an upload, or a PDF made with the `pdf-document` block), or the\nbytes (`ArrayBuffer` / `Uint8Array`, copied — your buffer stays usable).\n\nPDF.js parses in a Web Worker. Nothing to set up: the block points\nPDF.js at `new URL(\"pdfjs-dist/build/pdf.worker.min.mjs\", import.meta.url)`,\nwhich the app's bundler resolves at build time and emits as a hashed\nfile under `/_next/static/media/`, with the base path applied. So the\nworker ships with the app, always matches the PDF.js version that calls\nit, and comes from the app's own origin — no file in `public/`, no CDN.\n\n## The data\n\nA highlight is one row:\n\n```js\n{\n id: \"hl_81\", // yours\n page: 3, // 1-based\n rects: [{ x, y, w, h }], // fractions of the page (0…1), top-left origin — hold at any zoom\n text: \"Fees rise by 4% at each annual renewal\", // the words highlighted\n note: \"Ask for a 3% cap.\", // optional\n tone: \"warning\", // warning (default) · info · success · destructive · primary\n author: \"Priya Raman\", // optional, shown under the note\n createdAt: \"2026-09-21\", // optional\n}\n```\n\n`rects` may be left out: a highlight given only by its `text` (and\noptionally its `page`) is placed where those words first appear, once\nthe pages' text has been read. That is how an agent or a server flags a\nclause without knowing any geometry. A quote that is not in the text is\nlisted under \"Not found in the text\". Store the `rects` the viewer sends\nyou in `onHighlight` — they are exact; placing by words is close (the\nwidth inside a line is shared out by characters) and assumes pages that\nare not rotated.\n\nStore a highlight as a row in its own table (`document_id`, `page`,\n`rects` jsonb, `text`, `note`, `tone`, `author`, `created_at`).\n\n## Props\n\n- `file` — the PDF (see above). `null` shows the empty state.\n- `title` — names the viewer for screen readers and shows in the\n toolbar on a wide viewer. Default `\"PDF document\"` for readers, nothing\n shown.\n- `fileName` — what Download saves as. Default: the title + `.pdf`.\n- `highlights` — the rows above. Default `[]`.\n- `onHighlight(h)` — save a new highlight: `h` is `{ page, rects, text,\n note, tone }`. Return `{ ok: true, id }` (your id replaces the viewer's\n temporary one) or `{ ok: false, error }` (a throw counts as failed): it\n is removed and a toast shows `error`. **Without `onHighlight` the viewer\n is read-only**: no selection bar, and the Highlights list (shown only\n when there are highlights) cannot edit.\n- `onHighlightChange(id, patch)` — `patch` is `{ note }` or `{ tone }`.\n Same return; a failure puts the old value back.\n- `onHighlightDelete(id)` — called 5 seconds after a delete, once the\n Undo in its toast has passed (or at once if the viewer unmounts). Same\n return; a failure puts the highlight back.\n- `initialPage` — the page to open on. Default `1`.\n- `onPageChange(page, pageCount)` — whenever the page at the top of the\n window changes.\n- `defaultZoom` — `\"fit\"` (width, the default) · `\"page\"` · a number\n (`1` is 100%).\n- `thumbnails` — the page rail. Default `true`.\n- `search` — find in the text. Default `true`.\n- `download` — the Download button. Default `true`.\n- `notes` — open the Highlights list on a wide viewer at first. Default:\n open when there are highlights.\n- `height` — the viewer's height, any CSS length. Default\n `\"min(80vh, 860px)\"`; it never goes under 384px.\n- `loading` — `true` shows the skeleton (while you fetch or make the\n file) instead of the empty state.\n- `emptyTitle` · `emptyBody` — the words when `file` is empty. Default\n \"No document yet\" / \"When a PDF is attached, it shows here.\"\n- `viewerRef` — a ref from `usePdfViewer()`; `ref.current` then has\n `goToPage(n)`, `goToHighlight(id)`, `search(text)`, `setZoom(z)`, and\n `page` / `pageCount`.\n- `className` — added to the frame (a margin, a width). Its border,\n radius and height are the block's; set height through `height`.\n\nKeys, while focus is in the viewer: ⌘/Ctrl + F find, Enter / Shift+Enter\nnext / previous match, Esc closes find; ⌘/Ctrl + `+` / `-` zoom, `0`\nfits the width; Page Up / Page Down and the arrows scroll; Tab reaches\nevery control and each page thumbnail. Notes: ⌘/Ctrl + Enter saves, Esc\ncancels.\n\n## Example\n\n```jsx\n// surfaces/contract.jsx — client: one document and its highlights\n\"use client\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useRow, useRows, whenLanded } from \"../lib/data\";\nimport { deleteRow, insertRow, updateRow } from \"../lib/actions\";\nimport { PdfViewer } from \"../components/blocks/pdf-viewer\";\n\nconst source = \"highlights\";\n\n// A write answers once the highlights are read again, so the viewer never\n// shows the list from before it.\nasync function landed(res) {\n if (res.ok) await whenLanded();\n return res;\n}\n\nexport default function ContractReview() {\n const id = useSearchParams().get(\"row\");\n const { row: doc, loading } = useRow({ source: \"documents\", id });\n const { rows: highlights } = useRows({ source, filter: { document_id: id } });\n if (loading || !doc) return null;\n return (\n <PdfViewer\n file={doc.url}\n title={doc.title}\n highlights={highlights}\n onHighlight={(h) => insertRow({ source, values: { document_id: doc.id, ...h } }).then(landed)}\n onHighlightChange={(hid, patch) => updateRow({ source, id: hid, patch }).then(landed)}\n onHighlightDelete={(hid) => deleteRow({ source, id: hid }).then(landed)}\n />\n );\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\nPreview a PDF you make (the `pdf-document` block):\n\n```jsx\nconst { blob } = usePdf(() => <QuoteDocument {...quote} />, [quote]);\n<PdfViewer file={blob} loading={!blob} title={`Quote ${quote.number}`} thumbnails={false} />\n```\n\n## What it will not do\n\n- Edit the PDF: fill its forms, sign it, rotate or reorder pages, or\n write highlights into the file. Highlights are rows you store; the PDF\n is untouched. (For signing, the `signature-pad` block.)\n- Search across a line break in one phrase: PDF.js splits text into\n lines, and a match must sit inside one (like most quick finds).\n- Highlight in a scanned PDF (an image with no text layer): there is no\n text to select or find.\n- Keep the old pages up while a NEW file loads: a changed `file` shows\n the skeleton until it has opened.\n- Open a URL on another origin that does not allow CORS: fetch it on\n your server and pass the bytes.\n- Print; the browser's own PDF reader does that from Download.\n\n## Credits\n\nBuilt on [React-PDF](https://github.com/wojtekmaj/react-pdf) by Wojciech\nMaj (MIT) and [PDF.js](https://github.com/mozilla/pdf.js) by Mozilla\n(Apache-2.0), used unmodified. See THIRD-PARTY-NOTICES.md.\n"
61
+ "content": "# PdfViewer\n\nA PDF on screen, in the workspace theme. The pages scroll in one column;\na rail of thumbnails sits on the left and a page box in the toolbar\njumps anywhere. Zoom by steps, fit the width, fit the page, or hold\nCtrl/⌘ and scroll (a trackpad pinch works too) — the line under the\npointer stays put. ⌘F finds words in the text: every match is marked on\nthe page, Enter walks them.\n\nPeople can highlight. Select a sentence and a small bar offers\n**Highlight**, **Add note** and **Copy**. A highlight shows as a marker\nstroke in a theme colour; its note sits in the Highlights list beside\nthe pages, where it is edited, recoloured or deleted (with Undo). Every\nchange shows at once and saves through your callback; when a save fails,\nthe change goes back and a toast says why.\n\nA narrow viewer (under 900px wide — a phone, a side column) turns the\npage rail and the Highlights list into sheets. Long files stay fast:\nonly the pages near the window are drawn. Password-protected files ask\nfor their password. A file that will not open says why and offers Try\nagain.\n\nIt is [React-PDF](https://github.com/wojtekmaj/react-pdf) (MIT) over\nMozilla's [PDF.js](https://github.com/mozilla/pdf.js) (Apache-2.0)\nunderneath: they parse and draw the pages and the selectable text. The\ntoolbar, rails, search, highlights and states are ours, in the kit. The\nblocks built on this one (proposal-accept, a contract review screen…)\nimport it, never `react-pdf` or `pdfjs-dist`.\n\n```jsx\nimport { PdfViewer } from \"../components/blocks/pdf-viewer\"; // a Makersclaw app (\"./blocks/pdf-viewer\" from components/)\nimport { PdfViewer } from \"@/components/makersclaw/blocks/pdf-viewer\"; // a shadcn project\n```\n\n`PdfViewer` is a client component: render it from a `\"use client\"`\ncomponent. The helpers (`findMatches`, `toPageRects`, `sortHighlights`…)\nare plain functions and work on the server.\n\n## The file, and the worker\n\n`file` is a URL (same origin, or a server that allows CORS), a `Blob` /\n`File` (an upload, or a PDF made with the `pdf-document` block), or the\nbytes (`ArrayBuffer` / `Uint8Array`, copied — your buffer stays usable).\n\nPDF.js parses in a Web Worker. Nothing to set up: the block points\nPDF.js at `new URL(\"pdfjs-dist/build/pdf.worker.min.mjs\", import.meta.url)`,\nwhich the app's bundler resolves at build time and emits as a hashed\nfile under `/_next/static/media/`, with the base path applied. So the\nworker ships with the app, always matches the PDF.js version that calls\nit, and comes from the app's own origin — no file in `public/`, no CDN.\n\n## The data\n\nA highlight is one row:\n\n```js\n{\n id: \"hl_81\", // yours\n page: 3, // 1-based\n rects: [{ x, y, w, h }], // fractions of the page (0…1), top-left origin — hold at any zoom\n text: \"Fees rise by 4% at each annual renewal\", // the words highlighted\n note: \"Ask for a 3% cap.\", // optional\n tone: \"warning\", // warning (default) · info · success · destructive · primary\n author: \"Priya Raman\", // optional, shown under the note\n createdAt: \"2026-09-21\", // optional\n}\n```\n\n`rects` may be left out: a highlight given only by its `text` (and\noptionally its `page`) is placed where those words first appear, once\nthe pages' text has been read. That is how an agent or a server flags a\nclause without knowing any geometry. A quote that is not in the text is\nlisted under \"Not found in the text\". Store the `rects` the viewer sends\nyou in `onHighlight` — they are exact; placing by words is close (the\nwidth inside a line is shared out by characters) and assumes pages that\nare not rotated.\n\nStore a highlight as a row in its own table (`document_id`, `page`,\n`rects` jsonb, `text`, `note`, `tone`, `author`, `created_at`).\n\n## Props\n\n- `file` — the PDF (see above). `null` shows the empty state.\n- `title` — names the viewer for screen readers and shows in the\n toolbar on a wide viewer. Default `\"PDF document\"` for readers, nothing\n shown.\n- `fileName` — what Download saves as. Default: the title + `.pdf`.\n- `highlights` — the rows above. Default `[]`.\n- `onHighlight(h)` — save a new highlight: `h` is `{ page, rects, text,\n note, tone }`. Return `{ ok: true, id }` (your id replaces the viewer's\n temporary one) or `{ ok: false, error }` (a throw counts as failed): it\n is removed and a toast shows `error`. **Without `onHighlight` the viewer\n is read-only**: no selection bar, and the Highlights list (shown only\n when there are highlights) cannot edit.\n- `onHighlightChange(id, patch)` — `patch` is `{ note }` or `{ tone }`.\n Same return; a failure puts the old value back.\n- `onHighlightDelete(id)` — called 5 seconds after a delete, once the\n Undo in its toast has passed (or at once if the viewer unmounts). Same\n return; a failure puts the highlight back.\n- `initialPage` — the page to open on. Default `1`.\n- `onPageChange(page, pageCount)` — whenever the page at the top of the\n window changes.\n- `defaultZoom` — `\"fit\"` (width, the default) · `\"page\"` · a number\n (`1` is 100%).\n- `thumbnails` — the page rail. Default `true`.\n- `search` — find in the text. Default `true`.\n- `download` — the Download button. Default `true`.\n- `notes` — open the Highlights list on a wide viewer at first. Default:\n open when there are highlights.\n- `height` — the viewer's height, any CSS length. Default\n `\"min(80vh, 860px)\"` (the block's own); it never goes under 384px.\n Yours is a length, or `\"100%\"` in a sized box such as the kit's\n `AppLayout contentMode=\"fill\"`: a `vh` length in your code is refused\n by `npm run check`.\n- `loading` — `true` shows the skeleton (while you fetch or make the\n file) instead of the empty state.\n- `emptyTitle` · `emptyBody` — the words when `file` is empty. Default\n \"No document yet\" / \"When a PDF is attached, it shows here.\"\n- `viewerRef` — a ref from `usePdfViewer()`; `ref.current` then has\n `goToPage(n)`, `goToHighlight(id)`, `search(text)`, `setZoom(z)`, and\n `page` / `pageCount`.\n- `className` — added to the frame (a margin, a width). Its border,\n radius and height are the block's; set height through `height`.\n\nKeys, while focus is in the viewer: ⌘/Ctrl + F find, Enter / Shift+Enter\nnext / previous match, Esc closes find; ⌘/Ctrl + `+` / `-` zoom, `0`\nfits the width; Page Up / Page Down and the arrows scroll; Tab reaches\nevery control and each page thumbnail. Notes: ⌘/Ctrl + Enter saves, Esc\ncancels.\n\n## Example\n\n```jsx\n// surfaces/contract.jsx — client: one document and its highlights\n\"use client\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useRow, useRows, whenLanded } from \"../lib/data\";\nimport { deleteRow, insertRow, updateRow } from \"../lib/actions\";\nimport { PdfViewer } from \"../components/blocks/pdf-viewer\";\n\nconst source = \"highlights\";\n\n// A write answers once the highlights are read again, so the viewer never\n// shows the list from before it.\nasync function landed(res) {\n if (res.ok) await whenLanded();\n return res;\n}\n\nexport default function ContractReview() {\n const id = useSearchParams().get(\"row\");\n const { row: doc, loading } = useRow({ source: \"documents\", id });\n const { rows: highlights } = useRows({ source, filter: { document_id: id } });\n if (loading || !doc) return null;\n return (\n <PdfViewer\n file={doc.url}\n title={doc.title}\n highlights={highlights}\n onHighlight={(h) => insertRow({ source, values: { document_id: doc.id, ...h } }).then(landed)}\n onHighlightChange={(hid, patch) => updateRow({ source, id: hid, patch }).then(landed)}\n onHighlightDelete={(hid) => deleteRow({ source, id: hid }).then(landed)}\n />\n );\n}\n```\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\nPreview a PDF you make (the `pdf-document` block):\n\n```jsx\nconst { blob } = usePdf(() => <QuoteDocument {...quote} />, [quote]);\n<PdfViewer file={blob} loading={!blob} title={`Quote ${quote.number}`} thumbnails={false} />\n```\n\n## What it will not do\n\n- Edit the PDF: fill its forms, sign it, rotate or reorder pages, or\n write highlights into the file. Highlights are rows you store; the PDF\n is untouched. (For signing, the `signature-pad` block.)\n- Search across a line break in one phrase: PDF.js splits text into\n lines, and a match must sit inside one (like most quick finds).\n- Highlight in a scanned PDF (an image with no text layer): there is no\n text to select or find.\n- Keep the old pages up while a NEW file loads: a changed `file` shows\n the skeleton until it has opened.\n- Open a URL on another origin that does not allow CORS: fetch it on\n your server and pass the bytes.\n- Print; the browser's own PDF reader does that from Download.\n\n## Credits\n\nBuilt on [React-PDF](https://github.com/wojtekmaj/react-pdf) by Wojciech\nMaj (MIT) and [PDF.js](https://github.com/mozilla/pdf.js) by Mozilla\n(Apache-2.0), used unmodified. See THIRD-PARTY-NOTICES.md.\n"
62
62
  }
63
63
  ],
64
64
  "categories": [
@@ -81,7 +81,7 @@
81
81
  "docs": "Installed to components/makersclaw/blocks/pdf-viewer. Import: import { PdfViewer, usePdfViewer, HIGHLIGHT_TONES, findMatches, toPageRects, mergeRects, sortHighlights, highlightAt, fitScale, ZOOM_STEPS } from \"@/components/makersclaw/blocks/pdf-viewer\". Props and an example: components/makersclaw/blocks/pdf-viewer/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
82
82
  "meta": {
83
83
  "makersclaw": {
84
- "version": "0.7.0",
84
+ "version": "0.8.0",
85
85
  "useWhen": "A screen shows an existing PDF: a contract or proposal to review, a signed agreement, an uploaded invoice or statement, a report, a generated quote before it is sent; or people mark up a PDF and leave notes on passages. Pair it with pdf-document to preview a PDF you make. Not for making PDFs (pdf-document), for rich text people edit (doc-editor), or for marking up a screenshot (annotate)."
86
86
  }
87
87
  }
@@ -5,7 +5,7 @@
5
5
  "title": "Product detail",
6
6
  "description": "One product on its own page: pictures (thumbnails, a side rail, a carousel, a tall stack, or one scene with the buy box floating over it), brand, name, price with the old price struck through, star rating and review count, options (sizes as a segmented row, colours as swatches, long lists as a select, plans as cards with their own price), a quantity control, add to cart through an async callback that may fail, buy now, save to favourites, perks, and details in an accordion or tabs. Loading, error, not-found and sold-out states.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -69,7 +69,7 @@
69
69
  "docs": "Installed to components/makersclaw/blocks/product-detail. Import: import { ProductDetail, ProductMedia, OptionPicker, QuantityControl, Price, formatPrice, cartLine } from \"@/components/makersclaw/blocks/product-detail\". Props and an example: components/makersclaw/blocks/product-detail/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
70
70
  "meta": {
71
71
  "makersclaw": {
72
- "version": "0.7.0",
72
+ "version": "0.8.0",
73
73
  "useWhen": "Selling one thing a customer looks at before buying: a product page, a template or digital download with plans, a merch item, a booking add-on. Not for many products at once (product-grid), the cart itself (cart), or a plan comparison table."
74
74
  }
75
75
  }
@@ -5,8 +5,8 @@
5
5
  "title": "Product grid",
6
6
  "description": "Products as cards in a grid: picture, badge, favourite heart, brand, name, star rating, price with the old one struck through, and a quick add to cart through an async callback that may fail (\"Choose options\" when the product has sizes or colours). Facet filters as selects that also match a product's option values, a sort, a live count, a lead tile that takes a double cell (a picture or a muted looping video), and a promo card for collection tiles. Loading, error, empty and no-matches states; two columns on a phone.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/product-detail.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/product-detail.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -58,7 +58,7 @@
58
58
  "docs": "Installed to components/makersclaw/blocks/product-grid. Import: import { ProductGrid, ProductCard, PromoTile, productMatches } from \"@/components/makersclaw/blocks/product-grid\". Props and an example: components/makersclaw/blocks/product-grid/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
59
59
  "meta": {
60
60
  "makersclaw": {
61
- "version": "0.7.0",
61
+ "version": "0.8.0",
62
62
  "useWhen": "A shop's catalogue, a collection page, related products under a product page, a row of promotions ('New', 'Sale'). Not for records to scan and act on in columns (data-table), one product's page (product-detail), or the cart (cart)."
63
63
  }
64
64
  }