@lattice-php/lattice 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/action/components/action-form.d.ts +2 -2
- package/dist/action/components/action-form.js +35 -67
- package/dist/action/components/action-form.js.map +1 -1
- package/dist/action/components/action-group.js +18 -6
- package/dist/action/components/action-group.js.map +1 -1
- package/dist/action/components/action-menu-context.d.ts +6 -0
- package/dist/action/components/action-menu-context.js +18 -0
- package/dist/action/components/action-menu-context.js.map +1 -0
- package/dist/action/components/action.js +20 -13
- package/dist/action/components/action.js.map +1 -1
- package/dist/action/index.js.map +1 -1
- package/dist/appearance/index.js +7 -2
- package/dist/appearance/index.js.map +1 -1
- package/dist/chat/components/chat-box.d.ts +3 -0
- package/dist/chat/components/chat-box.js +53 -0
- package/dist/chat/components/chat-box.js.map +1 -0
- package/dist/chat/components/message-list.d.ts +5 -0
- package/dist/chat/components/message-list.js +22 -0
- package/dist/chat/components/message-list.js.map +1 -0
- package/dist/chat/components/message.d.ts +5 -0
- package/dist/chat/components/message.js +20 -0
- package/dist/chat/components/message.js.map +1 -0
- package/dist/chat/components/prompt-input.d.ts +8 -0
- package/dist/chat/components/prompt-input.js +49 -0
- package/dist/chat/components/prompt-input.js.map +1 -0
- package/dist/chat/index.d.ts +1 -0
- package/dist/chat/index.js +15 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/parts/text.d.ts +3 -0
- package/dist/chat/parts/text.js +12 -0
- package/dist/chat/parts/text.js.map +1 -0
- package/dist/chat/parts/tool-call.d.ts +3 -0
- package/dist/chat/parts/tool-call.js +20 -0
- package/dist/chat/parts/tool-call.js.map +1 -0
- package/dist/chat/transport.d.ts +4 -0
- package/dist/chat/transport.js +67 -0
- package/dist/chat/transport.js.map +1 -0
- package/dist/chat/types.d.ts +39 -0
- package/dist/chat/use-chat.d.ts +15 -0
- package/dist/chat/use-chat.js +161 -0
- package/dist/chat/use-chat.js.map +1 -0
- package/dist/core/api.d.ts +25 -0
- package/dist/core/api.js +127 -0
- package/dist/core/api.js.map +1 -0
- package/dist/core/collapsed-context.d.ts +2 -0
- package/dist/core/collapsed-context.js +10 -0
- package/dist/core/collapsed-context.js.map +1 -0
- package/dist/core/column-sizing.d.ts +17 -0
- package/dist/core/column-sizing.js +61 -0
- package/dist/core/column-sizing.js.map +1 -0
- package/dist/core/components/badge.js +3 -3
- package/dist/core/components/badge.js.map +1 -1
- package/dist/core/components/button.d.ts +1 -1
- package/dist/core/components/button.js +19 -15
- package/dist/core/components/button.js.map +1 -1
- package/dist/core/components/card.js +1 -1
- package/dist/core/components/card.js.map +1 -1
- package/dist/core/components/chart.d.ts +3 -0
- package/dist/core/components/chart.js +175 -0
- package/dist/core/components/chart.js.map +1 -0
- package/dist/{form/components/base → core/components}/checkbox.js +4 -4
- package/dist/core/components/checkbox.js.map +1 -0
- package/dist/core/components/combobox.d.ts +33 -0
- package/dist/core/components/combobox.js +99 -0
- package/dist/core/components/combobox.js.map +1 -0
- package/dist/core/components/confirm-dialog.js +6 -12
- package/dist/core/components/confirm-dialog.js.map +1 -1
- package/dist/core/components/control.d.ts +13 -0
- package/dist/core/components/control.js +27 -0
- package/dist/core/components/control.js.map +1 -0
- package/dist/core/components/dialog.d.ts +19 -0
- package/dist/core/components/dialog.js +76 -0
- package/dist/core/components/dialog.js.map +1 -0
- package/dist/core/components/dropdown-menu.d.ts +14 -0
- package/dist/core/components/dropdown-menu.js +42 -0
- package/dist/core/components/dropdown-menu.js.map +1 -0
- package/dist/core/components/floating-panel.d.ts +3 -0
- package/dist/core/components/floating-panel.js +65 -0
- package/dist/core/components/floating-panel.js.map +1 -0
- package/dist/core/components/fragment.js +25 -13
- package/dist/core/components/fragment.js.map +1 -1
- package/dist/core/components/grid.js.map +1 -1
- package/dist/core/components/heading.js +1 -1
- package/dist/core/components/heading.js.map +1 -1
- package/dist/core/components/icon.js +1 -0
- package/dist/core/components/icon.js.map +1 -1
- package/dist/core/components/index.js +8 -1
- package/dist/core/components/index.js.map +1 -1
- package/dist/core/components/info-tooltip.d.ts +3 -0
- package/dist/core/components/info-tooltip.js +26 -0
- package/dist/core/components/info-tooltip.js.map +1 -0
- package/dist/core/components/link.js +12 -6
- package/dist/core/components/link.js.map +1 -1
- package/dist/core/components/modal.js +9 -31
- package/dist/core/components/modal.js.map +1 -1
- package/dist/core/components/popover.d.ts +8 -0
- package/dist/core/components/popover.js +37 -0
- package/dist/core/components/popover.js.map +1 -0
- package/dist/core/components/raw-block.d.ts +3 -0
- package/dist/core/components/raw-block.js +10 -0
- package/dist/core/components/raw-block.js.map +1 -0
- package/dist/core/components/section.js +8 -12
- package/dist/core/components/section.js.map +1 -1
- package/dist/core/components/segmented-control.js.map +1 -1
- package/dist/core/components/segmented-pills.js +1 -1
- package/dist/core/components/segmented-pills.js.map +1 -1
- package/dist/core/components/skeleton.d.ts +3 -0
- package/dist/core/components/skeleton.js +16 -0
- package/dist/core/components/skeleton.js.map +1 -0
- package/dist/core/components/spinner.js.map +1 -1
- package/dist/core/components/stack.js +25 -8
- package/dist/core/components/stack.js.map +1 -1
- package/dist/core/components/tabs.js +1 -1
- package/dist/core/components/tabs.js.map +1 -1
- package/dist/core/components/text.js +17 -1
- package/dist/core/components/text.js.map +1 -1
- package/dist/core/headers.d.ts +1 -0
- package/dist/core/headers.js +14 -0
- package/dist/core/headers.js.map +1 -0
- package/dist/core/registry-context.d.ts +21 -0
- package/dist/core/registry-context.js +40 -0
- package/dist/core/registry-context.js.map +1 -0
- package/dist/core/registry.d.ts +4 -1
- package/dist/core/registry.js +10 -1
- package/dist/core/registry.js.map +1 -1
- package/dist/core/renderer.d.ts +8 -14
- package/dist/core/renderer.js +17 -27
- package/dist/core/renderer.js.map +1 -1
- package/dist/core/test-id.d.ts +8 -0
- package/dist/core/test-id.js +21 -0
- package/dist/core/test-id.js.map +1 -0
- package/dist/core/types.d.ts +25 -9
- package/dist/core/use-column-resizing.d.ts +27 -0
- package/dist/core/use-column-resizing.js +228 -0
- package/dist/core/use-column-resizing.js.map +1 -0
- package/dist/effects/dispatch.d.ts +20 -0
- package/dist/effects/dispatch.js +39 -0
- package/dist/effects/dispatch.js.map +1 -0
- package/dist/effects/registry.d.ts +38 -0
- package/dist/effects/registry.js +51 -0
- package/dist/effects/registry.js.map +1 -0
- package/dist/effects/use-effect-dispatcher.d.ts +8 -0
- package/dist/effects/use-effect-dispatcher.js +20 -0
- package/dist/effects/use-effect-dispatcher.js.map +1 -0
- package/dist/effects/use-flash-effects.d.ts +5 -0
- package/dist/effects/use-flash-effects.js +22 -0
- package/dist/effects/use-flash-effects.js.map +1 -0
- package/dist/events/event-bridge.d.ts +3 -7
- package/dist/events/event-bridge.js +17 -47
- package/dist/events/event-bridge.js.map +1 -1
- package/dist/events/event-names.d.ts +3 -1
- package/dist/events/event-names.js +3 -1
- package/dist/events/event-names.js.map +1 -1
- package/dist/form/components/base/affix-group.d.ts +17 -0
- package/dist/form/components/base/affix-group.js +49 -0
- package/dist/form/components/base/affix-group.js.map +1 -0
- package/dist/form/components/base/field.d.ts +2 -1
- package/dist/form/components/base/field.js +17 -2
- package/dist/form/components/base/field.js.map +1 -1
- package/dist/form/components/base/input-error.js +1 -1
- package/dist/form/components/base/input-error.js.map +1 -1
- package/dist/form/components/base/input.js +2 -1
- package/dist/form/components/base/input.js.map +1 -1
- package/dist/form/components/base/label.js +1 -1
- package/dist/form/components/base/label.js.map +1 -1
- package/dist/form/components/base/password-input.js.map +1 -1
- package/dist/form/components/base/submit-button.js +4 -2
- package/dist/form/components/base/submit-button.js.map +1 -1
- package/dist/form/components/base/textarea.js +2 -1
- package/dist/form/components/base/textarea.js.map +1 -1
- package/dist/form/components/conditions.js.map +1 -1
- package/dist/form/components/context.d.ts +1 -0
- package/dist/form/components/context.js +1 -0
- package/dist/form/components/context.js.map +1 -1
- package/dist/form/components/field-props.d.ts +4 -0
- package/dist/form/components/field-props.js.map +1 -1
- package/dist/form/components/field-scope.d.ts +21 -0
- package/dist/form/components/field-scope.js +54 -0
- package/dist/form/components/field-scope.js.map +1 -0
- package/dist/form/components/fields/block-add-menu.d.ts +9 -0
- package/dist/form/components/fields/block-add-menu.js +27 -0
- package/dist/form/components/fields/block-add-menu.js.map +1 -0
- package/dist/form/components/fields/builder.d.ts +2 -0
- package/dist/form/components/fields/builder.js +124 -0
- package/dist/form/components/fields/builder.js.map +1 -0
- package/dist/form/components/fields/checkbox.d.ts +1 -1
- package/dist/form/components/fields/checkbox.js +14 -11
- package/dist/form/components/fields/checkbox.js.map +1 -1
- package/dist/form/components/fields/choice.d.ts +1 -1
- package/dist/form/components/fields/choice.js +5 -4
- package/dist/form/components/fields/choice.js.map +1 -1
- package/dist/form/components/fields/date-input.d.ts +1 -1
- package/dist/form/components/fields/date-input.js +2 -1
- package/dist/form/components/fields/date-input.js.map +1 -1
- package/dist/form/components/fields/file-upload.d.ts +2 -0
- package/dist/form/components/fields/file-upload.js +282 -0
- package/dist/form/components/fields/file-upload.js.map +1 -0
- package/dist/form/components/fields/hidden-input.d.ts +1 -1
- package/dist/form/components/fields/hidden-input.js +11 -5
- package/dist/form/components/fields/hidden-input.js.map +1 -1
- package/dist/form/components/fields/number-input.d.ts +1 -1
- package/dist/form/components/fields/number-input.js +23 -15
- package/dist/form/components/fields/number-input.js.map +1 -1
- package/dist/form/components/fields/password-input.d.ts +1 -1
- package/dist/form/components/fields/password-input.js +42 -27
- package/dist/form/components/fields/password-input.js.map +1 -1
- package/dist/form/components/fields/repeater-rows.d.ts +12 -0
- package/dist/form/components/fields/repeater-rows.js +46 -0
- package/dist/form/components/fields/repeater-rows.js.map +1 -0
- package/dist/form/components/fields/repeater.d.ts +2 -0
- package/dist/form/components/fields/repeater.js +97 -0
- package/dist/form/components/fields/repeater.js.map +1 -0
- package/dist/form/components/fields/rich-editor.d.ts +1 -1
- package/dist/form/components/fields/rich-editor.js +39 -11
- package/dist/form/components/fields/rich-editor.js.map +1 -1
- package/dist/form/components/fields/row-action-menu.d.ts +17 -0
- package/dist/form/components/fields/row-action-menu.js +61 -0
- package/dist/form/components/fields/row-action-menu.js.map +1 -0
- package/dist/form/components/fields/row-actions.d.ts +10 -0
- package/dist/form/components/fields/row-actions.js +45 -0
- package/dist/form/components/fields/row-actions.js.map +1 -0
- package/dist/form/components/fields/row-item.d.ts +27 -0
- package/dist/form/components/fields/row-item.js +69 -0
- package/dist/form/components/fields/row-item.js.map +1 -0
- package/dist/form/components/fields/select.d.ts +1 -1
- package/dist/form/components/fields/select.js +69 -104
- package/dist/form/components/fields/select.js.map +1 -1
- package/dist/form/components/fields/simple-field.js +1 -0
- package/dist/form/components/fields/simple-field.js.map +1 -1
- package/dist/form/components/fields/table-rows.d.ts +32 -0
- package/dist/form/components/fields/table-rows.js +188 -0
- package/dist/form/components/fields/table-rows.js.map +1 -0
- package/dist/form/components/fields/text-input.d.ts +1 -1
- package/dist/form/components/fields/text-input.js +19 -12
- package/dist/form/components/fields/text-input.js.map +1 -1
- package/dist/form/components/fields/textarea.d.ts +1 -1
- package/dist/form/components/fields/textarea.js +2 -1
- package/dist/form/components/fields/textarea.js.map +1 -1
- package/dist/form/components/fields/use-flip-reorder.d.ts +6 -0
- package/dist/form/components/fields/use-flip-reorder.js +51 -0
- package/dist/form/components/fields/use-flip-reorder.js.map +1 -0
- package/dist/form/components/fields/use-row-collection.d.ts +11 -0
- package/dist/form/components/fields/use-row-collection.js +44 -0
- package/dist/form/components/fields/use-row-collection.js.map +1 -0
- package/dist/form/components/form-path.d.ts +5 -0
- package/dist/form/components/form-path.js +40 -0
- package/dist/form/components/form-path.js.map +1 -0
- package/dist/form/components/form-transport.d.ts +3 -4
- package/dist/form/components/form-transport.js +17 -18
- package/dist/form/components/form-transport.js.map +1 -1
- package/dist/form/components/form.js +24 -18
- package/dist/form/components/form.js.map +1 -1
- package/dist/form/components/index.d.ts +3 -0
- package/dist/form/components/index.js +4 -1
- package/dist/form/components/override-keys.d.ts +2 -0
- package/dist/form/components/override-keys.js +12 -0
- package/dist/form/components/override-keys.js.map +1 -0
- package/dist/form/components/prefill-context.d.ts +5 -0
- package/dist/form/components/prefill-context.js +11 -0
- package/dist/form/components/prefill-context.js.map +1 -0
- package/dist/form/components/prefill-targets.d.ts +17 -0
- package/dist/form/components/prefill-targets.js +73 -0
- package/dist/form/components/prefill-targets.js.map +1 -0
- package/dist/form/components/resolved-nodes.js +4 -1
- package/dist/form/components/resolved-nodes.js.map +1 -1
- package/dist/form/components/row-layout-context.d.ts +5 -0
- package/dist/form/components/row-layout-context.js +18 -0
- package/dist/form/components/row-layout-context.js.map +1 -0
- package/dist/form/components/use-controlled-field.d.ts +2 -0
- package/dist/form/components/use-controlled-field.js +13 -5
- package/dist/form/components/use-controlled-field.js.map +1 -1
- package/dist/form/components/use-dependent-field.js +7 -1
- package/dist/form/components/use-dependent-field.js.map +1 -1
- package/dist/form/components/use-field-commit.d.ts +4 -0
- package/dist/form/components/use-field-commit.js +21 -7
- package/dist/form/components/use-field-commit.js.map +1 -1
- package/dist/form/components/use-form-resolver.d.ts +6 -1
- package/dist/form/components/use-form-resolver.js +50 -5
- package/dist/form/components/use-form-resolver.js.map +1 -1
- package/dist/form/components/use-seed-default.js +8 -3
- package/dist/form/components/use-seed-default.js.map +1 -1
- package/dist/form/components/values.js +20 -5
- package/dist/form/components/values.js.map +1 -1
- package/dist/form/index.js +13 -10
- package/dist/form/index.js.map +1 -1
- package/dist/form/skeleton.js +5 -4
- package/dist/form/skeleton.js.map +1 -1
- package/dist/i18n/backend.d.ts +7 -5
- package/dist/i18n/backend.js +23 -8
- package/dist/i18n/backend.js.map +1 -1
- package/dist/i18n/config.d.ts +6 -0
- package/dist/i18n/config.js +36 -0
- package/dist/i18n/config.js.map +1 -0
- package/dist/i18n/index.d.ts +7 -1
- package/dist/i18n/index.js +5 -1
- package/dist/i18n/instance.d.ts +27 -0
- package/dist/i18n/instance.js +63 -16
- package/dist/i18n/instance.js.map +1 -1
- package/dist/i18n/locale-reload.d.ts +3 -0
- package/dist/i18n/locale-reload.js +17 -0
- package/dist/i18n/locale-reload.js.map +1 -0
- package/dist/i18n/locale-switcher.d.ts +21 -0
- package/dist/i18n/locale-switcher.js +25 -0
- package/dist/i18n/locale-switcher.js.map +1 -0
- package/dist/i18n/locale.d.ts +9 -0
- package/dist/i18n/locale.js +93 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/i18n/page-props.d.ts +3 -0
- package/dist/i18n/page-props.js +24 -0
- package/dist/i18n/page-props.js.map +1 -0
- package/dist/i18n/translatable.d.ts +4 -0
- package/dist/i18n/translatable.js +24 -0
- package/dist/i18n/translatable.js.map +1 -0
- package/dist/index.d.ts +20 -8
- package/dist/index.js +19 -7
- package/dist/inertia.d.ts +2 -1
- package/dist/inertia.js +9 -2
- package/dist/inertia.js.map +1 -1
- package/dist/lattice.css +148 -22
- package/dist/layout/breadcrumbs.d.ts +3 -0
- package/dist/layout/breadcrumbs.js +34 -0
- package/dist/layout/breadcrumbs.js.map +1 -0
- package/dist/layout/callouts.d.ts +3 -0
- package/dist/layout/callouts.js +67 -0
- package/dist/layout/callouts.js.map +1 -0
- package/dist/layout/components.js +9 -1
- package/dist/layout/components.js.map +1 -1
- package/dist/layout/context.d.ts +2 -10
- package/dist/layout/context.js +3 -14
- package/dist/layout/context.js.map +1 -1
- package/dist/layout/dropdown.d.ts +3 -0
- package/dist/layout/dropdown.js +24 -0
- package/dist/layout/dropdown.js.map +1 -0
- package/dist/layout/index.d.ts +2 -1
- package/dist/layout/index.js +3 -1
- package/dist/layout/menu-item.js +54 -62
- package/dist/layout/menu-item.js.map +1 -1
- package/dist/layout/menu.js.map +1 -1
- package/dist/layout/outlet.js +2 -2
- package/dist/layout/outlet.js.map +1 -1
- package/dist/layout/popover.d.ts +17 -0
- package/dist/layout/popover.js +46 -0
- package/dist/layout/popover.js.map +1 -0
- package/dist/layout/schema-layout.js +2 -9
- package/dist/layout/schema-layout.js.map +1 -1
- package/dist/layout/sidebar.js +4 -4
- package/dist/layout/sidebar.js.map +1 -1
- package/dist/layout/topbar.d.ts +3 -0
- package/dist/layout/topbar.js +15 -0
- package/dist/layout/topbar.js.map +1 -0
- package/dist/page.js +16 -17
- package/dist/page.js.map +1 -1
- package/dist/provider.d.ts +2 -4
- package/dist/provider.js +5 -9
- package/dist/provider.js.map +1 -1
- package/dist/realtime/build-effects.d.ts +4 -0
- package/dist/realtime/build-effects.js +23 -0
- package/dist/realtime/build-effects.js.map +1 -0
- package/dist/realtime/listeners.d.ts +8 -0
- package/dist/realtime/listeners.js +31 -0
- package/dist/realtime/listeners.js.map +1 -0
- package/dist/realtime/subscriptions.d.ts +4 -0
- package/dist/realtime/subscriptions.js +45 -0
- package/dist/realtime/subscriptions.js.map +1 -0
- package/dist/registry.js +3 -1
- package/dist/registry.js.map +1 -1
- package/dist/remote/components/chat-box.d.ts +3 -0
- package/dist/remote/components/chat-box.js +65 -0
- package/dist/remote/components/chat-box.js.map +1 -0
- package/dist/remote/components/data-list.d.ts +10 -0
- package/dist/remote/components/data-list.js +106 -0
- package/dist/remote/components/data-list.js.map +1 -0
- package/dist/remote/index.d.ts +1 -0
- package/dist/remote/index.js +15 -0
- package/dist/remote/index.js.map +1 -0
- package/dist/svg-sprite-client/index.d.ts +1 -0
- package/dist/svg-sprite-client/index.js +0 -0
- package/dist/svg-sprite-client.d.ts +1 -0
- package/dist/table/align.d.ts +4 -0
- package/dist/table/align.js +23 -0
- package/dist/table/align.js.map +1 -0
- package/dist/table/bulk.js +1 -1
- package/dist/table/bulk.js.map +1 -1
- package/dist/table/components/bulk-bar.js +8 -5
- package/dist/table/components/bulk-bar.js.map +1 -1
- package/dist/table/components/cells/badge-cell.d.ts +2 -5
- package/dist/table/components/cells/badge-cell.js +3 -3
- package/dist/table/components/cells/badge-cell.js.map +1 -1
- package/dist/table/components/cells/boolean-cell.d.ts +3 -0
- package/dist/table/components/cells/boolean-cell.js +23 -0
- package/dist/table/components/cells/boolean-cell.js.map +1 -0
- package/dist/table/components/cells/icon-cell.d.ts +2 -5
- package/dist/table/components/cells/icon-cell.js +4 -5
- package/dist/table/components/cells/icon-cell.js.map +1 -1
- package/dist/table/components/cells/image-cell.d.ts +2 -5
- package/dist/table/components/cells/image-cell.js +4 -5
- package/dist/table/components/cells/image-cell.js.map +1 -1
- package/dist/table/components/cells/money-cell.d.ts +2 -0
- package/dist/table/components/cells/money-cell.js +28 -0
- package/dist/table/components/cells/money-cell.js.map +1 -0
- package/dist/table/components/cells/number-cell.d.ts +2 -0
- package/dist/table/components/cells/number-cell.js +25 -0
- package/dist/table/components/cells/number-cell.js.map +1 -0
- package/dist/table/components/cells/numeric.d.ts +2 -0
- package/dist/table/components/cells/numeric.js +10 -0
- package/dist/table/components/cells/numeric.js.map +1 -0
- package/dist/table/components/cells/stack-cell.d.ts +3 -0
- package/dist/table/components/cells/stack-cell.js +17 -0
- package/dist/table/components/cells/stack-cell.js.map +1 -0
- package/dist/table/components/cells/text-cell.d.ts +2 -6
- package/dist/table/components/cells/text-cell.js +6 -7
- package/dist/table/components/cells/text-cell.js.map +1 -1
- package/dist/table/components/column-filter-control.d.ts +4 -1
- package/dist/table/components/column-filter-control.js +82 -11
- package/dist/table/components/column-filter-control.js.map +1 -1
- package/dist/table/components/column-header.d.ts +3 -1
- package/dist/table/components/column-header.js +11 -8
- package/dist/table/components/column-header.js.map +1 -1
- package/dist/table/components/filter-bar.d.ts +16 -0
- package/dist/table/components/filter-bar.js +104 -0
- package/dist/table/components/filter-bar.js.map +1 -0
- package/dist/table/components/filter-controls.d.ts +18 -0
- package/dist/table/components/filter-controls.js +256 -0
- package/dist/table/components/filter-controls.js.map +1 -0
- package/dist/table/components/filter-stack-bar.js +4 -3
- package/dist/table/components/filter-stack-bar.js.map +1 -1
- package/dist/table/components/filter-value-input.d.ts +1 -0
- package/dist/table/components/filter-value-input.js +7 -6
- package/dist/table/components/filter-value-input.js.map +1 -1
- package/dist/table/components/pagination.js +1 -1
- package/dist/table/components/pagination.js.map +1 -1
- package/dist/table/components/sort-bar.js +1 -1
- package/dist/table/components/sort-bar.js.map +1 -1
- package/dist/table/components/table-action-node.js.map +1 -1
- package/dist/table/components/table-cell.d.ts +1 -1
- package/dist/table/components/table-cell.js +18 -28
- package/dist/table/components/table-cell.js.map +1 -1
- package/dist/table/components/table.js +200 -152
- package/dist/table/components/table.js.map +1 -1
- package/dist/table/filter-values.d.ts +13 -0
- package/dist/table/filter-values.js +30 -0
- package/dist/table/filter-values.js.map +1 -0
- package/dist/table/format.js.map +1 -1
- package/dist/table/index.js.map +1 -1
- package/dist/table/payload.js +24 -3
- package/dist/table/payload.js.map +1 -1
- package/dist/table/query.d.ts +10 -1
- package/dist/table/query.js +60 -6
- package/dist/table/query.js.map +1 -1
- package/dist/table/registry.d.ts +19 -0
- package/dist/table/registry.js +14 -0
- package/dist/table/registry.js.map +1 -0
- package/dist/table/types.d.ts +9 -19
- package/dist/table/use-table.d.ts +8 -2
- package/dist/table/use-table.js +61 -7
- package/dist/table/use-table.js.map +1 -1
- package/dist/toast/callout.d.ts +6 -0
- package/dist/toast/callout.js +31 -0
- package/dist/toast/callout.js.map +1 -0
- package/dist/toast/index.d.ts +4 -0
- package/dist/toast/index.js +3 -1
- package/dist/toast/toast.d.ts +14 -0
- package/dist/toast/toast.js +54 -0
- package/dist/toast/toast.js.map +1 -0
- package/dist/toast/toaster.js +14 -77
- package/dist/toast/toaster.js.map +1 -1
- package/dist/toast/variant-styles.d.ts +6 -0
- package/dist/toast/variant-styles.js +37 -0
- package/dist/toast/variant-styles.js.map +1 -0
- package/dist/types/generated.d.ts +547 -103
- package/dist/types/vite-env.d.ts +1 -0
- package/dist/vite.d.ts +25 -0
- package/dist/vite.js +72 -0
- package/dist/vite.js.map +1 -0
- package/package.json +130 -13
- package/resources/icons/rotate-ccw.svg +16 -0
- package/dist/_virtual/_rolldown/runtime.js +0 -8
- package/dist/action/effects.d.ts +0 -11
- package/dist/action/effects.js +0 -44
- package/dist/action/effects.js.map +0 -1
- package/dist/form/components/base/checkbox.js.map +0 -1
- package/dist/node_modules/i18next/dist/esm/i18next.js +0 -1982
- package/dist/node_modules/i18next/dist/esm/i18next.js.map +0 -1
- package/dist/node_modules/i18next-http-backend/esm/index.js +0 -352
- package/dist/node_modules/i18next-http-backend/esm/index.js.map +0 -1
- package/dist/node_modules/react-i18next/dist/es/context.js +0 -23
- package/dist/node_modules/react-i18next/dist/es/context.js.map +0 -1
- package/dist/node_modules/react-i18next/dist/es/defaults.js +0 -29
- package/dist/node_modules/react-i18next/dist/es/defaults.js.map +0 -1
- package/dist/node_modules/react-i18next/dist/es/i18nInstance.js +0 -10
- package/dist/node_modules/react-i18next/dist/es/i18nInstance.js.map +0 -1
- package/dist/node_modules/react-i18next/dist/es/initReactI18next.js +0 -14
- package/dist/node_modules/react-i18next/dist/es/initReactI18next.js.map +0 -1
- package/dist/node_modules/react-i18next/dist/es/unescape.js +0 -30
- package/dist/node_modules/react-i18next/dist/es/unescape.js.map +0 -1
- package/dist/node_modules/react-i18next/dist/es/useTranslation.js +0 -157
- package/dist/node_modules/react-i18next/dist/es/useTranslation.js.map +0 -1
- package/dist/node_modules/react-i18next/dist/es/utils.js +0 -58
- package/dist/node_modules/react-i18next/dist/es/utils.js.map +0 -1
- package/dist/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js +0 -70
- package/dist/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js.map +0 -1
- package/dist/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js +0 -61
- package/dist/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js.map +0 -1
- package/dist/node_modules/use-sync-external-store/shim/index.js +0 -13
- package/dist/node_modules/use-sync-external-store/shim/index.js.map +0 -1
- package/dist/table/column-registry.d.ts +0 -9
- package/dist/test-support.d.ts +0 -14
- package/dist/test-support.js +0 -14
- package/dist/test-support.js.map +0 -1
- /package/dist/{table/column-registry.js → chat/types.js} +0 -0
- /package/dist/{form/components/base → core/components}/checkbox.d.ts +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import { testIdentity } from "../../core/test-id.js";
|
|
3
|
+
import { useT } from "../../i18n/instance.js";
|
|
4
|
+
import { apiFetch } from "../../core/api.js";
|
|
5
|
+
import { useChat } from "../use-chat.js";
|
|
6
|
+
import { MessageList } from "./message-list.js";
|
|
7
|
+
import { PromptInput } from "./prompt-input.js";
|
|
8
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
//#region resources/js/chat/components/chat-box.tsx
|
|
11
|
+
var ChatBox = ({ node }) => {
|
|
12
|
+
const { streamEndpoint, historyEndpoint, placeholder, title, fill } = node.props;
|
|
13
|
+
const { t } = useT("lattice");
|
|
14
|
+
const { messages, status, sendMessage, setMessages } = useChat({ endpoint: streamEndpoint ?? "" });
|
|
15
|
+
const seededRef = useRef(false);
|
|
16
|
+
const seedHistory = useCallback(async () => {
|
|
17
|
+
if (seededRef.current || !historyEndpoint) return;
|
|
18
|
+
seededRef.current = true;
|
|
19
|
+
const response = await apiFetch(historyEndpoint, { throwOnError: false });
|
|
20
|
+
if (!response.ok) return;
|
|
21
|
+
setMessages((await response.json()).messages);
|
|
22
|
+
}, [historyEndpoint, setMessages]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
seedHistory();
|
|
25
|
+
}, [seedHistory]);
|
|
26
|
+
const busy = status === "submitted" || status === "streaming";
|
|
27
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
28
|
+
className: cn("flex flex-col overflow-hidden border border-lt-border bg-lt-bg", fill ? "sticky top-0 h-full min-h-[28rem] w-full" : "h-[28rem] w-80 rounded-lt shadow-lt-lg"),
|
|
29
|
+
"data-test": testIdentity("chat-box"),
|
|
30
|
+
children: [
|
|
31
|
+
/* @__PURE__ */ jsx("div", {
|
|
32
|
+
className: "flex items-center border-b border-lt-border px-3 py-2",
|
|
33
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
34
|
+
className: "text-sm font-medium text-lt-fg",
|
|
35
|
+
children: title ?? t("chat.title", "Chat")
|
|
36
|
+
})
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ jsx("div", {
|
|
39
|
+
className: "flex-1 overflow-y-auto",
|
|
40
|
+
children: /* @__PURE__ */ jsx(MessageList, { messages })
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ jsx(PromptInput, {
|
|
43
|
+
disabled: busy,
|
|
44
|
+
onSubmit: sendMessage,
|
|
45
|
+
placeholder: placeholder ?? void 0
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
export { ChatBox, ChatBox as default };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=chat-box.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-box.js","names":[],"sources":["../../../resources/js/chat/components/chat-box.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef } from \"react\";\nimport { apiFetch } from \"@lattice-php/lattice/core/api\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { useChat } from \"../use-chat\";\nimport type { ChatMessage } from \"../types\";\nimport { MessageList } from \"./message-list\";\nimport { PromptInput } from \"./prompt-input\";\n\ntype ChatHistoryResponse = { messages: ChatMessage[] };\n\nexport const ChatBox: RendererComponent<\"chat.box\"> = ({ node }) => {\n const { streamEndpoint, historyEndpoint, placeholder, title, fill } = node.props;\n const { t } = useT(\"lattice\");\n const { messages, status, sendMessage, setMessages } = useChat({\n endpoint: streamEndpoint ?? \"\",\n });\n\n const seededRef = useRef(false);\n\n const seedHistory = useCallback(async (): Promise<void> => {\n if (seededRef.current || !historyEndpoint) {\n return;\n }\n\n seededRef.current = true;\n\n const response = await apiFetch(historyEndpoint, { throwOnError: false });\n\n if (!response.ok) {\n return;\n }\n\n const payload = (await response.json()) as ChatHistoryResponse;\n setMessages(payload.messages);\n }, [historyEndpoint, setMessages]);\n\n useEffect(() => {\n void seedHistory();\n }, [seedHistory]);\n\n const busy = status === \"submitted\" || status === \"streaming\";\n\n return (\n <div\n className={cn(\n \"flex flex-col overflow-hidden border border-lt-border bg-lt-bg\",\n fill\n ? \"sticky top-0 h-full min-h-[28rem] w-full\"\n : \"h-[28rem] w-80 rounded-lt shadow-lt-lg\",\n )}\n data-test={testIdentity(\"chat-box\")}\n >\n <div className=\"flex items-center border-b border-lt-border px-3 py-2\">\n <span className=\"text-sm font-medium text-lt-fg\">{title ?? t(\"chat.title\", \"Chat\")}</span>\n </div>\n <div className=\"flex-1 overflow-y-auto\">\n <MessageList messages={messages} />\n </div>\n <PromptInput disabled={busy} onSubmit={sendMessage} placeholder={placeholder ?? undefined} />\n </div>\n );\n};\n\nexport default ChatBox;\n"],"mappings":";;;;;;;;;;AAaA,IAAa,WAA0C,EAAE,WAAW;CAClE,MAAM,EAAE,gBAAgB,iBAAiB,aAAa,OAAO,SAAS,KAAK;CAC3E,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,EAAE,UAAU,QAAQ,aAAa,gBAAgB,QAAQ,EAC7D,UAAU,kBAAkB,GAC9B,CAAC;CAED,MAAM,YAAY,OAAO,KAAK;CAE9B,MAAM,cAAc,YAAY,YAA2B;EACzD,IAAI,UAAU,WAAW,CAAC,iBACxB;EAGF,UAAU,UAAU;EAEpB,MAAM,WAAW,MAAM,SAAS,iBAAiB,EAAE,cAAc,MAAM,CAAC;EAExE,IAAI,CAAC,SAAS,IACZ;EAIF,aAAY,MADW,SAAS,KAAK,GACjB,QAAQ;CAC9B,GAAG,CAAC,iBAAiB,WAAW,CAAC;CAEjC,gBAAgB;EACd,YAAiB;CACnB,GAAG,CAAC,WAAW,CAAC;CAEhB,MAAM,OAAO,WAAW,eAAe,WAAW;CAElD,OACE,qBAAC,OAAD;EACE,WAAW,GACT,kEACA,OACI,6CACA,wCACN;EACA,aAAW,aAAa,UAAU;YAPpC;GASE,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,QAAD;KAAM,WAAU;eAAkC,SAAS,EAAE,cAAc,MAAM;IAAQ,CAAA;GACtF,CAAA;GACL,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,aAAD,EAAuB,SAAW,CAAA;GAC/B,CAAA;GACL,oBAAC,aAAD;IAAa,UAAU;IAAM,UAAU;IAAa,aAAa,eAAe,KAAA;GAAY,CAAA;EACzF;;AAET"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { testIdentity } from "../../core/test-id.js";
|
|
2
|
+
import { Message } from "./message.js";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region resources/js/chat/components/message-list.tsx
|
|
6
|
+
function MessageList({ messages }) {
|
|
7
|
+
const bottomRef = useRef(null);
|
|
8
|
+
const lastMessage = messages.at(-1);
|
|
9
|
+
const lastPartLength = lastMessage?.parts.at(-1) !== void 0 ? JSON.stringify(lastMessage.parts.at(-1)).length : 0;
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (bottomRef.current && typeof bottomRef.current.scrollIntoView === "function") bottomRef.current.scrollIntoView({ behavior: "smooth" });
|
|
12
|
+
}, [messages.length, lastPartLength]);
|
|
13
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
14
|
+
className: "flex flex-col gap-2 overflow-y-auto p-3",
|
|
15
|
+
"data-test": testIdentity("chat-messages"),
|
|
16
|
+
children: [messages.map((message) => /* @__PURE__ */ jsx(Message, { message }, message.id)), /* @__PURE__ */ jsx("div", { ref: bottomRef })]
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { MessageList };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=message-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-list.js","names":[],"sources":["../../../resources/js/chat/components/message-list.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { ChatMessage } from \"../types\";\nimport { Message } from \"./message\";\n\nexport function MessageList({ messages }: { messages: ChatMessage[] }): ReactNode {\n const bottomRef = useRef<HTMLDivElement>(null);\n const lastMessage = messages.at(-1);\n const lastPartLength =\n lastMessage?.parts.at(-1) !== undefined ? JSON.stringify(lastMessage.parts.at(-1)).length : 0;\n\n useEffect(() => {\n if (bottomRef.current && typeof bottomRef.current.scrollIntoView === \"function\") {\n bottomRef.current.scrollIntoView({ behavior: \"smooth\" });\n }\n }, [messages.length, lastPartLength]);\n\n return (\n <div\n className=\"flex flex-col gap-2 overflow-y-auto p-3\"\n data-test={testIdentity(\"chat-messages\")}\n >\n {messages.map((message) => (\n <Message key={message.id} message={message} />\n ))}\n <div ref={bottomRef} />\n </div>\n );\n}\n"],"mappings":";;;;;AAMA,SAAgB,YAAY,EAAE,YAAoD;CAChF,MAAM,YAAY,OAAuB,IAAI;CAC7C,MAAM,cAAc,SAAS,GAAG,EAAE;CAClC,MAAM,iBACJ,aAAa,MAAM,GAAG,EAAE,MAAM,KAAA,IAAY,KAAK,UAAU,YAAY,MAAM,GAAG,EAAE,CAAC,EAAE,SAAS;CAE9F,gBAAgB;EACd,IAAI,UAAU,WAAW,OAAO,UAAU,QAAQ,mBAAmB,YACnE,UAAU,QAAQ,eAAe,EAAE,UAAU,SAAS,CAAC;CAE3D,GAAG,CAAC,SAAS,QAAQ,cAAc,CAAC;CAEpC,OACE,qBAAC,OAAD;EACE,WAAU;EACV,aAAW,aAAa,eAAe;YAFzC,CAIG,SAAS,KAAK,YACb,oBAAC,SAAD,EAAmC,QAAU,GAA/B,QAAQ,EAAuB,CAC9C,GACD,oBAAC,OAAD,EAAK,KAAK,UAAY,CAAA,CACnB;;AAET"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import { testIdentity } from "../../core/test-id.js";
|
|
3
|
+
import { RenderNode } from "../../core/renderer.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region resources/js/chat/components/message.tsx
|
|
6
|
+
function Message({ message }) {
|
|
7
|
+
const isUser = message.role === "user";
|
|
8
|
+
return /* @__PURE__ */ jsx("div", {
|
|
9
|
+
className: cn("flex flex-col gap-1", isUser ? "items-end" : "items-start"),
|
|
10
|
+
"data-test": testIdentity(`chat-message-${message.role}`),
|
|
11
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
12
|
+
className: cn("max-w-[80%] rounded-lt px-3 py-2 text-sm", isUser ? "bg-lt-primary text-lt-primary-fg" : "bg-lt-muted text-lt-fg"),
|
|
13
|
+
children: message.parts.map((part, index) => /* @__PURE__ */ jsx(RenderNode, { node: part }, part.key ?? index))
|
|
14
|
+
})
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { Message };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.js","names":[],"sources":["../../../resources/js/chat/components/message.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { ChatMessage } from \"../types\";\n\nexport function Message({ message }: { message: ChatMessage }): ReactNode {\n const isUser = message.role === \"user\";\n\n return (\n <div\n className={cn(\"flex flex-col gap-1\", isUser ? \"items-end\" : \"items-start\")}\n data-test={testIdentity(`chat-message-${message.role}`)}\n >\n <div\n className={cn(\n \"max-w-[80%] rounded-lt px-3 py-2 text-sm\",\n isUser ? \"bg-lt-primary text-lt-primary-fg\" : \"bg-lt-muted text-lt-fg\",\n )}\n >\n {message.parts.map((part, index) => (\n <RenderNode key={part.key ?? index} node={part} />\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAMA,SAAgB,QAAQ,EAAE,WAAgD;CACxE,MAAM,SAAS,QAAQ,SAAS;CAEhC,OACE,oBAAC,OAAD;EACE,WAAW,GAAG,uBAAuB,SAAS,cAAc,aAAa;EACzE,aAAW,aAAa,gBAAgB,QAAQ,MAAM;YAEtD,oBAAC,OAAD;GACE,WAAW,GACT,4CACA,SAAS,qCAAqC,wBAChD;aAEC,QAAQ,MAAM,KAAK,MAAM,UACxB,oBAAC,YAAD,EAAoC,MAAM,KAAO,GAAhC,KAAK,OAAO,KAAoB,CAClD;EACE,CAAA;CACF,CAAA;AAET"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
type PromptInputProps = {
|
|
3
|
+
onSubmit: (text: string) => void;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function PromptInput({ onSubmit, disabled, placeholder, }: PromptInputProps): ReactNode;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import { testIdentity } from "../../core/test-id.js";
|
|
3
|
+
import { Button } from "../../core/components/button.js";
|
|
4
|
+
import { useT } from "../../i18n/instance.js";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region resources/js/chat/components/prompt-input.tsx
|
|
8
|
+
function PromptInput({ onSubmit, disabled = false, placeholder }) {
|
|
9
|
+
const { t } = useT("lattice");
|
|
10
|
+
const [value, setValue] = useState("");
|
|
11
|
+
function submit() {
|
|
12
|
+
const trimmed = value.trim();
|
|
13
|
+
if (trimmed === "" || disabled) return;
|
|
14
|
+
onSubmit(trimmed);
|
|
15
|
+
setValue("");
|
|
16
|
+
}
|
|
17
|
+
function handleKeyDown(event) {
|
|
18
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
submit();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
24
|
+
className: "flex items-end gap-2 border-t border-lt-border p-3",
|
|
25
|
+
children: [/* @__PURE__ */ jsx("textarea", {
|
|
26
|
+
"aria-label": t("chat.input-label", "Message input"),
|
|
27
|
+
className: cn("min-h-[2.5rem] flex-1 resize-none rounded-lt-sm border border-lt-input bg-lt-bg px-3 py-2 text-sm text-lt-fg placeholder:text-lt-muted-fg focus:outline-none focus:ring-1 focus:ring-lt-ring disabled:opacity-50"),
|
|
28
|
+
"data-test": testIdentity("chat-input"),
|
|
29
|
+
disabled,
|
|
30
|
+
onChange: (e) => setValue(e.target.value),
|
|
31
|
+
onKeyDown: handleKeyDown,
|
|
32
|
+
placeholder,
|
|
33
|
+
rows: 1,
|
|
34
|
+
value
|
|
35
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
36
|
+
"data-test": testIdentity("chat-send"),
|
|
37
|
+
disabled,
|
|
38
|
+
onClick: submit,
|
|
39
|
+
size: "sm",
|
|
40
|
+
type: "button",
|
|
41
|
+
variant: "default",
|
|
42
|
+
children: t("chat.send", "Send")
|
|
43
|
+
})]
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { PromptInput };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=prompt-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-input.js","names":[],"sources":["../../../resources/js/chat/components/prompt-input.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { Button } from \"@lattice-php/lattice/core/components/button\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\n\ntype PromptInputProps = {\n onSubmit: (text: string) => void;\n disabled?: boolean;\n placeholder?: string;\n};\n\nexport function PromptInput({\n onSubmit,\n disabled = false,\n placeholder,\n}: PromptInputProps): ReactNode {\n const { t } = useT(\"lattice\");\n const [value, setValue] = useState(\"\");\n\n function submit(): void {\n const trimmed = value.trim();\n if (trimmed === \"\" || disabled) {\n return;\n }\n\n onSubmit(trimmed);\n setValue(\"\");\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n }\n }\n\n return (\n <div className=\"flex items-end gap-2 border-t border-lt-border p-3\">\n <textarea\n aria-label={t(\"chat.input-label\", \"Message input\")}\n className={cn(\n \"min-h-[2.5rem] flex-1 resize-none rounded-lt-sm border border-lt-input bg-lt-bg px-3 py-2 text-sm text-lt-fg placeholder:text-lt-muted-fg focus:outline-none focus:ring-1 focus:ring-lt-ring disabled:opacity-50\",\n )}\n data-test={testIdentity(\"chat-input\")}\n disabled={disabled}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n rows={1}\n value={value}\n />\n <Button\n data-test={testIdentity(\"chat-send\")}\n disabled={disabled}\n onClick={submit}\n size=\"sm\"\n type=\"button\"\n variant=\"default\"\n >\n {t(\"chat.send\", \"Send\")}\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,YAAY,EAC1B,UACA,WAAW,OACX,eAC8B;CAC9B,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CAErC,SAAS,SAAe;EACtB,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,YAAY,MAAM,UACpB;EAGF,SAAS,OAAO;EAChB,SAAS,EAAE;CACb;CAEA,SAAS,cAAc,OAAiD;EACtE,IAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,UAAU;GAC5C,MAAM,eAAe;GACrB,OAAO;EACT;CACF;CAEA,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,YAAD;GACE,cAAY,EAAE,oBAAoB,eAAe;GACjD,WAAW,GACT,kNACF;GACA,aAAW,aAAa,YAAY;GAC1B;GACV,WAAW,MAAM,SAAS,EAAE,OAAO,KAAK;GACxC,WAAW;GACE;GACb,MAAM;GACC;EACR,CAAA,GACD,oBAAC,QAAD;GACE,aAAW,aAAa,WAAW;GACzB;GACV,SAAS;GACT,MAAK;GACL,MAAK;GACL,SAAQ;aAEP,EAAE,aAAa,MAAM;EAChB,CAAA,CACL;;AAET"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const chatPlugin: import('../core/registry').Plugin;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createPlugin, eagerComponent } from "../core/registry.js";
|
|
2
|
+
import { TextPart } from "./parts/text.js";
|
|
3
|
+
import { ToolCallPart } from "./parts/tool-call.js";
|
|
4
|
+
//#region resources/js/chat/index.ts
|
|
5
|
+
var chatPlugin = createPlugin({
|
|
6
|
+
name: "chat",
|
|
7
|
+
components: {
|
|
8
|
+
"chat.part.text": eagerComponent(TextPart),
|
|
9
|
+
"chat.part.tool-call": eagerComponent(ToolCallPart)
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
//#endregion
|
|
13
|
+
export { chatPlugin };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../resources/js/chat/index.ts"],"sourcesContent":["import { createPlugin, eagerComponent } from \"@lattice-php/lattice/core/registry\";\nimport { TextPart } from \"./parts/text\";\nimport { ToolCallPart } from \"./parts/tool-call\";\n\nexport const chatPlugin = createPlugin({\n name: \"chat\",\n components: {\n \"chat.part.text\": eagerComponent(TextPart),\n \"chat.part.tool-call\": eagerComponent(ToolCallPart),\n },\n});\n"],"mappings":";;;;AAIA,IAAa,aAAa,aAAa;CACrC,MAAM;CACN,YAAY;EACV,kBAAkB,eAAe,QAAQ;EACzC,uBAAuB,eAAe,YAAY;CACpD;AACF,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
//#region resources/js/chat/parts/text.tsx
|
|
3
|
+
var TextPart = ({ node }) => {
|
|
4
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5
|
+
className: "whitespace-pre-wrap text-sm",
|
|
6
|
+
children: node.props.text
|
|
7
|
+
});
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { TextPart, TextPart as default };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","names":[],"sources":["../../../resources/js/chat/parts/text.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\n\nexport const TextPart: RendererComponent<\"chat.part.text\"> = ({ node }) => {\n return <div className=\"whitespace-pre-wrap text-sm\">{node.props.text}</div>;\n};\n\nexport default TextPart;\n"],"mappings":";;AAEA,IAAa,YAAiD,EAAE,WAAW;CACzE,OAAO,oBAAC,OAAD;EAAK,WAAU;YAA+B,KAAK,MAAM;CAAU,CAAA;AAC5E"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { testIdentity } from "../../core/test-id.js";
|
|
2
|
+
import { jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region resources/js/chat/parts/tool-call.tsx
|
|
4
|
+
var ToolCallPart = ({ node }) => {
|
|
5
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
6
|
+
className: "mt-1 inline-flex items-center gap-1 rounded-lt-sm bg-lt-muted px-2 py-0.5 font-mono text-xs text-lt-muted-fg",
|
|
7
|
+
"data-test": testIdentity("chat-tool-call"),
|
|
8
|
+
children: [
|
|
9
|
+
"🔧 ",
|
|
10
|
+
node.props.name,
|
|
11
|
+
"(",
|
|
12
|
+
JSON.stringify(node.props.args),
|
|
13
|
+
")"
|
|
14
|
+
]
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { ToolCallPart, ToolCallPart as default };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=tool-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-call.js","names":[],"sources":["../../../resources/js/chat/parts/tool-call.tsx"],"sourcesContent":["import { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\n\nexport const ToolCallPart: RendererComponent<\"chat.part.tool-call\"> = ({ node }) => {\n return (\n <div\n className=\"mt-1 inline-flex items-center gap-1 rounded-lt-sm bg-lt-muted px-2 py-0.5 font-mono text-xs text-lt-muted-fg\"\n data-test={testIdentity(\"chat-tool-call\")}\n >\n 🔧 {node.props.name}({JSON.stringify(node.props.args)})\n </div>\n );\n};\n\nexport default ToolCallPart;\n"],"mappings":";;;AAGA,IAAa,gBAA0D,EAAE,WAAW;CAClF,OACE,qBAAC,OAAD;EACE,WAAU;EACV,aAAW,aAAa,gBAAgB;YAF1C;GAGC;GACK,KAAK,MAAM;GAAK;GAAE,KAAK,UAAU,KAAK,MAAM,IAAI;GAAE;EACnD;;AAET"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { RemoteAccess } from '../core/api';
|
|
2
|
+
import { ChatFrame, ChatTransportRequest } from './types';
|
|
3
|
+
export declare function ndjsonChatTransport({ url, body, signal, }: ChatTransportRequest): AsyncGenerator<ChatFrame>;
|
|
4
|
+
export declare function createRemoteNdjsonChatTransport(remote: RemoteAccess): ({ url, body, signal, }: ChatTransportRequest) => AsyncGenerator<ChatFrame>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { apiFetch, remoteFetch } from "../core/api.js";
|
|
2
|
+
//#region resources/js/chat/transport.ts
|
|
3
|
+
function parseFrame(line) {
|
|
4
|
+
try {
|
|
5
|
+
return JSON.parse(line);
|
|
6
|
+
} catch {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
async function* ndjsonChatTransport({ url, body, signal }) {
|
|
11
|
+
const res = await apiFetch(url, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
signal,
|
|
14
|
+
headers: { Accept: "application/x-ndjson" },
|
|
15
|
+
body: JSON.stringify(body),
|
|
16
|
+
throwOnError: false
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok || !res.body) throw new Error(`Chat stream failed (${res.status})`);
|
|
19
|
+
yield* readNdjsonFrames(res.body);
|
|
20
|
+
}
|
|
21
|
+
function createRemoteNdjsonChatTransport(remote) {
|
|
22
|
+
return async function* remoteNdjsonChatTransport({ url, body, signal }) {
|
|
23
|
+
const res = await remoteFetch(url, {
|
|
24
|
+
remote,
|
|
25
|
+
method: "POST",
|
|
26
|
+
signal,
|
|
27
|
+
headers: {
|
|
28
|
+
Accept: "application/x-ndjson",
|
|
29
|
+
"Content-Type": "application/json"
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify(body),
|
|
32
|
+
throwOnError: false
|
|
33
|
+
});
|
|
34
|
+
if (!res.ok || !res.body) throw new Error(`Chat stream failed (${res.status})`);
|
|
35
|
+
yield* readNdjsonFrames(res.body);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async function* readNdjsonFrames(body) {
|
|
39
|
+
const reader = body.getReader();
|
|
40
|
+
const decoder = new TextDecoder();
|
|
41
|
+
let buffer = "";
|
|
42
|
+
try {
|
|
43
|
+
for (;;) {
|
|
44
|
+
const { done, value } = await reader.read();
|
|
45
|
+
if (done) break;
|
|
46
|
+
buffer += decoder.decode(value, { stream: true });
|
|
47
|
+
const lines = buffer.split("\n");
|
|
48
|
+
buffer = lines.pop() ?? "";
|
|
49
|
+
for (const line of lines) {
|
|
50
|
+
if (line.trim() === "") continue;
|
|
51
|
+
const frame = parseFrame(line);
|
|
52
|
+
if (frame) yield frame;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
buffer += decoder.decode();
|
|
56
|
+
if (buffer.trim() !== "") {
|
|
57
|
+
const frame = parseFrame(buffer);
|
|
58
|
+
if (frame) yield frame;
|
|
59
|
+
}
|
|
60
|
+
} finally {
|
|
61
|
+
await reader.cancel().catch(() => {});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { createRemoteNdjsonChatTransport, ndjsonChatTransport };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","names":[],"sources":["../../resources/js/chat/transport.ts"],"sourcesContent":["import { apiFetch, remoteFetch, type RemoteAccess } from \"../core/api\";\nimport type { ChatFrame, ChatTransportRequest } from \"./types\";\n\nfunction parseFrame(line: string): ChatFrame | null {\n try {\n return JSON.parse(line) as ChatFrame;\n } catch {\n return null;\n }\n}\n\nexport async function* ndjsonChatTransport({\n url,\n body,\n signal,\n}: ChatTransportRequest): AsyncGenerator<ChatFrame> {\n const res = await apiFetch(url, {\n method: \"POST\",\n signal,\n headers: { Accept: \"application/x-ndjson\" },\n body: JSON.stringify(body),\n throwOnError: false,\n });\n\n if (!res.ok || !res.body) {\n throw new Error(`Chat stream failed (${res.status})`);\n }\n\n yield* readNdjsonFrames(res.body);\n}\n\nexport function createRemoteNdjsonChatTransport(remote: RemoteAccess) {\n return async function* remoteNdjsonChatTransport({\n url,\n body,\n signal,\n }: ChatTransportRequest): AsyncGenerator<ChatFrame> {\n const res = await remoteFetch(url, {\n remote,\n method: \"POST\",\n signal,\n headers: {\n Accept: \"application/x-ndjson\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n throwOnError: false,\n });\n\n if (!res.ok || !res.body) {\n throw new Error(`Chat stream failed (${res.status})`);\n }\n\n yield* readNdjsonFrames(res.body);\n };\n}\n\nasync function* readNdjsonFrames(body: ReadableStream<Uint8Array>): AsyncGenerator<ChatFrame> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.trim() === \"\") {\n continue;\n }\n\n const frame = parseFrame(line);\n if (frame) {\n yield frame;\n }\n }\n }\n\n buffer += decoder.decode();\n if (buffer.trim() !== \"\") {\n const frame = parseFrame(buffer);\n if (frame) {\n yield frame;\n }\n }\n } finally {\n await reader.cancel().catch(() => {});\n }\n}\n"],"mappings":";;AAGA,SAAS,WAAW,MAAgC;CAClD,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,gBAAuB,oBAAoB,EACzC,KACA,MACA,UACkD;CAClD,MAAM,MAAM,MAAM,SAAS,KAAK;EAC9B,QAAQ;EACR;EACA,SAAS,EAAE,QAAQ,uBAAuB;EAC1C,MAAM,KAAK,UAAU,IAAI;EACzB,cAAc;CAChB,CAAC;CAED,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAClB,MAAM,IAAI,MAAM,uBAAuB,IAAI,OAAO,EAAE;CAGtD,OAAO,iBAAiB,IAAI,IAAI;AAClC;AAEA,SAAgB,gCAAgC,QAAsB;CACpE,OAAO,gBAAgB,0BAA0B,EAC/C,KACA,MACA,UACkD;EAClD,MAAM,MAAM,MAAM,YAAY,KAAK;GACjC;GACA,QAAQ;GACR;GACA,SAAS;IACP,QAAQ;IACR,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU,IAAI;GACzB,cAAc;EAChB,CAAC;EAED,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAClB,MAAM,IAAI,MAAM,uBAAuB,IAAI,OAAO,EAAE;EAGtD,OAAO,iBAAiB,IAAI,IAAI;CAClC;AACF;AAEA,gBAAgB,iBAAiB,MAA6D;CAC5F,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,SAAS;CAEb,IAAI;EACF,SAAS;GACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MACF;GAGF,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;GAChD,MAAM,QAAQ,OAAO,MAAM,IAAI;GAC/B,SAAS,MAAM,IAAI,KAAK;GAExB,KAAK,MAAM,QAAQ,OAAO;IACxB,IAAI,KAAK,KAAK,MAAM,IAClB;IAGF,MAAM,QAAQ,WAAW,IAAI;IAC7B,IAAI,OACF,MAAM;GAEV;EACF;EAEA,UAAU,QAAQ,OAAO;EACzB,IAAI,OAAO,KAAK,MAAM,IAAI;GACxB,MAAM,QAAQ,WAAW,MAAM;GAC/B,IAAI,OACF,MAAM;EAEV;CACF,UAAU;EACR,MAAM,OAAO,OAAO,EAAE,YAAY,CAAC,CAAC;CACtC;AACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Node } from '../core/types';
|
|
2
|
+
import { ChatMessage as GeneratedChatMessage, ChatRole } from '../types/generated';
|
|
3
|
+
export type { ChatRole };
|
|
4
|
+
/**
|
|
5
|
+
* The generated message shape, but `parts` is widened from the generated
|
|
6
|
+
* `ChatNode` union to open `Node`s — a part is just a component node rendered by
|
|
7
|
+
* `type`, so a message can also carry a consumer's custom part.
|
|
8
|
+
*/
|
|
9
|
+
export type ChatMessage = Omit<GeneratedChatMessage, "parts"> & {
|
|
10
|
+
parts: Node[];
|
|
11
|
+
};
|
|
12
|
+
export type ChatStatus = "idle" | "submitted" | "streaming" | "error";
|
|
13
|
+
export type ChatFrame = {
|
|
14
|
+
type: "text";
|
|
15
|
+
value: string;
|
|
16
|
+
} | {
|
|
17
|
+
type: "part";
|
|
18
|
+
part: Node;
|
|
19
|
+
} | {
|
|
20
|
+
type: "done";
|
|
21
|
+
} | {
|
|
22
|
+
type: "error";
|
|
23
|
+
message?: string;
|
|
24
|
+
};
|
|
25
|
+
export type ChatTransportRequest = {
|
|
26
|
+
url: string;
|
|
27
|
+
body: unknown;
|
|
28
|
+
signal: AbortSignal;
|
|
29
|
+
};
|
|
30
|
+
export type ChatTransport = (request: ChatTransportRequest) => AsyncGenerator<ChatFrame>;
|
|
31
|
+
export type UseChatReturn = {
|
|
32
|
+
messages: ChatMessage[];
|
|
33
|
+
status: ChatStatus;
|
|
34
|
+
error: string | null;
|
|
35
|
+
sendMessage: (text: string) => void;
|
|
36
|
+
setMessages: (messages: ChatMessage[]) => void;
|
|
37
|
+
stop: () => void;
|
|
38
|
+
regenerate: () => void;
|
|
39
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ChatFrame, ChatMessage, ChatTransport, UseChatReturn } from './types';
|
|
2
|
+
export type UseChatOptions = {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
transport?: ChatTransport;
|
|
5
|
+
initialMessages?: ChatMessage[];
|
|
6
|
+
generateId?: () => string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Pure reducer for a single streamed frame: a `text` frame appends to the open
|
|
10
|
+
* assistant text part (opening one if the last part is not text); a `part` frame
|
|
11
|
+
* pushes a complete part, which closes the open text part. Operates immutably on
|
|
12
|
+
* the last assistant message.
|
|
13
|
+
*/
|
|
14
|
+
export declare function foldFrame(messages: ChatMessage[], frame: ChatFrame): ChatMessage[];
|
|
15
|
+
export declare function useChat({ endpoint, transport, initialMessages, generateId, }: UseChatOptions): UseChatReturn;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { ndjsonChatTransport } from "./transport.js";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
//#region resources/js/chat/use-chat.ts
|
|
4
|
+
var idCounter = 0;
|
|
5
|
+
function defaultGenerateId() {
|
|
6
|
+
idCounter += 1;
|
|
7
|
+
return `chat-${idCounter}`;
|
|
8
|
+
}
|
|
9
|
+
function isAbortError(error) {
|
|
10
|
+
return error instanceof DOMException && error.name === "AbortError";
|
|
11
|
+
}
|
|
12
|
+
function errorMessage(error) {
|
|
13
|
+
return error instanceof Error ? error.message : String(error);
|
|
14
|
+
}
|
|
15
|
+
function lastIsAssistant(messages) {
|
|
16
|
+
const last = messages.at(-1);
|
|
17
|
+
return last !== void 0 && last.role === "assistant";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Pure reducer for a single streamed frame: a `text` frame appends to the open
|
|
21
|
+
* assistant text part (opening one if the last part is not text); a `part` frame
|
|
22
|
+
* pushes a complete part, which closes the open text part. Operates immutably on
|
|
23
|
+
* the last assistant message.
|
|
24
|
+
*/
|
|
25
|
+
function foldFrame(messages, frame) {
|
|
26
|
+
if (!lastIsAssistant(messages)) return messages;
|
|
27
|
+
const index = messages.length - 1;
|
|
28
|
+
const target = messages[index];
|
|
29
|
+
if (frame.type === "text") {
|
|
30
|
+
const parts = [...target.parts];
|
|
31
|
+
const openIndex = parts.length - 1;
|
|
32
|
+
const open = parts[openIndex];
|
|
33
|
+
if (open !== void 0 && open.type === "chat.part.text") parts[openIndex] = {
|
|
34
|
+
type: "chat.part.text",
|
|
35
|
+
props: { text: `${open.props.text}${frame.value}` }
|
|
36
|
+
};
|
|
37
|
+
else parts.push({
|
|
38
|
+
type: "chat.part.text",
|
|
39
|
+
props: { text: frame.value }
|
|
40
|
+
});
|
|
41
|
+
const next = [...messages];
|
|
42
|
+
next[index] = {
|
|
43
|
+
...target,
|
|
44
|
+
parts
|
|
45
|
+
};
|
|
46
|
+
return next;
|
|
47
|
+
}
|
|
48
|
+
if (frame.type === "part") {
|
|
49
|
+
const next = [...messages];
|
|
50
|
+
next[index] = {
|
|
51
|
+
...target,
|
|
52
|
+
parts: [...target.parts, frame.part]
|
|
53
|
+
};
|
|
54
|
+
return next;
|
|
55
|
+
}
|
|
56
|
+
return messages;
|
|
57
|
+
}
|
|
58
|
+
function useChat({ endpoint, transport = ndjsonChatTransport, initialMessages = [], generateId = defaultGenerateId }) {
|
|
59
|
+
const [messages, setMessagesState] = useState(initialMessages);
|
|
60
|
+
const [status, setStatus] = useState("idle");
|
|
61
|
+
const [error, setError] = useState(null);
|
|
62
|
+
const abortRef = useRef(null);
|
|
63
|
+
const lastUserTextRef = useRef(null);
|
|
64
|
+
const messagesRef = useRef(messages);
|
|
65
|
+
messagesRef.current = messages;
|
|
66
|
+
const commitMessages = useCallback((next) => {
|
|
67
|
+
messagesRef.current = next;
|
|
68
|
+
setMessagesState(next);
|
|
69
|
+
}, []);
|
|
70
|
+
const runTurn = useCallback(async (history, userText) => {
|
|
71
|
+
abortRef.current?.abort();
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
abortRef.current = controller;
|
|
74
|
+
setError(null);
|
|
75
|
+
setStatus("submitted");
|
|
76
|
+
const userMessage = {
|
|
77
|
+
id: generateId(),
|
|
78
|
+
role: "user",
|
|
79
|
+
parts: [{
|
|
80
|
+
type: "chat.part.text",
|
|
81
|
+
props: { text: userText }
|
|
82
|
+
}]
|
|
83
|
+
};
|
|
84
|
+
const assistantMessage = {
|
|
85
|
+
id: generateId(),
|
|
86
|
+
role: "assistant",
|
|
87
|
+
parts: []
|
|
88
|
+
};
|
|
89
|
+
commitMessages([
|
|
90
|
+
...history,
|
|
91
|
+
userMessage,
|
|
92
|
+
assistantMessage
|
|
93
|
+
]);
|
|
94
|
+
setStatus("streaming");
|
|
95
|
+
try {
|
|
96
|
+
for await (const frame of transport({
|
|
97
|
+
url: endpoint,
|
|
98
|
+
body: { message: userText },
|
|
99
|
+
signal: controller.signal
|
|
100
|
+
})) {
|
|
101
|
+
if (frame.type === "done") {
|
|
102
|
+
setStatus("idle");
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (frame.type === "error") {
|
|
106
|
+
setError(frame.message ?? "Chat failed");
|
|
107
|
+
setStatus("error");
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
commitMessages(foldFrame(messagesRef.current, frame));
|
|
111
|
+
}
|
|
112
|
+
setStatus((current) => current === "streaming" || current === "submitted" ? "idle" : current);
|
|
113
|
+
} catch (caught) {
|
|
114
|
+
if (isAbortError(caught)) {
|
|
115
|
+
setStatus("idle");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
setError(errorMessage(caught));
|
|
119
|
+
setStatus("error");
|
|
120
|
+
}
|
|
121
|
+
}, [
|
|
122
|
+
commitMessages,
|
|
123
|
+
endpoint,
|
|
124
|
+
generateId,
|
|
125
|
+
transport
|
|
126
|
+
]);
|
|
127
|
+
const sendMessage = useCallback((text) => {
|
|
128
|
+
const trimmed = text.trim();
|
|
129
|
+
if (trimmed === "") return;
|
|
130
|
+
lastUserTextRef.current = trimmed;
|
|
131
|
+
runTurn(messagesRef.current, trimmed);
|
|
132
|
+
}, [runTurn]);
|
|
133
|
+
const stop = useCallback(() => {
|
|
134
|
+
abortRef.current?.abort();
|
|
135
|
+
setStatus("idle");
|
|
136
|
+
}, []);
|
|
137
|
+
const regenerate = useCallback(() => {
|
|
138
|
+
const userText = lastUserTextRef.current;
|
|
139
|
+
if (userText === null) return;
|
|
140
|
+
const current = messagesRef.current;
|
|
141
|
+
runTurn(lastIsAssistant(current) ? current.slice(0, -2) : current, userText);
|
|
142
|
+
}, [runTurn]);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
return () => {
|
|
145
|
+
abortRef.current?.abort();
|
|
146
|
+
};
|
|
147
|
+
}, []);
|
|
148
|
+
return {
|
|
149
|
+
messages,
|
|
150
|
+
status,
|
|
151
|
+
error,
|
|
152
|
+
sendMessage,
|
|
153
|
+
setMessages: commitMessages,
|
|
154
|
+
stop,
|
|
155
|
+
regenerate
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
export { foldFrame, useChat };
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=use-chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-chat.js","names":[],"sources":["../../resources/js/chat/use-chat.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { ndjsonChatTransport } from \"./transport\";\nimport type { ChatFrame, ChatMessage, ChatStatus, ChatTransport, UseChatReturn } from \"./types\";\n\nexport type UseChatOptions = {\n endpoint: string;\n transport?: ChatTransport;\n initialMessages?: ChatMessage[];\n generateId?: () => string;\n};\n\nlet idCounter = 0;\n\nfunction defaultGenerateId(): string {\n idCounter += 1;\n\n return `chat-${idCounter}`;\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction lastIsAssistant(messages: ChatMessage[]): boolean {\n const last = messages.at(-1);\n\n return last !== undefined && last.role === \"assistant\";\n}\n\n/**\n * Pure reducer for a single streamed frame: a `text` frame appends to the open\n * assistant text part (opening one if the last part is not text); a `part` frame\n * pushes a complete part, which closes the open text part. Operates immutably on\n * the last assistant message.\n */\nexport function foldFrame(messages: ChatMessage[], frame: ChatFrame): ChatMessage[] {\n if (!lastIsAssistant(messages)) {\n return messages;\n }\n\n const index = messages.length - 1;\n const target = messages[index];\n\n if (frame.type === \"text\") {\n const parts = [...target.parts];\n const openIndex = parts.length - 1;\n const open = parts[openIndex];\n\n if (open !== undefined && open.type === \"chat.part.text\") {\n const openText = (open.props as { text: string }).text;\n parts[openIndex] = { type: \"chat.part.text\", props: { text: `${openText}${frame.value}` } };\n } else {\n parts.push({ type: \"chat.part.text\", props: { text: frame.value } });\n }\n\n const next = [...messages];\n next[index] = { ...target, parts };\n\n return next;\n }\n\n if (frame.type === \"part\") {\n const next = [...messages];\n next[index] = { ...target, parts: [...target.parts, frame.part] };\n\n return next;\n }\n\n return messages;\n}\n\nexport function useChat({\n endpoint,\n transport = ndjsonChatTransport,\n initialMessages = [],\n generateId = defaultGenerateId,\n}: UseChatOptions): UseChatReturn {\n const [messages, setMessagesState] = useState<ChatMessage[]>(initialMessages);\n const [status, setStatus] = useState<ChatStatus>(\"idle\");\n const [error, setError] = useState<string | null>(null);\n\n const abortRef = useRef<AbortController | null>(null);\n const lastUserTextRef = useRef<string | null>(null);\n const messagesRef = useRef<ChatMessage[]>(messages);\n messagesRef.current = messages;\n\n const commitMessages = useCallback((next: ChatMessage[]): void => {\n messagesRef.current = next;\n setMessagesState(next);\n }, []);\n\n const runTurn = useCallback(\n async (history: ChatMessage[], userText: string): Promise<void> => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setError(null);\n setStatus(\"submitted\");\n\n const userMessage: ChatMessage = {\n id: generateId(),\n role: \"user\",\n parts: [{ type: \"chat.part.text\", props: { text: userText } }],\n };\n const assistantMessage: ChatMessage = { id: generateId(), role: \"assistant\", parts: [] };\n commitMessages([...history, userMessage, assistantMessage]);\n setStatus(\"streaming\");\n\n try {\n for await (const frame of transport({\n url: endpoint,\n body: { message: userText },\n signal: controller.signal,\n })) {\n if (frame.type === \"done\") {\n setStatus(\"idle\");\n continue;\n }\n\n if (frame.type === \"error\") {\n setError(frame.message ?? \"Chat failed\");\n setStatus(\"error\");\n continue;\n }\n\n commitMessages(foldFrame(messagesRef.current, frame));\n }\n\n setStatus((current) =>\n current === \"streaming\" || current === \"submitted\" ? \"idle\" : current,\n );\n } catch (caught) {\n if (isAbortError(caught)) {\n setStatus(\"idle\");\n\n return;\n }\n\n setError(errorMessage(caught));\n setStatus(\"error\");\n }\n },\n [commitMessages, endpoint, generateId, transport],\n );\n\n const sendMessage = useCallback(\n (text: string): void => {\n const trimmed = text.trim();\n if (trimmed === \"\") {\n return;\n }\n\n lastUserTextRef.current = trimmed;\n void runTurn(messagesRef.current, trimmed);\n },\n [runTurn],\n );\n\n const stop = useCallback((): void => {\n abortRef.current?.abort();\n setStatus(\"idle\");\n }, []);\n\n const regenerate = useCallback((): void => {\n const userText = lastUserTextRef.current;\n if (userText === null) {\n return;\n }\n\n const current = messagesRef.current;\n const history = lastIsAssistant(current) ? current.slice(0, -2) : current;\n void runTurn(history, userText);\n }, [runTurn]);\n\n useEffect(() => {\n return () => {\n abortRef.current?.abort();\n };\n }, []);\n\n return { messages, status, error, sendMessage, setMessages: commitMessages, stop, regenerate };\n}\n"],"mappings":";;;AAWA,IAAI,YAAY;AAEhB,SAAS,oBAA4B;CACnC,aAAa;CAEb,OAAO,QAAQ;AACjB;AAEA,SAAS,aAAa,OAAyB;CAC7C,OAAO,iBAAiB,gBAAgB,MAAM,SAAS;AACzD;AAEA,SAAS,aAAa,OAAwB;CAC5C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,SAAS,gBAAgB,UAAkC;CACzD,MAAM,OAAO,SAAS,GAAG,EAAE;CAE3B,OAAO,SAAS,KAAA,KAAa,KAAK,SAAS;AAC7C;;;;;;;AAQA,SAAgB,UAAU,UAAyB,OAAiC;CAClF,IAAI,CAAC,gBAAgB,QAAQ,GAC3B,OAAO;CAGT,MAAM,QAAQ,SAAS,SAAS;CAChC,MAAM,SAAS,SAAS;CAExB,IAAI,MAAM,SAAS,QAAQ;EACzB,MAAM,QAAQ,CAAC,GAAG,OAAO,KAAK;EAC9B,MAAM,YAAY,MAAM,SAAS;EACjC,MAAM,OAAO,MAAM;EAEnB,IAAI,SAAS,KAAA,KAAa,KAAK,SAAS,kBAEtC,MAAM,aAAa;GAAE,MAAM;GAAkB,OAAO,EAAE,MAAM,GAD1C,KAAK,MAA2B,OACwB,MAAM,QAAQ;EAAE;OAE1F,MAAM,KAAK;GAAE,MAAM;GAAkB,OAAO,EAAE,MAAM,MAAM,MAAM;EAAE,CAAC;EAGrE,MAAM,OAAO,CAAC,GAAG,QAAQ;EACzB,KAAK,SAAS;GAAE,GAAG;GAAQ;EAAM;EAEjC,OAAO;CACT;CAEA,IAAI,MAAM,SAAS,QAAQ;EACzB,MAAM,OAAO,CAAC,GAAG,QAAQ;EACzB,KAAK,SAAS;GAAE,GAAG;GAAQ,OAAO,CAAC,GAAG,OAAO,OAAO,MAAM,IAAI;EAAE;EAEhE,OAAO;CACT;CAEA,OAAO;AACT;AAEA,SAAgB,QAAQ,EACtB,UACA,YAAY,qBACZ,kBAAkB,CAAC,GACnB,aAAa,qBACmB;CAChC,MAAM,CAAC,UAAU,oBAAoB,SAAwB,eAAe;CAC5E,MAAM,CAAC,QAAQ,aAAa,SAAqB,MAAM;CACvD,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CAEtD,MAAM,WAAW,OAA+B,IAAI;CACpD,MAAM,kBAAkB,OAAsB,IAAI;CAClD,MAAM,cAAc,OAAsB,QAAQ;CAClD,YAAY,UAAU;CAEtB,MAAM,iBAAiB,aAAa,SAA8B;EAChE,YAAY,UAAU;EACtB,iBAAiB,IAAI;CACvB,GAAG,CAAC,CAAC;CAEL,MAAM,UAAU,YACd,OAAO,SAAwB,aAAoC;EACjE,SAAS,SAAS,MAAM;EACxB,MAAM,aAAa,IAAI,gBAAgB;EACvC,SAAS,UAAU;EAEnB,SAAS,IAAI;EACb,UAAU,WAAW;EAErB,MAAM,cAA2B;GAC/B,IAAI,WAAW;GACf,MAAM;GACN,OAAO,CAAC;IAAE,MAAM;IAAkB,OAAO,EAAE,MAAM,SAAS;GAAE,CAAC;EAC/D;EACA,MAAM,mBAAgC;GAAE,IAAI,WAAW;GAAG,MAAM;GAAa,OAAO,CAAC;EAAE;EACvF,eAAe;GAAC,GAAG;GAAS;GAAa;EAAgB,CAAC;EAC1D,UAAU,WAAW;EAErB,IAAI;GACF,WAAW,MAAM,SAAS,UAAU;IAClC,KAAK;IACL,MAAM,EAAE,SAAS,SAAS;IAC1B,QAAQ,WAAW;GACrB,CAAC,GAAG;IACF,IAAI,MAAM,SAAS,QAAQ;KACzB,UAAU,MAAM;KAChB;IACF;IAEA,IAAI,MAAM,SAAS,SAAS;KAC1B,SAAS,MAAM,WAAW,aAAa;KACvC,UAAU,OAAO;KACjB;IACF;IAEA,eAAe,UAAU,YAAY,SAAS,KAAK,CAAC;GACtD;GAEA,WAAW,YACT,YAAY,eAAe,YAAY,cAAc,SAAS,OAChE;EACF,SAAS,QAAQ;GACf,IAAI,aAAa,MAAM,GAAG;IACxB,UAAU,MAAM;IAEhB;GACF;GAEA,SAAS,aAAa,MAAM,CAAC;GAC7B,UAAU,OAAO;EACnB;CACF,GACA;EAAC;EAAgB;EAAU;EAAY;CAAS,CAClD;CAEA,MAAM,cAAc,aACjB,SAAuB;EACtB,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,YAAY,IACd;EAGF,gBAAgB,UAAU;EAC1B,QAAa,YAAY,SAAS,OAAO;CAC3C,GACA,CAAC,OAAO,CACV;CAEA,MAAM,OAAO,kBAAwB;EACnC,SAAS,SAAS,MAAM;EACxB,UAAU,MAAM;CAClB,GAAG,CAAC,CAAC;CAEL,MAAM,aAAa,kBAAwB;EACzC,MAAM,WAAW,gBAAgB;EACjC,IAAI,aAAa,MACf;EAGF,MAAM,UAAU,YAAY;EAE5B,QADgB,gBAAgB,OAAO,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI,SAC5C,QAAQ;CAChC,GAAG,CAAC,OAAO,CAAC;CAEZ,gBAAgB;EACd,aAAa;GACX,SAAS,SAAS,MAAM;EAC1B;CACF,GAAG,CAAC,CAAC;CAEL,OAAO;EAAE;EAAU;EAAQ;EAAO;EAAa,aAAa;EAAgB;EAAM;CAAW;AAC/F"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BrowserToken as GeneratedBrowserToken, RemoteAccess as GeneratedRemoteAccess } from '../types/generated';
|
|
2
|
+
export declare class ApiError extends Error {
|
|
3
|
+
readonly response: Response;
|
|
4
|
+
constructor(response: Response);
|
|
5
|
+
}
|
|
6
|
+
export type ApiInit = Omit<RequestInit, "headers"> & {
|
|
7
|
+
ref?: string;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
throwOnError?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type BrowserToken = GeneratedBrowserToken;
|
|
12
|
+
export type RemoteAccess = GeneratedRemoteAccess;
|
|
13
|
+
export type RemoteInit = Omit<RequestInit, "credentials" | "headers"> & {
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
remote: RemoteAccess;
|
|
16
|
+
retryOnUnauthorized?: boolean;
|
|
17
|
+
throwOnError?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare function apiFetch(url: string, init?: ApiInit): Promise<Response>;
|
|
20
|
+
export declare function apiJson<T>(url: string, init?: ApiInit): Promise<T>;
|
|
21
|
+
export declare function clearRemoteTokenCache(): void;
|
|
22
|
+
export declare function invalidateRemoteToken(remote: RemoteAccess): void;
|
|
23
|
+
export declare function remoteToken(remote: RemoteAccess): Promise<BrowserToken>;
|
|
24
|
+
export declare function remoteFetch(url: string, init: RemoteInit): Promise<Response>;
|
|
25
|
+
export declare function remoteJson<T>(url: string, init: RemoteInit): Promise<T>;
|