@signalflare-ai/ui 0.5.0 → 1.0.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/CHANGELOG.md +117 -1
- package/ai/USAGE.md +64 -0
- package/ai/component-registry.json +492 -618
- package/ai/component-registry.md +167 -85
- package/ai/schemas.ts +545 -102
- package/bin/sf.js +2 -3
- package/dist/.build-complete +1 -1
- package/dist/ai/schemas.d.ts +1659 -5532
- package/dist/ai/schemas.d.ts.map +1 -1
- package/dist/{ai-actions-DG1dhDMP.js → ai-actions-DSVeQn4e.js} +1 -1
- package/dist/{ai-actions-DG1dhDMP.js.map → ai-actions-DSVeQn4e.js.map} +1 -1
- package/dist/{ai-agent-card-BbtL4NII.js → ai-agent-card-BXHwhWAU.js} +1 -1
- package/dist/{ai-agent-card-BbtL4NII.js.map → ai-agent-card-BXHwhWAU.js.map} +1 -1
- package/dist/{ai-approval-Mb7-BY6i.js → ai-approval-aa0qvjFN.js} +1 -1
- package/dist/{ai-approval-Mb7-BY6i.js.map → ai-approval-aa0qvjFN.js.map} +1 -1
- package/dist/{ai-code-block-BI_z0UVR.js → ai-code-block-BgtIxtZZ.js} +1 -1
- package/dist/{ai-code-block-BI_z0UVR.js.map → ai-code-block-BgtIxtZZ.js.map} +1 -1
- package/dist/{ai-conversation-DYtExcrw.js → ai-conversation-CArP7C8K.js} +1 -1
- package/dist/{ai-conversation-DYtExcrw.js.map → ai-conversation-CArP7C8K.js.map} +1 -1
- package/dist/{ai-info-banner-BpzauUAY.js → ai-info-banner-uFxHHwBA.js} +1 -1
- package/dist/{ai-info-banner-BpzauUAY.js.map → ai-info-banner-uFxHHwBA.js.map} +1 -1
- package/dist/{ai-message-CV8SBoHM.js → ai-message-BjnFznXy.js} +1 -1
- package/dist/{ai-message-CV8SBoHM.js.map → ai-message-BjnFznXy.js.map} +1 -1
- package/dist/{ai-mission-header-ByYkJ6YP.js → ai-mission-header-08__gULL.js} +1 -1
- package/dist/{ai-mission-header-ByYkJ6YP.js.map → ai-mission-header-08__gULL.js.map} +1 -1
- package/dist/ai-part-group-DBtgTgAn.js +277 -0
- package/dist/ai-part-group-DBtgTgAn.js.map +1 -0
- package/dist/ai-prompt-input-Dy1LfxPk.js +1541 -0
- package/dist/ai-prompt-input-Dy1LfxPk.js.map +1 -0
- package/dist/{ai-question-Dp1g9k2o.js → ai-question-CHHoDJMg.js} +1 -1
- package/dist/{ai-question-Dp1g9k2o.js.map → ai-question-CHHoDJMg.js.map} +1 -1
- package/dist/{ai-reasoning-UAmNx_LD.js → ai-reasoning-CnL6ZSr5.js} +84 -5
- package/dist/ai-reasoning-CnL6ZSr5.js.map +1 -0
- package/dist/{ai-response-BWoVsNQG.js → ai-response-BEUg3xvd.js} +13 -16
- package/dist/ai-response-BEUg3xvd.js.map +1 -0
- package/dist/{ai-shimmer-BpOmfonu.js → ai-shimmer-By5_L05p.js} +1 -1
- package/dist/{ai-shimmer-BpOmfonu.js.map → ai-shimmer-By5_L05p.js.map} +1 -1
- package/dist/{ai-status-badge-WhbKVeqn.js → ai-status-badge-BGYGWYF6.js} +1 -1
- package/dist/{ai-status-badge-WhbKVeqn.js.map → ai-status-badge-BGYGWYF6.js.map} +1 -1
- package/dist/{ai-streaming-text-ClL7FwvD.js → ai-streaming-text-CMfoThV0.js} +1 -1
- package/dist/{ai-streaming-text-ClL7FwvD.js.map → ai-streaming-text-CMfoThV0.js.map} +1 -1
- package/dist/{ai-subagent-BruGN1UE.js → ai-subagent-DcPRqkAA.js} +1 -1
- package/dist/{ai-subagent-BruGN1UE.js.map → ai-subagent-DcPRqkAA.js.map} +1 -1
- package/dist/{ai-suggestion-CNsCZj5P.js → ai-suggestion-MgeCg5Ar.js} +1 -1
- package/dist/{ai-suggestion-CNsCZj5P.js.map → ai-suggestion-MgeCg5Ar.js.map} +1 -1
- package/dist/{ai-task-list-B9CpMDYN.js → ai-task-list-Da9zIm00.js} +9 -12
- package/dist/ai-task-list-Da9zIm00.js.map +1 -0
- package/dist/{ai-timeline-Bb5ntsr3.js → ai-timeline-Cwu045IR.js} +1 -1
- package/dist/ai-timeline-Cwu045IR.js.map +1 -0
- package/dist/{ai-tool-BGH8nQ_D.js → ai-tool-Cn1O4xjP.js} +168 -11
- package/dist/ai-tool-Cn1O4xjP.js.map +1 -0
- package/dist/{ai-usage-bar-BI-p-JBk.js → ai-usage-bar-DjS12DMp.js} +1 -1
- package/dist/{ai-usage-bar-BI-p-JBk.js.map → ai-usage-bar-DjS12DMp.js.map} +1 -1
- package/dist/catalog.js +3 -3
- package/dist/catalog.js.map +1 -1
- package/dist/{chart-Bes4MN3C.js → chart-BK3sVPnD.js} +442 -248
- package/dist/chart-BK3sVPnD.js.map +1 -0
- package/dist/{checkbox-CPX7lBaU.js → checkbox-DYhUmZNw.js} +4 -4
- package/dist/checkbox-DYhUmZNw.js.map +1 -0
- package/dist/{clipboard-text-92YeCybc.js → clipboard-text-ssybngLw.js} +2 -2
- package/dist/{clipboard-text-92YeCybc.js.map → clipboard-text-ssybngLw.js.map} +1 -1
- package/dist/{code-DE1Yy1Cu.js → code-Cx-QSoOT.js} +2 -2
- package/dist/{code-DE1Yy1Cu.js.map → code-Cx-QSoOT.js.map} +1 -1
- package/dist/{combobox-B0bLdsX8.js → combobox-C0iW6a0r.js} +2 -2
- package/dist/{combobox-B0bLdsX8.js.map → combobox-C0iW6a0r.js.map} +1 -1
- package/dist/command-line/cli.js +22 -27
- package/dist/{command-palette-CBTY8EiF.js → command-palette-DGzioeki.js} +2 -2
- package/dist/command-palette-DGzioeki.js.map +1 -0
- package/dist/components/ai-actions.js +1 -1
- package/dist/components/ai-agent-card.js +1 -1
- package/dist/components/ai-approval.js +1 -1
- package/dist/components/ai-code-block.js +1 -1
- package/dist/components/ai-conversation.js +1 -1
- package/dist/components/ai-info-banner.js +1 -1
- package/dist/components/ai-message.js +1 -1
- package/dist/components/ai-mission-header.js +1 -1
- package/dist/components/ai-part-group.js +3 -0
- package/dist/components/ai-prompt-input.js +2 -2
- package/dist/components/ai-question.js +1 -1
- package/dist/components/ai-reasoning.js +1 -1
- package/dist/components/ai-response.js +1 -1
- package/dist/components/ai-shimmer.js +1 -1
- package/dist/components/ai-status-badge.js +1 -1
- package/dist/components/ai-streaming-text.js +1 -1
- package/dist/components/ai-subagent.js +1 -1
- package/dist/components/ai-suggestion.js +1 -1
- package/dist/components/ai-task-list.js +1 -1
- package/dist/components/ai-timeline.js +1 -1
- package/dist/components/ai-tool.js +1 -1
- package/dist/components/ai-usage-bar.js +1 -1
- package/dist/components/chart.js +3 -2
- package/dist/components/checkbox.js +1 -1
- package/dist/components/clipboard-text.js +1 -1
- package/dist/components/code.js +1 -1
- package/dist/components/combobox.js +1 -1
- package/dist/components/command-palette.js +1 -1
- package/dist/components/data-grid.js +1 -1
- package/dist/components/date-picker.js +1 -1
- package/dist/components/dropdown.js +1 -1
- package/dist/components/filters.js +1 -1
- package/dist/components/input.js +2 -2
- package/dist/components/link.js +2 -2
- package/dist/components/link.js.map +1 -1
- package/dist/components/pagination.js +1 -1
- package/dist/components/radio.js +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/sensitive-input.js +1 -1
- package/dist/components/sidebar.js +1 -1
- package/dist/components/sparkline.js +3 -0
- package/dist/components/stat-card.js +3 -0
- package/dist/components/table.js +1 -1
- package/dist/components/text-roll.js +3 -0
- package/dist/components/theme-toggle.js +1 -1
- package/dist/components/use-agent-harness.js +1 -1
- package/dist/{data-grid-UJ9ja5cu.js → data-grid-CG76N_hK.js} +6 -6
- package/dist/data-grid-CG76N_hK.js.map +1 -0
- package/dist/{date-picker-ebekkC3R.js → date-picker-Dqg9L4xu.js} +2 -2
- package/dist/{date-picker-ebekkC3R.js.map → date-picker-Dqg9L4xu.js.map} +1 -1
- package/dist/{dist-BNlyONdD.js → dist-1-gcEL2L.js} +224 -135
- package/dist/dist-1-gcEL2L.js.map +1 -0
- package/dist/{dropdown-J5T4pHaR.js → dropdown-qnEYRFXZ.js} +2 -2
- package/dist/{dropdown-J5T4pHaR.js.map → dropdown-qnEYRFXZ.js.map} +1 -1
- package/dist/echart-DURZEyai.js +314 -0
- package/dist/echart-DURZEyai.js.map +1 -0
- package/dist/{filters-BdBogf7D.js → filters-Bw_U6ZTx.js} +5 -5
- package/dist/filters-Bw_U6ZTx.js.map +1 -0
- package/dist/flow-BRsYUCJa.js.map +1 -1
- package/dist/genui.js +2 -2
- package/dist/genui.js.map +1 -1
- package/dist/index.js +45 -42
- package/dist/index.js.map +1 -1
- package/dist/{input-BxQAnXki.js → input-DXYUjGgD.js} +2 -2
- package/dist/{input-BxQAnXki.js.map → input-DXYUjGgD.js.map} +1 -1
- package/dist/{input-Cn25I4o5.js → input-DddtBN-g.js} +2 -2
- package/dist/{input-Cn25I4o5.js.map → input-DddtBN-g.js.map} +1 -1
- package/dist/layer-card-BME0eljh.js.map +1 -1
- package/dist/{pagination-C_YqCy8l.js → pagination-BVqdlONY.js} +2 -2
- package/dist/{pagination-C_YqCy8l.js.map → pagination-BVqdlONY.js.map} +1 -1
- package/dist/primitives/otp-field.js +2 -0
- package/dist/primitives.js +1 -0
- package/dist/{radio-B7zg1wUI.js → radio-BNSwOt3B.js} +3 -3
- package/dist/radio-BNSwOt3B.js.map +1 -0
- package/dist/{select-9p721G00.js → select-1w2aebGQ.js} +2 -2
- package/dist/select-1w2aebGQ.js.map +1 -0
- package/dist/{sensitive-input-D5je2NLl.js → sensitive-input-82Cez3vj.js} +2 -2
- package/dist/{sensitive-input-D5je2NLl.js.map → sensitive-input-82Cez3vj.js.map} +1 -1
- package/dist/{sidebar-DOwBrq57.js → sidebar-CAsCmSpM.js} +3 -3
- package/dist/sidebar-CAsCmSpM.js.map +1 -0
- package/dist/sparkline-DdbeM4Ai.js +108 -0
- package/dist/sparkline-DdbeM4Ai.js.map +1 -0
- package/dist/src/blocks/agent-harness/agent-harness.d.ts +30 -9
- package/dist/src/blocks/agent-harness/agent-harness.d.ts.map +1 -1
- package/dist/src/blocks/agent-harness/agent-harness.stories.tsx +114 -0
- package/dist/src/blocks/agent-harness/agent-harness.tsx +144 -63
- package/dist/src/blocks/commander/commander.stories.tsx +31 -0
- package/dist/src/blocks/dashboard-grid/dashboard-grid.d.ts +48 -0
- package/dist/src/blocks/dashboard-grid/dashboard-grid.d.ts.map +1 -0
- package/dist/src/blocks/dashboard-grid/dashboard-grid.stories.tsx +19 -0
- package/dist/src/blocks/dashboard-grid/dashboard-grid.tsx +110 -0
- package/dist/src/blocks/dashboard-grid/index.d.ts +2 -0
- package/dist/src/blocks/dashboard-grid/index.d.ts.map +1 -0
- package/dist/src/blocks/delete-resource/delete-resource.stories.tsx +31 -0
- package/dist/src/blocks/map-block/map-block.stories.tsx +41 -0
- package/dist/src/blocks/metrics-overview/index.d.ts +2 -0
- package/dist/src/blocks/metrics-overview/index.d.ts.map +1 -0
- package/dist/src/blocks/metrics-overview/metrics-overview.d.ts +67 -0
- package/dist/src/blocks/metrics-overview/metrics-overview.d.ts.map +1 -0
- package/dist/src/blocks/metrics-overview/metrics-overview.stories.tsx +26 -0
- package/dist/src/blocks/metrics-overview/metrics-overview.tsx +139 -0
- package/dist/src/blocks/page-header/page-header.stories.tsx +56 -0
- package/dist/src/blocks/resource-list/resource-list.stories.tsx +31 -0
- package/dist/src/catalog/catalog.d.ts +1 -1
- package/dist/src/catalog/index.d.ts +1 -1
- package/dist/src/catalog/types.d.ts +1 -1
- package/dist/src/command-line/build-cli.d.ts +1 -1
- package/dist/src/command-line/cli.d.ts +1 -1
- package/dist/src/command-line/commands/add.d.ts +1 -1
- package/dist/src/command-line/commands/add.d.ts.map +1 -1
- package/dist/src/command-line/commands/ai.d.ts.map +1 -1
- package/dist/src/command-line/commands/blocks.d.ts +1 -1
- package/dist/src/command-line/commands/blocks.d.ts.map +1 -1
- package/dist/src/command-line/commands/doc.d.ts +1 -1
- package/dist/src/command-line/commands/doc.d.ts.map +1 -1
- package/dist/src/command-line/commands/ls.d.ts +1 -1
- package/dist/src/command-line/commands/ls.d.ts.map +1 -1
- package/dist/src/command-line/commands/migrate.d.ts +3 -3
- package/dist/src/command-line/commands/migrate.d.ts.map +1 -1
- package/dist/src/command-line/utils/config.d.ts +1 -1
- package/dist/src/command-line/utils/transformer.d.ts +1 -1
- package/dist/src/components/ai-part-group/ai-part-group.d.ts +134 -0
- package/dist/src/components/ai-part-group/ai-part-group.d.ts.map +1 -0
- package/dist/src/components/ai-part-group/index.d.ts +2 -0
- package/dist/src/components/ai-part-group/index.d.ts.map +1 -0
- package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts +196 -7
- package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
- package/dist/src/components/ai-prompt-input/controller.d.ts +41 -0
- package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -0
- package/dist/src/components/ai-prompt-input/index.d.ts +3 -1
- package/dist/src/components/ai-prompt-input/index.d.ts.map +1 -1
- package/dist/src/components/ai-prompt-input/types.d.ts +86 -0
- package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -0
- package/dist/src/components/ai-reasoning/ai-reasoning.d.ts +17 -1
- package/dist/src/components/ai-reasoning/ai-reasoning.d.ts.map +1 -1
- package/dist/src/components/ai-response/ai-response.d.ts +1 -2
- package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
- package/dist/src/components/ai-task-list/ai-task-list.d.ts.map +1 -1
- package/dist/src/components/ai-tool/ai-tool.d.ts +17 -1
- package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
- package/dist/src/components/chart/area-chart.d.ts +69 -0
- package/dist/src/components/chart/area-chart.d.ts.map +1 -0
- package/dist/src/components/chart/bar-chart.d.ts +60 -0
- package/dist/src/components/chart/bar-chart.d.ts.map +1 -0
- package/dist/src/components/chart/color.d.ts +56 -0
- package/dist/src/components/chart/color.d.ts.map +1 -1
- package/dist/src/components/chart/echart.d.ts.map +1 -1
- package/dist/src/components/chart/index.d.ts +5 -0
- package/dist/src/components/chart/index.d.ts.map +1 -1
- package/dist/src/components/chart/pie-chart.d.ts +64 -0
- package/dist/src/components/chart/pie-chart.d.ts.map +1 -0
- package/dist/src/components/chart/scatter-chart.d.ts +57 -0
- package/dist/src/components/chart/scatter-chart.d.ts.map +1 -0
- package/dist/src/components/chart/stacked-bar-chart.d.ts +59 -0
- package/dist/src/components/chart/stacked-bar-chart.d.ts.map +1 -0
- package/dist/src/components/chart/timeseries-chart.d.ts.map +1 -1
- package/dist/src/components/code/code.d.ts +1 -1
- package/dist/src/components/command-palette/command-palette.d.ts +1 -1
- package/dist/src/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/src/components/data-grid/index.d.ts +1 -1
- package/dist/src/components/data-grid/index.d.ts.map +1 -1
- package/dist/src/components/data-grid/types.d.ts.map +1 -1
- package/dist/src/components/date-picker/date-picker.d.ts +2 -2
- package/dist/src/components/filters/filters.d.ts.map +1 -1
- package/dist/src/components/filters/helpers.d.ts.map +1 -1
- package/dist/src/components/filters/types.d.ts.map +1 -1
- package/dist/src/components/layer-card/layer-card.d.ts +1 -1
- package/dist/src/components/link/link.d.ts +2 -2
- package/dist/src/components/radio/radio.d.ts +1 -1
- package/dist/src/components/select/select.d.ts +1 -1
- package/dist/src/components/sidebar/sidebar.d.ts +2 -2
- package/dist/src/components/sparkline/index.d.ts +2 -0
- package/dist/src/components/sparkline/index.d.ts.map +1 -0
- package/dist/src/components/sparkline/sparkline.d.ts +52 -0
- package/dist/src/components/sparkline/sparkline.d.ts.map +1 -0
- package/dist/src/components/stat-card/index.d.ts +2 -0
- package/dist/src/components/{ai-loader → stat-card}/index.d.ts.map +1 -1
- package/dist/src/components/stat-card/stat-card.d.ts +80 -0
- package/dist/src/components/stat-card/stat-card.d.ts.map +1 -0
- package/dist/src/components/text-roll/index.d.ts +2 -0
- package/dist/src/components/text-roll/index.d.ts.map +1 -0
- package/dist/src/components/text-roll/text-roll.d.ts +49 -0
- package/dist/src/components/text-roll/text-roll.d.ts.map +1 -0
- package/dist/src/components/toast/toast.d.ts.map +1 -1
- package/dist/src/components/use-agent-harness/use-agent-harness.d.ts +2 -2
- package/dist/src/genui/genui.d.ts.map +1 -1
- package/dist/src/index.d.ts +5 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/primitives/index.d.ts +1 -0
- package/dist/src/primitives/index.d.ts.map +1 -1
- package/dist/src/primitives/otp-field.d.ts +13 -0
- package/dist/src/primitives/otp-field.d.ts.map +1 -0
- package/dist/src/registry/index.d.ts +1 -1
- package/dist/src/registry/types.d.ts +1 -1
- package/dist/stat-card-CEZscNh8.js +103 -0
- package/dist/stat-card-CEZscNh8.js.map +1 -0
- package/dist/styles/sf-binding.css +20 -62
- package/dist/styles/sf-standalone.css +2 -2
- package/dist/styles/shadcn.css +120 -0
- package/dist/styles/theme-blue-tint.css +98 -0
- package/dist/styles/theme-fedramp.css +3 -12
- package/dist/styles/theme-minimal.css +26 -104
- package/dist/styles/theme-sf.css +96 -114
- package/dist/{table-CIMx0Oq0.js → table-Rv4JMy0B.js} +2 -2
- package/dist/{table-CIMx0Oq0.js.map → table-Rv4JMy0B.js.map} +1 -1
- package/dist/text-roll-BZ3I1umc.js +79 -0
- package/dist/text-roll-BZ3I1umc.js.map +1 -0
- package/dist/{theme-toggle-Dpgnoj_Q.js → theme-toggle-Bhu681D7.js} +1 -1
- package/dist/{theme-toggle-Dpgnoj_Q.js.map → theme-toggle-Bhu681D7.js.map} +1 -1
- package/dist/{use-agent-harness-DZzcn96L.js → use-agent-harness-BMyF8pTq.js} +5 -5
- package/dist/use-agent-harness-BMyF8pTq.js.map +1 -0
- package/package.json +48 -19
- package/scripts/component-registry/discovery.ts +2 -2
- package/scripts/component-registry/index.ts +1 -1
- package/scripts/component-registry/props-filter.ts +1 -1
- package/scripts/component-registry/utils.ts +5 -5
- package/scripts/component-registry/variant-parser.ts +124 -19
- package/scripts/convert-demos-to-stories.ts +253 -0
- package/scripts/css-build.ts +4 -3
- package/scripts/generate-primitives.ts +11 -3
- package/scripts/theme-generator/config.ts +339 -71
- package/scripts/theme-generator/generate-css.ts +17 -3
- package/scripts/theme-generator/index.ts +3 -3
- package/scripts/theme-generator/migrate.ts +1 -1
- package/dist/ai-loader-Cr3eQkNS.js +0 -134
- package/dist/ai-loader-Cr3eQkNS.js.map +0 -1
- package/dist/ai-prompt-input-Bo1YuJly.js +0 -769
- package/dist/ai-prompt-input-Bo1YuJly.js.map +0 -1
- package/dist/ai-reasoning-UAmNx_LD.js.map +0 -1
- package/dist/ai-response-BWoVsNQG.js.map +0 -1
- package/dist/ai-task-list-B9CpMDYN.js.map +0 -1
- package/dist/ai-timeline-Bb5ntsr3.js.map +0 -1
- package/dist/ai-tool-BGH8nQ_D.js.map +0 -1
- package/dist/chart-Bes4MN3C.js.map +0 -1
- package/dist/checkbox-CPX7lBaU.js.map +0 -1
- package/dist/command-palette-CBTY8EiF.js.map +0 -1
- package/dist/components/ai-loader.js +0 -3
- package/dist/data-grid-UJ9ja5cu.js.map +0 -1
- package/dist/dist-BNlyONdD.js.map +0 -1
- package/dist/filters-BdBogf7D.js.map +0 -1
- package/dist/radio-B7zg1wUI.js.map +0 -1
- package/dist/select-9p721G00.js.map +0 -1
- package/dist/sidebar-DOwBrq57.js.map +0 -1
- package/dist/src/components/ai-loader/ai-loader.d.ts +0 -44
- package/dist/src/components/ai-loader/ai-loader.d.ts.map +0 -1
- package/dist/src/components/ai-loader/index.d.ts +0 -2
- package/dist/styles/theme-navigator.css +0 -137
- package/dist/use-agent-harness-DZzcn96L.js.map +0 -1
|
@@ -1,769 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { t as cn } from "./cn-YROP2_ox.js";
|
|
3
|
-
import { t as Tooltip } from "./tooltip-Cb7QW-7H.js";
|
|
4
|
-
import { t as Button } from "./button-De0267YU.js";
|
|
5
|
-
import { t as InputGroup } from "./input-Cn25I4o5.js";
|
|
6
|
-
import { Fragment, createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
7
|
-
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
-
import { ArrowUpIcon, CaretDownIcon, FileIcon, FileTextIcon, ImageIcon, MicrophoneIcon, PaperclipIcon, PlusIcon, SpinnerGapIcon, SquareIcon, XIcon } from "@phosphor-icons/react";
|
|
9
|
-
import { Menu } from "@base-ui/react/menu";
|
|
10
|
-
//#region src/components/ai-prompt-input/ai-prompt-input.tsx
|
|
11
|
-
var SF_AI_PROMPT_INPUT_VARIANTS = { status: {
|
|
12
|
-
idle: {
|
|
13
|
-
classes: "",
|
|
14
|
-
description: "Ready to accept input"
|
|
15
|
-
},
|
|
16
|
-
submitted: {
|
|
17
|
-
classes: "",
|
|
18
|
-
description: "Waiting for response (shows spinner)"
|
|
19
|
-
},
|
|
20
|
-
streaming: {
|
|
21
|
-
classes: "",
|
|
22
|
-
description: "Streaming response (shows stop)"
|
|
23
|
-
},
|
|
24
|
-
error: {
|
|
25
|
-
classes: "",
|
|
26
|
-
description: "Error state (shows X)"
|
|
27
|
-
}
|
|
28
|
-
} };
|
|
29
|
-
var SF_AI_PROMPT_INPUT_DEFAULT_VARIANTS = { status: "idle" };
|
|
30
|
-
var PromptInputContext = createContext(null);
|
|
31
|
-
var ProviderAttachmentsContext = createContext(null);
|
|
32
|
-
var usePromptInputController = () => {
|
|
33
|
-
const ctx = useContext(PromptInputContext);
|
|
34
|
-
if (!ctx) throw new Error("Wrap your component inside <PromptInputProvider> to use usePromptInputController().");
|
|
35
|
-
return ctx;
|
|
36
|
-
};
|
|
37
|
-
var useOptionalPromptInputController = () => useContext(PromptInputContext);
|
|
38
|
-
var useProviderAttachments = () => {
|
|
39
|
-
const ctx = useContext(ProviderAttachmentsContext);
|
|
40
|
-
if (!ctx) throw new Error("Wrap your component inside <PromptInputProvider> to use useProviderAttachments().");
|
|
41
|
-
return ctx;
|
|
42
|
-
};
|
|
43
|
-
var useOptionalProviderAttachments = () => useContext(ProviderAttachmentsContext);
|
|
44
|
-
/**
|
|
45
|
-
* Optional global provider that lifts PromptInput state outside of PromptInput.
|
|
46
|
-
* If you don't use it, PromptInput stays fully self-managed.
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* ```tsx
|
|
50
|
-
* <PromptInputProvider>
|
|
51
|
-
* <PromptInput onSubmit={handleSubmit}>...</PromptInput>
|
|
52
|
-
* </PromptInputProvider>
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
function PromptInputProvider({ initialInput = "", children }) {
|
|
56
|
-
const [textInput, setTextInput] = useState(initialInput);
|
|
57
|
-
const clearInput = useCallback(() => setTextInput(""), []);
|
|
58
|
-
const [attachmentItems, setAttachmentItems] = useState([]);
|
|
59
|
-
const fileInputRef = useRef(null);
|
|
60
|
-
const openRef = useRef(() => {});
|
|
61
|
-
const add = useCallback((files) => {
|
|
62
|
-
const incoming = Array.from(files);
|
|
63
|
-
if (incoming.length === 0) return;
|
|
64
|
-
setAttachmentItems((prev) => prev.concat(incoming.map((file) => ({
|
|
65
|
-
id: `${Date.now()}-${Math.random()}`,
|
|
66
|
-
url: URL.createObjectURL(file),
|
|
67
|
-
mediaType: file.type,
|
|
68
|
-
filename: file.name
|
|
69
|
-
}))));
|
|
70
|
-
}, []);
|
|
71
|
-
const remove = useCallback((id) => {
|
|
72
|
-
setAttachmentItems((prev) => {
|
|
73
|
-
const found = prev.find((f) => f.id === id);
|
|
74
|
-
if (found?.url) URL.revokeObjectURL(found.url);
|
|
75
|
-
return prev.filter((f) => f.id !== id);
|
|
76
|
-
});
|
|
77
|
-
}, []);
|
|
78
|
-
const clear = useCallback(() => {
|
|
79
|
-
setAttachmentItems((prev) => {
|
|
80
|
-
for (const f of prev) if (f.url) URL.revokeObjectURL(f.url);
|
|
81
|
-
return [];
|
|
82
|
-
});
|
|
83
|
-
}, []);
|
|
84
|
-
const openFileDialog = useCallback(() => {
|
|
85
|
-
openRef.current?.();
|
|
86
|
-
}, []);
|
|
87
|
-
const attachments = useMemo(() => ({
|
|
88
|
-
files: attachmentItems,
|
|
89
|
-
add,
|
|
90
|
-
remove,
|
|
91
|
-
clear,
|
|
92
|
-
openFileDialog,
|
|
93
|
-
fileInputRef
|
|
94
|
-
}), [
|
|
95
|
-
attachmentItems,
|
|
96
|
-
add,
|
|
97
|
-
remove,
|
|
98
|
-
clear,
|
|
99
|
-
openFileDialog
|
|
100
|
-
]);
|
|
101
|
-
const __registerFileInput = useCallback((ref, open) => {
|
|
102
|
-
fileInputRef.current = ref.current;
|
|
103
|
-
openRef.current = open;
|
|
104
|
-
}, []);
|
|
105
|
-
const controller = useMemo(() => ({
|
|
106
|
-
textInput: {
|
|
107
|
-
value: textInput,
|
|
108
|
-
setInput: setTextInput,
|
|
109
|
-
clear: clearInput
|
|
110
|
-
},
|
|
111
|
-
attachments,
|
|
112
|
-
__registerFileInput
|
|
113
|
-
}), [
|
|
114
|
-
textInput,
|
|
115
|
-
clearInput,
|
|
116
|
-
attachments,
|
|
117
|
-
__registerFileInput
|
|
118
|
-
]);
|
|
119
|
-
return /* @__PURE__ */ jsx(PromptInputContext.Provider, {
|
|
120
|
-
value: controller,
|
|
121
|
-
children: /* @__PURE__ */ jsx(ProviderAttachmentsContext.Provider, {
|
|
122
|
-
value: attachments,
|
|
123
|
-
children
|
|
124
|
-
})
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
var LocalAttachmentsContext = createContext(null);
|
|
128
|
-
var usePromptInputAttachments = () => {
|
|
129
|
-
const provider = useOptionalProviderAttachments();
|
|
130
|
-
const local = useContext(LocalAttachmentsContext);
|
|
131
|
-
const context = provider ?? local;
|
|
132
|
-
if (!context) throw new Error("usePromptInputAttachments must be used within a PromptInput or PromptInputProvider");
|
|
133
|
-
return context;
|
|
134
|
-
};
|
|
135
|
-
/** Convert a blob URL to a base64 data URL for serialization. */
|
|
136
|
-
async function convertBlobToDataUrl(url) {
|
|
137
|
-
const blob = await (await fetch(url)).blob();
|
|
138
|
-
return new Promise((resolve, reject) => {
|
|
139
|
-
const reader = new FileReader();
|
|
140
|
-
reader.addEventListener("loadend", () => resolve(reader.result));
|
|
141
|
-
reader.addEventListener("error", () => reject(/* @__PURE__ */ new Error("FileReader error")));
|
|
142
|
-
reader.readAsDataURL(blob);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
function makeAttachmentId() {
|
|
146
|
-
return `${Date.now()}-${Math.random()}`;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Prompt input form. Can be self-managed or controlled via `PromptInputProvider`.
|
|
150
|
-
*
|
|
151
|
-
* @example
|
|
152
|
-
* ```tsx
|
|
153
|
-
* <PromptInput onSubmit={({ text, files }) => send(text, files)}>
|
|
154
|
-
* <PromptInputAttachments>{(f) => <PromptInputAttachment data={f} />}</PromptInputAttachments>
|
|
155
|
-
* <PromptInputTextarea placeholder="Ask anything…" />
|
|
156
|
-
* <PromptInputToolbar>
|
|
157
|
-
* <PromptInputTools>
|
|
158
|
-
* <PromptInputAttachButton />
|
|
159
|
-
* </PromptInputTools>
|
|
160
|
-
* <PromptInputSubmit />
|
|
161
|
-
* </PromptInputToolbar>
|
|
162
|
-
* </PromptInput>
|
|
163
|
-
* ```
|
|
164
|
-
*/
|
|
165
|
-
var PromptInput = ({ className, accept, multiple, globalDrop, maxFiles, maxFileSize, onError, onSubmit, children, backLayer, backLayerTitle = "Context", backLayerOpen, onBackLayerOpenChange, autoOpenBackLayerWhen, ...props }) => {
|
|
166
|
-
const hasBackLayer = backLayer !== void 0;
|
|
167
|
-
const controller = useOptionalPromptInputController();
|
|
168
|
-
const usingProvider = !!controller;
|
|
169
|
-
const inputRef = useRef(null);
|
|
170
|
-
const anchorRef = useRef(null);
|
|
171
|
-
const formRef = useRef(null);
|
|
172
|
-
useEffect(() => {
|
|
173
|
-
const root = anchorRef.current?.closest("form");
|
|
174
|
-
if (root instanceof HTMLFormElement) formRef.current = root;
|
|
175
|
-
}, []);
|
|
176
|
-
useEffect(() => {
|
|
177
|
-
if (autoOpenBackLayerWhen) onBackLayerOpenChange?.(true);
|
|
178
|
-
}, [autoOpenBackLayerWhen, onBackLayerOpenChange]);
|
|
179
|
-
const [items, setItems] = useState([]);
|
|
180
|
-
const files = usingProvider ? controller.attachments.files : items;
|
|
181
|
-
const openFileDialogLocal = useCallback(() => {
|
|
182
|
-
inputRef.current?.click();
|
|
183
|
-
}, []);
|
|
184
|
-
const matchesAccept = useCallback((f) => {
|
|
185
|
-
if (!accept || accept.trim() === "") return true;
|
|
186
|
-
if (accept.includes("image/*")) return f.type.startsWith("image/");
|
|
187
|
-
return true;
|
|
188
|
-
}, [accept]);
|
|
189
|
-
const addLocal = useCallback((fileList) => {
|
|
190
|
-
const incoming = Array.from(fileList);
|
|
191
|
-
const accepted = incoming.filter((f) => matchesAccept(f));
|
|
192
|
-
if (incoming.length && accepted.length === 0) {
|
|
193
|
-
onError?.({
|
|
194
|
-
code: "accept",
|
|
195
|
-
message: "No files match the accepted types."
|
|
196
|
-
});
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
|
|
200
|
-
const sized = accepted.filter(withinSize);
|
|
201
|
-
if (accepted.length > 0 && sized.length === 0) {
|
|
202
|
-
onError?.({
|
|
203
|
-
code: "max_file_size",
|
|
204
|
-
message: "All files exceed the maximum size."
|
|
205
|
-
});
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
setItems((prev) => {
|
|
209
|
-
const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : void 0;
|
|
210
|
-
const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
|
|
211
|
-
if (typeof capacity === "number" && sized.length > capacity) onError?.({
|
|
212
|
-
code: "max_files",
|
|
213
|
-
message: "Too many files. Some were not added."
|
|
214
|
-
});
|
|
215
|
-
const next = capped.map((file) => ({
|
|
216
|
-
id: makeAttachmentId(),
|
|
217
|
-
url: URL.createObjectURL(file),
|
|
218
|
-
mediaType: file.type,
|
|
219
|
-
filename: file.name
|
|
220
|
-
}));
|
|
221
|
-
return prev.concat(next);
|
|
222
|
-
});
|
|
223
|
-
}, [
|
|
224
|
-
matchesAccept,
|
|
225
|
-
maxFiles,
|
|
226
|
-
maxFileSize,
|
|
227
|
-
onError
|
|
228
|
-
]);
|
|
229
|
-
const add = usingProvider ? (f) => controller.attachments.add(f) : addLocal;
|
|
230
|
-
const remove = usingProvider ? (id) => controller.attachments.remove(id) : (id) => setItems((prev) => {
|
|
231
|
-
const found = prev.find((f) => f.id === id);
|
|
232
|
-
if (found?.url) URL.revokeObjectURL(found.url);
|
|
233
|
-
return prev.filter((f) => f.id !== id);
|
|
234
|
-
});
|
|
235
|
-
const clear = usingProvider ? () => controller.attachments.clear() : () => setItems((prev) => {
|
|
236
|
-
for (const f of prev) if (f.url) URL.revokeObjectURL(f.url);
|
|
237
|
-
return [];
|
|
238
|
-
});
|
|
239
|
-
const openFileDialog = usingProvider ? () => controller.attachments.openFileDialog() : openFileDialogLocal;
|
|
240
|
-
useEffect(() => {
|
|
241
|
-
if (!usingProvider) return;
|
|
242
|
-
controller.__registerFileInput(inputRef, () => inputRef.current?.click());
|
|
243
|
-
}, [usingProvider, controller]);
|
|
244
|
-
useEffect(() => {
|
|
245
|
-
const form = formRef.current;
|
|
246
|
-
if (!form) return;
|
|
247
|
-
const onDragOver = (e) => {
|
|
248
|
-
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
249
|
-
};
|
|
250
|
-
const onDrop = (e) => {
|
|
251
|
-
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
252
|
-
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) add(e.dataTransfer.files);
|
|
253
|
-
};
|
|
254
|
-
form.addEventListener("dragover", onDragOver);
|
|
255
|
-
form.addEventListener("drop", onDrop);
|
|
256
|
-
return () => {
|
|
257
|
-
form.removeEventListener("dragover", onDragOver);
|
|
258
|
-
form.removeEventListener("drop", onDrop);
|
|
259
|
-
};
|
|
260
|
-
}, [add]);
|
|
261
|
-
useEffect(() => {
|
|
262
|
-
if (!globalDrop) return;
|
|
263
|
-
const onDragOver = (e) => {
|
|
264
|
-
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
265
|
-
};
|
|
266
|
-
const onDrop = (e) => {
|
|
267
|
-
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
268
|
-
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) add(e.dataTransfer.files);
|
|
269
|
-
};
|
|
270
|
-
document.addEventListener("dragover", onDragOver);
|
|
271
|
-
document.addEventListener("drop", onDrop);
|
|
272
|
-
return () => {
|
|
273
|
-
document.removeEventListener("dragover", onDragOver);
|
|
274
|
-
document.removeEventListener("drop", onDrop);
|
|
275
|
-
};
|
|
276
|
-
}, [add, globalDrop]);
|
|
277
|
-
useEffect(() => {
|
|
278
|
-
return () => {
|
|
279
|
-
if (!usingProvider) {
|
|
280
|
-
for (const f of files) if (f.url) URL.revokeObjectURL(f.url);
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
}, [usingProvider, files]);
|
|
284
|
-
const handleFileInputChange = (event) => {
|
|
285
|
-
if (event.currentTarget.files) add(event.currentTarget.files);
|
|
286
|
-
};
|
|
287
|
-
const ctx = useMemo(() => ({
|
|
288
|
-
files,
|
|
289
|
-
add,
|
|
290
|
-
remove,
|
|
291
|
-
clear,
|
|
292
|
-
openFileDialog,
|
|
293
|
-
fileInputRef: inputRef
|
|
294
|
-
}), [
|
|
295
|
-
files,
|
|
296
|
-
add,
|
|
297
|
-
remove,
|
|
298
|
-
clear,
|
|
299
|
-
openFileDialog
|
|
300
|
-
]);
|
|
301
|
-
const handleSubmit = (event) => {
|
|
302
|
-
event.preventDefault();
|
|
303
|
-
const form = event.currentTarget;
|
|
304
|
-
const text = usingProvider ? controller.textInput.value : new FormData(form).get("message") || "";
|
|
305
|
-
if (!usingProvider) form.reset();
|
|
306
|
-
const doSubmit = async () => {
|
|
307
|
-
const convertedFiles = await Promise.all(files.map(async (item) => {
|
|
308
|
-
if (item.url.startsWith("blob:")) return {
|
|
309
|
-
...item,
|
|
310
|
-
url: await convertBlobToDataUrl(item.url)
|
|
311
|
-
};
|
|
312
|
-
return item;
|
|
313
|
-
}));
|
|
314
|
-
try {
|
|
315
|
-
await onSubmit({
|
|
316
|
-
text,
|
|
317
|
-
files: convertedFiles
|
|
318
|
-
}, event);
|
|
319
|
-
clear();
|
|
320
|
-
if (usingProvider) controller.textInput.clear();
|
|
321
|
-
} catch {}
|
|
322
|
-
};
|
|
323
|
-
doSubmit().catch(() => {});
|
|
324
|
-
};
|
|
325
|
-
const isBackLayerOpen = backLayerOpen ?? false;
|
|
326
|
-
const inputShell = hasBackLayer ? /* @__PURE__ */ jsxs("div", {
|
|
327
|
-
className: "flex w-full flex-col overflow-hidden rounded-xl bg-sf-elevated ring ring-sf-line/50",
|
|
328
|
-
children: [
|
|
329
|
-
/* @__PURE__ */ jsxs("button", {
|
|
330
|
-
"aria-expanded": isBackLayerOpen,
|
|
331
|
-
"aria-label": isBackLayerOpen ? "Collapse context" : "Expand context",
|
|
332
|
-
className: "flex w-full items-center justify-between gap-2 px-3 py-2 text-left",
|
|
333
|
-
onClick: () => onBackLayerOpenChange?.(!isBackLayerOpen),
|
|
334
|
-
type: "button",
|
|
335
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
336
|
-
className: "text-sm font-medium text-sf-subtle uppercase tracking-wide select-none",
|
|
337
|
-
children: backLayerTitle
|
|
338
|
-
}), /* @__PURE__ */ jsx(CaretDownIcon, { className: cn("size-3.5 text-sf-subtle transition-transform duration-200", isBackLayerOpen && "rotate-180") })]
|
|
339
|
-
}),
|
|
340
|
-
/* @__PURE__ */ jsx(PromptInputBackLayerPanel, {
|
|
341
|
-
open: isBackLayerOpen,
|
|
342
|
-
children: backLayer
|
|
343
|
-
}),
|
|
344
|
-
/* @__PURE__ */ jsx("div", {
|
|
345
|
-
className: "flex flex-col overflow-hidden rounded-xl bg-sf-base ring ring-sf-line/50 focus-within:ring-sf-ring",
|
|
346
|
-
children
|
|
347
|
-
})
|
|
348
|
-
]
|
|
349
|
-
}) : /* @__PURE__ */ jsx(InputGroup, { children });
|
|
350
|
-
const inner = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
351
|
-
/* @__PURE__ */ jsx("span", {
|
|
352
|
-
"aria-hidden": "true",
|
|
353
|
-
className: "hidden",
|
|
354
|
-
ref: anchorRef
|
|
355
|
-
}),
|
|
356
|
-
/* @__PURE__ */ jsx("input", {
|
|
357
|
-
accept,
|
|
358
|
-
"aria-label": "Upload files",
|
|
359
|
-
className: "hidden",
|
|
360
|
-
multiple,
|
|
361
|
-
onChange: handleFileInputChange,
|
|
362
|
-
ref: inputRef,
|
|
363
|
-
type: "file"
|
|
364
|
-
}),
|
|
365
|
-
/* @__PURE__ */ jsx("form", {
|
|
366
|
-
className: cn("w-full", className),
|
|
367
|
-
onSubmit: handleSubmit,
|
|
368
|
-
...props,
|
|
369
|
-
children: inputShell
|
|
370
|
-
})
|
|
371
|
-
] });
|
|
372
|
-
return usingProvider ? inner : /* @__PURE__ */ jsx(LocalAttachmentsContext.Provider, {
|
|
373
|
-
value: ctx,
|
|
374
|
-
children: inner
|
|
375
|
-
});
|
|
376
|
-
};
|
|
377
|
-
var PromptInputBody = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
378
|
-
className: cn("contents", className),
|
|
379
|
-
...props
|
|
380
|
-
});
|
|
381
|
-
function PromptInputBackLayerPanel({ open, children }) {
|
|
382
|
-
const isOpen = open ?? false;
|
|
383
|
-
return /* @__PURE__ */ jsx("div", {
|
|
384
|
-
"aria-hidden": !isOpen,
|
|
385
|
-
className: "grid",
|
|
386
|
-
style: {
|
|
387
|
-
gridTemplateRows: isOpen ? "1fr" : "0fr",
|
|
388
|
-
opacity: isOpen ? 1 : 0,
|
|
389
|
-
transition: "grid-template-rows 200ms ease-out, opacity 200ms ease-out"
|
|
390
|
-
},
|
|
391
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
392
|
-
className: "min-h-0 overflow-hidden",
|
|
393
|
-
children
|
|
394
|
-
})
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Container for back-layer content (HITL approvals, task lists, agent status,
|
|
399
|
-
* etc.). Pass instances of this as the `backLayer` prop on `PromptInput`.
|
|
400
|
-
*
|
|
401
|
-
* @example
|
|
402
|
-
* ```tsx
|
|
403
|
-
* <PromptInput
|
|
404
|
-
* onSubmit={handleSubmit}
|
|
405
|
-
* backLayer={<PromptInputBackLayer>...</PromptInputBackLayer>}
|
|
406
|
-
* backLayerOpen={open}
|
|
407
|
-
* onBackLayerOpenChange={setOpen}
|
|
408
|
-
* >
|
|
409
|
-
* <PromptInputTextarea />
|
|
410
|
-
* <PromptInputToolbar>
|
|
411
|
-
* <PromptInputTools />
|
|
412
|
-
* <PromptInputSubmit />
|
|
413
|
-
* </PromptInputToolbar>
|
|
414
|
-
* </PromptInput>
|
|
415
|
-
* ```
|
|
416
|
-
*/
|
|
417
|
-
function PromptInputBackLayer({ children, className }) {
|
|
418
|
-
return /* @__PURE__ */ jsx("div", {
|
|
419
|
-
className: cn("flex flex-col gap-2 px-3 py-2.5 text-sm text-sf-default", className),
|
|
420
|
-
children
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
var handleTextareaKeyDown = (e) => {
|
|
424
|
-
if (e.key === "Enter") {
|
|
425
|
-
if (e.nativeEvent.isComposing) return;
|
|
426
|
-
if (e.shiftKey) return;
|
|
427
|
-
e.preventDefault();
|
|
428
|
-
e.currentTarget.form?.requestSubmit();
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
/**
|
|
432
|
-
* Auto-resizing textarea for the prompt. Submits on Enter (Shift+Enter for newline).
|
|
433
|
-
* Pastes files into attachments.
|
|
434
|
-
*/
|
|
435
|
-
var PromptInputTextarea = ({ onChange, className, placeholder = "What would you like to know?", ...props }) => {
|
|
436
|
-
const controller = useOptionalPromptInputController();
|
|
437
|
-
const attachments = usePromptInputAttachments();
|
|
438
|
-
const handlePaste = (event) => {
|
|
439
|
-
const items = event.clipboardData?.items;
|
|
440
|
-
if (!items) return;
|
|
441
|
-
const pastedFiles = [];
|
|
442
|
-
for (const item of items) if (item.kind === "file") {
|
|
443
|
-
const file = item.getAsFile();
|
|
444
|
-
if (file) pastedFiles.push(file);
|
|
445
|
-
}
|
|
446
|
-
if (pastedFiles.length > 0) {
|
|
447
|
-
event.preventDefault();
|
|
448
|
-
attachments.add(pastedFiles);
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
const controlledProps = controller ? {
|
|
452
|
-
value: controller.textInput.value,
|
|
453
|
-
onChange: (e) => {
|
|
454
|
-
controller.textInput.setInput(e.currentTarget.value);
|
|
455
|
-
onChange?.(e);
|
|
456
|
-
}
|
|
457
|
-
} : { onChange };
|
|
458
|
-
return /* @__PURE__ */ jsx("textarea", {
|
|
459
|
-
className: cn("field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3 py-2 text-sm text-sf-default outline-none placeholder:text-sf-inactive", className),
|
|
460
|
-
name: "message",
|
|
461
|
-
onKeyDown: handleTextareaKeyDown,
|
|
462
|
-
onPaste: handlePaste,
|
|
463
|
-
placeholder,
|
|
464
|
-
rows: 1,
|
|
465
|
-
...props,
|
|
466
|
-
...controlledProps
|
|
467
|
-
});
|
|
468
|
-
};
|
|
469
|
-
var PromptInputToolbar = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
470
|
-
className: cn("flex items-center justify-between gap-1 px-2 py-1.5", className),
|
|
471
|
-
...props
|
|
472
|
-
});
|
|
473
|
-
var PromptInputTools = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
474
|
-
className: cn("flex items-center gap-1", className),
|
|
475
|
-
...props
|
|
476
|
-
});
|
|
477
|
-
var PromptInputButton = ({ variant = "ghost", size = "sm", className, ...props }) => /* @__PURE__ */ jsx(Button, {
|
|
478
|
-
className: cn("text-sf-subtle hover:text-sf-default", className),
|
|
479
|
-
size,
|
|
480
|
-
type: "button",
|
|
481
|
-
variant,
|
|
482
|
-
...props
|
|
483
|
-
});
|
|
484
|
-
/**
|
|
485
|
-
* Submit button. Icon changes based on `status`:
|
|
486
|
-
* - `idle` → send arrow
|
|
487
|
-
* - `submitted` → spinner
|
|
488
|
-
* - `streaming` → stop square
|
|
489
|
-
* - `error` → X
|
|
490
|
-
*/
|
|
491
|
-
var PromptInputSubmit = ({ className, variant = "primary", size = "sm", status = "idle", children, ...props }) => {
|
|
492
|
-
let Icon;
|
|
493
|
-
if (status === "submitted") Icon = /* @__PURE__ */ jsx(SpinnerGapIcon, { className: "size-4 animate-spin" });
|
|
494
|
-
else if (status === "streaming") Icon = /* @__PURE__ */ jsx(SquareIcon, { className: "size-4" });
|
|
495
|
-
else if (status === "error") Icon = /* @__PURE__ */ jsx(XIcon, { className: "size-4" });
|
|
496
|
-
else Icon = /* @__PURE__ */ jsx(ArrowUpIcon, { className: "size-4" });
|
|
497
|
-
return /* @__PURE__ */ jsx(Button, {
|
|
498
|
-
"aria-label": "Submit",
|
|
499
|
-
className: cn(className),
|
|
500
|
-
size,
|
|
501
|
-
type: "submit",
|
|
502
|
-
variant,
|
|
503
|
-
...props,
|
|
504
|
-
children: children ?? Icon
|
|
505
|
-
});
|
|
506
|
-
};
|
|
507
|
-
var DropdownMenu = Menu;
|
|
508
|
-
var PromptInputActionMenu = (props) => /* @__PURE__ */ jsx(DropdownMenu.Root, { ...props });
|
|
509
|
-
var PromptInputActionMenuTrigger = ({ className, children, "aria-label": ariaLabel = "More actions", ...props }) => /* @__PURE__ */ jsx(DropdownMenu.Trigger, { render: /* @__PURE__ */ jsx(Button, {
|
|
510
|
-
"aria-label": ariaLabel,
|
|
511
|
-
className: cn("text-sf-subtle hover:text-sf-default", className),
|
|
512
|
-
size: "sm",
|
|
513
|
-
type: "button",
|
|
514
|
-
variant: "ghost",
|
|
515
|
-
...props,
|
|
516
|
-
children: children ?? /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" })
|
|
517
|
-
}) });
|
|
518
|
-
var PromptInputActionMenuContent = ({ className, children, ...props }) => /* @__PURE__ */ jsx(DropdownMenu.Positioner, {
|
|
519
|
-
className: "z-50",
|
|
520
|
-
side: "top",
|
|
521
|
-
align: "start",
|
|
522
|
-
...props,
|
|
523
|
-
children: /* @__PURE__ */ jsx(DropdownMenu.Popup, {
|
|
524
|
-
className: cn("min-w-[160px] rounded-lg bg-sf-elevated p-1 shadow-md", "data-[starting-style]:opacity-0 data-[ending-style]:opacity-0 transition-opacity duration-100", className),
|
|
525
|
-
children
|
|
526
|
-
})
|
|
527
|
-
});
|
|
528
|
-
var PromptInputActionMenuItem = ({ className, ...props }) => /* @__PURE__ */ jsx(DropdownMenu.Item, {
|
|
529
|
-
className: cn("flex cursor-pointer select-none items-center gap-2 rounded-md px-2 py-1.5 text-sm text-sf-default outline-none", "data-highlighted:bg-sf-tint", className),
|
|
530
|
-
...props
|
|
531
|
-
});
|
|
532
|
-
var PromptInputActionAddAttachments = ({ label = "Add photos or files", ...props }) => {
|
|
533
|
-
const attachments = usePromptInputAttachments();
|
|
534
|
-
const handleSelect = useCallback(() => {
|
|
535
|
-
attachments.openFileDialog();
|
|
536
|
-
}, [attachments]);
|
|
537
|
-
return /* @__PURE__ */ jsxs(PromptInputActionMenuItem, {
|
|
538
|
-
...props,
|
|
539
|
-
onSelect: handleSelect,
|
|
540
|
-
children: [/* @__PURE__ */ jsx(ImageIcon, { className: "size-4" }), label]
|
|
541
|
-
});
|
|
542
|
-
};
|
|
543
|
-
var PromptInputModeSelector = ({ modes, value, onChange }) => {
|
|
544
|
-
if (modes.length === 0) return null;
|
|
545
|
-
const displayName = modes.find((m) => m.modeId === value)?.modeName ?? value ?? modes[0]?.modeName;
|
|
546
|
-
return /* @__PURE__ */ jsx("div", {
|
|
547
|
-
className: cn("flex items-center gap-0.5 rounded-md bg-sf-tint p-0.5"),
|
|
548
|
-
role: "group",
|
|
549
|
-
"aria-label": "Select mode",
|
|
550
|
-
children: modes.map((mode) => {
|
|
551
|
-
const isSelected = mode.modeId === value || mode.modeName === displayName;
|
|
552
|
-
return /* @__PURE__ */ jsx("button", {
|
|
553
|
-
"aria-pressed": isSelected,
|
|
554
|
-
className: cn("cursor-pointer rounded-[4px] px-2 py-0.5 text-[11px] font-medium transition-colors", isSelected ? "bg-sf-elevated text-sf-default shadow-sm" : "text-sf-subtle hover:text-sf-default"),
|
|
555
|
-
onClick: () => onChange?.(mode.modeId),
|
|
556
|
-
type: "button",
|
|
557
|
-
children: mode.modeName
|
|
558
|
-
}, mode.modeId);
|
|
559
|
-
})
|
|
560
|
-
});
|
|
561
|
-
};
|
|
562
|
-
var IMAGE_EXTS = new Set([
|
|
563
|
-
"jpg",
|
|
564
|
-
"jpeg",
|
|
565
|
-
"png",
|
|
566
|
-
"gif",
|
|
567
|
-
"webp",
|
|
568
|
-
"svg"
|
|
569
|
-
]);
|
|
570
|
-
function getFileIcon(filename) {
|
|
571
|
-
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
572
|
-
if (IMAGE_EXTS.has(ext)) return /* @__PURE__ */ jsx(ImageIcon, { className: "size-3.5" });
|
|
573
|
-
if ([
|
|
574
|
-
"pdf",
|
|
575
|
-
"doc",
|
|
576
|
-
"docx",
|
|
577
|
-
"txt",
|
|
578
|
-
"md"
|
|
579
|
-
].includes(ext)) return /* @__PURE__ */ jsx(FileTextIcon, { className: "size-3.5" });
|
|
580
|
-
return /* @__PURE__ */ jsx(FileIcon, { className: "size-3.5" });
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Single attachment chip for images or files. Includes a remove button.
|
|
584
|
-
*/
|
|
585
|
-
function PromptInputAttachment({ data, className, ...props }) {
|
|
586
|
-
const attachments = usePromptInputAttachments();
|
|
587
|
-
const isImage = data.mediaType?.startsWith("image/") && data.url;
|
|
588
|
-
const handleRemove = useCallback(() => {
|
|
589
|
-
attachments.remove(data.id);
|
|
590
|
-
}, [attachments, data.id]);
|
|
591
|
-
if (isImage) return /* @__PURE__ */ jsxs("div", {
|
|
592
|
-
className: cn("group relative size-14 overflow-hidden rounded-md", className),
|
|
593
|
-
...props,
|
|
594
|
-
children: [/* @__PURE__ */ jsx("img", {
|
|
595
|
-
alt: data.filename || "attachment",
|
|
596
|
-
className: "size-full object-cover",
|
|
597
|
-
src: data.url
|
|
598
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
599
|
-
"aria-label": "Remove attachment",
|
|
600
|
-
className: "absolute top-1 right-1 size-5 rounded-full bg-sf-overlay/80 p-0 opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100",
|
|
601
|
-
onClick: handleRemove,
|
|
602
|
-
size: "sm",
|
|
603
|
-
type: "button",
|
|
604
|
-
variant: "ghost",
|
|
605
|
-
children: /* @__PURE__ */ jsx(XIcon, { className: "size-3" })
|
|
606
|
-
})]
|
|
607
|
-
});
|
|
608
|
-
const ext = data.filename.split(".").pop()?.toUpperCase() ?? "FILE";
|
|
609
|
-
const name = data.filename.replace(/\.[^/.]+$/, "");
|
|
610
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
611
|
-
className: cn("group relative inline-flex h-8 items-center gap-2 rounded-md bg-sf-tint/50 px-2 text-sm", className),
|
|
612
|
-
...props,
|
|
613
|
-
children: [
|
|
614
|
-
/* @__PURE__ */ jsx("div", {
|
|
615
|
-
className: "flex size-5 shrink-0 items-center justify-center text-sf-subtle",
|
|
616
|
-
children: getFileIcon(data.filename)
|
|
617
|
-
}),
|
|
618
|
-
/* @__PURE__ */ jsx(Tooltip, {
|
|
619
|
-
content: data.filename,
|
|
620
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
621
|
-
className: "max-w-[160px] truncate font-medium text-sf-default",
|
|
622
|
-
children: name || "File"
|
|
623
|
-
})
|
|
624
|
-
}),
|
|
625
|
-
/* @__PURE__ */ jsx("span", {
|
|
626
|
-
className: "rounded bg-sf-tint px-1 py-0.5 font-semibold text-sf-subtle text-[10px] uppercase",
|
|
627
|
-
children: ext
|
|
628
|
-
}),
|
|
629
|
-
/* @__PURE__ */ jsx(Button, {
|
|
630
|
-
"aria-label": "Remove attachment",
|
|
631
|
-
className: "ml-0.5 size-4 shrink-0 rounded-full p-0 opacity-0 transition-opacity group-hover:opacity-100",
|
|
632
|
-
onClick: handleRemove,
|
|
633
|
-
size: "sm",
|
|
634
|
-
type: "button",
|
|
635
|
-
variant: "ghost",
|
|
636
|
-
children: /* @__PURE__ */ jsx(XIcon, { className: "size-3" })
|
|
637
|
-
})
|
|
638
|
-
]
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Renders all attachments using a render prop. Hidden when no files.
|
|
643
|
-
* Animates height as files are added/removed.
|
|
644
|
-
*/
|
|
645
|
-
function PromptInputAttachments({ className, children, ...props }) {
|
|
646
|
-
const attachments = usePromptInputAttachments();
|
|
647
|
-
const [height, setHeight] = useState(0);
|
|
648
|
-
const contentRef = useRef(null);
|
|
649
|
-
useLayoutEffect(() => {
|
|
650
|
-
const el = contentRef.current;
|
|
651
|
-
if (!el) return;
|
|
652
|
-
const ro = new ResizeObserver(() => {
|
|
653
|
-
setHeight(el.getBoundingClientRect().height);
|
|
654
|
-
});
|
|
655
|
-
ro.observe(el);
|
|
656
|
-
setHeight(el.getBoundingClientRect().height);
|
|
657
|
-
return () => ro.disconnect();
|
|
658
|
-
}, []);
|
|
659
|
-
useLayoutEffect(() => {
|
|
660
|
-
const el = contentRef.current;
|
|
661
|
-
if (el) setHeight(el.getBoundingClientRect().height);
|
|
662
|
-
}, [attachments.files.length]);
|
|
663
|
-
if (attachments.files.length === 0) return null;
|
|
664
|
-
return /* @__PURE__ */ jsx("div", {
|
|
665
|
-
"aria-live": "polite",
|
|
666
|
-
className: cn("overflow-hidden px-2 transition-[height] duration-200 ease-out", className),
|
|
667
|
-
style: { height: attachments.files.length ? height : 0 },
|
|
668
|
-
...props,
|
|
669
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
670
|
-
className: "flex flex-wrap gap-2 py-1",
|
|
671
|
-
ref: contentRef,
|
|
672
|
-
children: attachments.files.map((file) => /* @__PURE__ */ jsx(Fragment, { children: children(file) }, file.id))
|
|
673
|
-
})
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Voice input button. Uses the Web Speech API (Chrome/Edge). Hidden on unsupported browsers.
|
|
678
|
-
* Pulses while listening.
|
|
679
|
-
*/
|
|
680
|
-
var PromptInputSpeechButton = ({ className, textareaRef, onTranscriptionChange, "aria-label": ariaLabel = "Voice input", ...props }) => {
|
|
681
|
-
const [isListening, setIsListening] = useState(false);
|
|
682
|
-
const [recognition, setRecognition] = useState(null);
|
|
683
|
-
const recognitionRef = useRef(null);
|
|
684
|
-
useEffect(() => {
|
|
685
|
-
if (typeof window === "undefined") return;
|
|
686
|
-
const SR = window.SpeechRecognition ?? window.webkitSpeechRecognition;
|
|
687
|
-
if (!SR) return;
|
|
688
|
-
const sr = new SR();
|
|
689
|
-
sr.continuous = true;
|
|
690
|
-
sr.interimResults = true;
|
|
691
|
-
sr.lang = "en-US";
|
|
692
|
-
const handleStart = () => setIsListening(true);
|
|
693
|
-
const handleEnd = () => setIsListening(false);
|
|
694
|
-
const handleResult = (event) => {
|
|
695
|
-
const e = event;
|
|
696
|
-
let final = "";
|
|
697
|
-
for (const result of Array.from({ length: e.results.length }, (_, i) => e.results[i])) if (result?.isFinal && result[0]?.transcript) final += result[0].transcript;
|
|
698
|
-
if (final && textareaRef?.current) {
|
|
699
|
-
const ta = textareaRef.current;
|
|
700
|
-
const next = ta.value + (ta.value ? " " : "") + final;
|
|
701
|
-
ta.value = next;
|
|
702
|
-
ta.dispatchEvent(new Event("input", { bubbles: true }));
|
|
703
|
-
onTranscriptionChange?.(next);
|
|
704
|
-
}
|
|
705
|
-
};
|
|
706
|
-
const handleError = (_event) => setIsListening(false);
|
|
707
|
-
sr.addEventListener("start", handleStart);
|
|
708
|
-
sr.addEventListener("end", handleEnd);
|
|
709
|
-
sr.addEventListener("result", handleResult);
|
|
710
|
-
sr.addEventListener("error", handleError);
|
|
711
|
-
recognitionRef.current = sr;
|
|
712
|
-
setRecognition(sr);
|
|
713
|
-
return () => {
|
|
714
|
-
sr.removeEventListener("start", handleStart);
|
|
715
|
-
sr.removeEventListener("end", handleEnd);
|
|
716
|
-
sr.removeEventListener("result", handleResult);
|
|
717
|
-
sr.removeEventListener("error", handleError);
|
|
718
|
-
recognitionRef.current?.stop();
|
|
719
|
-
};
|
|
720
|
-
}, [textareaRef, onTranscriptionChange]);
|
|
721
|
-
const handleToggleListening = useCallback(() => {
|
|
722
|
-
if (!recognition) return;
|
|
723
|
-
const sr = recognition;
|
|
724
|
-
if (isListening) sr.stop();
|
|
725
|
-
else sr.start();
|
|
726
|
-
}, [recognition, isListening]);
|
|
727
|
-
const button = /* @__PURE__ */ jsx(Button, {
|
|
728
|
-
"aria-label": ariaLabel,
|
|
729
|
-
className: cn("text-sf-subtle hover:text-sf-default transition-all", isListening && "animate-pulse text-sf-brand", className),
|
|
730
|
-
disabled: !recognition,
|
|
731
|
-
onClick: handleToggleListening,
|
|
732
|
-
size: "sm",
|
|
733
|
-
type: "button",
|
|
734
|
-
variant: "ghost",
|
|
735
|
-
...props,
|
|
736
|
-
children: /* @__PURE__ */ jsx(MicrophoneIcon, { className: "size-4" })
|
|
737
|
-
});
|
|
738
|
-
if (!recognition) return null;
|
|
739
|
-
return /* @__PURE__ */ jsx(Tooltip, {
|
|
740
|
-
content: isListening ? "Stop listening" : "Voice input",
|
|
741
|
-
children: button
|
|
742
|
-
});
|
|
743
|
-
};
|
|
744
|
-
/**
|
|
745
|
-
* Convenience button that opens the file dialog directly (no dropdown needed).
|
|
746
|
-
*/
|
|
747
|
-
var PromptInputAttachButton = ({ "aria-label": ariaLabel = "Attach file", className, ...props }) => {
|
|
748
|
-
const attachments = usePromptInputAttachments();
|
|
749
|
-
const handleClick = useCallback(() => {
|
|
750
|
-
attachments.openFileDialog();
|
|
751
|
-
}, [attachments]);
|
|
752
|
-
return /* @__PURE__ */ jsx(Tooltip, {
|
|
753
|
-
content: ariaLabel,
|
|
754
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
755
|
-
"aria-label": ariaLabel,
|
|
756
|
-
className: cn("text-sf-subtle hover:text-sf-default", className),
|
|
757
|
-
onClick: handleClick,
|
|
758
|
-
size: "sm",
|
|
759
|
-
type: "button",
|
|
760
|
-
variant: "ghost",
|
|
761
|
-
...props,
|
|
762
|
-
children: /* @__PURE__ */ jsx(PaperclipIcon, { className: "size-4" })
|
|
763
|
-
})
|
|
764
|
-
});
|
|
765
|
-
};
|
|
766
|
-
//#endregion
|
|
767
|
-
export { usePromptInputController as C, usePromptInputAttachments as S, PromptInputTextarea as _, PromptInputActionMenuItem as a, SF_AI_PROMPT_INPUT_DEFAULT_VARIANTS as b, PromptInputAttachment as c, PromptInputBody as d, PromptInputButton as f, PromptInputSubmit as g, PromptInputSpeechButton as h, PromptInputActionMenuContent as i, PromptInputAttachments as l, PromptInputProvider as m, PromptInputActionAddAttachments as n, PromptInputActionMenuTrigger as o, PromptInputModeSelector as p, PromptInputActionMenu as r, PromptInputAttachButton as s, PromptInput as t, PromptInputBackLayer as u, PromptInputToolbar as v, useProviderAttachments as w, SF_AI_PROMPT_INPUT_VARIANTS as x, PromptInputTools as y };
|
|
768
|
-
|
|
769
|
-
//# sourceMappingURL=ai-prompt-input-Bo1YuJly.js.map
|