makersclaw 0.8.3 → 0.8.5
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/activity-log.json +3 -3
- package/r/agent-changes.json +3 -3
- package/r/agent-chat.json +3 -3
- package/r/agent-column.json +3 -3
- package/r/agent-dock.json +3 -3
- package/r/agent-kit.json +2 -2
- package/r/agent-marks.json +2 -2
- package/r/agent-pane.json +4 -4
- package/r/ai-action.json +4 -4
- package/r/annotate.json +3 -3
- package/r/approval-request.json +2 -2
- package/r/approval.json +3 -3
- package/r/ask-bar.json +2 -2
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +7 -7
- package/r/automation-list.json +3 -3
- package/r/automation-model.json +2 -2
- package/r/automations-screen.json +7 -7
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +2 -2
- package/r/chat-pane.json +4 -4
- package/r/chat.json +2 -2
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +2 -2
- package/r/contact-section.json +2 -2
- package/r/cost.json +2 -2
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +3 -3
- package/r/data-table.json +2 -2
- 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/first-run.json +2 -2
- package/r/fix-note.json +4 -4
- package/r/flow-canvas.json +2 -2
- package/r/flow-editor.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/library-shelf.json +2 -2
- package/r/map.json +2 -2
- package/r/margin-notes.json +3 -3
- package/r/menubar.json +2 -2
- package/r/mind-map.json +3 -3
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +2 -2
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +3 -3
- package/r/pace-bar.json +3 -3
- 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 +2 -2
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +2 -2
- package/r/record-notes.json +4 -4
- package/r/registry.json +290 -249
- package/r/run-history.json +2 -2
- package/r/run-view.json +6 -6
- package/r/schedule-card.json +2 -2
- package/r/schedule-engine.json +2 -2
- package/r/schedule-gantt.json +6 -6
- package/r/schedule-ruler.json +2 -2
- 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 +2 -2
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +2 -2
- package/r/source-receipt.json +3 -3
- package/r/spreadsheet.json +2 -2
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +3 -3
- package/r/step-settings.json +5 -5
- package/r/suggestion.json +2 -2
- package/r/sum-bar.json +2 -2
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +2 -2
- package/r/time-grid.json +2 -2
- package/r/time-strip.json +5 -5
- package/r/top-bar.json +2 -2
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +3 -3
- package/r/whiteboard.json +3 -3
- package/r/workflow-view.json +3 -3
- package/r/workspace-objects.json +45 -0
- package/registry/blocks/chat-pane/README.md +4 -0
- package/registry/blocks/chat-pane/chat-attach.js +6 -1
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +15 -10
- package/registry/blocks/schedule-gantt/sheet.jsx +8 -6
- package/registry/blocks/schedule-gantt/track.jsx +1 -1
- package/registry/blocks/time-strip/README.md +5 -2
- package/registry/blocks/time-strip/strip-model.js +6 -0
- package/registry/blocks/time-strip/time-strip.jsx +41 -4
- package/registry/blocks/workspace-objects/README.md +23 -0
- package/registry/blocks/workspace-objects/block.json +10 -0
- package/registry/blocks/workspace-objects/demo.jsx +3 -0
- package/registry/blocks/workspace-objects/index.js +1 -0
- package/registry/blocks/workspace-objects/objects.js +147 -0
package/r/setup-checklist.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "SetupChecklist",
|
|
6
6
|
"description": "The steps to get set up: which are done, how far along the whole is (a ring, a bar, or \"Step 2 of 4\"), and the button for the next one, sent out through an async onAction that can fail on the step. Four shapes: folding rows in a card with a dismiss menu, numbered step cards, a plain step list with a help panel, and bordered accordion rows that can be locked until the step before is done.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"docs": "Installed to components/makersclaw/blocks/setup-checklist. Import: import { SetupChecklist } from \"@/components/makersclaw/blocks/setup-checklist\". Props and an example: components/makersclaw/blocks/setup-checklist/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
45
45
|
"meta": {
|
|
46
46
|
"makersclaw": {
|
|
47
|
-
"version": "0.8.
|
|
47
|
+
"version": "0.8.5",
|
|
48
48
|
"useWhen": "A new account, workspace or shop has a few things to do before it is useful (complete the profile, invite the team, connect a tool, launch) and the founder should see what is left. Not for a running job's stages (kit Steps or job-panel), and not for a multi-page form wizard."
|
|
49
49
|
}
|
|
50
50
|
}
|
package/r/share-dialog.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "ShareDialog",
|
|
6
6
|
"description": "Who can see a record and how to let more people in: invite by email (comma separated, with an access level), each person with their access in a menu (can view, can edit, remove), sharing switches, and a link with a copy button that turns to a check. A dialog, or the same body inline (SharePanel). Every change is an async callback; a failure shows and the old value comes back.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"docs": "Installed to components/makersclaw/blocks/share-dialog. Import: import { ShareDialog, SharePanel } from \"@/components/makersclaw/blocks/share-dialog\". Props and an example: components/makersclaw/blocks/share-dialog/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
46
46
|
"meta": {
|
|
47
47
|
"makersclaw": {
|
|
48
|
-
"version": "0.8.
|
|
48
|
+
"version": "0.8.5",
|
|
49
49
|
"useWhen": "A record, project, document or workspace can be shared: an \"Invite\" or \"Share\" button, a members list with roles, a public link to copy."
|
|
50
50
|
}
|
|
51
51
|
}
|
package/r/sidebar.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Sidebar",
|
|
6
6
|
"description": "An app's navigation down the left edge: app name or workspace switcher, groups of items with icons, counts and nested items, the signed-in person with their menu; collapses to icons (remembered) and becomes a Sheet behind a trigger on a phone. Optional: inset or floating look, a ⌘K search over every item, folding groups with colour dots, a second column (a mail list, or a parent's children to drill into), a \"New\" button and a \"…\" menu per item (a chat history).",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"docs": "Installed to components/makersclaw/blocks/sidebar. Import: import { Sidebar, SidebarLayout, SidebarTrigger, useSidebar } from \"@/components/makersclaw/blocks/sidebar\". Props and an example: components/makersclaw/blocks/sidebar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
63
63
|
"meta": {
|
|
64
64
|
"makersclaw": {
|
|
65
|
-
"version": "0.8.
|
|
65
|
+
"version": "0.8.5",
|
|
66
66
|
"useWhen": "An app with more than a handful of places to go (inbox, deals, contacts, reports, settings, projects) that wants a real app shell: compose it in app/shell.jsx around the surfaces. Not for in-page tabs (Tabs) or a short strip of links."
|
|
67
67
|
}
|
|
68
68
|
}
|
package/r/signature-pad.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "SignaturePad",
|
|
6
6
|
"description": "A signature field (signature_pad) in the workspace theme: a full-name field, then draw on a paper box (smooth pen strokes, undo the last stroke, clear with Undo) or type the name set as a signature. Sign returns a record: a cropped transparent PNG in dark ink, the typed name, the mode and the time; the signed view shows it with Sign again. Works as a form field too, through a handle (toPNG, getSignature).",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"signature_pad@5.1.4"
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"docs": "Installed to components/makersclaw/blocks/signature-pad. Import: import { SignaturePad, useSignaturePad, signatureRecord, formatSignedAt } from \"@/components/makersclaw/blocks/signature-pad\". Props and an example: components/makersclaw/blocks/signature-pad/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
66
66
|
"meta": {
|
|
67
67
|
"makersclaw": {
|
|
68
|
-
"version": "0.8.
|
|
68
|
+
"version": "0.8.5",
|
|
69
69
|
"useWhen": "A screen needs someone to sign: accepting a proposal or a quote, a delivery or work-done sign-off, a waiver, a consent form, an approval on a record. The proposal-accept block is built on it. It records an acceptance with a picture of the signature — it is not a certified legal e-signature (no identity check, no audit trail beyond what you store)."
|
|
70
70
|
}
|
|
71
71
|
}
|
package/r/sketch-canvas.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "SketchCanvas",
|
|
6
6
|
"description": "A freeform drawing canvas (Excalidraw) in the workspace theme: a Miro-style tool rail (select, pan, draw, shapes, arrows, text, image, eraser, frames), a floating toolbar over the selection with colours from the theme's tokens, zoom and undo controls, a friendly empty state, and stamp tools that place ready-made drawings (UI wireframe pieces built in). Optional locked background image for annotating a screenshot. The sketch is plain JSON; helpers read its words for an agent, and the handle exports a PNG.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"@excalidraw/excalidraw@0.18.1"
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"docs": "Installed to components/makersclaw/blocks/sketch-canvas. Import: import { SketchCanvas, useSketchCanvas, SKETCH_TOOLS, WIREFRAME_STAMPS, STROKES, FILLS, emptySketch, isSketchEmpty, sketchToText, sketchItems, toSketch } from \"@/components/makersclaw/blocks/sketch-canvas\". Props and an example: components/makersclaw/blocks/sketch-canvas/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
93
93
|
"meta": {
|
|
94
94
|
"makersclaw": {
|
|
95
|
-
"version": "0.8.
|
|
95
|
+
"version": "0.8.5",
|
|
96
96
|
"useWhen": "A screen needs freehand drawing: a whiteboard, marking up a screenshot, sketching a UI wireframe, a diagram drawn by hand. The whiteboard, annotate and sketch-to-app blocks are built on it. Not for structured boards of cards and connectors (flow-canvas), charts (chart) or a kanban (board)."
|
|
97
97
|
}
|
|
98
98
|
}
|
package/r/source-receipt.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"title": "SourceReceipt",
|
|
6
6
|
"description": "Where one value came from, as a clipping: the page or the mail and its title, the address, the date it was read, a trust word (\"their own site\", \"a 2023 directory\") and the trust mark's shape, the words on it with the deciding ones underlined wherever they appear (case-insensitive, never HTML), and Open the source. A thin wrapper over agent-column's clipping (Data Enrichment's receipt) for a value that stands on its own, so every app draws a source the same way.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/agent-column.json"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": [],
|
|
12
12
|
"files": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"docs": "Installed to components/makersclaw/blocks/source-receipt. Import: import { SourceReceipt, markExcerpt } from \"@/components/makersclaw/blocks/source-receipt\". Props and an example: components/makersclaw/blocks/source-receipt/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
46
46
|
"meta": {
|
|
47
47
|
"makersclaw": {
|
|
48
|
-
"version": "0.8.
|
|
48
|
+
"version": "0.8.5",
|
|
49
49
|
"useWhen": "A value the agent found should show its proof beside it: a contact's title from an email signature, a company's size from its site, a figure in a note or a newsletter with its source in the margin, two sources side by side for a person to pick. For a whole column of answers across records use agent-column; for a value proposed but not kept yet, put the receipt under a suggestion."
|
|
50
50
|
}
|
|
51
51
|
}
|
package/r/spreadsheet.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Spreadsheet",
|
|
6
6
|
"description": "A table you edit like Google Sheets or Airtable, virtualised for 10,000+ rows: typed columns (text, number, currency, date, single and multiple select, person, checkbox, link, rating) each with its own in-cell editor, a cell cursor and range selection with the mouse and arrow keys, typing to edit, copy and paste ranges as TSV (to and from Sheets and Excel), drag-to-fill and ⌘D fill down, undo/redo, column resize, drag to reorder, hide, freeze, rename, change type and add fields, row insert and delete with Undo, sort and filter panels, search, a sum/average of the selection, validation marks, and a phone layout. Edits go out as async callbacks answering { ok }, applied at once and rolled back on failure. A column-type registry and an imperative handle for blocks built on it.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"@tanstack/react-virtual@3.14.13"
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"docs": "Installed to components/makersclaw/blocks/spreadsheet. Import: import { Spreadsheet, CellValue, CellPanel, OptionBadge, editorFor, TYPE_ORDER, sortWords, COLUMN_TYPES, typeOf, parseCell, convertValue, guessType, parseNumberText, parseDateText, optionValue, isBlank, emptyView, normalizeView, orderedColumns, visibleColumns, processRows, passesFilters, parseTSV, toTSV, pasteChanges, fillChanges, clearChanges, applyChanges, invertChanges } from \"@/components/makersclaw/blocks/spreadsheet\". Props and an example: components/makersclaw/blocks/spreadsheet/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
84
84
|
"meta": {
|
|
85
85
|
"makersclaw": {
|
|
86
|
-
"version": "0.8.
|
|
86
|
+
"version": "0.8.5",
|
|
87
87
|
"useWhen": "People need to enter or clean up many records in a grid, fast, with the keyboard: a lead list, an inventory, a budget, a content calendar, a table view over any collection, the grid under an Airtable-like base. Not for a read-mostly list to scan and act on row by row (data-table), records moving through stages (board), or a single record (record-form)."
|
|
88
88
|
}
|
|
89
89
|
}
|
package/r/stat-cards.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"title": "Stat cards",
|
|
6
6
|
"description": "A row of key numbers, each with its name and its change: one bordered strip, or a card each with a change in the corner or after the value, a \"from\" value, a status dot and a goal meter, a sparkline, a \"View more\" footer; or a usage tile per amount against its limit (a bar or a ring, a split by kind with what is free, a warning line near the cap, an action and a limit the founder can change); or a plain list of values with their change. Loading, empty and error states.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/chart.json"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": [],
|
|
12
12
|
"files": [
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"docs": "Installed to components/makersclaw/blocks/stat-cards. Import: import { StatCards } from \"@/components/makersclaw/blocks/stat-cards\". Props and an example: components/makersclaw/blocks/stat-cards/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
60
60
|
"meta": {
|
|
61
61
|
"makersclaw": {
|
|
62
|
-
"version": "0.8.
|
|
62
|
+
"version": "0.8.5",
|
|
63
63
|
"useWhen": "The few numbers at the top of a dashboard or a report: revenue, users, conversion, spend against budget, seats and storage against a plan. Not for a trend worth reading on its own (a chart), many rows of records (a table), or one subject scored against thresholds (a scorecard)."
|
|
64
64
|
}
|
|
65
65
|
}
|
package/r/step-picker.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"title": "StepPicker",
|
|
6
6
|
"description": "The two ways to choose a step of an automation: StepPicker, the popover a + on a connector opens (a search box, the kinds grouped Your apps / Think / Flow / Data / Tell, one line on what each does and whether it is free, paid or coming), and PaletteBar, the strip along the bottom of a canvas (one icon per kind). Both only say which kind a person chose; where it goes is the editor's call.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/automation-model.json"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": [],
|
|
12
12
|
"files": [
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"docs": "Installed to components/makersclaw/blocks/step-picker. Import: import { PaletteBar, StepPicker } from \"@/components/makersclaw/blocks/step-picker\". Props and an example: components/makersclaw/blocks/step-picker/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
47
47
|
"meta": {
|
|
48
48
|
"makersclaw": {
|
|
49
|
-
"version": "0.8.
|
|
49
|
+
"version": "0.8.5",
|
|
50
50
|
"useWhen": "A screen lets a person add a step to an automation: from a + on the canvas or the outline (flow-editor does it for you), or from a button of your own. Not a general command palette (use Command)."
|
|
51
51
|
}
|
|
52
52
|
}
|
package/r/step-settings.json
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"title": "StepSettings",
|
|
6
6
|
"description": "The panel for one step of an automation, made for its kind: a name; an app action picked from the app's declared actions with what to give it; a connected tool by kind of account; a condition; a for-each's list and limit; a model call's question and tier; an agent step's instructions and credit limit; a paid-data step's service, address and price ceiling; a wait; a stop; a note for the owner; and, for every step, what happens if it fails (stop or carry on, how many tries) and a plain list of what is wrong with it. Every text field takes data from earlier steps (data-picker). In a run the same panel is read-only and shows how the step went, what went in and what came out, and what the agent changed (fix-note). FlowSettings is the panel for the automation as a whole: name, what it does, overlap, what happens when a step fails, the most a run may spend, what a manual run asks first, and its versions.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
10
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
11
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/automation-model.json",
|
|
10
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/data-picker.json",
|
|
11
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/fix-note.json"
|
|
12
12
|
],
|
|
13
13
|
"dependencies": [],
|
|
14
14
|
"files": [
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"docs": "Installed to components/makersclaw/blocks/step-settings. Import: import { FlowSettings, NumberField, SelectField, StepSettings, SwitchField, TextField } from \"@/components/makersclaw/blocks/step-settings\". Props and an example: components/makersclaw/blocks/step-settings/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
66
66
|
"meta": {
|
|
67
67
|
"makersclaw": {
|
|
68
|
-
"version": "0.8.
|
|
68
|
+
"version": "0.8.5",
|
|
69
69
|
"useWhen": "A step or an automation is edited or read in your own screen. automation-editor and run-view place these for you."
|
|
70
70
|
}
|
|
71
71
|
}
|
package/r/suggestion.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Suggestion",
|
|
6
6
|
"description": "A value the agent proposes, drawn in pencil — dashed while it is a proposal, solid once kept — with its source always shown and a keep or a drop. Five shapes: FieldSuggestion under a field (\"empty now\", the proposed value, where it came from, Keep and Drop); InlineSuggestion found in place, a small dashed chip inside the cell it would fill with a tick and a cross; TextSuggestion, a span of text struck with its replacement beside it, the reason and the source; Pencil, the dashed frame for a whole proposed record (\"pencil to ink\"); SuggestionGroup, the head over several (\"Keep all 6\", \"Drop all\"); and Looking while a value is still being found. The verbs are props (Keep/Drop, Apply/Dismiss, Accept/Decline). Keeps and drops go out through async callbacks that answer { ok }; a failure says why in place.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"docs": "Installed to components/makersclaw/blocks/suggestion. Import: import { FieldSuggestion, InlineSuggestion, TextSuggestion, Pencil, SuggestionGroup, Looking, suggestionSource } from \"@/components/makersclaw/blocks/suggestion\". Props and an example: components/makersclaw/blocks/suggestion/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
52
52
|
"meta": {
|
|
53
53
|
"makersclaw": {
|
|
54
|
-
"version": "0.8.
|
|
54
|
+
"version": "0.8.5",
|
|
55
55
|
"useWhen": "The agent proposes values or changes a person keeps or drops: a CRM filling in a contact, a cleaner value in a table cell, a priority on an issue, a proofread sentence, a drafted plan for the week, a batch of moves on a board. For a whole column of answers across many records use agent-column; for one ask's finished changes with an undo use agent-changes."
|
|
56
56
|
}
|
|
57
57
|
}
|
package/r/sum-bar.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "SumBar",
|
|
6
6
|
"description": "A total with its parts cut out of it, drawn to scale. The bar runs from 0 to the total (100 by default); what is left is solid with its number, each part's cost is a hatched block cut from the top end downward in the tone of its part (warning, danger, info, neutral) with its cost written in it and its name under it, and a part can be pressed. When a rule holds the result below the sum, the stretch between them is dotted and says so. Posts stand on the bar where a rule applies (a cap at 74, a cap at 49), each with a flag above. Coloured bands under the bar name the ranges (off track under 50, at risk to 75, on track). Under it the sum is written out so no number is taken on trust: \"100 − 22 − 25 − 5 − 8 − 0 − 5 = 35\", or \"100 − 44 = 56, held at 49\". Positions are percentages, so it draws the same on the server; a screen reader hears the whole bar as one sentence; loading, failed and nothing-taken-off are drawn; the sums (resultOf, cutsOf, postsOf, bandsOf, receiptOf, sumWords) are exported as plain functions.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"docs": "Installed to components/makersclaw/blocks/sum-bar. Import: import { SumBar, bandsOf, costOf, cutsOf, labelledCuts, lostOf, postsOf, receiptOf, resultOf, sumWords, totalOf } from \"@/components/makersclaw/blocks/sum-bar\". Props and an example: components/makersclaw/blocks/sum-bar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
58
58
|
"meta": {
|
|
59
59
|
"makersclaw": {
|
|
60
|
-
"version": "0.8.
|
|
60
|
+
"version": "0.8.5",
|
|
61
61
|
"useWhen": "Any screen where a number is a total with things taken off it and the member should be able to check the arithmetic: a health score of 100 points less what each signal cost, a risk score, a quote's price less its discounts, a budget less what each line spent, a credit balance less each charge, a grade out of 100 less each lost mark. It draws a result the app has already worked out and changes no number. Not a total split into shares (Distribution, breakdown), progress against a clock (pace-bar), a meter against a limit (Meter), or a time series (chart)."
|
|
62
62
|
}
|
|
63
63
|
}
|
package/r/task-list.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"title": "Task list",
|
|
6
6
|
"description": "The List view of work, like ClickUp's or Linear's: tasks under headings that fold (group by status, assignee, priority, project, due date or any field), a round check to complete each, the title edited in place, subtasks indented under their parent with their own fold, the fields you choose (assignees, due date, priority, tags, project, estimate) as inline pickers, a quick-add row at the end of every group, drag to reorder and to move between groups (saving the new status, assignee or due date), multi-select with a bulk bar (status, assign, due, priority, delete with undo), a toolbar with search, filter chips, group by, sort and fields, and friendly empty states with starter templates. One async onChange saves every change; failures roll back with a notice.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/task-model.json"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": [],
|
|
12
12
|
"files": [
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"docs": "Installed to components/makersclaw/blocks/task-list. Import: import { TaskList } from \"@/components/makersclaw/blocks/task-list\". Props and an example: components/makersclaw/blocks/task-list/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
57
57
|
"meta": {
|
|
58
58
|
"makersclaw": {
|
|
59
|
-
"version": "0.8.
|
|
59
|
+
"version": "0.8.5",
|
|
60
60
|
"useWhen": "A screen that lists work to get done and moves it along: a team's tasks, a launch checklist, a sprint, a personal to-do list, onboarding steps, content to publish. Not for records that are not work (data-table), for stage columns side by side (board), or for dates on a grid (calendar)."
|
|
61
61
|
}
|
|
62
62
|
}
|
package/r/task-model.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Task model",
|
|
6
6
|
"description": "The shared work-item model every task view reads: one task shape (status, assignees, start/due, estimate, progress, priority, parent, dependencies, location, tags, project, extra fields) with statuses, people and projects; pure helpers to search, filter, group by any field, sort, nest subtasks, do date math (week and month grids, working days, overlaps, time scales), sum capacity per person, follow dependencies and the critical path, and apply, undo and persist changes; the shared parts (StatusPill, AssigneeStack, DueLabel, PriorityFlag, TaskChip, CompleteToggle), the ViewToolbar (search, filter chips, group by, sort, show fields), drag primitives on @dnd-kit, a hook for optimistic saves with rollback and undo, and adapters that drive the Board and DataTable blocks from tasks.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"@dnd-kit/core@6.3.1",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"docs": "Installed to components/makersclaw/blocks/task-model. Import: import { NONE, PRIORITIES, DEFAULT_STATUSES, TASK_FIELDS, DEFAULT_VIEW, normalizeTask, normalizeTasks, createTaskContext, fieldOf, isDone, doneStatusId, openStatusId, toggleDoneChange, newTaskId, draftTask, valuesOf, optionsOf, labelOf, searchText, searchTasks, filterTasks, orderOf, sortTasks, sortOptions, groupTasks, groupOptions, movePatch, applyView, activeFilterCount, rankBetween, placeChange, childrenMap, buildTree, flattenTree, descendantIds, ancestorIds, subtaskCounts, rollup, progressOf, dependencyEdges, blockersOf, wouldCycle, dateConflicts, criticalPath, hoursPerDay, workload, sumOf, applyChange, invertChange, changeWrites, changedIds, deleteChange, toBoard, toTableRows, taskFacets, StatusPill, CompleteToggle, AssigneeStack, PersonLabel, DueLabel, PriorityFlag, TagList, SubtaskCount, ProgressLine, TaskChip, GroupLabel, taskColumns, TONE_TEXT, HUE_EDGE, HUE_BG, TaskDragProvider, SortableGroup, useSortableTask, useDraggableTask, useDropTarget, DragHandle, DropLine, LIFTED, useReducedMotion, arrayMove, isDay, dayNumber, fromDayNumber, dayOf, minutesOf, withTime, todayDay, addDays, diffDays, weekdayOf, startOfWeek, endOfWeek, weekDays, startOfMonth, endOfMonth, addMonths, daysBetween, monthGrid, WEEKEND, isWeekend, isWorkingDay, workingDaysBetween, addWorkingDays, spanOf, spansOverlap, tasksInRange, tasksOnDay, shiftDates, spanPatch, dueBucket, DUE_BUCKETS, dayForBucket, formatDay, formatTime, formatDate, timeScale, rangeOfTasks, isNewId } from \"@/components/makersclaw/blocks/task-model\". Props and an example: components/makersclaw/blocks/task-model/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
92
92
|
"meta": {
|
|
93
93
|
"makersclaw": {
|
|
94
|
-
"version": "0.8.
|
|
94
|
+
"version": "0.8.5",
|
|
95
95
|
"useWhen": "You are building a view of work items — a list, calendar, gantt, workload, roadmap or a switcher between them — or driving Board / DataTable from tasks. Views import it; a surface rarely renders it alone. Not for records that are not work (use data-table or board directly)."
|
|
96
96
|
}
|
|
97
97
|
}
|
package/r/testimonials.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Testimonials",
|
|
6
6
|
"description": "A marketing section of customer quotes: name, role, company, face or logo, the quote, an optional star rating, a verified mark and a \"view on X\" source, and optional headline numbers (stats). Layouts: a grid of cards (plain, card or raised; hover can light one and dim the rest; a \"write a review\" tile), one large quote, a switcher of faces, and a carousel, optionally over a picture.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"docs": "Installed to components/makersclaw/blocks/testimonials. Import: import { Testimonials } from \"@/components/makersclaw/blocks/testimonials\". Props and an example: components/makersclaw/blocks/testimonials/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
46
46
|
"meta": {
|
|
47
47
|
"makersclaw": {
|
|
48
|
-
"version": "0.8.
|
|
48
|
+
"version": "0.8.5",
|
|
49
49
|
"useWhen": "Social proof on a public page: a landing page, a pricing page, a product page, a case-study teaser. Not for reviews a signed-in user writes and manages (a record list or form), or an app's own activity feed."
|
|
50
50
|
}
|
|
51
51
|
}
|
package/r/tier-map.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "TierMap",
|
|
6
6
|
"description": "A map of cards in tiers, from a flat list where each item names its parent. The first tier is the tops; pressing a card opens the cards that serve it as the next tier beneath, joined by elbow lines (pressing it again closes them), and ONE LAYER OPENS AT A TIME. A card with something under it says how many (\"6 serving it\") and whether it is open. A card holds a small kicker line with a mark and a person's face, a title, a progress bar in a status colour with its percent, and one muted foot line. Items that sit under nothing and are not tops are shown together in an unparented group with a note and a slot for an action; items marked ghost (drafts, counted nowhere) are drawn dashed in a group of their own and can serve each other. The lines are CSS only (each tier is a grid of equal columns and the lines are placed in percent), so nothing is measured; below the width a tier needs to sit in one row it stacks, the lines become a rail down its left, and the cards go full width. Arrow keys walk the cards (left and right beside, down into the first kid, up to the parent, Escape closes). A loop of parents is broken, and a parent that is not in the list makes the item unparented. Loading, failed, empty and read-only are drawn; the model (buildTierModel, openPath, togglePath, neighbour, elbowOf) is exported as plain functions.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"docs": "Installed to components/makersclaw/blocks/tier-map. Import: import { TierMap, TierCard, CARD_REM, MAX_COLUMNS, NEVER, TONES, barOf, buildTierModel, centreOf, countText, elbowOf, fitKey, neighbour, openPath, pathTo, siblingsOf, thresholdRem, tiersOf, togglePath, toneOf } from \"@/components/makersclaw/blocks/tier-map\". Props and an example: components/makersclaw/blocks/tier-map/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
61
61
|
"meta": {
|
|
62
62
|
"makersclaw": {
|
|
63
|
-
"version": "0.8.
|
|
63
|
+
"version": "0.8.5",
|
|
64
64
|
"useWhen": "Any screen that shows what serves what, one layer at a time: a company's goals and the goals beneath them, a strategy map, initiatives under goals, work under a roadmap theme, an account and its sub-accounts, a category tree whose members are cards, a product and its components. Draws a structure the app has already worked out; it does not move cards between parents or collect anything. Not for a person-and-manager chart with zoom and drag (org-chart), a free-form map of ideas (mind-map), a full node-and-edge canvas (flow-canvas), a table with expandable rows (data-table), or a board of columns (board)."
|
|
65
65
|
}
|
|
66
66
|
}
|
package/r/time-grid.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Time grid",
|
|
6
6
|
"description": "Days side by side on an hour grid, with the agent's proposals in pencil. What is already there is ink (events in their kind's colour, a lock on the ones another calendar owns); what the agent suggests is pencil: a hatched card on a dashed edge sitting on its hour, with a tag hanging off it that quotes the words it came from (\"Thursday at 3pm your time\"). Keep inks a block in (the same block, so its colours fade from pencil to ink; kept together they ink one by one), Dismiss drops it, Back to pencil takes a kept one back. \"Keep all\" says what it will do before it does it (\"Keeps 7 of 8 in ink: 5 Focus, 2 Admin. Leaves 1 in pencil: it clashes with Standup 9:30.\"). A clash is hatched where a proposal overlaps something already there, before anything is kept. A goals strip (GoalStrip) can sit above the days, goals in pencil until kept, with a total against a target that turns red once it is over. Blocks are events, not tasks: drag to move or resize (snapping to 15 minutes), drag across free working hours to make one, drop an item in from a list, arrow keys move a focused block, J and K walk the proposals that are waiting. Hours outside the working day are hatched but still there; a now line on today; one day at a time under a strip of days when the container is narrow. Loading, failed, empty and read-only are drawn.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"docs": "Installed to components/makersclaw/blocks/time-grid. Import: import { TimeGrid, GoalStrip, TIME_GRID_MIME, timeGridDragProps, PENCIL_STATES, clashesOf, clock, durationWords, gridLanes, hoursOf, isWaiting, loadOf, lookOf, namesOf, newlyInked, planKeepAll, previewOf, readItem, tagPlacement, toHHMM, toMin, walkProposal, waitingOrder } from \"@/components/makersclaw/blocks/time-grid\". Props and an example: components/makersclaw/blocks/time-grid/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
67
67
|
"meta": {
|
|
68
68
|
"makersclaw": {
|
|
69
|
-
"version": "0.8.
|
|
69
|
+
"version": "0.8.5",
|
|
70
70
|
"useWhen": "A screen where a person reviews what an agent would put on their week: a weekly planner whose focus blocks are drafted in pencil and committed in ink, an inbox that finds meetings and deadlines in mail and shows each on its hour next to the real calendar with the sender's own words, a shift or room plan an assistant drafts, a day plan with proposals. Events are plain items with a date, a start and an end (not tasks). Not for tasks with start and due dates that live in a task list (calendar-view), for a month, for long timelines (gantt), or for a booking page's open slots (calendar-view)."
|
|
71
71
|
}
|
|
72
72
|
}
|
package/r/time-strip.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "TimeStrip",
|
|
6
6
|
"description": "One bar for a span that has a length (a call, a recording, a shift), from 0 to its end, with marks at the times that matter on it. Each mark is a thin coloured post at its time (a category colour 1 to 5, or a status tone); a pressed mark is taller and ringed, and arrow keys walk from one mark to the next. Stretches somebody was away are hatched; a part can be dragged out on the bar and its two ends nudged with the arrow keys (a slider each), and the bar says it back in words. An axis under it names the start, the end and the whole steps between (00:00, 15:00, 30:00, 58:40), or the app's own labels (\"before the client joined\"). A small bar (22 px) sits under a record's title; a large one (44 px) is the screen's signature. Positions are percentages of the length, so it draws the same on the server. Loading and failed are drawn; the sums (toSeconds, clockOf, marksOf, rangeOf, ticksFor, nextMark) are exported as plain functions.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -19,19 +19,19 @@
|
|
|
19
19
|
"path": "registry/blocks/time-strip/time-strip.jsx",
|
|
20
20
|
"type": "registry:component",
|
|
21
21
|
"target": "@components/makersclaw/blocks/time-strip/time-strip.jsx",
|
|
22
|
-
"content": "\"use client\";\n\nimport { useRef, useState } from \"react\";\nimport { Button, Notice, Skeleton, cx } from \"../../ui\";\nimport { clockOf, countMarks, lengthOf, marksOf, nextMark, pctOf, rangeOf, rangesOf, secondsAt, shareOf, stripWords, ticksFor, toSeconds } from \"./strip-model\";\n\n// One bar for a span that has a length (a call, a recording, a shift), with\n// marks at the times that matter on it. The marks are the app's: a cut, a\n// comment, a decision. The bar can show the stretch somebody was away\n// (hatched), and can let a person drag out a part of it.\n//\n// Props in, callbacks out (`onMark`, `onSelect`): the block never reads or\n// writes data. Positions are percentages of the length, so the server draws\n// what the browser draws. README.md beside this file lists every prop.\n\n// Literal class names, so Tailwind sees every one of them.\nconst COLOR = {\n neutral: \"bg-muted-foreground\",\n primary: \"bg-primary\",\n info: \"bg-info\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n danger: \"bg-destructive\",\n};\nconst HUE = { 1: \"bg-chart-1\", 2: \"bg-chart-2\", 3: \"bg-chart-3\", 4: \"bg-chart-4\", 5: \"bg-chart-5\" };\n\nconst SIZE = {\n sm: { track: \"h-[22px]\", mark: \"inset-y-[3px] w-[5px]\", on: \"inset-y-0 w-[7px]\", handle: \"inset-y-0 w-2\" },\n lg: { track: \"h-11\", mark: \"inset-y-1.5 w-1.5\", on: \"inset-y-0.5 w-2\", handle: \"inset-y-0 w-2.5\" },\n};\n\nconst HATCH = { backgroundImage: \"repeating-linear-gradient(135deg, currentColor 0 1px, transparent 1px 5px)\" };\n\nconst colorOf = (m) => (m.hue && HUE[m.hue] ? HUE[m.hue] : COLOR[m.tone] ?? \"bg-foreground\");\n\n/**\n * The strip.\n *\n * Props: `length` (seconds, or \"mm:ss\"), `marks` `[{ id, at, hue?, tone?,\n * label? }]`, `away` `[[from, to]]`, `selection` `{ from, to }`,\n * `onSelect(range | null)`, `selected` (a mark id), `onMark(mark)`, `size`\n * (sm | lg), `axis` (true, false, or `[{ at, label? }]`), `step`, `noun`,\n * `label`, `status`, `error`, `onRetry`, `className`.\n */\nexport function TimeStrip({\n length,\n marks,\n away,\n selection,\n onSelect,\n selected,\n onMark,\n size = \"sm\",\n axis = true,\n ticks = 5,\n step = 1,\n noun = \"mark\",\n label = \"Timeline\",\n status = \"ready\",\n error,\n onRetry,\n className,\n}) {\n const track = useRef(null);\n const [drag, setDrag] = useState(null);\n const total = lengthOf(length);\n const look = SIZE[size] ?? SIZE.sm;\n const placed = marksOf(marks, total);\n const gaps = rangesOf(away, total);\n const shown = drag ?? (selection ? rangeOf(selection.from, selection.to, total) : null);\n const canPick = Boolean(onSelect) && total > 0;\n\n if (status === \"loading\") {\n return (\n <div role=\"status\" aria-label={`${label}, loading`} className={cx(\"grid gap-1\", className)}>\n <Skeleton className={cx(\"w-full\", look.track)} />\n {axis ? <Skeleton className=\"h-3 w-full\" /> : null}\n </div>\n );\n }\n if (status === \"failed\") {\n return (\n <Notice\n tone=\"danger\"\n className={className}\n action={\n onRetry ? (\n <Button size=\"sm\" onClick={onRetry}>\n Try again\n </Button>\n ) : undefined\n }\n >\n {error || \"The timeline could not be read.\"}\n </Notice>\n );\n }\n\n // The bar's own ticks, or the app's.\n const labels = (() => {\n if (axis === false || total <= 0) return [];\n if (Array.isArray(axis)) {\n const list = axis\n .map((t) => ({ ...t, seconds: toSeconds(t?.at) }))\n .filter((t) => t.seconds != null)\n .sort((a, b) => a.seconds - b.seconds);\n return list.map((t, i) => ({ at: t.seconds, label: t.label ?? clockOf(t.seconds), pct: pctOf(shareOf(t.seconds, total)), edge: i === 0 && t.seconds <= 0 ? \"start\" : i === list.length - 1 && t.seconds >= total ? \"end\" : null }));\n }\n return ticksFor(total, ticks);\n })();\n\n function secondsFromPointer(e) {\n const box = track.current?.getBoundingClientRect();\n if (!box || !box.width) return 0;\n return secondsAt((e.clientX - box.left) / box.width, total, step);\n }\n\n function down(e) {\n if (!canPick || e.button !== 0) return;\n const at = secondsFromPointer(e);\n e.currentTarget.setPointerCapture?.(e.pointerId);\n setDrag({ anchor: at, from: at, to: at, fromPct: 0, widthPct: 0, moving: false });\n }\n function move(e) {\n if (!drag) return;\n const at = secondsFromPointer(e);\n const r = rangeOf(drag.anchor, at, total);\n setDrag({ ...drag, moving: true, ...(r ?? { from: at, to: at, fromPct: pctOf(at / total), widthPct: 0 }) });\n }\n function up(e) {\n if (!drag) return;\n const at = secondsFromPointer(e);\n const r = rangeOf(drag.anchor, at, total, { min: Math.max(1, Number(step) || 1) - 1 });\n setDrag(null);\n e.currentTarget.releasePointerCapture?.(e.pointerId);\n // A press that went nowhere clears the part; a drag picks one.\n onSelect(r ? { from: r.from, to: r.to } : null);\n }\n\n function nudge(which, e) {\n if (!shown) return;\n const unit = Math.max(1, Number(step) || 1) * (e.shiftKey ? 10 : 1);\n const keys = { ArrowLeft: -unit, ArrowDown: -unit, ArrowRight: unit, ArrowUp: unit };\n let from = shown.from;\n let to = shown.to;\n const move = which === \"from\" ? \"from\" : \"to\";\n let value = move === \"from\" ? from : to;\n if (e.key in keys) value += keys[e.key];\n else if (e.key === \"Home\") value = 0;\n else if (e.key === \"End\") value = total;\n else if (e.key === \"Escape\") {\n e.preventDefault();\n onSelect(null);\n return;\n } else return;\n e.preventDefault();\n if (move === \"from\") from = value;\n else to = value;\n const r = rangeOf(from, to, total, { min: 0 });\n if (r) onSelect({ from: r.from, to: r.to });\n }\n\n const words = stripWords({ length: total, marks: placed, noun });\n const kinds = countMarks(placed);\n\n return (\n <div role=\"group\" aria-label={label} className={cx(\"relative grid min-w-0 gap-1\", className)}>\n <div\n ref={track}\n className={cx(\"relative overflow-hidden rounded-[4px] border border-border bg-muted\", look.track, canPick && \"cursor-crosshair touch-none select-none\")}\n onPointerDown={down}\n onPointerMove={move}\n onPointerUp={up}\n onPointerCancel={() => setDrag(null)}\n >\n {gaps.map((g, i) => (\n <span key={`away-${i}`} aria-hidden=\"true\" title={g.label} className=\"absolute inset-y-0 left-(--at) w-(--width) text-input\" style={{ ...HATCH, \"--at\": `${g.fromPct}%`, \"--width\": `${g.widthPct}%` }} />\n ))}\n {shown && shown.widthPct > 0 ? (\n <span aria-hidden=\"true\" className=\"absolute inset-y-0 left-(--at) w-(--width) rounded-[4px] border-2 border-primary bg-primary/10\" style={{ \"--at\": `${shown.fromPct}%`, \"--width\": `${shown.widthPct}%` }} />\n ) : null}\n {placed.map((m) => {\n const on = selected != null && m.id === selected;\n const name = `${m.label ? `${m.label}, ` : \"\"}${noun} at ${clockOf(m.seconds)}`;\n const className = cx(\n \"absolute left-(--at) -translate-x-1/2 rounded-[2px] outline-none before:absolute before:inset-y-0 before:-inset-x-1.5 before:content-[''] focus-visible:ring-2 focus-visible:ring-ring\",\n on ? look.on : look.mark,\n colorOf(m),\n on && \"ring-2 ring-background\",\n );\n const style = { \"--at\": `${m.pct}%` };\n return onMark ? (\n <button\n key={m.id}\n type=\"button\"\n aria-label={name}\n aria-pressed={on}\n title={name}\n className={className}\n style={style}\n onPointerDown={(e) => e.stopPropagation()}\n onClick={() => onMark(m)}\n onKeyDown={(e) => {\n if (e.key !== \"ArrowRight\" && e.key !== \"ArrowLeft\") return;\n const to = nextMark(placed, m.id, e.key === \"ArrowRight\" ? 1 : -1);\n const el = e.currentTarget.parentElement?.querySelector(`[data-mark=\"${CSS.escape(String(to))}\"]`);\n if (el && to !== m.id) {\n e.preventDefault();\n el.focus();\n }\n }}\n data-mark={m.id}\n />\n ) : (\n <span key={m.id} role=\"img\" aria-label={name} title={name} className={className} style={style} />\n );\n })}\n {canPick && shown && shown.widthPct > 0\n ? [\n [\"from\", shown.from, shown.fromPct],\n [\"to\", shown.to, shown.fromPct + shown.widthPct],\n ].map(([which, value, at]) => (\n <button\n key={which}\n type=\"button\"\n role=\"slider\"\n aria-label={which === \"from\" ? \"Part starts at\" : \"Part ends at\"}\n aria-valuemin={0}\n aria-valuemax={total}\n aria-valuenow={Math.round(value)}\n aria-valuetext={clockOf(value)}\n className={cx(\"absolute left-(--at) -translate-x-1/2 cursor-ew-resize rounded-[2px] bg-primary outline-none focus-visible:ring-2 focus-visible:ring-ring\", look.handle)}\n style={{ \"--at\": `${at}%` }}\n onPointerDown={(e) => e.stopPropagation()}\n onKeyDown={(e) => nudge(which, e)}\n />\n ))\n : null}\n </div>\n {labels.length ? (\n <div aria-hidden=\"true\" className=\"relative h-4 text-[11px] tabular-nums text-muted-foreground\">\n {labels.map((t, i) => (\n <span\n key={`${t.at}-${i}`}\n className={cx(\"absolute left-(--at) top-0 whitespace-nowrap\", t.edge === \"end\" ? \"-translate-x-full\" : t.edge === \"start\" ? \"\" : \"-translate-x-1/2\")}\n style={{ \"--at\": `${t.pct}%` }}\n >\n {t.label}\n </span>\n ))}\n </div>\n ) : null}\n <span className=\"sr-only\">\n {words}\n {Object.keys(kinds).length > 1 ? `, in ${Object.keys(kinds).length} kinds` : \"\"}\n {shown && shown.widthPct > 0 ? `. Part picked: ${clockOf(shown.from)} to ${clockOf(shown.to)}.` : \"\"}\n </span>\n </div>\n );\n}\n"
|
|
22
|
+
"content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { Button, Notice, Skeleton, cx } from \"../../ui\";\nimport { clockOf, countMarks, lengthOf, marksAreDense, marksOf, nextMark, pctOf, rangeOf, rangesOf, secondsAt, shareOf, stripWords, ticksFor, toSeconds } from \"./strip-model\";\n\n// One bar for a span that has a length (a call, a recording, a shift), with\n// marks at the times that matter on it. The marks are the app's: a cut, a\n// comment, a decision. The bar can show the stretch somebody was away\n// (hatched), and can let a person drag out a part of it.\n//\n// Props in, callbacks out (`onMark`, `onSelect`): the block never reads or\n// writes data. Positions are percentages of the length, so the server draws\n// what the browser draws. README.md beside this file lists every prop.\n\n// Literal class names, so Tailwind sees every one of them.\nconst COLOR = {\n neutral: \"bg-muted-foreground\",\n primary: \"bg-primary\",\n info: \"bg-info\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n danger: \"bg-destructive\",\n};\nconst HUE = { 1: \"bg-chart-1\", 2: \"bg-chart-2\", 3: \"bg-chart-3\", 4: \"bg-chart-4\", 5: \"bg-chart-5\" };\n\nconst SIZE = {\n sm: { track: \"h-[22px]\", mark: \"inset-y-[3px] w-[5px]\", on: \"inset-y-0 w-[7px]\", handle: \"inset-y-0 w-2\" },\n lg: { track: \"h-11\", mark: \"inset-y-1.5 w-1.5\", on: \"inset-y-0.5 w-2\", handle: \"inset-y-0 w-2.5\" },\n};\n\nconst HATCH = { backgroundImage: \"repeating-linear-gradient(135deg, currentColor 0 1px, transparent 1px 5px)\" };\n\nconst colorOf = (m) => (m.hue && HUE[m.hue] ? HUE[m.hue] : COLOR[m.tone] ?? \"bg-foreground\");\n\n/**\n * The strip.\n *\n * Props: `length` (seconds, or \"mm:ss\"), `marks` `[{ id, at, hue?, tone?,\n * label? }]`, `away` `[[from, to]]`, `selection` `{ from, to }`,\n * `onSelect(range | null)`, `selected` (a mark id), `onMark(mark)`, `size`\n * (sm | lg), `axis` (true, false, or `[{ at, label? }]`), `step`, `noun`,\n * `label`, `status`, `error`, `onRetry`, `className`.\n */\nexport function TimeStrip({\n length,\n marks,\n away,\n selection,\n onSelect,\n selected,\n onMark,\n size = \"sm\",\n axis = true,\n ticks = 5,\n step = 1,\n noun = \"mark\",\n label = \"Timeline\",\n status = \"ready\",\n error,\n onRetry,\n className,\n}) {\n const track = useRef(null);\n const [drag, setDrag] = useState(null);\n const [trackWidth, setTrackWidth] = useState(0);\n useEffect(() => {\n const element = track.current;\n if (!element) return;\n const measure = () => setTrackWidth(element.getBoundingClientRect().width);\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n return () => observer.disconnect();\n }, [status]);\n const total = lengthOf(length);\n const look = SIZE[size] ?? SIZE.sm;\n const placed = marksOf(marks, total);\n const dense = Boolean(onMark) && marksAreDense(placed, trackWidth);\n const gaps = rangesOf(away, total);\n const shown = drag ?? (selection ? rangeOf(selection.from, selection.to, total) : null);\n const canPick = Boolean(onSelect) && total > 0;\n\n if (status === \"loading\") {\n return (\n <div role=\"status\" aria-label={`${label}, loading`} className={cx(\"grid gap-1\", className)}>\n <Skeleton className={cx(\"w-full\", look.track)} />\n {axis ? <Skeleton className=\"h-3 w-full\" /> : null}\n </div>\n );\n }\n if (status === \"failed\") {\n return (\n <Notice\n tone=\"danger\"\n className={className}\n action={\n onRetry ? (\n <Button size=\"sm\" onClick={onRetry}>\n Try again\n </Button>\n ) : undefined\n }\n >\n {error || \"The timeline could not be read.\"}\n </Notice>\n );\n }\n\n // The bar's own ticks, or the app's.\n const labels = (() => {\n if (axis === false || total <= 0) return [];\n if (Array.isArray(axis)) {\n const list = axis\n .map((t) => ({ ...t, seconds: toSeconds(t?.at) }))\n .filter((t) => t.seconds != null)\n .sort((a, b) => a.seconds - b.seconds);\n return list.map((t, i) => ({ at: t.seconds, label: t.label ?? clockOf(t.seconds), pct: pctOf(shareOf(t.seconds, total)), edge: i === 0 && t.seconds <= 0 ? \"start\" : i === list.length - 1 && t.seconds >= total ? \"end\" : null }));\n }\n return ticksFor(total, ticks);\n })();\n\n function secondsFromPointer(e) {\n const box = track.current?.getBoundingClientRect();\n if (!box || !box.width) return 0;\n return secondsAt((e.clientX - box.left) / box.width, total, step);\n }\n\n function down(e) {\n if (!canPick || e.button !== 0) return;\n const at = secondsFromPointer(e);\n e.currentTarget.setPointerCapture?.(e.pointerId);\n setDrag({ anchor: at, from: at, to: at, fromPct: 0, widthPct: 0, moving: false });\n }\n function move(e) {\n if (!drag) return;\n const at = secondsFromPointer(e);\n const r = rangeOf(drag.anchor, at, total);\n setDrag({ ...drag, moving: true, ...(r ?? { from: at, to: at, fromPct: pctOf(at / total), widthPct: 0 }) });\n }\n function up(e) {\n if (!drag) return;\n const at = secondsFromPointer(e);\n const r = rangeOf(drag.anchor, at, total, { min: Math.max(1, Number(step) || 1) - 1 });\n setDrag(null);\n e.currentTarget.releasePointerCapture?.(e.pointerId);\n // A press that went nowhere clears the part; a drag picks one.\n onSelect(r ? { from: r.from, to: r.to } : null);\n }\n\n function nudge(which, e) {\n if (!shown) return;\n const unit = Math.max(1, Number(step) || 1) * (e.shiftKey ? 10 : 1);\n const keys = { ArrowLeft: -unit, ArrowDown: -unit, ArrowRight: unit, ArrowUp: unit };\n let from = shown.from;\n let to = shown.to;\n const move = which === \"from\" ? \"from\" : \"to\";\n let value = move === \"from\" ? from : to;\n if (e.key in keys) value += keys[e.key];\n else if (e.key === \"Home\") value = 0;\n else if (e.key === \"End\") value = total;\n else if (e.key === \"Escape\") {\n e.preventDefault();\n onSelect(null);\n return;\n } else return;\n e.preventDefault();\n if (move === \"from\") from = value;\n else to = value;\n const r = rangeOf(from, to, total, { min: 0 });\n if (r) onSelect({ from: r.from, to: r.to });\n }\n\n const words = stripWords({ length: total, marks: placed, noun });\n const kinds = countMarks(placed);\n\n return (\n <div role=\"group\" aria-label={label} className={cx(\"relative grid min-w-0 gap-1\", className)}>\n <div\n ref={track}\n className={cx(\"relative overflow-hidden rounded-[4px] border border-border bg-muted\", look.track, canPick && \"cursor-crosshair touch-none select-none\")}\n onPointerDown={down}\n onPointerMove={move}\n onPointerUp={up}\n onPointerCancel={() => setDrag(null)}\n >\n {gaps.map((g, i) => (\n <span key={`away-${i}`} aria-hidden=\"true\" title={g.label} className=\"absolute inset-y-0 left-(--at) w-(--width) text-input\" style={{ ...HATCH, \"--at\": `${g.fromPct}%`, \"--width\": `${g.widthPct}%` }} />\n ))}\n {shown && shown.widthPct > 0 ? (\n <span aria-hidden=\"true\" className=\"absolute inset-y-0 left-(--at) w-(--width) rounded-[4px] border-2 border-primary bg-primary/10\" style={{ \"--at\": `${shown.fromPct}%`, \"--width\": `${shown.widthPct}%` }} />\n ) : null}\n {placed.map((m) => {\n const on = selected != null && m.id === selected;\n const name = `${m.label ? `${m.label}, ` : \"\"}${noun} at ${clockOf(m.seconds)}`;\n const className = cx(\n \"absolute left-(--at) -translate-x-1/2 rounded-[2px] outline-none before:absolute before:inset-y-0 before:-inset-x-1.5 before:content-[''] focus-visible:ring-2 focus-visible:ring-ring\",\n on ? look.on : look.mark,\n colorOf(m),\n on && \"ring-2 ring-background\",\n );\n const style = { \"--at\": `${m.pct}%` };\n return onMark && !dense ? (\n <button\n key={m.id}\n type=\"button\"\n aria-label={name}\n aria-pressed={on}\n title={name}\n className={className}\n style={style}\n onPointerDown={(e) => e.stopPropagation()}\n onClick={() => onMark(m)}\n onKeyDown={(e) => {\n if (e.key !== \"ArrowRight\" && e.key !== \"ArrowLeft\") return;\n const to = nextMark(placed, m.id, e.key === \"ArrowRight\" ? 1 : -1);\n const el = e.currentTarget.parentElement?.querySelector(`[data-mark=\"${CSS.escape(String(to))}\"]`);\n if (el && to !== m.id) {\n e.preventDefault();\n el.focus();\n }\n }}\n data-mark={m.id}\n />\n ) : (\n <span key={m.id} role=\"img\" aria-label={name} title={name} className={cx(className, dense && \"pointer-events-none\")} style={style} />\n );\n })}\n {canPick && shown && shown.widthPct > 0\n ? [\n [\"from\", shown.from, shown.fromPct],\n [\"to\", shown.to, shown.fromPct + shown.widthPct],\n ].map(([which, value, at]) => (\n <button\n key={which}\n type=\"button\"\n role=\"slider\"\n aria-label={which === \"from\" ? \"Part starts at\" : \"Part ends at\"}\n aria-valuemin={0}\n aria-valuemax={total}\n aria-valuenow={Math.round(value)}\n aria-valuetext={clockOf(value)}\n className={cx(\"absolute left-(--at) -translate-x-1/2 cursor-ew-resize rounded-[2px] bg-primary outline-none focus-visible:ring-2 focus-visible:ring-ring\", look.handle)}\n style={{ \"--at\": `${at}%` }}\n onPointerDown={(e) => e.stopPropagation()}\n onKeyDown={(e) => nudge(which, e)}\n />\n ))\n : null}\n </div>\n {labels.length ? (\n <div aria-hidden=\"true\" className=\"relative h-4 text-[11px] tabular-nums text-muted-foreground\">\n {labels.map((t, i) => (\n <span\n key={`${t.at}-${i}`}\n className={cx(\"absolute left-(--at) top-0 whitespace-nowrap\", t.edge === \"end\" ? \"-translate-x-full\" : t.edge === \"start\" ? \"\" : \"-translate-x-1/2\")}\n style={{ \"--at\": `${t.pct}%` }}\n >\n {t.label}\n </span>\n ))}\n </div>\n ) : null}\n {dense ? (\n <div className=\"grid min-w-0 gap-1\">\n <p className=\"text-xs text-muted-foreground\">Marks are close together. Choose a time:</p>\n <div role=\"group\" aria-label={`${label} marks`} className=\"flex min-w-0 gap-1 overflow-x-auto pb-1\">\n {placed.map((mark) => (\n <Button\n key={mark.id}\n data-mark={mark.id}\n variant={selected === mark.id ? \"secondary\" : \"quiet\"}\n aria-label={`${mark.label ? `${mark.label}, ` : \"\"}${noun} at ${clockOf(mark.seconds)}`}\n aria-pressed={selected === mark.id}\n title={mark.label}\n className=\"shrink-0 tabular-nums\"\n style={{ minWidth: 44, minHeight: 44 }}\n onClick={() => onMark(mark)}\n onKeyDown={(event) => {\n if (event.key !== \"ArrowRight\" && event.key !== \"ArrowLeft\") return;\n const to = nextMark(placed, mark.id, event.key === \"ArrowRight\" ? 1 : -1);\n const element = event.currentTarget.parentElement?.querySelector(`[data-mark=\"${CSS.escape(String(to))}\"]`);\n if (element && to !== mark.id) { event.preventDefault(); element.focus(); }\n }}\n >{clockOf(mark.seconds)}</Button>\n ))}\n </div>\n </div>\n ) : null}\n <span className=\"sr-only\">\n {words}\n {Object.keys(kinds).length > 1 ? `, in ${Object.keys(kinds).length} kinds` : \"\"}\n {shown && shown.widthPct > 0 ? `. Part picked: ${clockOf(shown.from)} to ${clockOf(shown.to)}.` : \"\"}\n </span>\n </div>\n );\n}\n"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"path": "registry/blocks/time-strip/strip-model.js",
|
|
26
26
|
"type": "registry:component",
|
|
27
27
|
"target": "@components/makersclaw/blocks/time-strip/strip-model.js",
|
|
28
|
-
"content": "// time-strip: the arithmetic (registry/blocks/time-strip/strip-model.js).\n//\n// The block draws one bar for a span that has a length (a call, a recording,\n// a shift) and puts marks on it. What has to be right is where a mark lands\n// (a share of the length, clamped), how a time is read and said (\"04:12\",\n// \"1:02:03\"), which ticks the axis shows, how a dragged range is ordered\n// and kept inside the bar, and which mark a key moves to. All plain\n// functions, no React, so the server draws what the browser draws.\n//\n// A time is a number of seconds from the start, or a string \"mm:ss\",\n// \"h:mm:ss\" or \"hh:mm:ss\". Anything else is null: a bad time is never\n// rounded into a place on the bar.\n\nconst MAX_LENGTH = 7 * 24 * 3600;\n\n/** Seconds in a time: 76, \"01:16\", \"1:02:03\", \"76\" → 76, 76, 3723, 76. null when it is not a time. */\nexport function toSeconds(value) {\n if (typeof value === \"number\") return Number.isFinite(value) && value >= 0 ? value : null;\n if (typeof value !== \"string\") return null;\n const text = value.trim();\n if (!text) return null;\n if (/^\\d+(\\.\\d+)?$/.test(text)) return Number(text);\n const parts = text.split(\":\");\n if (parts.length < 2 || parts.length > 3 || !parts.every((p) => /^\\d{1,2}(\\.\\d+)?$/.test(p))) return null;\n const nums = parts.map(Number);\n if (nums.slice(1).some((n) => n >= 60)) return null;\n return nums.reduce((total, n) => total * 60 + n, 0);\n}\n\n/** A span's length in whole seconds, or 0 when it has none. */\nexport function lengthOf(value) {\n const s = toSeconds(value);\n if (s == null || s <= 0) return 0;\n return Math.min(MAX_LENGTH, Math.round(s));\n}\n\nconst two = (n) => String(n).padStart(2, \"0\");\n\n/** Seconds as a clock: 252 → \"04:12\"; 3723 → \"1:02:03\". `long` forces hours: 252 → \"0:04:12\". */\nexport function clockOf(seconds, { long = false } = {}) {\n const s = toSeconds(seconds);\n if (s == null) return \"\";\n const whole = Math.round(s);\n const h = Math.floor(whole / 3600);\n const m = Math.floor((whole % 3600) / 60);\n const sec = whole % 60;\n return h > 0 || long ? `${h}:${two(m)}:${two(sec)}` : `${two(m)}:${two(sec)}`;\n}\n\n/** Where a time sits on the bar, as a share 0..1 of `length`. null when it is not a time or there is no length. */\nexport function shareOf(at, length) {\n const s = toSeconds(at);\n const total = lengthOf(length);\n if (s == null || total <= 0) return null;\n return Math.min(1, Math.max(0, s / total));\n}\n\n/** A share as a percent of the bar, rounded to a thousandth so the markup is steady. */\nexport function pctOf(share) {\n if (share == null) return null;\n return Math.round(share * 100000) / 1000;\n}\n\n/**\n * The marks a bar draws: `[{ id, at, ... }]` in time order, each with its\n * `seconds`, `share` and `pct`. A mark with no readable time is left out (it\n * has no place); a time past the end sits at the end. Ties keep the order\n * given. `id` falls back to its index.\n */\nexport function marksOf(marks, length) {\n const total = lengthOf(length);\n if (!Array.isArray(marks) || total <= 0) return [];\n const out = [];\n marks.forEach((m, index) => {\n if (!m || typeof m !== \"object\") return;\n const seconds = toSeconds(m.at);\n if (seconds == null) return;\n const share = shareOf(seconds, total);\n out.push({ ...m, id: m.id ?? `m${index}`, index, seconds: Math.min(seconds, total), share, pct: pctOf(share) });\n });\n return out.sort((a, b) => a.seconds - b.seconds || a.index - b.index);\n}\n\n/**\n * Stretches laid on the bar (the part the person was away, a selection):\n * `[[from, to]]` or `[{ from, to, label? }]`. Each is ordered, kept inside\n * [0, length] and dropped when nothing is left of it.\n */\nexport function rangesOf(ranges, length) {\n const total = lengthOf(length);\n if (!Array.isArray(ranges) || total <= 0) return [];\n const out = [];\n for (const r of ranges) {\n const raw = Array.isArray(r) ? { from: r[0], to: r[1] } : r;\n const r2 = rangeOf(raw?.from, raw?.to, total);\n if (r2) out.push({ ...(Array.isArray(r) ? {} : raw), ...r2 });\n }\n return out;\n}\n\n/** Two times as a range kept inside the bar and in order: { from, to, fromPct, widthPct } or null when they enclose nothing. */\nexport function rangeOf(a, b, length, { min = 0 } = {}) {\n const total = lengthOf(length);\n const x = toSeconds(a);\n const y = toSeconds(b);\n if (x == null || y == null || total <= 0) return null;\n const from = Math.min(total, Math.max(0, Math.min(x, y)));\n const to = Math.min(total, Math.max(0, Math.max(x, y)));\n if (to - from <= min) return null;\n return { from, to, fromPct: pctOf(from / total), widthPct: pctOf((to - from) / total) };\n}\n\n/** A pointer's place on the bar (0..1 across it) as a time, snapped to `step` seconds. */\nexport function secondsAt(fraction, length, step = 1) {\n const total = lengthOf(length);\n if (total <= 0 || !Number.isFinite(fraction)) return 0;\n const raw = Math.min(1, Math.max(0, fraction)) * total;\n const s = Math.max(1, Number(step) || 1);\n return Math.min(total, Math.round(raw / s) * s);\n}\n\n/** A nice step under `want` seconds: 1, 2, 5, 10, 15, 30 s; 1, 2, 5, 10, 15, 30 min; 1, 2, 3, 6, 12 h. */\nconst STEPS = [1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200];\n\n/**\n * Axis ticks for a bar: about `count` evenly spaced labels, always the start\n * and the end. `[{ at, label, share, pct, edge }]`; `edge` is \"start\" or\n * \"end\" for the two ends so a label can be kept inside the bar. The inner\n * ticks fall on whole steps (a quarter hour, a minute) and are dropped when\n * they would crowd the end.\n */\nexport function ticksFor(length, count = 5) {\n const total = lengthOf(length);\n if (total <= 0) return [];\n const want = Math.max(2, Math.min(12, Math.round(count)));\n const raw = total / (want - 1);\n // The whole step nearest the even spacing, a little over at most: 63 s apart is a minute, not two.\n const step = [...STEPS].reverse().find((s) => s <= raw * 1.25) ?? STEPS[0];\n const at = [0];\n for (let t = step; t < total; t += step) {\n if (total - t < step * 0.4) break;\n at.push(t);\n }\n at.push(total);\n return at.map((t, i) => ({ at: t, label: clockOf(t), share: t / total, pct: pctOf(t / total), edge: i === 0 ? \"start\" : i === at.length - 1 ? \"end\" : null }));\n}\n\n/**\n * The mark a key moves to from `current` (an id, or null): `dir` 1 the next\n * in time, -1 the one before; at either end it stays. With nothing current,\n * 1 goes to the first and -1 to the last.\n */\nexport function nextMark(marks, current, dir) {\n if (!marks.length) return null;\n const at = marks.findIndex((m) => m.id === current);\n if (at < 0) return (dir < 0 ? marks[marks.length - 1] : marks[0]).id;\n const to = Math.min(marks.length - 1, Math.max(0, at + (dir < 0 ? -1 : 1)));\n return marks[to].id;\n}\n\n/** How many marks of each hue / tone: { 1: 2, danger: 1, none: 3 }, for the words a screen reader hears. */\nexport function countMarks(marks) {\n const out = {};\n for (const m of marks) {\n const key = m.hue ? String(m.hue) : (m.tone ?? \"none\");\n out[key] = (out[key] ?? 0) + 1;\n }\n return out;\n}\n\n/** One line for a reader: \"9 marks on a 04:12 call\", \"No marks\". */\nexport function stripWords({ length, marks, noun = \"mark\" }) {\n const total = lengthOf(length);\n const n = marks.length;\n if (total <= 0) return \"Nothing to show\";\n const word = n === 1 ? noun : `${noun}s`;\n return n ? `${n} ${word} in ${clockOf(total)}` : `No ${noun}s in ${clockOf(total)}`;\n}\n"
|
|
28
|
+
"content": "// time-strip: the arithmetic (registry/blocks/time-strip/strip-model.js).\n//\n// The block draws one bar for a span that has a length (a call, a recording,\n// a shift) and puts marks on it. What has to be right is where a mark lands\n// (a share of the length, clamped), how a time is read and said (\"04:12\",\n// \"1:02:03\"), which ticks the axis shows, how a dragged range is ordered\n// and kept inside the bar, and which mark a key moves to. All plain\n// functions, no React, so the server draws what the browser draws.\n//\n// A time is a number of seconds from the start, or a string \"mm:ss\",\n// \"h:mm:ss\" or \"hh:mm:ss\". Anything else is null: a bad time is never\n// rounded into a place on the bar.\n\nconst MAX_LENGTH = 7 * 24 * 3600;\n\n/** Seconds in a time: 76, \"01:16\", \"1:02:03\", \"76\" → 76, 76, 3723, 76. null when it is not a time. */\nexport function toSeconds(value) {\n if (typeof value === \"number\") return Number.isFinite(value) && value >= 0 ? value : null;\n if (typeof value !== \"string\") return null;\n const text = value.trim();\n if (!text) return null;\n if (/^\\d+(\\.\\d+)?$/.test(text)) return Number(text);\n const parts = text.split(\":\");\n if (parts.length < 2 || parts.length > 3 || !parts.every((p) => /^\\d{1,2}(\\.\\d+)?$/.test(p))) return null;\n const nums = parts.map(Number);\n if (nums.slice(1).some((n) => n >= 60)) return null;\n return nums.reduce((total, n) => total * 60 + n, 0);\n}\n\n/** A span's length in whole seconds, or 0 when it has none. */\nexport function lengthOf(value) {\n const s = toSeconds(value);\n if (s == null || s <= 0) return 0;\n return Math.min(MAX_LENGTH, Math.round(s));\n}\n\nconst two = (n) => String(n).padStart(2, \"0\");\n\n/** Seconds as a clock: 252 → \"04:12\"; 3723 → \"1:02:03\". `long` forces hours: 252 → \"0:04:12\". */\nexport function clockOf(seconds, { long = false } = {}) {\n const s = toSeconds(seconds);\n if (s == null) return \"\";\n const whole = Math.round(s);\n const h = Math.floor(whole / 3600);\n const m = Math.floor((whole % 3600) / 60);\n const sec = whole % 60;\n return h > 0 || long ? `${h}:${two(m)}:${two(sec)}` : `${two(m)}:${two(sec)}`;\n}\n\n/** Where a time sits on the bar, as a share 0..1 of `length`. null when it is not a time or there is no length. */\nexport function shareOf(at, length) {\n const s = toSeconds(at);\n const total = lengthOf(length);\n if (s == null || total <= 0) return null;\n return Math.min(1, Math.max(0, s / total));\n}\n\n/** A share as a percent of the bar, rounded to a thousandth so the markup is steady. */\nexport function pctOf(share) {\n if (share == null) return null;\n return Math.round(share * 100000) / 1000;\n}\n\n/**\n * The marks a bar draws: `[{ id, at, ... }]` in time order, each with its\n * `seconds`, `share` and `pct`. A mark with no readable time is left out (it\n * has no place); a time past the end sits at the end. Ties keep the order\n * given. `id` falls back to its index.\n */\nexport function marksOf(marks, length) {\n const total = lengthOf(length);\n if (!Array.isArray(marks) || total <= 0) return [];\n const out = [];\n marks.forEach((m, index) => {\n if (!m || typeof m !== \"object\") return;\n const seconds = toSeconds(m.at);\n if (seconds == null) return;\n const share = shareOf(seconds, total);\n out.push({ ...m, id: m.id ?? `m${index}`, index, seconds: Math.min(seconds, total), share, pct: pctOf(share) });\n });\n return out.sort((a, b) => a.seconds - b.seconds || a.index - b.index);\n}\n\n/**\n * Stretches laid on the bar (the part the person was away, a selection):\n * `[[from, to]]` or `[{ from, to, label? }]`. Each is ordered, kept inside\n * [0, length] and dropped when nothing is left of it.\n */\nexport function rangesOf(ranges, length) {\n const total = lengthOf(length);\n if (!Array.isArray(ranges) || total <= 0) return [];\n const out = [];\n for (const r of ranges) {\n const raw = Array.isArray(r) ? { from: r[0], to: r[1] } : r;\n const r2 = rangeOf(raw?.from, raw?.to, total);\n if (r2) out.push({ ...(Array.isArray(r) ? {} : raw), ...r2 });\n }\n return out;\n}\n\n/** Two times as a range kept inside the bar and in order: { from, to, fromPct, widthPct } or null when they enclose nothing. */\nexport function rangeOf(a, b, length, { min = 0 } = {}) {\n const total = lengthOf(length);\n const x = toSeconds(a);\n const y = toSeconds(b);\n if (x == null || y == null || total <= 0) return null;\n const from = Math.min(total, Math.max(0, Math.min(x, y)));\n const to = Math.min(total, Math.max(0, Math.max(x, y)));\n if (to - from <= min) return null;\n return { from, to, fromPct: pctOf(from / total), widthPct: pctOf((to - from) / total) };\n}\n\n/** A pointer's place on the bar (0..1 across it) as a time, snapped to `step` seconds. */\nexport function secondsAt(fraction, length, step = 1) {\n const total = lengthOf(length);\n if (total <= 0 || !Number.isFinite(fraction)) return 0;\n const raw = Math.min(1, Math.max(0, fraction)) * total;\n const s = Math.max(1, Number(step) || 1);\n return Math.min(total, Math.round(raw / s) * s);\n}\n\n/** A nice step under `want` seconds: 1, 2, 5, 10, 15, 30 s; 1, 2, 5, 10, 15, 30 min; 1, 2, 3, 6, 12 h. */\nconst STEPS = [1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200];\n\n/**\n * Axis ticks for a bar: about `count` evenly spaced labels, always the start\n * and the end. `[{ at, label, share, pct, edge }]`; `edge` is \"start\" or\n * \"end\" for the two ends so a label can be kept inside the bar. The inner\n * ticks fall on whole steps (a quarter hour, a minute) and are dropped when\n * they would crowd the end.\n */\nexport function ticksFor(length, count = 5) {\n const total = lengthOf(length);\n if (total <= 0) return [];\n const want = Math.max(2, Math.min(12, Math.round(count)));\n const raw = total / (want - 1);\n // The whole step nearest the even spacing, a little over at most: 63 s apart is a minute, not two.\n const step = [...STEPS].reverse().find((s) => s <= raw * 1.25) ?? STEPS[0];\n const at = [0];\n for (let t = step; t < total; t += step) {\n if (total - t < step * 0.4) break;\n at.push(t);\n }\n at.push(total);\n return at.map((t, i) => ({ at: t, label: clockOf(t), share: t / total, pct: pctOf(t / total), edge: i === 0 ? \"start\" : i === at.length - 1 ? \"end\" : null }));\n}\n\n/**\n * The mark a key moves to from `current` (an id, or null): `dir` 1 the next\n * in time, -1 the one before; at either end it stays. With nothing current,\n * 1 goes to the first and -1 to the last.\n */\nexport function nextMark(marks, current, dir) {\n if (!marks.length) return null;\n const at = marks.findIndex((m) => m.id === current);\n if (at < 0) return (dir < 0 ? marks[marks.length - 1] : marks[0]).id;\n const to = Math.min(marks.length - 1, Math.max(0, at + (dir < 0 ? -1 : 1)));\n return marks[to].id;\n}\n\n/** How many marks of each hue / tone: { 1: 2, danger: 1, none: 3 }, for the words a screen reader hears. */\nexport function countMarks(marks) {\n const out = {};\n for (const m of marks) {\n const key = m.hue ? String(m.hue) : (m.tone ?? \"none\");\n out[key] = (out[key] ?? 0) + 1;\n }\n return out;\n}\n\n/** One line for a reader: \"9 marks on a 04:12 call\", \"No marks\". */\nexport function stripWords({ length, marks, noun = \"mark\" }) {\n const total = lengthOf(length);\n const n = marks.length;\n if (total <= 0) return \"Nothing to show\";\n const word = n === 1 ? noun : `${noun}s`;\n return n ? `${n} ${word} in ${clockOf(total)}` : `No ${noun}s in ${clockOf(total)}`;\n}\n\n/** Whether neighboring hit regions would overlap at the rendered track width. */\nexport function marksAreDense(placed, width, hit = 20) {\n if (!(width > 0)) return false;\n return placed.some((mark, index) => index > 0 && (mark.share - placed[index - 1].share) * width < hit);\n}\n"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"path": "registry/blocks/time-strip/README.md",
|
|
32
32
|
"type": "registry:file",
|
|
33
33
|
"target": "@components/makersclaw/blocks/time-strip/README.md",
|
|
34
|
-
"content": "# TimeStrip\n\nOne bar for a span that has a length, with marks at the times that matter.\n\nA four-minute client call is a bar from `00:00` to `04:12`. Nine things were\ncut from it, and each is a thin post on the bar at the time it was said, in the\ncolour of its kind. A person sees *where in the call* the sensitive things\nhappened, and presses one to go there. The stretch before the client joined is\nhatched. The same bar sits small under a record's title and large as a\nscreen's signature.\n\nIt draws marks the app has already placed. It plays nothing, cuts nothing and\nmeasures nothing. For a chain of dated phases with gates use `schedule-ruler`;\nfor progress against a clock `pace-bar`; for events on an hour grid\n`time-grid`; for a waveform with a playhead `audio-player`.\n\nBorn in a minutes app that drew it by hand on three screens: a call's cuts under\nits title, the same strip large with the transcript, and a part of the call\npicked out to minute.\n\n```jsx\nimport { TimeStrip, toSeconds, clockOf } from \"../components/blocks/time-strip\"; // a Makersclaw app (\"./blocks/time-strip\" from components/)\nimport { TimeStrip } from \"@/components/makersclaw/blocks/time-strip\"; // a shadcn project\n```\n\nA client component. No other block and no npm package. Positions are\npercentages of the length, so the server and the browser draw the same picture;\
|
|
34
|
+
"content": "# TimeStrip\n\nOne bar for a span that has a length, with marks at the times that matter.\n\nA four-minute client call is a bar from `00:00` to `04:12`. Nine things were\ncut from it, and each is a thin post on the bar at the time it was said, in the\ncolour of its kind. A person sees *where in the call* the sensitive things\nhappened, and presses one to go there. The stretch before the client joined is\nhatched. The same bar sits small under a record's title and large as a\nscreen's signature.\n\nIt draws marks the app has already placed. It plays nothing, cuts nothing and\nmeasures nothing. For a chain of dated phases with gates use `schedule-ruler`;\nfor progress against a clock `pace-bar`; for events on an hour grid\n`time-grid`; for a waveform with a playhead `audio-player`.\n\nBorn in a minutes app that drew it by hand on three screens: a call's cuts under\nits title, the same strip large with the transcript, and a part of the call\npicked out to minute.\n\n```jsx\nimport { TimeStrip, toSeconds, clockOf } from \"../components/blocks/time-strip\"; // a Makersclaw app (\"./blocks/time-strip\" from components/)\nimport { TimeStrip } from \"@/components/makersclaw/blocks/time-strip\"; // a shadcn project\n```\n\nA client component. No other block and no npm package. Positions are\npercentages of the length, so the server and the browser draw the same picture;\nthe rendered width is measured to keep crowded mark controls separate.\n\n## Times\n\nA time is a number of seconds from the start, or a string: `\"76\"`, `\"01:16\"`,\n`\"1:02:03\"`. A time that is none of these has no place on the bar: its mark is\nleft out, never rounded into one. A time past the end sits at the end.\n`toSeconds(\"01:16\")` is `76`; `clockOf(3723)` is `\"1:02:03\"` and `clockOf(252)`\nis `\"04:12\"`.\n\n## Props\n\n- `length` — how long the span is: seconds, or a time string. Required. With no\n length the bar is empty and says \"Nothing to show\".\n- `marks` — `[{ id, at, hue?, tone?, label? }]`. `at` is the mark's time.\n - `hue` — `1` to `5`: a category colour (a reason, a speaker, a kind).\n - `tone` — `neutral` · `primary` · `info` · `success` · `warning` ·\n `danger`: a status. `hue` wins over `tone`. Neither: ink.\n - `label` — what a reader hears: \"Pricing\". Spoken as \"Pricing, mark at\n 00:12\".\n - Any other key is kept and handed back to `onMark`.\n- `away` — stretches somebody was away, hatched: `[[from, to]]` or `[{ from, to,\n label? }]`. A stretch with nothing inside the bar is dropped.\n- `selection` — `{ from, to }`: the part picked, drawn with a frame and a handle\n at each end.\n- `onSelect(range | null)` — makes the bar pick-able: drag across it to pick a\n part (`{ from, to }`, in seconds, snapped to `step`); a press that goes nowhere\n clears it (`null`). The two handles are sliders: ← → move one by `step`, with\n Shift by ten steps, Home and End go to the ends, Esc clears. **Give a keyboard\n path of your own as well** (two time fields) for a person who cannot drag.\n- `selected` — the id of the mark to show pressed (taller, ringed).\n- `onMark(mark)` — makes the marks buttons. Called with the mark as laid out\n (`{ ...yours, seconds, share, pct }`). ← → on a focused mark moves to the next\n or the one before. When their hit regions would overlap, the exact visual\n marks remain on the track and a scrolling row of 44px timestamp buttons\n selects each mark separately. It returns to direct track buttons when there\n is room. Without `onMark` the marks are plain pictures with a label.\n- `size` — `sm` (22 px, default) or `lg` (44 px).\n- `axis` — `true` (default): about `ticks` labels under the bar on whole steps,\n always the start and the end. `false`: none. Or your own labels,\n `[{ at, label? }]`: `[{ at: 0, label: \"00:00 · before the client joined\" },\n { at: 252, label: \"after they left · 04:12\" }]`. The first label keeps to the\n left edge and the last to the right, so a long word never leaves the bar.\n- `ticks` — how many labels `axis: true` aims for (2 to 12). Default 5.\n- `step` — seconds a drag snaps to and a key moves by. Default 1.\n- `noun` — what a mark is called in the words a reader hears. Default `mark`.\n- `label` — the bar's name for a reader. Default \"Timeline\".\n- `status` — `ready` (default), `loading` (a grey bar) or `failed` (a notice\n with `error` and, if you pass `onRetry`, Try again).\n- `className` — layout: width and margin. Never colour.\n\n## Helpers\n\nPlain functions from the same folder, safe on the server:\n\n- `toSeconds(value)` → seconds or `null`; `clockOf(seconds)` → `\"04:12\"`.\n- `marksOf(marks, length)` — the marks as laid out, in time order, each with\n `seconds`, `share` (0..1) and `pct`.\n- `rangeOf(a, b, length)` — two times as an ordered range kept inside the bar,\n or `null` when they enclose nothing. `rangesOf(ranges, length)` for a list.\n- `ticksFor(length, count)` — the axis ticks. `shareOf`, `pctOf`, `secondsAt`.\n- `nextMark(marks, currentId, dir)` — where a key goes.\n- `stripWords({ length, marks, noun })` — \"9 marks in 04:12\".\n\n## Example\n\nA call's cuts under its title, large with the transcript, and a part to pick.\n\n```jsx\n\"use client\";\nimport { useState } from \"react\";\nimport { TimeStrip } from \"../components/blocks/time-strip\";\n\nconst FAMILY = { pricing: 1, third_party: 2, off_record: 3, joke: 4 };\n\nexport function CallStrip({ call, cuts, onGoTo }) {\n const [part, setPart] = useState(null);\n return (\n <>\n <TimeStrip\n label=\"The call, with every cut where it was said\"\n noun=\"cut\"\n length={call.length}\n away={[[0, call.joinedAt], [call.leftAt, call.length]]}\n marks={cuts.map((c) => ({ id: c.id, at: c.at, hue: FAMILY[c.reason] ?? 4, label: c.reasonLabel }))}\n onMark={(m) => onGoTo(m.id)}\n axis={[\n { at: 0, label: \"00:00 · before the client joined\" },\n { at: call.length, label: `after they left · ${call.lengthWords}` },\n ]}\n size=\"lg\"\n />\n <TimeStrip length={call.length} selection={part} onSelect={setPart} step={5} />\n </>\n );\n}\n```\n\n## States\n\nDrawn in `demo.jsx`: a call with nine marks in four colours, large and small; a\nstretch away at each end; a pressed mark; marks pressable and read-only; a part\npicked, and the bar to pick one; an hour-long call with twenty marks; marks on\nthe same second; a mark past the end and one with a bad time; a call with no\nmarks; no length; the app's own axis labels; loading; failed; a 320 px\ncontainer.\n\n## What it will not do\n\n- Play, scrub or cut the thing it shows. It is a picture of marks.\n- Keep the marks. Add, move and remove them in your own data and pass the list.\n- Draw two rows (one lane per speaker): use one strip per lane.\n- Colour a mark by anything but `hue` or `tone`.\n\nA strip it cannot be is your own component in `components/`, built from the\nkit, not an edit to this block. Its files are fixed: `npm run check` refuses a\nchange, and `makersclaw add time-strip` restores them.\n"
|
|
35
35
|
}
|
|
36
36
|
],
|
|
37
37
|
"categories": [
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"docs": "Installed to components/makersclaw/blocks/time-strip. Import: import { TimeStrip, clockOf, countMarks, lengthOf, marksOf, nextMark, pctOf, rangeOf, rangesOf, secondsAt, shareOf, stripWords, ticksFor, toSeconds } from \"@/components/makersclaw/blocks/time-strip\". Props and an example: components/makersclaw/blocks/time-strip/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
58
58
|
"meta": {
|
|
59
59
|
"makersclaw": {
|
|
60
|
-
"version": "0.8.
|
|
60
|
+
"version": "0.8.5",
|
|
61
61
|
"useWhen": "A thing that has a length in time and events at moments inside it: the cuts, decisions and comments on a recorded call or meeting, the chapters of a video, the notes pinned to a recording, the incidents in a shift or a day, the stops in a route. It draws marks the app already placed; it does not play, cut or measure anything. Not a dated chain with gates (schedule-ruler), progress against a clock (pace-bar), a calendar of events on an hour grid (time-grid, calendar-view), or an audio waveform with a playhead (audio-player)."
|
|
62
62
|
}
|
|
63
63
|
}
|
package/r/top-bar.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "TopBar",
|
|
6
6
|
"description": "The one bar every app draws over its work: where you are (a back button, a breadcrumb that keeps its last label and gives up the earlier ones first, a note), the week's allowance (words and the kit's compact Meter), Find, Ask and the screen's main action. It gives way in a set order as the app's window narrows, measured by the bar's own width (a container query, not the window: an open agent dock takes a quarter of it): under 32rem Find, Ask and the action are icons; from 32rem their words; from 36rem the earlier crumbs and the note; from 42rem the ⌘K key; from 48rem `extra`; from 56rem the allowance bar; from 64rem the allowance in words. A table of every piece's width is the single source (top-bar-model.js), and a test walks every width from a phone to a wide desk and checks what is drawn fits, so nothing overlaps. Ask is never optional: when the app has an agent to ask (a dock from useAgentDock, an onAsk, or an AskProvider above) the bar draws Ask and it opens the agent; no prop leaves it out, and `npm run check` refuses an app that mounts an AgentDock with no way to open it. It draws where it is rendered (an AppLayout header) or into the shell's header slot (TopBarSlot). AskProvider, AskButton, FindButton, BarText and the model's plain functions are exported.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"docs": "Installed to components/makersclaw/blocks/top-bar. Import: import { AskButton, AskProvider, BAR_DEFAULTS, BarText, FindButton, GAP, PIECES, SHOW, SIZES, TopBar, TopBarSlot, allowanceOf, allowanceWords, askAction, askDrawn, askOpen, barDefaultsOf, fitsAt, floorRem, resolveBar, useAsk, usedAt, visibleAt } from \"@/components/makersclaw/blocks/top-bar\". Props and an example: components/makersclaw/blocks/top-bar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
60
60
|
"meta": {
|
|
61
61
|
"makersclaw": {
|
|
62
|
-
"version": "0.8.
|
|
62
|
+
"version": "0.8.5",
|
|
63
63
|
"useWhen": "Every app's top bar, replacing each app's own components/<app>/frame.jsx TopBar: the shell or each screen's AppLayout header. Any app that mounts an AgentDock uses it (or AskButton in a bar it cannot replace) so the agent can be opened. Not for a bar inside a card or a sheet (use the parts), and not for the rail or the navigation (sidebar, navigation-menu)."
|
|
64
64
|
}
|
|
65
65
|
}
|
package/r/transaction-list.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "TransactionList",
|
|
6
6
|
"description": "Money records as a plain list, in two variants: history (payments in and out, grouped by month, a received / paid / pending / failed mark and a signed amount) and invoices (number, date, a Paid / Due in N days / N days overdue badge, amount, download). Optional date-range filter, Export and per-row download. Empty, loading and error states built in.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.5/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"docs": "Installed to components/makersclaw/blocks/transaction-list. Import: import { TransactionList } from \"@/components/makersclaw/blocks/transaction-list\". Props and an example: components/makersclaw/blocks/transaction-list/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
47
47
|
"meta": {
|
|
48
48
|
"makersclaw": {
|
|
49
|
-
"version": "0.8.
|
|
49
|
+
"version": "0.8.5",
|
|
50
50
|
"useWhen": "A billing page's invoices, an account's payment history, a wallet's recent activity: records read top to bottom, newest first. Not for records the founder sorts, filters by column or totals (data-table), or for one order (order-detail)."
|
|
51
51
|
}
|
|
52
52
|
}
|