makersclaw 0.6.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.
- package/README.md +56 -9
- package/package.json +1 -1
- package/r/annotate.json +4 -4
- package/r/approval-request.json +3 -3
- package/r/audio-player.json +3 -3
- package/r/auth-form.json +2 -2
- package/r/board.json +4 -4
- package/r/calendar-view.json +4 -4
- package/r/cart.json +3 -3
- package/r/chart.json +6 -6
- package/r/chat.json +3 -3
- package/r/code-editor.json +4 -4
- package/r/comment-box.json +4 -4
- package/r/contact-section.json +3 -3
- package/r/core.json +10 -4
- package/r/csv-import.json +4 -4
- package/r/data-table.json +3 -3
- package/r/diagram.json +3 -3
- package/r/doc-editor.json +4 -4
- package/r/doc-page.json +4 -4
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +3 -3
- package/r/flow-canvas.json +4 -4
- package/r/footer.json +3 -3
- package/r/gantt.json +4 -4
- package/r/job-panel.json +3 -3
- package/r/json-editor.json +4 -4
- package/r/map.json +5 -5
- package/r/menubar.json +2 -2
- package/r/mind-map.json +4 -4
- package/r/navigation-menu.json +2 -2
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +4 -4
- package/r/page-builder.json +6 -6
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +6 -6
- package/r/product-detail.json +3 -3
- package/r/product-grid.json +4 -4
- package/r/record-form.json +3 -3
- package/r/record-notes.json +5 -5
- package/r/registry.json +126 -121
- package/r/search-dialog.json +3 -3
- package/r/settings-list.json +3 -3
- package/r/setup-checklist.json +3 -3
- package/r/share-dialog.json +3 -3
- package/r/sidebar.json +3 -3
- package/r/signature-pad.json +3 -3
- package/r/sketch-canvas.json +5 -5
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +4 -4
- package/r/task-list.json +4 -4
- package/r/task-model.json +3 -3
- package/r/testimonials.json +2 -2
- package/r/transaction-list.json +3 -3
- package/r/whiteboard.json +4 -4
- package/registry/blocks/annotate/README.md +24 -12
- package/registry/blocks/approval-request/README.md +15 -8
- package/registry/blocks/audio-player/README.md +17 -10
- package/registry/blocks/board/README.md +13 -9
- package/registry/blocks/board/board.jsx +1 -1
- package/registry/blocks/calendar-view/README.md +28 -4
- package/registry/blocks/cart/README.md +14 -10
- package/registry/blocks/chart/README.md +14 -7
- package/registry/blocks/chart/cartesian.jsx +1 -1
- package/registry/blocks/chart/core.jsx +33 -23
- package/registry/blocks/chart/pie.jsx +1 -1
- package/registry/blocks/chat/README.md +47 -14
- package/registry/blocks/chat/demo.jsx +2 -2
- package/registry/blocks/code-editor/README.md +4 -0
- package/registry/blocks/code-editor/code-editor.jsx +1 -1
- package/registry/blocks/comment-box/README.md +20 -13
- package/registry/blocks/contact-section/README.md +7 -6
- package/registry/blocks/csv-import/README.md +34 -12
- package/registry/blocks/data-table/README.md +41 -8
- package/registry/blocks/diagram/diagram.jsx +1 -1
- package/registry/blocks/doc-editor/README.md +18 -6
- package/registry/blocks/doc-editor/doc-editor.jsx +1 -1
- package/registry/blocks/doc-page/README.md +16 -6
- package/registry/blocks/file-upload/README.md +7 -1
- package/registry/blocks/flow-canvas/README.md +30 -12
- package/registry/blocks/flow-canvas/flow-canvas.jsx +1 -1
- package/registry/blocks/footer/README.md +7 -4
- package/registry/blocks/gantt/README.md +18 -12
- package/registry/blocks/job-panel/README.md +1 -1
- package/registry/blocks/json-editor/README.md +16 -4
- package/registry/blocks/map/README.md +19 -3
- package/registry/blocks/map/map-view.jsx +1 -1
- package/registry/blocks/map/maplibre.js +1 -1
- package/registry/blocks/mind-map/README.md +20 -7
- package/registry/blocks/org-chart/README.md +14 -9
- package/registry/blocks/page-builder/README.md +14 -7
- package/registry/blocks/page-builder/demo.jsx +3 -3
- package/registry/blocks/page-builder/page-builder.jsx +1 -1
- package/registry/blocks/page-builder/page-view.jsx +1 -1
- package/registry/blocks/page-builder/page.js +1 -1
- package/registry/blocks/pdf-viewer/README.md +26 -21
- package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1 -1
- package/registry/blocks/pdf-viewer/pdfjs.js +1 -1
- package/registry/blocks/pdf-viewer/ssr-shim.js +1 -1
- package/registry/blocks/product-detail/README.md +13 -7
- package/registry/blocks/product-detail/demo.jsx +1 -1
- package/registry/blocks/product-grid/README.md +12 -10
- package/registry/blocks/record-form/README.md +26 -9
- package/registry/blocks/record-notes/README.md +42 -30
- package/registry/blocks/search-dialog/README.md +38 -7
- package/registry/blocks/settings-list/README.md +29 -17
- package/registry/blocks/setup-checklist/README.md +12 -14
- package/registry/blocks/share-dialog/README.md +1 -1
- package/registry/blocks/sidebar/README.md +23 -13
- package/registry/blocks/signature-pad/README.md +15 -4
- package/registry/blocks/sketch-canvas/README.md +25 -10
- package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1 -1
- package/registry/blocks/sketch-canvas/ssr-shim.js +3 -3
- package/registry/blocks/spreadsheet/README.md +17 -8
- package/registry/blocks/stat-cards/README.md +17 -8
- package/registry/blocks/task-list/README.md +14 -11
- package/registry/blocks/task-model/README.md +3 -3
- package/registry/blocks/transaction-list/README.md +15 -7
- package/registry/blocks/whiteboard/README.md +34 -22
- package/src/cli.mjs +33 -6
- package/src/install.mjs +285 -26
- package/src/registry.mjs +109 -1
- package/templates/next/browser/app/auto-refresh.jsx +67 -0
- package/templates/next/browser/app/layout.jsx +94 -0
- package/templates/next/browser/app/page.jsx +60 -0
- package/templates/next/browser/app/s/[key]/page.jsx +95 -0
- package/templates/next/browser/lib/actions.js +16 -0
- package/templates/next/browser/lib/data.js +43 -0
- package/templates/next/files/README.md +1100 -89
- package/templates/next/files/app/frame.js +128 -4
- package/templates/next/files/app/shell.jsx +54 -37
- package/templates/next/files/check/run.mjs +1514 -45
- package/templates/next/files/components/ui/index.js +1 -0
- package/templates/next/files/components/ui/item.jsx +2 -2
- package/templates/next/files/components/ui/layout.jsx +307 -0
- package/templates/next/files/components/ui/notice.jsx +1 -1
- package/templates/next/files/components/ui/separator.jsx +1 -1
- package/templates/next/files/lib/actions.js +170 -51
- package/templates/next/files/lib/brain-server.js +96 -0
- package/templates/next/files/lib/brain.js +413 -0
- package/templates/next/files/lib/claw.js +15 -1
- package/templates/next/files/lib/data.js +119 -29
- package/templates/next/files/lib/db.js +97 -17
- package/templates/next/files/lib/door.js +680 -0
- package/templates/next/files/lib/hooks.js +71 -2
- package/templates/next/files/lib/platform.js +93 -4
- package/templates/next/files/next.config.mjs +36 -2
- package/templates/next/files/surfaces.js +3 -2
- package/templates/next/template.json +18 -3
- package/templates/next/files/schema.sql +0 -22
package/r/search-dialog.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "SearchDialog",
|
|
6
6
|
"description": "One ⌘K search over every kind of record — chats, documents, images, projects, people. Before typing it shows your sections (Last opened, Recent); while typing, ranked matches with the typed text marked, a date, image thumbnails, and kind chips (All, Chats, Images…) to narrow them. Searches your list in the browser, or your server through an async onSearch. Built on the kit's Command; SearchPanel is the same box inline.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
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/search-dialog/README.md",
|
|
26
26
|
"type": "registry:file",
|
|
27
27
|
"target": "@components/makersclaw/blocks/search-dialog/README.md",
|
|
28
|
-
"content": "# SearchDialog\n\nOne search over every kind of record in an app: chats, documents, images,\nprojects, people. Before anything is typed it shows your sections (\"Last\nopened\", \"Recent chats\"). While typing it shows the matches, best first,\nwith the typed text marked, a date, and a thumbnail for an image. Chips\nunder the list (All, Chats, Images, Documents, Projects) narrow it to one\nkind. Arrow keys move, Enter opens, Esc closes, ⌘K / Ctrl+K toggles it.\n\nIt is the kit's `Command` (the list, the keys, the screen-reader wiring)\ninside the kit's `Sheet`: a dialog near the top of a wide window, a\nbottom sheet on a phone. `SearchPanel` is the same box inline, for a\nsearch page or a sidebar.\n\n```jsx\nimport { SearchDialog, SearchPanel } from \"../components/blocks/search-dialog\"; // a Makersclaw app (\"./blocks/search-dialog\" from components/)\nimport { SearchDialog, SearchPanel } from \"@/components/makersclaw/blocks/search-dialog\"; // a shadcn project\n```\n\nA client component: render it from a `\"use client\"` component.\n\n## The data\n\nA result is `{ id, kind, title, snippet?, date?, thumb?, icon?, keywords? }`.\n\n- `id` — unique within its kind list.\n- `kind` — `\"chat\"`, `\"document\"`, `\"image\"`, `\"project\"`, `\"person\"`,\n `\"company\"`, `\"task\"`, `\"page\"`, or your own word. It picks the icon\n and the chip.\n- `title` — the line that is searched and marked.\n- `snippet` — a line under it: an excerpt, a file type. Searched too.\n- `date` — shown after the snippet while searching (\"Sep 4\", \"Today\").\n Format it yourself.\n- `thumb` — an image URL (or a node) shown instead of the icon. Every\n `image` result gets a square, with an image glyph when there is no URL.\n- `icon` — a kit `Icon` name, instead of the kind's.\n- `keywords` — matched, never shown (a person's company, a project's client).\n\n## Props\n\nBoth components take these.\n\n- `results` — everything that can be found, searched in the browser. Leave\n it out when `onSearch` does the searching.\n- `onSelect(result)` — the result chosen (click or Enter). Open it here.\n The dialog closes itself first.\n- `sections` — what shows before anything is typed:\n `[{ heading, results }]`, in order. A result may sit in two sections.\n Leave it out and the empty box shows `hint`.\n- `onSearch(query, { signal })` — async; return the results for `query`.\n Called 200ms after typing stops. A newer query aborts the older one\n (pass `signal` to `fetch`). A throw shows \"Search failed\". The chips\n filter what comes back.\n- `kinds` — the chips, in order: `[{ value, label, icon? }]`. Default:\n every kind in `results` and `sections`, in order of first appearance,\n labelled from the list above (\"Chats\", \"Images\"). One kind, no chips.\n- `loading` — `true` while you fetch `results` yourself.\n- `placeholder` — default \"Search…\".\n- `hint` — what the empty box says before typing. Default \"Type to search.\"\n- `showDate` — default `true`.\n- `label` — what a screen reader calls the box. Default \"Search\".\n- `className` — on the box (`SearchPanel`) or the dialog (`SearchDialog`).\n\n`SearchDialog` also takes:\n\n- `open` · `onOpenChange(open)` — controlled; or `defaultOpen`.\n- `hotkey` — `\"k\"` binds ⌘K / Ctrl+K anywhere on the page to open and\n close it. Bind no key an input on the page already uses.\n\n## Example\n\n```jsx\n\"use client\";\nimport { useRouter } from \"next/navigation\";\nimport {
|
|
28
|
+
"content": "# SearchDialog\n\nOne search over every kind of record in an app: chats, documents, images,\nprojects, people. Before anything is typed it shows your sections (\"Last\nopened\", \"Recent chats\"). While typing it shows the matches, best first,\nwith the typed text marked, a date, and a thumbnail for an image. Chips\nunder the list (All, Chats, Images, Documents, Projects) narrow it to one\nkind. Arrow keys move, Enter opens, Esc closes, ⌘K / Ctrl+K toggles it.\n\nIt is the kit's `Command` (the list, the keys, the screen-reader wiring)\ninside the kit's `Sheet`: a dialog near the top of a wide window, a\nbottom sheet on a phone. `SearchPanel` is the same box inline, for a\nsearch page or a sidebar.\n\n```jsx\nimport { SearchDialog, SearchPanel } from \"../components/blocks/search-dialog\"; // a Makersclaw app (\"./blocks/search-dialog\" from components/)\nimport { SearchDialog, SearchPanel } from \"@/components/makersclaw/blocks/search-dialog\"; // a shadcn project\n```\n\nA client component: render it from a `\"use client\"` component.\n\n## The data\n\nA result is `{ id, kind, title, snippet?, date?, thumb?, icon?, keywords? }`.\n\n- `id` — unique within its kind list.\n- `kind` — `\"chat\"`, `\"document\"`, `\"image\"`, `\"project\"`, `\"person\"`,\n `\"company\"`, `\"task\"`, `\"page\"`, or your own word. It picks the icon\n and the chip.\n- `title` — the line that is searched and marked.\n- `snippet` — a line under it: an excerpt, a file type. Searched too.\n- `date` — shown after the snippet while searching (\"Sep 4\", \"Today\").\n Format it yourself.\n- `thumb` — an image URL (or a node) shown instead of the icon. Every\n `image` result gets a square, with an image glyph when there is no URL.\n- `icon` — a kit `Icon` name, instead of the kind's.\n- `keywords` — matched, never shown (a person's company, a project's client).\n\n## Props\n\nBoth components take these.\n\n- `results` — everything that can be found, searched in the browser. Leave\n it out when `onSearch` does the searching.\n- `onSelect(result)` — the result chosen (click or Enter). Open it here.\n The dialog closes itself first.\n- `sections` — what shows before anything is typed:\n `[{ heading, results }]`, in order. A result may sit in two sections.\n Leave it out and the empty box shows `hint`.\n- `onSearch(query, { signal })` — async; return the results for `query`.\n Called 200ms after typing stops. A newer query aborts the older one\n (pass `signal` to `fetch`). A throw shows \"Search failed\". The chips\n filter what comes back.\n- `kinds` — the chips, in order: `[{ value, label, icon? }]`. Default:\n every kind in `results` and `sections`, in order of first appearance,\n labelled from the list above (\"Chats\", \"Images\"). One kind, no chips.\n- `loading` — `true` while you fetch `results` yourself.\n- `placeholder` — default \"Search…\".\n- `hint` — what the empty box says before typing. Default \"Type to search.\"\n- `showDate` — default `true`.\n- `label` — what a screen reader calls the box. Default \"Search\".\n- `className` — on the box (`SearchPanel`) or the dialog (`SearchDialog`).\n\n`SearchDialog` also takes:\n\n- `open` · `onOpenChange(open)` — controlled; or `defaultOpen`.\n- `hotkey` — `\"k\"` binds ⌘K / Ctrl+K anywhere on the page to open and\n close it. Bind no key an input on the page already uses.\n\n## Example\n\n```jsx\n// components/GlobalSearch.jsx — client: search as the member types\n\"use client\";\nimport { useState } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { loadRows, useRows } from \"../lib/data\";\nimport { Button } from \"./ui\";\nimport { SearchDialog } from \"./blocks/search-dialog\";\n\nconst asResult = (d) => ({ id: d.id, kind: \"deal\", title: d.name, snippet: d.stage });\n\nexport default function GlobalSearch() {\n const router = useRouter();\n const [open, setOpen] = useState(false);\n const { rows: recent } = useRows({ source: \"deals\", orderBy: \"updated_at desc\", limit: 5 });\n\n // The app's own table: names that start with what was typed, case ignored.\n // The dialog aborts the older query through `signal`.\n async function search(q, { signal }) {\n const { rows, error } = await loadRows({ source: \"deals\", filter: { name: { prefix: q } }, orderBy: \"name asc\", limit: 20, signal });\n if (error) throw new Error(error); // \"Search failed\"; an aborted read's answer is ignored\n return rows.map(asResult);\n }\n\n return (\n <>\n <Button icon=\"search\" onClick={() => setOpen(true)}>Search</Button>\n <SearchDialog\n open={open}\n onOpenChange={setOpen}\n hotkey=\"k\"\n sections={[{ heading: \"Recent\", results: recent.map(asResult) }]}\n onSearch={search}\n onSelect={(r) => router.push(`/s/deals?row=${r.id}`)}\n />\n </>\n );\n}\n```\n\nThe company's records, through the brain (`lib/brain.js`), with\n`meaning: false` while a person types: keywords only, nothing embedded\nor charged. A search by meaning is charged per call; keep it for a\nsearch page or an Enter.\n\n```jsx\nimport { searchBrain } from \"../lib/brain\";\n\nasync function search(q, { signal }) {\n const { results, error } = await searchBrain({ q, meaning: false, limit: 20, signal });\n if (error) throw new Error(error);\n return results.map(({ record, snippet }) => ({ id: record.id, kind: record.kind, title: record.label, snippet }));\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- Index anything. It searches the list you give it, or asks your server.\n- Lay images out as a grid. An image result is a row with a thumbnail.\n- Remember recent searches. Keep them yourself and pass them as a section.\n- Run commands (\"New invoice\", \"Go to settings\"). For a command palette\n use the kit's `CommandDialog`.\n\n## Credits\n\nPorted from blocks.so (https://blocks.so, command-menu-04, MIT), rewritten on the Makersclaw kit.\n"
|
|
29
29
|
}
|
|
30
30
|
],
|
|
31
31
|
"categories": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"docs": "Installed to components/makersclaw/blocks/search-dialog. Import: import { SearchDialog, SearchPanel } from \"@/components/makersclaw/blocks/search-dialog\". Props and an example: components/makersclaw/blocks/search-dialog/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
45
45
|
"meta": {
|
|
46
46
|
"makersclaw": {
|
|
47
|
-
"version": "0.
|
|
47
|
+
"version": "0.8.0",
|
|
48
48
|
"useWhen": "An app has more than one kind of record and wants one global search box or ⌘K palette to find any of them, or a search page with type filters."
|
|
49
49
|
}
|
|
50
50
|
}
|
package/r/settings-list.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "SettingsList",
|
|
6
6
|
"description": "A card of settings, one row each — icon, label, description, meta lines, a badge, and its control: a Switch, a Select, a Button, a remove button or a menu of actions, with optional details that open under the row. Every change saves on its own through an async callback; the row shows the new value at once, a spinner while it saves, and on failure the old value back with the reason and Try again. variant=\"matrix\" is the notification grid: events by group down the side, channels (email, push, SMS) across, a switch per cell, stacked on a phone.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"path": "registry/blocks/settings-list/README.md",
|
|
38
38
|
"type": "registry:file",
|
|
39
39
|
"target": "@components/makersclaw/blocks/settings-list/README.md",
|
|
40
|
-
"content": "# SettingsList\n\nA card of settings, one row each: an icon, a label, a description, a few\nmeta lines, a badge, and the row's control. Every change saves on its own —\nno Save button. A flipped switch or a new choice shows at once, a spinner\nsits beside it while it saves, and when the save fails the old value comes\nback with the reason and Try again, on that row only. `variant=\"matrix\"`\nis the notification grid: events down the side, channels across the top.\n\n```jsx\nimport { SettingsList } from \"../components/blocks/settings-list\"; // a Makersclaw app (\"./blocks/settings-list\" from components/)\nimport { SettingsList } from \"@/components/makersclaw/blocks/settings-list\"; // a shadcn project\n```\n\nIt is a client component: render it from a `\"use client\"` component, and\nload the settings in the server surface (README §4).\n\n## The data\n\nA row:\n\n- `key` — unique in the list (required).\n- `label` — what the setting is (required).\n- `description` — a muted line under it.\n- `icon` — a kit `Icon` name in a muted square; or `media`, any node (an\n Avatar, a logo).\n- `badge` — `\"Current\"` or `{ label, tone?, hue?, icon? }` beside the label.\n `tone` for a status (\"Recommended\", success), `hue` 1–5 for a kind\n (\"Development\").\n- `value` — a line saying what is set now (\"+44 7700 900 461\", \"Not set\n up\"); `valueTone: \"set\"` draws it in full weight with a check.\n- `meta` — small lines under it: strings, or `{ icon, text }` (\"globe\n Lisbon\", \"clock Active now\").\n- `group` — rows with the same group sit under one heading, in the order\n the groups first appear.\n- `control` — one of:\n - `{ type: \"switch\", value: true }` — saves through `onChange(row, bool)`.\n - `{ type: \"select\", value, options: [{ value, label }] }` — saves\n through `onChange(row, value)`.\n - `{ type: \"button\", label, icon?, variant?, tone?, confirm? }` — runs\n `onAction(row, \"button\")` (or `key` if given).\n - `{ type: \"actions\", items: [{ key, label, icon?, tone?, confirm? }],\n menu? }` — up to two buttons, or a \"⋯\" menu for three or more (or\n `menu: true`); runs `onAction(row, item.key)`.\n - `{ type: \"remove\", label?, confirm? }` — an × button (sign a device out,\n disconnect); runs `onAction(row, \"remove\")`.\n - none — a row that only says something.\n `confirm` asks first, with the kit Confirm: `{ title, body?,\n confirmLabel?, tone?, consequences?, requireText?, acknowledge? }`.\n- `details` — a node, or `(row) => node`, that opens under the row from a\n chevron button (the connected account, a copyable code). `defaultOpen`\n starts it open.\n- `disabled` — the control is shown and cannot be used.\n\nThe matrix (`variant=\"matrix\"`) rows are events:\n\n- `key`, `label`, `description`, `group` — as above.\n- `values` — `{ email: true, push: false, sms: null }`, one per channel\n key. `null` (or missing) means the event is not sent on that channel: a\n dash, not a switch.\n- `locked` — channel keys that cannot be turned off (`[\"email\"]` for a\n security alert): the switch shows, disabled, with a lock.\n\n## Props\n\n- `rows` — the rows above. Keep them as the saved truth: when a save\n succeeds, update the row (or let the server revalidate). Until the rows\n change, the row shows the new value it saved.\n- `onChange(row, value, channel?)` — save a switch or a select (in the\n matrix, `channel` is the channel key). Return `{ ok: true }` or `{ ok:\n false, error }`; a throw counts as failed. Without it, switches and\n selects are shown and cannot be changed. In a Makersclaw app it is one\n line over `saveSetting` (lib/actions.js):\n `onChange={(row, value) => saveSetting({ source: \"
|
|
40
|
+
"content": "# SettingsList\n\nA card of settings, one row each: an icon, a label, a description, a few\nmeta lines, a badge, and the row's control. Every change saves on its own —\nno Save button. A flipped switch or a new choice shows at once, a spinner\nsits beside it while it saves, and when the save fails the old value comes\nback with the reason and Try again, on that row only. `variant=\"matrix\"`\nis the notification grid: events down the side, channels across the top.\n\n```jsx\nimport { SettingsList } from \"../components/blocks/settings-list\"; // a Makersclaw app (\"./blocks/settings-list\" from components/)\nimport { SettingsList } from \"@/components/makersclaw/blocks/settings-list\"; // a shadcn project\n```\n\nIt is a client component: render it from a `\"use client\"` component, and\nload the settings in the server surface (README §4).\n\n## The data\n\nA row:\n\n- `key` — unique in the list (required).\n- `label` — what the setting is (required).\n- `description` — a muted line under it.\n- `icon` — a kit `Icon` name in a muted square; or `media`, any node (an\n Avatar, a logo).\n- `badge` — `\"Current\"` or `{ label, tone?, hue?, icon? }` beside the label.\n `tone` for a status (\"Recommended\", success), `hue` 1–5 for a kind\n (\"Development\").\n- `value` — a line saying what is set now (\"+44 7700 900 461\", \"Not set\n up\"); `valueTone: \"set\"` draws it in full weight with a check.\n- `meta` — small lines under it: strings, or `{ icon, text }` (\"globe\n Lisbon\", \"clock Active now\").\n- `group` — rows with the same group sit under one heading, in the order\n the groups first appear.\n- `control` — one of:\n - `{ type: \"switch\", value: true }` — saves through `onChange(row, bool)`.\n - `{ type: \"select\", value, options: [{ value, label }] }` — saves\n through `onChange(row, value)`.\n - `{ type: \"button\", label, icon?, variant?, tone?, confirm? }` — runs\n `onAction(row, \"button\")` (or `key` if given).\n - `{ type: \"actions\", items: [{ key, label, icon?, tone?, confirm? }],\n menu? }` — up to two buttons, or a \"⋯\" menu for three or more (or\n `menu: true`); runs `onAction(row, item.key)`.\n - `{ type: \"remove\", label?, confirm? }` — an × button (sign a device out,\n disconnect); runs `onAction(row, \"remove\")`.\n - none — a row that only says something.\n `confirm` asks first, with the kit Confirm: `{ title, body?,\n confirmLabel?, tone?, consequences?, requireText?, acknowledge? }`.\n- `details` — a node, or `(row) => node`, that opens under the row from a\n chevron button (the connected account, a copyable code). `defaultOpen`\n starts it open.\n- `disabled` — the control is shown and cannot be used.\n\nThe matrix (`variant=\"matrix\"`) rows are events:\n\n- `key`, `label`, `description`, `group` — as above.\n- `values` — `{ email: true, push: false, sms: null }`, one per channel\n key. `null` (or missing) means the event is not sent on that channel: a\n dash, not a switch.\n- `locked` — channel keys that cannot be turned off (`[\"email\"]` for a\n security alert): the switch shows, disabled, with a lock.\n\n## Props\n\n- `rows` — the rows above. Keep them as the saved truth: when a save\n succeeds, update the row (or let the server revalidate). Until the rows\n change, the row shows the new value it saved.\n- `onChange(row, value, channel?)` — save a switch or a select (in the\n matrix, `channel` is the channel key). Return `{ ok: true }` or `{ ok:\n false, error }`; a throw counts as failed. Without it, switches and\n selects are shown and cannot be changed. In a Makersclaw app it is one\n line over `saveSetting` (lib/actions.js):\n `onChange={(row, value) => saveSetting({ source: \"settings\", key: row.key, value })}`.\n- `onAction(row, actionKey)` — run a button, a menu item or a remove.\n Return `{ ok }` like `onChange`. Remove the row from `rows` on success\n when it is gone (a signed-out device).\n- `variant` — `\"list\"` (default) or `\"matrix\"`.\n- `channels` — the matrix's columns: `[{ key, label, icon? }]`.\n- `title`, `description`, `icon` — the card's heading; `icon` in a muted\n square before it.\n- `badge` — a state beside the heading (`{ label: \"On\", tone: \"success\",\n icon: \"circle-check\" }`).\n- `actions` — nodes at the heading's end (\"Add profile\").\n- `footer` — a node under the rows (a Notice with a tip).\n- `loading` — skeleton rows.\n- `error` — the settings could not be loaded: a Notice in place of the\n rows; `onRetry` adds \"Try again\".\n- `emptyTitle` (`\"Nothing here yet\"`) · `emptyBody` · `emptyAction` —\n shown when `rows` is empty.\n- `disabled` — every control shown and none usable.\n- `failedText` — the reason shown when a save fails without an `error`.\n- `className` — layout around it (width, margin). Never colour.\n\n## Example\n\n```jsx\n// surfaces/preferences.jsx — client: settings, read and saved\n\"use client\";\nimport { useMemo } from \"react\";\nimport { useRows } from \"../lib/data\";\nimport { saveSetting } from \"../lib/actions\";\nimport { SettingsList } from \"../components/blocks/settings-list\";\n\nexport default function Preferences() {\n const { rows: saved, loading, error, reload } = useRows({ source: \"settings\" });\n // Built once per read: until `rows` changes, a row shows the value it saved.\n const rows = useMemo(() => {\n const settings = Object.fromEntries(saved.map((s) => [s.key, s.value]));\n return [\n { key: \"digest\", icon: \"mail\", label: \"Weekly digest\", description: \"Every Monday at 8:00.\", control: { type: \"switch\", value: settings.digest === true } },\n {\n key: \"currency\",\n icon: \"dollar\",\n label: \"Currency\",\n control: { type: \"select\", value: settings.currency ?? \"usd\", options: [{ value: \"usd\", label: \"US dollar\" }, { value: \"eur\", label: \"Euro\" }] },\n },\n ];\n }, [saved]);\n return (\n <SettingsList\n title=\"Preferences\"\n rows={rows}\n loading={loading}\n error={error}\n onRetry={reload}\n onChange={(row, value) => saveSetting({ source: \"settings\", key: row.key, value })}\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\nNotifications per channel:\n\n```jsx\n<SettingsList\n variant=\"matrix\"\n title=\"Notifications\"\n channels={[{ key: \"email\", label: \"Email\", icon: \"mail\" }, { key: \"push\", label: \"Push\", icon: \"smartphone\" }]}\n rows={[\n { key: \"mentions\", group: \"Conversations\", label: \"Mentions\", values: { email: true, push: true } },\n { key: \"signin\", group: \"Security\", label: \"New sign-in\", values: { email: true, push: false }, locked: [\"email\"] },\n ]}\n onChange={(row, on, channel) => saveSetting({ mode: \"json\", source: \"members\", column: \"notify\", id: me.id, key: `${row.key}.${channel}`, value: on })}\n/>\n```\n\nSigned-in devices, each with sign-out:\n\n```jsx\n<SettingsList\n icon=\"shield-check\"\n title=\"Signed-in devices\"\n rows={sessions.map((s) => ({\n key: s.id,\n icon: s.kind === \"phone\" ? \"smartphone\" : \"laptop\",\n label: s.device,\n badge: s.current ? { label: \"This device\", tone: \"info\" } : null,\n meta: [{ icon: \"globe\", text: s.place }, { icon: \"clock\", text: s.lastSeen }],\n control: s.current ? null : { type: \"remove\", label: `Sign out ${s.device}`, confirm: { title: \"Sign out this device?\", confirmLabel: \"Sign out\" } },\n }))}\n onAction={(row) => deleteRow({ source: \"sessions\", id: row.key })}\n/>\n```\n\n## What it will not do\n\n- Save several settings with one button: that is `record-form`.\n- Open a setup flow: a button's `onAction` is yours — open your `Sheet`\n from it and return `{ ok: true }`.\n- Scroll the matrix sideways on a phone: under 768px each event's switches\n stack under it, named by channel.\n\nA settings screen it cannot be is your own component in `components/`,\nbuilt from the kit — not an edit to this block. Its files are fixed: `npm\nrun check` refuses a change, and `makersclaw add settings-list` restores\nthem.\n\n## Credits\n\nShapes from Creative Tim UI (https://github.com/creativetimofficial/ui, MIT); this is a rewrite on the Makersclaw kit.\n"
|
|
41
41
|
}
|
|
42
42
|
],
|
|
43
43
|
"categories": [
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"docs": "Installed to components/makersclaw/blocks/settings-list. Import: import { SettingsList } from \"@/components/makersclaw/blocks/settings-list\". Props and an example: components/makersclaw/blocks/settings-list/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
60
60
|
"meta": {
|
|
61
61
|
"makersclaw": {
|
|
62
|
-
"version": "0.
|
|
62
|
+
"version": "0.8.0",
|
|
63
63
|
"useWhen": "An account or workspace settings screen where each setting applies the moment it is changed: preferences, two-step sign-in methods, signed-in devices, connected integrations, billing profiles, notification preferences per channel. Not for a form of fields saved together with one button (record-form)."
|
|
64
64
|
}
|
|
65
65
|
}
|
package/r/setup-checklist.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "SetupChecklist",
|
|
6
6
|
"description": "The steps to get set up: which are done, how far along the whole is (a ring, a bar, or \"Step 2 of 4\"), and the button for the next one, sent out through an async onAction that can fail on the step. Four shapes: folding rows in a card with a dismiss menu, numbered step cards, a plain step list with a help panel, and bordered accordion rows that can be locked until the step before is done.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
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/setup-checklist/README.md",
|
|
26
26
|
"type": "registry:file",
|
|
27
27
|
"target": "@components/makersclaw/blocks/setup-checklist/README.md",
|
|
28
|
-
"content": "# SetupChecklist\n\nThe steps to get set up, which of them are done, how far along the whole\nis, and the button for the next one. The steps are yours: their `done`\ncomes from the app's data (the profile has a name, a teammate was\ninvited), so the list is never a second record of the truth. A step's\nbutton goes out through `onAction`; while it runs the button spins, and\nwhen it fails the step says why.\n\n```jsx\nimport { SetupChecklist } from \"../components/blocks/setup-checklist\"; // a Makersclaw app (\"./blocks/setup-checklist\" from components/)\nimport { SetupChecklist } from \"@/components/makersclaw/blocks/setup-checklist\"; // a shadcn project\n```\n\nA client component. Work out `done` for each step in the server surface\n(README §4) and pass the steps in.\n\n## The data\n\n`steps` — in order:\n`[{ key, title, description?, actionLabel?, href?, icon?, summary?, done }]`.\n\n- `key` — unique, stable.\n- `actionLabel` — the step's button (\"Send invites\"); default \"Start\"\n (\"Open\" once done).\n- `href` — the button is a link to the page where the step is done;\n `onAction` still hears about the click.\n- `icon` — a kit `Icon` name: on the right of a card and in its button\n (`cards`), above the text (`accordion`).\n- `summary` — `accordion`: the bold line in the step's panel.\n\nThe next step is the first one not done.\n\n## Props\n\n- `steps` — as above. Required.\n- `variant` — default `\"collapsible\"`:\n - `collapsible` — a card: the title, a ring with \"2 of 6 done\", a \"⋯\"\n menu (Dismiss and `menuItems`), then each step a folding row with its\n description and button. The next step starts open, and when the open\n step gets done the next one opens.\n - `cards` — the title, \"1 of 4 done\" and a bar, then a numbered card\n per step. Only the next step shows its button.\n - `list` — the title, \"Step 2 of 4\" and a short bar, then a row per\n step, numbered \"1.\", \"2.\"; the next one is outlined and shows its\n button. `footer` sits in a filled panel below (\"Need a hand?\").\n - `accordion` — each step a bordered folding row; its panel is centred:\n icon, `summary`, description, button. Pair it with `sequential`.\n- `onAction(step)` — the step's button (or a click on its link). Do the\n thing, or open your `Sheet` for it; return `{ ok: true }` or\n `{ ok: false, error }` (a throw counts as failed) and the step shows the\n error under its button. Mark it done by passing the step back with\n `done: true`; the block never marks a step itself. Without `onAction`\n only steps with an `href` have a button.\n- `sequential` — a step's button stays disabled, with \"Finish the steps\n before this one first\", until every step before it is done. Default\n `false`: any step can be done in any order.\n- `title` — default \"Get set up\". `description` — a line under it.\n- `onDismiss()` — adds Dismiss to the \"⋯\" menu (`collapsible` shows the\n menu in its header, the others beside the title). Hide the checklist\n and remember that you did; return `{ ok: false, error }` to show why it\n could not.\n- `menuItems` — more \"⋯\" items, kit `Menu` shape: `[{ label, onSelect,\n icon? }]` (\"Send feedback\").\n- `footer` — a node under the steps: help, a link to the docs.\n- `doneLabel` — what the header says when every step is done, default\n \"All set\" (the bar turns to the success tone).\n- `loading` — skeleton rows while the steps load.\n- `error` — a string: the steps did not load. Shown in a notice with\n Retry when `onRetry` is given.\n- `className` — layout around it (width, margin). Never colour.\n\nWith no steps it says \"Nothing to set up\".\n\n## Example\n\n```jsx\n// surfaces/home.jsx —
|
|
28
|
+
"content": "# SetupChecklist\n\nThe steps to get set up, which of them are done, how far along the whole\nis, and the button for the next one. The steps are yours: their `done`\ncomes from the app's data (the profile has a name, a teammate was\ninvited), so the list is never a second record of the truth. A step's\nbutton goes out through `onAction`; while it runs the button spins, and\nwhen it fails the step says why.\n\n```jsx\nimport { SetupChecklist } from \"../components/blocks/setup-checklist\"; // a Makersclaw app (\"./blocks/setup-checklist\" from components/)\nimport { SetupChecklist } from \"@/components/makersclaw/blocks/setup-checklist\"; // a shadcn project\n```\n\nA client component. Work out `done` for each step in the server surface\n(README §4) and pass the steps in.\n\n## The data\n\n`steps` — in order:\n`[{ key, title, description?, actionLabel?, href?, icon?, summary?, done }]`.\n\n- `key` — unique, stable.\n- `actionLabel` — the step's button (\"Send invites\"); default \"Start\"\n (\"Open\" once done).\n- `href` — the button is a link to the page where the step is done;\n `onAction` still hears about the click.\n- `icon` — a kit `Icon` name: on the right of a card and in its button\n (`cards`), above the text (`accordion`).\n- `summary` — `accordion`: the bold line in the step's panel.\n\nThe next step is the first one not done.\n\n## Props\n\n- `steps` — as above. Required.\n- `variant` — default `\"collapsible\"`:\n - `collapsible` — a card: the title, a ring with \"2 of 6 done\", a \"⋯\"\n menu (Dismiss and `menuItems`), then each step a folding row with its\n description and button. The next step starts open, and when the open\n step gets done the next one opens.\n - `cards` — the title, \"1 of 4 done\" and a bar, then a numbered card\n per step. Only the next step shows its button.\n - `list` — the title, \"Step 2 of 4\" and a short bar, then a row per\n step, numbered \"1.\", \"2.\"; the next one is outlined and shows its\n button. `footer` sits in a filled panel below (\"Need a hand?\").\n - `accordion` — each step a bordered folding row; its panel is centred:\n icon, `summary`, description, button. Pair it with `sequential`.\n- `onAction(step)` — the step's button (or a click on its link). Do the\n thing, or open your `Sheet` for it; return `{ ok: true }` or\n `{ ok: false, error }` (a throw counts as failed) and the step shows the\n error under its button. Mark it done by passing the step back with\n `done: true`; the block never marks a step itself. Without `onAction`\n only steps with an `href` have a button.\n- `sequential` — a step's button stays disabled, with \"Finish the steps\n before this one first\", until every step before it is done. Default\n `false`: any step can be done in any order.\n- `title` — default \"Get set up\". `description` — a line under it.\n- `onDismiss()` — adds Dismiss to the \"⋯\" menu (`collapsible` shows the\n menu in its header, the others beside the title). Hide the checklist\n and remember that you did; return `{ ok: false, error }` to show why it\n could not.\n- `menuItems` — more \"⋯\" items, kit `Menu` shape: `[{ label, onSelect,\n icon? }]` (\"Send feedback\").\n- `footer` — a node under the steps: help, a link to the docs.\n- `doneLabel` — what the header says when every step is done, default\n \"All set\" (the bar turns to the success tone).\n- `loading` — skeleton rows while the steps load.\n- `error` — a string: the steps did not load. Shown in a notice with\n Retry when `onRetry` is given.\n- `className` — layout around it (width, margin). Never colour.\n\nWith no steps it says \"Nothing to set up\".\n\n## Example\n\n```jsx\n// surfaces/home.jsx — client: work out what is done\n\"use client\";\nimport { useCount } from \"../lib/data\";\nimport GettingStarted from \"../components/GettingStarted\";\n\nexport default function Home() {\n const members = useCount({ source: \"members\" });\n const deals = useCount({ source: \"deals\" });\n return <GettingStarted loading={members.loading || deals.loading} invited={members.count > 1} hasDeal={deals.count > 0} />;\n}\n```\n\n```jsx\n// components/GettingStarted.jsx — client\n\"use client\";\nimport { SetupChecklist } from \"./blocks/setup-checklist\";\n\nexport default function GettingStarted({ loading, invited, hasDeal, onHide }) {\n return (\n <SetupChecklist\n title=\"Get started\"\n loading={loading}\n steps={[\n { key: \"invite\", title: \"Invite your team\", description: \"Bring in the people who own accounts.\", actionLabel: \"Invite\", href: \"/team\", done: invited },\n { key: \"deal\", title: \"Add your first deal\", description: \"One deal is enough to see the pipeline work.\", actionLabel: \"Add a deal\", href: \"/deals?new=1\", done: hasDeal },\n ]}\n onDismiss={onHide}\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\nIn order, with buttons that do the work in place:\n\n```jsx\n<SetupChecklist\n variant=\"accordion\"\n sequential\n title=\"Quick start\"\n steps={steps}\n onAction={(step) => runAction({ name: \"run_setup_step\", args: { key: step.key } })} // an action named in references/actions.md; its ok re-reads every hook, so the step comes back done\n/>\n```\n\n## What it will not do\n\n- Decide a step is done: `done` is yours, from your data.\n- Remember that it was dismissed: `onDismiss` is where you save that.\n- Nest steps inside steps, or show a running job's stages: that is the kit\n `Steps` or the `job-panel` block.\n- Page through a wizard of forms: each step's button goes where the work\n is.\n\nA checklist 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 setup-checklist` restores\nthem.\n\n## Credits\n\nPorted from blocks.so (https://blocks.so, onboarding 01–04, MIT), rewritten on the Makersclaw kit.\n"
|
|
29
29
|
}
|
|
30
30
|
],
|
|
31
31
|
"categories": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"docs": "Installed to components/makersclaw/blocks/setup-checklist. Import: import { SetupChecklist } from \"@/components/makersclaw/blocks/setup-checklist\". Props and an example: components/makersclaw/blocks/setup-checklist/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
45
45
|
"meta": {
|
|
46
46
|
"makersclaw": {
|
|
47
|
-
"version": "0.
|
|
47
|
+
"version": "0.8.0",
|
|
48
48
|
"useWhen": "A new account, workspace or shop has a few things to do before it is useful (complete the profile, invite the team, connect a tool, launch) and the founder should see what is left. Not for a running job's stages (kit Steps or job-panel), and not for a multi-page form wizard."
|
|
49
49
|
}
|
|
50
50
|
}
|
package/r/share-dialog.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "ShareDialog",
|
|
6
6
|
"description": "Who can see a record and how to let more people in: invite by email (comma separated, with an access level), each person with their access in a menu (can view, can edit, remove), sharing switches, and a link with a copy button that turns to a check. A dialog, or the same body inline (SharePanel). Every change is an async callback; a failure shows and the old value comes back.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
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/share-dialog/README.md",
|
|
26
26
|
"type": "registry:file",
|
|
27
27
|
"target": "@components/makersclaw/blocks/share-dialog/README.md",
|
|
28
|
-
"content": "# ShareDialog\n\nWho can see a record, and how to let more people in. An invite field\nthat takes several emails at once (with an access level), the people who\nalready have access (each with a menu: can view, can edit, remove),\nsharing switches (\"Allow comments\"), and a link with a copy button that\nturns to a check. Each section shows when you give its data or callback.\n`ShareDialog` puts it in the kit's `Sheet` (a dialog; a bottom sheet on a\nphone); `SharePanel` is the same body inline, for a settings page.\n\nEvery change goes out through an async callback that returns `{ ok: true }`\nor `{ ok: false, error }`. A role change or a switch shows at once and goes\nback when the save fails, with the reason on screen.\n\n```jsx\nimport { ShareDialog, SharePanel } from \"../components/blocks/share-dialog\"; // a Makersclaw app (\"./blocks/share-dialog\" from components/)\nimport { ShareDialog, SharePanel } from \"@/components/makersclaw/blocks/share-dialog\"; // a shadcn project\n```\n\nA client component: render it from a `\"use client\"` component.\n\n## The data\n\n`people` is who has access now: `[{ id?, name?, email, src?, role, roleLabel?, locked?, pending? }]`.\n\n- `role` is one of `roles`' values. A role outside them (an owner) shows\n as a badge, with `roleLabel` as its words.\n- `locked: true` — no menu for this person (the owner, yourself).\n- `pending: true` — invited, not joined yet: \"Invite sent\" or an\n \"Invited\" badge.\n- `src` — a photo; the kit's `Avatar` shows initials without one.\n\n## Props\n\nBoth components take these. Everything is optional; a section shows only\nwhen its data or callback is there.\n\n- `people` — the list above. `[]` shows `empty`; leave it out to hide the\n section.\n- `roles` — the access levels, in order: `[{ value, label }]`. Default\n `[{ value: \"view\", label: \"Can view\" }, { value: \"edit\", label: \"Can edit\" }]`.\n- `onInvite(emails, role)` — shows the invite field. Async, `{ ok, error }`.\n The field splits on commas, spaces and semicolons, drops duplicates and\n people who already have access, and refuses anything that is not an\n email before it calls you. Add the invited people to `people` yourself\n (with `pending: true`). With two or more `roles`, a picker beside the\n field chooses the role.\n- `inviteRole` — every invite gets this role, and no picker shows.\n- `onRoleChange(person, role)` — each person's access becomes a menu.\n Async, `{ ok, error }`. Update `people` on success.\n- `onRemove(person)` — \"Remove access\" in that menu. Async. Take the\n person out of `people` on success.\n- `link` — a URL to share: a read-only field with a copy button, then a\n \"Copy link\" button. Copying uses the browser's clipboard. A blocked\n clipboard shows a message.\n- `onCopy(link)` — called after a copy (analytics, \"link created\"). Async\n and optional; a failure shows.\n- `previewHref` or `onPreview` — a \"Preview\" button beside \"Copy link\".\n- `settings` — switches: `[{ id, label, description?, icon?, checked, disabled? }]`.\n- `onSettingChange(id, next)` — async, `{ ok, error }`. The switch goes\n back when it fails.\n- `youId` — the `id` (or email) of the person looking: \"(you)\" after their name.\n- `inviteLabel` · `invitePlaceholder` · `peopleLabel` · `linkLabel` ·\n `empty` — the words. Defaults: \"Invite by email\", \"Emails, separated by\n commas\", \"People with access\", \"Share link\", \"Only you have access so far.\"\n- `className` — on the panel.\n\n`ShareDialog` also takes:\n\n- `open` · `onClose()` — controlled. Esc, a press outside and the ✕ close it.\n- `title` — default \"Share\". `description` — a line under it.\n- `footer` — buttons under the body (a \"Done\").\n\n## Example\n\n```jsx\n\"use client\";\nimport { useState } from \"react\";\nimport { Button } from \"../components/ui\";\nimport { ShareDialog } from \"../components/blocks/share-dialog\";\n\nexport function ShareProject({ project, members, invite, setRole, remove }) {\n const [open, setOpen] = useState(false);\n return (\n <>\n <Button icon=\"share\" onClick={() => setOpen(true)}>Share</Button>\n <ShareDialog\n open={open}\n onClose={() => setOpen(false)}\n title={`Share “${project.name}”`}\n people={members}\n onInvite={(emails, role) => invite(project.id, emails, role)} //
|
|
28
|
+
"content": "# ShareDialog\n\nWho can see a record, and how to let more people in. An invite field\nthat takes several emails at once (with an access level), the people who\nalready have access (each with a menu: can view, can edit, remove),\nsharing switches (\"Allow comments\"), and a link with a copy button that\nturns to a check. Each section shows when you give its data or callback.\n`ShareDialog` puts it in the kit's `Sheet` (a dialog; a bottom sheet on a\nphone); `SharePanel` is the same body inline, for a settings page.\n\nEvery change goes out through an async callback that returns `{ ok: true }`\nor `{ ok: false, error }`. A role change or a switch shows at once and goes\nback when the save fails, with the reason on screen.\n\n```jsx\nimport { ShareDialog, SharePanel } from \"../components/blocks/share-dialog\"; // a Makersclaw app (\"./blocks/share-dialog\" from components/)\nimport { ShareDialog, SharePanel } from \"@/components/makersclaw/blocks/share-dialog\"; // a shadcn project\n```\n\nA client component: render it from a `\"use client\"` component.\n\n## The data\n\n`people` is who has access now: `[{ id?, name?, email, src?, role, roleLabel?, locked?, pending? }]`.\n\n- `role` is one of `roles`' values. A role outside them (an owner) shows\n as a badge, with `roleLabel` as its words.\n- `locked: true` — no menu for this person (the owner, yourself).\n- `pending: true` — invited, not joined yet: \"Invite sent\" or an\n \"Invited\" badge.\n- `src` — a photo; the kit's `Avatar` shows initials without one.\n\n## Props\n\nBoth components take these. Everything is optional; a section shows only\nwhen its data or callback is there.\n\n- `people` — the list above. `[]` shows `empty`; leave it out to hide the\n section.\n- `roles` — the access levels, in order: `[{ value, label }]`. Default\n `[{ value: \"view\", label: \"Can view\" }, { value: \"edit\", label: \"Can edit\" }]`.\n- `onInvite(emails, role)` — shows the invite field. Async, `{ ok, error }`.\n The field splits on commas, spaces and semicolons, drops duplicates and\n people who already have access, and refuses anything that is not an\n email before it calls you. Add the invited people to `people` yourself\n (with `pending: true`). With two or more `roles`, a picker beside the\n field chooses the role.\n- `inviteRole` — every invite gets this role, and no picker shows.\n- `onRoleChange(person, role)` — each person's access becomes a menu.\n Async, `{ ok, error }`. Update `people` on success.\n- `onRemove(person)` — \"Remove access\" in that menu. Async. Take the\n person out of `people` on success.\n- `link` — a URL to share: a read-only field with a copy button, then a\n \"Copy link\" button. Copying uses the browser's clipboard. A blocked\n clipboard shows a message.\n- `onCopy(link)` — called after a copy (analytics, \"link created\"). Async\n and optional; a failure shows.\n- `previewHref` or `onPreview` — a \"Preview\" button beside \"Copy link\".\n- `settings` — switches: `[{ id, label, description?, icon?, checked, disabled? }]`.\n- `onSettingChange(id, next)` — async, `{ ok, error }`. The switch goes\n back when it fails.\n- `youId` — the `id` (or email) of the person looking: \"(you)\" after their name.\n- `inviteLabel` · `invitePlaceholder` · `peopleLabel` · `linkLabel` ·\n `empty` — the words. Defaults: \"Invite by email\", \"Emails, separated by\n commas\", \"People with access\", \"Share link\", \"Only you have access so far.\"\n- `className` — on the panel.\n\n`ShareDialog` also takes:\n\n- `open` · `onClose()` — controlled. Esc, a press outside and the ✕ close it.\n- `title` — default \"Share\". `description` — a line under it.\n- `footer` — buttons under the body (a \"Done\").\n\n## Example\n\n```jsx\n\"use client\";\nimport { useState } from \"react\";\nimport { Button } from \"../components/ui\";\nimport { ShareDialog } from \"../components/blocks/share-dialog\";\n\nexport function ShareProject({ project, members, invite, setRole, remove }) {\n const [open, setOpen] = useState(false);\n return (\n <>\n <Button icon=\"share\" onClick={() => setOpen(true)}>Share</Button>\n <ShareDialog\n open={open}\n onClose={() => setOpen(false)}\n title={`Share “${project.name}”`}\n people={members}\n onInvite={(emails, role) => invite(project.id, emails, role)} // each answers { ok } or { ok: false, error }, as lib/actions' writes do\n onRoleChange={(person, role) => setRole(project.id, person.id, role)}\n onRemove={(person) => remove(project.id, person.id)}\n link={`https://app.example.com/p/${project.id}`}\n />\n </>\n );\n}\n```\n\n## What it will not do\n\n- Send the invite email or create the access row: `onInvite` does.\n- Search your directory as you type. The field takes addresses; for a\n people picker, use the kit's `Combobox`.\n- Make the link public or private by itself. Put that switch in `settings`\n and change `link` from your callback.\n- Show a person's access per page or field. It has one role per person.\n\n## Credits\n\nPorted from blocks.so (https://blocks.so, dialogs 08 and 09, MIT) and Creative Tim UI (https://github.com/creativetimofficial/ui, modals-03, MIT), rewritten on the Makersclaw kit.\n"
|
|
29
29
|
}
|
|
30
30
|
],
|
|
31
31
|
"categories": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"docs": "Installed to components/makersclaw/blocks/share-dialog. Import: import { ShareDialog, SharePanel } from \"@/components/makersclaw/blocks/share-dialog\". Props and an example: components/makersclaw/blocks/share-dialog/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
46
46
|
"meta": {
|
|
47
47
|
"makersclaw": {
|
|
48
|
-
"version": "0.
|
|
48
|
+
"version": "0.8.0",
|
|
49
49
|
"useWhen": "A record, project, document or workspace can be shared: an \"Invite\" or \"Share\" button, a members list with roles, a public link to copy."
|
|
50
50
|
}
|
|
51
51
|
}
|
package/r/sidebar.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Sidebar",
|
|
6
6
|
"description": "An app's navigation down the left edge: app name or workspace switcher, groups of items with icons, counts and nested items, the signed-in person with their menu; collapses to icons (remembered) and becomes a Sheet behind a trigger on a phone. Optional: inset or floating look, a ⌘K search over every item, folding groups with colour dots, a second column (a mail list, or a parent's children to drill into), a \"New\" button and a \"…\" menu per item (a chat history).",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"path": "registry/blocks/sidebar/README.md",
|
|
32
32
|
"type": "registry:file",
|
|
33
33
|
"target": "@components/makersclaw/blocks/sidebar/README.md",
|
|
34
|
-
"content": "# Sidebar\n\nAn app's navigation down the left edge: the app's name (or a workspace\nswitcher), groups of items with icons, counts and nested items, a slot of\nyour own, and the signed-in person at the foot with their menu. On a wide\nwindow it collapses to icons — every label stays, as a tooltip — and\nremembers that. On a phone it is not a column at all: it is a Sheet that\n`SidebarTrigger` opens (from the leading edge; a bottom sheet under\n640px), and it closes when an item is chosen.\n\n```jsx\nimport { Sidebar, SidebarLayout, SidebarTrigger } from \"../components/blocks/sidebar\"; // a Makersclaw app (\"./blocks/sidebar\" from components/)\nimport { Sidebar, SidebarLayout, SidebarTrigger } from \"@/components/makersclaw/blocks/sidebar\"; // a shadcn project\n```\n\nIt is a client component. In a Makersclaw app it belongs in\n`app/shell.jsx`, around the surfaces — the shell's nav is what it replaces.\n\n## The data\n\n`groups` is a list of `{ label?, items }`. An item:\n\n- `label` — what it says (required).\n- `href` — where it goes; rendered as a link (base path applied). Or\n- `onSelect(item)` — what choosing it does. Both may be given.\n- `icon` — a kit icon name. Needed for the icon rail (a missing one shows\n `file`).\n- `active` — true on the current page: `aria-current=\"page\"` and the\n highlighted look. The parent of an active item is open to begin with.\n- `badge` — a count or a word, shown on the right; a dot in the rail.\n- `items` — nested items (same shape, one level): the item becomes a\n collapsible row. In the rail its icon widens the sidebar with it open.\n With `drilldown` it opens them instead (see Sidebar).\n- `hue` — 1..5: a colour dot in place of the icon (a favourite, a label).\n- `description` — a muted second line (shown on children in drill-in).\n- `actions` — kit `Menu` items: a \"…\" at the row's end, shown on hover\n or focus (always on a touch screen). Each `onSelect(item)` may be async\n and return `{ ok, error? }`; `{ ok: false }` or a throw shows an error\n toast. Not drawn in the icon rail.\n- `keywords` — extra words the ⌘K search matches, never shown.\n- `disabled` · `key` (default `href`, then `label`).\n\nA group is `{ label?, items, collapsible?, defaultOpen?, key? }`.\n`collapsible: true` makes its heading fold it; `defaultOpen` (default\ntrue) is where it starts. The icon rail shows every group unfolded.\n\n## Props\n\n### SidebarLayout\n\n- `sidebar` — the `<Sidebar>` element.\n- `children` — the page beside it. Put `<SidebarTrigger />` in its header.\n- `storageKey` — remember collapsed/expanded in this browser under this\n key. Optional; without it every visit starts at `defaultCollapsed`.\n- `defaultCollapsed` — default `false`.\n- `collapsed` + `onCollapsedChange(next)` — control it yourself instead.\n- `shortcut` — Cmd/Ctrl + this key toggles it; default `\"b\"`, `null` for none.\n- `variant` — `\"inset\"` draws the page as a card on the frame's ground.\n Default: the Sidebar's own `variant`, so you rarely pass it here.\n- `className` — the frame's
|
|
34
|
+
"content": "# Sidebar\n\nAn app's navigation down the left edge: the app's name (or a workspace\nswitcher), groups of items with icons, counts and nested items, a slot of\nyour own, and the signed-in person at the foot with their menu. On a wide\nwindow it collapses to icons — every label stays, as a tooltip — and\nremembers that. On a phone it is not a column at all: it is a Sheet that\n`SidebarTrigger` opens (from the leading edge; a bottom sheet under\n640px), and it closes when an item is chosen.\n\n```jsx\nimport { Sidebar, SidebarLayout, SidebarTrigger } from \"../components/blocks/sidebar\"; // a Makersclaw app (\"./blocks/sidebar\" from components/)\nimport { Sidebar, SidebarLayout, SidebarTrigger } from \"@/components/makersclaw/blocks/sidebar\"; // a shadcn project\n```\n\nIt is a client component. In a Makersclaw app it belongs in\n`app/shell.jsx`, around the surfaces — the shell's nav is what it replaces.\n\n## The data\n\n`groups` is a list of `{ label?, items }`. An item:\n\n- `label` — what it says (required).\n- `href` — where it goes; rendered as a link (base path applied). Or\n- `onSelect(item)` — what choosing it does. Both may be given.\n- `icon` — a kit icon name. Needed for the icon rail (a missing one shows\n `file`).\n- `active` — true on the current page: `aria-current=\"page\"` and the\n highlighted look. The parent of an active item is open to begin with.\n- `badge` — a count or a word, shown on the right; a dot in the rail.\n- `items` — nested items (same shape, one level): the item becomes a\n collapsible row. In the rail its icon widens the sidebar with it open.\n With `drilldown` it opens them instead (see Sidebar).\n- `hue` — 1..5: a colour dot in place of the icon (a favourite, a label).\n- `description` — a muted second line (shown on children in drill-in).\n- `actions` — kit `Menu` items: a \"…\" at the row's end, shown on hover\n or focus (always on a touch screen). Each `onSelect(item)` may be async\n and return `{ ok, error? }`; `{ ok: false }` or a throw shows an error\n toast. Not drawn in the icon rail.\n- `keywords` — extra words the ⌘K search matches, never shown.\n- `disabled` · `key` (default `href`, then `label`).\n\nA group is `{ label?, items, collapsible?, defaultOpen?, key? }`.\n`collapsible: true` makes its heading fold it; `defaultOpen` (default\ntrue) is where it starts. The icon rail shows every group unfolded.\n\n## Props\n\n### SidebarLayout\n\n- `sidebar` — the `<Sidebar>` element.\n- `children` — the page beside it. Put `<SidebarTrigger />` in its header.\n- `storageKey` — remember collapsed/expanded in this browser under this\n key. Optional; without it every visit starts at `defaultCollapsed`.\n- `defaultCollapsed` — default `false`.\n- `collapsed` + `onCollapsedChange(next)` — control it yourself instead.\n- `shortcut` — Cmd/Ctrl + this key toggles it; default `\"b\"`, `null` for none.\n- `variant` — `\"inset\"` draws the page as a card on the frame's ground.\n Default: the Sidebar's own `variant`, so you rarely pass it here.\n- `className` — the frame's edges, and its height from its parent: in a\n shell it sits in the kit's `AppLayout contentMode=\"fill\"` with\n `min-h-0 flex-1` (the Example). The kit's `AppLayout` owns the\n window's height; `min-h-dvh` or `h-dvh` here is refused by `npm run\n check`. Never colour.\n\n### Sidebar\n\n- `groups` — see The data. `items` is the one-group shorthand.\n- `title` — the app's or workspace's name, at the top and on the sheet.\n- `subtitle` — a muted line under it (a plan, a team).\n- `logo` — a node beside the title; default a square `Avatar` of `title`.\n- `switcher` — kit `Menu` items (`[{ label, onSelect, icon? }]`): the title\n becomes a menu for changing workspace. `switcherLabel` names it\n (default \"Switch workspace\"); a reader hears \"<title>, <switcherLabel>\",\n so a voice user who says the title still hits it.\n- `user` — `{ name, email?, src? }`, shown at the foot with an `Avatar`.\n- `userMenu` — kit `Menu` items (`[{ label, onSelect, icon?, tone?: \"danger\",\n disabled? }]`, and groups and separators): the person becomes a menu\n button opening these upward (beside the rail when it is icons), flipping\n where there is no room. A choice also closes the phone sheet.\n- `children` — your own content under the groups (a target, a promo, a\n help link). Hidden in the icon rail.\n- `label` — the nav landmark's name, default \"Main\".\n- `className` — layout only. Never colour.\n\nShapes and extras (all optional; leave them out and it is the column\nabove):\n\n- `variant` — `\"sidebar\"` (default: a bordered column) · `\"inset\"` (no\n border; the page beside it becomes a rounded card) · `\"floating\"` (a\n rounded card held off the window's edge). A phone is a sheet either way.\n- `headerActions` — a node at the top right: a notifications `Menu`, a\n button. Hidden in the icon rail; first row of the phone sheet.\n- `switcherPlacement` — `\"header\"` (default) · `\"footer\"`: the switcher\n moves to the foot, over the person, and the top keeps only `logo`.\n- `search` — `true` or `{ placeholder?, hotkey?, label?, empty?,\n groupLabel? }`: a search row under the top that opens the kit\n `CommandDialog` over every item (children listed under their parent's\n name). `hotkey` defaults to `\"k\"` (⌘K / Ctrl+K), `null` for none. A\n palette choice runs the item's `onSelect`; an item with only `href`\n is followed as a page load (the app's base path applied). Give items\n `onSelect` with `router.push` for a client-side jump.\n- `primaryAction` — `{ label, icon?, onSelect?, href? }`: one button above\n the groups (\"New chat\"). `onSelect` may return `{ ok: false, error }`,\n shown as a toast.\n- `footerActions` — kit `Menu` items behind a \"+\" beside the person (New\n deal, Invite). `footerActionsLabel` names it, default \"Create\". Same\n `{ ok }` rule as `actions`.\n- `drilldown` — `\"inline\"` (default: a parent folds open) · `\"panel\"`\n (its children open in a second column with a close button; Esc closes\n it; the icon rail stays narrow beside it) · `\"replace\"` (its children\n replace the list, under a Back row). The parent holding the active\n item starts open. On a phone both are the replace form.\n- `panel` — a second column beside the nav from 768px, 320px wide: your\n own node, or `{ title?, actions?, search?, items, loading?, error?,\n onRetry?, empty?, onSelect?, query?, onQueryChange?, filter? }`.\n - `items`: `[{ key?, title, subtitle?, description?, meta?, icon?,\n media?, active?, href?, onSelect?, disabled? }]` — a mail is sender\n (title), date (meta), subject (subtitle), teaser (description).\n - `search`: `true` or `{ placeholder }`: a field that filters the items\n here; `filter: false` when you filter them yourself from\n `onQueryChange(query)`.\n - `loading` draws skeleton rows, `error` (text) a notice with Try\n again (`onRetry`), and no items the `empty` line.\n - On a phone it is the sheet's second screen: choosing a nav item (or\n the panel's own row at the top) shows it, with a Back row.\n\n### SidebarTrigger\n\nThe header button: opens the sheet on a phone, collapses to icons from\n768px. `className` for placement.\n\n### useSidebar()\n\n`{ collapsed, setCollapsed, toggle, mobileOpen, setMobileOpen }` inside a\n`SidebarLayout`, for a control of your own; `null` outside one.\n\n## Example\n\n```jsx\n// app/shell.jsx — the app's shell, the surfaces as the sidebar's items\n\"use client\";\nimport { usePathname } from \"next/navigation\";\nimport { AppLayout } from \"../components/ui\";\nimport { Sidebar, SidebarLayout, SidebarTrigger } from \"../components/blocks/sidebar\";\n\nconst ICONS = { inbox: \"inbox\", deals: \"dollar\", contacts: \"users\", reports: \"chart\" };\n\nexport default function Shell({ name, surfaces, children }) {\n const pathname = usePathname() ?? \"\";\n const activeKey = decodeURIComponent(/^\\/s\\/([^/?#]+)/.exec(pathname)?.[1] ?? \"\");\n const items = surfaces.map((s) => ({\n key: s.key,\n label: s.label,\n href: `/s/${encodeURIComponent(s.key)}`,\n icon: ICONS[s.key] ?? \"file\",\n active: s.key === activeKey,\n badge: s.built ? undefined : \"planned\",\n }));\n // The outer AppLayout owns the window; the inner one (it fills its\n // slot) is the content that scrolls, back to its top on a new route.\n return (\n <AppLayout contentMode=\"fill\">\n <SidebarLayout\n storageKey={`${name}.sidebar`}\n className=\"min-h-0 flex-1\"\n sidebar={<Sidebar title={name} items={items} label={`${name} surfaces`} />}\n >\n <header className=\"flex min-h-14 items-center gap-2 border-b border-border px-2 md:hidden\">\n <SidebarTrigger />\n <span className=\"font-semibold\">{name}</span>\n </header>\n <AppLayout contentClassName=\"p-4 sm:p-6\" scrollKey={pathname}>\n {children}\n </AppLayout>\n </SidebarLayout>\n </AppLayout>\n );\n}\n```\n\n## What it will not do\n\n- Know the route: say which item is `active` (from `usePathname()`).\n- Nest deeper than one level: a second level is a page of its own.\n- Cover the page on a wide window or dock on the right: floating is a\n card beside the page, not over it; it is on the left from 768px and a\n sheet below that.\n- Hold the panel's data: the list, its loading and its errors are yours.\n- Take a colour: it wears the workspace theme.\n\nA sidebar it cannot be is your own component in `components/`, built from\nthe kit — not an edit to this block. Its files are fixed: `npm run check`\nrefuses a change, and `makersclaw add sidebar` restores them.\n\n## Credits\n\nThe inset, floating, search, folding-group, panel, drill-in and item-menu\nshapes follow blocks.so (https://blocks.so, MIT) — a rewrite on the\nMakersclaw kit, none of its code.\n"
|
|
35
35
|
}
|
|
36
36
|
],
|
|
37
37
|
"categories": [
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"docs": "Installed to components/makersclaw/blocks/sidebar. Import: import { Sidebar, SidebarLayout, SidebarTrigger, useSidebar } from \"@/components/makersclaw/blocks/sidebar\". Props and an example: components/makersclaw/blocks/sidebar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
63
63
|
"meta": {
|
|
64
64
|
"makersclaw": {
|
|
65
|
-
"version": "0.
|
|
65
|
+
"version": "0.8.0",
|
|
66
66
|
"useWhen": "An app with more than a handful of places to go (inbox, deals, contacts, reports, settings, projects) that wants a real app shell: compose it in app/shell.jsx around the surfaces. Not for in-page tabs (Tabs) or a short strip of links."
|
|
67
67
|
}
|
|
68
68
|
}
|
package/r/signature-pad.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "SignaturePad",
|
|
6
6
|
"description": "A signature field (signature_pad) in the workspace theme: a full-name field, then draw on a paper box (smooth pen strokes, undo the last stroke, clear with Undo) or type the name set as a signature. Sign returns a record: a cropped transparent PNG in dark ink, the typed name, the mode and the time; the signed view shows it with Sign again. Works as a form field too, through a handle (toPNG, getSignature).",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"signature_pad@5.1.4"
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"path": "registry/blocks/signature-pad/README.md",
|
|
46
46
|
"type": "registry:file",
|
|
47
47
|
"target": "@components/makersclaw/blocks/signature-pad/README.md",
|
|
48
|
-
"content": "# SignaturePad\n\nA signature field in the workspace theme. The signer adds their full\nname, then either **draws** on a paper box (smooth pen strokes that\nfollow speed and pressure; undo the last stroke; clear, with Undo in a\ntoast) or **types** — their name is set as a signature in the theme's\ndisplay face. \"Sign\" saves it and the field turns into the signed view:\nthe signature, the name and the time, with \"Sign again\". Nothing drawn,\nno name, a dot: it says what is missing, next to where it is missing.\n\nWhat comes out is a record: a PNG (transparent, cropped to the\nsignature, in dark ink so it reads on a document, a PDF or an email), the\ntyped name, how it was made and when.\n\n**The pad is paper.** It is whichever of the theme's background and\nforeground is lighter, inked with the other: on a light theme, the page\nitself; on a dark theme, a light sheet with dark ink. So what the signer\nsees is exactly what the PNG holds.\n\nIt is [signature_pad](https://github.com/szimek/signature_pad) (MIT)\nunderneath, for the drawing. The field, the modes, the states and the\nrecord are ours, in the kit. The blocks built on this one —\n`proposal-accept` — import it, never `signature_pad`.\n\n```jsx\nimport { SignaturePad, useSignaturePad } from \"../components/blocks/signature-pad\"; // a Makersclaw app (\"./blocks/signature-pad\" from components/)\nimport { SignaturePad, useSignaturePad } from \"@/components/makersclaw/blocks/signature-pad\"; // a shadcn project\n```\n\n`SignaturePad` is a client component: render it from a `\"use client\"`\ncomponent. `signatureRecord` and `formatSignedAt` are plain functions.\n\n`makersclaw add signature-pad` adds `signature_pad` (pinned, exact) to\npackage.json: run `npm install` after. It loads with the page (no\n`import()`); the pad starts in the browser, and fades in once it has\nread the theme's ink.\n\n**It records an acceptance — it is not a certified e-signature.** There\nis no identity check and no audit trail beyond what you store. Say so\nwhere people sign (`note`), and store the record with who was signed in,\nwhen, and what they were shown.\n\n## The data\n\nThe record `onSubmit` receives, `getSignature()` returns and `value`\nshows:\n\n```js\n{\n png: \"data:image/png;base64,…\", // transparent, dark ink, cropped; a few KB\n name: \"Maya Chen\", // the typed full name, trimmed\n mode: \"draw\", // \"draw\" | \"type\"\n signedAt: \"2026-09-23T14:04:11.512Z\",\n}\n```\n\nStore it as it is — one `jsonb` column holds the whole record (the PNG\nis a few KB) — beside the signed-in user and the thing that was signed.\n\n## Props\n\n- `onSubmit(record)` — shows the Sign button (`submitLabel`). It shows as\n signed at once while you save; return `{ ok: true }`, or `{ ok: false,\n error }` (or throw) and the pad comes back as it was, strokes and name\n kept, with a notice that says why. Without it there is no button: read\n the signature through the handle (a field inside your own form).\n- `signer` — the name to start with (\"Maya Chen\"), default empty.\n- `value` — a saved record: the field opens signed, showing it. \"Sign\n again\" (unless `disabled`) goes back to an empty pad.\n- `onChange({ mode, name, empty })` — after each stroke, undo, clear,\n keystroke in the name and mode switch. No PNG (that is `toPNG()`).\n- `label` — the field's label, default \"Signature\" (\"Received by\",\n \"Customer signature\").\n- `nameLabel` — the name field's label, default \"Full name\".\n- `modes` — `[\"draw\", \"type\"]` (the default), or one of them.\n- `defaultMode` — the mode it opens in, default the first of `modes`.\n- `requireName` — a name is needed to sign by drawing too. Default\n `true`; typing always needs one.\n- `submitLabel` — default \"Sign\" (\"Accept and sign\", \"Approve\").\n- `note` — a line beside the button: what signing means.\n- `disabled` — nothing can be drawn, typed or signed. Default `false`.\n- `height` — the paper's height in px, default `176`.\n- `bare` — no card around it (no border, background or padding), for a\n field inside your own card or form. Default `false`.\n- `padRef` — a ref from `useSignaturePad()` for the handle below.\n- `className` — on the outer element.\n\n### The handle\n\n```jsx\nconst pad = useSignaturePad();\n<SignaturePad padRef={pad} />;\nconst record = await pad.current?.getSignature(); // null when nothing is there\n```\n\n`getSignature()` → the record or `null`; `toPNG()` → the PNG data URL\nor `null`; `getName()`, `getMode()`, `isEmpty()`; `undo()`, `clear()`\n(no toast); `submit()` (as the button, when `onSubmit` is given).\n\n### Keys and access\n\nDrawing needs a pointer (mouse, pen, finger). **Type is the keyboard\npath**: Tab to the Draw / Type switch, arrow to Type, and the name field\nis the signature; Enter in it signs (when `onSubmit` is given). The hint\nunder the paper says so. Undo and clear are buttons with names.\n\n## Example\n\nAccepting a quote in a Makersclaw app.\n\n```jsx\n\"use client\";\nimport { updateRow } from \"../lib/actions\";\nimport { SignaturePad } from \"
|
|
48
|
+
"content": "# SignaturePad\n\nA signature field in the workspace theme. The signer adds their full\nname, then either **draws** on a paper box (smooth pen strokes that\nfollow speed and pressure; undo the last stroke; clear, with Undo in a\ntoast) or **types** — their name is set as a signature in the theme's\ndisplay face. \"Sign\" saves it and the field turns into the signed view:\nthe signature, the name and the time, with \"Sign again\". Nothing drawn,\nno name, a dot: it says what is missing, next to where it is missing.\n\nWhat comes out is a record: a PNG (transparent, cropped to the\nsignature, in dark ink so it reads on a document, a PDF or an email), the\ntyped name, how it was made and when.\n\n**The pad is paper.** It is whichever of the theme's background and\nforeground is lighter, inked with the other: on a light theme, the page\nitself; on a dark theme, a light sheet with dark ink. So what the signer\nsees is exactly what the PNG holds.\n\nIt is [signature_pad](https://github.com/szimek/signature_pad) (MIT)\nunderneath, for the drawing. The field, the modes, the states and the\nrecord are ours, in the kit. The blocks built on this one —\n`proposal-accept` — import it, never `signature_pad`.\n\n```jsx\nimport { SignaturePad, useSignaturePad } from \"../components/blocks/signature-pad\"; // a Makersclaw app (\"./blocks/signature-pad\" from components/)\nimport { SignaturePad, useSignaturePad } from \"@/components/makersclaw/blocks/signature-pad\"; // a shadcn project\n```\n\n`SignaturePad` is a client component: render it from a `\"use client\"`\ncomponent. `signatureRecord` and `formatSignedAt` are plain functions.\n\n`makersclaw add signature-pad` adds `signature_pad` (pinned, exact) to\npackage.json: run `npm install` after. It loads with the page (no\n`import()`); the pad starts in the browser, and fades in once it has\nread the theme's ink.\n\n**It records an acceptance — it is not a certified e-signature.** There\nis no identity check and no audit trail beyond what you store. Say so\nwhere people sign (`note`), and store the record with who was signed in,\nwhen, and what they were shown.\n\n## The data\n\nThe record `onSubmit` receives, `getSignature()` returns and `value`\nshows:\n\n```js\n{\n png: \"data:image/png;base64,…\", // transparent, dark ink, cropped; a few KB\n name: \"Maya Chen\", // the typed full name, trimmed\n mode: \"draw\", // \"draw\" | \"type\"\n signedAt: \"2026-09-23T14:04:11.512Z\",\n}\n```\n\nStore it as it is — one `jsonb` column holds the whole record (the PNG\nis a few KB) — beside the signed-in user and the thing that was signed.\n\n## Props\n\n- `onSubmit(record)` — shows the Sign button (`submitLabel`). It shows as\n signed at once while you save; return `{ ok: true }`, or `{ ok: false,\n error }` (or throw) and the pad comes back as it was, strokes and name\n kept, with a notice that says why. Without it there is no button: read\n the signature through the handle (a field inside your own form).\n- `signer` — the name to start with (\"Maya Chen\"), default empty.\n- `value` — a saved record: the field opens signed, showing it. \"Sign\n again\" (unless `disabled`) goes back to an empty pad.\n- `onChange({ mode, name, empty })` — after each stroke, undo, clear,\n keystroke in the name and mode switch. No PNG (that is `toPNG()`).\n- `label` — the field's label, default \"Signature\" (\"Received by\",\n \"Customer signature\").\n- `nameLabel` — the name field's label, default \"Full name\".\n- `modes` — `[\"draw\", \"type\"]` (the default), or one of them.\n- `defaultMode` — the mode it opens in, default the first of `modes`.\n- `requireName` — a name is needed to sign by drawing too. Default\n `true`; typing always needs one.\n- `submitLabel` — default \"Sign\" (\"Accept and sign\", \"Approve\").\n- `note` — a line beside the button: what signing means.\n- `disabled` — nothing can be drawn, typed or signed. Default `false`.\n- `height` — the paper's height in px, default `176`.\n- `bare` — no card around it (no border, background or padding), for a\n field inside your own card or form. Default `false`.\n- `padRef` — a ref from `useSignaturePad()` for the handle below.\n- `className` — on the outer element.\n\n### The handle\n\n```jsx\nconst pad = useSignaturePad();\n<SignaturePad padRef={pad} />;\nconst record = await pad.current?.getSignature(); // null when nothing is there\n```\n\n`getSignature()` → the record or `null`; `toPNG()` → the PNG data URL\nor `null`; `getName()`, `getMode()`, `isEmpty()`; `undo()`, `clear()`\n(no toast); `submit()` (as the button, when `onSubmit` is given).\n\n### Keys and access\n\nDrawing needs a pointer (mouse, pen, finger). **Type is the keyboard\npath**: Tab to the Draw / Type switch, arrow to Type, and the name field\nis the signature; Enter in it signs (when `onSubmit` is given). The hint\nunder the paper says so. Undo and clear are buttons with names.\n\n## Example\n\nAccepting a quote in a Makersclaw app.\n\n```jsx\n// surfaces/quote.jsx — client: one quote, accepted and signed\n\"use client\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useArtifact } from \"../lib/hooks\";\nimport { useRow } from \"../lib/data\";\nimport { updateRow } from \"../lib/actions\";\nimport { SignaturePad } from \"../components/blocks/signature-pad\";\n\nexport default function AcceptQuote() {\n const id = useSearchParams().get(\"row\");\n const { user } = useArtifact(); // the member signed in: { id, email }\n const { row: quote, loading } = useRow({ source: \"quotes\", id });\n if (loading || !quote) return null;\n return (\n <SignaturePad\n signer={user?.email}\n value={quote.signature ?? undefined}\n submitLabel=\"Accept and sign\"\n note=\"Signing records that you accept this quote. It is not a certified e-signature.\"\n // signed_by: a before-update trigger sets it from the member, never the page (the app's README §5)\n onSubmit={(signature) =>\n updateRow({ source: \"quotes\", id: quote.id, patch: { status: \"accepted\", signature }, detail: \"accepted\" })\n }\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- Verify who signed, send the document for signature, or keep an audit\n trail: it is a field, not an e-signature service.\n- Pick a colour or pen width: ink is the theme's, width follows speed.\n- Upload the PNG: it returns a data URL; storing it is yours.\n- Take an image of a signature to upload (use `file-upload`).\n"
|
|
49
49
|
}
|
|
50
50
|
],
|
|
51
51
|
"categories": [
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"docs": "Installed to components/makersclaw/blocks/signature-pad. Import: import { SignaturePad, useSignaturePad, signatureRecord, formatSignedAt } from \"@/components/makersclaw/blocks/signature-pad\". Props and an example: components/makersclaw/blocks/signature-pad/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
66
66
|
"meta": {
|
|
67
67
|
"makersclaw": {
|
|
68
|
-
"version": "0.
|
|
68
|
+
"version": "0.8.0",
|
|
69
69
|
"useWhen": "A screen needs someone to sign: accepting a proposal or a quote, a delivery or work-done sign-off, a waiver, a consent form, an approval on a record. The proposal-accept block is built on it. It records an acceptance with a picture of the signature — it is not a certified legal e-signature (no identity check, no audit trail beyond what you store)."
|
|
70
70
|
}
|
|
71
71
|
}
|