@treenity/mods 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/board/board.test.ts +212 -0
- package/board/client.ts +1 -0
- package/board/seed.ts +26 -0
- package/board/server.ts +5 -0
- package/board/types.ts +87 -0
- package/board/view.tsx +574 -0
- package/brahman/CLAUDE.md +18 -0
- package/brahman/brahman.test.ts +855 -0
- package/brahman/client.ts +2 -0
- package/brahman/helpers.ts +374 -0
- package/brahman/server.ts +2 -0
- package/brahman/service.ts +328 -0
- package/brahman/types.ts +727 -0
- package/brahman/view.tsx +73 -0
- package/brahman/views/action-cards.tsx +615 -0
- package/brahman/views/bot-view.tsx +76 -0
- package/brahman/views/chat-editor.tsx +782 -0
- package/brahman/views/chat-preview.tsx +266 -0
- package/brahman/views/menu-editor.tsx +451 -0
- package/brahman/views/page-layout.tsx +285 -0
- package/brahman/views/tstring-input.tsx +84 -0
- package/cafe/CLAUDE.md +7 -0
- package/cafe/seed.ts +8 -0
- package/cafe/server.ts +2 -0
- package/cafe/types.ts +32 -0
- package/canary/seed.ts +8 -0
- package/canary/server.ts +3 -0
- package/canary/service.ts +101 -0
- package/canary/types.ts +16 -0
- package/dist/board/client.d.ts +2 -0
- package/dist/board/client.d.ts.map +1 -0
- package/dist/board/client.js +2 -0
- package/dist/board/client.js.map +1 -0
- package/dist/board/seed.d.ts +2 -0
- package/dist/board/seed.d.ts.map +1 -0
- package/dist/board/seed.js +23 -0
- package/dist/board/seed.js.map +1 -0
- package/dist/board/server.d.ts +3 -0
- package/dist/board/server.d.ts.map +1 -0
- package/dist/board/server.js +5 -0
- package/dist/board/server.js.map +1 -0
- package/dist/board/types.d.ts +45 -0
- package/dist/board/types.d.ts.map +1 -0
- package/dist/board/types.js +82 -0
- package/dist/board/types.js.map +1 -0
- package/dist/board/view.d.ts +2 -0
- package/dist/board/view.d.ts.map +1 -0
- package/dist/board/view.js +254 -0
- package/dist/board/view.js.map +1 -0
- package/dist/brahman/client.d.ts +3 -0
- package/dist/brahman/client.d.ts.map +1 -0
- package/dist/brahman/client.js +3 -0
- package/dist/brahman/client.js.map +1 -0
- package/dist/brahman/helpers.d.ts +51 -0
- package/dist/brahman/helpers.d.ts.map +1 -0
- package/dist/brahman/helpers.js +321 -0
- package/dist/brahman/helpers.js.map +1 -0
- package/dist/brahman/server.d.ts +3 -0
- package/dist/brahman/server.d.ts.map +1 -0
- package/dist/brahman/server.js +3 -0
- package/dist/brahman/server.js.map +1 -0
- package/dist/brahman/service.d.ts +2 -0
- package/dist/brahman/service.d.ts.map +1 -0
- package/dist/brahman/service.js +310 -0
- package/dist/brahman/service.js.map +1 -0
- package/dist/brahman/types.d.ts +335 -0
- package/dist/brahman/types.d.ts.map +1 -0
- package/dist/brahman/types.js +633 -0
- package/dist/brahman/types.js.map +1 -0
- package/dist/brahman/view.d.ts +2 -0
- package/dist/brahman/view.d.ts.map +1 -0
- package/dist/brahman/view.js +47 -0
- package/dist/brahman/view.js.map +1 -0
- package/dist/brahman/views/action-cards.d.ts +60 -0
- package/dist/brahman/views/action-cards.d.ts.map +1 -0
- package/dist/brahman/views/action-cards.js +283 -0
- package/dist/brahman/views/action-cards.js.map +1 -0
- package/dist/brahman/views/bot-view.d.ts +5 -0
- package/dist/brahman/views/bot-view.d.ts.map +1 -0
- package/dist/brahman/views/bot-view.js +14 -0
- package/dist/brahman/views/bot-view.js.map +1 -0
- package/dist/brahman/views/chat-editor.d.ts +5 -0
- package/dist/brahman/views/chat-editor.d.ts.map +1 -0
- package/dist/brahman/views/chat-editor.js +306 -0
- package/dist/brahman/views/chat-editor.js.map +1 -0
- package/dist/brahman/views/chat-preview.d.ts +5 -0
- package/dist/brahman/views/chat-preview.d.ts.map +1 -0
- package/dist/brahman/views/chat-preview.js +145 -0
- package/dist/brahman/views/chat-preview.js.map +1 -0
- package/dist/brahman/views/menu-editor.d.ts +11 -0
- package/dist/brahman/views/menu-editor.d.ts.map +1 -0
- package/dist/brahman/views/menu-editor.js +171 -0
- package/dist/brahman/views/menu-editor.js.map +1 -0
- package/dist/brahman/views/page-layout.d.ts +5 -0
- package/dist/brahman/views/page-layout.d.ts.map +1 -0
- package/dist/brahman/views/page-layout.js +114 -0
- package/dist/brahman/views/page-layout.js.map +1 -0
- package/dist/brahman/views/tstring-input.d.ts +15 -0
- package/dist/brahman/views/tstring-input.d.ts.map +1 -0
- package/dist/brahman/views/tstring-input.js +23 -0
- package/dist/brahman/views/tstring-input.js.map +1 -0
- package/dist/cafe/seed.d.ts +2 -0
- package/dist/cafe/seed.d.ts.map +1 -0
- package/dist/cafe/seed.js +7 -0
- package/dist/cafe/seed.js.map +1 -0
- package/dist/cafe/server.d.ts +3 -0
- package/dist/cafe/server.d.ts.map +1 -0
- package/dist/cafe/server.js +3 -0
- package/dist/cafe/server.js.map +1 -0
- package/dist/cafe/types.d.ts +2 -0
- package/dist/cafe/types.d.ts.map +1 -0
- package/dist/cafe/types.js +31 -0
- package/dist/cafe/types.js.map +1 -0
- package/dist/canary/seed.d.ts +2 -0
- package/dist/canary/seed.d.ts.map +1 -0
- package/dist/canary/seed.js +7 -0
- package/dist/canary/seed.js.map +1 -0
- package/dist/canary/server.d.ts +4 -0
- package/dist/canary/server.d.ts.map +1 -0
- package/dist/canary/server.js +4 -0
- package/dist/canary/server.js.map +1 -0
- package/dist/canary/service.d.ts +2 -0
- package/dist/canary/service.d.ts.map +1 -0
- package/dist/canary/service.js +101 -0
- package/dist/canary/service.js.map +1 -0
- package/dist/canary/types.d.ts +9 -0
- package/dist/canary/types.d.ts.map +1 -0
- package/dist/canary/types.js +13 -0
- package/dist/canary/types.js.map +1 -0
- package/dist/doc/client.d.ts +3 -0
- package/dist/doc/client.d.ts.map +1 -0
- package/dist/doc/client.js +5 -0
- package/dist/doc/client.js.map +1 -0
- package/dist/doc/fs-codec.d.ts +2 -0
- package/dist/doc/fs-codec.d.ts.map +1 -0
- package/dist/doc/fs-codec.js +44 -0
- package/dist/doc/fs-codec.js.map +1 -0
- package/dist/doc/markdown.d.ts +13 -0
- package/dist/doc/markdown.d.ts.map +1 -0
- package/dist/doc/markdown.js +250 -0
- package/dist/doc/markdown.js.map +1 -0
- package/dist/doc/prefab.d.ts +2 -0
- package/dist/doc/prefab.d.ts.map +1 -0
- package/dist/doc/prefab.js +23 -0
- package/dist/doc/prefab.js.map +1 -0
- package/dist/doc/renderers.d.ts +2 -0
- package/dist/doc/renderers.d.ts.map +1 -0
- package/dist/doc/renderers.js +94 -0
- package/dist/doc/renderers.js.map +1 -0
- package/dist/doc/seed.d.ts +2 -0
- package/dist/doc/seed.d.ts.map +1 -0
- package/dist/doc/seed.js +9 -0
- package/dist/doc/seed.js.map +1 -0
- package/dist/doc/server.d.ts +6 -0
- package/dist/doc/server.d.ts.map +1 -0
- package/dist/doc/server.js +6 -0
- package/dist/doc/server.js.map +1 -0
- package/dist/doc/slash-command.d.ts +3 -0
- package/dist/doc/slash-command.d.ts.map +1 -0
- package/dist/doc/slash-command.js +123 -0
- package/dist/doc/slash-command.js.map +1 -0
- package/dist/doc/slash-menu.d.ts +15 -0
- package/dist/doc/slash-menu.d.ts.map +1 -0
- package/dist/doc/slash-menu.js +54 -0
- package/dist/doc/slash-menu.js.map +1 -0
- package/dist/doc/text.d.ts +2 -0
- package/dist/doc/text.d.ts.map +1 -0
- package/dist/doc/text.js +22 -0
- package/dist/doc/text.js.map +1 -0
- package/dist/doc/toolbar.d.ts +5 -0
- package/dist/doc/toolbar.d.ts.map +1 -0
- package/dist/doc/toolbar.js +15 -0
- package/dist/doc/toolbar.js.map +1 -0
- package/dist/doc/treenity-block-view.d.ts +2 -0
- package/dist/doc/treenity-block-view.d.ts.map +1 -0
- package/dist/doc/treenity-block-view.js +37 -0
- package/dist/doc/treenity-block-view.js.map +1 -0
- package/dist/doc/treenity-block.d.ts +3 -0
- package/dist/doc/treenity-block.d.ts.map +1 -0
- package/dist/doc/treenity-block.js +27 -0
- package/dist/doc/treenity-block.js.map +1 -0
- package/dist/doc/types.d.ts +2 -0
- package/dist/doc/types.d.ts.map +1 -0
- package/dist/doc/types.js +10 -0
- package/dist/doc/types.js.map +1 -0
- package/dist/launcher/client.d.ts +5 -0
- package/dist/launcher/client.d.ts.map +1 -0
- package/dist/launcher/client.js +5 -0
- package/dist/launcher/client.js.map +1 -0
- package/dist/launcher/icons.d.ts +2 -0
- package/dist/launcher/icons.d.ts.map +1 -0
- package/dist/launcher/icons.js +57 -0
- package/dist/launcher/icons.js.map +1 -0
- package/dist/launcher/seed.d.ts +2 -0
- package/dist/launcher/seed.d.ts.map +1 -0
- package/dist/launcher/seed.js +36 -0
- package/dist/launcher/seed.js.map +1 -0
- package/dist/launcher/server.d.ts +3 -0
- package/dist/launcher/server.d.ts.map +1 -0
- package/dist/launcher/server.js +3 -0
- package/dist/launcher/server.js.map +1 -0
- package/dist/launcher/types.d.ts +21 -0
- package/dist/launcher/types.d.ts.map +1 -0
- package/dist/launcher/types.js +47 -0
- package/dist/launcher/types.js.map +1 -0
- package/dist/launcher/view.d.ts +2 -0
- package/dist/launcher/view.d.ts.map +1 -0
- package/dist/launcher/view.js +187 -0
- package/dist/launcher/view.js.map +1 -0
- package/dist/launcher/widgets.d.ts +2 -0
- package/dist/launcher/widgets.d.ts.map +1 -0
- package/dist/launcher/widgets.js +73 -0
- package/dist/launcher/widgets.js.map +1 -0
- package/dist/mindmap/branch.d.ts +17 -0
- package/dist/mindmap/branch.d.ts.map +1 -0
- package/dist/mindmap/branch.js +47 -0
- package/dist/mindmap/branch.js.map +1 -0
- package/dist/mindmap/client.d.ts +3 -0
- package/dist/mindmap/client.d.ts.map +1 -0
- package/dist/mindmap/client.js +3 -0
- package/dist/mindmap/client.js.map +1 -0
- package/dist/mindmap/radial-tree.d.ts +14 -0
- package/dist/mindmap/radial-tree.d.ts.map +1 -0
- package/dist/mindmap/radial-tree.js +184 -0
- package/dist/mindmap/radial-tree.js.map +1 -0
- package/dist/mindmap/sidebar.d.ts +8 -0
- package/dist/mindmap/sidebar.d.ts.map +1 -0
- package/dist/mindmap/sidebar.js +24 -0
- package/dist/mindmap/sidebar.js.map +1 -0
- package/dist/mindmap/types.d.ts +8 -0
- package/dist/mindmap/types.d.ts.map +1 -0
- package/dist/mindmap/types.js +10 -0
- package/dist/mindmap/types.js.map +1 -0
- package/dist/mindmap/use-tree-data.d.ts +14 -0
- package/dist/mindmap/use-tree-data.d.ts.map +1 -0
- package/dist/mindmap/use-tree-data.js +95 -0
- package/dist/mindmap/use-tree-data.js.map +1 -0
- package/dist/mindmap/view.d.ts +3 -0
- package/dist/mindmap/view.d.ts.map +1 -0
- package/dist/mindmap/view.js +141 -0
- package/dist/mindmap/view.js.map +1 -0
- package/dist/sensor-demo/client.d.ts +2 -0
- package/dist/sensor-demo/client.d.ts.map +1 -0
- package/dist/sensor-demo/client.js +2 -0
- package/dist/sensor-demo/client.js.map +1 -0
- package/dist/sensor-demo/server.d.ts +2 -0
- package/dist/sensor-demo/server.d.ts.map +1 -0
- package/dist/sensor-demo/server.js +2 -0
- package/dist/sensor-demo/server.js.map +1 -0
- package/dist/sensor-demo/service.d.ts +2 -0
- package/dist/sensor-demo/service.d.ts.map +1 -0
- package/dist/sensor-demo/service.js +27 -0
- package/dist/sensor-demo/service.js.map +1 -0
- package/dist/sensor-demo/types.d.ts +15 -0
- package/dist/sensor-demo/types.d.ts.map +1 -0
- package/dist/sensor-demo/types.js +18 -0
- package/dist/sensor-demo/types.js.map +1 -0
- package/dist/sensor-demo/view.d.ts +2 -0
- package/dist/sensor-demo/view.d.ts.map +1 -0
- package/dist/sensor-demo/view.js +33 -0
- package/dist/sensor-demo/view.js.map +1 -0
- package/dist/sensor-generator/action.d.ts +2 -0
- package/dist/sensor-generator/action.d.ts.map +1 -0
- package/dist/sensor-generator/action.js +23 -0
- package/dist/sensor-generator/action.js.map +1 -0
- package/dist/sensor-generator/client.d.ts +2 -0
- package/dist/sensor-generator/client.d.ts.map +1 -0
- package/dist/sensor-generator/client.js +2 -0
- package/dist/sensor-generator/client.js.map +1 -0
- package/dist/sensor-generator/server.d.ts +2 -0
- package/dist/sensor-generator/server.d.ts.map +1 -0
- package/dist/sensor-generator/server.js +2 -0
- package/dist/sensor-generator/server.js.map +1 -0
- package/dist/sensor-generator/view.d.ts +2 -0
- package/dist/sensor-generator/view.d.ts.map +1 -0
- package/dist/sensor-generator/view.js +64 -0
- package/dist/sensor-generator/view.js.map +1 -0
- package/dist/sim/client.d.ts +2 -0
- package/dist/sim/client.d.ts.map +1 -0
- package/dist/sim/client.js +2 -0
- package/dist/sim/client.js.map +1 -0
- package/dist/sim/seed.d.ts +2 -0
- package/dist/sim/seed.d.ts.map +1 -0
- package/dist/sim/seed.js +50 -0
- package/dist/sim/seed.js.map +1 -0
- package/dist/sim/server.d.ts +3 -0
- package/dist/sim/server.d.ts.map +1 -0
- package/dist/sim/server.js +3 -0
- package/dist/sim/server.js.map +1 -0
- package/dist/sim/service.d.ts +4 -0
- package/dist/sim/service.d.ts.map +1 -0
- package/dist/sim/service.js +528 -0
- package/dist/sim/service.js.map +1 -0
- package/dist/sim/types.d.ts +63 -0
- package/dist/sim/types.d.ts.map +1 -0
- package/dist/sim/types.js +57 -0
- package/dist/sim/types.js.map +1 -0
- package/dist/sim/view.d.ts +2 -0
- package/dist/sim/view.d.ts.map +1 -0
- package/dist/sim/view.js +205 -0
- package/dist/sim/view.js.map +1 -0
- package/dist/table/client.d.ts +4 -0
- package/dist/table/client.d.ts.map +1 -0
- package/dist/table/client.js +4 -0
- package/dist/table/client.js.map +1 -0
- package/dist/table/edit.d.ts +2 -0
- package/dist/table/edit.d.ts.map +1 -0
- package/dist/table/edit.js +115 -0
- package/dist/table/edit.js.map +1 -0
- package/dist/table/server.d.ts +2 -0
- package/dist/table/server.d.ts.map +1 -0
- package/dist/table/server.js +2 -0
- package/dist/table/server.js.map +1 -0
- package/dist/table/types.d.ts +18 -0
- package/dist/table/types.d.ts.map +1 -0
- package/dist/table/types.js +13 -0
- package/dist/table/types.js.map +1 -0
- package/dist/table/use-debounced-sync.d.ts +8 -0
- package/dist/table/use-debounced-sync.d.ts.map +1 -0
- package/dist/table/use-debounced-sync.js +56 -0
- package/dist/table/use-debounced-sync.js.map +1 -0
- package/dist/table/view.d.ts +2 -0
- package/dist/table/view.d.ts.map +1 -0
- package/dist/table/view.js +199 -0
- package/dist/table/view.js.map +1 -0
- package/dist/tasks/server.d.ts +2 -0
- package/dist/tasks/server.d.ts.map +1 -0
- package/dist/tasks/server.js +2 -0
- package/dist/tasks/server.js.map +1 -0
- package/dist/tasks/types.d.ts +22 -0
- package/dist/tasks/types.d.ts.map +1 -0
- package/dist/tasks/types.js +34 -0
- package/dist/tasks/types.js.map +1 -0
- package/dist/three/client.d.ts +2 -0
- package/dist/three/client.d.ts.map +1 -0
- package/dist/three/client.js +2 -0
- package/dist/three/client.js.map +1 -0
- package/dist/three/seed.d.ts +2 -0
- package/dist/three/seed.d.ts.map +1 -0
- package/dist/three/seed.js +45 -0
- package/dist/three/seed.js.map +1 -0
- package/dist/three/server.d.ts +3 -0
- package/dist/three/server.d.ts.map +1 -0
- package/dist/three/server.js +3 -0
- package/dist/three/server.js.map +1 -0
- package/dist/three/types.d.ts +178 -0
- package/dist/three/types.d.ts.map +1 -0
- package/dist/three/types.js +209 -0
- package/dist/three/types.js.map +1 -0
- package/dist/three/view.d.ts +2 -0
- package/dist/three/view.d.ts.map +1 -0
- package/dist/three/view.js +307 -0
- package/dist/three/view.js.map +1 -0
- package/dist/todo/client.d.ts +3 -0
- package/dist/todo/client.d.ts.map +1 -0
- package/dist/todo/client.js +3 -0
- package/dist/todo/client.js.map +1 -0
- package/dist/todo/seed.d.ts +2 -0
- package/dist/todo/seed.d.ts.map +1 -0
- package/dist/todo/seed.js +8 -0
- package/dist/todo/seed.js.map +1 -0
- package/dist/todo/server.d.ts +3 -0
- package/dist/todo/server.d.ts.map +1 -0
- package/dist/todo/server.js +3 -0
- package/dist/todo/server.js.map +1 -0
- package/dist/todo/types.d.ts +15 -0
- package/dist/todo/types.d.ts.map +1 -0
- package/dist/todo/types.js +29 -0
- package/dist/todo/types.js.map +1 -0
- package/dist/todo/view.d.ts +2 -0
- package/dist/todo/view.d.ts.map +1 -0
- package/dist/todo/view.js +27 -0
- package/dist/todo/view.js.map +1 -0
- package/dist/whisper/client.d.ts +2 -0
- package/dist/whisper/client.d.ts.map +1 -0
- package/dist/whisper/client.js +2 -0
- package/dist/whisper/client.js.map +1 -0
- package/dist/whisper/inbox.d.ts +2 -0
- package/dist/whisper/inbox.d.ts.map +1 -0
- package/dist/whisper/inbox.js +50 -0
- package/dist/whisper/inbox.js.map +1 -0
- package/dist/whisper/route.d.ts +10 -0
- package/dist/whisper/route.d.ts.map +1 -0
- package/dist/whisper/route.js +154 -0
- package/dist/whisper/route.js.map +1 -0
- package/dist/whisper/seed.d.ts +2 -0
- package/dist/whisper/seed.d.ts.map +1 -0
- package/dist/whisper/seed.js +14 -0
- package/dist/whisper/seed.js.map +1 -0
- package/dist/whisper/server.d.ts +5 -0
- package/dist/whisper/server.d.ts.map +1 -0
- package/dist/whisper/server.js +5 -0
- package/dist/whisper/server.js.map +1 -0
- package/dist/whisper/service.d.ts +2 -0
- package/dist/whisper/service.d.ts.map +1 -0
- package/dist/whisper/service.js +26 -0
- package/dist/whisper/service.js.map +1 -0
- package/dist/whisper/types.d.ts +38 -0
- package/dist/whisper/types.d.ts.map +1 -0
- package/dist/whisper/types.js +45 -0
- package/dist/whisper/types.js.map +1 -0
- package/dist/whisper/view.d.ts +2 -0
- package/dist/whisper/view.d.ts.map +1 -0
- package/dist/whisper/view.js +40 -0
- package/dist/whisper/view.js.map +1 -0
- package/doc/CLAUDE.md +49 -0
- package/doc/client.ts +5 -0
- package/doc/editor.css +283 -0
- package/doc/fs-codec.test.ts +119 -0
- package/doc/fs-codec.ts +50 -0
- package/doc/markdown.test.ts +152 -0
- package/doc/markdown.ts +284 -0
- package/doc/prefab.ts +26 -0
- package/doc/renderers.tsx +126 -0
- package/doc/seed.ts +10 -0
- package/doc/server.ts +5 -0
- package/doc/slash-command.ts +136 -0
- package/doc/slash-menu.tsx +91 -0
- package/doc/text.ts +20 -0
- package/doc/toolbar.tsx +86 -0
- package/doc/treenity-block-view.tsx +116 -0
- package/doc/treenity-block.ts +31 -0
- package/doc/types.ts +10 -0
- package/launcher/client.ts +4 -0
- package/launcher/icons.tsx +234 -0
- package/launcher/launcher.css +64 -0
- package/launcher/seed.ts +41 -0
- package/launcher/server.ts +2 -0
- package/launcher/types.ts +53 -0
- package/launcher/view.tsx +401 -0
- package/launcher/widgets.tsx +171 -0
- package/mindmap/branch.tsx +163 -0
- package/mindmap/client.ts +2 -0
- package/mindmap/mindmap.css +243 -0
- package/mindmap/sidebar.tsx +127 -0
- package/mindmap/types.ts +10 -0
- package/mindmap/view.tsx +247 -0
- package/package.json +75 -0
- package/sensor-demo/CLAUDE.md +3 -0
- package/sensor-demo/client.ts +1 -0
- package/sensor-demo/server.ts +1 -0
- package/sensor-demo/service.ts +27 -0
- package/sensor-demo/types.ts +20 -0
- package/sensor-demo/view.tsx +64 -0
- package/sensor-generator/CLAUDE.md +3 -0
- package/sensor-generator/action.ts +28 -0
- package/sensor-generator/client.ts +1 -0
- package/sensor-generator/server.ts +1 -0
- package/sensor-generator/view.tsx +107 -0
- package/sim/CLAUDE.md +16 -0
- package/sim/client.ts +1 -0
- package/sim/seed.ts +55 -0
- package/sim/server.ts +2 -0
- package/sim/service.ts +594 -0
- package/sim/sim.test.ts +282 -0
- package/sim/types.ts +87 -0
- package/sim/view.tsx +446 -0
- package/table/client.ts +3 -0
- package/table/edit.tsx +241 -0
- package/table/server.ts +1 -0
- package/table/types.ts +22 -0
- package/table/use-debounced-sync.ts +67 -0
- package/table/view.tsx +339 -0
- package/tasks/CLAUDE.md +6 -0
- package/tasks/server.ts +1 -0
- package/tasks/types.ts +36 -0
- package/three/CLAUDE.md +6 -0
- package/three/client.ts +1 -0
- package/three/seed.ts +54 -0
- package/three/server.ts +2 -0
- package/three/types.ts +238 -0
- package/three/view.tsx +453 -0
- package/todo/client.ts +2 -0
- package/todo/seed.ts +9 -0
- package/todo/server.ts +2 -0
- package/todo/types.ts +32 -0
- package/todo/view.tsx +67 -0
- package/whisper/CLAUDE.md +16 -0
- package/whisper/client.ts +1 -0
- package/whisper/inbox.ts +54 -0
- package/whisper/route.ts +182 -0
- package/whisper/seed.ts +15 -0
- package/whisper/server.ts +4 -0
- package/whisper/service.ts +29 -0
- package/whisper/types.ts +51 -0
- package/whisper/view.tsx +81 -0
package/todo/seed.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type NodeData } from '@treenity/core';
|
|
2
|
+
import { registerPrefab } from '@treenity/core/mod';
|
|
3
|
+
|
|
4
|
+
registerPrefab('todo', 'seed', [
|
|
5
|
+
{ $path: 'todo', $type: 'dir' },
|
|
6
|
+
{ $path: 'todo/list', $type: 'todo.list', title: 'My Todos' },
|
|
7
|
+
{ $path: 'todo/list/1', $type: 'todo.item', title: 'Read the quickstart', done: true },
|
|
8
|
+
{ $path: 'todo/list/2', $type: 'todo.item', title: 'Build something', done: false },
|
|
9
|
+
] as NodeData[]);
|
package/todo/server.ts
ADDED
package/todo/types.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getCtx, registerType } from '@treenity/core/comp';
|
|
2
|
+
|
|
3
|
+
class TodoItem {
|
|
4
|
+
title = '';
|
|
5
|
+
done = false;
|
|
6
|
+
|
|
7
|
+
/** @description Toggle done state */
|
|
8
|
+
toggle() {
|
|
9
|
+
this.done = !this.done;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class TodoList {
|
|
14
|
+
title = 'My Todos';
|
|
15
|
+
|
|
16
|
+
/** @description Add a new todo item */
|
|
17
|
+
async add(data: { title: string }) {
|
|
18
|
+
if (!data.title?.trim()) throw new Error('Title required');
|
|
19
|
+
const { node, store } = getCtx();
|
|
20
|
+
const id = Date.now().toString(36);
|
|
21
|
+
await store.set({
|
|
22
|
+
$path: `${node.$path}/${id}`,
|
|
23
|
+
$type: 'todo.item',
|
|
24
|
+
title: data.title.trim(),
|
|
25
|
+
done: false,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
registerType('todo.item', TodoItem);
|
|
31
|
+
registerType('todo.list', TodoList);
|
|
32
|
+
export { TodoItem, TodoList };
|
package/todo/view.tsx
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type NodeData, register } from '@treenity/core';
|
|
2
|
+
import { useChildren, usePath } from '@treenity/react/hooks';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { TodoItem, TodoList } from './types';
|
|
5
|
+
|
|
6
|
+
function TodoListView({ value }: { value: NodeData }) {
|
|
7
|
+
const list = usePath(value.$path, TodoList);
|
|
8
|
+
const children = useChildren(value.$path, { watch: true, watchNew: true });
|
|
9
|
+
const [draft, setDraft] = useState('');
|
|
10
|
+
|
|
11
|
+
const handleAdd = async () => {
|
|
12
|
+
if (!draft.trim()) return;
|
|
13
|
+
await list.add({ title: draft });
|
|
14
|
+
setDraft('');
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="max-w-md mx-auto p-4">
|
|
19
|
+
<h2 className="text-xl font-bold mb-4">{list.title}</h2>
|
|
20
|
+
|
|
21
|
+
<div className="flex gap-2 mb-4">
|
|
22
|
+
<input
|
|
23
|
+
className="flex-1 border rounded px-3 py-1.5 text-sm"
|
|
24
|
+
placeholder="What needs to be done?"
|
|
25
|
+
value={draft}
|
|
26
|
+
onChange={e => setDraft(e.target.value)}
|
|
27
|
+
onKeyDown={e => e.key === 'Enter' && handleAdd()}
|
|
28
|
+
/>
|
|
29
|
+
<button
|
|
30
|
+
className="bg-blue-600 text-white px-3 py-1.5 rounded text-sm"
|
|
31
|
+
onClick={handleAdd}
|
|
32
|
+
>Add</button>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<ul className="space-y-1">
|
|
36
|
+
{children.map(child => (
|
|
37
|
+
<TodoItemRow key={child.$path} value={child} />
|
|
38
|
+
))}
|
|
39
|
+
</ul>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function TodoItemRow({ value }: { value: NodeData }) {
|
|
45
|
+
const item = usePath(value.$path, TodoItem);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<li
|
|
49
|
+
className="flex items-center gap-2 px-3 py-2 rounded
|
|
50
|
+
hover:bg-neutral-100 cursor-pointer"
|
|
51
|
+
onClick={() => item.toggle()}
|
|
52
|
+
>
|
|
53
|
+
<span className={`w-4 h-4 rounded border flex items-center
|
|
54
|
+
justify-center text-xs ${item.done
|
|
55
|
+
? 'bg-blue-600 border-blue-600 text-white'
|
|
56
|
+
: 'border-neutral-300'}`}>
|
|
57
|
+
{item.done ? '✓' : ''}
|
|
58
|
+
</span>
|
|
59
|
+
<span className={item.done ? 'line-through text-neutral-400' : ''}>
|
|
60
|
+
{item.title}
|
|
61
|
+
</span>
|
|
62
|
+
</li>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
register('todo.list', 'react', TodoListView as any);
|
|
67
|
+
register('todo.item', 'react', TodoItemRow as any);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## mods/whisper
|
|
2
|
+
Audio transcription via @huggingface/transformers (Whisper). HTTP POST → node in tree immediately, transcribe in background.
|
|
3
|
+
|
|
4
|
+
### Файлы
|
|
5
|
+
- route.ts — createWhisperHandler(cfg): POST audio → create node with status '...', respond 200, transcribe async via ffmpeg+transformers.js, update node
|
|
6
|
+
- types.ts — WhisperAudio, WhisperText, WhisperMeta, WhisperChecklist component classes
|
|
7
|
+
- service.ts — service registration
|
|
8
|
+
- server.ts — mounts HTTP handler, tRPC routes
|
|
9
|
+
- client.ts — frontend API
|
|
10
|
+
- view.tsx — React UI
|
|
11
|
+
|
|
12
|
+
### Конвенции
|
|
13
|
+
- Pipeline: POST → ffmpeg → 16kHz mono WAV → Float32Array → Whisper pipeline
|
|
14
|
+
- Node appears in tree immediately (status '...'), updated when transcription finishes
|
|
15
|
+
- Models lazy-loaded and cached: `onnx-community/whisper-{model}` or full HF model id
|
|
16
|
+
- timeId() = YYYYMMDD-HHmmss-SSS for sortable node names
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './view';
|
package/whisper/inbox.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Whisper → Agent bridge service
|
|
2
|
+
// Watches a whisper channel for completed transcriptions, creates agent tasks
|
|
3
|
+
|
|
4
|
+
import { createNode, getComponent, type NodeData, register } from '@treenity/core';
|
|
5
|
+
import { WhisperInbox, WhisperText } from './types';
|
|
6
|
+
|
|
7
|
+
const log = (msg: string) => console.log(`[whisper.inbox] ${msg}`);
|
|
8
|
+
|
|
9
|
+
function getText(node: NodeData): string | undefined {
|
|
10
|
+
const comp = getComponent(node, WhisperText);
|
|
11
|
+
if (!comp) return undefined;
|
|
12
|
+
return comp.content && comp.content !== '...' ? comp.content : undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
register('whisper.inbox', 'service', async (node, ctx) => {
|
|
16
|
+
const config = getComponent(node, WhisperInbox);
|
|
17
|
+
if (!config) throw new Error(`missing config on ${node.$path}`);
|
|
18
|
+
|
|
19
|
+
const sent = new Set<string>();
|
|
20
|
+
|
|
21
|
+
// Mark existing COMPLETED transcriptions as already processed
|
|
22
|
+
const { items } = await ctx.store.getChildren(config.source);
|
|
23
|
+
for (const child of items) {
|
|
24
|
+
if (child.$type !== 'whisper.transcription') continue;
|
|
25
|
+
if (getText(child)) sent.add(child.$path);
|
|
26
|
+
}
|
|
27
|
+
log(`watching ${config.source} → ${config.target} (${sent.size} existing)`);
|
|
28
|
+
|
|
29
|
+
const unsub = ctx.subscribe(config.source, (event) => {
|
|
30
|
+
if (event.type !== 'set' && event.type !== 'patch') return;
|
|
31
|
+
|
|
32
|
+
ctx.store.get(event.path).then(async (n) => {
|
|
33
|
+
if (!n || n.$type !== 'whisper.transcription') return;
|
|
34
|
+
if (sent.has(n.$path)) return;
|
|
35
|
+
|
|
36
|
+
const text = getText(n);
|
|
37
|
+
if (!text) return;
|
|
38
|
+
|
|
39
|
+
sent.add(n.$path);
|
|
40
|
+
|
|
41
|
+
const taskId = `t-${Date.now()}`;
|
|
42
|
+
const taskPath = `${config.target}/tasks/${taskId}`;
|
|
43
|
+
await ctx.store.set(createNode(taskPath, 'agent.task', {
|
|
44
|
+
prompt: text,
|
|
45
|
+
status: 'pending',
|
|
46
|
+
createdAt: Date.now(),
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
log(`${n.$path} → ${taskPath}`);
|
|
50
|
+
}).catch(err => console.error(`[whisper.inbox] error processing ${event.path}:`, err));
|
|
51
|
+
}, { children: true });
|
|
52
|
+
|
|
53
|
+
return { stop: async () => unsub() };
|
|
54
|
+
});
|
package/whisper/route.ts
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Whisper audio transcription — HTTP handler
|
|
2
|
+
// POST ?id=channel-id with audio body → create node immediately, transcribe in background, update node
|
|
3
|
+
|
|
4
|
+
import { type AutomaticSpeechRecognitionPipeline, pipeline } from '@huggingface/transformers';
|
|
5
|
+
import { createNode } from '@treenity/core';
|
|
6
|
+
import { newComp } from '@treenity/core/comp';
|
|
7
|
+
import type { Tree } from '@treenity/core/tree';
|
|
8
|
+
import { execFile } from 'node:child_process';
|
|
9
|
+
import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { type IncomingMessage, type ServerResponse } from 'node:http';
|
|
11
|
+
import { join, resolve } from 'node:path';
|
|
12
|
+
import { promisify } from 'node:util';
|
|
13
|
+
import WaveFile from 'wavefile';
|
|
14
|
+
import { WhisperAudio, WhisperChecklist, WhisperMeta, WhisperText } from './types';
|
|
15
|
+
|
|
16
|
+
const execFileAsync = promisify(execFile);
|
|
17
|
+
|
|
18
|
+
// Lazy-initialized pipelines keyed by model name
|
|
19
|
+
const pipelines = new Map<string, Promise<AutomaticSpeechRecognitionPipeline>>();
|
|
20
|
+
|
|
21
|
+
function getTranscriber(model: string): Promise<AutomaticSpeechRecognitionPipeline> {
|
|
22
|
+
let p = pipelines.get(model);
|
|
23
|
+
if (!p) {
|
|
24
|
+
const modelId = model.includes('/') ? model : `onnx-community/whisper-${model}`;
|
|
25
|
+
console.log(`[whisper] loading model ${modelId}...`);
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- pipeline() union type is too complex for TS
|
|
27
|
+
p = (pipeline as any)('automatic-speech-recognition', modelId, {
|
|
28
|
+
dtype: {
|
|
29
|
+
encoder_model: 'fp32',
|
|
30
|
+
decoder_model_merged: 'q4',
|
|
31
|
+
},
|
|
32
|
+
}) as Promise<AutomaticSpeechRecognitionPipeline>;
|
|
33
|
+
p.then(() => console.log(`[whisper] model ${modelId} ready`))
|
|
34
|
+
.catch((e: unknown) => {
|
|
35
|
+
console.error(`[whisper] model ${modelId} failed:`, e);
|
|
36
|
+
pipelines.delete(model); // allow retry on next request
|
|
37
|
+
});
|
|
38
|
+
pipelines.set(model, p);
|
|
39
|
+
}
|
|
40
|
+
return p;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function respond(res: ServerResponse, status: number, body: unknown) {
|
|
44
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
45
|
+
res.end(JSON.stringify(body));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Sortable time-based id: YYYYMMDD-HHmmss-SSS
|
|
49
|
+
function timeId(): string {
|
|
50
|
+
const d = new Date();
|
|
51
|
+
const pad = (n: number, w = 2) => String(n).padStart(w, '0');
|
|
52
|
+
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}-${pad(d.getMilliseconds(), 3)}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Read 16kHz mono WAV file → Float32Array for transformers.js */
|
|
56
|
+
async function readWavAsFloat32(wavPath: string): Promise<Float32Array> {
|
|
57
|
+
const buf = await readFile(wavPath);
|
|
58
|
+
const wav = new WaveFile.WaveFile(buf);
|
|
59
|
+
wav.toBitDepth('32f');
|
|
60
|
+
wav.toSampleRate(16000);
|
|
61
|
+
let samples = wav.getSamples();
|
|
62
|
+
if (Array.isArray(samples)) samples = samples[0];
|
|
63
|
+
return new Float32Array(samples);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type WhisperRouteConfig = {
|
|
67
|
+
nodePath: string;
|
|
68
|
+
model: string;
|
|
69
|
+
language: string;
|
|
70
|
+
audioDir: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export function createWhisperHandler(cfg: WhisperRouteConfig) {
|
|
74
|
+
const audioDir = resolve(cfg.audioDir);
|
|
75
|
+
|
|
76
|
+
// Pre-warm the pipeline
|
|
77
|
+
getTranscriber(cfg.model);
|
|
78
|
+
|
|
79
|
+
return async (req: IncomingMessage, res: ServerResponse, store: Tree) => {
|
|
80
|
+
if (req.method !== 'POST') {
|
|
81
|
+
return respond(res, 405, { error: 'Method not allowed' });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
85
|
+
const id = url.searchParams.get('id');
|
|
86
|
+
if (!id) {
|
|
87
|
+
return respond(res, 400, { error: 'Missing ?id= query parameter' });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const chunks: Buffer[] = [];
|
|
91
|
+
for await (const chunk of req) chunks.push(chunk as Buffer);
|
|
92
|
+
const body = Buffer.concat(chunks);
|
|
93
|
+
|
|
94
|
+
if (body.length === 0) {
|
|
95
|
+
return respond(res, 400, { error: 'Empty request body' });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const mime = req.headers['content-type'] || 'audio/wav';
|
|
99
|
+
const ext = mime.includes('mp3') ? 'mp3'
|
|
100
|
+
: mime.includes('ogg') ? 'ogg'
|
|
101
|
+
: mime.includes('webm') ? 'webm'
|
|
102
|
+
: mime.includes('m4a') ? 'm4a'
|
|
103
|
+
: 'wav';
|
|
104
|
+
|
|
105
|
+
const noteId = timeId();
|
|
106
|
+
const filename = `${id}-${noteId}.${ext}`;
|
|
107
|
+
const filePath = join(audioDir, filename);
|
|
108
|
+
const wavPath = join(audioDir, `${id}-${noteId}_16k.wav`);
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await mkdir(audioDir, { recursive: true });
|
|
112
|
+
await writeFile(filePath, body);
|
|
113
|
+
|
|
114
|
+
// Ensure {servicePath}/{id} dir exists
|
|
115
|
+
const idDirPath = `${cfg.nodePath}/${id}`;
|
|
116
|
+
if (!(await store.get(idDirPath))) {
|
|
117
|
+
await store.set(createNode(idDirPath, 'whisper.channel', {}, {
|
|
118
|
+
checklist: newComp(WhisperChecklist, {}),
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 1. Create node immediately with audio — appears in tree right away
|
|
123
|
+
const nodePath = `${idDirPath}/${noteId}`;
|
|
124
|
+
const node = createNode(nodePath, 'whisper.transcription', {}, {
|
|
125
|
+
audio: newComp(WhisperAudio, { filename, size: body.length, mime }),
|
|
126
|
+
text: newComp(WhisperText, { content: '...' }),
|
|
127
|
+
meta: newComp(WhisperMeta, {
|
|
128
|
+
model: cfg.model,
|
|
129
|
+
language: cfg.language,
|
|
130
|
+
duration: 0,
|
|
131
|
+
segments: 0,
|
|
132
|
+
transcribedAt: Date.now(),
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
await store.set(node);
|
|
136
|
+
console.log(`[whisper] ${filename} → ${nodePath} (processing...)`);
|
|
137
|
+
|
|
138
|
+
// 2. Respond immediately — client sees the node path
|
|
139
|
+
respond(res, 200, { path: nodePath, status: 'processing' });
|
|
140
|
+
|
|
141
|
+
// 3. Transcribe in background, update node when done
|
|
142
|
+
execFileAsync('ffmpeg', ['-i', filePath, '-ar', '16000', '-ac', '1', '-f', 'wav', '-y', wavPath])
|
|
143
|
+
.then(() => readWavAsFloat32(wavPath))
|
|
144
|
+
.then(async (audioData) => {
|
|
145
|
+
const transcriber = await getTranscriber(cfg.model);
|
|
146
|
+
const result = await transcriber(audioData, {
|
|
147
|
+
language: cfg.language,
|
|
148
|
+
return_timestamps: true,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const output = Array.isArray(result) ? result[0] : result;
|
|
152
|
+
const text = (output.text ?? '').trim();
|
|
153
|
+
const outputChunks = (output as any).chunks as Array<{ text: string; timestamp: [number, number | null] }> | undefined;
|
|
154
|
+
|
|
155
|
+
const duration = outputChunks?.length
|
|
156
|
+
? (outputChunks[outputChunks.length - 1].timestamp[1] ?? 0)
|
|
157
|
+
: 0;
|
|
158
|
+
|
|
159
|
+
const updated = await store.get(nodePath);
|
|
160
|
+
if (!updated) return;
|
|
161
|
+
updated.text = newComp(WhisperText, { content: text });
|
|
162
|
+
updated.meta = newComp(WhisperMeta, {
|
|
163
|
+
model: cfg.model,
|
|
164
|
+
language: cfg.language,
|
|
165
|
+
duration,
|
|
166
|
+
segments: outputChunks?.length ?? 0,
|
|
167
|
+
transcribedAt: Date.now(),
|
|
168
|
+
});
|
|
169
|
+
await store.set(updated);
|
|
170
|
+
console.log(`[whisper] ${nodePath} done (${outputChunks?.length ?? 0} seg, ${duration}s)`);
|
|
171
|
+
})
|
|
172
|
+
.catch((err) => console.error(`[whisper] ${nodePath} transcription failed:`, err))
|
|
173
|
+
.finally(() => {
|
|
174
|
+
unlink(wavPath).catch(() => {});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.error('[whisper] transcription error:', err);
|
|
179
|
+
respond(res, 500, { error: String(err) });
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
package/whisper/seed.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type NodeData } from '@treenity/core';
|
|
2
|
+
import { registerPrefab } from '@treenity/core/mod';
|
|
3
|
+
|
|
4
|
+
registerPrefab('whisper', 'seed', [
|
|
5
|
+
{ $path: 'whisper', $type: 'whisper.service',
|
|
6
|
+
config: {
|
|
7
|
+
$type: 'whisper.config', model: 'small', language: 'ru',
|
|
8
|
+
audioDir: './data/audio', url: '/api/notice/audio',
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
{ $path: '/sys/autostart/whisper', $type: 'ref', $ref: '/whisper' },
|
|
12
|
+
{ $path: 'whisper/inbox', $type: 'whisper.inbox',
|
|
13
|
+
source: '/whisper/default', target: '/agent' },
|
|
14
|
+
{ $path: '/sys/autostart/whisper-inbox', $type: 'ref', $ref: '/whisper/inbox' },
|
|
15
|
+
] as NodeData[]);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Whisper service — autostart-compatible, registers HTTP route dynamically
|
|
2
|
+
|
|
3
|
+
import { getComponent, register } from '@treenity/core';
|
|
4
|
+
import { routeRegistry } from '@treenity/core/server/server';
|
|
5
|
+
import { createWhisperHandler } from './route';
|
|
6
|
+
import { WhisperConfig } from './types';
|
|
7
|
+
|
|
8
|
+
register('whisper.service', 'service', async (node, _ctx) => {
|
|
9
|
+
const config = getComponent(node, WhisperConfig);
|
|
10
|
+
if (!config) throw new Error(`[whisper] missing config on ${node.$path}`);
|
|
11
|
+
|
|
12
|
+
const routePath = config.url || node.$path;
|
|
13
|
+
const handler = createWhisperHandler({
|
|
14
|
+
nodePath: node.$path,
|
|
15
|
+
model: config.model,
|
|
16
|
+
language: config.language,
|
|
17
|
+
audioDir: config.audioDir,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
routeRegistry.set(routePath, handler);
|
|
21
|
+
console.log(`[whisper] route ${routePath} (model: ${config.model})`);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
stop: async () => {
|
|
25
|
+
routeRegistry.delete(routePath);
|
|
26
|
+
console.log(`[whisper] unregistered ${routePath}`);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
});
|
package/whisper/types.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { registerType } from '@treenity/core/comp';
|
|
2
|
+
|
|
3
|
+
/** Speech-to-text config — Whisper model, language, audio path */
|
|
4
|
+
export class WhisperConfig {
|
|
5
|
+
model = 'small';
|
|
6
|
+
language = 'ru';
|
|
7
|
+
audioDir = './data/audio';
|
|
8
|
+
url = ''; // override route path; empty = use node's $path
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Audio file metadata — filename, size, MIME type */
|
|
12
|
+
export class WhisperAudio {
|
|
13
|
+
filename = '';
|
|
14
|
+
size = 0;
|
|
15
|
+
mime = 'audio/wav';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Transcription result — recognized text content */
|
|
19
|
+
export class WhisperText {
|
|
20
|
+
/** @format textarea */
|
|
21
|
+
content = '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Transcription metadata — model, language, duration, segments */
|
|
25
|
+
export class WhisperMeta {
|
|
26
|
+
model = '';
|
|
27
|
+
language = '';
|
|
28
|
+
duration = 0;
|
|
29
|
+
segments = 0;
|
|
30
|
+
transcribedAt = 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Meeting checklist — action items from transcription */
|
|
34
|
+
export class WhisperChecklist {
|
|
35
|
+
checked: string[] = [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Bridge: auto-send whisper transcriptions to a task inbox */
|
|
39
|
+
export class WhisperInbox {
|
|
40
|
+
/** @format path @description Whisper channel to watch, e.g. /whisper/kriz */
|
|
41
|
+
source = '';
|
|
42
|
+
/** @format path @description Target inbox, e.g. /agent */
|
|
43
|
+
target = '';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
registerType('whisper.config', WhisperConfig);
|
|
47
|
+
registerType('whisper.audio', WhisperAudio);
|
|
48
|
+
registerType('whisper.text', WhisperText);
|
|
49
|
+
registerType('whisper.meta', WhisperMeta);
|
|
50
|
+
registerType('whisper.checklist', WhisperChecklist);
|
|
51
|
+
registerType('whisper.inbox', WhisperInbox);
|
package/whisper/view.tsx
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Whisper channel view — checklist of transcribed audio notes
|
|
2
|
+
|
|
3
|
+
import { type NodeData, register } from '@treenity/core';
|
|
4
|
+
import { set, useChildren, usePath } from '@treenity/react/hooks';
|
|
5
|
+
|
|
6
|
+
function ChannelView({ value }: { value: NodeData }) {
|
|
7
|
+
const children = useChildren(value.$path, { watchNew: true });
|
|
8
|
+
const node = usePath(value.$path);
|
|
9
|
+
const checklist = node?.checklist as { $type: string; checked?: string[] } | undefined;
|
|
10
|
+
|
|
11
|
+
const checked = new Set<string>(checklist?.checked ?? []);
|
|
12
|
+
|
|
13
|
+
const items = children.filter(c => {
|
|
14
|
+
const text = c.text as { $type: string; content: string } | undefined;
|
|
15
|
+
return text?.$type === 'whisper.text' && text.content && text.content !== '...';
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const processing = children.filter(c => {
|
|
19
|
+
const text = c.text as { $type: string; content: string } | undefined;
|
|
20
|
+
return text?.$type === 'whisper.text' && text.content === '...';
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const toggle = (path: string) => {
|
|
24
|
+
if (!node) return;
|
|
25
|
+
const next = new Set(checked);
|
|
26
|
+
if (next.has(path)) next.delete(path);
|
|
27
|
+
else next.add(path);
|
|
28
|
+
set({ ...node, checklist: { $type: 'whisper.checklist', checked: [...next] } });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="node-default-view">
|
|
33
|
+
{items.map(child => {
|
|
34
|
+
const text = (child.text as any).content as string;
|
|
35
|
+
const name = child.$path.slice(child.$path.lastIndexOf('/') + 1);
|
|
36
|
+
const meta = child.meta as { duration?: number } | undefined;
|
|
37
|
+
const done = checked.has(child.$path);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<label
|
|
41
|
+
key={child.$path}
|
|
42
|
+
className={`flex gap-2.5 px-3 py-2.5 cursor-pointer border-b border-[var(--border)] ${done ? 'opacity-50' : ''}`}
|
|
43
|
+
>
|
|
44
|
+
<input
|
|
45
|
+
type="checkbox"
|
|
46
|
+
checked={done}
|
|
47
|
+
onChange={() => toggle(child.$path)}
|
|
48
|
+
className="mt-0.5 shrink-0 w-4 h-4 p-0"
|
|
49
|
+
/>
|
|
50
|
+
<div className="flex-1">
|
|
51
|
+
<div className={`text-[13px] leading-snug ${done ? 'line-through' : ''}`}>
|
|
52
|
+
{text}
|
|
53
|
+
</div>
|
|
54
|
+
<div className="text-[11px] text-[var(--text-3)] mt-1">
|
|
55
|
+
{name}{meta?.duration ? ` · ${meta.duration}s` : ''}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</label>
|
|
59
|
+
);
|
|
60
|
+
})}
|
|
61
|
+
|
|
62
|
+
{processing.map(child => {
|
|
63
|
+
const name = child.$path.slice(child.$path.lastIndexOf('/') + 1);
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
key={child.$path}
|
|
67
|
+
className="px-3 py-2.5 border-b border-[var(--border)] text-[var(--text-3)] text-[13px] italic"
|
|
68
|
+
>
|
|
69
|
+
{name} — transcribing...
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
})}
|
|
73
|
+
|
|
74
|
+
{items.length === 0 && processing.length === 0 && (
|
|
75
|
+
<div className="node-empty">No transcriptions yet</div>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
register('whisper.channel', 'react', ChannelView as any);
|