makersclaw 0.4.0 → 0.5.1
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/package.json +1 -1
- package/r/annotate.json +3 -3
- package/r/approval-request.json +4 -4
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/board.json +2 -2
- package/r/calendar-view.json +9 -9
- package/r/cart.json +2 -2
- package/r/chart.json +7 -7
- package/r/chat.json +3 -3
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +5 -5
- package/r/contact-section.json +2 -2
- package/r/core.json +5 -5
- package/r/csv-import.json +3 -3
- package/r/data-table.json +4 -4
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/flow-canvas.json +4 -4
- package/r/footer.json +2 -2
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/map.json +2 -2
- package/r/menubar.json +2 -2
- package/r/mind-map.json +6 -6
- 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 +2 -2
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +3 -3
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-notes.json +6 -6
- package/r/registry.json +121 -121
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +4 -4
- package/r/signature-pad.json +3 -3
- package/r/sketch-canvas.json +2 -2
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/transaction-list.json +2 -2
- package/r/whiteboard.json +3 -3
- package/registry/blocks/approval-request/README.md +2 -1
- package/registry/blocks/approval-request/approval-request.jsx +5 -3
- package/registry/blocks/calendar-view/README.md +8 -0
- package/registry/blocks/calendar-view/agenda.jsx +5 -4
- package/registry/blocks/calendar-view/calendar.jsx +23 -8
- package/registry/blocks/calendar-view/demo.jsx +2 -0
- package/registry/blocks/calendar-view/month.jsx +6 -5
- package/registry/blocks/calendar-view/time-grid.jsx +9 -7
- package/registry/blocks/calendar-view/tray.jsx +5 -4
- package/registry/blocks/chart/README.md +9 -4
- package/registry/blocks/chart/cartesian.jsx +31 -24
- package/registry/blocks/chart/core.jsx +21 -0
- package/registry/blocks/chart/pie.jsx +15 -10
- package/registry/blocks/chart/sparkline.jsx +32 -14
- package/registry/blocks/chat/message.jsx +4 -4
- package/registry/blocks/comment-box/README.md +1 -1
- package/registry/blocks/comment-box/comment-time.jsx +4 -6
- package/registry/blocks/data-table/README.md +4 -0
- package/registry/blocks/data-table/data-table.jsx +20 -6
- package/registry/blocks/flow-canvas/edges.jsx +4 -2
- package/registry/blocks/flow-canvas/theme.js +59 -48
- package/registry/blocks/mind-map/README.md +9 -2
- package/registry/blocks/mind-map/mind-map.jsx +51 -12
- package/registry/blocks/mind-map/tree.js +33 -0
- package/registry/blocks/org-chart/org-chart.jsx +1 -1
- package/registry/blocks/pdf-viewer/notes.jsx +4 -3
- package/registry/blocks/record-notes/README.md +1 -1
- package/registry/blocks/record-notes/record-notes.jsx +4 -5
- package/registry/blocks/sidebar/sidebar-panel.jsx +4 -4
- package/registry/blocks/sidebar/sidebar.jsx +13 -13
- package/registry/blocks/signature-pad/signature.js +8 -1
- package/registry/blocks/spreadsheet/cells.jsx +2 -2
- package/templates/next/files/README.md +77 -19
- package/templates/next/files/app/auto-refresh.jsx +39 -74
- package/templates/next/files/app/frame.js +197 -6
- package/templates/next/files/check/run.mjs +26 -10
- package/templates/next/files/components/ui/calendar.jsx +24 -0
- package/templates/next/files/components/ui/field.jsx +12 -3
- package/templates/next/files/components/ui/index.js +1 -0
- package/templates/next/files/components/ui/sheet.jsx +38 -2
- package/templates/next/files/components/ui/toggle.jsx +20 -2
- package/templates/next/files/lib/actions.js +63 -5
- package/templates/next/files/lib/data.js +4 -1
- package/templates/next/files/lib/db.js +18 -1
- package/templates/next/files/lib/hooks.js +52 -17
- package/templates/next/template.json +2 -2
package/package.json
CHANGED
package/r/annotate.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"title": "Annotate",
|
|
6
6
|
"description": "Mark up a screenshot: drop, pick or paste one, then draw arrows, boxes, highlights, redaction bars and numbered notes on it (Excalidraw underneath, via sketch-canvas). A side list holds the words of each numbered note; a click on a note's number shows its pin. Starter templates (bug report, design review, walkthrough) on a fresh screenshot. The annotation is plain JSON saved through onChange / an async onSave; onSubmit hands over a PNG of the marked-up screenshot with the notes and the sketch.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.5.1/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.5.1/r/sketch-canvas.json"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": [],
|
|
12
12
|
"files": [
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"docs": "Installed to components/makersclaw/blocks/annotate. Import: import { Annotate, ANNOTATE_TOOLS, ANNOTATE_TEMPLATES, emptyAnnotation, toAnnotation, annotationNotes, annotationToText, noteMarkers, nextNoteNumber, markerSkeleton } from \"@/components/makersclaw/blocks/annotate\". Props and an example: components/makersclaw/blocks/annotate/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
56
56
|
"meta": {
|
|
57
57
|
"makersclaw": {
|
|
58
|
-
"version": "0.
|
|
58
|
+
"version": "0.5.1",
|
|
59
59
|
"useWhen": "Someone needs to point at parts of a screen and say something about each: a bug report, design feedback, a support question, a walkthrough, a request to an agent about what to change. Not a blank drawing board (whiteboard or sketch-canvas), not a wireframe (sketch-to-app), not an image upload alone (file-upload)."
|
|
60
60
|
}
|
|
61
61
|
}
|
package/r/approval-request.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "ApprovalRequest",
|
|
6
6
|
"description": "One request waiting on a decision: what is asked for as label/value facts (an amount in large type), who asked and when, why, a status badge, and Approve / Reject (reject asks for a reason). The decision goes out through async onApprove / onReject; the block shows it working, a failure, then the outcome in place of the buttons. A card alone, or a page of detail sections beside the card.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.5.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"path": "registry/blocks/approval-request/approval-request.jsx",
|
|
20
20
|
"type": "registry:component",
|
|
21
21
|
"target": "@components/makersclaw/blocks/approval-request/approval-request.jsx",
|
|
22
|
-
"content": "\"use client\";\n\nimport { useEffect, useId, useState } from \"react\";\nimport { Avatar, Badge, Button, Empty, Field, Icon, Notice, Separator, Skeleton, Textarea, cx } from \"../../ui\";\n\n// ApprovalRequest — one request waiting on a decision: what is asked for\n// (label/value facts), who asked and when, why, and Approve / Reject. The\n// decision goes out through `onApprove()` / `onReject(reason)`; the\n// block shows it working, shows a failure, and once it lands shows the\n// outcome in place of the buttons.\n\nconst STATUS = {\n pending: { tone: \"warning\", label: \"Pending approval\" },\n approved: { tone: \"success\", label: \"Approved\" },\n rejected: { tone: \"danger\", label: \"Rejected\" },\n};\n\nfunction defaultFormat(value) {\n if (!value) return \"\";\n const d = value instanceof Date ? value : new Date(value);\n if (Number.isNaN(d.getTime())) return String(value);\n return new Intl.DateTimeFormat(undefined, { dateStyle: \"long\" }).format(d);\n}\n\nfunction When({ value, format }) {\n if (!value) return null;\n const iso = value instanceof Date ? value.toISOString() : String(value);\n return (\n <time dateTime={iso} suppressHydrationWarning>\n {format(value)}\n </time>\n );\n}\n\nfunction StatusBadge({ status }) {\n const s = STATUS[status] ?? STATUS.pending;\n return (\n <Badge tone={s.tone} variant=\"soft\">\n {s.label}\n </Badge>\n );\n}\n\nfunction Requester({ requester, requestedAt, format }) {\n if (!requester && !requestedAt) return null;\n return (\n <div className=\"flex min-w-0 items-center gap-2 text-sm text-muted-foreground\">\n {requester ? <Avatar name={requester.name} src={requester.src} size=\"xs\" /> : null}\n <p className=\"min-w-0\">\n {requester ? (\n <>\n <span className=\"font-medium text-foreground\">{requester.name}</span>\n {requester.role ? `, ${requester.role}` : \"\"}\n </>\n ) : null}\n {requester && requestedAt ? \" · \" : null}\n <When value={requestedAt} format={format} />\n </p>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------------\n The card: facts and the decision\n ------------------------------------------------------------------------ */\n\nfunction DecisionCard({\n title,\n kind,\n details,\n requester,\n requestedAt,\n justification,\n status,\n decidedBy,\n decidedAt,\n decisionNote,\n onApprove,\n onReject,\n reasonRequired,\n approveLabel,\n rejectLabel,\n format,\n showContext,\n className,\n}) {\n const [local, setLocal] = useState(null);\n const [mode, setMode] = useState(\"idle\"); // idle · rejecting\n const [busy, setBusy] = useState(null); // approve · reject\n const [reason, setReason] = useState(\"\");\n const [error, setError] = useState(null);\n const reasonId = useId();\n\n // A new status from the app wins over what the block showed meanwhile.\n useEffect(() => setLocal(null), [status]);\n\n const shown = local?.status ?? status ?? \"pending\";\n const pending = shown === \"pending\";\n const facts = (details ?? []).filter(Boolean);\n\n async function decide(kindOf) {\n const fn = kindOf === \"approve\" ? onApprove : onReject;\n if (!fn) return;\n setBusy(kindOf);\n setError(null);\n let res;\n try {\n res = await fn(kindOf === \"reject\" ? reason.trim() : undefined);\n } catch (e) {\n res = { ok: false, error: e?.message };\n }\n setBusy(null);\n if (res?.ok) {\n setLocal({ status: kindOf === \"approve\" ? \"approved\" : \"rejected\", note: kindOf === \"reject\" ? reason.trim() : undefined, at: new Date() });\n setMode(\"idle\");\n } else {\n setError(res?.error || (kindOf === \"approve\" ? \"The approval was not saved. Try again.\" : \"The rejection was not saved. Try again.\"));\n }\n }\n\n const note = local?.note ?? decisionNote;\n const at = local?.at ?? decidedAt;\n const by = local ? \"you\" : decidedBy;\n\n return (\n <section aria-label={title} className={cx(\"grid gap-4 rounded-lg border border-border bg-card p-4 text-card-foreground sm:p-5\", className)}>\n <header className=\"grid gap-2\">\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <h2 className=\"min-w-0 text-lg font-semibold leading-tight\">{title}</h2>\n <div className=\"flex flex-wrap gap-1.5\">\n {kind ? <Badge variant=\"soft\">{kind}</Badge> : null}\n <StatusBadge status={shown} />\n </div>\n </div>\n {showContext ? <Requester requester={requester} requestedAt={requestedAt} format={format} /> : null}\n </header>\n\n {facts.length ? (\n <dl className=\"grid gap-4\">\n {facts.map((f, i) => (\n <div key={f.label ?? i} className=\"flex items-center gap-3\">\n {f.icon ? (\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground\">\n <Icon name={f.icon} size={16} />\n </span>\n ) : null}\n <div className=\"grid min-w-0 gap-0.5\">\n <dt className=\"text-xs text-muted-foreground\">{f.label}</dt>\n <dd data-nums className={cx(\"min-w-0 break-words\", f.emphasis ? \"text-xl font-semibold\" : \"text-sm font-medium\")}>\n {f.value}\n </dd>\n </div>\n </div>\n ))}\n </dl>\n ) : null}\n\n {showContext && justification ? (\n <div className=\"grid gap-1\">\n <p className=\"text-xs text-muted-foreground\">Why</p>\n <p className=\"text-sm leading-relaxed text-foreground\">{justification}</p>\n </div>\n ) : null}\n\n <Separator />\n\n {pending ? (\n mode === \"rejecting\" ? (\n <form\n className=\"grid gap-3\"\n onSubmit={(e) => {\n e.preventDefault();\n if (reasonRequired && !reason.trim()) return;\n decide(\"reject\");\n }}\n >\n <Field id={reasonId} label=\"Why is it rejected?\" hint=\"The requester sees this.\" required={reasonRequired}>\n <Textarea\n id={reasonId}\n rows={3}\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n autoFocus\n disabled={busy !== null}\n placeholder=\"Over this quarter's budget; ask again in January.\"\n />\n </Field>\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button\n variant=\"quiet\"\n onClick={() => {\n setMode(\"idle\");\n setError(null);\n }}\n disabled={busy !== null}\n >\n Back\n </Button>\n <Button type=\"submit\" variant=\"danger\" loading={busy === \"reject\"} disabled={reasonRequired && !reason.trim()}>\n {rejectLabel}\n </Button>\n </div>\n </form>\n ) : (\n <div className=\"flex gap-3\">\n {onReject ? (\n <Button\n className=\"flex-1\"\n onClick={() => {\n setError(null);\n if (reasonRequired) setMode(\"rejecting\");\n else decide(\"reject\");\n }}\n loading={busy === \"reject\"}\n disabled={busy !== null}\n >\n {rejectLabel}\n </Button>\n ) : null}\n {onApprove ? (\n <Button className=\"flex-1\" variant=\"primary\" icon=\"check\" onClick={() => decide(\"approve\")} loading={busy === \"approve\"} disabled={busy !== null}>\n {approveLabel}\n </Button>\n ) : null}\n </div>\n )\n ) : (\n <div role=\"status\" className=\"flex items-start gap-3 text-sm\">\n <Icon name={shown === \"approved\" ? \"circle-check\" : \"circle-x\"} size={20} className={cx(\"mt-0.5 shrink-0\", shown === \"approved\" ? \"text-success\" : \"text-destructive\")} />\n <div className=\"min-w-0\">\n <p className=\"font-medium\">\n {shown === \"approved\" ? \"Approved\" : \"Rejected\"}\n {by ? ` by ${by}` : \"\"}\n {at ? (\n <span className=\"font-normal text-muted-foreground\">\n {\" · \"}\n <When value={at} format={format} />\n </span>\n ) : null}\n </p>\n {note ? <p className=\"mt-0.5 text-muted-foreground\">{note}</p> : null}\n </div>\n </div>\n )}\n\n {error ? (\n <Notice tone=\"danger\" onDismiss={() => setError(null)}>\n {error}\n </Notice>\n ) : null}\n </section>\n );\n}\n\n/* ------------------------------------------------------------------------\n ApprovalRequest\n ------------------------------------------------------------------------ */\n\nexport function ApprovalRequest({\n variant = \"card\",\n title,\n kind,\n details,\n sections,\n requester,\n requestedAt,\n justification,\n status = \"pending\",\n decidedBy,\n decidedAt,\n decisionNote,\n onApprove,\n onReject,\n reasonRequired = true,\n approveLabel = \"Approve\",\n rejectLabel = \"Reject\",\n heading,\n description,\n formatDate = defaultFormat,\n loading = false,\n emptyLabel = \"Nothing is waiting for a decision.\",\n className,\n}) {\n const card = {\n title,\n kind,\n details,\n requester,\n requestedAt,\n justification,\n status,\n decidedBy,\n decidedAt,\n decisionNote,\n onApprove,\n onReject,\n reasonRequired,\n approveLabel,\n rejectLabel,\n format: formatDate,\n };\n\n if (loading) {\n return (\n <div aria-busy=\"true\" aria-label=\"Loading the request\" className={cx(\"grid w-full max-w-md gap-4 rounded-lg border border-border bg-card p-5\", className)}>\n <Skeleton className=\"w-2/3\" />\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <Skeleton shape=\"rect\" className=\"size-8\" />\n <Skeleton className=\"w-1/2\" />\n </div>\n ))}\n <div className=\"flex gap-3\">\n <Skeleton shape=\"rect\" className=\"h-11 flex-1\" />\n <Skeleton shape=\"rect\" className=\"h-11 flex-1\" />\n </div>\n </div>\n );\n }\n if (!title) return <Empty icon=\"inbox\" title=\"No request\" body={emptyLabel} className={className} />;\n\n if (variant !== \"page\") {\n return <DecisionCard {...card} showContext className={cx(\"w-full max-w-md\", className)} />;\n }\n\n const groups = (sections ?? []).filter(Boolean);\n return (\n <div className={cx(\"grid gap-8 lg:grid-cols-[minmax(0,1fr)_24rem] lg:items-start\", className)}>\n <div className=\"grid min-w-0 gap-6\">\n <div className=\"grid gap-1\">\n <h1 className=\"text-2xl font-semibold leading-tight\">{heading ?? title}</h1>\n {description ? <p className=\"text-muted-foreground\">{description}</p> : null}\n </div>\n {groups.map((g, gi) => (\n <section key={g.title ?? gi} className=\"grid gap-4 border-t border-border pt-6\">\n {g.title ? <h2 className=\"text-lg font-semibold\">{g.title}</h2> : null}\n <dl className=\"grid gap-4 sm:grid-cols-2\">\n {(g.fields ?? []).filter(Boolean).map((f, fi) => (\n <div key={f.label ?? fi} className=\"grid min-w-0 gap-1\">\n <dt className=\"text-sm font-medium\">{f.label}</dt>\n <dd className={cx(\"min-w-0 break-words text-muted-foreground\", f.emphasis && \"font-semibold text-foreground\")}>{f.value}</dd>\n </div>\n ))}\n </dl>\n </section>\n ))}\n {requester || requestedAt || justification ? (\n <section className=\"grid gap-4 border-t border-border pt-6\">\n <h2 className=\"text-lg font-semibold\">The request</h2>\n <dl className=\"grid gap-4 sm:grid-cols-2\">\n {requester ? (\n <div className=\"grid min-w-0 gap-1\">\n <dt className=\"text-sm font-medium\">Requested by</dt>\n <dd className=\"flex min-w-0 items-center gap-2 text-muted-foreground\">\n <Avatar name={requester.name} src={requester.src} size=\"xs\" />\n <span className=\"min-w-0\">\n {requester.name}\n {requester.role ? ` (${requester.role})` : \"\"}\n </span>\n </dd>\n </div>\n ) : null}\n {requestedAt ? (\n <div className=\"grid gap-1\">\n <dt className=\"text-sm font-medium\">Requested on</dt>\n <dd className=\"text-muted-foreground\">\n <When value={requestedAt} format={formatDate} />\n </dd>\n </div>\n ) : null}\n {justification ? (\n <div className=\"grid gap-1 sm:col-span-2\">\n <dt className=\"text-sm font-medium\">Why</dt>\n <dd className=\"text-sm leading-relaxed text-muted-foreground\">{justification}</dd>\n </div>\n ) : null}\n </dl>\n </section>\n ) : null}\n </div>\n <div className=\"lg:sticky lg:top-6\">\n <DecisionCard {...card} showContext={false} />\n </div>\n </div>\n );\n}\n"
|
|
22
|
+
"content": "\"use client\";\n\nimport { useEffect, useId, useState } from \"react\";\nimport { Avatar, Badge, Button, Empty, Field, Icon, Notice, Separator, Skeleton, Textarea, cx, toLocalDate } from \"../../ui\";\n\n// ApprovalRequest — one request waiting on a decision: what is asked for\n// (label/value facts), who asked and when, why, and Approve / Reject. The\n// decision goes out through `onApprove()` / `onReject(reason)`; the\n// block shows it working, shows a failure, and once it lands shows the\n// outcome in place of the buttons.\n\nconst STATUS = {\n pending: { tone: \"warning\", label: \"Pending approval\" },\n approved: { tone: \"success\", label: \"Approved\" },\n rejected: { tone: \"danger\", label: \"Rejected\" },\n};\n\n// A day alone (\"2026-09-28\", a `date` column) reads as that day wherever\n// the viewer is: toLocalDate makes it local midnight, never UTC's.\nfunction defaultFormat(value) {\n if (!value) return \"\";\n const d = toLocalDate(value);\n if (!d) return String(value);\n return new Intl.DateTimeFormat(undefined, { dateStyle: \"long\" }).format(d);\n}\n\nfunction When({ value, format }) {\n if (!value) return null;\n const iso = value instanceof Date ? value.toISOString() : String(value);\n return (\n <time dateTime={iso} suppressHydrationWarning>\n {format(value)}\n </time>\n );\n}\n\nfunction StatusBadge({ status }) {\n const s = STATUS[status] ?? STATUS.pending;\n return (\n <Badge tone={s.tone} variant=\"soft\">\n {s.label}\n </Badge>\n );\n}\n\nfunction Requester({ requester, requestedAt, format }) {\n if (!requester && !requestedAt) return null;\n return (\n <div className=\"flex min-w-0 items-center gap-2 text-sm text-muted-foreground\">\n {requester ? <Avatar name={requester.name} src={requester.src} size=\"xs\" /> : null}\n <p className=\"min-w-0\">\n {requester ? (\n <>\n <span className=\"font-medium text-foreground\">{requester.name}</span>\n {requester.role ? `, ${requester.role}` : \"\"}\n </>\n ) : null}\n {requester && requestedAt ? \" · \" : null}\n <When value={requestedAt} format={format} />\n </p>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------------\n The card: facts and the decision\n ------------------------------------------------------------------------ */\n\nfunction DecisionCard({\n title,\n kind,\n details,\n requester,\n requestedAt,\n justification,\n status,\n decidedBy,\n decidedAt,\n decisionNote,\n onApprove,\n onReject,\n reasonRequired,\n approveLabel,\n rejectLabel,\n format,\n showContext,\n className,\n}) {\n const [local, setLocal] = useState(null);\n const [mode, setMode] = useState(\"idle\"); // idle · rejecting\n const [busy, setBusy] = useState(null); // approve · reject\n const [reason, setReason] = useState(\"\");\n const [error, setError] = useState(null);\n const reasonId = useId();\n\n // A new status from the app wins over what the block showed meanwhile.\n useEffect(() => setLocal(null), [status]);\n\n const shown = local?.status ?? status ?? \"pending\";\n const pending = shown === \"pending\";\n const facts = (details ?? []).filter(Boolean);\n\n async function decide(kindOf) {\n const fn = kindOf === \"approve\" ? onApprove : onReject;\n if (!fn) return;\n setBusy(kindOf);\n setError(null);\n let res;\n try {\n res = await fn(kindOf === \"reject\" ? reason.trim() : undefined);\n } catch (e) {\n res = { ok: false, error: e?.message };\n }\n setBusy(null);\n if (res?.ok) {\n setLocal({ status: kindOf === \"approve\" ? \"approved\" : \"rejected\", note: kindOf === \"reject\" ? reason.trim() : undefined, at: new Date() });\n setMode(\"idle\");\n } else {\n setError(res?.error || (kindOf === \"approve\" ? \"The approval was not saved. Try again.\" : \"The rejection was not saved. Try again.\"));\n }\n }\n\n const note = local?.note ?? decisionNote;\n const at = local?.at ?? decidedAt;\n const by = local ? \"you\" : decidedBy;\n\n return (\n <section aria-label={title} className={cx(\"grid gap-4 rounded-lg border border-border bg-card p-4 text-card-foreground sm:p-5\", className)}>\n <header className=\"grid gap-2\">\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <h2 className=\"min-w-0 text-lg font-semibold leading-tight\">{title}</h2>\n <div className=\"flex flex-wrap gap-1.5\">\n {kind ? <Badge variant=\"soft\">{kind}</Badge> : null}\n <StatusBadge status={shown} />\n </div>\n </div>\n {showContext ? <Requester requester={requester} requestedAt={requestedAt} format={format} /> : null}\n </header>\n\n {facts.length ? (\n <dl className=\"grid gap-4\">\n {facts.map((f, i) => (\n <div key={f.label ?? i} className=\"flex items-center gap-3\">\n {f.icon ? (\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground\">\n <Icon name={f.icon} size={16} />\n </span>\n ) : null}\n <div className=\"grid min-w-0 gap-0.5\">\n <dt className=\"text-xs text-muted-foreground\">{f.label}</dt>\n <dd data-nums className={cx(\"min-w-0 break-words\", f.emphasis ? \"text-xl font-semibold\" : \"text-sm font-medium\")}>\n {f.value}\n </dd>\n </div>\n </div>\n ))}\n </dl>\n ) : null}\n\n {showContext && justification ? (\n <div className=\"grid gap-1\">\n <p className=\"text-xs text-muted-foreground\">Why</p>\n <p className=\"text-sm leading-relaxed text-foreground\">{justification}</p>\n </div>\n ) : null}\n\n <Separator />\n\n {pending ? (\n mode === \"rejecting\" ? (\n <form\n className=\"grid gap-3\"\n onSubmit={(e) => {\n e.preventDefault();\n if (reasonRequired && !reason.trim()) return;\n decide(\"reject\");\n }}\n >\n <Field id={reasonId} label=\"Why is it rejected?\" hint=\"The requester sees this.\" required={reasonRequired}>\n <Textarea\n id={reasonId}\n rows={3}\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n autoFocus\n disabled={busy !== null}\n placeholder=\"Over this quarter's budget; ask again in January.\"\n />\n </Field>\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button\n variant=\"quiet\"\n onClick={() => {\n setMode(\"idle\");\n setError(null);\n }}\n disabled={busy !== null}\n >\n Back\n </Button>\n <Button type=\"submit\" variant=\"danger\" loading={busy === \"reject\"} disabled={reasonRequired && !reason.trim()}>\n {rejectLabel}\n </Button>\n </div>\n </form>\n ) : (\n <div className=\"flex gap-3\">\n {onReject ? (\n <Button\n className=\"flex-1\"\n onClick={() => {\n setError(null);\n if (reasonRequired) setMode(\"rejecting\");\n else decide(\"reject\");\n }}\n loading={busy === \"reject\"}\n disabled={busy !== null}\n >\n {rejectLabel}\n </Button>\n ) : null}\n {onApprove ? (\n <Button className=\"flex-1\" variant=\"primary\" icon=\"check\" onClick={() => decide(\"approve\")} loading={busy === \"approve\"} disabled={busy !== null}>\n {approveLabel}\n </Button>\n ) : null}\n </div>\n )\n ) : (\n <div role=\"status\" className=\"flex items-start gap-3 text-sm\">\n <Icon name={shown === \"approved\" ? \"circle-check\" : \"circle-x\"} size={20} className={cx(\"mt-0.5 shrink-0\", shown === \"approved\" ? \"text-success\" : \"text-destructive\")} />\n <div className=\"min-w-0\">\n <p className=\"font-medium\">\n {shown === \"approved\" ? \"Approved\" : \"Rejected\"}\n {by ? ` by ${by}` : \"\"}\n {at ? (\n <span className=\"font-normal text-muted-foreground\">\n {\" · \"}\n <When value={at} format={format} />\n </span>\n ) : null}\n </p>\n {note ? <p className=\"mt-0.5 text-muted-foreground\">{note}</p> : null}\n </div>\n </div>\n )}\n\n {error ? (\n <Notice tone=\"danger\" onDismiss={() => setError(null)}>\n {error}\n </Notice>\n ) : null}\n </section>\n );\n}\n\n/* ------------------------------------------------------------------------\n ApprovalRequest\n ------------------------------------------------------------------------ */\n\nexport function ApprovalRequest({\n variant = \"card\",\n title,\n kind,\n details,\n sections,\n requester,\n requestedAt,\n justification,\n status = \"pending\",\n decidedBy,\n decidedAt,\n decisionNote,\n onApprove,\n onReject,\n reasonRequired = true,\n approveLabel = \"Approve\",\n rejectLabel = \"Reject\",\n heading,\n description,\n formatDate = defaultFormat,\n loading = false,\n emptyLabel = \"Nothing is waiting for a decision.\",\n className,\n}) {\n const card = {\n title,\n kind,\n details,\n requester,\n requestedAt,\n justification,\n status,\n decidedBy,\n decidedAt,\n decisionNote,\n onApprove,\n onReject,\n reasonRequired,\n approveLabel,\n rejectLabel,\n format: formatDate,\n };\n\n if (loading) {\n return (\n <div aria-busy=\"true\" aria-label=\"Loading the request\" className={cx(\"grid w-full max-w-md gap-4 rounded-lg border border-border bg-card p-5\", className)}>\n <Skeleton className=\"w-2/3\" />\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <Skeleton shape=\"rect\" className=\"size-8\" />\n <Skeleton className=\"w-1/2\" />\n </div>\n ))}\n <div className=\"flex gap-3\">\n <Skeleton shape=\"rect\" className=\"h-11 flex-1\" />\n <Skeleton shape=\"rect\" className=\"h-11 flex-1\" />\n </div>\n </div>\n );\n }\n if (!title) return <Empty icon=\"inbox\" title=\"No request\" body={emptyLabel} className={className} />;\n\n if (variant !== \"page\") {\n return <DecisionCard {...card} showContext className={cx(\"w-full max-w-md\", className)} />;\n }\n\n const groups = (sections ?? []).filter(Boolean);\n return (\n <div className={cx(\"grid gap-8 lg:grid-cols-[minmax(0,1fr)_24rem] lg:items-start\", className)}>\n <div className=\"grid min-w-0 gap-6\">\n <div className=\"grid gap-1\">\n <h1 className=\"text-2xl font-semibold leading-tight\">{heading ?? title}</h1>\n {description ? <p className=\"text-muted-foreground\">{description}</p> : null}\n </div>\n {groups.map((g, gi) => (\n <section key={g.title ?? gi} className=\"grid gap-4 border-t border-border pt-6\">\n {g.title ? <h2 className=\"text-lg font-semibold\">{g.title}</h2> : null}\n <dl className=\"grid gap-4 sm:grid-cols-2\">\n {(g.fields ?? []).filter(Boolean).map((f, fi) => (\n <div key={f.label ?? fi} className=\"grid min-w-0 gap-1\">\n <dt className=\"text-sm font-medium\">{f.label}</dt>\n <dd className={cx(\"min-w-0 break-words text-muted-foreground\", f.emphasis && \"font-semibold text-foreground\")}>{f.value}</dd>\n </div>\n ))}\n </dl>\n </section>\n ))}\n {requester || requestedAt || justification ? (\n <section className=\"grid gap-4 border-t border-border pt-6\">\n <h2 className=\"text-lg font-semibold\">The request</h2>\n <dl className=\"grid gap-4 sm:grid-cols-2\">\n {requester ? (\n <div className=\"grid min-w-0 gap-1\">\n <dt className=\"text-sm font-medium\">Requested by</dt>\n <dd className=\"flex min-w-0 items-center gap-2 text-muted-foreground\">\n <Avatar name={requester.name} src={requester.src} size=\"xs\" />\n <span className=\"min-w-0\">\n {requester.name}\n {requester.role ? ` (${requester.role})` : \"\"}\n </span>\n </dd>\n </div>\n ) : null}\n {requestedAt ? (\n <div className=\"grid gap-1\">\n <dt className=\"text-sm font-medium\">Requested on</dt>\n <dd className=\"text-muted-foreground\">\n <When value={requestedAt} format={formatDate} />\n </dd>\n </div>\n ) : null}\n {justification ? (\n <div className=\"grid gap-1 sm:col-span-2\">\n <dt className=\"text-sm font-medium\">Why</dt>\n <dd className=\"text-sm leading-relaxed text-muted-foreground\">{justification}</dd>\n </div>\n ) : null}\n </dl>\n </section>\n ) : null}\n </div>\n <div className=\"lg:sticky lg:top-6\">\n <DecisionCard {...card} showContext={false} />\n </div>\n </div>\n );\n}\n"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"path": "registry/blocks/approval-request/README.md",
|
|
26
26
|
"type": "registry:file",
|
|
27
27
|
"target": "@components/makersclaw/blocks/approval-request/README.md",
|
|
28
|
-
"content": "# ApprovalRequest\n\nOne request waiting on a decision: what is asked for (label and value\nfacts, the amount in large type), who asked and when, why, and Approve /\nReject. Reject asks for a reason the requester will see. The decision\ngoes out through your callbacks; while one runs its button spins, when it\nfails the card says why and the buttons stay, and once it lands the card\nshows the outcome (\"Approved by you · 23 September 2026\") in their place.\n\n```jsx\nimport { ApprovalRequest } from \"../components/blocks/approval-request\"; // a Makersclaw app (\"./blocks/approval-request\" from components/)\nimport { ApprovalRequest } from \"@/components/makersclaw/blocks/approval-request\"; // a shadcn project\n```\n\nA client component. Load the request in the server surface (README §4)\nand pass its fields in.\n\n## The data\n\n- `details` — the card's facts, in order:\n `[{ label, value, icon?, emphasis? }]`. `value` is a string or a node;\n `icon` is a kit `Icon` name; `emphasis` sets the value large (the\n amount).\n- `sections` — `variant=\"page\"` only, the detail beside the card:\n `[{ title, fields: [{ label, value, emphasis? }] }]`.\n- `requester` — `{ name, role?, src? }` (`src` a picture URL).\n\n## Props\n\n- `title` — what is asked for (\"Linear Business\", \"Refund for order\n 20931\"). Without it (and not `loading`) the block shows `emptyLabel`.\n- `onApprove()` — save the yes. Return `{ ok: true }` or\n `{ ok: false, error }`; a throw counts as failed and its message shows.\n- `onReject(reason)` — save the no, with the reason typed (a trimmed\n string, \"\" when `reasonRequired` is off). Same return as `onApprove`.\n Leave either out and its button is not drawn.\n- `variant` — `\"card\"` (default: the card alone, with the requester, the\n date and the reason why) or `\"page\"`: a heading, the `sections` and \"The\n request\" (requester, date, why) on the left, the card on the right\n (under them on a phone), sticky from 1024px.\n- `status` — `\"pending\"` (default) · `\"approved\"` · `\"rejected\"`. The\n block shows its own outcome as soon as a decision saves; a new\n `status` from you replaces it.\n- `decidedBy` · `decidedAt` · `decisionNote` — for a request decided\n before: who, when, and the reason, shown under the outcome.\n- `kind` — a small badge beside the title (\"Per seat\", \"Refund\").\n- `requestedAt` — an ISO
|
|
28
|
+
"content": "# ApprovalRequest\n\nOne request waiting on a decision: what is asked for (label and value\nfacts, the amount in large type), who asked and when, why, and Approve /\nReject. Reject asks for a reason the requester will see. The decision\ngoes out through your callbacks; while one runs its button spins, when it\nfails the card says why and the buttons stay, and once it lands the card\nshows the outcome (\"Approved by you · 23 September 2026\") in their place.\n\n```jsx\nimport { ApprovalRequest } from \"../components/blocks/approval-request\"; // a Makersclaw app (\"./blocks/approval-request\" from components/)\nimport { ApprovalRequest } from \"@/components/makersclaw/blocks/approval-request\"; // a shadcn project\n```\n\nA client component. Load the request in the server surface (README §4)\nand pass its fields in.\n\n## The data\n\n- `details` — the card's facts, in order:\n `[{ label, value, icon?, emphasis? }]`. `value` is a string or a node;\n `icon` is a kit `Icon` name; `emphasis` sets the value large (the\n amount).\n- `sections` — `variant=\"page\"` only, the detail beside the card:\n `[{ title, fields: [{ label, value, emphasis? }] }]`.\n- `requester` — `{ name, role?, src? }` (`src` a picture URL).\n\n## Props\n\n- `title` — what is asked for (\"Linear Business\", \"Refund for order\n 20931\"). Without it (and not `loading`) the block shows `emptyLabel`.\n- `onApprove()` — save the yes. Return `{ ok: true }` or\n `{ ok: false, error }`; a throw counts as failed and its message shows.\n- `onReject(reason)` — save the no, with the reason typed (a trimmed\n string, \"\" when `reasonRequired` is off). Same return as `onApprove`.\n Leave either out and its button is not drawn.\n- `variant` — `\"card\"` (default: the card alone, with the requester, the\n date and the reason why) or `\"page\"`: a heading, the `sections` and \"The\n request\" (requester, date, why) on the left, the card on the right\n (under them on a phone), sticky from 1024px.\n- `status` — `\"pending\"` (default) · `\"approved\"` · `\"rejected\"`. The\n block shows its own outcome as soon as a decision saves; a new\n `status` from you replaces it.\n- `decidedBy` · `decidedAt` · `decisionNote` — for a request decided\n before: who, when, and the reason, shown under the outcome.\n- `kind` — a small badge beside the title (\"Per seat\", \"Refund\").\n- `requestedAt` — an ISO time, a `Date`, or a day (`\"2026-09-28\"`, a\n `date` column), which reads as that day wherever the viewer is.\n- `justification` — the requester's reason, as a paragraph.\n- `reasonRequired` — default `true`: Reject opens a reason field and\n sends only when it is filled. `false`: Reject sends at once.\n- `approveLabel` · `rejectLabel` — default \"Approve\" / \"Reject\"\n (\"Refund €149.00\", \"Decline\").\n- `heading` · `description` — `page`: the page's title (default `title`)\n and the line under it.\n- `formatDate(value)` — how a date reads; default the viewer's long date\n (\"21 September 2026\").\n- `loading` — a skeleton card while the request loads.\n- `emptyLabel` — default \"Nothing is waiting for a decision.\".\n- `className` — layout around it (width, margin). Never colour.\n\n## Example\n\n```jsx\n// components/PurchaseApproval.jsx — client\n\"use client\";\nimport { useRouter } from \"next/navigation\";\nimport { updateRow } from \"../lib/actions\";\nimport { ApprovalRequest } from \"./blocks/approval-request\";\n\nconst usd = new Intl.NumberFormat(\"en-US\", { style: \"currency\", currency: \"USD\" });\n\nexport default function PurchaseApproval({ request }) {\n const router = useRouter();\n const decide = (status, reason) =>\n updateRow({ source: \"public.purchase_requests\", id: request.id, patch: { status, reason: reason ?? null }, detail: request.product }).then(\n (res) => (res.ok && router.refresh(), res),\n );\n return (\n <ApprovalRequest\n title={request.product}\n kind={request.pricing}\n details={[\n { label: \"Starts\", value: request.starts_on, icon: \"calendar\" },\n { label: \"Seats\", value: `${request.seats} people`, icon: \"users\" },\n { label: \"Yearly cost\", value: usd.format(request.amount), icon: \"dollar\", emphasis: true },\n ]}\n requester={{ name: request.requested_by, role: request.team }}\n requestedAt={request.created_at}\n justification={request.why}\n status={request.status}\n decidedBy={request.decided_by}\n decidedAt={request.decided_at}\n decisionNote={request.reason}\n onApprove={() => decide(\"approved\")}\n onReject={(reason) => decide(\"rejected\", reason)}\n />\n );\n}\n```\n\n## What it will not do\n\n- List many requests: put them in a `data-table` and open one in this\n block (a `Sheet`), or render one per row.\n- Take a decision in several steps (two approvers, a delegate): `status`\n is one of three.\n- Edit the request: its facts are read-only here.\n\nA request 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 approval-request` restores\nthem.\n\n## Credits\n\nPorted from Creative Tim UI (https://github.com/creativetimofficial/ui, software-purchase-01 and software-purchase-card, 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/approval-request. Import: import { ApprovalRequest } from \"@/components/makersclaw/blocks/approval-request\". Props and an example: components/makersclaw/blocks/approval-request/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
46
46
|
"meta": {
|
|
47
47
|
"makersclaw": {
|
|
48
|
-
"version": "0.
|
|
48
|
+
"version": "0.5.1",
|
|
49
49
|
"useWhen": "Someone must say yes or no to one thing another person asked for: a purchase, a refund, time off, a discount, an access request, a published post. Not for a queue of many requests (a data-table with row actions) or a yes/no about the founder's own action (kit Confirm)."
|
|
50
50
|
}
|
|
51
51
|
}
|
package/r/audio-player.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "AudioPlayer",
|
|
6
6
|
"description": "A waveform audio player (wavesurfer.js) in the workspace theme: play, skip back and forward, speed, mute; regions (spans of the audio in a theme tone, labelled in a lane under the waveform, click to play one) and markers (pins at moments, click to jump); a hover time readout; drag across the waveform to mark a new region; add a marker at the playhead; keyboard like a video player (K, J, L, M, arrows). Time events and a handle (seek, play a range) for syncing a transcript.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.5.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"wavesurfer.js@8.0.0",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"docs": "Installed to components/makersclaw/blocks/audio-player. Import: import { AudioPlayer, useAudioPlayer, formatTime, formatRange, spokenTime, regionAt, TONES, RATES } from \"@/components/makersclaw/blocks/audio-player\". Props and an example: components/makersclaw/blocks/audio-player/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
68
68
|
"meta": {
|
|
69
69
|
"makersclaw": {
|
|
70
|
-
"version": "0.
|
|
70
|
+
"version": "0.5.1",
|
|
71
71
|
"useWhen": "A screen plays a recording: a call or meeting recording, a voice note, a podcast clip, a voicemail, a sound file on a record. The call-review block is built on it. Not for video, not for recording audio from the microphone."
|
|
72
72
|
}
|
|
73
73
|
}
|
package/r/auth-form.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "AuthForm",
|
|
6
6
|
"description": "The account screens in one form: sign in (password, email only, or provider buttons for Google, GitHub, Apple and Microsoft), sign up with extra fields, a magic link, a reset link, and changing a password against a live rules checklist. The auth is yours: each submit is an async callback that returns { ok } or an error the form shows.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.5.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"docs": "Installed to components/makersclaw/blocks/auth-form. Import: import { AuthForm, PASSWORD_RULES } from \"@/components/makersclaw/blocks/auth-form\". Props and an example: components/makersclaw/blocks/auth-form/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
49
49
|
"meta": {
|
|
50
50
|
"makersclaw": {
|
|
51
|
-
"version": "0.
|
|
51
|
+
"version": "0.5.1",
|
|
52
52
|
"useWhen": "An app needs a sign-in, sign-up, forgot-password or change-password screen, or a settings page where a signed-in person changes their password."
|
|
53
53
|
}
|
|
54
54
|
}
|
package/r/board.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Board",
|
|
6
6
|
"description": "Records as cards in columns, one per stage; drag a card to another column (mouse, touch, or the card's menu from the keyboard) and the stage is saved. Optional column icons, a per-column total (\"13pt\"), a category colour on each card's edge, and a stacked layout of folding sections with compact rows.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.5.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"docs": "Installed to components/makersclaw/blocks/board. Import: import { Board } from \"@/components/makersclaw/blocks/board\". Props and an example: components/makersclaw/blocks/board/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
49
49
|
"meta": {
|
|
50
50
|
"makersclaw": {
|
|
51
|
-
"version": "0.
|
|
51
|
+
"version": "0.5.1",
|
|
52
52
|
"useWhen": "A process where each record sits in one stage and moves forward: a pipeline, a hiring funnel, a sprint, a content calendar by status, outreach by reply state. Not for records with no stages (a table) or items placed on a timeline."
|
|
53
53
|
}
|
|
54
54
|
}
|