makersclaw 0.3.2 → 0.4.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 (81) hide show
  1. package/README.md +3 -1
  2. package/package.json +1 -1
  3. package/r/annotate.json +3 -3
  4. package/r/approval-request.json +2 -2
  5. package/r/audio-player.json +2 -2
  6. package/r/auth-form.json +2 -2
  7. package/r/board.json +2 -2
  8. package/r/calendar-view.json +3 -3
  9. package/r/cart.json +2 -2
  10. package/r/chart.json +2 -2
  11. package/r/chat.json +2 -2
  12. package/r/code-editor.json +2 -2
  13. package/r/comment-box.json +3 -3
  14. package/r/contact-section.json +2 -2
  15. package/r/core.json +12 -6
  16. package/r/csv-import.json +3 -3
  17. package/r/data-table.json +2 -2
  18. package/r/diagram.json +2 -2
  19. package/r/doc-editor.json +2 -2
  20. package/r/doc-page.json +3 -3
  21. package/r/email-render.json +2 -2
  22. package/r/faq.json +2 -2
  23. package/r/file-upload.json +2 -2
  24. package/r/flow-canvas.json +2 -2
  25. package/r/footer.json +2 -2
  26. package/r/gantt.json +3 -3
  27. package/r/job-panel.json +8 -5
  28. package/r/json-editor.json +3 -3
  29. package/r/map.json +2 -2
  30. package/r/menubar.json +2 -2
  31. package/r/mind-map.json +3 -3
  32. package/r/navigation-menu.json +2 -2
  33. package/r/order-detail.json +2 -2
  34. package/r/org-chart.json +3 -3
  35. package/r/page-builder.json +2 -2
  36. package/r/payment-methods.json +2 -2
  37. package/r/pdf-document.json +2 -2
  38. package/r/pdf-viewer.json +2 -2
  39. package/r/product-detail.json +2 -2
  40. package/r/product-grid.json +3 -3
  41. package/r/record-form.json +2 -2
  42. package/r/record-notes.json +4 -4
  43. package/r/registry.json +131 -123
  44. package/r/search-dialog.json +2 -2
  45. package/r/settings-list.json +2 -2
  46. package/r/setup-checklist.json +2 -2
  47. package/r/share-dialog.json +2 -2
  48. package/r/sidebar.json +2 -2
  49. package/r/signature-pad.json +2 -2
  50. package/r/sketch-canvas.json +2 -2
  51. package/r/spreadsheet.json +2 -2
  52. package/r/stat-cards.json +3 -3
  53. package/r/task-list.json +3 -3
  54. package/r/task-model.json +2 -2
  55. package/r/testimonials.json +2 -2
  56. package/r/transaction-list.json +2 -2
  57. package/r/whiteboard.json +3 -3
  58. package/registry/blocks/job-panel/README.md +40 -3
  59. package/registry/blocks/job-panel/block.json +2 -2
  60. package/registry/blocks/job-panel/demo.jsx +75 -6
  61. package/registry/blocks/job-panel/job-panel.jsx +60 -11
  62. package/src/shadcn.mjs +1 -1
  63. package/templates/next/files/README.md +339 -73
  64. package/templates/next/files/app/auto-refresh.jsx +72 -10
  65. package/templates/next/files/app/claw-alive/route.js +17 -0
  66. package/templates/next/files/app/error-relay.jsx +9 -4
  67. package/templates/next/files/app/escape-relay.jsx +27 -0
  68. package/templates/next/files/app/frame.js +218 -0
  69. package/templates/next/files/app/layout.jsx +18 -3
  70. package/templates/next/files/check/run.mjs +25 -8
  71. package/templates/next/files/components/parts/fixture.jsx +29 -4
  72. package/templates/next/files/components/parts/index.js +2 -1
  73. package/templates/next/files/components/parts/live-work.jsx +235 -0
  74. package/templates/next/files/components/ui/button.jsx +6 -1
  75. package/templates/next/files/components/ui/date-picker.jsx +15 -2
  76. package/templates/next/files/components/ui/icon.jsx +37 -2
  77. package/templates/next/files/lib/claw.js +127 -28
  78. package/templates/next/files/lib/hooks.js +454 -33
  79. package/templates/next/files/lib/platform.js +113 -15
  80. package/templates/next/files/surfaces/README.md +3 -0
  81. package/templates/next/template.json +17 -6
package/r/data-table.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Data table",
6
6
  "description": "Records as a table the founder can work: sort by a column (shift for several), search every column, faceted filters with counts, column visibility, row selection with a bulk-actions bar, a menu per row or its first actions as buttons in the row (async, with a spinner and a failure notice), row click, pagination with a page size, loading, empty and no-matches states, and cards on a phone. Opt in to expandable rows, row groups with subtotals, an aligned totals row, arrow-key navigation and a sticky first column. Client-side over rows, or driven by a server.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -62,7 +62,7 @@
62
62
  "docs": "Installed to components/makersclaw/blocks/data-table. Import: import { DataTable, useControllable, processRows, sortRows, searchRows, filterRows, facetOptions, nextSort, groupRows, groupValueOf } from \"@/components/makersclaw/blocks/data-table\". Props and an example: components/makersclaw/blocks/data-table/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
63
63
  "meta": {
64
64
  "makersclaw": {
65
- "version": "0.3.2",
65
+ "version": "0.4.0",
66
66
  "useWhen": "A list of records to scan, find, sort and act on: invoices, customers, orders, leads, users, tickets. Not for records that move through stages (a board), a few key numbers (stats), or a grid of cards whose picture matters more than its columns."
67
67
  }
68
68
  }
package/r/diagram.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Diagram",
6
6
  "description": "Draws Mermaid text (flowcharts, sequence, class, state, ER, Gantt, pie, mind maps, timelines, git graphs…) in the workspace theme: the tokens are read from the page at run time and handed to Mermaid, so nodes, lines, text and chart colours are the theme's and follow light/dark. Zoom and pan (buttons, Cmd/Ctrl+scroll, pinch, drag, keys), fit to view, a Source tab with line numbers, copy the source or the SVG, download SVG. A mistake shows its line and Mermaid's words, and keeps the last drawing that worked. On the server the frame shows the Mermaid text.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "mermaid@12.0.0"
@@ -67,7 +67,7 @@
67
67
  "docs": "Installed to components/makersclaw/blocks/diagram. Import: import { Diagram, diagramKind, diagramTitle, describeError, mermaidTheme } from \"@/components/makersclaw/blocks/diagram\". Props and an example: components/makersclaw/blocks/diagram/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
68
68
  "meta": {
69
69
  "makersclaw": {
70
- "version": "0.3.2",
70
+ "version": "0.4.0",
71
71
  "useWhen": "A screen shows a diagram written as text: a process or decision flow an agent wrote, a sequence of API calls, a data model, a project timeline, an org or system sketch in docs, a mind map. Not for a board people arrange by hand (flow-canvas), freehand drawing (sketch-canvas), or charts of numbers (chart)."
72
72
  }
73
73
  }
package/r/doc-editor.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "DocEditor",
6
6
  "description": "A Notion-style rich-text editor (BlockNote) painted in the workspace theme: headings, lists, checklists, quotes, code, tables, images and dividers from a / menu or markdown shortcuts, a formatting toolbar, drag handles; @mentions and {merge fields} as inline chips; a minimal mode for comment boxes with Cmd/Ctrl+Enter to submit. The document is BlockNote JSON; helpers turn it into markdown or plain text and read its checklist, fields and mentions.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "@blocknote/core@0.55.0",
@@ -75,7 +75,7 @@
75
75
  "docs": "Installed to components/makersclaw/blocks/doc-editor. Import: import { DocEditor, useDocEditor, docToMarkdown, docToText, emptyDoc, isDocEmpty, docChecklist, setChecklistItem, docFields, docMentions } from \"@/components/makersclaw/blocks/doc-editor\". Props and an example: components/makersclaw/blocks/doc-editor/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
76
76
  "meta": {
77
77
  "makersclaw": {
78
- "version": "0.3.2",
78
+ "version": "0.4.0",
79
79
  "useWhen": "A screen edits rich text: a wiki or doc page, notes on a record, a brief, meeting notes, an email body with merge fields, a comment field with mentions. The other text blocks (doc-page, record-notes, comment-box…) are built on it. Not for one line of plain text (the kit's Input) or a form of fields (record-form)."
80
80
  }
81
81
  }
package/r/doc-page.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "title": "DocPage",
6
6
  "description": "A Notion-style page: an emoji icon (picked from a grid), a big plain title, and the rich-text editor under it (doc-editor: / menu, markdown shortcuts, @mentions). Edits autosave through an async onSave a moment after typing stops, with Saved / Saving… / Couldn't save + Retry in the top bar, Cmd/Ctrl+S to save now and unsent edits sent when the page is left. A read-only mode shows the same page with nothing to edit; slots for a breadcrumb, actions and a meta line.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/doc-editor.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/doc-editor.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -54,7 +54,7 @@
54
54
  "docs": "Installed to components/makersclaw/blocks/doc-page. Import: import { DocPage } from \"@/components/makersclaw/blocks/doc-page\". Props and an example: components/makersclaw/blocks/doc-page/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
55
55
  "meta": {
56
56
  "makersclaw": {
57
- "version": "0.3.2",
57
+ "version": "0.4.0",
58
58
  "useWhen": "A screen is one document someone reads and writes: a wiki or handbook page, a playbook, a policy, a project page, a one-pager. The knowledge-base block puts a page tree beside it. Not for a short field of rich text inside a form (doc-editor), notes on a record (record-notes) or a comment (comment-box)."
59
59
  }
60
60
  }
@@ -5,7 +5,7 @@
5
5
  "title": "EmailRender",
6
6
  "description": "Email HTML from React (react-email) in the workspace theme: a layout with the 600px column and the inbox preview line, a header brand line, a footer with the address and unsubscribe link, buttons, headings and text, and DocEmail, which draws a doc-editor document as email markup (lists, quotes, code, tables, images, merge fields). Email cannot read CSS variables, so resolveEmailTheme reads the tokens where the editor sits and hands the email plain hex values; renderEmail returns { html, text }, in the browser for a preview and on the server for a send.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "@react-email/components@1.0.12",
@@ -66,7 +66,7 @@
66
66
  "docs": "Installed to components/makersclaw/blocks/email-render. Import: import { EmailLayout, EmailCard, EmailHeader, EmailFooter, EmailButton, EmailHeading, EmailText, EmailDivider, EMAIL_WIDTH, DocEmail, fieldText, renderEmail, emailPlainText, EMAIL_TOKENS, EMAIL_FALLBACK, resolveEmailTheme, emailColor, emailFontStack, emailThemeKey, luminance, toHex } from \"@/components/makersclaw/blocks/email-render\". Props and an example: components/makersclaw/blocks/email-render/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
67
67
  "meta": {
68
68
  "makersclaw": {
69
- "version": "0.3.2",
69
+ "version": "0.4.0",
70
70
  "useWhen": "Code that must produce an email's HTML: a newsletter, a transactional email (receipt, invite, reset), a digest, a notification, a test send from an editor. newsletter-builder is built on it. Not for writing the email (doc-editor or email-composer), sending it (your sender or the app's server) or a web page (page-builder)."
71
71
  }
72
72
  }
package/r/faq.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "FAQ",
6
6
  "description": "A marketing section of questions and answers: an accordion (one open at a time, or several), a grid of cards with optional icons, or a plain list with every answer shown; the title above or beside them, and an optional \"need more help?\" call to action. Loading and empty states.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -43,7 +43,7 @@
43
43
  "docs": "Installed to components/makersclaw/blocks/faq. Import: import { Faq } from \"@/components/makersclaw/blocks/faq\". Props and an example: components/makersclaw/blocks/faq/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
44
44
  "meta": {
45
45
  "makersclaw": {
46
- "version": "0.3.2",
46
+ "version": "0.4.0",
47
47
  "useWhen": "The questions section of a public page: a landing page, pricing, a help centre's front page, a product page. Not for a searchable help centre with articles and categories, or collapsible settings inside an app (use the kit's Accordion)."
48
48
  }
49
49
  }
@@ -5,7 +5,7 @@
5
5
  "title": "FileUpload",
6
6
  "description": "Files chosen or dropped, each uploaded through your async onUpload(file, { onProgress, signal }) with a progress bar, cancel while it runs, a retry when it fails and a remove once done. Variants: a list of file cards, one file at a time, Uploading/Finished sections, image thumbnails (one or two columns), and a profile picture.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -47,7 +47,7 @@
47
47
  "docs": "Installed to components/makersclaw/blocks/file-upload. Import: import { FileUpload, formatSize, fileIcon } from \"@/components/makersclaw/blocks/file-upload\". Props and an example: components/makersclaw/blocks/file-upload/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
48
48
  "meta": {
49
49
  "makersclaw": {
50
- "version": "0.3.2",
50
+ "version": "0.4.0",
51
51
  "useWhen": "A form or page takes files the app stores: attachments on a record, a CSV to import, product photos, a logo, a profile picture. Not for showing files already stored with no way to add (a table or list), and not for a chat message's attachments (the chat block's composer has those)."
52
52
  }
53
53
  }
@@ -5,7 +5,7 @@
5
5
  "title": "FlowCanvas",
6
6
  "description": "A Miro-like board (React Flow) in the workspace theme: sticky notes, shapes, text, frames, cards and images on an infinite dotted canvas; a tool rail with shortcuts, double-click for a sticky, drag-out connectors that re-route as cards move, labels on connectors, a floating toolbar over the selection, right-click menu, undo/redo, copy/paste, snap to grid, minimap, list view and presentation mode. The board is plain JSON; helpers turn it into text or markdown and lay it out. Other blocks add their own node types and rail tools.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "@xyflow/react@12.11.6"
@@ -86,7 +86,7 @@
86
86
  "docs": "Installed to components/makersclaw/blocks/flow-canvas. Import: import { FlowCanvas, useFlowCanvas, useCanvas, NodeShell, NodeHandles, EditableText, Palette, StickySwatch, ColorDot, ShapeGlyph, colorClasses, emptyBoard, createNode, createEdge, cleanBoard, boardToText, boardToMarkdown, autoLayout, snapToCell, frameContents, historyReducer, starterTemplates } from \"@/components/makersclaw/blocks/flow-canvas\". Props and an example: components/makersclaw/blocks/flow-canvas/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
87
87
  "meta": {
88
88
  "makersclaw": {
89
- "version": "0.3.2",
89
+ "version": "0.4.0",
90
90
  "useWhen": "A screen where people arrange things spatially and connect them: a brainstorm, a flowchart or process map, an account or org map, a journey map, a retro, a planning board. The canvas blocks (account-map, process-map, journey-map, idea-board) are built on it. Not for freehand drawing (sketch-canvas), records in stage columns (board), or a chart of numbers (chart)."
91
91
  }
92
92
  }
package/r/footer.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Footer",
6
6
  "description": "A public site's footer: brand and tagline, link columns or one row of links, social buttons in the kit's brand marks (X, LinkedIn, GitHub, YouTube, Instagram, Facebook), and the legal line with its links; optionally a newsletter sign-up with consent (validates, saves through an async onSubscribe returning { ok }, shows the failure), a call to action, a large brand statement, language and currency selects, and a version badge. Simple, columns or centred.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -45,7 +45,7 @@
45
45
  "docs": "Installed to components/makersclaw/blocks/footer. Import: import { Footer } from \"@/components/makersclaw/blocks/footer\". Props and an example: components/makersclaw/blocks/footer/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
46
46
  "meta": {
47
47
  "makersclaw": {
48
- "version": "0.3.2",
48
+ "version": "0.4.0",
49
49
  "useWhen": "The bottom of every page of a public site: a marketing site, a docs hub, a shop, a customer portal. Not for an app's own status bar or a toolbar pinned to the bottom of a screen."
50
50
  }
51
51
  }
package/r/gantt.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "title": "Gantt",
6
6
  "description": "The Gantt view of work, like Linear's timeline or TeamGantt: a task list on the left (groups that fold, subtasks indented under a summary bar) beside a timeline with a two-tier date header, bars coloured by project or status with their progress, milestones as diamonds, dependency arrows (red when the dates disagree), a today line and weekend shading. Drag a bar to move it, an edge to change its start or end, the dot past its end onto another bar to link them; tasks waiting on a moved one slide along while you drag and the finish date shows what the move cost. Zoom day, week, month or quarter; highlight the critical path; click an empty row to schedule an undated task; quick-add per group; arrow keys move a focused bar; search, filters, group and sort from the shared toolbar. One async onChange saves every change; failures roll back with a notice.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/task-model.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/task-model.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -63,7 +63,7 @@
63
63
  "docs": "Installed to components/makersclaw/blocks/gantt. Import: import { Gantt, ZOOMS, ganttRange, headerTiers, planMove, afterBlockersPatch, isMilestone, summarySpan, arrowPath } from \"@/components/makersclaw/blocks/gantt\". Props and an example: components/makersclaw/blocks/gantt/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
64
64
  "meta": {
65
65
  "makersclaw": {
66
- "version": "0.3.2",
66
+ "version": "0.4.0",
67
67
  "useWhen": "A screen that plans dated work and shows what waits on what: a launch plan, a project schedule, a content calendar with lead times, an office move, a release. Not for an undated to-do list (task-list), stage columns (board), or days on a month grid (calendar)."
68
68
  }
69
69
  }
package/r/job-panel.json CHANGED
@@ -3,9 +3,9 @@
3
3
  "name": "job-panel",
4
4
  "type": "registry:block",
5
5
  "title": "JobPanel",
6
- "description": "The panel that follows work the founder asked the app's agent for (useJob): each step's label as it runs, Stop, then done or the reason it stopped — and nothing on screen when no job runs. variant=\"generator\" wraps it in a prompt, the surface's options, Generate and a results shelf, with no model, provider or API key of its own.",
6
+ "description": "The panel that follows work the founder asked the app's agent for (useJob): each step's label as it runs, Stop, \"Needs your answer\" with Answer while the agent waits on the founder, \"Queued · after …\" while it waits its turn, then done or the reason it stopped — and nothing on screen when no job runs. variant=\"generator\" wraps it in a prompt, the surface's options, Generate and a results shelf, with no model, provider or API key of its own.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -19,13 +19,13 @@
19
19
  "path": "registry/blocks/job-panel/job-panel.jsx",
20
20
  "type": "registry:component",
21
21
  "target": "@components/makersclaw/blocks/job-panel/job-panel.jsx",
22
- "content": "\"use client\";\n\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { Button, Card, Empty, Field, Icon, Skeleton, Textarea, cx } from \"../../ui\";\n\n// JobPanel — the panel that follows work the founder asked the app's\n// agent for (useJob in lib/hooks.js, README §5 \"Commissioning work\"). It\n// keeps the four rules a hand-drawn panel breaks: it is not there when\n// nothing runs, it shows each step's label (the founder's phrase, never\n// the tool's name), the agent's text is its account and not the\n// deliverable (the rows are), and Stop / Close are always one press away.\n//\n// const job = useJob();\n// <Button onClick={() => job.start(\"Draft a launch plan for Rowhouse\")}>Generate a launch plan</Button>\n// <JobPanel job={job} title=\"Drafting the launch plan\" doneTitle=\"Launch plan drafted\" />\n//\n// variant=\"generator\" is the same panel inside a small studio: a prompt,\n// the surface's own options, Generate, then the results the job made. The\n// block never calls a model and takes no API key or provider: Generate\n// hands the prompt to the app's agent (job.start, or `onGenerate`), and\n// the results are the rows the job wrote, passed back in as `results`.\n//\n// <JobPanel variant=\"generator\" job={job} promptLabel=\"Describe the photo\"\n// options={<Field …><Select …/></Field>}\n// onGenerate={(prompt) => job.start(`Make a product photo: ${prompt}. Size ${size}.`)}\n// results={photos.map((p) => <PhotoCard key={p.id} photo={p} />)} />\n\nfunction StepMark({ status }) {\n if (status === \"done\") return <Icon name=\"check\" size={14} className=\"shrink-0 text-success\" />;\n if (status === \"failed\") return <Icon name=\"warning\" size={14} className=\"shrink-0 text-destructive\" />;\n return <span aria-hidden=\"true\" className=\"mx-[3px] size-2 shrink-0 animate-pulse rounded-full bg-primary\" />;\n}\n\nexport function JobPanel({ variant = \"panel\", ...props }) {\n if (variant === \"generator\") return <Generator {...props} />;\n return <Panel {...props} />;\n}\n\nfunction Panel({\n job,\n title = \"Working on it\",\n doneTitle = \"Done\",\n failedTitle = \"It stopped\",\n onRetry,\n autoClose = 6000,\n maxSteps = 6,\n showText = true,\n className,\n}) {\n const textRef = useRef(null);\n const running = Boolean(job?.running);\n const failed = !running && Boolean(job?.error);\n const done = !running && !failed && Boolean(job?.finishedAt);\n\n // The agent's account grows at the bottom; keep the newest line in view.\n useEffect(() => {\n const el = textRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n }, [job?.text]);\n\n // A clean finish closes itself: the rows it made are the answer.\n useEffect(() => {\n if (!done || !autoClose || !job?.reset) return undefined;\n const t = setTimeout(() => job.reset(), autoClose);\n return () => clearTimeout(t);\n }, [done, autoClose, job?.finishedAt]); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!job || !(running || failed || done)) return null;\n\n const steps = job.steps ?? [];\n const hidden = Math.max(0, steps.length - maxSteps);\n const visible = steps.slice(hidden);\n const heading = running ? title : failed ? failedTitle : doneTitle;\n\n return (\n <section\n aria-label={heading}\n className={cx(\"rounded-lg border bg-card p-4 text-card-foreground\", failed ? \"border-destructive/50\" : \"border-border\", className)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p aria-live=\"polite\" className=\"flex items-center gap-2 text-sm font-semibold\">\n {running ? <StepMark status=\"running\" /> : <StepMark status={failed ? \"failed\" : \"done\"} />}\n <span className=\"min-w-0\">{heading}</span>\n </p>\n {failed ? <p className=\"mt-1 text-sm text-muted-foreground\">{job.error}</p> : null}\n </div>\n <div className=\"flex shrink-0 gap-2\">\n {failed && onRetry ? (\n <Button size=\"sm\" variant=\"secondary\" onClick={onRetry}>\n Try again\n </Button>\n ) : null}\n {running ? (\n <Button size=\"sm\" variant=\"quiet\" onClick={() => job.cancel?.()}>\n Stop\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"quiet\" onClick={() => job.reset?.()}>\n Close\n </Button>\n )}\n </div>\n </div>\n {visible.length ? (\n <ol className=\"mt-3 grid gap-1.5 text-sm\">\n {hidden > 0 ? <li className=\"text-xs text-muted-foreground\">{hidden} earlier {hidden === 1 ? \"step\" : \"steps\"}</li> : null}\n {visible.map((step, i) => (\n <li key={`${hidden + i}-${step.name}`} className=\"flex items-center gap-2\">\n <StepMark status={step.status} />\n <span className={step.status === \"running\" ? \"text-foreground\" : \"text-muted-foreground\"}>{step.label ?? \"Working\"}</span>\n </li>\n ))}\n </ol>\n ) : null}\n {showText && job.text ? (\n <div\n ref={textRef}\n className=\"mt-3 max-h-32 overflow-y-auto whitespace-pre-wrap border-t border-border pt-3 text-xs leading-relaxed text-muted-foreground\"\n >\n {job.text}\n </div>\n ) : null}\n </section>\n );\n}\n\nconst isEmpty = (node) => node == null || node === false || node === \"\" || (Array.isArray(node) && node.filter(Boolean).length === 0);\n\n// The generator: a form (prompt, options, Generate), the running job's\n// panel under it, and the results. Stacked, or side by side from lg up.\nfunction Generator({\n job,\n prompt: controlled,\n defaultPrompt = \"\",\n onPromptChange,\n onGenerate,\n heading,\n description,\n promptLabel = \"What should it make?\",\n placeholder = \"Describe what you want…\",\n promptHint,\n promptRows = 4,\n options,\n generateLabel = \"Generate\",\n generateIcon = \"wand\",\n canGenerateEmpty = false,\n disabled = false,\n aside,\n results,\n resultsTitle = \"Results\",\n resultsActions,\n resultsColumns = 1,\n placeholderResult,\n emptyTitle = \"Nothing made yet\",\n emptyBody = \"Describe what you want and press Generate. What it makes lands here.\",\n emptyState,\n layout = \"stacked\",\n title = \"Working on it\",\n doneTitle = \"Done\",\n failedTitle = \"It stopped\",\n onRetry,\n autoClose = 6000,\n maxSteps = 6,\n showText = true,\n className,\n}) {\n const id = useId();\n const [own, setOwn] = useState(defaultPrompt);\n const [error, setError] = useState(null);\n const [starting, setStarting] = useState(false);\n const lastPrompt = useRef(\"\");\n const value = controlled ?? own;\n const setValue = (v) => {\n if (controlled === undefined) setOwn(v);\n onPromptChange?.(v);\n };\n const running = Boolean(job?.running);\n const canGenerate = !disabled && !running && !starting && (canGenerateEmpty || value.trim().length > 0);\n\n async function generate(text = value) {\n if (disabled || running || starting || (!canGenerateEmpty && !text.trim())) return;\n lastPrompt.current = text.trim();\n setError(null);\n setStarting(true);\n let r;\n try {\n r = onGenerate ? await onGenerate(text.trim()) : await job?.start?.(text.trim());\n } catch (e) {\n r = { ok: false, error: e?.message ?? String(e) };\n }\n setStarting(false);\n if (r && r.ok === false && !r.aborted) setError(r.error ?? \"It did not start\");\n }\n // useJob's start resolves when the job ends; a failure it reports is\n // already the panel's error, so the field says only what the panel does not.\n const fieldError = error && !job?.error ? error : null;\n\n const cols = { 1: \"grid-cols-1\", 2: \"grid-cols-1 sm:grid-cols-2\", 3: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\", 4: \"grid-cols-2 lg:grid-cols-4\" };\n const empty = isEmpty(results);\n const split = layout === \"split\";\n\n const form = (\n <Card title={heading} description={description} className=\"min-w-0\">\n <div className=\"grid gap-4\">\n <Field id={id} label={promptLabel} hint={promptHint} error={fieldError ?? undefined}>\n <Textarea\n id={id}\n rows={promptRows}\n value={value}\n placeholder={placeholder}\n disabled={disabled}\n invalid={Boolean(fieldError)}\n aria-describedby={fieldError ? `${id}-error` : promptHint ? `${id}-hint` : undefined}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={(e) => {\n // Cmd/Ctrl+Enter generates; Enter alone is a new line.\n if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey) && !e.nativeEvent.isComposing) {\n e.preventDefault();\n generate();\n }\n }}\n />\n </Field>\n {options ? <div className=\"grid gap-4 sm:grid-cols-2\">{options}</div> : null}\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button variant=\"primary\" icon={running || starting ? undefined : generateIcon} loading={starting} disabled={!canGenerate} onClick={() => generate()}>\n {running ? \"Working…\" : generateLabel}\n </Button>\n <p aria-hidden=\"true\" className=\"hidden text-xs text-muted-foreground md:block\">\n ⌘/Ctrl + Enter\n </p>\n </div>\n <Panel\n job={job}\n title={title}\n doneTitle={doneTitle}\n failedTitle={failedTitle}\n onRetry={onRetry ?? (lastPrompt.current || canGenerateEmpty ? () => generate(lastPrompt.current) : undefined)}\n autoClose={autoClose}\n maxSteps={maxSteps}\n showText={showText}\n />\n </div>\n </Card>\n );\n\n const shelf = (\n <section aria-label={resultsTitle} aria-busy={running || undefined} className=\"grid min-w-0 content-start gap-3\">\n <div className=\"flex min-h-9 items-center justify-between gap-3\">\n <h3 className=\"text-sm font-semibold\">{resultsTitle}</h3>\n {resultsActions ? <div className=\"flex shrink-0 items-center gap-2\">{resultsActions}</div> : null}\n </div>\n {running ? (\n <div className={cx(\"grid gap-3\", cols[resultsColumns] ?? cols[1])}>\n {placeholderResult ?? <Skeleton shape=\"rect\" className=\"h-32 w-full\" />}\n </div>\n ) : null}\n {empty ? (\n running ? null : emptyState ?? <Empty icon=\"sparkles\" title={emptyTitle} body={emptyBody} className=\"rounded-lg border border-dashed border-border\" />\n ) : (\n <div className={cx(\"grid gap-3\", cols[resultsColumns] ?? cols[1])}>{results}</div>\n )}\n </section>\n );\n\n return (\n <div className={cx(\"grid gap-4\", split && \"lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)] lg:items-start\", className)}>\n <div className=\"grid min-w-0 gap-4\">\n {form}\n {aside}\n </div>\n {shelf}\n </div>\n );\n}\n"
22
+ "content": "\"use client\";\n\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { Button, Card, Empty, Field, Icon, Skeleton, Textarea, cx } from \"../../ui\";\n\n// JobPanel — the panel that follows work the founder asked the app's\n// agent for (useJob in lib/hooks.js, README §5 \"Commissioning work\"). It\n// keeps the four rules a hand-drawn panel breaks: it is not there when\n// nothing runs, it shows each step's label (the founder's phrase, never\n// the tool's name), the agent's text is its account and not the\n// deliverable (the rows are), and Stop / Close are always one press away\n// (Answer in Stop's place while the agent waits on the founder).\n//\n// const job = useJob();\n// <Button onClick={() => job.start(\"Draft a launch plan for Rowhouse\")}>Generate a launch plan</Button>\n// <JobPanel job={job} title=\"Drafting the launch plan\" doneTitle=\"Launch plan drafted\" />\n//\n// variant=\"generator\" is the same panel inside a small studio: a prompt,\n// the surface's own options, Generate, then the results the job made. The\n// block never calls a model and takes no API key or provider: Generate\n// hands the prompt to the app's agent (job.start, or `onGenerate`), and\n// the results are the rows the job wrote, passed back in as `results`.\n//\n// <JobPanel variant=\"generator\" job={job} promptLabel=\"Describe the photo\"\n// options={<Field …><Select …/></Field>}\n// onGenerate={(prompt) => job.start(`Make a product photo: ${prompt}. Size ${size}.`)}\n// results={photos.map((p) => <PhotoCard key={p.id} photo={p} />)} />\n//\n// Since 0.4.0 a job is a request, and its conversation lives in the\n// desk's agent column: the agent's questions and the founder's yes are\n// there, not here. So the panel reads two more states off the job, when\n// the job has them: needsYou (the agent waits on the founder — the\n// heading says so and Answer, in Stop's place, opens the question in the\n// column) and queued (one job per list runs at a time — the heading names\n// the job it waits behind). A 0.3.x job object has neither and draws\n// exactly as it always did.\n\nfunction StepMark({ status }) {\n if (status === \"done\") return <Icon name=\"check\" size={14} className=\"shrink-0 text-success\" />;\n if (status === \"failed\") return <Icon name=\"warning\" size={14} className=\"shrink-0 text-destructive\" />;\n if (status === \"needs\") return <Icon name=\"circle-help\" size={14} className=\"shrink-0 text-warning\" />;\n if (status === \"queued\") return <Icon name=\"clock\" size={14} className=\"shrink-0 text-muted-foreground\" />;\n return <span aria-hidden=\"true\" className=\"mx-[3px] size-2 shrink-0 animate-pulse rounded-full bg-primary\" />;\n}\n\nexport function JobPanel({ variant = \"panel\", ...props }) {\n if (variant === \"generator\") return <Generator {...props} />;\n return <Panel {...props} />;\n}\n\n// Waiting on the founder or on another job is still a live job: not over,\n// only paused. useJob counts both as running; an object of its own shape\n// that sets only the flag gets the same panel.\nconst isLive = (job) => Boolean(job?.running || job?.needsYou || job?.queued);\n\nfunction Panel({\n job,\n title = \"Working on it\",\n doneTitle = \"Done\",\n failedTitle = \"It stopped\",\n needsTitle = \"Needs your answer\",\n onRetry,\n autoClose = 6000,\n maxSteps = 6,\n showText = true,\n className,\n}) {\n const textRef = useRef(null);\n const needsYou = Boolean(job?.needsYou);\n const queued = !needsYou && Boolean(job?.queued);\n const running = isLive(job);\n const failed = !running && Boolean(job?.error);\n const done = !running && !failed && Boolean(job?.finishedAt);\n\n // The agent's account grows at the bottom; keep the newest line in view.\n useEffect(() => {\n const el = textRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n }, [job?.text]);\n\n // A clean finish closes itself: the rows it made are the answer.\n useEffect(() => {\n if (!done || !autoClose || !job?.reset) return undefined;\n const t = setTimeout(() => job.reset(), autoClose);\n return () => clearTimeout(t);\n }, [done, autoClose, job?.finishedAt]); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!job || !(running || failed || done)) return null;\n\n // What the job waits on, if anything: its mark replaces the pulse.\n const waiting = needsYou ? \"needs\" : queued ? \"queued\" : null;\n const heading = needsYou\n ? needsTitle\n : queued\n ? `Queued · after ${job.queuedAfter?.after_title ?? \"the job before it\"}`\n : running\n ? title\n : failed\n ? failedTitle\n : doneTitle;\n // A queued request's one step is its line, and its line is this\n // heading again: say it once.\n const steps = (job.steps ?? []).filter((step) => !(queued && step.label === heading));\n const hidden = Math.max(0, steps.length - maxSteps);\n const visible = steps.slice(hidden);\n // The question is in the desk's agent column; open() takes the founder\n // there. Without it there is nowhere to point, and Stop stays.\n const answer = needsYou && typeof job.open === \"function\";\n\n return (\n <section\n aria-label={heading}\n className={cx(\n \"rounded-lg border bg-card p-4 text-card-foreground\",\n failed ? \"border-destructive/50\" : needsYou ? \"border-warning/50\" : \"border-border\",\n className,\n )}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p aria-live=\"polite\" className=\"flex items-center gap-2 text-sm font-semibold\">\n <StepMark status={waiting ?? (running ? \"running\" : failed ? \"failed\" : \"done\")} />\n <span className=\"min-w-0\">{heading}</span>\n </p>\n {failed ? <p className=\"mt-1 text-sm text-muted-foreground\">{job.error}</p> : null}\n {needsYou ? <p className=\"mt-1 text-sm text-muted-foreground\">The question is in the agent column.</p> : null}\n </div>\n <div className=\"flex shrink-0 gap-2\">\n {failed && onRetry ? (\n <Button size=\"sm\" variant=\"secondary\" onClick={onRetry}>\n Try again\n </Button>\n ) : null}\n {answer ? (\n <Button size=\"sm\" variant=\"secondary\" onClick={() => job.open()}>\n Answer\n </Button>\n ) : running ? (\n <Button size=\"sm\" variant=\"quiet\" onClick={() => job.cancel?.()}>\n Stop\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"quiet\" onClick={() => job.reset?.()}>\n Close\n </Button>\n )}\n </div>\n </div>\n {visible.length ? (\n <ol className=\"mt-3 grid gap-1.5 text-sm\">\n {hidden > 0 ? <li className=\"text-xs text-muted-foreground\">{hidden} earlier {hidden === 1 ? \"step\" : \"steps\"}</li> : null}\n {visible.map((step, i) => (\n <li key={`${hidden + i}-${step.name}`} className=\"flex items-center gap-2\">\n <StepMark status={waiting && step.status === \"running\" ? waiting : step.status} />\n <span className={step.status === \"running\" ? \"text-foreground\" : \"text-muted-foreground\"}>{step.label ?? \"Working\"}</span>\n </li>\n ))}\n </ol>\n ) : null}\n {/* The agent's account, on the 0.3.x path. A request's text is empty\n (its account is in the column), so the area is not drawn. */}\n {showText && job.text ? (\n <div\n ref={textRef}\n className=\"mt-3 max-h-32 overflow-y-auto whitespace-pre-wrap border-t border-border pt-3 text-xs leading-relaxed text-muted-foreground\"\n >\n {job.text}\n </div>\n ) : null}\n </section>\n );\n}\n\nconst isEmpty = (node) => node == null || node === false || node === \"\" || (Array.isArray(node) && node.filter(Boolean).length === 0);\n\n// The generator: a form (prompt, options, Generate), the running job's\n// panel under it, and the results. Stacked, or side by side from lg up.\nfunction Generator({\n job,\n prompt: controlled,\n defaultPrompt = \"\",\n onPromptChange,\n onGenerate,\n heading,\n description,\n promptLabel = \"What should it make?\",\n placeholder = \"Describe what you want…\",\n promptHint,\n promptRows = 4,\n options,\n generateLabel = \"Generate\",\n generateIcon = \"wand\",\n canGenerateEmpty = false,\n disabled = false,\n aside,\n results,\n resultsTitle = \"Results\",\n resultsActions,\n resultsColumns = 1,\n placeholderResult,\n emptyTitle = \"Nothing made yet\",\n emptyBody = \"Describe what you want and press Generate. What it makes lands here.\",\n emptyState,\n layout = \"stacked\",\n title = \"Working on it\",\n doneTitle = \"Done\",\n failedTitle = \"It stopped\",\n needsTitle = \"Needs your answer\",\n onRetry,\n autoClose = 6000,\n maxSteps = 6,\n showText = true,\n className,\n}) {\n const id = useId();\n const [own, setOwn] = useState(defaultPrompt);\n const [error, setError] = useState(null);\n const [starting, setStarting] = useState(false);\n const lastPrompt = useRef(\"\");\n const value = controlled ?? own;\n const setValue = (v) => {\n if (controlled === undefined) setOwn(v);\n onPromptChange?.(v);\n };\n const running = isLive(job);\n const canGenerate = !disabled && !running && !starting && (canGenerateEmpty || value.trim().length > 0);\n\n async function generate(text = value) {\n if (disabled || running || starting || (!canGenerateEmpty && !text.trim())) return;\n lastPrompt.current = text.trim();\n setError(null);\n setStarting(true);\n let r;\n try {\n r = onGenerate ? await onGenerate(text.trim()) : await job?.start?.(text.trim());\n } catch (e) {\n r = { ok: false, error: e?.message ?? String(e) };\n }\n setStarting(false);\n if (r && r.ok === false && !r.aborted) setError(r.error ?? \"It did not start\");\n }\n // useJob's start resolves when the ask is taken (on a 0.3.x claw, when\n // the job ends); a failure it reports is already the panel's error, so\n // the field says only what the panel does not.\n const fieldError = error && !job?.error ? error : null;\n\n const cols = { 1: \"grid-cols-1\", 2: \"grid-cols-1 sm:grid-cols-2\", 3: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\", 4: \"grid-cols-2 lg:grid-cols-4\" };\n const empty = isEmpty(results);\n const split = layout === \"split\";\n\n const form = (\n <Card title={heading} description={description} className=\"min-w-0\">\n <div className=\"grid gap-4\">\n <Field id={id} label={promptLabel} hint={promptHint} error={fieldError ?? undefined}>\n <Textarea\n id={id}\n rows={promptRows}\n value={value}\n placeholder={placeholder}\n disabled={disabled}\n invalid={Boolean(fieldError)}\n aria-describedby={fieldError ? `${id}-error` : promptHint ? `${id}-hint` : undefined}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={(e) => {\n // Cmd/Ctrl+Enter generates; Enter alone is a new line.\n if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey) && !e.nativeEvent.isComposing) {\n e.preventDefault();\n generate();\n }\n }}\n />\n </Field>\n {options ? <div className=\"grid gap-4 sm:grid-cols-2\">{options}</div> : null}\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button variant=\"primary\" icon={running || starting ? undefined : generateIcon} loading={starting} disabled={!canGenerate} onClick={() => generate()}>\n {running ? \"Working…\" : generateLabel}\n </Button>\n <p aria-hidden=\"true\" className=\"hidden text-xs text-muted-foreground md:block\">\n ⌘/Ctrl + Enter\n </p>\n </div>\n <Panel\n job={job}\n title={title}\n doneTitle={doneTitle}\n failedTitle={failedTitle}\n needsTitle={needsTitle}\n onRetry={onRetry ?? (lastPrompt.current || canGenerateEmpty ? () => generate(lastPrompt.current) : undefined)}\n autoClose={autoClose}\n maxSteps={maxSteps}\n showText={showText}\n />\n </div>\n </Card>\n );\n\n const shelf = (\n <section aria-label={resultsTitle} aria-busy={running || undefined} className=\"grid min-w-0 content-start gap-3\">\n <div className=\"flex min-h-9 items-center justify-between gap-3\">\n <h3 className=\"text-sm font-semibold\">{resultsTitle}</h3>\n {resultsActions ? <div className=\"flex shrink-0 items-center gap-2\">{resultsActions}</div> : null}\n </div>\n {running ? (\n <div className={cx(\"grid gap-3\", cols[resultsColumns] ?? cols[1])}>\n {placeholderResult ?? <Skeleton shape=\"rect\" className=\"h-32 w-full\" />}\n </div>\n ) : null}\n {empty ? (\n running ? null : emptyState ?? <Empty icon=\"sparkles\" title={emptyTitle} body={emptyBody} className=\"rounded-lg border border-dashed border-border\" />\n ) : (\n <div className={cx(\"grid gap-3\", cols[resultsColumns] ?? cols[1])}>{results}</div>\n )}\n </section>\n );\n\n return (\n <div className={cx(\"grid gap-4\", split && \"lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)] lg:items-start\", className)}>\n <div className=\"grid min-w-0 gap-4\">\n {form}\n {aside}\n </div>\n {shelf}\n </div>\n );\n}\n"
23
23
  },
24
24
  {
25
25
  "path": "registry/blocks/job-panel/README.md",
26
26
  "type": "registry:file",
27
27
  "target": "@components/makersclaw/blocks/job-panel/README.md",
28
- "content": "# JobPanel\n\nThe panel that follows work the founder asked the app's agent for. It\nshows each step's label as the agent works, a Stop button while it runs,\nthen \"done\" (closing itself a few seconds later) or the reason it\nstopped. When no job runs it renders nothing — a panel that is always\nthere is a monitoring console, and apps do not have one.\n\n```jsx\nimport { JobPanel } from \"../components/blocks/job-panel\"; // a Makersclaw app (\"./blocks/job-panel\" from components/)\nimport { JobPanel } from \"@/components/makersclaw/blocks/job-panel\"; // a shadcn project\n```\n\nA client component. It takes the object `useJob()` returns (lib/hooks.js,\nREADME §5 \"Commissioning work\") and draws it; starting the job stays with\nyour surface.\n\n`variant=\"generator\"` is the panel inside a small studio: a prompt, your\noptions, Generate, the running job, and a shelf of what it made. It holds\nno model, provider or API key: Generate hands the prompt to the app's\nagent (`job.start`, or your `onGenerate`), the agent writes rows, and you\npass those rows back in as `results`.\n\n## Props\n\n- `job` — what `useJob()` returns. Required. Outside Makersclaw, any\n object of the same shape: `running`, `steps` (`[{ name, label, status:\n \"running\" | \"done\" | \"failed\" }]`), `text`, `error`, `finishedAt`, and\n `cancel()` / `reset()`.\n- `title` — while it runs, what the agent is doing, in the founder's\n words: `\"Drafting the launch plan\"`. Default `\"Working on it\"`.\n- `doneTitle` — after a clean finish: `\"Launch plan drafted\"`.\n- `failedTitle` — when it stopped with an error; the error follows it.\n- `onRetry` — shows \"Try again\" beside Close when the job failed: start\n the same job again.\n- `autoClose` — milliseconds after a clean finish before the panel\n closes itself (`job.reset()`); default 6000, `false` to keep it until\n Close.\n- `maxSteps` — the newest steps shown, default 6; older ones become one\n \"N earlier steps\" line.\n- `showText` — the agent's own account under the steps, default true. It\n is what the agent says it did, never the result: the result is rows.\n- `className` — layout around it. Never colour.\n\n### The generator (`variant=\"generator\"`)\n\nEverything above (`job`, `title`, `doneTitle`, `failedTitle`, `onRetry`,\n`autoClose`, `maxSteps`, `showText`) goes to the panel inside it. Then:\n\n- `onGenerate(prompt)` — start the job. Build the ask from the prompt and\n your options and call `job.start(ask)`; return what it returns, or\n `{ ok: false, error }` and the reason shows under the prompt. Default:\n `job.start(prompt)`. Cmd/Ctrl+Enter in the prompt presses Generate.\n Without `onRetry`, the panel's \"Try again\" re-runs the last prompt.\n- `prompt` / `onPromptChange(text)` — control the prompt; or\n `defaultPrompt`. `promptLabel` (\"What should it make?\"), `placeholder`,\n `promptHint`, `promptRows` (4).\n- `options` — your fields, in a two-column grid under the prompt (one\n column on a phone): a size `Select`, a style `ToggleGroup`, a `Slider`.\n Their values are yours; read them in `onGenerate`.\n- `generateLabel` (\"Generate\") · `generateIcon` (\"wand\") ·\n `canGenerateEmpty` (false) · `disabled` (false).\n- `heading` · `description` — the form card's title and line under it.\n- `aside` — a node under the form: tips, a note on cost.\n- `results` — what the job made, as nodes (an array of cards, say). The\n shelf draws them in `resultsColumns` columns (1–4; fewer on a phone).\n `resultsTitle` (\"Results\") and `resultsActions` (nodes beside it).\n- `placeholderResult` — drawn at the top of the shelf while the job runs\n (default a skeleton); the shelf is `aria-busy` meanwhile.\n- `emptyTitle` · `emptyBody` · `emptyState` — the shelf before anything\n is made.\n- `layout` — `\"stacked\"` (default: the form, then the shelf) or\n `\"split\"` (side by side from lg up; stacked on a phone).\n\n## Example\n\n```jsx\n\"use client\";\nimport { useEffect } from \"react\";\nimport { useJob } from \"../lib/hooks\";\nimport { Button } from \"./ui\";\nimport { JobPanel } from \"./blocks/job-panel\";\n\nconst ASK = \"Draft a launch plan for the next product in the list, with dates and owners.\";\n\nexport default function Launches({ launches, reload }) {\n const job = useJob();\n useEffect(() => { reload?.(); }, [job.progress]); // rows appear as they land\n\n return (\n <div className=\"grid gap-4\">\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"secondary\" onClick={() => job.start(ASK)} disabled={job.running}>Generate a launch plan</Button>\n <Button variant=\"primary\" icon=\"plus\" onClick={openNewLaunch}>New launch plan</Button>\n </div>\n <JobPanel job={job} title=\"Drafting the launch plan\" doneTitle=\"Launch plan drafted\" onRetry={() => job.start(ASK)} />\n {/* the launches list — the job's result lands here */}\n </div>\n );\n}\n```\n\nThe verb sits on the object list beside the manual \"New\" (README §5, rule\n1), and the panel sits where the result will appear.\n\nA generator for product photos:\n\n```jsx\n\"use client\";\nimport { useEffect, useState } from \"react\";\nimport { useJob } from \"../lib/hooks\";\nimport { Field, Image, Select } from \"./ui\";\nimport { JobPanel } from \"./blocks/job-panel\";\n\nexport default function PhotoStudio({ photos, reload }) {\n const job = useJob();\n const [size, setSize] = useState(\"square\");\n useEffect(() => { reload?.(); }, [job.progress]); // new photos appear as they land\n\n return (\n <JobPanel\n variant=\"generator\"\n job={job}\n heading=\"New product photo\"\n promptLabel=\"Describe the photo\"\n options={\n <Field id=\"size\" label=\"Size\">\n <Select id=\"size\" value={size} onChange={(e) => setSize(e.target.value)}\n options={[{ value: \"square\", label: \"Square\" }, { value: \"portrait\", label: \"Portrait\" }]} />\n </Field>\n }\n onGenerate={(prompt) => job.start(`Make one ${size} product photo: ${prompt}. Save it to public.photos.`)}\n title=\"Making the photo\"\n doneTitle=\"Photo saved\"\n results={photos.map((p) => <Image key={p.id} src={p.url} alt={p.prompt} ratio=\"square\" rounded=\"lg\" />)}\n resultsColumns={3}\n />\n );\n}\n```\n\nWhat it will not do: call a model, hold a provider or an API key, or\nkeep what was made. The agent does the work through the job; the rows\nare the results.\n\nIts files are fixed: `npm run check` refuses a change, and `makersclaw add\njob-panel` restores them. A different panel is your own component in\n`components/`, built from the kit.\n\n## Credits\n\nThe generator variant is ported from Creative Tim UI (https://github.com/creativetimofficial/ui, ai-image-generator-01 and ai-video-generator-01, MIT), rewritten on the Makersclaw kit without its provider and API-key fields.\n"
28
+ "content": "# JobPanel\n\nThe panel that follows work the founder asked the app's agent for. It\nshows each step's label as the agent works, a Stop button while it runs,\nthen \"done\" (closing itself a few seconds later) or the reason it\nstopped. When no job runs it renders nothing — a panel that is always\nthere is a monitoring console, and apps do not have one.\n\nThe conversation is not here. Since 0.4.0 a job is a request, and its\nconversation lives in the desk's agent column: the agent's questions,\nthe founder's answers and the yes before it acts are all there. The\npanel says where the work is and points there — \"Needs your answer\"\nwith an Answer button when the agent waits on the founder, \"Queued ·\nafter …\" when it waits its turn.\n\n```jsx\nimport { JobPanel } from \"../components/blocks/job-panel\"; // a Makersclaw app (\"./blocks/job-panel\" from components/)\nimport { JobPanel } from \"@/components/makersclaw/blocks/job-panel\"; // a shadcn project\n```\n\nA client component. It takes the object `useJob()` returns (lib/hooks.js,\nREADME §5 \"Commissioning work\") and draws it; starting the job stays with\nyour surface.\n\n`variant=\"generator\"` is the panel inside a small studio: a prompt, your\noptions, Generate, the running job, and a shelf of what it made. It holds\nno model, provider or API key: Generate hands the prompt to the app's\nagent (`job.start`, or your `onGenerate`), the agent writes rows, and you\npass those rows back in as `results`.\n\n## Props\n\n- `job` — what `useJob()` returns. Required. Outside Makersclaw, any\n object of the same shape: `running`, `steps` (`[{ name, label, status:\n \"running\" | \"done\" | \"failed\" }]`), `text`, `error`, `finishedAt`, and\n `cancel()` / `reset()`. Optional, read when present (useJob sets them\n since 0.4.0): `needsYou`, `queued`, `queuedAfter` (`{ after_title }`)\n and `open()` — see \"Needs you and queued\" below. An object without\n them (the 0.3.x shape) draws exactly as before.\n- `title` — while it runs, what the agent is doing, in the founder's\n words: `\"Drafting the launch plan\"`. Default `\"Working on it\"`.\n- `doneTitle` — after a clean finish: `\"Launch plan drafted\"`.\n- `failedTitle` — when it stopped with an error; the error follows it.\n- `needsTitle` — while the agent waits on the founder's answer. Default\n `\"Needs your answer\"`.\n- `onRetry` — shows \"Try again\" beside Close when the job failed: start\n the same job again.\n- `autoClose` — milliseconds after a clean finish before the panel\n closes itself (`job.reset()`); default 6000, `false` to keep it until\n Close.\n- `maxSteps` — the newest steps shown, default 6; older ones become one\n \"N earlier steps\" line.\n- `showText` — the agent's own account under the steps, default true. It\n is what the agent says it did, never the result: the result is rows.\n It hides itself when `job.text` is empty, as it is on a request: the\n account is in the desk's agent column.\n- `className` — layout around it. Never colour.\n\n### The generator (`variant=\"generator\"`)\n\nEverything above (`job`, `title`, `doneTitle`, `failedTitle`,\n`needsTitle`, `onRetry`, `autoClose`, `maxSteps`, `showText`) goes to the\npanel inside it. Then:\n\n- `onGenerate(prompt)` — start the job. Build the ask from the prompt and\n your options and call `job.start(ask)`; return what it returns, or\n `{ ok: false, error }` and the reason shows under the prompt. Default:\n `job.start(prompt)`. Cmd/Ctrl+Enter in the prompt presses Generate.\n Without `onRetry`, the panel's \"Try again\" re-runs the last prompt.\n- `prompt` / `onPromptChange(text)` — control the prompt; or\n `defaultPrompt`. `promptLabel` (\"What should it make?\"), `placeholder`,\n `promptHint`, `promptRows` (4).\n- `options` — your fields, in a two-column grid under the prompt (one\n column on a phone): a size `Select`, a style `ToggleGroup`, a `Slider`.\n Their values are yours; read them in `onGenerate`.\n- `generateLabel` (\"Generate\") · `generateIcon` (\"wand\") ·\n `canGenerateEmpty` (false) · `disabled` (false).\n- `heading` · `description` — the form card's title and line under it.\n- `aside` — a node under the form: tips, a note on cost.\n- `results` — what the job made, as nodes (an array of cards, say). The\n shelf draws them in `resultsColumns` columns (1–4; fewer on a phone).\n `resultsTitle` (\"Results\") and `resultsActions` (nodes beside it).\n- `placeholderResult` — drawn at the top of the shelf while the job runs\n (default a skeleton); the shelf is `aria-busy` meanwhile.\n- `emptyTitle` · `emptyBody` · `emptyState` — the shelf before anything\n is made.\n- `layout` — `\"stacked\"` (default: the form, then the shelf) or\n `\"split\"` (side by side from lg up; stacked on a phone).\n\n## Needs you and queued\n\nA request can be live and still not moving. The panel draws two such\nstates from the job, and both count as running: no Close, Generate\nstays off, and the panel stays until the job moves on.\n\n- **Needs you** (`job.needsYou`) — the agent stopped to ask the founder\n something, or for a yes before it acts. The heading is `needsTitle`\n (\"Needs your answer\"), the panel takes the warning tone, a line says\n the question is in the agent column, and **Answer** takes Stop's\n place: it calls `job.open()`, which opens the request's conversation\n in the desk's agent column. The founder answers there (or stops it\n there), never in the app; the job then runs on and the panel follows\n it. A job with no `open()` has nowhere to point, so Stop stays.\n- **Queued** (`job.queued`) — one job per list runs at a time, so this\n one waits behind another. The heading names the job it waits for:\n \"Queued · after Find 20 leads\" (`job.queuedAfter.after_title`, else\n \"the job before it\"). Stop stays: it takes the job out of the queue.\n\nWhen it runs again the heading is back to `title`, and the one step\nunder it is the request's line (\"Drafting · 12 of 50\").\n\n## Example\n\n```jsx\n\"use client\";\nimport { useEffect } from \"react\";\nimport { useJob } from \"../lib/hooks\";\nimport { Button } from \"./ui\";\nimport { JobPanel } from \"./blocks/job-panel\";\n\nconst ASK = \"Draft a launch plan for the next product in the list, with dates and owners.\";\n\nexport default function Launches({ launches, reload }) {\n const job = useJob();\n useEffect(() => { reload?.(); }, [job.progress]); // rows appear as they land\n\n return (\n <div className=\"grid gap-4\">\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"secondary\" onClick={() => job.start(ASK)} disabled={job.running}>Generate a launch plan</Button>\n <Button variant=\"primary\" icon=\"plus\" onClick={openNewLaunch}>New launch plan</Button>\n </div>\n <JobPanel job={job} title=\"Drafting the launch plan\" doneTitle=\"Launch plan drafted\" onRetry={() => job.start(ASK)} />\n {/* the launches list — the job's result lands here */}\n </div>\n );\n}\n```\n\nThe verb sits on the object list beside the manual \"New\" (README §5, rule\n1), and the panel sits where the result will appear.\n\nA generator for product photos:\n\n```jsx\n\"use client\";\nimport { useEffect, useState } from \"react\";\nimport { useJob } from \"../lib/hooks\";\nimport { Field, Image, Select } from \"./ui\";\nimport { JobPanel } from \"./blocks/job-panel\";\n\nexport default function PhotoStudio({ photos, reload }) {\n const job = useJob();\n const [size, setSize] = useState(\"square\");\n useEffect(() => { reload?.(); }, [job.progress]); // new photos appear as they land\n\n return (\n <JobPanel\n variant=\"generator\"\n job={job}\n heading=\"New product photo\"\n promptLabel=\"Describe the photo\"\n options={\n <Field id=\"size\" label=\"Size\">\n <Select id=\"size\" value={size} onChange={(e) => setSize(e.target.value)}\n options={[{ value: \"square\", label: \"Square\" }, { value: \"portrait\", label: \"Portrait\" }]} />\n </Field>\n }\n onGenerate={(prompt) => job.start(`Make one ${size} product photo: ${prompt}. Save it to public.photos.`)}\n title=\"Making the photo\"\n doneTitle=\"Photo saved\"\n results={photos.map((p) => <Image key={p.id} src={p.url} alt={p.prompt} ratio=\"square\" rounded=\"lg\" />)}\n resultsColumns={3}\n />\n );\n}\n```\n\nWhat it will not do: call a model, hold a provider or an API key, or\nkeep what was made. The agent does the work through the job; the rows\nare the results.\n\nIts files are fixed: `npm run check` refuses a change, and `makersclaw add\njob-panel` restores them. A different panel is your own component in\n`components/`, built from the kit.\n\n## Credits\n\nThe generator variant is ported from Creative Tim UI (https://github.com/creativetimofficial/ui, ai-image-generator-01 and ai-video-generator-01, MIT), rewritten on the Makersclaw kit without its provider and API-key fields.\n"
29
29
  }
30
30
  ],
31
31
  "categories": [
@@ -40,6 +40,9 @@
40
40
  "ai",
41
41
  "stop",
42
42
  "status",
43
+ "queued",
44
+ "needs-you",
45
+ "answer",
43
46
  "generator",
44
47
  "prompt",
45
48
  "results",
@@ -49,7 +52,7 @@
49
52
  "docs": "Installed to components/makersclaw/blocks/job-panel. Import: import { JobPanel } from \"@/components/makersclaw/blocks/job-panel\". Props and an example: components/makersclaw/blocks/job-panel/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
50
53
  "meta": {
51
54
  "makersclaw": {
52
- "version": "0.3.2",
55
+ "version": "0.4.0",
53
56
  "useWhen": "A surface has a verb that asks the agent to make or find something (\"Generate a launch plan\", \"Find 20 leads\", \"Plan next sprint\") and the founder should watch it happen where the result will land. variant=\"generator\" when the screen IS the place to ask: a prompt and a few options, the results under them (product photos, ad copy, names)."
54
57
  }
55
58
  }
@@ -5,8 +5,8 @@
5
5
  "title": "JsonEditor",
6
6
  "description": "A JSON editor in the workspace theme with two views of one value: a Tree of folding rows (edit a value or rename a key in place, add keys and items, change a type, copy a path, delete with Undo, arrow keys throughout) and Code (the code-editor block in JSON mode). Checked as it changes: the status line says what is wrong in plain words and which line, and clicking it goes there. A Format button, copy as pretty or one-line JSON, a friendly empty state with starters, and an optional Save that is optimistic and rolls back with a notice when it fails.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/code-editor.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/code-editor.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -59,7 +59,7 @@
59
59
  "docs": "Installed to components/makersclaw/blocks/json-editor. Import: import { JsonEditor, JsonTree, typeOf, pathLabel, getAt, setAt, removeAt, insertAt, renameKey, flattenTree, expandedTo, summary, preview } from \"@/components/makersclaw/blocks/json-editor\". Props and an example: components/makersclaw/blocks/json-editor/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
60
60
  "meta": {
61
61
  "makersclaw": {
62
- "version": "0.3.2",
62
+ "version": "0.4.0",
63
63
  "useWhen": "A screen edits or inspects a JSON value: an integration's settings, a webhook payload, a record's metadata or custom fields column (jsonb), an API response, feature flags. Not for editing SQL or other code (code-editor), a form of known fields (record-form), or a table of rows (data-table)."
64
64
  }
65
65
  }
package/r/map.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "MapView",
6
6
  "description": "An interactive map (MapLibre) in the workspace theme: places from rows as markers in token tones or your own kit content, clustered when they crowd, a popup with kit parts, a searchable list beside the map that shares one selection and hover with it, fit-to-all, zoom controls, draggable markers with an optimistic save, and tinted areas for territories. Light and dark tiles follow the workspace scheme (OpenFreeMap by default, no key); `styleUrl` swaps them. Helpers for bounds, clustering and distances run anywhere.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "maplibre-gl@6.11.1",
@@ -82,7 +82,7 @@
82
82
  "docs": "Installed to components/makersclaw/blocks/map. Import: import { MapView, useMapView, MAP_STYLES, DEFAULT_STYLE, TONES, readPosition, boundsOf, inBounds, clusterPoints, zoomToReveal, distanceKm, formatDistance, sortByDistance } from \"@/components/makersclaw/blocks/map\". Props and an example: components/makersclaw/blocks/map/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
83
83
  "meta": {
84
84
  "makersclaw": {
85
- "version": "0.3.2",
85
+ "version": "0.4.0",
86
86
  "useWhen": "A screen shows records that have a place: customers, stores, offices, field visits, deliveries, events, territories. The location-map and store-locator blocks and the Map view of work-views are built on it. Not for a chart of numbers by region (chart) or a hand-drawn diagram (sketch-canvas, flow-canvas)."
87
87
  }
88
88
  }
package/r/menubar.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Menubar",
6
6
  "description": "A desktop app's menu bar — File, Edit, View, Help — each a menu of actions with shortcuts, checkboxes, radio groups and submenus; click to open, hover to switch, arrow keys along the bar; one menu button on a phone.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -45,7 +45,7 @@
45
45
  "docs": "Installed to components/makersclaw/blocks/menubar. Import: import { Menubar } from \"@/components/makersclaw/blocks/menubar\". Props and an example: components/makersclaw/blocks/menubar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
46
46
  "meta": {
47
47
  "makersclaw": {
48
- "version": "0.3.2",
48
+ "version": "0.4.0",
49
49
  "useWhen": "A document-style tool with many commands grouped by kind: an editor, a canvas, a spreadsheet, an admin console that mimics a desktop app. Not for a site's navigation (a navigation menu) or one record's actions (a menu on the row)."
50
50
  }
51
51
  }
package/r/mind-map.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "title": "MindMap",
6
6
  "description": "A mind map you can build without the mouse, on flow-canvas: a centre topic with main branches balanced on both sides, each branch a tidy auto-laid-out tree in its own chart hue. Tab adds a subtopic, Enter the next topic, typing renames, arrows walk the map, ⌘↑/⌘↓ fold a branch (a folded one shows how many it hides), ⌥↑/⌥↓ reorder, Shift+Tab moves up a level; drag a topic onto another to move its branch. Undo/redo, a colour per branch, an Outline view, copy a branch as an outline and paste an outline as topics, starter templates. The map is plain JSON (a tree of topics); saves are async callbacks answering { ok }.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/flow-canvas.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/flow-canvas.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -60,7 +60,7 @@
60
60
  "docs": "Installed to components/makersclaw/blocks/mind-map. Import: import { MindMap, BRANCH_COLORS, normalizeMap, createTopic, findTopic, updateTopic, insertTopic, removeTopic, moveTopic, layoutMindMap, mindMapToBoard, mindMapToText, mindMapToMarkdown, outlineToMindMap, outlineToTopics, mindMapTemplates } from \"@/components/makersclaw/blocks/mind-map\". Props and an example: components/makersclaw/blocks/mind-map/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
61
61
  "meta": {
62
62
  "makersclaw": {
63
- "version": "0.3.2",
63
+ "version": "0.4.0",
64
64
  "useWhen": "A screen for thinking in a tree: brainstorming around one idea, planning a project, breaking a goal down, notes from a meeting, a topic outline an agent drafted. Not for free-form boards with stickies and arrows (flow-canvas, whiteboard), a people hierarchy (org-chart), or a process with steps (process-map)."
65
65
  }
66
66
  }
@@ -5,7 +5,7 @@
5
5
  "title": "Navigation Menu",
6
6
  "description": "A site's top nav: plain links, and triggers that open a mega-menu panel of links (icon, title, one-line description) with an optional featured card; hover with intent, click, arrow keys and Escape; a menu button and a sheet of collapsible groups on a phone.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -45,7 +45,7 @@
45
45
  "docs": "Installed to components/makersclaw/blocks/navigation-menu. Import: import { NavigationMenu } from \"@/components/makersclaw/blocks/navigation-menu\". Props and an example: components/makersclaw/blocks/navigation-menu/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
46
46
  "meta": {
47
47
  "makersclaw": {
48
- "version": "0.3.2",
48
+ "version": "0.4.0",
49
49
  "useWhen": "The header of a public-facing site or a product with many destinations: a marketing site, a docs hub, a customer portal. Not for an app's own sidebar or a row of tabs that switch views on one page."
50
50
  }
51
51
  }
@@ -5,7 +5,7 @@
5
5
  "title": "OrderDetail",
6
6
  "description": "One placed order on a page: its number, status and date, the items, the delivery address and contact, a timeline from placed to delivered (Steps), and the totals, with Track order and Invoice actions. Loading, error and not-found states built in.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -47,7 +47,7 @@
47
47
  "docs": "Installed to components/makersclaw/blocks/order-detail. Import: import { OrderDetail } from \"@/components/makersclaw/blocks/order-detail\". Props and an example: components/makersclaw/blocks/order-detail/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
48
48
  "meta": {
49
49
  "makersclaw": {
50
- "version": "0.3.2",
50
+ "version": "0.4.0",
51
51
  "useWhen": "A buyer or an operator opens one order: \"where is my order\", an order confirmation page, a support view of a purchase. Not for a list of orders (data-table) or for a basket before paying (cart)."
52
52
  }
53
53
  }
package/r/org-chart.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "title": "OrgChart",
6
6
  "description": "An org chart on flow-canvas: people laid out top down from a flat list (each names a manager), managers centred over their teams, a team with no managers in it as a compact column, every line drawn for you. Teams fold past the first levels behind a chip that says how many are hidden; search finds a person, unfolds the way to them, centres them and lights their chain of managers; a card opens a details sheet (reports to, direct reports, team size). Colour strips and a legend by department. Optional: drag a card onto another to change someone's manager (async { ok }, optimistic, with Undo). Keyboard throughout.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/flow-canvas.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/flow-canvas.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -59,7 +59,7 @@
59
59
  "docs": "Installed to components/makersclaw/blocks/org-chart. Import: import { OrgChart, buildOrg, layoutOrg, chainOf, orgToText, groupHues } from \"@/components/makersclaw/blocks/org-chart\". Props and an example: components/makersclaw/blocks/org-chart/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
60
60
  "meta": {
61
61
  "makersclaw": {
62
- "version": "0.3.2",
62
+ "version": "0.4.0",
63
63
  "useWhen": "A screen that shows who reports to whom: a team or company directory's chart, an account's buying committee as a hierarchy, a reporting line to check before a reorg. Not for a free-form map of people and notes (account-map, flow-canvas), a plain list of people (data-table), or a tree of ideas (mind-map)."
64
64
  }
65
65
  }
@@ -5,7 +5,7 @@
5
5
  "title": "PageBuilder",
6
6
  "description": "A visual page editor (GrapesJS core) in the workspace theme: our own clean panels instead of GrapesJS's chrome — an Add panel of section and basic blocks (click or drag), a Layers tree, a Design panel limited to theme-token choices (colours, type, spacing, width, corners, borders), a device toggle (desktop, tablet, phone), undo/redo, preview, and a friendly empty state with starter templates. Pages style with CSS variables, so a saved page repaints with the theme. Saves out as { html, css, project }; PageView shows a saved page in an app.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "grapesjs@0.22.16",
@@ -98,7 +98,7 @@
98
98
  "docs": "Installed to components/makersclaw/blocks/page-builder. Import: import { PageBuilder, usePageBuilder, readPage, DEVICES, PageView, standaloneDocument, BlockThumb, PageThumb, THUMB_NAMES, BASE_CSS, SECTION_BLOCKS, BASIC_BLOCKS, STARTER_TEMPLATES, STYLE_GROUPS, COLOR_TOKENS, groupsFor, currentOption, patchFor, tokenCss, fontFaceCss, fontLinks, PAGE_BASE_CSS, emptyPage, isPageEmpty, cleanHtml, cleanCss, normalizeBlock, templatePage, pageDocument, projectSections } from \"@/components/makersclaw/blocks/page-builder\". Props and an example: components/makersclaw/blocks/page-builder/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
99
99
  "meta": {
100
100
  "makersclaw": {
101
- "version": "0.3.2",
101
+ "version": "0.4.0",
102
102
  "useWhen": "A screen where someone builds or edits a web page visually: a landing page, a waitlist or launch page, a simple site section, an announcement page. landing-builder is built on it (section templates, publishing). Not for rich text in a document (doc-editor), an email body (newsletter-builder), a form of fields (record-form) or a freeform drawing (sketch-canvas)."
103
103
  }
104
104
  }
@@ -5,7 +5,7 @@
5
5
  "title": "PaymentMethods",
6
6
  "description": "A customer's SAVED payment methods as cards: brand, last four digits, expiry (expired and expiring soon flagged) and a Default badge; Make default, Remove behind a Confirm, and Add, which hands off to the payment provider (Paddle). Never a card-number field. Empty, loading and error states built in.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -44,7 +44,7 @@
44
44
  "docs": "Installed to components/makersclaw/blocks/payment-methods. Import: import { PaymentMethods } from \"@/components/makersclaw/blocks/payment-methods\". Props and an example: components/makersclaw/blocks/payment-methods/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
45
45
  "meta": {
46
46
  "makersclaw": {
47
- "version": "0.3.2",
47
+ "version": "0.4.0",
48
48
  "useWhen": "A billing or account page where a customer manages the cards and wallets on file. Not for taking a payment (cart's checkout step hands off to Paddle) or for a list of charges (transaction-list)."
49
49
  }
50
50
  }
@@ -5,7 +5,7 @@
5
5
  "title": "PdfDocument",
6
6
  "description": "Make PDFs in the workspace theme (@react-pdf/renderer): a small set of document primitives (headings, text, label/value fields, tables with repeating headers, totals, callouts, badges, rows and columns) and a clean ready-made quote / estimate / invoice layout, rendered in the browser to a Blob with renderPdf(doc). The theme's colours are read from the page when the PDF is made (a PDF cannot read CSS variables): white paper, near-black ink and the theme's primary as the one accent. Also a live-preview hook (usePdf), a Download PDF button, and the quote sums and money formatting as plain functions.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "@react-pdf/renderer@4.9.0"
@@ -72,7 +72,7 @@
72
72
  "docs": "Installed to components/makersclaw/blocks/pdf-document. Import: import { renderPdf, usePdf, PdfDownloadButton, downloadBlob, registerPdfFont, PdfDocument, PdfStack, PdfRow, PdfCol, PdfText, PdfHeading, PdfLabel, PdfField, PdfDivider, PdfCallout, PdfBadge, PdfTable, PdfTotals, PdfLines, PdfView, PdfRawText, PdfImage, PdfLink, PdfThemeProvider, usePdfTheme, PDF_TYPE, QuoteDocument, quoteTotals, lineAmount, round2, formatMoney, formatQuantity, formatPdfDate, resolvePdfTheme, buildPdfTheme, DEFAULT_PDF_THEME } from \"@/components/makersclaw/blocks/pdf-document\". Props and an example: components/makersclaw/blocks/pdf-document/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
73
73
  "meta": {
74
74
  "makersclaw": {
75
- "version": "0.3.2",
75
+ "version": "0.4.0",
76
76
  "useWhen": "A screen produces a document someone downloads, prints, emails or signs: a quote, an invoice, an estimate, a receipt, a statement, a certificate, a one-page report. The quote-builder and proposal blocks print through it. Not for showing an existing PDF on screen (pdf-viewer) or for a web page people read in the app (doc-page)."
77
77
  }
78
78
  }
package/r/pdf-viewer.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "PdfViewer",
6
6
  "description": "A PDF on screen, in the workspace theme (react-pdf + pdf.js): pages in one smooth scrolling column (only the pages near the window are drawn, so long files stay fast), a thumbnail rail, a page box, zoom by steps, fit width, fit page and Ctrl/⌘ + wheel or pinch, find in the text with every match marked, and highlights with notes: select text, choose Highlight or Add note, and it saves through onHighlight; notes are edited in a side list, colours are theme tokens, delete has Undo. Opens a URL, a Blob or bytes; password-protected files ask for the password; a narrow viewer turns its rails into sheets. The pdf.js worker ships with the app (no public/ file, no CDN).",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.3.2/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.4.0/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "react-pdf@11.0.0",
@@ -81,7 +81,7 @@
81
81
  "docs": "Installed to components/makersclaw/blocks/pdf-viewer. Import: import { PdfViewer, usePdfViewer, HIGHLIGHT_TONES, findMatches, toPageRects, mergeRects, sortHighlights, highlightAt, fitScale, ZOOM_STEPS } from \"@/components/makersclaw/blocks/pdf-viewer\". Props and an example: components/makersclaw/blocks/pdf-viewer/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
82
82
  "meta": {
83
83
  "makersclaw": {
84
- "version": "0.3.2",
84
+ "version": "0.4.0",
85
85
  "useWhen": "A screen shows an existing PDF: a contract or proposal to review, a signed agreement, an uploaded invoice or statement, a report, a generated quote before it is sent; or people mark up a PDF and leave notes on passages. Pair it with pdf-document to preview a PDF you make. Not for making PDFs (pdf-document), for rich text people edit (doc-editor), or for marking up a screenshot (annotate)."
86
86
  }
87
87
  }