makersclaw 0.5.2 → 0.7.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 (148) hide show
  1. package/README.md +73 -7
  2. package/package.json +3 -2
  3. package/r/annotate.json +4 -4
  4. package/r/approval-request.json +3 -3
  5. package/r/audio-player.json +3 -3
  6. package/r/auth-form.json +2 -2
  7. package/r/board.json +4 -4
  8. package/r/calendar-view.json +4 -4
  9. package/r/cart.json +3 -3
  10. package/r/chart.json +3 -3
  11. package/r/chat.json +3 -3
  12. package/r/code-editor.json +4 -4
  13. package/r/comment-box.json +4 -4
  14. package/r/contact-section.json +3 -3
  15. package/r/core.json +3 -3
  16. package/r/csv-import.json +4 -4
  17. package/r/data-table.json +3 -3
  18. package/r/diagram.json +3 -3
  19. package/r/doc-editor.json +4 -4
  20. package/r/doc-page.json +4 -4
  21. package/r/email-render.json +2 -2
  22. package/r/faq.json +2 -2
  23. package/r/file-upload.json +3 -3
  24. package/r/flow-canvas.json +4 -4
  25. package/r/footer.json +3 -3
  26. package/r/gantt.json +4 -4
  27. package/r/job-panel.json +3 -3
  28. package/r/json-editor.json +4 -4
  29. package/r/map.json +5 -5
  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 +6 -6
  36. package/r/payment-methods.json +2 -2
  37. package/r/pdf-document.json +2 -2
  38. package/r/pdf-viewer.json +6 -6
  39. package/r/product-detail.json +3 -3
  40. package/r/product-grid.json +4 -4
  41. package/r/record-form.json +3 -3
  42. package/r/record-notes.json +5 -5
  43. package/r/registry.json +121 -121
  44. package/r/search-dialog.json +3 -3
  45. package/r/settings-list.json +3 -3
  46. package/r/setup-checklist.json +3 -3
  47. package/r/share-dialog.json +3 -3
  48. package/r/sidebar.json +2 -2
  49. package/r/signature-pad.json +3 -3
  50. package/r/sketch-canvas.json +5 -5
  51. package/r/spreadsheet.json +3 -3
  52. package/r/stat-cards.json +4 -4
  53. package/r/task-list.json +4 -4
  54. package/r/task-model.json +3 -3
  55. package/r/testimonials.json +2 -2
  56. package/r/transaction-list.json +3 -3
  57. package/r/whiteboard.json +4 -4
  58. package/registry/blocks/annotate/README.md +14 -5
  59. package/registry/blocks/approval-request/README.md +15 -8
  60. package/registry/blocks/audio-player/README.md +17 -10
  61. package/registry/blocks/board/README.md +13 -9
  62. package/registry/blocks/board/board.jsx +1 -1
  63. package/registry/blocks/calendar-view/README.md +28 -4
  64. package/registry/blocks/cart/README.md +14 -10
  65. package/registry/blocks/chart/README.md +14 -7
  66. package/registry/blocks/chat/README.md +43 -13
  67. package/registry/blocks/code-editor/README.md +4 -0
  68. package/registry/blocks/code-editor/code-editor.jsx +1 -1
  69. package/registry/blocks/comment-box/README.md +20 -13
  70. package/registry/blocks/contact-section/README.md +7 -6
  71. package/registry/blocks/csv-import/README.md +34 -12
  72. package/registry/blocks/data-table/README.md +41 -8
  73. package/registry/blocks/diagram/diagram.jsx +1 -1
  74. package/registry/blocks/doc-editor/README.md +18 -6
  75. package/registry/blocks/doc-editor/doc-editor.jsx +1 -1
  76. package/registry/blocks/doc-page/README.md +16 -6
  77. package/registry/blocks/file-upload/README.md +7 -1
  78. package/registry/blocks/flow-canvas/README.md +16 -5
  79. package/registry/blocks/flow-canvas/flow-canvas.jsx +1 -1
  80. package/registry/blocks/footer/README.md +4 -2
  81. package/registry/blocks/gantt/README.md +18 -12
  82. package/registry/blocks/job-panel/README.md +1 -1
  83. package/registry/blocks/json-editor/README.md +16 -4
  84. package/registry/blocks/map/README.md +19 -3
  85. package/registry/blocks/map/map-view.jsx +1 -1
  86. package/registry/blocks/map/maplibre.js +1 -1
  87. package/registry/blocks/mind-map/README.md +16 -5
  88. package/registry/blocks/org-chart/README.md +10 -7
  89. package/registry/blocks/page-builder/README.md +13 -6
  90. package/registry/blocks/page-builder/page-builder.jsx +1 -1
  91. package/registry/blocks/page-builder/page-view.jsx +1 -1
  92. package/registry/blocks/page-builder/page.js +1 -1
  93. package/registry/blocks/pdf-viewer/README.md +22 -20
  94. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1 -1
  95. package/registry/blocks/pdf-viewer/pdfjs.js +1 -1
  96. package/registry/blocks/pdf-viewer/ssr-shim.js +1 -1
  97. package/registry/blocks/product-detail/README.md +13 -7
  98. package/registry/blocks/product-detail/demo.jsx +1 -1
  99. package/registry/blocks/product-grid/README.md +12 -10
  100. package/registry/blocks/record-form/README.md +26 -9
  101. package/registry/blocks/record-notes/README.md +42 -30
  102. package/registry/blocks/search-dialog/README.md +38 -7
  103. package/registry/blocks/settings-list/README.md +29 -17
  104. package/registry/blocks/setup-checklist/README.md +12 -14
  105. package/registry/blocks/share-dialog/README.md +1 -1
  106. package/registry/blocks/signature-pad/README.md +15 -4
  107. package/registry/blocks/sketch-canvas/README.md +13 -4
  108. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1 -1
  109. package/registry/blocks/sketch-canvas/ssr-shim.js +3 -3
  110. package/registry/blocks/spreadsheet/README.md +17 -8
  111. package/registry/blocks/stat-cards/README.md +17 -8
  112. package/registry/blocks/task-list/README.md +14 -11
  113. package/registry/blocks/task-model/README.md +3 -3
  114. package/registry/blocks/transaction-list/README.md +15 -7
  115. package/registry/blocks/whiteboard/README.md +19 -13
  116. package/src/cli.mjs +65 -9
  117. package/src/install.mjs +360 -21
  118. package/src/lockfile.mjs +468 -0
  119. package/src/pm.mjs +473 -0
  120. package/src/registry.mjs +125 -1
  121. package/templates/next/browser/app/auto-refresh.jsx +67 -0
  122. package/templates/next/browser/app/layout.jsx +94 -0
  123. package/templates/next/browser/app/page.jsx +60 -0
  124. package/templates/next/browser/app/s/[key]/page.jsx +95 -0
  125. package/templates/next/browser/lib/actions.js +16 -0
  126. package/templates/next/browser/lib/data.js +43 -0
  127. package/templates/next/files/README.md +818 -82
  128. package/templates/next/files/app/claw-alive/route.js +2 -2
  129. package/templates/next/files/app/frame.js +134 -9
  130. package/templates/next/files/app/layout.jsx +2 -2
  131. package/templates/next/files/check/run.mjs +1491 -53
  132. package/templates/next/files/components/ui/item.jsx +2 -2
  133. package/templates/next/files/components/ui/notice.jsx +1 -1
  134. package/templates/next/files/components/ui/separator.jsx +1 -1
  135. package/templates/next/files/lib/actions.js +171 -52
  136. package/templates/next/files/lib/brain-server.js +96 -0
  137. package/templates/next/files/lib/brain.js +413 -0
  138. package/templates/next/files/lib/claw.js +11 -0
  139. package/templates/next/files/lib/data.js +120 -30
  140. package/templates/next/files/lib/db.js +97 -17
  141. package/templates/next/files/lib/door.js +680 -0
  142. package/templates/next/files/next.config.mjs +85 -9
  143. package/templates/next/files/package.json +3 -3
  144. package/templates/next/files/{middleware.js → proxy.js} +13 -6
  145. package/templates/next/files/surfaces.js +3 -2
  146. package/templates/next/pnpm-lock.yaml +5870 -0
  147. package/templates/next/template.json +13 -6
  148. package/templates/next/files/schema.sql +0 -22
@@ -5,7 +5,7 @@
5
5
  "title": "RecordForm",
6
6
  "description": "Edit or create one record from a list of fields given as data (type, label, required, options, help, validation), grouped into titled sections — text, email, number, password, select, combobox, tags, radio, choice cards with prices, checkboxes, switch, date, time, rating, a photo and a row of images. It checks as you type, saves through an async onSave that answers { ok } or per-field errors shown under each field, and sits in a card, on a page (sections split title-left) or in a dialog.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.5.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -37,7 +37,7 @@
37
37
  "path": "registry/blocks/record-form/README.md",
38
38
  "type": "registry:file",
39
39
  "target": "@components/makersclaw/blocks/record-form/README.md",
40
- "content": "# RecordForm\n\nOne record's fields, described as data and drawn with the kit: a label, a\ncontrol, help under it, an error in its place. The fields are grouped into\ntitled sections. The form holds the draft, checks each field when you\nleave it (and again as you fix it), and saves the whole record through\n`onSave`. A save that fails keeps every edit on screen and says why — over\nthe form, and under each field the server refused.\n\n```jsx\nimport { RecordForm } from \"../components/blocks/record-form\"; // a Makersclaw app (\"./blocks/record-form\" from components/)\nimport { RecordForm } from \"@/components/makersclaw/blocks/record-form\"; // a shadcn project\n```\n\nIt is a client component: render it from a `\"use client\"` component, and\nload the record in the server surface (README §4).\n\n## The data\n\n`values` is the record: `{ name: \"…\", price: 189, tags: [\"gift\"] }`. Each\nfield reads and writes `values[field.name]`.\n\nA field:\n\n- `name` — the key in `values` (required, unique in the form).\n- `label` — what it is called.\n- `type` — how it is entered (default `\"text\"`):\n - `text` · `email` · `url` · `tel` · `number` — an Input. `icon`,\n `prefix` (\"$\") or `suffix` (\"kg\") make it an InputGroup. A number is\n sent as a number (empty → `null`).\n - `password` — a PasswordInput with show/hide.\n - `textarea` — `rows` (4).\n - `select` — a native Select over `options`; `placeholder` is the empty\n first choice.\n - `combobox` — a searchable Combobox over `options` (with `description`\n per option); `multiple: true` holds an array.\n - `tags` — a Combobox that holds an array and takes new words.\n - `radio` — a RadioGroup; `orientation: \"horizontal\"` for a short row.\n - `choice-cards` — the RadioGroup's card look: each option\n `{ value, label, description?, price?, per?, badge? }` — a plan, a\n package, a mode.\n - `checkbox` — one box that must be ticked when `required` (\"I agree…\");\n `description` under its label. Holds `true`/`false`.\n - `checkboxes` — several boxes over `options`; holds an array of values.\n - `switch` — an on/off with `description`; holds `true`/`false`.\n - `date` — a DatePicker; the value is `\"YYYY-MM-DD\"`. `min`, `max`,\n `captionLayout: \"dropdown\"` for a birth date.\n - `time` — a TimeInput; `\"HH:MM\"`.\n - `rating` — Rating stars as an input; `max` (5). Holds a whole number.\n - `photo` — an Avatar with Upload and Remove. `nameFrom(values)` gives\n the initials shown with no picture. Holds a URL string, or\n `{ src, file, name }` after a pick: upload `file` in `onSave`.\n - `images` — a row of thumbnails, each removable, and Add while under\n `maxItems`. Holds an array of URL strings or `{ src, file, name }`.\n - `static` — the value as text, not editable (\"Verified\"); `format(value,\n values)` to word it.\n- `required` — it must hold something (a ticked box, a chosen option).\n- `options` — for the choosing types: `[{ value, label, description?,\n disabled? }]` or plain strings.\n- `help` — the muted line under it (hidden while an error shows).\n- `placeholder`, `autoComplete`, `min`, `max`, `step`, `disabled`,\n `readOnly` — as the control's own.\n- `width` — `\"full\"` spans both columns; `\"half\"` keeps a wide type in one.\n Text areas, choices, boxes, switches, a photo and images are full by\n default.\n- `when(values)` — shown only while it returns true; a hidden field is not\n checked and not sent.\n- `defaultValue` — used when `values` has none.\n\nValidation, first broken rule wins, one message per field:\n\n- `required` (`requiredMessage` to word it);\n- the type's own shape: an email, a full `https://` address, a number\n within `min`/`max`;\n- `minLength`, `maxLength`;\n- `pattern` — a regular expression the whole text must match, with\n `patternMessage`;\n- `matches` — the name of another field this one must equal (a confirm),\n with `matchMessage`;\n- `maxItems` — for arrays (tags, images, checkboxes);\n- `validate(value, values)` — return a message, or nothing when fine.\n\n## Props\n\n- `fields` — the fields, one untitled section. Or:\n- `sections` — `[{ title?, description?, columns?, fields }]`: titled\n groups in order.\n- `values` — the record as it stands. Pass a different record and the form\n starts over from it (and forgets unsaved edits).\n- `onSave(values, { changed })` — save the record. `values` is every\n shown field, text trimmed, numbers as numbers; `changed` names the keys\n that differ from `values`. Return `{ ok: true }`, or `{ ok: false,\n error?, errors? }` where `errors` is `{ fieldName: \"message\" }` — each\n shows under its field and the first one is focused. A throw counts as\n failed. In a Makersclaw app it is one line over `updateRow`:\n `onSave={(v) => updateRow({ source: \"public.products\", id: p.id, patch: v })}`.\n- `container` — `\"card\"` (default: a Card with `title` and `description`)\n · `\"page\"` (no box: a page heading, then the sections) · `\"dialog\"` (a\n centred Sheet; on a phone a bottom sheet). A dialog needs `open` and\n `onClose`; it closes on a good save, and opens on the record as it\n stands, never on last time's edits.\n- `layout` — `\"stacked\"` (default: each section's title over its fields)\n or `\"split\"` (the title and description on the left, the fields on the\n right, a rule between sections, from 768px up). A dialog is always\n stacked.\n- `columns` — `2` (default: two columns from 640px) or `1`. A dialog is\n one column.\n- `title`, `description` — the card's, the page's, or the dialog's.\n- `onCancel()` — shows Cancel; it drops the edits first. Without it, a\n Discard button shows while there are edits.\n- `onDelete()` — shows a danger button at the start of the footer (not in\n a dialog) that asks first. Return `{ ok }` like `onSave`.\n- `confirmDelete` — the ask: `{ title?, body?, confirmLabel?,\n consequences?, requireText?, acknowledge? }` (the kit Confirm's).\n- `deleteLabel` — `\"Delete\"`.\n- `onChange(values)` — every edit, for a live preview beside the form.\n- `submitLabel` (`\"Save changes\"`) · `cancelLabel` (`\"Cancel\"`) ·\n `savedLabel` (`\"Saved\"`, shown for three seconds after a good save).\n- `requireChanges` — `true` (default): Save waits for an edit. Pass\n `false` for a form that creates a record.\n- `loading` — skeleton fields while the record loads.\n- `error` — the record could not be loaded: a Notice in place of the\n fields; `onRetry` adds \"Try again\".\n- `emptyLabel` — shown when there are no fields.\n- `className` — layout around it (width, margin). Never colour.\n\n## Example\n\n```jsx\n// components/ProductEditor.jsx — client\n\"use client\";\nimport { updateRow, deleteRow } from \"../lib/actions\";\nimport { RecordForm } from \"./blocks/record-form\";\n\nconst FIELDS = [\n { name: \"name\", label: \"Product name\", required: true },\n { name: \"price\", label: \"Price\", type: \"number\", prefix: \"$\", min: 0, required: true },\n { name: \"category\", label: \"Category\", type: \"select\", options: [\"Outerwear\", \"Tops\", \"Footwear\"] },\n { name: \"launch\", label: \"Launch date\", type: \"date\" },\n { name: \"details\", label: \"Details\", type: \"textarea\" },\n];\n\nexport default function ProductEditor({ product }) {\n return (\n <RecordForm\n title=\"Edit product\"\n fields={FIELDS}\n values={product}\n onSave={(values) => updateRow({ source: \"public.products\", id: product.id, patch: values })}\n onDelete={() => deleteRow({ source: \"public.products\", id: product.id })}\n deleteLabel=\"Remove product\"\n />\n );\n}\n```\n\nIn a dialog, from a list:\n\n```jsx\nconst [editing, setEditing] = useState(null);\n\n<RecordForm\n container=\"dialog\"\n open={Boolean(editing)}\n onClose={() => setEditing(null)}\n title=\"Edit member\"\n values={editing ?? {}}\n sections={[\n { title: \"Profile\", fields: [{ name: \"photo\", label: \"Photo\", type: \"photo\" }, { name: \"name\", label: \"Name\", required: true }] },\n { title: \"Access\", fields: [{ name: \"role\", label: \"Role\", type: \"radio\", options: [\"Admin\", \"Member\", \"Viewer\"] }] },\n ]}\n onSave={async (values) => {\n const res = await updateRow({ source: \"public.members\", id: editing.id, patch: values });\n return res.ok ? res : { ok: false, errors: res.error?.includes(\"email\") ? { email: \"Already in use.\" } : undefined, error: res.error };\n }}\n/>\n```\n\n## What it will not do\n\n- Upload files: a picked photo or image arrives in `onSave` as\n `{ src, file }` — upload `file`, store the URL.\n- Save field by field: it saves the record with one button. Settings that\n each save when flipped are `settings-list`.\n- Sign in, sign up or change a password with rules beside it: that is\n `auth-form`.\n- Lay out a field by hand: a field it cannot draw is your own form in\n `components/`, built from the kit's `Field` and controls.\n\nA form it cannot be is your own component in `components/`, built from the\nkit — not an edit to this block. Its files are fixed: `npm run check`\nrefuses a change, and `makersclaw add record-form` restores them.\n\n## Credits\n\nShapes from Creative Tim UI (https://github.com/creativetimofficial/ui, MIT) and blocks.so (https://github.com/ephraimduncan/blocks, MIT); this is a rewrite on the Makersclaw kit.\n"
40
+ "content": "# RecordForm\n\nOne record's fields, described as data and drawn with the kit: a label, a\ncontrol, help under it, an error in its place. The fields are grouped into\ntitled sections. The form holds the draft, checks each field when you\nleave it (and again as you fix it), and saves the whole record through\n`onSave`. A save that fails keeps every edit on screen and says why — over\nthe form, and under each field the server refused.\n\n```jsx\nimport { RecordForm } from \"../components/blocks/record-form\"; // a Makersclaw app (\"./blocks/record-form\" from components/)\nimport { RecordForm } from \"@/components/makersclaw/blocks/record-form\"; // a shadcn project\n```\n\nIt is a client component: render it from a `\"use client\"` component, and\nload the record in the server surface (README §4).\n\n## The data\n\n`values` is the record: `{ name: \"…\", price: 189, tags: [\"gift\"] }`. Each\nfield reads and writes `values[field.name]`.\n\nA field:\n\n- `name` — the key in `values` (required, unique in the form).\n- `label` — what it is called.\n- `type` — how it is entered (default `\"text\"`):\n - `text` · `email` · `url` · `tel` · `number` — an Input. `icon`,\n `prefix` (\"$\") or `suffix` (\"kg\") make it an InputGroup. A number is\n sent as a number (empty → `null`).\n - `password` — a PasswordInput with show/hide.\n - `textarea` — `rows` (4).\n - `select` — a native Select over `options`; `placeholder` is the empty\n first choice.\n - `combobox` — a searchable Combobox over `options` (with `description`\n per option); `multiple: true` holds an array.\n - `tags` — a Combobox that holds an array and takes new words.\n - `radio` — a RadioGroup; `orientation: \"horizontal\"` for a short row.\n - `choice-cards` — the RadioGroup's card look: each option\n `{ value, label, description?, price?, per?, badge? }` — a plan, a\n package, a mode.\n - `checkbox` — one box that must be ticked when `required` (\"I agree…\");\n `description` under its label. Holds `true`/`false`.\n - `checkboxes` — several boxes over `options`; holds an array of values.\n - `switch` — an on/off with `description`; holds `true`/`false`.\n - `date` — a DatePicker; the value is `\"YYYY-MM-DD\"`. `min`, `max`,\n `captionLayout: \"dropdown\"` for a birth date.\n - `time` — a TimeInput; `\"HH:MM\"`.\n - `rating` — Rating stars as an input; `max` (5). Holds a whole number.\n - `photo` — an Avatar with Upload and Remove. `nameFrom(values)` gives\n the initials shown with no picture. Holds a URL string, or\n `{ src, file, name }` after a pick: upload `file` in `onSave`.\n - `images` — a row of thumbnails, each removable, and Add while under\n `maxItems`. Holds an array of URL strings or `{ src, file, name }`.\n - `static` — the value as text, not editable (\"Verified\"); `format(value,\n values)` to word it.\n- `required` — it must hold something (a ticked box, a chosen option).\n- `options` — for the choosing types: `[{ value, label, description?,\n disabled? }]` or plain strings.\n- `help` — the muted line under it (hidden while an error shows).\n- `placeholder`, `autoComplete`, `min`, `max`, `step`, `disabled`,\n `readOnly` — as the control's own.\n- `width` — `\"full\"` spans both columns; `\"half\"` keeps a wide type in one.\n Text areas, choices, boxes, switches, a photo and images are full by\n default.\n- `when(values)` — shown only while it returns true; a hidden field is not\n checked and not sent.\n- `defaultValue` — used when `values` has none.\n\nValidation, first broken rule wins, one message per field:\n\n- `required` (`requiredMessage` to word it);\n- the type's own shape: an email, a full `https://` address, a number\n within `min`/`max`;\n- `minLength`, `maxLength`;\n- `pattern` — a regular expression the whole text must match, with\n `patternMessage`;\n- `matches` — the name of another field this one must equal (a confirm),\n with `matchMessage`;\n- `maxItems` — for arrays (tags, images, checkboxes);\n- `validate(value, values)` — return a message, or nothing when fine.\n\n## Props\n\n- `fields` — the fields, one untitled section. Or:\n- `sections` — `[{ title?, description?, columns?, fields }]`: titled\n groups in order.\n- `values` — the record as it stands. Pass a different record and the form\n starts over from it (and forgets unsaved edits).\n- `onSave(values, { changed })` — save the record. `values` is every\n shown field, text trimmed, numbers as numbers; `changed` names the keys\n that differ from `values`. Return `{ ok: true }`, or `{ ok: false,\n error?, errors? }` where `errors` is `{ fieldName: \"message\" }` — each\n shows under its field and the first one is focused. A throw counts as\n failed. In a Makersclaw app it is one line over `updateRow`:\n `onSave={(v) => updateRow({ source: \"products\", id: p.id, patch: v })}`.\n- `container` — `\"card\"` (default: a Card with `title` and `description`)\n · `\"page\"` (no box: a page heading, then the sections) · `\"dialog\"` (a\n centred Sheet; on a phone a bottom sheet). A dialog needs `open` and\n `onClose`; it closes on a good save, and opens on the record as it\n stands, never on last time's edits.\n- `layout` — `\"stacked\"` (default: each section's title over its fields)\n or `\"split\"` (the title and description on the left, the fields on the\n right, a rule between sections, from 768px up). A dialog is always\n stacked.\n- `columns` — `2` (default: two columns from 640px) or `1`. A dialog is\n one column.\n- `title`, `description` — the card's, the page's, or the dialog's.\n- `onCancel()` — shows Cancel; it drops the edits first. Without it, a\n Discard button shows while there are edits.\n- `onDelete()` — shows a danger button at the start of the footer (not in\n a dialog) that asks first. Return `{ ok }` like `onSave`.\n- `confirmDelete` — the ask: `{ title?, body?, confirmLabel?,\n consequences?, requireText?, acknowledge? }` (the kit Confirm's).\n- `deleteLabel` — `\"Delete\"`.\n- `onChange(values)` — every edit, for a live preview beside the form.\n- `submitLabel` (`\"Save changes\"`) · `cancelLabel` (`\"Cancel\"`) ·\n `savedLabel` (`\"Saved\"`, shown for three seconds after a good save).\n- `requireChanges` — `true` (default): Save waits for an edit. Pass\n `false` for a form that creates a record.\n- `loading` — skeleton fields while the record loads.\n- `error` — the record could not be loaded: a Notice in place of the\n fields; `onRetry` adds \"Try again\".\n- `emptyLabel` — shown when there are no fields.\n- `className` — layout around it (width, margin). Never colour.\n\n## Example\n\n```jsx\n// surfaces/product.jsx — client: one product, edited\n\"use client\";\nimport { useRouter, useSearchParams } from \"next/navigation\";\nimport { useRow } from \"../lib/data\";\nimport { deleteRow, updateRow } from \"../lib/actions\";\nimport { RecordForm } from \"../components/blocks/record-form\";\n\nconst FIELDS = [\n { name: \"name\", label: \"Product name\", required: true },\n { name: \"price\", label: \"Price\", type: \"number\", prefix: \"$\", min: 0, required: true },\n { name: \"category\", label: \"Category\", type: \"select\", options: [\"Outerwear\", \"Tops\", \"Footwear\"] },\n { name: \"launch\", label: \"Launch date\", type: \"date\" },\n { name: \"details\", label: \"Details\", type: \"textarea\" },\n];\n\nexport default function ProductEditor() {\n const router = useRouter();\n const id = useSearchParams().get(\"row\");\n const { row: product, loading, error, reload } = useRow({ source: \"products\", id });\n return (\n <RecordForm\n title=\"Edit product\"\n fields={FIELDS}\n values={product ?? {}}\n loading={loading}\n error={error}\n onRetry={reload}\n onSave={(values) => updateRow({ source: \"products\", id, patch: values })}\n onDelete={async () => {\n const res = await deleteRow({ source: \"products\", id });\n if (res.ok) router.push(\"/s/products\");\n return res;\n }}\n deleteLabel=\"Remove product\"\n />\n );\n}\n```\n\nAfter a save the row is read again and comes back as `values`: the\nform starts over from what it just saved.\n\nIn a server-mode app the same calls are `loadRows` in a server surface and the Server Actions of `lib/actions`.\n\nIn a dialog, from a list:\n\n```jsx\nconst [editing, setEditing] = useState(null);\n\n<RecordForm\n container=\"dialog\"\n open={Boolean(editing)}\n onClose={() => setEditing(null)}\n title=\"Edit member\"\n values={editing ?? {}}\n sections={[\n { title: \"Profile\", fields: [{ name: \"photo\", label: \"Photo\", type: \"photo\" }, { name: \"name\", label: \"Name\", required: true }] },\n { title: \"Access\", fields: [{ name: \"role\", label: \"Role\", type: \"radio\", options: [\"Admin\", \"Member\", \"Viewer\"] }] },\n ]}\n onSave={async (values) => {\n const res = await updateRow({ source: \"members\", id: editing.id, patch: values });\n return res.ok ? res : { ok: false, errors: res.error?.includes(\"email\") ? { email: \"Already in use.\" } : undefined, error: res.error };\n }}\n/>\n```\n\n## What it will not do\n\n- Upload files: a picked photo or image arrives in `onSave` as\n `{ src, file }` — upload `file`, store the URL.\n- Save field by field: it saves the record with one button. Settings that\n each save when flipped are `settings-list`.\n- Sign in, sign up or change a password with rules beside it: that is\n `auth-form`.\n- Lay out a field by hand: a field it cannot draw is your own form in\n `components/`, built from the kit's `Field` and controls.\n\nA form it cannot be is your own component in `components/`, built from the\nkit — not an edit to this block. Its files are fixed: `npm run check`\nrefuses a change, and `makersclaw add record-form` restores them.\n\n## Credits\n\nShapes from Creative Tim UI (https://github.com/creativetimofficial/ui, MIT) and blocks.so (https://github.com/ephraimduncan/blocks, MIT); this is a rewrite on the Makersclaw kit.\n"
41
41
  }
42
42
  ],
43
43
  "categories": [
@@ -60,7 +60,7 @@
60
60
  "docs": "Installed to components/makersclaw/blocks/record-form. Import: import { RecordForm } from \"@/components/makersclaw/blocks/record-form\". Props and an example: components/makersclaw/blocks/record-form/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
61
61
  "meta": {
62
62
  "makersclaw": {
63
- "version": "0.5.2",
63
+ "version": "0.7.0",
64
64
  "useWhen": "A screen edits one record's fields: a profile, a product, a team member, account details, a sign-up or application form, a settings page of fields with one Save. Not for a list of records (data-table), for toggles that each save on their own (settings-list), or for sign-in (auth-form)."
65
65
  }
66
66
  }
@@ -5,9 +5,9 @@
5
5
  "title": "RecordNotes",
6
6
  "description": "Dated rich-text notes on one record (a deal, a contact, a company), to sit beside record-form: a composer with @mentions and Cmd/Ctrl+Enter adds a note; the notes follow newest first (or oldest), grouped by day, each with author avatar, name and relative time; your own notes edit in place and delete after a confirm. Every save is an async callback answering { ok }; a failure keeps the words and says why where it happened.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.5.2/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.5.2/r/comment-box.json",
10
- "https://cdn.jsdelivr.net/npm/makersclaw@0.5.2/r/doc-editor.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/comment-box.json",
10
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.7.0/r/doc-editor.json"
11
11
  ],
12
12
  "dependencies": [],
13
13
  "files": [
@@ -27,7 +27,7 @@
27
27
  "path": "registry/blocks/record-notes/README.md",
28
28
  "type": "registry:file",
29
29
  "target": "@components/makersclaw/blocks/record-notes/README.md",
30
- "content": "# RecordNotes\n\nThe notes kept on one record — a deal, a contact, a company — to sit\nbeside its `record-form`. A composer at the top adds a note (rich text,\n`@` to mention a teammate, Cmd/Ctrl+Enter or Add note). The notes follow,\nnewest first, grouped under \"Today\", \"Yesterday\", \"Mon 21 Sep\", each with\nits author, \"2 h ago\" and the body as written. Your own notes have a ⋯\nmenu: Edit opens the same composer in place, Delete asks first.\n\nEvery save goes out through an async callback that answers `{ ok }`. The\nblock keeps no copy of the notes: when a callback succeeds, the page puts\nthe change into `notes`. When one fails, the words stay and the block\nsays why — under the composer, or on the note.\n\n```jsx\nimport { RecordNotes } from \"../components/blocks/record-notes\"; // a Makersclaw app (\"./blocks/record-notes\" from components/)\nimport { RecordNotes } from \"@/components/makersclaw/blocks/record-notes\"; // a shadcn project\n```\n\nIt is a client component: render it from a `\"use client\"` component, and\nload the notes in the server surface (README §4). `makersclaw add\nrecord-notes` adds `comment-box` and `doc-editor` with it (and the editor's\nnpm packages: run `npm install` after).\n\n## The data\n\nA note in `notes`:\n\n```js\n{\n id: \"n-204\",\n author: { id: \"u-lena\", name: \"Lena Fischer\", src: \"https://…/lena.jpg\" },\n body: [ /* the doc onAdd gave you (BlockNote JSON) */ ], // or a plain string\n createdAt: \"2026-09-23T09:12:00Z\", // ISO string, Date, ms, or a day (\"2026-09-23\": that day, never UTC's)\n updatedAt: null, // set → \"(edited)\"\n}\n```\n\nOne table, a row per note: `record_id`, `author_id`, `body jsonb`,\n`body_text` (`docToText(doc)` from `doc-editor`, for search), `created_at`,\n`updated_at`. A note is the author's: by default only notes whose\n`author.id` is `me.id` (or that carry `mine: true`) get Edit and Delete.\n\n## Props\n\n- `notes` — required: the record's notes, any order (the block sorts by\n `createdAt`).\n- `me` — `{ id, name, src? }`, who is writing: their avatar is beside the\n composer, and their notes are editable.\n- `onAdd(doc)` — `async`, returns `{ ok: true }` or `{ ok: false, error? }`.\n Add the note to `notes` before answering ok. Without it there is no\n composer (a read-only log).\n- `onEdit(note, doc)` — `async`, `{ ok }`. Replace the note's body (and set\n `updatedAt`). Without it there is no Edit.\n- `onDelete(note)` — `async`, `{ ok }`, after the reader confirms. Drop the\n note from `notes`. Without it there is no Delete.\n- `canEdit(note)` — who may edit and delete which note, when \"the author\"\n is not the rule (an admin may delete any note). Default: the author.\n- `mentions` — `async (query) => [{ id, label, description? }]`: turns on\n `@` in the composer.\n- `title` — `\"Notes\"`; the card's heading and the list's name.\n- `description` — a line under the title.\n- `placeholder` — `\"Add a note…\"`.\n- `addLabel` — the composer's button, `\"Add note\"`.\n- `emptyText` — shown when there are no notes.\n- `order` — `\"newest\"` (default: composer on top, newest first) ·\n `\"oldest\"` (a log read top to bottom, composer at the end).\n- `frame` — `\"card\"` (default: a Card with the title and a count) ·\n `\"none\"` (just the composer and notes, for a tab or a sheet that has its\n own heading).\n- `className` — layout around it.\n\n## Example\n\n```jsx\n// components/DealNotes.jsx — client\n\"use client\";\nimport { useState } from \"react\";\nimport { deleteRow, insertRow, updateRow } from \"../lib/actions\";\nimport { RecordNotes } from \"./blocks/record-notes\";\nimport { docToText } from \"./blocks/doc-editor\";\n\nexport default function DealNotes({ deal, me, team, initial }) {\n const [notes, setNotes] = useState(initial);\n const findPeople = async (q) =>\n team.filter((p) => p.name.toLowerCase().includes(q.toLowerCase())).map((p) => ({ id: p.id, label: p.name }));\n\n async function add(doc) {\n const res = await insertRow({\n source: \"public.deal_notes\",\n values: { deal_id: deal.id, author_id: me.id, body: doc, body_text: docToText(doc) },\n });\n if (!res.ok) return res;\n setNotes((n) => [{ id: res.id, author: me, body: doc, createdAt: new Date() }, ...n]);\n return { ok: true };\n }\n\n async function edit(note, doc) {\n const updatedAt = new Date();\n const res = await updateRow({ source: \"public.deal_notes\", id: note.id, patch: { body: doc, body_text: docToText(doc), updated_at: updatedAt } });\n if (res.ok) setNotes((n) => n.map((x) => (x.id === note.id ? { ...x, body: doc, updatedAt } : x)));\n return res;\n }\n\n async function remove(note) {\n const res = await deleteRow({ source: \"public.deal_notes\", id: note.id });\n if (res.ok) setNotes((n) => n.filter((x) => x.id !== note.id));\n return res;\n }\n\n return <RecordNotes notes={notes} me={me} onAdd={add} onEdit={edit} onDelete={remove} mentions={findPeople} />;\n}\n```\n\nBeside the form, on a record page:\n\n```jsx\n<div className=\"grid gap-4 lg:grid-cols-[minmax(0,1fr)_24rem]\">\n <RecordForm … />\n <DealNotes … />\n</div>\n```\n\n## What it will not do\n\n- Pin, tag, react to or reply under a note: a note is a dated entry, not\n a thread (`comment-box` for a discussion).\n- Log activity by itself (emails sent, stage changes): pass those in as\n notes if you want them in the list, with no `onEdit`/`onDelete` rights.\n- Page or search the notes: it draws what it is given, sorted. Load the\n latest few dozen and a \"Show older\" of your own.\n- Collaborate live: two people editing one note is last-write-wins.\n- Notify anyone mentioned: read `docMentions(doc)` in `onAdd` and send it\n yourself.\n\nA notes panel it cannot be is your own component in `components/`,\nbuilt from `comment-box`'s `CommentComposer`, `CommentBody` and\n`CommentTime` — not an edit to this block. Its files are fixed: `npm run\ncheck` refuses a change, and `makersclaw add record-notes` restores them.\n"
30
+ "content": "# RecordNotes\n\nThe notes kept on one record — a deal, a contact, a company — to sit\nbeside its `record-form`. A composer at the top adds a note (rich text,\n`@` to mention a teammate, Cmd/Ctrl+Enter or Add note). The notes follow,\nnewest first, grouped under \"Today\", \"Yesterday\", \"Mon 21 Sep\", each with\nits author, \"2 h ago\" and the body as written. Your own notes have a ⋯\nmenu: Edit opens the same composer in place, Delete asks first.\n\nEvery save goes out through an async callback that answers `{ ok }`. The\nblock keeps no copy of the notes: when a callback succeeds, the page puts\nthe change into `notes`. When one fails, the words stay and the block\nsays why — under the composer, or on the note.\n\n```jsx\nimport { RecordNotes } from \"../components/blocks/record-notes\"; // a Makersclaw app (\"./blocks/record-notes\" from components/)\nimport { RecordNotes } from \"@/components/makersclaw/blocks/record-notes\"; // a shadcn project\n```\n\nIt is a client component: render it from a `\"use client\"` component, and\nload the notes in the server surface (README §4). `makersclaw add\nrecord-notes` adds `comment-box` and `doc-editor` with it (and the editor's\nnpm packages: run `npm install` after).\n\n## The data\n\nA note in `notes`:\n\n```js\n{\n id: \"n-204\",\n author: { id: \"u-lena\", name: \"Lena Fischer\", src: \"https://…/lena.jpg\" },\n body: [ /* the doc onAdd gave you (BlockNote JSON) */ ], // or a plain string\n createdAt: \"2026-09-23T09:12:00Z\", // ISO string, Date, ms, or a day (\"2026-09-23\": that day, never UTC's)\n updatedAt: null, // set → \"(edited)\"\n}\n```\n\nOne table, a row per note: `record_id`, `author_id`, `body jsonb`,\n`body_text` (`docToText(doc)` from `doc-editor`, for search), `created_at`,\n`updated_at`. A note is the author's: by default only notes whose\n`author.id` is `me.id` (or that carry `mine: true`) get Edit and Delete.\n\n## Props\n\n- `notes` — required: the record's notes, any order (the block sorts by\n `createdAt`).\n- `me` — `{ id, name, src? }`, who is writing: their avatar is beside the\n composer, and their notes are editable.\n- `onAdd(doc)` — `async`, returns `{ ok: true }` or `{ ok: false, error? }`.\n Add the note to `notes` before answering ok. Without it there is no\n composer (a read-only log).\n- `onEdit(note, doc)` — `async`, `{ ok }`. Replace the note's body (and set\n `updatedAt`). Without it there is no Edit.\n- `onDelete(note)` — `async`, `{ ok }`, after the reader confirms. Drop the\n note from `notes`. Without it there is no Delete.\n- `canEdit(note)` — who may edit and delete which note, when \"the author\"\n is not the rule (an admin may delete any note). Default: the author.\n- `mentions` — `async (query) => [{ id, label, description? }]`: turns on\n `@` in the composer.\n- `title` — `\"Notes\"`; the card's heading and the list's name.\n- `description` — a line under the title.\n- `placeholder` — `\"Add a note…\"`.\n- `addLabel` — the composer's button, `\"Add note\"`.\n- `emptyText` — shown when there are no notes.\n- `order` — `\"newest\"` (default: composer on top, newest first) ·\n `\"oldest\"` (a log read top to bottom, composer at the end).\n- `frame` — `\"card\"` (default: a Card with the title and a count) ·\n `\"none\"` (just the composer and notes, for a tab or a sheet that has its\n own heading).\n- `className` — layout around it.\n\n## Example\n\n```jsx\n// surfaces/deal-notes.jsx — client: one deal's notes\n\"use client\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useArtifact } from \"../lib/hooks\";\nimport { useRows, whenLanded } from \"../lib/data\";\nimport { deleteRow, insertRow, updateRow } from \"../lib/actions\";\nimport { RecordNotes } from \"../components/blocks/record-notes\";\nimport { docToText } from \"../components/blocks/doc-editor\";\n\nconst source = \"deal_notes\";\n\n// A write answers once the notes are read again, so the list never shows\n// the notes from before it.\nasync function landed(res) {\n if (res.ok) await whenLanded();\n return res;\n}\n\nexport default function DealNotes() {\n const dealId = useSearchParams().get(\"row\");\n const { user } = useArtifact(); // the member signed in: { id, email }\n const { rows, loading } = useRows({ source, filter: { deal_id: dealId }, orderBy: \"created_at desc\" });\n const { rows: team } = useRows({ source: \"people\", orderBy: \"name asc\" });\n const me = { id: user?.email, name: user?.email ?? \"You\" };\n // created_by: a before-insert trigger sets it from the member, never the page (the app's README §5)\n const notes = rows.map((n) => ({\n id: n.id,\n author: { id: n.created_by, name: n.created_by },\n body: n.body,\n createdAt: n.created_at,\n updatedAt: n.updated_at,\n }));\n const findPeople = async (q) =>\n team.filter((p) => p.name.toLowerCase().includes(q.toLowerCase())).map((p) => ({ id: p.id, label: p.name }));\n\n if (loading) return null;\n return (\n <RecordNotes\n notes={notes}\n me={me}\n onAdd={(doc) => insertRow({ source, values: { deal_id: dealId, body: doc, body_text: docToText(doc) } }).then(landed)}\n onEdit={(note, doc) => updateRow({ source, id: note.id, patch: { body: doc, body_text: docToText(doc), updated_at: new Date() } }).then(landed)}\n onDelete={(note) => deleteRow({ source, id: note.id }).then(landed)}\n mentions={findPeople}\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\nBeside the form, on a record page:\n\n```jsx\n<div className=\"grid gap-4 lg:grid-cols-[minmax(0,1fr)_24rem]\">\n <RecordForm … />\n <DealNotes … />\n</div>\n```\n\n## What it will not do\n\n- Pin, tag, react to or reply under a note: a note is a dated entry, not\n a thread (`comment-box` for a discussion).\n- Log activity by itself (emails sent, stage changes): pass those in as\n notes if you want them in the list, with no `onEdit`/`onDelete` rights.\n- Page or search the notes: it draws what it is given, sorted. Load the\n latest few dozen and a \"Show older\" of your own.\n- Collaborate live: two people editing one note is last-write-wins.\n- Notify anyone mentioned: read `docMentions(doc)` in `onAdd` and send it\n yourself.\n\nA notes panel it cannot be is your own component in `components/`,\nbuilt from `comment-box`'s `CommentComposer`, `CommentBody` and\n`CommentTime` — not an edit to this block. Its files are fixed: `npm run\ncheck` refuses a change, and `makersclaw add record-notes` restores them.\n"
31
31
  }
32
32
  ],
33
33
  "categories": [
@@ -48,7 +48,7 @@
48
48
  "docs": "Installed to components/makersclaw/blocks/record-notes. Import: import { RecordNotes } from \"@/components/makersclaw/blocks/record-notes\". Props and an example: components/makersclaw/blocks/record-notes/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
49
49
  "meta": {
50
50
  "makersclaw": {
51
- "version": "0.5.2",
51
+ "version": "0.7.0",
52
52
  "useWhen": "A record page keeps a running log of notes: call notes on a deal, context on a contact, a history on a customer or a candidate. Not a discussion thread without edit and delete (comment-box), not one long document per record (doc-editor or doc-page), not an automatic activity feed."
53
53
  }
54
54
  }