@joel_jsp/vortex-ui 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +48 -2
- package/ai/README.md +18 -0
- package/ai/ai-assistant-dialog.component.d.ts +25 -0
- package/ai/ai-prompt-editor.component.d.ts +14 -0
- package/ai/ai.types.d.ts +109 -0
- package/ai/ai.utils.d.ts +3 -0
- package/ai/index.d.ts +8 -0
- package/ai/mock-ai-integration.d.ts +8 -0
- package/ai/vortex-ui-ai.d.ts +5 -0
- package/esm2022/ai/ai-assistant-dialog.component.js +226 -0
- package/esm2022/ai/ai-assistant-dialog.component.js.map +1 -0
- package/esm2022/ai/ai-prompt-editor.component.js +112 -0
- package/esm2022/ai/ai-prompt-editor.component.js.map +1 -0
- package/esm2022/ai/ai.types.js +2 -0
- package/esm2022/ai/ai.types.js.map +1 -0
- package/esm2022/ai/ai.utils.js +24 -0
- package/esm2022/ai/ai.utils.js.map +1 -0
- package/esm2022/ai/index.js +9 -0
- package/esm2022/ai/index.js.map +1 -0
- package/esm2022/ai/mock-ai-integration.js +169 -0
- package/esm2022/ai/mock-ai-integration.js.map +1 -0
- package/esm2022/ai/vortex-ui-ai.js +5 -0
- package/esm2022/ai/vortex-ui-ai.js.map +1 -0
- package/esm2022/index.js +4 -0
- package/esm2022/index.js.map +1 -0
- package/esm2022/lib/components/accordion/accordion.component.js +104 -0
- package/esm2022/lib/components/accordion/accordion.component.js.map +1 -0
- package/esm2022/lib/components/action-sheet/action-sheet.component.js +91 -0
- package/esm2022/lib/components/action-sheet/action-sheet.component.js.map +1 -0
- package/esm2022/lib/components/autocomplete/autocomplete.component.js +79 -0
- package/esm2022/lib/components/autocomplete/autocomplete.component.js.map +1 -0
- package/esm2022/lib/components/box/box.component.js +49 -0
- package/esm2022/lib/components/box/box.component.js.map +1 -0
- package/esm2022/lib/components/bullet/bullet.component.js +74 -0
- package/esm2022/lib/components/bullet/bullet.component.js.map +1 -0
- package/esm2022/lib/components/button/button.component.js +57 -0
- package/esm2022/lib/components/button/button.component.js.map +1 -0
- package/esm2022/lib/components/button-group/button-group.component.js +48 -0
- package/esm2022/lib/components/button-group/button-group.component.js.map +1 -0
- package/esm2022/lib/components/calendar/calendar.component.js +104 -0
- package/esm2022/lib/components/calendar/calendar.component.js.map +1 -0
- package/esm2022/lib/components/card/card.component.js +52 -0
- package/esm2022/lib/components/card/card.component.js.map +1 -0
- package/esm2022/lib/components/card-view/card-view.component.js +160 -0
- package/esm2022/lib/components/card-view/card-view.component.js.map +1 -0
- package/esm2022/lib/components/chart/chart.component.js +471 -0
- package/esm2022/lib/components/chart/chart.component.js.map +1 -0
- package/esm2022/lib/components/chat/chat.component.js +75 -0
- package/esm2022/lib/components/chat/chat.component.js.map +1 -0
- package/esm2022/lib/components/checkbox/checkbox.component.js +48 -0
- package/esm2022/lib/components/checkbox/checkbox.component.js.map +1 -0
- package/esm2022/lib/components/color-box/color-box.component.js +36 -0
- package/esm2022/lib/components/color-box/color-box.component.js.map +1 -0
- package/esm2022/lib/components/context-menu/context-menu.component.js +76 -0
- package/esm2022/lib/components/context-menu/context-menu.component.js.map +1 -0
- package/esm2022/lib/components/data-grid/data-grid.component.js +1203 -0
- package/esm2022/lib/components/data-grid/data-grid.component.js.map +1 -0
- package/esm2022/lib/components/data-grid/data-grid.types.js +2 -0
- package/esm2022/lib/components/data-grid/data-grid.types.js.map +1 -0
- package/esm2022/lib/components/data-grid/data-source.js +121 -0
- package/esm2022/lib/components/data-grid/data-source.js.map +1 -0
- package/esm2022/lib/components/data-grid/export-csv.js +28 -0
- package/esm2022/lib/components/data-grid/export-csv.js.map +1 -0
- package/esm2022/lib/components/date-box/date-box.component.js +67 -0
- package/esm2022/lib/components/date-box/date-box.component.js.map +1 -0
- package/esm2022/lib/components/date-range-box/date-range-box.component.js +48 -0
- package/esm2022/lib/components/date-range-box/date-range-box.component.js.map +1 -0
- package/esm2022/lib/components/diagram/diagram.component.js +103 -0
- package/esm2022/lib/components/diagram/diagram.component.js.map +1 -0
- package/esm2022/lib/components/dialog/dialog.component.js +93 -0
- package/esm2022/lib/components/dialog/dialog.component.js.map +1 -0
- package/esm2022/lib/components/drawer/drawer.component.js +98 -0
- package/esm2022/lib/components/drawer/drawer.component.js.map +1 -0
- package/esm2022/lib/components/drop-down-button/drop-down-button.component.js +71 -0
- package/esm2022/lib/components/drop-down-button/drop-down-button.component.js.map +1 -0
- package/esm2022/lib/components/fab/fab.component.js +46 -0
- package/esm2022/lib/components/fab/fab.component.js.map +1 -0
- package/esm2022/lib/components/field-set/field-set.component.js +25 -0
- package/esm2022/lib/components/field-set/field-set.component.js.map +1 -0
- package/esm2022/lib/components/file-manager/file-manager.component.js +862 -0
- package/esm2022/lib/components/file-manager/file-manager.component.js.map +1 -0
- package/esm2022/lib/components/file-manager/file-manager.types.js +2 -0
- package/esm2022/lib/components/file-manager/file-manager.types.js.map +1 -0
- package/esm2022/lib/components/file-uploader/file-uploader.component.js +43 -0
- package/esm2022/lib/components/file-uploader/file-uploader.component.js.map +1 -0
- package/esm2022/lib/components/filter-builder/filter-builder.component.js +261 -0
- package/esm2022/lib/components/filter-builder/filter-builder.component.js.map +1 -0
- package/esm2022/lib/components/form/form.component.js +54 -0
- package/esm2022/lib/components/form/form.component.js.map +1 -0
- package/esm2022/lib/components/funnel/funnel.component.js +85 -0
- package/esm2022/lib/components/funnel/funnel.component.js.map +1 -0
- package/esm2022/lib/components/gallery/gallery.component.js +54 -0
- package/esm2022/lib/components/gallery/gallery.component.js.map +1 -0
- package/esm2022/lib/components/gantt/gantt.component.js +130 -0
- package/esm2022/lib/components/gantt/gantt.component.js.map +1 -0
- package/esm2022/lib/components/gauge/bar-gauge.component.js +59 -0
- package/esm2022/lib/components/gauge/bar-gauge.component.js.map +1 -0
- package/esm2022/lib/components/gauge/circular-gauge.component.js +68 -0
- package/esm2022/lib/components/gauge/circular-gauge.component.js.map +1 -0
- package/esm2022/lib/components/gauge/linear-gauge.component.js +54 -0
- package/esm2022/lib/components/gauge/linear-gauge.component.js.map +1 -0
- package/esm2022/lib/components/html-editor/html-editor.component.js +186 -0
- package/esm2022/lib/components/html-editor/html-editor.component.js.map +1 -0
- package/esm2022/lib/components/index.js +73 -0
- package/esm2022/lib/components/index.js.map +1 -0
- package/esm2022/lib/components/list/list.component.js +121 -0
- package/esm2022/lib/components/list/list.component.js.map +1 -0
- package/esm2022/lib/components/load-panel/load-panel.component.js +31 -0
- package/esm2022/lib/components/load-panel/load-panel.component.js.map +1 -0
- package/esm2022/lib/components/lookup/lookup.component.js +66 -0
- package/esm2022/lib/components/lookup/lookup.component.js.map +1 -0
- package/esm2022/lib/components/map/map.component.js +73 -0
- package/esm2022/lib/components/map/map.component.js.map +1 -0
- package/esm2022/lib/components/map/vector-map.component.js +47 -0
- package/esm2022/lib/components/map/vector-map.component.js.map +1 -0
- package/esm2022/lib/components/menu/menu.component.js +107 -0
- package/esm2022/lib/components/menu/menu.component.js.map +1 -0
- package/esm2022/lib/components/multi-view/multi-view.component.js +82 -0
- package/esm2022/lib/components/multi-view/multi-view.component.js.map +1 -0
- package/esm2022/lib/components/number-box/number-box.component.js +57 -0
- package/esm2022/lib/components/number-box/number-box.component.js.map +1 -0
- package/esm2022/lib/components/pagination/pagination.component.js +62 -0
- package/esm2022/lib/components/pagination/pagination.component.js.map +1 -0
- package/esm2022/lib/components/pivot-grid/pivot-field-chooser.component.js +60 -0
- package/esm2022/lib/components/pivot-grid/pivot-field-chooser.component.js.map +1 -0
- package/esm2022/lib/components/pivot-grid/pivot-grid.component.js +102 -0
- package/esm2022/lib/components/pivot-grid/pivot-grid.component.js.map +1 -0
- package/esm2022/lib/components/popover/popover.component.js +42 -0
- package/esm2022/lib/components/popover/popover.component.js.map +1 -0
- package/esm2022/lib/components/progress/index.js +3 -0
- package/esm2022/lib/components/progress/index.js.map +1 -0
- package/esm2022/lib/components/progress/load-indicator.component.js +33 -0
- package/esm2022/lib/components/progress/load-indicator.component.js.map +1 -0
- package/esm2022/lib/components/progress/progress-bar.component.js +63 -0
- package/esm2022/lib/components/progress/progress-bar.component.js.map +1 -0
- package/esm2022/lib/components/radio-group/radio-group.component.js +42 -0
- package/esm2022/lib/components/radio-group/radio-group.component.js.map +1 -0
- package/esm2022/lib/components/range-slider/range-slider.component.js +60 -0
- package/esm2022/lib/components/range-slider/range-slider.component.js.map +1 -0
- package/esm2022/lib/components/sankey/sankey.component.js +97 -0
- package/esm2022/lib/components/sankey/sankey.component.js.map +1 -0
- package/esm2022/lib/components/scheduler/scheduler.component.js +377 -0
- package/esm2022/lib/components/scheduler/scheduler.component.js.map +1 -0
- package/esm2022/lib/components/scroll-view/scroll-view.component.js +25 -0
- package/esm2022/lib/components/scroll-view/scroll-view.component.js.map +1 -0
- package/esm2022/lib/components/select-box/select-box.component.js +77 -0
- package/esm2022/lib/components/select-box/select-box.component.js.map +1 -0
- package/esm2022/lib/components/slider/slider.component.js +59 -0
- package/esm2022/lib/components/slider/slider.component.js.map +1 -0
- package/esm2022/lib/components/sparkline/sparkline.component.js +95 -0
- package/esm2022/lib/components/sparkline/sparkline.component.js.map +1 -0
- package/esm2022/lib/components/splitter/splitter.component.js +123 -0
- package/esm2022/lib/components/splitter/splitter.component.js.map +1 -0
- package/esm2022/lib/components/stepper/stepper.component.js +120 -0
- package/esm2022/lib/components/stepper/stepper.component.js.map +1 -0
- package/esm2022/lib/components/switch/switch.component.js +54 -0
- package/esm2022/lib/components/switch/switch.component.js.map +1 -0
- package/esm2022/lib/components/tabs/tabs.component.js +118 -0
- package/esm2022/lib/components/tabs/tabs.component.js.map +1 -0
- package/esm2022/lib/components/tag-box/tag-box.component.js +75 -0
- package/esm2022/lib/components/tag-box/tag-box.component.js.map +1 -0
- package/esm2022/lib/components/text-area/text-area.component.js +50 -0
- package/esm2022/lib/components/text-area/text-area.component.js.map +1 -0
- package/esm2022/lib/components/text-box/text-box.component.js +64 -0
- package/esm2022/lib/components/text-box/text-box.component.js.map +1 -0
- package/esm2022/lib/components/tile-view/tile-view.component.js +34 -0
- package/esm2022/lib/components/tile-view/tile-view.component.js.map +1 -0
- package/esm2022/lib/components/toast/index.js +3 -0
- package/esm2022/lib/components/toast/index.js.map +1 -0
- package/esm2022/lib/components/toast/toast-outlet.component.js +35 -0
- package/esm2022/lib/components/toast/toast-outlet.component.js.map +1 -0
- package/esm2022/lib/components/toast/toast.service.js +42 -0
- package/esm2022/lib/components/toast/toast.service.js.map +1 -0
- package/esm2022/lib/components/toolbar/toolbar.component.js +37 -0
- package/esm2022/lib/components/toolbar/toolbar.component.js.map +1 -0
- package/esm2022/lib/components/tooltip/tooltip.component.js +21 -0
- package/esm2022/lib/components/tooltip/tooltip.component.js.map +1 -0
- package/esm2022/lib/components/tree-list/tree-list.component.js +166 -0
- package/esm2022/lib/components/tree-list/tree-list.component.js.map +1 -0
- package/esm2022/lib/components/tree-map/tree-map.component.js +94 -0
- package/esm2022/lib/components/tree-map/tree-map.component.js.map +1 -0
- package/esm2022/lib/components/tree-view/tree-view.component.js +140 -0
- package/esm2022/lib/components/tree-view/tree-view.component.js.map +1 -0
- package/esm2022/lib/components/validation/validation-summary.component.js +37 -0
- package/esm2022/lib/components/validation/validation-summary.component.js.map +1 -0
- package/esm2022/lib/components/wrappers/draggable.component.js +64 -0
- package/esm2022/lib/components/wrappers/draggable.component.js.map +1 -0
- package/esm2022/lib/components/wrappers/index.js +4 -0
- package/esm2022/lib/components/wrappers/index.js.map +1 -0
- package/esm2022/lib/components/wrappers/resizable.component.js +90 -0
- package/esm2022/lib/components/wrappers/resizable.component.js.map +1 -0
- package/esm2022/lib/components/wrappers/sortable.component.js +88 -0
- package/esm2022/lib/components/wrappers/sortable.component.js.map +1 -0
- package/esm2022/lib/core/index.js +3 -0
- package/esm2022/lib/core/index.js.map +1 -0
- package/esm2022/lib/core/theme/theme.service.js +84 -0
- package/esm2022/lib/core/theme/theme.service.js.map +1 -0
- package/esm2022/lib/core/validation/validators.js +53 -0
- package/esm2022/lib/core/validation/validators.js.map +1 -0
- package/esm2022/vortex-ui.js +5 -0
- package/esm2022/vortex-ui.js.map +1 -0
- package/index.d.ts +2 -0
- package/lib/components/accordion/accordion.component.d.ts +19 -0
- package/lib/components/action-sheet/action-sheet.component.d.ts +18 -0
- package/lib/components/autocomplete/autocomplete.component.d.ts +14 -0
- package/lib/components/box/box.component.d.ts +16 -0
- package/lib/components/bullet/bullet.component.d.ts +15 -0
- package/lib/components/button/button.component.d.ts +13 -0
- package/lib/components/button-group/button-group.component.d.ts +10 -0
- package/lib/components/calendar/calendar.component.d.ts +19 -0
- package/lib/components/card/card.component.d.ts +10 -0
- package/lib/components/card-view/card-view.component.d.ts +31 -0
- package/lib/components/chart/chart.component.d.ts +85 -0
- package/lib/components/chat/chat.component.d.ts +18 -0
- package/lib/components/checkbox/checkbox.component.d.ts +9 -0
- package/lib/components/color-box/color-box.component.d.ts +9 -0
- package/lib/components/context-menu/context-menu.component.d.ts +18 -0
- package/lib/components/data-grid/data-grid.component.d.ts +162 -0
- package/lib/components/data-grid/data-grid.types.d.ts +51 -0
- package/lib/components/data-grid/data-source.d.ts +41 -0
- package/lib/components/data-grid/export-csv.d.ts +3 -0
- package/lib/components/date-box/date-box.component.d.ts +14 -0
- package/lib/components/date-range-box/date-range-box.component.d.ts +16 -0
- package/lib/components/diagram/diagram.component.d.ts +30 -0
- package/lib/components/dialog/dialog.component.d.ts +19 -0
- package/lib/components/drawer/drawer.component.d.ts +16 -0
- package/lib/components/drop-down-button/drop-down-button.component.d.ts +11 -0
- package/lib/components/fab/fab.component.d.ts +10 -0
- package/lib/components/field-set/field-set.component.d.ts +7 -0
- package/lib/components/file-manager/file-manager.component.d.ts +86 -0
- package/lib/components/file-manager/file-manager.types.d.ts +33 -0
- package/lib/components/file-uploader/file-uploader.component.d.ts +12 -0
- package/lib/components/filter-builder/filter-builder.component.d.ts +38 -0
- package/lib/components/form/form.component.d.ts +9 -0
- package/lib/components/funnel/funnel.component.d.ts +21 -0
- package/lib/components/gallery/gallery.component.d.ts +15 -0
- package/lib/components/gantt/gantt.component.d.ts +29 -0
- package/lib/components/gauge/bar-gauge.component.d.ts +19 -0
- package/lib/components/gauge/circular-gauge.component.d.ts +12 -0
- package/lib/components/gauge/linear-gauge.component.d.ts +10 -0
- package/lib/components/html-editor/html-editor.component.d.ts +22 -0
- package/lib/components/index.d.ts +72 -0
- package/lib/components/list/list.component.d.ts +23 -0
- package/lib/components/load-panel/load-panel.component.d.ts +8 -0
- package/lib/components/lookup/lookup.component.d.ts +18 -0
- package/lib/components/map/map.component.d.ts +19 -0
- package/lib/components/map/vector-map.component.d.ts +16 -0
- package/lib/components/menu/menu.component.d.ts +17 -0
- package/lib/components/multi-view/multi-view.component.d.ts +16 -0
- package/lib/components/number-box/number-box.component.d.ts +12 -0
- package/lib/components/pagination/pagination.component.d.ts +12 -0
- package/lib/components/pivot-grid/pivot-field-chooser.component.d.ts +28 -0
- package/lib/components/pivot-grid/pivot-grid.component.d.ts +14 -0
- package/lib/components/popover/popover.component.d.ts +8 -0
- package/lib/components/progress/index.d.ts +2 -0
- package/lib/components/progress/load-indicator.component.d.ts +8 -0
- package/lib/components/progress/progress-bar.component.d.ts +10 -0
- package/lib/components/radio-group/radio-group.component.d.ts +11 -0
- package/lib/components/range-slider/range-slider.component.d.ts +18 -0
- package/lib/components/sankey/sankey.component.d.ts +34 -0
- package/lib/components/scheduler/scheduler.component.d.ts +55 -0
- package/lib/components/scroll-view/scroll-view.component.d.ts +8 -0
- package/lib/components/select-box/select-box.component.d.ts +20 -0
- package/lib/components/slider/slider.component.d.ts +14 -0
- package/lib/components/sparkline/sparkline.component.d.ts +13 -0
- package/lib/components/splitter/splitter.component.d.ts +13 -0
- package/lib/components/stepper/stepper.component.d.ts +18 -0
- package/lib/components/switch/switch.component.d.ts +9 -0
- package/lib/components/tabs/tabs.component.d.ts +28 -0
- package/lib/components/tag-box/tag-box.component.d.ts +13 -0
- package/lib/components/text-area/text-area.component.d.ts +11 -0
- package/lib/components/text-box/text-box.component.d.ts +13 -0
- package/lib/components/tile-view/tile-view.component.d.ts +13 -0
- package/lib/components/toast/index.d.ts +2 -0
- package/lib/components/toast/toast-outlet.component.d.ts +7 -0
- package/lib/components/toast/toast.service.d.ts +22 -0
- package/lib/components/toolbar/toolbar.component.d.ts +6 -0
- package/lib/components/tooltip/tooltip.component.d.ts +6 -0
- package/lib/components/tree-list/tree-list.component.d.ts +35 -0
- package/lib/components/tree-map/tree-map.component.d.ts +24 -0
- package/lib/components/tree-view/tree-view.component.d.ts +30 -0
- package/lib/components/validation/validation-summary.component.d.ts +7 -0
- package/lib/components/wrappers/draggable.component.d.ts +15 -0
- package/lib/components/wrappers/index.d.ts +3 -0
- package/lib/components/wrappers/resizable.component.d.ts +16 -0
- package/lib/components/wrappers/sortable.component.d.ts +18 -0
- package/lib/core/index.d.ts +2 -0
- package/lib/core/theme/theme.service.d.ts +33 -0
- package/lib/core/validation/validators.d.ts +12 -0
- package/package.json +58 -4
- package/src/lib/core/tokens/design-tokens.css +71 -0
- package/src/lib/styles/themes/dark.css +50 -0
- package/src/lib/styles/themes/fluent-blue-dark.css +77 -0
- package/src/lib/styles/themes/fluent-blue-light.css +79 -0
- package/src/lib/styles/themes/fluent-saas-dark.css +77 -0
- package/src/lib/styles/themes/fluent-saas-light.css +77 -0
- package/src/lib/styles/themes/fluent.css +5 -0
- package/src/lib/styles/themes/lds.css +78 -0
- package/src/lib/styles/themes/light.css +60 -0
- package/src/lib/styles/themes/material.css +55 -0
- package/src/lib/styles/vortex-ui.css +20 -0
- package/vortex-ui.d.ts +5 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joel Schecheleski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the Software), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,49 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @joel_jsp/vortex-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**VortexUI** — suite de componentes Angular 22+ (Standalone, Signals, zoneless-ready), com DataGrid de alta performance, AI opt-in e temas Fluent / LDS.
|
|
4
|
+
|
|
5
|
+
Repositório: [JoelSchecheleski/vortex-ui](https://github.com/JoelSchecheleski/vortex-ui)
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @joel_jsp/vortex-ui
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Uso
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { VxButtonComponent, VxDataGridComponent, VortexThemeService } from '@joel_jsp/vortex-ui';
|
|
17
|
+
import { VortexMockAIIntegration, VortexAIConfig } from '@joel_jsp/vortex-ui/ai';
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```css
|
|
21
|
+
@import '@joel_jsp/vortex-ui/styles/vortex-ui.css';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
inject(VortexThemeService).setTheme('fluent-blue-light');
|
|
26
|
+
// fluent-blue-dark | fluent-saas-light | fluent-saas-dark | lds
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Temas
|
|
30
|
+
|
|
31
|
+
| ID | Descrição |
|
|
32
|
+
|----|-----------|
|
|
33
|
+
| `fluent-blue-light` | Default (alias `fluent`) |
|
|
34
|
+
| `fluent-blue-dark` | Fluent Blue Dark |
|
|
35
|
+
| `fluent-saas-light` | Fluent SaaS Light |
|
|
36
|
+
| `fluent-saas-dark` | Fluent SaaS Dark |
|
|
37
|
+
| `lds` | LDS (Poppins) |
|
|
38
|
+
|
|
39
|
+
## Dev (monorepo)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm start # Widgets Gallery :4200
|
|
43
|
+
npm run build # build da lib
|
|
44
|
+
npm run storybook # Storybook
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
MIT
|
package/ai/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# vortex-ui/ai
|
|
2
|
+
|
|
3
|
+
Secondary entry point for VortexUI AI Foundation.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
VortexAIIntegration,
|
|
10
|
+
VortexMockAIIntegration,
|
|
11
|
+
VxAiAssistantDialogComponent,
|
|
12
|
+
VxAiPromptEditorComponent,
|
|
13
|
+
} from 'vortex-ui/ai';
|
|
14
|
+
// monorepo alias:
|
|
15
|
+
// import { ... } from '@vortex-ui/ai';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Keep this entry opt-in: apps that never enable AI should import only from `vortex-ui`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { VortexAIMessage, VortexAISuggestion } from './ai.types';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export interface VxAIChatMessage extends VortexAIMessage {
|
|
4
|
+
title?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class VxAIAssistantDialogComponent {
|
|
7
|
+
readonly open: import("@angular/core").ModelSignal<boolean>;
|
|
8
|
+
readonly title: import("@angular/core").InputSignal<string>;
|
|
9
|
+
readonly placeholder: import("@angular/core").InputSignal<string>;
|
|
10
|
+
readonly messages: import("@angular/core").InputSignal<VxAIChatMessage[]>;
|
|
11
|
+
readonly suggestions: import("@angular/core").InputSignal<VortexAISuggestion[]>;
|
|
12
|
+
readonly busy: import("@angular/core").InputSignal<boolean>;
|
|
13
|
+
readonly sendMessage: import("@angular/core").OutputEmitterRef<string>;
|
|
14
|
+
readonly suggestionSelect: import("@angular/core").OutputEmitterRef<VortexAISuggestion>;
|
|
15
|
+
readonly clear: import("@angular/core").OutputEmitterRef<void>;
|
|
16
|
+
readonly closed: import("@angular/core").OutputEmitterRef<void>;
|
|
17
|
+
draft: string;
|
|
18
|
+
close(): void;
|
|
19
|
+
onOverlayClick(_: MouseEvent): void;
|
|
20
|
+
onEnter(event: Event): void;
|
|
21
|
+
send(): void;
|
|
22
|
+
onSuggestion(s: VortexAISuggestion): void;
|
|
23
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<VxAIAssistantDialogComponent, never>;
|
|
24
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<VxAIAssistantDialogComponent, "vx-ai-assistant-dialog", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "messages": { "alias": "messages"; "required": false; "isSignal": true; }; "suggestions": { "alias": "suggestions"; "required": false; "isSignal": true; }; "busy": { "alias": "busy"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "sendMessage": "sendMessage"; "suggestionSelect": "suggestionSelect"; "clear": "clear"; "closed": "closed"; }, never, never, true, never>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class VxAIPromptEditorComponent {
|
|
3
|
+
readonly open: import("@angular/core").ModelSignal<boolean>;
|
|
4
|
+
readonly title: import("@angular/core").InputSignal<string>;
|
|
5
|
+
readonly placeholder: import("@angular/core").InputSignal<string>;
|
|
6
|
+
readonly prompt: import("@angular/core").ModelSignal<string>;
|
|
7
|
+
readonly canApply: import("@angular/core").InputSignal<boolean>;
|
|
8
|
+
readonly regenerate: import("@angular/core").OutputEmitterRef<string>;
|
|
9
|
+
readonly apply: import("@angular/core").OutputEmitterRef<string>;
|
|
10
|
+
readonly closed: import("@angular/core").OutputEmitterRef<void>;
|
|
11
|
+
close(): void;
|
|
12
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<VxAIPromptEditorComponent, never>;
|
|
13
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<VxAIPromptEditorComponent, "vx-ai-prompt-editor", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "prompt": { "alias": "prompt"; "required": false; "isSignal": true; }; "canApply": { "alias": "canApply"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "prompt": "promptChange"; "regenerate": "regenerate"; "apply": "apply"; "closed": "closed"; }, never, never, true, never>;
|
|
14
|
+
}
|
package/ai/ai.types.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/** Config opt-in por componente (deny by default). */
|
|
2
|
+
export interface VortexAIConfig {
|
|
3
|
+
enabled?: boolean;
|
|
4
|
+
assistant?: {
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
title?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Campos permitidos no contexto enviado ao modelo (R2). */
|
|
9
|
+
contextFields?: string[];
|
|
10
|
+
/** Cap de linhas no contexto (R3). Default: 100. */
|
|
11
|
+
maxRowsInContext?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface VortexAIMessage {
|
|
14
|
+
role: 'user' | 'assistant' | 'system';
|
|
15
|
+
content: string;
|
|
16
|
+
}
|
|
17
|
+
export interface VortexAIContext {
|
|
18
|
+
schemaVersion: 1;
|
|
19
|
+
componentType: string;
|
|
20
|
+
columns?: Array<{
|
|
21
|
+
field: string;
|
|
22
|
+
header: string;
|
|
23
|
+
}>;
|
|
24
|
+
sort?: {
|
|
25
|
+
field: string;
|
|
26
|
+
direction: 'asc' | 'desc';
|
|
27
|
+
} | null;
|
|
28
|
+
filter?: VortexAIFilterState | null;
|
|
29
|
+
/** Linhas já projetadas (só campos allowlist). */
|
|
30
|
+
rows?: Record<string, unknown>[];
|
|
31
|
+
totalRows?: number;
|
|
32
|
+
meta?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
export interface VortexAIFilterState {
|
|
35
|
+
field: string;
|
|
36
|
+
value: string;
|
|
37
|
+
operator?: 'contains' | 'equals';
|
|
38
|
+
}
|
|
39
|
+
export interface VortexAIToolDefinition {
|
|
40
|
+
name: string;
|
|
41
|
+
description: string;
|
|
42
|
+
parameters?: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
export interface VortexAIRequest {
|
|
45
|
+
feature: 'assistant' | 'ai-column' | 'smart-paste' | 'semantic-search' | 'editor' | string;
|
|
46
|
+
componentType: string;
|
|
47
|
+
componentId?: string;
|
|
48
|
+
messages?: VortexAIMessage[];
|
|
49
|
+
prompt?: string;
|
|
50
|
+
context: VortexAIContext;
|
|
51
|
+
tools?: VortexAIToolDefinition[];
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
}
|
|
54
|
+
export interface VortexAICommand {
|
|
55
|
+
type: string;
|
|
56
|
+
payload?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
export interface VortexAIResponse {
|
|
59
|
+
title?: string;
|
|
60
|
+
text: string;
|
|
61
|
+
commands?: VortexAICommand[];
|
|
62
|
+
/** Resultados estruturados (ex.: AI Column cells). */
|
|
63
|
+
data?: {
|
|
64
|
+
cells?: Array<{
|
|
65
|
+
rowKey: string;
|
|
66
|
+
value: string;
|
|
67
|
+
}>;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export type VortexAIStreamEvent = {
|
|
71
|
+
type: 'token';
|
|
72
|
+
text: string;
|
|
73
|
+
} | {
|
|
74
|
+
type: 'command';
|
|
75
|
+
command: VortexAICommand;
|
|
76
|
+
} | {
|
|
77
|
+
type: 'done';
|
|
78
|
+
response: VortexAIResponse;
|
|
79
|
+
};
|
|
80
|
+
export interface VortexAISuggestion {
|
|
81
|
+
id: string;
|
|
82
|
+
label: string;
|
|
83
|
+
/** Prompt enviado ao adapter se não houver command direto. */
|
|
84
|
+
prompt?: string;
|
|
85
|
+
command?: VortexAICommand;
|
|
86
|
+
}
|
|
87
|
+
export interface VortexAIRequestCreatingEvent {
|
|
88
|
+
request: VortexAIRequest;
|
|
89
|
+
cancel: boolean;
|
|
90
|
+
}
|
|
91
|
+
export interface VortexAIPrivacyPolicy {
|
|
92
|
+
contextFields?: string[];
|
|
93
|
+
maxRowsInContext?: number;
|
|
94
|
+
}
|
|
95
|
+
export interface VortexAICommandSpec {
|
|
96
|
+
type: string;
|
|
97
|
+
description: string;
|
|
98
|
+
}
|
|
99
|
+
export interface VortexAICapability {
|
|
100
|
+
componentType: string;
|
|
101
|
+
commands: VortexAICommandSpec[];
|
|
102
|
+
projectContext(state: unknown, policy: VortexAIPrivacyPolicy): VortexAIContext;
|
|
103
|
+
execute(command: VortexAICommand): void | Promise<void>;
|
|
104
|
+
suggest?(state: unknown): VortexAISuggestion[];
|
|
105
|
+
}
|
|
106
|
+
export interface VortexAIIntegration {
|
|
107
|
+
sendRequest(req: VortexAIRequest): Promise<VortexAIResponse>;
|
|
108
|
+
stream?(req: VortexAIRequest): AsyncIterable<VortexAIStreamEvent>;
|
|
109
|
+
}
|
package/ai/ai.utils.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { VortexAIContext, VortexAIPrivacyPolicy } from './ai.types';
|
|
2
|
+
export declare function projectRowsForAI(rows: Record<string, unknown>[], policy: VortexAIPrivacyPolicy): Record<string, unknown>[];
|
|
3
|
+
export declare function emptyAIContext(componentType: string): VortexAIContext;
|
package/ai/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary entry: `vortex-ui/ai` (alias monorepo `@vortex-ui/ai`).
|
|
3
|
+
*/
|
|
4
|
+
export * from './ai.types';
|
|
5
|
+
export * from './ai.utils';
|
|
6
|
+
export * from './mock-ai-integration';
|
|
7
|
+
export * from './ai-assistant-dialog.component';
|
|
8
|
+
export * from './ai-prompt-editor.component';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { VortexAIIntegration, VortexAIRequest, VortexAIResponse } from './ai.types';
|
|
2
|
+
/**
|
|
3
|
+
* Adapter local sem LLM — interpreta intents simples (PT/EN)
|
|
4
|
+
* e devolve commands tipados da allowlist do DataGrid.
|
|
5
|
+
*/
|
|
6
|
+
export declare class VortexMockAIIntegration implements VortexAIIntegration {
|
|
7
|
+
sendRequest(req: VortexAIRequest): Promise<VortexAIResponse>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { Component, ChangeDetectionStrategy, input, model, output, } from '@angular/core';
|
|
2
|
+
import { FormsModule } from '@angular/forms';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/forms";
|
|
5
|
+
export class VxAIAssistantDialogComponent {
|
|
6
|
+
open = model(false, /* @ts-ignore */
|
|
7
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
8
|
+
title = input('AI Assistant', /* @ts-ignore */
|
|
9
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
10
|
+
placeholder = input('Digite uma mensagem', /* @ts-ignore */
|
|
11
|
+
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
12
|
+
messages = input([], /* @ts-ignore */
|
|
13
|
+
...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
14
|
+
suggestions = input([], /* @ts-ignore */
|
|
15
|
+
...(ngDevMode ? [{ debugName: "suggestions" }] : /* istanbul ignore next */ []));
|
|
16
|
+
busy = input(false, /* @ts-ignore */
|
|
17
|
+
...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
18
|
+
sendMessage = output();
|
|
19
|
+
suggestionSelect = output();
|
|
20
|
+
clear = output();
|
|
21
|
+
closed = output();
|
|
22
|
+
draft = '';
|
|
23
|
+
close() {
|
|
24
|
+
this.open.set(false);
|
|
25
|
+
this.closed.emit();
|
|
26
|
+
}
|
|
27
|
+
onOverlayClick(_) {
|
|
28
|
+
this.close();
|
|
29
|
+
}
|
|
30
|
+
onEnter(event) {
|
|
31
|
+
const ke = event;
|
|
32
|
+
if (ke.shiftKey)
|
|
33
|
+
return;
|
|
34
|
+
ke.preventDefault();
|
|
35
|
+
this.send();
|
|
36
|
+
}
|
|
37
|
+
send() {
|
|
38
|
+
const text = this.draft.trim();
|
|
39
|
+
if (!text || this.busy())
|
|
40
|
+
return;
|
|
41
|
+
this.sendMessage.emit(text);
|
|
42
|
+
this.draft = '';
|
|
43
|
+
}
|
|
44
|
+
onSuggestion(s) {
|
|
45
|
+
this.suggestionSelect.emit(s);
|
|
46
|
+
}
|
|
47
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.6", ngImport: i0, type: VxAIAssistantDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
48
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.6", type: VxAIAssistantDialogComponent, isStandalone: true, selector: "vx-ai-assistant-dialog", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, suggestions: { classPropertyName: "suggestions", publicName: "suggestions", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", sendMessage: "sendMessage", suggestionSelect: "suggestionSelect", clear: "clear", closed: "closed" }, ngImport: i0, template: `
|
|
49
|
+
@if (open()) {
|
|
50
|
+
<div class="vx-ai-overlay" (click)="onOverlayClick($event)" role="presentation">
|
|
51
|
+
<div
|
|
52
|
+
class="vx-ai-dialog"
|
|
53
|
+
role="dialog"
|
|
54
|
+
aria-modal="true"
|
|
55
|
+
[attr.aria-label]="title()"
|
|
56
|
+
(click)="$event.stopPropagation()"
|
|
57
|
+
>
|
|
58
|
+
<header class="vx-ai-dialog__header">
|
|
59
|
+
<h2 class="vx-ai-dialog__title">{{ title() }}</h2>
|
|
60
|
+
<div class="vx-ai-dialog__actions">
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
class="vx-ai-icon-btn"
|
|
64
|
+
aria-label="Limpar conversa"
|
|
65
|
+
title="Limpar conversa"
|
|
66
|
+
(click)="clear.emit()"
|
|
67
|
+
>
|
|
68
|
+
⌫
|
|
69
|
+
</button>
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
class="vx-ai-icon-btn"
|
|
73
|
+
aria-label="Fechar"
|
|
74
|
+
(click)="close()"
|
|
75
|
+
>
|
|
76
|
+
✕
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
</header>
|
|
80
|
+
|
|
81
|
+
<div class="vx-ai-dialog__body">
|
|
82
|
+
@if (messages().length === 0) {
|
|
83
|
+
<div class="vx-ai-empty">
|
|
84
|
+
<div class="vx-ai-empty__icon" aria-hidden="true">✦</div>
|
|
85
|
+
<p class="vx-ai-empty__text">
|
|
86
|
+
Chat vazio. O assistente está pronto para responder.
|
|
87
|
+
</p>
|
|
88
|
+
@if (suggestions().length) {
|
|
89
|
+
<div class="vx-ai-chips">
|
|
90
|
+
@for (s of suggestions(); track s.id) {
|
|
91
|
+
<button type="button" class="vx-ai-chip" (click)="onSuggestion(s)">
|
|
92
|
+
{{ s.label }}
|
|
93
|
+
</button>
|
|
94
|
+
}
|
|
95
|
+
</div>
|
|
96
|
+
}
|
|
97
|
+
</div>
|
|
98
|
+
} @else {
|
|
99
|
+
<div class="vx-ai-messages">
|
|
100
|
+
@for (m of messages(); track $index) {
|
|
101
|
+
<div class="vx-ai-msg" [class.vx-ai-msg--user]="m.role === 'user'">
|
|
102
|
+
@if (m.title) {
|
|
103
|
+
<div class="vx-ai-msg__title">{{ m.title }}</div>
|
|
104
|
+
}
|
|
105
|
+
<div class="vx-ai-msg__text">{{ m.content }}</div>
|
|
106
|
+
</div>
|
|
107
|
+
}
|
|
108
|
+
</div>
|
|
109
|
+
}
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<footer class="vx-ai-dialog__footer">
|
|
113
|
+
<textarea
|
|
114
|
+
class="vx-ai-input"
|
|
115
|
+
rows="2"
|
|
116
|
+
[placeholder]="placeholder()"
|
|
117
|
+
[(ngModel)]="draft"
|
|
118
|
+
(keydown.enter)="onEnter($event)"
|
|
119
|
+
[disabled]="busy()"
|
|
120
|
+
></textarea>
|
|
121
|
+
<button
|
|
122
|
+
type="button"
|
|
123
|
+
class="vx-ai-send"
|
|
124
|
+
[disabled]="busy() || !draft.trim()"
|
|
125
|
+
aria-label="Enviar"
|
|
126
|
+
(click)="send()"
|
|
127
|
+
>
|
|
128
|
+
➤
|
|
129
|
+
</button>
|
|
130
|
+
</footer>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
}
|
|
134
|
+
`, isInline: true, styles: [".vx-ai-overlay{position:fixed;inset:0;z-index:var(--vx-z-modal);display:flex;justify-content:flex-end;align-items:stretch;padding:var(--vx-space-4);background:#00000040}.vx-ai-dialog{width:min(380px,100%);max-height:calc(100vh - 2rem);display:flex;flex-direction:column;background:var(--vx-color-surface);color:var(--vx-color-text);border:1px solid var(--vx-color-border);border-radius:var(--vx-radius-lg);box-shadow:var(--vx-shadow-xl);font-family:var(--vx-font-sans);overflow:hidden}.vx-ai-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:var(--vx-space-2);padding:var(--vx-space-3) var(--vx-space-4);border-bottom:1px solid var(--vx-color-border);flex-shrink:0}.vx-ai-dialog__title{margin:0;font-size:var(--vx-font-size-md);font-weight:var(--vx-font-weight-semibold)}.vx-ai-dialog__actions{display:flex;gap:var(--vx-space-1)}.vx-ai-icon-btn{border:none;background:transparent;color:var(--vx-color-text-secondary);width:2rem;height:2rem;border-radius:var(--vx-radius-md);cursor:pointer;font-size:1rem}.vx-ai-icon-btn:hover{background:var(--vx-color-bg-hover);color:var(--vx-color-text)}.vx-ai-dialog__body{flex:1;overflow:auto;padding:var(--vx-space-4);min-height:220px}.vx-ai-empty{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--vx-space-3);padding:var(--vx-space-6) var(--vx-space-2)}.vx-ai-empty__icon{width:3rem;height:3rem;border-radius:999px;display:grid;place-items:center;background:var(--vx-color-primary-subtle);color:var(--vx-color-primary);font-size:1.25rem}.vx-ai-empty__text{margin:0;color:var(--vx-color-text-secondary);font-size:var(--vx-font-size-sm);max-width:16rem}.vx-ai-chips{display:flex;flex-wrap:wrap;gap:var(--vx-space-2);justify-content:center}.vx-ai-chip{border:1px solid var(--vx-color-border);background:var(--vx-color-bg-elevated);color:var(--vx-color-text);border-radius:var(--vx-radius-md);padding:.4rem .75rem;font-size:var(--vx-font-size-xs);font-family:inherit;cursor:pointer}.vx-ai-chip:hover{border-color:var(--vx-color-primary);color:var(--vx-color-primary-ink, var(--vx-color-primary))}.vx-ai-messages{display:flex;flex-direction:column;gap:var(--vx-space-3)}.vx-ai-msg{align-self:flex-start;max-width:95%;padding:var(--vx-space-3);border-radius:var(--vx-radius-md);background:var(--vx-color-bg-muted);font-size:var(--vx-font-size-sm)}.vx-ai-msg--user{align-self:flex-end;background:var(--vx-color-primary-subtle);color:var(--vx-color-primary-ink, var(--vx-color-text))}.vx-ai-msg__title{font-weight:var(--vx-font-weight-semibold);margin-bottom:var(--vx-space-1)}.vx-ai-dialog__footer{display:flex;gap:var(--vx-space-2);padding:var(--vx-space-3);border-top:1px solid var(--vx-color-border);flex-shrink:0}.vx-ai-input{flex:1;resize:none;border:1px solid var(--vx-color-border-strong);border-radius:var(--vx-radius-md);padding:var(--vx-space-2) var(--vx-space-3);font-family:inherit;font-size:var(--vx-font-size-sm);background:var(--vx-color-bg-elevated);color:var(--vx-color-text)}.vx-ai-input:focus{outline:none;box-shadow:var(--vx-focus-ring);border-color:var(--vx-color-border-focus)}.vx-ai-send{width:2.5rem;border:none;border-radius:var(--vx-radius-md);background:var(--vx-color-primary);color:var(--vx-color-primary-text);cursor:pointer;font-size:1rem}.vx-ai-send:disabled{opacity:.45;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
135
|
+
}
|
|
136
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.6", ngImport: i0, type: VxAIAssistantDialogComponent, decorators: [{
|
|
137
|
+
type: Component,
|
|
138
|
+
args: [{ selector: 'vx-ai-assistant-dialog', standalone: true, imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
139
|
+
@if (open()) {
|
|
140
|
+
<div class="vx-ai-overlay" (click)="onOverlayClick($event)" role="presentation">
|
|
141
|
+
<div
|
|
142
|
+
class="vx-ai-dialog"
|
|
143
|
+
role="dialog"
|
|
144
|
+
aria-modal="true"
|
|
145
|
+
[attr.aria-label]="title()"
|
|
146
|
+
(click)="$event.stopPropagation()"
|
|
147
|
+
>
|
|
148
|
+
<header class="vx-ai-dialog__header">
|
|
149
|
+
<h2 class="vx-ai-dialog__title">{{ title() }}</h2>
|
|
150
|
+
<div class="vx-ai-dialog__actions">
|
|
151
|
+
<button
|
|
152
|
+
type="button"
|
|
153
|
+
class="vx-ai-icon-btn"
|
|
154
|
+
aria-label="Limpar conversa"
|
|
155
|
+
title="Limpar conversa"
|
|
156
|
+
(click)="clear.emit()"
|
|
157
|
+
>
|
|
158
|
+
⌫
|
|
159
|
+
</button>
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
class="vx-ai-icon-btn"
|
|
163
|
+
aria-label="Fechar"
|
|
164
|
+
(click)="close()"
|
|
165
|
+
>
|
|
166
|
+
✕
|
|
167
|
+
</button>
|
|
168
|
+
</div>
|
|
169
|
+
</header>
|
|
170
|
+
|
|
171
|
+
<div class="vx-ai-dialog__body">
|
|
172
|
+
@if (messages().length === 0) {
|
|
173
|
+
<div class="vx-ai-empty">
|
|
174
|
+
<div class="vx-ai-empty__icon" aria-hidden="true">✦</div>
|
|
175
|
+
<p class="vx-ai-empty__text">
|
|
176
|
+
Chat vazio. O assistente está pronto para responder.
|
|
177
|
+
</p>
|
|
178
|
+
@if (suggestions().length) {
|
|
179
|
+
<div class="vx-ai-chips">
|
|
180
|
+
@for (s of suggestions(); track s.id) {
|
|
181
|
+
<button type="button" class="vx-ai-chip" (click)="onSuggestion(s)">
|
|
182
|
+
{{ s.label }}
|
|
183
|
+
</button>
|
|
184
|
+
}
|
|
185
|
+
</div>
|
|
186
|
+
}
|
|
187
|
+
</div>
|
|
188
|
+
} @else {
|
|
189
|
+
<div class="vx-ai-messages">
|
|
190
|
+
@for (m of messages(); track $index) {
|
|
191
|
+
<div class="vx-ai-msg" [class.vx-ai-msg--user]="m.role === 'user'">
|
|
192
|
+
@if (m.title) {
|
|
193
|
+
<div class="vx-ai-msg__title">{{ m.title }}</div>
|
|
194
|
+
}
|
|
195
|
+
<div class="vx-ai-msg__text">{{ m.content }}</div>
|
|
196
|
+
</div>
|
|
197
|
+
}
|
|
198
|
+
</div>
|
|
199
|
+
}
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<footer class="vx-ai-dialog__footer">
|
|
203
|
+
<textarea
|
|
204
|
+
class="vx-ai-input"
|
|
205
|
+
rows="2"
|
|
206
|
+
[placeholder]="placeholder()"
|
|
207
|
+
[(ngModel)]="draft"
|
|
208
|
+
(keydown.enter)="onEnter($event)"
|
|
209
|
+
[disabled]="busy()"
|
|
210
|
+
></textarea>
|
|
211
|
+
<button
|
|
212
|
+
type="button"
|
|
213
|
+
class="vx-ai-send"
|
|
214
|
+
[disabled]="busy() || !draft.trim()"
|
|
215
|
+
aria-label="Enviar"
|
|
216
|
+
(click)="send()"
|
|
217
|
+
>
|
|
218
|
+
➤
|
|
219
|
+
</button>
|
|
220
|
+
</footer>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
}
|
|
224
|
+
`, styles: [".vx-ai-overlay{position:fixed;inset:0;z-index:var(--vx-z-modal);display:flex;justify-content:flex-end;align-items:stretch;padding:var(--vx-space-4);background:#00000040}.vx-ai-dialog{width:min(380px,100%);max-height:calc(100vh - 2rem);display:flex;flex-direction:column;background:var(--vx-color-surface);color:var(--vx-color-text);border:1px solid var(--vx-color-border);border-radius:var(--vx-radius-lg);box-shadow:var(--vx-shadow-xl);font-family:var(--vx-font-sans);overflow:hidden}.vx-ai-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:var(--vx-space-2);padding:var(--vx-space-3) var(--vx-space-4);border-bottom:1px solid var(--vx-color-border);flex-shrink:0}.vx-ai-dialog__title{margin:0;font-size:var(--vx-font-size-md);font-weight:var(--vx-font-weight-semibold)}.vx-ai-dialog__actions{display:flex;gap:var(--vx-space-1)}.vx-ai-icon-btn{border:none;background:transparent;color:var(--vx-color-text-secondary);width:2rem;height:2rem;border-radius:var(--vx-radius-md);cursor:pointer;font-size:1rem}.vx-ai-icon-btn:hover{background:var(--vx-color-bg-hover);color:var(--vx-color-text)}.vx-ai-dialog__body{flex:1;overflow:auto;padding:var(--vx-space-4);min-height:220px}.vx-ai-empty{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--vx-space-3);padding:var(--vx-space-6) var(--vx-space-2)}.vx-ai-empty__icon{width:3rem;height:3rem;border-radius:999px;display:grid;place-items:center;background:var(--vx-color-primary-subtle);color:var(--vx-color-primary);font-size:1.25rem}.vx-ai-empty__text{margin:0;color:var(--vx-color-text-secondary);font-size:var(--vx-font-size-sm);max-width:16rem}.vx-ai-chips{display:flex;flex-wrap:wrap;gap:var(--vx-space-2);justify-content:center}.vx-ai-chip{border:1px solid var(--vx-color-border);background:var(--vx-color-bg-elevated);color:var(--vx-color-text);border-radius:var(--vx-radius-md);padding:.4rem .75rem;font-size:var(--vx-font-size-xs);font-family:inherit;cursor:pointer}.vx-ai-chip:hover{border-color:var(--vx-color-primary);color:var(--vx-color-primary-ink, var(--vx-color-primary))}.vx-ai-messages{display:flex;flex-direction:column;gap:var(--vx-space-3)}.vx-ai-msg{align-self:flex-start;max-width:95%;padding:var(--vx-space-3);border-radius:var(--vx-radius-md);background:var(--vx-color-bg-muted);font-size:var(--vx-font-size-sm)}.vx-ai-msg--user{align-self:flex-end;background:var(--vx-color-primary-subtle);color:var(--vx-color-primary-ink, var(--vx-color-text))}.vx-ai-msg__title{font-weight:var(--vx-font-weight-semibold);margin-bottom:var(--vx-space-1)}.vx-ai-dialog__footer{display:flex;gap:var(--vx-space-2);padding:var(--vx-space-3);border-top:1px solid var(--vx-color-border);flex-shrink:0}.vx-ai-input{flex:1;resize:none;border:1px solid var(--vx-color-border-strong);border-radius:var(--vx-radius-md);padding:var(--vx-space-2) var(--vx-space-3);font-family:inherit;font-size:var(--vx-font-size-sm);background:var(--vx-color-bg-elevated);color:var(--vx-color-text)}.vx-ai-input:focus{outline:none;box-shadow:var(--vx-focus-ring);border-color:var(--vx-color-border-focus)}.vx-ai-send{width:2.5rem;border:none;border-radius:var(--vx-radius-md);background:var(--vx-color-primary);color:var(--vx-color-primary-text);cursor:pointer;font-size:1rem}.vx-ai-send:disabled{opacity:.45;cursor:not-allowed}\n"] }]
|
|
225
|
+
}], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], suggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "suggestions", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], sendMessage: [{ type: i0.Output, args: ["sendMessage"] }], suggestionSelect: [{ type: i0.Output, args: ["suggestionSelect"] }], clear: [{ type: i0.Output, args: ["clear"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
|
|
226
|
+
//# sourceMappingURL=ai-assistant-dialog.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-assistant-dialog.component.js","sourceRoot":"","sources":["../../../../../libs/vortex-ui/ai/src/ai-assistant-dialog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,uBAAuB,EACvB,KAAK,EACL,KAAK,EACL,MAAM,GACP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;;;AA+R7C,MAAM,OAAO,4BAA4B;IAC9B,IAAI,GAAG,KAAK,CAAC,KAAK;6EAAC,CAAC;IACpB,KAAK,GAAG,KAAK,CAAC,cAAc;8EAAC,CAAC;IAC9B,WAAW,GAAG,KAAK,CAAC,qBAAqB;oFAAC,CAAC;IAC3C,QAAQ,GAAG,KAAK,CAAoB,EAAE;iFAAC,CAAC;IACxC,WAAW,GAAG,KAAK,CAAuB,EAAE;oFAAC,CAAC;IAC9C,IAAI,GAAG,KAAK,CAAC,KAAK;6EAAC,CAAC;IAEpB,WAAW,GAAG,MAAM,EAAU,CAAC;IAC/B,gBAAgB,GAAG,MAAM,EAAsB,CAAC;IAChD,KAAK,GAAG,MAAM,EAAQ,CAAC;IACvB,MAAM,GAAG,MAAM,EAAQ,CAAC;IAEjC,KAAK,GAAG,EAAE,CAAC;IAEX,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACrB,CAAC;IAED,cAAc,CAAC,CAAa;QAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,OAAO,CAAC,KAAY;QAClB,MAAM,EAAE,GAAG,KAAsB,CAAC;QAClC,IAAI,EAAE,CAAC,QAAQ;YAAE,OAAO;QACxB,EAAE,CAAC,cAAc,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,IAAI;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO;QACjC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAClB,CAAC;IAED,YAAY,CAAC,CAAqB;QAChC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;uGAxCU,4BAA4B;2FAA5B,4BAA4B,09BAnR7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFT,g0GAxFS,WAAW;;2FAqRV,4BAA4B;kBAxRxC,SAAS;+BACE,wBAAwB,cACtB,IAAI,WACP,CAAC,WAAW,CAAC,mBACL,uBAAuB,CAAC,MAAM,YACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFT","sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n model,\n output,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { VortexAIMessage, VortexAISuggestion } from './ai.types';\n\nexport interface VxAIChatMessage extends VortexAIMessage {\n title?: string;\n}\n\n@Component({\n selector: 'vx-ai-assistant-dialog',\n standalone: true,\n imports: [FormsModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (open()) {\n <div class=\"vx-ai-overlay\" (click)=\"onOverlayClick($event)\" role=\"presentation\">\n <div\n class=\"vx-ai-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"title()\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"vx-ai-dialog__header\">\n <h2 class=\"vx-ai-dialog__title\">{{ title() }}</h2>\n <div class=\"vx-ai-dialog__actions\">\n <button\n type=\"button\"\n class=\"vx-ai-icon-btn\"\n aria-label=\"Limpar conversa\"\n title=\"Limpar conversa\"\n (click)=\"clear.emit()\"\n >\n ⌫\n </button>\n <button\n type=\"button\"\n class=\"vx-ai-icon-btn\"\n aria-label=\"Fechar\"\n (click)=\"close()\"\n >\n ✕\n </button>\n </div>\n </header>\n\n <div class=\"vx-ai-dialog__body\">\n @if (messages().length === 0) {\n <div class=\"vx-ai-empty\">\n <div class=\"vx-ai-empty__icon\" aria-hidden=\"true\">✦</div>\n <p class=\"vx-ai-empty__text\">\n Chat vazio. O assistente está pronto para responder.\n </p>\n @if (suggestions().length) {\n <div class=\"vx-ai-chips\">\n @for (s of suggestions(); track s.id) {\n <button type=\"button\" class=\"vx-ai-chip\" (click)=\"onSuggestion(s)\">\n {{ s.label }}\n </button>\n }\n </div>\n }\n </div>\n } @else {\n <div class=\"vx-ai-messages\">\n @for (m of messages(); track $index) {\n <div class=\"vx-ai-msg\" [class.vx-ai-msg--user]=\"m.role === 'user'\">\n @if (m.title) {\n <div class=\"vx-ai-msg__title\">{{ m.title }}</div>\n }\n <div class=\"vx-ai-msg__text\">{{ m.content }}</div>\n </div>\n }\n </div>\n }\n </div>\n\n <footer class=\"vx-ai-dialog__footer\">\n <textarea\n class=\"vx-ai-input\"\n rows=\"2\"\n [placeholder]=\"placeholder()\"\n [(ngModel)]=\"draft\"\n (keydown.enter)=\"onEnter($event)\"\n [disabled]=\"busy()\"\n ></textarea>\n <button\n type=\"button\"\n class=\"vx-ai-send\"\n [disabled]=\"busy() || !draft.trim()\"\n aria-label=\"Enviar\"\n (click)=\"send()\"\n >\n ➤\n </button>\n </footer>\n </div>\n </div>\n }\n `,\n styles: `\n .vx-ai-overlay {\n position: fixed;\n inset: 0;\n z-index: var(--vx-z-modal);\n display: flex;\n justify-content: flex-end;\n align-items: stretch;\n padding: var(--vx-space-4);\n background: rgb(0 0 0 / 0.25);\n }\n\n .vx-ai-dialog {\n width: min(380px, 100%);\n max-height: calc(100vh - 2rem);\n display: flex;\n flex-direction: column;\n background: var(--vx-color-surface);\n color: var(--vx-color-text);\n border: 1px solid var(--vx-color-border);\n border-radius: var(--vx-radius-lg);\n box-shadow: var(--vx-shadow-xl);\n font-family: var(--vx-font-sans);\n overflow: hidden;\n }\n\n .vx-ai-dialog__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--vx-space-2);\n padding: var(--vx-space-3) var(--vx-space-4);\n border-bottom: 1px solid var(--vx-color-border);\n flex-shrink: 0;\n }\n\n .vx-ai-dialog__title {\n margin: 0;\n font-size: var(--vx-font-size-md);\n font-weight: var(--vx-font-weight-semibold);\n }\n\n .vx-ai-dialog__actions {\n display: flex;\n gap: var(--vx-space-1);\n }\n\n .vx-ai-icon-btn {\n border: none;\n background: transparent;\n color: var(--vx-color-text-secondary);\n width: 2rem;\n height: 2rem;\n border-radius: var(--vx-radius-md);\n cursor: pointer;\n font-size: 1rem;\n }\n\n .vx-ai-icon-btn:hover {\n background: var(--vx-color-bg-hover);\n color: var(--vx-color-text);\n }\n\n .vx-ai-dialog__body {\n flex: 1;\n overflow: auto;\n padding: var(--vx-space-4);\n min-height: 220px;\n }\n\n .vx-ai-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n gap: var(--vx-space-3);\n padding: var(--vx-space-6) var(--vx-space-2);\n }\n\n .vx-ai-empty__icon {\n width: 3rem;\n height: 3rem;\n border-radius: 999px;\n display: grid;\n place-items: center;\n background: var(--vx-color-primary-subtle);\n color: var(--vx-color-primary);\n font-size: 1.25rem;\n }\n\n .vx-ai-empty__text {\n margin: 0;\n color: var(--vx-color-text-secondary);\n font-size: var(--vx-font-size-sm);\n max-width: 16rem;\n }\n\n .vx-ai-chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--vx-space-2);\n justify-content: center;\n }\n\n .vx-ai-chip {\n border: 1px solid var(--vx-color-border);\n background: var(--vx-color-bg-elevated);\n color: var(--vx-color-text);\n border-radius: var(--vx-radius-md);\n padding: 0.4rem 0.75rem;\n font-size: var(--vx-font-size-xs);\n font-family: inherit;\n cursor: pointer;\n }\n\n .vx-ai-chip:hover {\n border-color: var(--vx-color-primary);\n color: var(--vx-color-primary-ink, var(--vx-color-primary));\n }\n\n .vx-ai-messages {\n display: flex;\n flex-direction: column;\n gap: var(--vx-space-3);\n }\n\n .vx-ai-msg {\n align-self: flex-start;\n max-width: 95%;\n padding: var(--vx-space-3);\n border-radius: var(--vx-radius-md);\n background: var(--vx-color-bg-muted);\n font-size: var(--vx-font-size-sm);\n }\n\n .vx-ai-msg--user {\n align-self: flex-end;\n background: var(--vx-color-primary-subtle);\n color: var(--vx-color-primary-ink, var(--vx-color-text));\n }\n\n .vx-ai-msg__title {\n font-weight: var(--vx-font-weight-semibold);\n margin-bottom: var(--vx-space-1);\n }\n\n .vx-ai-dialog__footer {\n display: flex;\n gap: var(--vx-space-2);\n padding: var(--vx-space-3);\n border-top: 1px solid var(--vx-color-border);\n flex-shrink: 0;\n }\n\n .vx-ai-input {\n flex: 1;\n resize: none;\n border: 1px solid var(--vx-color-border-strong);\n border-radius: var(--vx-radius-md);\n padding: var(--vx-space-2) var(--vx-space-3);\n font-family: inherit;\n font-size: var(--vx-font-size-sm);\n background: var(--vx-color-bg-elevated);\n color: var(--vx-color-text);\n }\n\n .vx-ai-input:focus {\n outline: none;\n box-shadow: var(--vx-focus-ring);\n border-color: var(--vx-color-border-focus);\n }\n\n .vx-ai-send {\n width: 2.5rem;\n border: none;\n border-radius: var(--vx-radius-md);\n background: var(--vx-color-primary);\n color: var(--vx-color-primary-text);\n cursor: pointer;\n font-size: 1rem;\n }\n\n .vx-ai-send:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n }\n `,\n})\nexport class VxAIAssistantDialogComponent {\n readonly open = model(false);\n readonly title = input('AI Assistant');\n readonly placeholder = input('Digite uma mensagem');\n readonly messages = input<VxAIChatMessage[]>([]);\n readonly suggestions = input<VortexAISuggestion[]>([]);\n readonly busy = input(false);\n\n readonly sendMessage = output<string>();\n readonly suggestionSelect = output<VortexAISuggestion>();\n readonly clear = output<void>();\n readonly closed = output<void>();\n\n draft = '';\n\n close(): void {\n this.open.set(false);\n this.closed.emit();\n }\n\n onOverlayClick(_: MouseEvent): void {\n this.close();\n }\n\n onEnter(event: Event): void {\n const ke = event as KeyboardEvent;\n if (ke.shiftKey) return;\n ke.preventDefault();\n this.send();\n }\n\n send(): void {\n const text = this.draft.trim();\n if (!text || this.busy()) return;\n this.sendMessage.emit(text);\n this.draft = '';\n }\n\n onSuggestion(s: VortexAISuggestion): void {\n this.suggestionSelect.emit(s);\n }\n}\n"]}
|