makersclaw 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +180 -0
- package/package.json +3 -2
- package/r/annotate.json +62 -0
- package/r/approval-request.json +52 -0
- package/r/audio-player.json +74 -0
- package/r/auth-form.json +55 -0
- package/r/board.json +10 -6
- package/r/calendar-view.json +105 -0
- package/r/cart.json +62 -0
- package/r/chart.json +83 -0
- package/r/chat.json +95 -0
- package/r/code-editor.json +94 -0
- package/r/comment-box.json +64 -0
- package/r/contact-section.json +53 -0
- package/r/core.json +205 -8
- package/r/csv-import.json +63 -0
- package/r/data-table.json +69 -0
- package/r/diagram.json +74 -0
- package/r/doc-editor.json +82 -0
- package/r/doc-page.json +61 -0
- package/r/email-render.json +73 -0
- package/r/faq.json +50 -0
- package/r/file-upload.json +54 -0
- package/r/flow-canvas.json +93 -0
- package/r/footer.json +52 -0
- package/r/gantt.json +70 -0
- package/r/job-panel.json +12 -7
- package/r/json-editor.json +66 -0
- package/r/map.json +89 -0
- package/r/menubar.json +52 -0
- package/r/mind-map.json +67 -0
- package/r/navigation-menu.json +52 -0
- package/r/order-detail.json +54 -0
- package/r/org-chart.json +66 -0
- package/r/page-builder.json +105 -0
- package/r/payment-methods.json +51 -0
- package/r/pdf-document.json +79 -0
- package/r/pdf-viewer.json +88 -0
- package/r/product-detail.json +76 -0
- package/r/product-grid.json +65 -0
- package/r/record-form.json +67 -0
- package/r/record-notes.json +55 -0
- package/r/registry.json +3411 -29
- package/r/search-dialog.json +51 -0
- package/r/settings-list.json +66 -0
- package/r/setup-checklist.json +51 -0
- package/r/share-dialog.json +52 -0
- package/r/sidebar.json +69 -0
- package/r/signature-pad.json +72 -0
- package/r/sketch-canvas.json +99 -0
- package/r/spreadsheet.json +90 -0
- package/r/stat-cards.json +66 -0
- package/r/task-list.json +63 -0
- package/r/task-model.json +98 -0
- package/r/testimonials.json +52 -0
- package/r/transaction-list.json +53 -0
- package/r/whiteboard.json +66 -0
- package/registry/blocks/annotate/README.md +179 -0
- package/registry/blocks/annotate/annotate-data.js +250 -0
- package/registry/blocks/annotate/annotate.jsx +646 -0
- package/registry/blocks/annotate/block.json +10 -0
- package/registry/blocks/annotate/demo.jsx +135 -0
- package/registry/blocks/annotate/index.js +15 -0
- package/registry/blocks/approval-request/README.md +116 -0
- package/registry/blocks/approval-request/approval-request.jsx +386 -0
- package/registry/blocks/approval-request/block.json +10 -0
- package/registry/blocks/approval-request/demo.jsx +127 -0
- package/registry/blocks/approval-request/index.js +4 -0
- package/registry/blocks/audio-player/README.md +167 -0
- package/registry/blocks/audio-player/audio-player.jsx +623 -0
- package/registry/blocks/audio-player/block.json +14 -0
- package/registry/blocks/audio-player/colors.js +109 -0
- package/registry/blocks/audio-player/demo.jsx +209 -0
- package/registry/blocks/audio-player/index.js +6 -0
- package/registry/blocks/audio-player/time.js +67 -0
- package/registry/blocks/audio-player/wavesurfer.js +9 -0
- package/registry/blocks/auth-form/README.md +126 -0
- package/registry/blocks/auth-form/auth-form.jsx +568 -0
- package/registry/blocks/auth-form/block.json +10 -0
- package/registry/blocks/auth-form/demo.jsx +131 -0
- package/registry/blocks/auth-form/index.js +3 -0
- package/registry/blocks/board/README.md +55 -5
- package/registry/blocks/board/block.json +2 -2
- package/registry/blocks/board/board.jsx +230 -86
- package/registry/blocks/board/demo.jsx +103 -24
- package/registry/blocks/calendar-view/README.md +180 -0
- package/registry/blocks/calendar-view/agenda.jsx +147 -0
- package/registry/blocks/calendar-view/block.json +76 -0
- package/registry/blocks/calendar-view/calendar-model.js +442 -0
- package/registry/blocks/calendar-view/calendar.jsx +627 -0
- package/registry/blocks/calendar-view/demo.jsx +195 -0
- package/registry/blocks/calendar-view/draft.jsx +49 -0
- package/registry/blocks/calendar-view/index.js +39 -0
- package/registry/blocks/calendar-view/month.jsx +346 -0
- package/registry/blocks/calendar-view/paint.js +69 -0
- package/registry/blocks/calendar-view/time-grid.jsx +527 -0
- package/registry/blocks/calendar-view/tray.jsx +87 -0
- package/registry/blocks/cart/README.md +146 -0
- package/registry/blocks/cart/block.json +10 -0
- package/registry/blocks/cart/cart.jsx +597 -0
- package/registry/blocks/cart/demo.jsx +98 -0
- package/registry/blocks/cart/index.js +4 -0
- package/registry/blocks/cart/money.js +28 -0
- package/registry/blocks/chart/README.md +316 -0
- package/registry/blocks/chart/block.json +10 -0
- package/registry/blocks/chart/cartesian.jsx +642 -0
- package/registry/blocks/chart/core.jsx +262 -0
- package/registry/blocks/chart/demo.jsx +357 -0
- package/registry/blocks/chart/heatmap.jsx +336 -0
- package/registry/blocks/chart/index.js +8 -0
- package/registry/blocks/chart/pie.jsx +245 -0
- package/registry/blocks/chart/sparkline.jsx +88 -0
- package/registry/blocks/chat/README.md +273 -0
- package/registry/blocks/chat/attachment.jsx +95 -0
- package/registry/blocks/chat/block.json +10 -0
- package/registry/blocks/chat/chat.jsx +132 -0
- package/registry/blocks/chat/composer.jsx +364 -0
- package/registry/blocks/chat/demo.jsx +578 -0
- package/registry/blocks/chat/header.jsx +34 -0
- package/registry/blocks/chat/index.js +9 -0
- package/registry/blocks/chat/message.jsx +450 -0
- package/registry/blocks/chat/scroller.jsx +247 -0
- package/registry/blocks/chat/text.jsx +104 -0
- package/registry/blocks/code-editor/README.md +210 -0
- package/registry/blocks/code-editor/block.json +22 -0
- package/registry/blocks/code-editor/code-editor.jsx +534 -0
- package/registry/blocks/code-editor/codemirror.js +38 -0
- package/registry/blocks/code-editor/demo.jsx +205 -0
- package/registry/blocks/code-editor/index.js +8 -0
- package/registry/blocks/code-editor/json.js +317 -0
- package/registry/blocks/code-editor/sql.js +136 -0
- package/registry/blocks/code-editor/text.js +101 -0
- package/registry/blocks/code-editor/theme.js +186 -0
- package/registry/blocks/comment-box/README.md +123 -0
- package/registry/blocks/comment-box/block.json +10 -0
- package/registry/blocks/comment-box/comment-body.jsx +217 -0
- package/registry/blocks/comment-box/comment-box.jsx +198 -0
- package/registry/blocks/comment-box/comment-time.jsx +77 -0
- package/registry/blocks/comment-box/demo.jsx +141 -0
- package/registry/blocks/comment-box/index.js +8 -0
- package/registry/blocks/contact-section/README.md +152 -0
- package/registry/blocks/contact-section/block.json +11 -0
- package/registry/blocks/contact-section/contact-section.jsx +509 -0
- package/registry/blocks/contact-section/demo.jsx +93 -0
- package/registry/blocks/contact-section/index.js +4 -0
- package/registry/blocks/csv-import/README.md +133 -0
- package/registry/blocks/csv-import/block.json +13 -0
- package/registry/blocks/csv-import/csv-import.jsx +479 -0
- package/registry/blocks/csv-import/demo.jsx +106 -0
- package/registry/blocks/csv-import/import.js +200 -0
- package/registry/blocks/csv-import/index.js +7 -0
- package/registry/blocks/data-table/README.md +386 -0
- package/registry/blocks/data-table/block.json +10 -0
- package/registry/blocks/data-table/data-table.jsx +1164 -0
- package/registry/blocks/data-table/demo.jsx +588 -0
- package/registry/blocks/data-table/index.js +5 -0
- package/registry/blocks/data-table/model.js +169 -0
- package/registry/blocks/diagram/README.md +108 -0
- package/registry/blocks/diagram/block.json +13 -0
- package/registry/blocks/diagram/demo.jsx +113 -0
- package/registry/blocks/diagram/diagram.jsx +481 -0
- package/registry/blocks/diagram/index.js +7 -0
- package/registry/blocks/diagram/mermaid.js +52 -0
- package/registry/blocks/diagram/palette.js +313 -0
- package/registry/blocks/diagram/source.js +98 -0
- package/registry/blocks/doc-editor/README.md +215 -0
- package/registry/blocks/doc-editor/block.json +15 -0
- package/registry/blocks/doc-editor/demo.jsx +225 -0
- package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
- package/registry/blocks/doc-editor/doc.js +309 -0
- package/registry/blocks/doc-editor/index.js +7 -0
- package/registry/blocks/doc-editor/schema.jsx +56 -0
- package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
- package/registry/blocks/doc-editor/theme.js +82 -0
- package/registry/blocks/doc-page/README.md +119 -0
- package/registry/blocks/doc-page/autosave.js +104 -0
- package/registry/blocks/doc-page/block.json +10 -0
- package/registry/blocks/doc-page/demo.jsx +137 -0
- package/registry/blocks/doc-page/doc-page.jsx +276 -0
- package/registry/blocks/doc-page/index.js +4 -0
- package/registry/blocks/email-render/README.md +151 -0
- package/registry/blocks/email-render/block.json +14 -0
- package/registry/blocks/email-render/demo.jsx +153 -0
- package/registry/blocks/email-render/doc-email.jsx +210 -0
- package/registry/blocks/email-render/email.jsx +221 -0
- package/registry/blocks/email-render/index.js +10 -0
- package/registry/blocks/email-render/render.js +50 -0
- package/registry/blocks/email-render/theme.js +215 -0
- package/registry/blocks/faq/README.md +101 -0
- package/registry/blocks/faq/block.json +11 -0
- package/registry/blocks/faq/demo.jsx +89 -0
- package/registry/blocks/faq/faq.jsx +220 -0
- package/registry/blocks/faq/index.js +4 -0
- package/registry/blocks/file-upload/README.md +138 -0
- package/registry/blocks/file-upload/block.json +10 -0
- package/registry/blocks/file-upload/demo.jsx +204 -0
- package/registry/blocks/file-upload/file-upload.jsx +635 -0
- package/registry/blocks/file-upload/index.js +4 -0
- package/registry/blocks/flow-canvas/README.md +420 -0
- package/registry/blocks/flow-canvas/block.json +13 -0
- package/registry/blocks/flow-canvas/board.js +819 -0
- package/registry/blocks/flow-canvas/chrome.jsx +649 -0
- package/registry/blocks/flow-canvas/context.jsx +35 -0
- package/registry/blocks/flow-canvas/demo.jsx +249 -0
- package/registry/blocks/flow-canvas/edges.jsx +107 -0
- package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
- package/registry/blocks/flow-canvas/index.js +41 -0
- package/registry/blocks/flow-canvas/nodes.jsx +392 -0
- package/registry/blocks/flow-canvas/theme.js +174 -0
- package/registry/blocks/footer/README.md +133 -0
- package/registry/blocks/footer/block.json +11 -0
- package/registry/blocks/footer/demo.jsx +119 -0
- package/registry/blocks/footer/footer.jsx +406 -0
- package/registry/blocks/footer/index.js +4 -0
- package/registry/blocks/gantt/README.md +220 -0
- package/registry/blocks/gantt/block.json +10 -0
- package/registry/blocks/gantt/demo.jsx +148 -0
- package/registry/blocks/gantt/gantt.jsx +923 -0
- package/registry/blocks/gantt/index.js +7 -0
- package/registry/blocks/gantt/rows.jsx +468 -0
- package/registry/blocks/gantt/schedule.js +278 -0
- package/registry/blocks/job-panel/README.md +80 -0
- package/registry/blocks/job-panel/block.json +3 -3
- package/registry/blocks/job-panel/demo.jsx +230 -3
- package/registry/blocks/job-panel/job-panel.jsx +169 -3
- package/registry/blocks/json-editor/README.md +133 -0
- package/registry/blocks/json-editor/block.json +10 -0
- package/registry/blocks/json-editor/demo.jsx +92 -0
- package/registry/blocks/json-editor/index.js +7 -0
- package/registry/blocks/json-editor/json-editor.jsx +305 -0
- package/registry/blocks/json-editor/json-tree.jsx +393 -0
- package/registry/blocks/json-editor/tree.js +225 -0
- package/registry/blocks/map/README.md +221 -0
- package/registry/blocks/map/block.json +14 -0
- package/registry/blocks/map/demo.jsx +201 -0
- package/registry/blocks/map/geo.js +198 -0
- package/registry/blocks/map/index.js +8 -0
- package/registry/blocks/map/list.jsx +128 -0
- package/registry/blocks/map/map-view.jsx +715 -0
- package/registry/blocks/map/maplibre.js +30 -0
- package/registry/blocks/map/marks.jsx +146 -0
- package/registry/blocks/map/theme.js +142 -0
- package/registry/blocks/menubar/README.md +109 -0
- package/registry/blocks/menubar/block.json +10 -0
- package/registry/blocks/menubar/demo.jsx +128 -0
- package/registry/blocks/menubar/index.js +4 -0
- package/registry/blocks/menubar/menubar.jsx +199 -0
- package/registry/blocks/mind-map/README.md +206 -0
- package/registry/blocks/mind-map/block.json +10 -0
- package/registry/blocks/mind-map/demo.jsx +103 -0
- package/registry/blocks/mind-map/index.js +31 -0
- package/registry/blocks/mind-map/mind-map.jsx +1125 -0
- package/registry/blocks/mind-map/topic.jsx +143 -0
- package/registry/blocks/mind-map/tree.js +645 -0
- package/registry/blocks/navigation-menu/README.md +124 -0
- package/registry/blocks/navigation-menu/block.json +10 -0
- package/registry/blocks/navigation-menu/demo.jsx +84 -0
- package/registry/blocks/navigation-menu/index.js +4 -0
- package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
- package/registry/blocks/order-detail/README.md +109 -0
- package/registry/blocks/order-detail/block.json +10 -0
- package/registry/blocks/order-detail/demo.jsx +88 -0
- package/registry/blocks/order-detail/index.js +4 -0
- package/registry/blocks/order-detail/order-detail.jsx +354 -0
- package/registry/blocks/org-chart/README.md +145 -0
- package/registry/blocks/org-chart/block.json +10 -0
- package/registry/blocks/org-chart/demo.jsx +94 -0
- package/registry/blocks/org-chart/index.js +7 -0
- package/registry/blocks/org-chart/org-chart.jsx +631 -0
- package/registry/blocks/org-chart/org.js +245 -0
- package/registry/blocks/org-chart/person.jsx +96 -0
- package/registry/blocks/page-builder/README.md +257 -0
- package/registry/blocks/page-builder/block.json +14 -0
- package/registry/blocks/page-builder/demo.jsx +145 -0
- package/registry/blocks/page-builder/grapes.js +18 -0
- package/registry/blocks/page-builder/index.js +11 -0
- package/registry/blocks/page-builder/page-builder.jsx +704 -0
- package/registry/blocks/page-builder/page-view.jsx +33 -0
- package/registry/blocks/page-builder/page.js +118 -0
- package/registry/blocks/page-builder/panels.jsx +588 -0
- package/registry/blocks/page-builder/sections.js +209 -0
- package/registry/blocks/page-builder/theme.js +87 -0
- package/registry/blocks/page-builder/thumbs.jsx +189 -0
- package/registry/blocks/page-builder/tokens.js +329 -0
- package/registry/blocks/payment-methods/README.md +104 -0
- package/registry/blocks/payment-methods/block.json +10 -0
- package/registry/blocks/payment-methods/demo.jsx +71 -0
- package/registry/blocks/payment-methods/index.js +4 -0
- package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
- package/registry/blocks/pdf-document/README.md +213 -0
- package/registry/blocks/pdf-document/block.json +13 -0
- package/registry/blocks/pdf-document/demo.jsx +136 -0
- package/registry/blocks/pdf-document/index.js +33 -0
- package/registry/blocks/pdf-document/money.js +83 -0
- package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
- package/registry/blocks/pdf-document/primitives.jsx +271 -0
- package/registry/blocks/pdf-document/quote.jsx +179 -0
- package/registry/blocks/pdf-document/theme.js +193 -0
- package/registry/blocks/pdf-viewer/README.md +191 -0
- package/registry/blocks/pdf-viewer/block.json +14 -0
- package/registry/blocks/pdf-viewer/demo.jsx +252 -0
- package/registry/blocks/pdf-viewer/helpers.js +270 -0
- package/registry/blocks/pdf-viewer/index.js +8 -0
- package/registry/blocks/pdf-viewer/notes.jsx +195 -0
- package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
- package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
- package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
- package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
- package/registry/blocks/product-detail/README.md +217 -0
- package/registry/blocks/product-detail/block.json +10 -0
- package/registry/blocks/product-detail/demo.jsx +233 -0
- package/registry/blocks/product-detail/index.js +7 -0
- package/registry/blocks/product-detail/price.jsx +115 -0
- package/registry/blocks/product-detail/product-detail.jsx +348 -0
- package/registry/blocks/product-detail/product-media.jsx +102 -0
- package/registry/blocks/product-detail/product-options.jsx +144 -0
- package/registry/blocks/product-grid/README.md +162 -0
- package/registry/blocks/product-grid/block.json +10 -0
- package/registry/blocks/product-grid/demo.jsx +255 -0
- package/registry/blocks/product-grid/index.js +5 -0
- package/registry/blocks/product-grid/product-card.jsx +232 -0
- package/registry/blocks/product-grid/product-grid.jsx +235 -0
- package/registry/blocks/record-form/README.md +196 -0
- package/registry/blocks/record-form/block.json +10 -0
- package/registry/blocks/record-form/demo.jsx +525 -0
- package/registry/blocks/record-form/index.js +4 -0
- package/registry/blocks/record-form/record-fields.jsx +457 -0
- package/registry/blocks/record-form/record-form.jsx +415 -0
- package/registry/blocks/record-form/rules.js +81 -0
- package/registry/blocks/record-notes/README.md +139 -0
- package/registry/blocks/record-notes/block.json +10 -0
- package/registry/blocks/record-notes/demo.jsx +179 -0
- package/registry/blocks/record-notes/index.js +4 -0
- package/registry/blocks/record-notes/record-notes.jsx +266 -0
- package/registry/blocks/search-dialog/README.md +108 -0
- package/registry/blocks/search-dialog/block.json +10 -0
- package/registry/blocks/search-dialog/demo.jsx +116 -0
- package/registry/blocks/search-dialog/index.js +3 -0
- package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
- package/registry/blocks/settings-list/README.md +171 -0
- package/registry/blocks/settings-list/block.json +10 -0
- package/registry/blocks/settings-list/demo.jsx +333 -0
- package/registry/blocks/settings-list/index.js +4 -0
- package/registry/blocks/settings-list/row-state.jsx +93 -0
- package/registry/blocks/settings-list/settings-list.jsx +349 -0
- package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
- package/registry/blocks/setup-checklist/README.md +142 -0
- package/registry/blocks/setup-checklist/block.json +10 -0
- package/registry/blocks/setup-checklist/demo.jsx +159 -0
- package/registry/blocks/setup-checklist/index.js +4 -0
- package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
- package/registry/blocks/share-dialog/README.md +113 -0
- package/registry/blocks/share-dialog/block.json +10 -0
- package/registry/blocks/share-dialog/demo.jsx +140 -0
- package/registry/blocks/share-dialog/index.js +3 -0
- package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
- package/registry/blocks/sidebar/README.md +190 -0
- package/registry/blocks/sidebar/block.json +43 -0
- package/registry/blocks/sidebar/demo.jsx +577 -0
- package/registry/blocks/sidebar/index.js +4 -0
- package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
- package/registry/blocks/sidebar/sidebar.jsx +864 -0
- package/registry/blocks/signature-pad/README.md +137 -0
- package/registry/blocks/signature-pad/block.json +13 -0
- package/registry/blocks/signature-pad/colors.js +115 -0
- package/registry/blocks/signature-pad/demo.jsx +94 -0
- package/registry/blocks/signature-pad/index.js +6 -0
- package/registry/blocks/signature-pad/pad.js +7 -0
- package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
- package/registry/blocks/signature-pad/signature.js +68 -0
- package/registry/blocks/sketch-canvas/README.md +280 -0
- package/registry/blocks/sketch-canvas/block.json +13 -0
- package/registry/blocks/sketch-canvas/demo.jsx +159 -0
- package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
- package/registry/blocks/sketch-canvas/index.js +9 -0
- package/registry/blocks/sketch-canvas/palette.js +226 -0
- package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
- package/registry/blocks/sketch-canvas/sketch.js +179 -0
- package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
- package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
- package/registry/blocks/sketch-canvas/stamps.js +104 -0
- package/registry/blocks/sketch-canvas/theme.js +131 -0
- package/registry/blocks/spreadsheet/README.md +307 -0
- package/registry/blocks/spreadsheet/block.json +13 -0
- package/registry/blocks/spreadsheet/cells.jsx +455 -0
- package/registry/blocks/spreadsheet/chrome.jsx +325 -0
- package/registry/blocks/spreadsheet/demo.jsx +177 -0
- package/registry/blocks/spreadsheet/grid.js +387 -0
- package/registry/blocks/spreadsheet/index.js +34 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
- package/registry/blocks/spreadsheet/types.js +473 -0
- package/registry/blocks/stat-cards/README.md +173 -0
- package/registry/blocks/stat-cards/block.json +10 -0
- package/registry/blocks/stat-cards/demo.jsx +257 -0
- package/registry/blocks/stat-cards/index.js +4 -0
- package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
- package/registry/blocks/stat-cards/usage.jsx +243 -0
- package/registry/blocks/task-list/README.md +176 -0
- package/registry/blocks/task-list/block.json +10 -0
- package/registry/blocks/task-list/demo.jsx +134 -0
- package/registry/blocks/task-list/index.js +5 -0
- package/registry/blocks/task-list/task-list.jsx +907 -0
- package/registry/blocks/task-model/README.md +455 -0
- package/registry/blocks/task-model/block.json +167 -0
- package/registry/blocks/task-model/changes.js +89 -0
- package/registry/blocks/task-model/dates.js +371 -0
- package/registry/blocks/task-model/demo.jsx +215 -0
- package/registry/blocks/task-model/dnd.jsx +240 -0
- package/registry/blocks/task-model/index.js +97 -0
- package/registry/blocks/task-model/model.js +919 -0
- package/registry/blocks/task-model/parts.jsx +449 -0
- package/registry/blocks/task-model/toolbar.jsx +271 -0
- package/registry/blocks/testimonials/README.md +139 -0
- package/registry/blocks/testimonials/block.json +11 -0
- package/registry/blocks/testimonials/demo.jsx +137 -0
- package/registry/blocks/testimonials/index.js +4 -0
- package/registry/blocks/testimonials/testimonials.jsx +430 -0
- package/registry/blocks/transaction-list/README.md +126 -0
- package/registry/blocks/transaction-list/block.json +10 -0
- package/registry/blocks/transaction-list/demo.jsx +79 -0
- package/registry/blocks/transaction-list/index.js +4 -0
- package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
- package/registry/blocks/whiteboard/README.md +179 -0
- package/registry/blocks/whiteboard/block.json +10 -0
- package/registry/blocks/whiteboard/demo.jsx +169 -0
- package/registry/blocks/whiteboard/index.js +6 -0
- package/registry/blocks/whiteboard/templates.js +150 -0
- package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
- package/src/install.mjs +18 -0
- package/src/shadcn.mjs +8 -0
- package/templates/next/files/README.md +145 -11
- package/templates/next/files/app/globals.css +55 -4
- package/templates/next/files/check/run.mjs +3 -3
- package/templates/next/files/components/ui/avatar.jsx +141 -0
- package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
- package/templates/next/files/components/ui/button-group.jsx +70 -0
- package/templates/next/files/components/ui/button.jsx +132 -12
- package/templates/next/files/components/ui/calendar.jsx +609 -0
- package/templates/next/files/components/ui/carousel.jsx +281 -0
- package/templates/next/files/components/ui/collapsible.jsx +208 -0
- package/templates/next/files/components/ui/combobox.jsx +608 -0
- package/templates/next/files/components/ui/command.jsx +577 -0
- package/templates/next/files/components/ui/context-menu.jsx +191 -0
- package/templates/next/files/components/ui/date-picker.jsx +360 -0
- package/templates/next/files/components/ui/distribution.jsx +117 -0
- package/templates/next/files/components/ui/feedback.jsx +143 -0
- package/templates/next/files/components/ui/field.jsx +115 -3
- package/templates/next/files/components/ui/file-input.jsx +234 -0
- package/templates/next/files/components/ui/icon.jsx +130 -8
- package/templates/next/files/components/ui/image.jsx +143 -0
- package/templates/next/files/components/ui/index.js +47 -2
- package/templates/next/files/components/ui/input-group.jsx +213 -0
- package/templates/next/files/components/ui/input-otp.jsx +170 -0
- package/templates/next/files/components/ui/item.jsx +206 -0
- package/templates/next/files/components/ui/menu.jsx +454 -82
- package/templates/next/files/components/ui/notice.jsx +126 -14
- package/templates/next/files/components/ui/pagination.jsx +156 -0
- package/templates/next/files/components/ui/popover.jsx +284 -0
- package/templates/next/files/components/ui/radio.jsx +193 -0
- package/templates/next/files/components/ui/rating.jsx +131 -0
- package/templates/next/files/components/ui/resizable.jsx +338 -0
- package/templates/next/files/components/ui/scroll-area.jsx +123 -0
- package/templates/next/files/components/ui/separator.jsx +119 -0
- package/templates/next/files/components/ui/sheet.jsx +96 -11
- package/templates/next/files/components/ui/slider.jsx +174 -0
- package/templates/next/files/components/ui/stat.jsx +574 -0
- package/templates/next/files/components/ui/steps.jsx +338 -0
- package/templates/next/files/components/ui/swatch.jsx +33 -0
- package/templates/next/files/components/ui/table.jsx +130 -0
- package/templates/next/files/components/ui/toast.jsx +440 -0
- package/templates/next/files/components/ui/toggle.jsx +195 -0
- package/templates/next/files/components/ui/tooltip.jsx +270 -0
- package/templates/next/template.json +264 -8
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// AudioPlayer — a waveform player in the workspace theme (wavesurfer.js
|
|
4
|
+
// underneath). Play, skip, speed and mute; regions (spans of the audio in
|
|
5
|
+
// a tone: a topic, a speaker turn, a clip) and markers (moments: a
|
|
6
|
+
// comment, a flag); a hover time readout; drag across the waveform to
|
|
7
|
+
// mark a new region; keyboard throughout.
|
|
8
|
+
//
|
|
9
|
+
// What is ours and what is wavesurfer's: wavesurfer decodes the audio,
|
|
10
|
+
// plays it and paints the waveform on its canvas. Everything drawn over
|
|
11
|
+
// or around the waveform — regions, markers, the hover line, the drag
|
|
12
|
+
// selection, the controls, the loading and error states — is ours, in the
|
|
13
|
+
// kit and in tokens, so it follows the theme like any other block. The
|
|
14
|
+
// canvas's own colours are read from the tokens at run time (colors.js).
|
|
15
|
+
//
|
|
16
|
+
// Client only in effect, not in import: the modules load anywhere, the
|
|
17
|
+
// wavesurfer instance is created in an effect. The server (and the first
|
|
18
|
+
// paint) shows the frame with a skeleton waveform.
|
|
19
|
+
import { useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
20
|
+
import { Button, Empty, IconButton, Menu, Notice, Tooltip, cx } from "../../ui";
|
|
21
|
+
import { useWavesurfer } from "./wavesurfer";
|
|
22
|
+
import { readTokens, waveColours, watchTheme } from "./colors";
|
|
23
|
+
import { RATES, clamp, cleanMarkers, cleanRegions, formatRange, formatRate, formatTime, regionAt, spokenTime } from "./time";
|
|
24
|
+
|
|
25
|
+
/** A ref for the player's handle: `const player = useAudioPlayer()`, then
|
|
26
|
+
* `<AudioPlayer playerRef={player} />` and `player.current?.seek(42)`. */
|
|
27
|
+
export function useAudioPlayer() {
|
|
28
|
+
return useRef(null);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Tailwind needs whole class names in the source: one per tone.
|
|
32
|
+
const TONE = {
|
|
33
|
+
primary: { dot: "bg-primary", band: "bg-primary/10", active: "bg-primary/20", line: "bg-primary/60" },
|
|
34
|
+
"chart-2": { dot: "bg-chart-2", band: "bg-chart-2/10", active: "bg-chart-2/25", line: "bg-chart-2/60" },
|
|
35
|
+
"chart-3": { dot: "bg-chart-3", band: "bg-chart-3/10", active: "bg-chart-3/25", line: "bg-chart-3/60" },
|
|
36
|
+
"chart-4": { dot: "bg-chart-4", band: "bg-chart-4/10", active: "bg-chart-4/25", line: "bg-chart-4/60" },
|
|
37
|
+
"chart-5": { dot: "bg-chart-5", band: "bg-chart-5/10", active: "bg-chart-5/25", line: "bg-chart-5/60" },
|
|
38
|
+
success: { dot: "bg-success", band: "bg-success/10", active: "bg-success/20", line: "bg-success/60" },
|
|
39
|
+
warning: { dot: "bg-warning", band: "bg-warning/10", active: "bg-warning/20", line: "bg-warning/60" },
|
|
40
|
+
info: { dot: "bg-info", band: "bg-info/10", active: "bg-info/20", line: "bg-info/60" },
|
|
41
|
+
destructive: { dot: "bg-destructive", band: "bg-destructive/10", active: "bg-destructive/20", line: "bg-destructive/60" },
|
|
42
|
+
muted: { dot: "bg-muted-foreground", band: "bg-muted-foreground/10", active: "bg-muted-foreground/20", line: "bg-muted-foreground/50" },
|
|
43
|
+
};
|
|
44
|
+
const tone = (t, fallback = "primary") => TONE[t] ?? TONE[fallback];
|
|
45
|
+
|
|
46
|
+
const WAVE_HEIGHT = 72;
|
|
47
|
+
const DRAG_PX = 4; // a press that moves less than this is a click (seek)
|
|
48
|
+
const MIN_REGION = 0.5; // seconds: a shorter drag is treated as a click
|
|
49
|
+
const EDITABLE = /^(INPUT|TEXTAREA|SELECT)$/;
|
|
50
|
+
|
|
51
|
+
// The skeleton's bars: a fixed, gentle shape (no randomness: the server
|
|
52
|
+
// and the first client paint must match).
|
|
53
|
+
const SKELETON = Array.from({ length: 72 }, (_, i) => 22 + Math.round(38 * Math.abs(Math.sin(i * 0.37) * Math.cos(i * 0.11))));
|
|
54
|
+
|
|
55
|
+
export function AudioPlayer({
|
|
56
|
+
src,
|
|
57
|
+
peaks,
|
|
58
|
+
duration: knownDuration,
|
|
59
|
+
title,
|
|
60
|
+
description,
|
|
61
|
+
regions,
|
|
62
|
+
markers,
|
|
63
|
+
onAddMarker,
|
|
64
|
+
onRegionCreate,
|
|
65
|
+
onMarkerClick,
|
|
66
|
+
onRegionClick,
|
|
67
|
+
onActiveRegionChange,
|
|
68
|
+
onReady,
|
|
69
|
+
onPlay,
|
|
70
|
+
onPause,
|
|
71
|
+
onFinish,
|
|
72
|
+
onSeek,
|
|
73
|
+
onTimeUpdate,
|
|
74
|
+
onError,
|
|
75
|
+
rates = RATES,
|
|
76
|
+
defaultRate = 1,
|
|
77
|
+
skipSeconds = 15,
|
|
78
|
+
emptyTitle = "No recording yet",
|
|
79
|
+
emptyBody = "When there is audio to play, it shows up here.",
|
|
80
|
+
emptyAction,
|
|
81
|
+
bare = false,
|
|
82
|
+
playerRef,
|
|
83
|
+
className,
|
|
84
|
+
}) {
|
|
85
|
+
const rootRef = useRef(null);
|
|
86
|
+
const waveRef = useRef(null);
|
|
87
|
+
const surfaceRef = useRef(null);
|
|
88
|
+
const tipId = useId();
|
|
89
|
+
|
|
90
|
+
// A Blob plays through an object URL, made here and revoked when it goes.
|
|
91
|
+
const isBlob = typeof Blob !== "undefined" && src instanceof Blob;
|
|
92
|
+
const [blobUrl, setBlobUrl] = useState(null);
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (!isBlob) return setBlobUrl(null);
|
|
95
|
+
const u = URL.createObjectURL(src);
|
|
96
|
+
setBlobUrl(u);
|
|
97
|
+
return () => URL.revokeObjectURL(u);
|
|
98
|
+
}, [src, isBlob]);
|
|
99
|
+
const url = typeof src === "string" && src ? src : isBlob ? blobUrl ?? undefined : undefined;
|
|
100
|
+
const hasAudio = Boolean((typeof src === "string" && src) || isBlob || peaks);
|
|
101
|
+
|
|
102
|
+
// The instance. Every key is always passed (the hook's effect depends on
|
|
103
|
+
// the list of values; its length must not change) and none of them is a
|
|
104
|
+
// colour: colours are set after, so a theme change repaints instead of
|
|
105
|
+
// rebuilding the player.
|
|
106
|
+
const { wavesurfer: ws, isReady, isPlaying, currentTime } = useWavesurfer({
|
|
107
|
+
container: waveRef,
|
|
108
|
+
url,
|
|
109
|
+
peaks,
|
|
110
|
+
duration: knownDuration,
|
|
111
|
+
height: WAVE_HEIGHT,
|
|
112
|
+
barWidth: 2,
|
|
113
|
+
barGap: 2,
|
|
114
|
+
barRadius: 2,
|
|
115
|
+
cursorWidth: 2,
|
|
116
|
+
normalize: true,
|
|
117
|
+
interact: false,
|
|
118
|
+
hideScrollbar: true,
|
|
119
|
+
autoScroll: false,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const [duration, setDuration] = useState(knownDuration > 0 ? knownDuration : 0);
|
|
123
|
+
const [error, setError] = useState(null);
|
|
124
|
+
const [rate, setRate] = useState(defaultRate);
|
|
125
|
+
const [muted, setMuted] = useState(false);
|
|
126
|
+
const [notice, setNotice] = useState(null);
|
|
127
|
+
const [pending, setPending] = useState({ markers: [], regions: [] });
|
|
128
|
+
const [hover, setHover] = useState(null); // ratio 0..1 under the pointer
|
|
129
|
+
const [draft, setDraft] = useState(null); // { a, b } ratios while dragging a region
|
|
130
|
+
const [width, setWidth] = useState(0);
|
|
131
|
+
const drag = useRef(null);
|
|
132
|
+
|
|
133
|
+
// The latest callbacks, so the instance's listeners never go stale.
|
|
134
|
+
const cbs = useRef({});
|
|
135
|
+
cbs.current = { onReady, onPlay, onPause, onFinish, onTimeUpdate, onError };
|
|
136
|
+
|
|
137
|
+
// Settings the instance must keep across a reload (a new src).
|
|
138
|
+
const settings = useRef({ rate, muted });
|
|
139
|
+
settings.current = { rate, muted };
|
|
140
|
+
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (!ws) return;
|
|
143
|
+
setError(null);
|
|
144
|
+
const off = [
|
|
145
|
+
ws.on("ready", (d) => {
|
|
146
|
+
setDuration(d);
|
|
147
|
+
setError(null);
|
|
148
|
+
ws.setPlaybackRate(settings.current.rate);
|
|
149
|
+
ws.setMuted(settings.current.muted);
|
|
150
|
+
cbs.current.onReady?.({ duration: d });
|
|
151
|
+
}),
|
|
152
|
+
ws.on("decode", (d) => setDuration(d)),
|
|
153
|
+
ws.on("play", () => cbs.current.onPlay?.()),
|
|
154
|
+
ws.on("pause", () => cbs.current.onPause?.()),
|
|
155
|
+
ws.on("finish", () => cbs.current.onFinish?.()),
|
|
156
|
+
ws.on("timeupdate", (t) => cbs.current.onTimeUpdate?.(t)),
|
|
157
|
+
ws.on("error", (e) => {
|
|
158
|
+
setError(e ?? new Error("The audio could not be loaded."));
|
|
159
|
+
cbs.current.onError?.(e);
|
|
160
|
+
}),
|
|
161
|
+
];
|
|
162
|
+
return () => off.forEach((f) => f());
|
|
163
|
+
}, [ws]);
|
|
164
|
+
|
|
165
|
+
// The canvas's colours, from the tokens where the player sits; again
|
|
166
|
+
// whenever the theme or the scheme changes.
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (!ws) return;
|
|
169
|
+
return watchTheme(rootRef.current, () => {
|
|
170
|
+
const c = waveColours(readTokens(rootRef.current, ["foreground", "muted-foreground", "primary"]));
|
|
171
|
+
const opts = Object.fromEntries(Object.entries(c).filter(([, v]) => v));
|
|
172
|
+
if (Object.keys(opts).length) ws.setOptions(opts);
|
|
173
|
+
});
|
|
174
|
+
}, [ws]);
|
|
175
|
+
|
|
176
|
+
// The waveform's width in pixels: for hiding labels that would not fit.
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
const el = surfaceRef.current;
|
|
179
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
180
|
+
const ro = new ResizeObserver(([e]) => setWidth(e.contentRect.width));
|
|
181
|
+
ro.observe(el);
|
|
182
|
+
return () => ro.disconnect();
|
|
183
|
+
}, []);
|
|
184
|
+
|
|
185
|
+
const ready = Boolean(ws && isReady && duration > 0 && !error);
|
|
186
|
+
const shownRegions = useMemo(() => cleanRegions([...(regions ?? []), ...pending.regions], duration), [regions, pending.regions, duration]);
|
|
187
|
+
const shownMarkers = useMemo(() => cleanMarkers([...(markers ?? []), ...pending.markers], duration), [markers, pending.markers, duration]);
|
|
188
|
+
const active = ready ? regionAt(shownRegions, currentTime) : null;
|
|
189
|
+
|
|
190
|
+
// The region under the playhead, told once per change (transcript sync).
|
|
191
|
+
const lastActive = useRef(undefined);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
const id = active?.id ?? null;
|
|
194
|
+
if (lastActive.current === id) return;
|
|
195
|
+
lastActive.current = id;
|
|
196
|
+
onActiveRegionChange?.(active ?? null);
|
|
197
|
+
}, [active, onActiveRegionChange]);
|
|
198
|
+
|
|
199
|
+
// ---- actions --------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
const seek = useCallback(
|
|
202
|
+
(t) => {
|
|
203
|
+
if (!ws || !(duration > 0)) return;
|
|
204
|
+
const to = clamp(t, 0, duration);
|
|
205
|
+
ws.setTime(to);
|
|
206
|
+
onSeek?.(to);
|
|
207
|
+
},
|
|
208
|
+
[ws, duration, onSeek],
|
|
209
|
+
);
|
|
210
|
+
const skip = useCallback((d) => ws && seek(ws.getCurrentTime() + d), [ws, seek]);
|
|
211
|
+
// play() can be refused by the browser (no user gesture yet): not an error to show.
|
|
212
|
+
const quiet = (p) => p?.catch?.(() => {});
|
|
213
|
+
const toggle = useCallback(() => ready && quiet(ws.playPause()), [ready, ws]);
|
|
214
|
+
const changeRate = useCallback(
|
|
215
|
+
(r) => {
|
|
216
|
+
setRate(r);
|
|
217
|
+
ws?.setPlaybackRate(r);
|
|
218
|
+
},
|
|
219
|
+
[ws],
|
|
220
|
+
);
|
|
221
|
+
const toggleMute = useCallback(() => {
|
|
222
|
+
setMuted((m) => {
|
|
223
|
+
ws?.setMuted(!m);
|
|
224
|
+
return !m;
|
|
225
|
+
});
|
|
226
|
+
}, [ws]);
|
|
227
|
+
const playRange = useCallback(
|
|
228
|
+
(start, end) => {
|
|
229
|
+
if (!ready) return;
|
|
230
|
+
onSeek?.(start);
|
|
231
|
+
quiet(ws.play(clamp(start, 0, duration), end == null ? undefined : clamp(end, 0, duration)));
|
|
232
|
+
},
|
|
233
|
+
[ready, ws, duration, onSeek],
|
|
234
|
+
);
|
|
235
|
+
const retry = useCallback(() => {
|
|
236
|
+
if (!ws || !url) return;
|
|
237
|
+
setError(null);
|
|
238
|
+
ws.load(url).catch(() => {});
|
|
239
|
+
}, [ws, url]);
|
|
240
|
+
|
|
241
|
+
// Optimistic: the marker shows at once, dimmed, and goes if the save fails.
|
|
242
|
+
const addMarker = useCallback(async () => {
|
|
243
|
+
if (!onAddMarker || !ready) return;
|
|
244
|
+
const time = ws.getCurrentTime();
|
|
245
|
+
const temp = { id: `pending-${Date.now()}`, time, label: "Saving…", tone: "muted", pending: true };
|
|
246
|
+
setPending((p) => ({ ...p, markers: [...p.markers, temp] }));
|
|
247
|
+
setNotice(null);
|
|
248
|
+
let res;
|
|
249
|
+
try {
|
|
250
|
+
res = await onAddMarker({ time });
|
|
251
|
+
} catch (e) {
|
|
252
|
+
res = { ok: false, error: e?.message };
|
|
253
|
+
}
|
|
254
|
+
setPending((p) => ({ ...p, markers: p.markers.filter((m) => m.id !== temp.id) }));
|
|
255
|
+
if (!res?.ok) setNotice(`Couldn't add the marker at ${formatTime(time)}. ${res?.error || "Try again."}`);
|
|
256
|
+
}, [onAddMarker, ready, ws]);
|
|
257
|
+
|
|
258
|
+
const createRegion = useCallback(
|
|
259
|
+
async (start, end) => {
|
|
260
|
+
if (!onRegionCreate) return;
|
|
261
|
+
const temp = { id: `pending-${Date.now()}`, start, end, label: "Saving…", tone: "muted", pending: true };
|
|
262
|
+
setPending((p) => ({ ...p, regions: [...p.regions, temp] }));
|
|
263
|
+
setNotice(null);
|
|
264
|
+
let res;
|
|
265
|
+
try {
|
|
266
|
+
res = await onRegionCreate({ start, end });
|
|
267
|
+
} catch (e) {
|
|
268
|
+
res = { ok: false, error: e?.message };
|
|
269
|
+
}
|
|
270
|
+
setPending((p) => ({ ...p, regions: p.regions.filter((r) => r.id !== temp.id) }));
|
|
271
|
+
if (!res?.ok) setNotice(`Couldn't save the region ${formatRange(start, end)}. ${res?.error || "Try again."}`);
|
|
272
|
+
},
|
|
273
|
+
[onRegionCreate],
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
useImperativeHandle(
|
|
277
|
+
playerRef,
|
|
278
|
+
() => ({
|
|
279
|
+
play: () => ready && quiet(ws.play()),
|
|
280
|
+
pause: () => ws?.pause(),
|
|
281
|
+
toggle,
|
|
282
|
+
seek,
|
|
283
|
+
skip,
|
|
284
|
+
playRange,
|
|
285
|
+
playRegion: (id) => {
|
|
286
|
+
const r = shownRegions.find((x) => x.id === id);
|
|
287
|
+
if (r) playRange(r.start, r.end);
|
|
288
|
+
},
|
|
289
|
+
setRate: changeRate,
|
|
290
|
+
setMuted: (m) => {
|
|
291
|
+
setMuted(Boolean(m));
|
|
292
|
+
ws?.setMuted(Boolean(m));
|
|
293
|
+
},
|
|
294
|
+
addMarker,
|
|
295
|
+
getTime: () => ws?.getCurrentTime() ?? 0,
|
|
296
|
+
getDuration: () => duration,
|
|
297
|
+
isPlaying: () => Boolean(ws?.isPlaying()),
|
|
298
|
+
isReady: () => ready,
|
|
299
|
+
}),
|
|
300
|
+
[ready, ws, toggle, seek, skip, playRange, shownRegions, changeRate, addMarker, duration],
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// ---- pointer and keys on the waveform ---------------------------------------
|
|
304
|
+
|
|
305
|
+
const ratioAt = (e) => {
|
|
306
|
+
const r = surfaceRef.current?.getBoundingClientRect();
|
|
307
|
+
return r && r.width ? clamp((e.clientX - r.left) / r.width, 0, 1) : 0;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
function onPointerDown(e) {
|
|
311
|
+
if (!ready || e.button > 0) return;
|
|
312
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
313
|
+
drag.current = { x: e.clientX, start: ratioAt(e), moved: false };
|
|
314
|
+
}
|
|
315
|
+
function onPointerMove(e) {
|
|
316
|
+
const ratio = ratioAt(e);
|
|
317
|
+
if (e.pointerType !== "touch") setHover(ratio);
|
|
318
|
+
const d = drag.current;
|
|
319
|
+
if (!d) return;
|
|
320
|
+
if (!d.moved && Math.abs(e.clientX - d.x) < DRAG_PX) return;
|
|
321
|
+
d.moved = true;
|
|
322
|
+
if (onRegionCreate) setDraft({ a: d.start, b: ratio });
|
|
323
|
+
else seek(ratio * duration); // no regions to make: a drag scrubs
|
|
324
|
+
}
|
|
325
|
+
function onPointerUp(e) {
|
|
326
|
+
const d = drag.current;
|
|
327
|
+
drag.current = null;
|
|
328
|
+
if (!d) return;
|
|
329
|
+
const ratio = ratioAt(e);
|
|
330
|
+
if (!d.moved) return seek(ratio * duration);
|
|
331
|
+
if (onRegionCreate) {
|
|
332
|
+
setDraft(null);
|
|
333
|
+
const start = Math.min(d.start, ratio) * duration;
|
|
334
|
+
const end = Math.max(d.start, ratio) * duration;
|
|
335
|
+
if (end - start >= MIN_REGION) createRegion(start, end);
|
|
336
|
+
else seek(ratio * duration);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function onPointerCancel() {
|
|
340
|
+
drag.current = null;
|
|
341
|
+
setDraft(null);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function onSurfaceKey(e) {
|
|
345
|
+
if (!ready) return;
|
|
346
|
+
const small = 5;
|
|
347
|
+
const moves = { ArrowLeft: -small, ArrowRight: small, PageDown: -skipSeconds, PageUp: skipSeconds };
|
|
348
|
+
if (e.key in moves) {
|
|
349
|
+
e.preventDefault();
|
|
350
|
+
skip(moves[e.key]);
|
|
351
|
+
} else if (e.key === "Home") {
|
|
352
|
+
e.preventDefault();
|
|
353
|
+
seek(0);
|
|
354
|
+
} else if (e.key === "End") {
|
|
355
|
+
e.preventDefault();
|
|
356
|
+
seek(duration);
|
|
357
|
+
} else if (e.key === " " || e.key === "Enter") {
|
|
358
|
+
e.preventDefault();
|
|
359
|
+
toggle();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Player-wide letters, like a video player: K play/pause, J/L skip, M
|
|
364
|
+
// marker. Never while typing, never with a modifier (those are the app's).
|
|
365
|
+
function onRootKey(e) {
|
|
366
|
+
if (!ready || e.metaKey || e.ctrlKey || e.altKey || e.defaultPrevented) return;
|
|
367
|
+
const t = e.target;
|
|
368
|
+
if (EDITABLE.test(t?.tagName ?? "") || t?.isContentEditable) return;
|
|
369
|
+
const k = e.key.toLowerCase();
|
|
370
|
+
if (k === "k") toggle();
|
|
371
|
+
else if (k === "j") skip(-skipSeconds);
|
|
372
|
+
else if (k === "l") skip(skipSeconds);
|
|
373
|
+
else if (k === "m" && onAddMarker) addMarker();
|
|
374
|
+
else return;
|
|
375
|
+
e.preventDefault();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ---- render -----------------------------------------------------------------
|
|
379
|
+
|
|
380
|
+
const frame = bare ? "" : "rounded-lg border border-border bg-card p-4 sm:p-5";
|
|
381
|
+
|
|
382
|
+
if (!hasAudio) {
|
|
383
|
+
return (
|
|
384
|
+
<div className={cx(frame, className)}>
|
|
385
|
+
<Header title={title} description={description} />
|
|
386
|
+
<Empty icon="mic" title={emptyTitle} body={emptyBody} action={emptyAction} className={title || description ? "mt-4" : ""} />
|
|
387
|
+
</div>
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const pct = (t) => `${(duration > 0 ? (t / duration) * 100 : 0).toFixed(3)}%`;
|
|
392
|
+
const draftSpan = draft ? { from: Math.min(draft.a, draft.b), to: Math.max(draft.a, draft.b) } : null;
|
|
393
|
+
const loading = !ready && !error;
|
|
394
|
+
|
|
395
|
+
return (
|
|
396
|
+
<div ref={rootRef} className={cx(frame, "text-card-foreground", className)} onKeyDown={onRootKey}>
|
|
397
|
+
<Header title={title} description={description} />
|
|
398
|
+
|
|
399
|
+
{/* Markers: pins above the waveform. */}
|
|
400
|
+
<div className={cx("relative h-6", (title || description) && "mt-3")}>
|
|
401
|
+
{ready
|
|
402
|
+
? shownMarkers.map((m) => (
|
|
403
|
+
<div key={m.id} className="absolute top-0 -translate-x-1/2" style={{ left: pct(m.time) }}>
|
|
404
|
+
<Tooltip label={`${formatTime(m.time)} · ${m.label ?? "Marker"}`} delay={150}>
|
|
405
|
+
{/* raw: a pin placed on the timeline at its moment — a kit Button cannot be positioned by time or be this small */}
|
|
406
|
+
<button
|
|
407
|
+
type="button"
|
|
408
|
+
disabled={m.pending}
|
|
409
|
+
aria-label={`${m.label ?? "Marker"}, at ${spokenTime(m.time)}`}
|
|
410
|
+
onClick={() => {
|
|
411
|
+
seek(m.time);
|
|
412
|
+
onMarkerClick?.(m);
|
|
413
|
+
}}
|
|
414
|
+
className="group grid size-6 place-items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default"
|
|
415
|
+
>
|
|
416
|
+
<span
|
|
417
|
+
className={cx(
|
|
418
|
+
"size-2.5 rounded-full ring-2 ring-card transition-transform duration-150 group-hover:scale-125 group-focus-visible:scale-125 motion-reduce:transition-none",
|
|
419
|
+
tone(m.tone).dot,
|
|
420
|
+
m.pending && "animate-pulse motion-reduce:animate-none",
|
|
421
|
+
Math.abs(currentTime - m.time) < 0.75 && "scale-125",
|
|
422
|
+
)}
|
|
423
|
+
/>
|
|
424
|
+
</button>
|
|
425
|
+
</Tooltip>
|
|
426
|
+
</div>
|
|
427
|
+
))
|
|
428
|
+
: null}
|
|
429
|
+
</div>
|
|
430
|
+
|
|
431
|
+
{/* The waveform: wavesurfer's canvas, with our layer over it. */}
|
|
432
|
+
<div className="relative" style={{ height: WAVE_HEIGHT }}>
|
|
433
|
+
<div ref={waveRef} className={cx("w-full transition-opacity duration-150 motion-reduce:transition-none", loading && "opacity-0")} />
|
|
434
|
+
|
|
435
|
+
{loading ? (
|
|
436
|
+
<div aria-hidden="true" className="pointer-events-none absolute inset-0 flex items-center gap-[2px] overflow-hidden">
|
|
437
|
+
{SKELETON.map((h, i) => (
|
|
438
|
+
<span key={i} className="w-[2px] flex-1 animate-pulse rounded-full bg-muted motion-reduce:animate-none" style={{ height: `${h}%` }} />
|
|
439
|
+
))}
|
|
440
|
+
</div>
|
|
441
|
+
) : null}
|
|
442
|
+
|
|
443
|
+
{error ? (
|
|
444
|
+
<div className="absolute inset-0 flex items-center">
|
|
445
|
+
<span aria-hidden="true" className="h-px w-full bg-border" />
|
|
446
|
+
</div>
|
|
447
|
+
) : null}
|
|
448
|
+
|
|
449
|
+
{/* raw: the seek surface — a slider over a canvas; the kit Slider is a native range input and cannot host the waveform, regions or markers */}
|
|
450
|
+
<div
|
|
451
|
+
ref={surfaceRef}
|
|
452
|
+
role="slider"
|
|
453
|
+
tabIndex={ready ? 0 : -1}
|
|
454
|
+
aria-label="Seek"
|
|
455
|
+
aria-valuemin={0}
|
|
456
|
+
aria-valuemax={Math.round(duration)}
|
|
457
|
+
aria-valuenow={Math.round(currentTime)}
|
|
458
|
+
aria-valuetext={`${spokenTime(currentTime)} of ${spokenTime(duration)}`}
|
|
459
|
+
aria-disabled={!ready || undefined}
|
|
460
|
+
aria-describedby={tipId}
|
|
461
|
+
onKeyDown={onSurfaceKey}
|
|
462
|
+
onPointerDown={onPointerDown}
|
|
463
|
+
onPointerMove={onPointerMove}
|
|
464
|
+
onPointerUp={onPointerUp}
|
|
465
|
+
onPointerCancel={onPointerCancel}
|
|
466
|
+
onPointerLeave={() => setHover(null)}
|
|
467
|
+
className={cx(
|
|
468
|
+
"absolute inset-0 z-10 touch-pan-y rounded-md outline-none select-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card",
|
|
469
|
+
ready ? (onRegionCreate ? "cursor-crosshair" : "cursor-pointer") : "cursor-default",
|
|
470
|
+
)}
|
|
471
|
+
>
|
|
472
|
+
{ready
|
|
473
|
+
? shownRegions.map((r) => (
|
|
474
|
+
<span
|
|
475
|
+
key={r.id}
|
|
476
|
+
aria-hidden="true"
|
|
477
|
+
className={cx("pointer-events-none absolute inset-y-0 transition-colors duration-150 motion-reduce:transition-none", active?.id === r.id ? tone(r.tone).active : tone(r.tone).band)}
|
|
478
|
+
style={{ left: pct(r.start), width: pct(r.end - r.start) }}
|
|
479
|
+
/>
|
|
480
|
+
))
|
|
481
|
+
: null}
|
|
482
|
+
{ready
|
|
483
|
+
? shownMarkers.map((m) => (
|
|
484
|
+
<span key={m.id} aria-hidden="true" className={cx("pointer-events-none absolute inset-y-0 w-px", tone(m.tone).line)} style={{ left: pct(m.time) }} />
|
|
485
|
+
))
|
|
486
|
+
: null}
|
|
487
|
+
|
|
488
|
+
{draftSpan ? (
|
|
489
|
+
<span
|
|
490
|
+
aria-hidden="true"
|
|
491
|
+
className="pointer-events-none absolute inset-y-0 border-x border-primary bg-primary/15"
|
|
492
|
+
style={{ left: `${draftSpan.from * 100}%`, width: `${(draftSpan.to - draftSpan.from) * 100}%` }}
|
|
493
|
+
/>
|
|
494
|
+
) : null}
|
|
495
|
+
|
|
496
|
+
{ready && hover != null ? (
|
|
497
|
+
<>
|
|
498
|
+
<span aria-hidden="true" className="pointer-events-none absolute inset-y-0 w-px bg-foreground/40" style={{ left: `${hover * 100}%` }} />
|
|
499
|
+
<span
|
|
500
|
+
aria-hidden="true"
|
|
501
|
+
className="pointer-events-none absolute -top-1 z-30 -translate-x-1/2 -translate-y-full rounded-md bg-foreground px-1.5 py-0.5 text-xs font-medium whitespace-nowrap text-background tabular-nums shadow-sm"
|
|
502
|
+
style={{ left: `${clamp(hover * 100, 4, 96)}%` }}
|
|
503
|
+
>
|
|
504
|
+
{draftSpan ? formatRange(draftSpan.from * duration, draftSpan.to * duration) : formatTime(hover * duration)}
|
|
505
|
+
</span>
|
|
506
|
+
</>
|
|
507
|
+
) : null}
|
|
508
|
+
</div>
|
|
509
|
+
<span id={tipId} className="sr-only">
|
|
510
|
+
{onRegionCreate ? "Click to jump, drag to mark a region. " : "Click or drag to jump. "}
|
|
511
|
+
Arrow keys move 5 seconds, Page Up and Page Down {skipSeconds}, Space plays or pauses.
|
|
512
|
+
</span>
|
|
513
|
+
</div>
|
|
514
|
+
|
|
515
|
+
{/* Regions: labels in a lane under the waveform; a click plays one. */}
|
|
516
|
+
{ready && shownRegions.length ? (
|
|
517
|
+
<div className="relative mt-1.5 h-7">
|
|
518
|
+
{shownRegions.map((r) => {
|
|
519
|
+
const px = ((r.end - r.start) / duration) * width;
|
|
520
|
+
const on = active?.id === r.id;
|
|
521
|
+
return (
|
|
522
|
+
<div key={r.id} className="absolute inset-y-0 px-px" style={{ left: pct(r.start), width: pct(r.end - r.start) }}>
|
|
523
|
+
<Tooltip label={`${r.label ?? "Region"} · ${formatRange(r.start, r.end)}`} delay={300}>
|
|
524
|
+
{/* raw: a segment of the timeline, sized to its span — a kit Button has a fixed height and padding */}
|
|
525
|
+
<button
|
|
526
|
+
type="button"
|
|
527
|
+
disabled={r.pending}
|
|
528
|
+
aria-label={`Play ${r.label ?? "region"}, ${spokenTime(r.start)} to ${spokenTime(r.end)}`}
|
|
529
|
+
aria-current={on || undefined}
|
|
530
|
+
onClick={() => {
|
|
531
|
+
playRange(r.start, r.end);
|
|
532
|
+
onRegionClick?.(r);
|
|
533
|
+
}}
|
|
534
|
+
className="group flex h-7 w-full min-w-0 flex-col gap-1 rounded-sm pt-0.5 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default"
|
|
535
|
+
>
|
|
536
|
+
<span className={cx("h-1 w-full rounded-full transition-opacity duration-150 motion-reduce:transition-none", tone(r.tone).dot, on ? "opacity-100" : "opacity-60 group-hover:opacity-100", r.pending && "animate-pulse motion-reduce:animate-none")} />
|
|
537
|
+
{px >= 44 ? (
|
|
538
|
+
<span className={cx("block w-full truncate text-xs leading-4", on ? "font-medium text-foreground" : "text-muted-foreground group-hover:text-foreground")}>{r.label ?? "Region"}</span>
|
|
539
|
+
) : null}
|
|
540
|
+
</button>
|
|
541
|
+
</Tooltip>
|
|
542
|
+
</div>
|
|
543
|
+
);
|
|
544
|
+
})}
|
|
545
|
+
</div>
|
|
546
|
+
) : null}
|
|
547
|
+
|
|
548
|
+
{error ? (
|
|
549
|
+
<Notice
|
|
550
|
+
tone="danger"
|
|
551
|
+
title="Couldn't play this recording"
|
|
552
|
+
className="mt-3"
|
|
553
|
+
action={
|
|
554
|
+
url ? (
|
|
555
|
+
<Button size="sm" variant="secondary" icon="refresh" onClick={retry}>
|
|
556
|
+
Try again
|
|
557
|
+
</Button>
|
|
558
|
+
) : null
|
|
559
|
+
}
|
|
560
|
+
>
|
|
561
|
+
The file may be missing, or in a format this browser can't play.
|
|
562
|
+
</Notice>
|
|
563
|
+
) : null}
|
|
564
|
+
|
|
565
|
+
{notice ? (
|
|
566
|
+
<Notice tone="danger" className="mt-3" onDismiss={() => setNotice(null)}>
|
|
567
|
+
{notice}
|
|
568
|
+
</Notice>
|
|
569
|
+
) : null}
|
|
570
|
+
|
|
571
|
+
{/* Controls: one row on a wide screen; on a phone the transport gets
|
|
572
|
+
its own centred row and the time sits under it with the rest. */}
|
|
573
|
+
<div className="mt-3 flex flex-wrap items-center gap-x-2 gap-y-2">
|
|
574
|
+
<div className="flex w-full items-center justify-center gap-2 sm:w-auto sm:justify-start">
|
|
575
|
+
<Tooltip label={`Back ${skipSeconds}s · J`}>
|
|
576
|
+
<IconButton icon="rotate-ccw" label={`Back ${skipSeconds} seconds`} disabled={!ready} onClick={() => skip(-skipSeconds)} />
|
|
577
|
+
</Tooltip>
|
|
578
|
+
<Tooltip label={isPlaying ? "Pause · K" : "Play · K"}>
|
|
579
|
+
<IconButton icon={isPlaying ? "pause" : "play"} variant="primary" label={isPlaying ? "Pause" : "Play"} disabled={!ready} onClick={toggle} />
|
|
580
|
+
</Tooltip>
|
|
581
|
+
<Tooltip label={`Forward ${skipSeconds}s · L`}>
|
|
582
|
+
<IconButton icon="rotate-cw" label={`Forward ${skipSeconds} seconds`} disabled={!ready} onClick={() => skip(skipSeconds)} />
|
|
583
|
+
</Tooltip>
|
|
584
|
+
</div>
|
|
585
|
+
|
|
586
|
+
<p className="px-1 text-sm whitespace-nowrap text-muted-foreground tabular-nums sm:ml-1">
|
|
587
|
+
<span className="text-foreground">{formatTime(currentTime)}</span>
|
|
588
|
+
<span aria-hidden="true"> / </span>
|
|
589
|
+
<span className="sr-only"> of </span>
|
|
590
|
+
{ready || knownDuration ? formatTime(duration) : "–:––"}
|
|
591
|
+
</p>
|
|
592
|
+
|
|
593
|
+
<div className="ml-auto flex items-center gap-1">
|
|
594
|
+
{onAddMarker ? (
|
|
595
|
+
<Tooltip label="Add a marker here · M">
|
|
596
|
+
<Button size="sm" variant="quiet" icon="flag" disabled={!ready} onClick={addMarker}>
|
|
597
|
+
Marker
|
|
598
|
+
</Button>
|
|
599
|
+
</Tooltip>
|
|
600
|
+
) : null}
|
|
601
|
+
<Menu
|
|
602
|
+
label={`Playback speed, ${formatRate(rate)}`}
|
|
603
|
+
trigger={<span className="px-1 text-sm font-medium tabular-nums">{formatRate(rate)}</span>}
|
|
604
|
+
items={[{ type: "radio", label: "Speed", value: String(rate), onValueChange: (v) => changeRate(Number(v)), options: rates.map((r) => ({ value: String(r), label: r === 1 ? "Normal" : formatRate(r) })) }]}
|
|
605
|
+
/>
|
|
606
|
+
<Tooltip label={muted ? "Unmute" : "Mute"}>
|
|
607
|
+
<IconButton icon={muted ? "volume-x" : "volume"} label={muted ? "Unmute" : "Mute"} size="sm" onClick={toggleMute} aria-pressed={muted} />
|
|
608
|
+
</Tooltip>
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function Header({ title, description }) {
|
|
616
|
+
if (!title && !description) return null;
|
|
617
|
+
return (
|
|
618
|
+
<div className="min-w-0">
|
|
619
|
+
{title ? <h3 className="truncate text-sm font-medium text-foreground">{title}</h3> : null}
|
|
620
|
+
{description ? <p className="mt-0.5 truncate text-xs text-muted-foreground">{description}</p> : null}
|
|
621
|
+
</div>
|
|
622
|
+
);
|
|
623
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "audio-player",
|
|
3
|
+
"title": "AudioPlayer",
|
|
4
|
+
"description": "A waveform audio player (wavesurfer.js) in the workspace theme: play, skip back and forward, speed, mute; regions (spans of the audio in a theme tone, labelled in a lane under the waveform, click to play one) and markers (pins at moments, click to jump); a hover time readout; drag across the waveform to mark a new region; add a marker at the playhead; keyboard like a video player (K, J, L, M, arrows). Time events and a handle (seek, play a range) for syncing a transcript.",
|
|
5
|
+
"useWhen": "A screen plays a recording: a call or meeting recording, a voice note, a podcast clip, a voicemail, a sound file on a record. The call-review block is built on it. Not for video, not for recording audio from the microphone.",
|
|
6
|
+
"tags": ["audio", "player", "waveform", "wavesurfer", "recording", "call", "voice note", "podcast", "regions", "markers", "playback", "speed", "transcript"],
|
|
7
|
+
"exports": ["AudioPlayer", "useAudioPlayer", "formatTime", "formatRange", "spokenTime", "regionAt", "TONES", "RATES"],
|
|
8
|
+
"files": ["index.js", "audio-player.jsx", "wavesurfer.js", "colors.js", "time.js"],
|
|
9
|
+
"requires": [],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"wavesurfer.js": "8.0.0",
|
|
12
|
+
"@wavesurfer/react": "1.0.12"
|
|
13
|
+
}
|
|
14
|
+
}
|