@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
|
@@ -0,0 +1,1541 @@
|
|
|
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-DddtBN-g.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, ArrowsClockwiseIcon, CaretDownIcon, CheckIcon, FileIcon, FileTextIcon, ImageIcon, MicrophoneIcon, PaperclipIcon, PlusIcon, SpinnerGapIcon, SquareIcon, XIcon } from "@phosphor-icons/react";
|
|
9
|
+
import { Menu } from "@base-ui/react/menu";
|
|
10
|
+
import { Select } from "@base-ui/react/select";
|
|
11
|
+
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector";
|
|
12
|
+
//#region ../../node_modules/.bun/@tanstack+store@0.11.0/node_modules/@tanstack/store/dist/alien.js
|
|
13
|
+
var ReactiveFlags = /* @__PURE__ */ function(ReactiveFlags) {
|
|
14
|
+
ReactiveFlags[ReactiveFlags["None"] = 0] = "None";
|
|
15
|
+
ReactiveFlags[ReactiveFlags["Mutable"] = 1] = "Mutable";
|
|
16
|
+
ReactiveFlags[ReactiveFlags["Watching"] = 2] = "Watching";
|
|
17
|
+
ReactiveFlags[ReactiveFlags["RecursedCheck"] = 4] = "RecursedCheck";
|
|
18
|
+
ReactiveFlags[ReactiveFlags["Recursed"] = 8] = "Recursed";
|
|
19
|
+
ReactiveFlags[ReactiveFlags["Dirty"] = 16] = "Dirty";
|
|
20
|
+
ReactiveFlags[ReactiveFlags["Pending"] = 32] = "Pending";
|
|
21
|
+
return ReactiveFlags;
|
|
22
|
+
}({});
|
|
23
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
24
|
+
function createReactiveSystem({ update, notify, unwatched }) {
|
|
25
|
+
return {
|
|
26
|
+
link,
|
|
27
|
+
unlink,
|
|
28
|
+
propagate,
|
|
29
|
+
checkDirty,
|
|
30
|
+
shallowPropagate
|
|
31
|
+
};
|
|
32
|
+
function link(dep, sub, version) {
|
|
33
|
+
const prevDep = sub.depsTail;
|
|
34
|
+
if (prevDep !== void 0 && prevDep.dep === dep) return;
|
|
35
|
+
const nextDep = prevDep !== void 0 ? prevDep.nextDep : sub.deps;
|
|
36
|
+
if (nextDep !== void 0 && nextDep.dep === dep) {
|
|
37
|
+
nextDep.version = version;
|
|
38
|
+
sub.depsTail = nextDep;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const prevSub = dep.subsTail;
|
|
42
|
+
if (prevSub !== void 0 && prevSub.version === version && prevSub.sub === sub) return;
|
|
43
|
+
const newLink = sub.depsTail = dep.subsTail = {
|
|
44
|
+
version,
|
|
45
|
+
dep,
|
|
46
|
+
sub,
|
|
47
|
+
prevDep,
|
|
48
|
+
nextDep,
|
|
49
|
+
prevSub,
|
|
50
|
+
nextSub: void 0
|
|
51
|
+
};
|
|
52
|
+
if (nextDep !== void 0) nextDep.prevDep = newLink;
|
|
53
|
+
if (prevDep !== void 0) prevDep.nextDep = newLink;
|
|
54
|
+
else sub.deps = newLink;
|
|
55
|
+
if (prevSub !== void 0) prevSub.nextSub = newLink;
|
|
56
|
+
else dep.subs = newLink;
|
|
57
|
+
}
|
|
58
|
+
function unlink(link, sub = link.sub) {
|
|
59
|
+
const dep = link.dep;
|
|
60
|
+
const prevDep = link.prevDep;
|
|
61
|
+
const nextDep = link.nextDep;
|
|
62
|
+
const nextSub = link.nextSub;
|
|
63
|
+
const prevSub = link.prevSub;
|
|
64
|
+
if (nextDep !== void 0) nextDep.prevDep = prevDep;
|
|
65
|
+
else sub.depsTail = prevDep;
|
|
66
|
+
if (prevDep !== void 0) prevDep.nextDep = nextDep;
|
|
67
|
+
else sub.deps = nextDep;
|
|
68
|
+
if (nextSub !== void 0) nextSub.prevSub = prevSub;
|
|
69
|
+
else dep.subsTail = prevSub;
|
|
70
|
+
if (prevSub !== void 0) prevSub.nextSub = nextSub;
|
|
71
|
+
else if ((dep.subs = nextSub) === void 0) unwatched(dep);
|
|
72
|
+
return nextDep;
|
|
73
|
+
}
|
|
74
|
+
function propagate(link) {
|
|
75
|
+
let next = link.nextSub;
|
|
76
|
+
let stack;
|
|
77
|
+
top: do {
|
|
78
|
+
const sub = link.sub;
|
|
79
|
+
let flags = sub.flags;
|
|
80
|
+
if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed | ReactiveFlags.Dirty | ReactiveFlags.Pending))) sub.flags = flags | ReactiveFlags.Pending;
|
|
81
|
+
else if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed))) flags = ReactiveFlags.None;
|
|
82
|
+
else if (!(flags & ReactiveFlags.RecursedCheck)) sub.flags = flags & ~ReactiveFlags.Recursed | ReactiveFlags.Pending;
|
|
83
|
+
else if (!(flags & (ReactiveFlags.Dirty | ReactiveFlags.Pending)) && isValidLink(link, sub)) {
|
|
84
|
+
sub.flags = flags | (ReactiveFlags.Recursed | ReactiveFlags.Pending);
|
|
85
|
+
flags &= ReactiveFlags.Mutable;
|
|
86
|
+
} else flags = ReactiveFlags.None;
|
|
87
|
+
if (flags & ReactiveFlags.Watching) notify(sub);
|
|
88
|
+
if (flags & ReactiveFlags.Mutable) {
|
|
89
|
+
const subSubs = sub.subs;
|
|
90
|
+
if (subSubs !== void 0) {
|
|
91
|
+
const nextSub = (link = subSubs).nextSub;
|
|
92
|
+
if (nextSub !== void 0) {
|
|
93
|
+
stack = {
|
|
94
|
+
value: next,
|
|
95
|
+
prev: stack
|
|
96
|
+
};
|
|
97
|
+
next = nextSub;
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if ((link = next) !== void 0) {
|
|
103
|
+
next = link.nextSub;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
while (stack !== void 0) {
|
|
107
|
+
link = stack.value;
|
|
108
|
+
stack = stack.prev;
|
|
109
|
+
if (link !== void 0) {
|
|
110
|
+
next = link.nextSub;
|
|
111
|
+
continue top;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
} while (true);
|
|
116
|
+
}
|
|
117
|
+
function checkDirty(link, sub) {
|
|
118
|
+
let stack;
|
|
119
|
+
let checkDepth = 0;
|
|
120
|
+
let dirty = false;
|
|
121
|
+
top: do {
|
|
122
|
+
const dep = link.dep;
|
|
123
|
+
const flags = dep.flags;
|
|
124
|
+
if (sub.flags & ReactiveFlags.Dirty) dirty = true;
|
|
125
|
+
else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) === (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) {
|
|
126
|
+
if (update(dep)) {
|
|
127
|
+
const subs = dep.subs;
|
|
128
|
+
if (subs.nextSub !== void 0) shallowPropagate(subs);
|
|
129
|
+
dirty = true;
|
|
130
|
+
}
|
|
131
|
+
} else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Pending)) === (ReactiveFlags.Mutable | ReactiveFlags.Pending)) {
|
|
132
|
+
if (link.nextSub !== void 0 || link.prevSub !== void 0) stack = {
|
|
133
|
+
value: link,
|
|
134
|
+
prev: stack
|
|
135
|
+
};
|
|
136
|
+
link = dep.deps;
|
|
137
|
+
sub = dep;
|
|
138
|
+
++checkDepth;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!dirty) {
|
|
142
|
+
const nextDep = link.nextDep;
|
|
143
|
+
if (nextDep !== void 0) {
|
|
144
|
+
link = nextDep;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
while (checkDepth--) {
|
|
149
|
+
const firstSub = sub.subs;
|
|
150
|
+
const hasMultipleSubs = firstSub.nextSub !== void 0;
|
|
151
|
+
if (hasMultipleSubs) {
|
|
152
|
+
link = stack.value;
|
|
153
|
+
stack = stack.prev;
|
|
154
|
+
} else link = firstSub;
|
|
155
|
+
if (dirty) {
|
|
156
|
+
if (update(sub)) {
|
|
157
|
+
if (hasMultipleSubs) shallowPropagate(firstSub);
|
|
158
|
+
sub = link.sub;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
dirty = false;
|
|
162
|
+
} else sub.flags &= ~ReactiveFlags.Pending;
|
|
163
|
+
sub = link.sub;
|
|
164
|
+
const nextDep = link.nextDep;
|
|
165
|
+
if (nextDep !== void 0) {
|
|
166
|
+
link = nextDep;
|
|
167
|
+
continue top;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return dirty;
|
|
171
|
+
} while (true);
|
|
172
|
+
}
|
|
173
|
+
function shallowPropagate(link) {
|
|
174
|
+
do {
|
|
175
|
+
const sub = link.sub;
|
|
176
|
+
const flags = sub.flags;
|
|
177
|
+
if ((flags & (ReactiveFlags.Pending | ReactiveFlags.Dirty)) === ReactiveFlags.Pending) {
|
|
178
|
+
sub.flags = flags | ReactiveFlags.Dirty;
|
|
179
|
+
if ((flags & (ReactiveFlags.Watching | ReactiveFlags.RecursedCheck)) === ReactiveFlags.Watching) notify(sub);
|
|
180
|
+
}
|
|
181
|
+
} while ((link = link.nextSub) !== void 0);
|
|
182
|
+
}
|
|
183
|
+
function isValidLink(checkLink, sub) {
|
|
184
|
+
let link = sub.depsTail;
|
|
185
|
+
while (link !== void 0) {
|
|
186
|
+
if (link === checkLink) return true;
|
|
187
|
+
link = link.prevDep;
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region ../../node_modules/.bun/@tanstack+store@0.11.0/node_modules/@tanstack/store/dist/atom.js
|
|
194
|
+
function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
195
|
+
const isObserver = typeof nextHandler === "object";
|
|
196
|
+
const self = isObserver ? nextHandler : void 0;
|
|
197
|
+
return {
|
|
198
|
+
next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
|
|
199
|
+
error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
|
|
200
|
+
complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
var queuedEffects = [];
|
|
204
|
+
var cycle = 0;
|
|
205
|
+
var { link, unlink, propagate, checkDirty, shallowPropagate } = /* @__PURE__ */ createReactiveSystem({
|
|
206
|
+
update(atom) {
|
|
207
|
+
return atom._update();
|
|
208
|
+
},
|
|
209
|
+
notify(effect) {
|
|
210
|
+
queuedEffects[queuedEffectsLength++] = effect;
|
|
211
|
+
effect.flags &= ~ReactiveFlags.Watching;
|
|
212
|
+
},
|
|
213
|
+
unwatched(atom) {
|
|
214
|
+
if (atom.depsTail !== void 0) {
|
|
215
|
+
atom.depsTail = void 0;
|
|
216
|
+
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;
|
|
217
|
+
purgeDeps(atom);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
var notifyIndex = 0;
|
|
222
|
+
var queuedEffectsLength = 0;
|
|
223
|
+
var activeSub;
|
|
224
|
+
var batchDepth = 0;
|
|
225
|
+
function purgeDeps(sub) {
|
|
226
|
+
const depsTail = sub.depsTail;
|
|
227
|
+
let dep = depsTail !== void 0 ? depsTail.nextDep : sub.deps;
|
|
228
|
+
while (dep !== void 0) dep = unlink(dep, sub);
|
|
229
|
+
}
|
|
230
|
+
function flush() {
|
|
231
|
+
if (batchDepth > 0) return;
|
|
232
|
+
while (notifyIndex < queuedEffectsLength) {
|
|
233
|
+
const effect = queuedEffects[notifyIndex];
|
|
234
|
+
queuedEffects[notifyIndex++] = void 0;
|
|
235
|
+
effect.notify();
|
|
236
|
+
}
|
|
237
|
+
notifyIndex = 0;
|
|
238
|
+
queuedEffectsLength = 0;
|
|
239
|
+
}
|
|
240
|
+
function createAtom(valueOrFn, options) {
|
|
241
|
+
const isComputed = typeof valueOrFn === "function";
|
|
242
|
+
const getter = valueOrFn;
|
|
243
|
+
const atom = {
|
|
244
|
+
_snapshot: isComputed ? void 0 : valueOrFn,
|
|
245
|
+
subs: void 0,
|
|
246
|
+
subsTail: void 0,
|
|
247
|
+
deps: void 0,
|
|
248
|
+
depsTail: void 0,
|
|
249
|
+
flags: isComputed ? ReactiveFlags.None : ReactiveFlags.Mutable,
|
|
250
|
+
get() {
|
|
251
|
+
if (activeSub !== void 0) link(atom, activeSub, cycle);
|
|
252
|
+
return atom._snapshot;
|
|
253
|
+
},
|
|
254
|
+
subscribe(observerOrFn) {
|
|
255
|
+
const obs = toObserver(observerOrFn);
|
|
256
|
+
const observed = { current: false };
|
|
257
|
+
const e = effect(() => {
|
|
258
|
+
atom.get();
|
|
259
|
+
if (!observed.current) observed.current = true;
|
|
260
|
+
else obs.next?.(atom._snapshot);
|
|
261
|
+
});
|
|
262
|
+
return { unsubscribe: () => {
|
|
263
|
+
e.stop();
|
|
264
|
+
} };
|
|
265
|
+
},
|
|
266
|
+
_update(getValue) {
|
|
267
|
+
const prevSub = activeSub;
|
|
268
|
+
const compare = options?.compare ?? Object.is;
|
|
269
|
+
if (isComputed) {
|
|
270
|
+
activeSub = atom;
|
|
271
|
+
++cycle;
|
|
272
|
+
atom.depsTail = void 0;
|
|
273
|
+
} else if (getValue === void 0) return false;
|
|
274
|
+
if (isComputed) atom.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck;
|
|
275
|
+
try {
|
|
276
|
+
const oldValue = atom._snapshot;
|
|
277
|
+
const newValue = typeof getValue === "function" ? getValue(oldValue) : getValue === void 0 && isComputed ? getter(oldValue) : getValue;
|
|
278
|
+
if (oldValue === void 0 || !compare(oldValue, newValue)) {
|
|
279
|
+
atom._snapshot = newValue;
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
} finally {
|
|
284
|
+
activeSub = prevSub;
|
|
285
|
+
if (isComputed) atom.flags &= ~ReactiveFlags.RecursedCheck;
|
|
286
|
+
purgeDeps(atom);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
if (isComputed) {
|
|
291
|
+
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;
|
|
292
|
+
atom.get = function() {
|
|
293
|
+
const flags = atom.flags;
|
|
294
|
+
if (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(atom.deps, atom)) {
|
|
295
|
+
if (atom._update()) {
|
|
296
|
+
const subs = atom.subs;
|
|
297
|
+
if (subs !== void 0) shallowPropagate(subs);
|
|
298
|
+
}
|
|
299
|
+
} else if (flags & ReactiveFlags.Pending) atom.flags = flags & ~ReactiveFlags.Pending;
|
|
300
|
+
if (activeSub !== void 0) link(atom, activeSub, cycle);
|
|
301
|
+
return atom._snapshot;
|
|
302
|
+
};
|
|
303
|
+
} else atom.set = function(valueOrFn) {
|
|
304
|
+
if (atom._update(valueOrFn)) {
|
|
305
|
+
const subs = atom.subs;
|
|
306
|
+
if (subs !== void 0) {
|
|
307
|
+
propagate(subs);
|
|
308
|
+
shallowPropagate(subs);
|
|
309
|
+
flush();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
return atom;
|
|
314
|
+
}
|
|
315
|
+
function effect(fn) {
|
|
316
|
+
const run = () => {
|
|
317
|
+
const prevSub = activeSub;
|
|
318
|
+
activeSub = effectObj;
|
|
319
|
+
++cycle;
|
|
320
|
+
effectObj.depsTail = void 0;
|
|
321
|
+
effectObj.flags = ReactiveFlags.Watching | ReactiveFlags.RecursedCheck;
|
|
322
|
+
try {
|
|
323
|
+
return fn();
|
|
324
|
+
} finally {
|
|
325
|
+
activeSub = prevSub;
|
|
326
|
+
effectObj.flags &= ~ReactiveFlags.RecursedCheck;
|
|
327
|
+
purgeDeps(effectObj);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
const effectObj = {
|
|
331
|
+
deps: void 0,
|
|
332
|
+
depsTail: void 0,
|
|
333
|
+
subs: void 0,
|
|
334
|
+
subsTail: void 0,
|
|
335
|
+
flags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck,
|
|
336
|
+
notify() {
|
|
337
|
+
const flags = this.flags;
|
|
338
|
+
if (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(this.deps, this)) run();
|
|
339
|
+
else this.flags = ReactiveFlags.Watching;
|
|
340
|
+
},
|
|
341
|
+
stop() {
|
|
342
|
+
this.flags = ReactiveFlags.None;
|
|
343
|
+
this.depsTail = void 0;
|
|
344
|
+
purgeDeps(this);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
run();
|
|
348
|
+
return effectObj;
|
|
349
|
+
}
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region ../../node_modules/.bun/@tanstack+store@0.11.0/node_modules/@tanstack/store/dist/store.js
|
|
352
|
+
var Store = class {
|
|
353
|
+
constructor(valueOrFn, actionsFactory) {
|
|
354
|
+
this.atom = createAtom(valueOrFn);
|
|
355
|
+
this.get = this.get.bind(this);
|
|
356
|
+
this.setState = this.setState.bind(this);
|
|
357
|
+
this.subscribe = this.subscribe.bind(this);
|
|
358
|
+
if (actionsFactory) this.actions = actionsFactory(this);
|
|
359
|
+
}
|
|
360
|
+
setState(updater) {
|
|
361
|
+
this.atom.set(updater);
|
|
362
|
+
}
|
|
363
|
+
get state() {
|
|
364
|
+
return this.atom.get();
|
|
365
|
+
}
|
|
366
|
+
get() {
|
|
367
|
+
return this.state;
|
|
368
|
+
}
|
|
369
|
+
subscribe(observerOrFn) {
|
|
370
|
+
return this.atom.subscribe(toObserver(observerOrFn));
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region ../../node_modules/.bun/@tanstack+react-store@0.11.0+21ccd8898788a04d/node_modules/@tanstack/react-store/dist/useSelector.js
|
|
375
|
+
function defaultCompare(a, b) {
|
|
376
|
+
return a === b;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Selects a slice of state from an atom or store and subscribes the component
|
|
380
|
+
* to that selection.
|
|
381
|
+
*
|
|
382
|
+
* This is the primary React read hook for TanStack Store. It works with any
|
|
383
|
+
* source that exposes `get()` and `subscribe()`, including atoms, readonly
|
|
384
|
+
* atoms, stores, and readonly stores.
|
|
385
|
+
*
|
|
386
|
+
* Omit the selector to subscribe to the whole value.
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```tsx
|
|
390
|
+
* const count = useSelector(counterStore, (state) => state.count)
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* ```tsx
|
|
395
|
+
* const value = useSelector(countAtom)
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
function useSelector(source, selector = (s) => s, options) {
|
|
399
|
+
const compare = options?.compare ?? defaultCompare;
|
|
400
|
+
const subscribe = useCallback((handleStoreChange) => {
|
|
401
|
+
const { unsubscribe } = source.subscribe(handleStoreChange);
|
|
402
|
+
return unsubscribe;
|
|
403
|
+
}, [source]);
|
|
404
|
+
const getSnapshot = useCallback(() => source.get(), [source]);
|
|
405
|
+
return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, selector, compare);
|
|
406
|
+
}
|
|
407
|
+
//#endregion
|
|
408
|
+
//#region ../../node_modules/.bun/@tanstack+react-store@0.11.0+21ccd8898788a04d/node_modules/@tanstack/react-store/dist/useStore.js
|
|
409
|
+
/**
|
|
410
|
+
* Deprecated alias for {@link useSelector}.
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* ```tsx
|
|
414
|
+
* const count = useStore(counterStore, (state) => state.count)
|
|
415
|
+
* ```
|
|
416
|
+
*
|
|
417
|
+
* @deprecated Use `useSelector` instead.
|
|
418
|
+
*/
|
|
419
|
+
var useStore = (source, selector = (s) => s, compare) => useSelector(source, selector, { compare });
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/components/ai-prompt-input/controller.ts
|
|
422
|
+
/**
|
|
423
|
+
* PromptInputController — the single source of truth for a prompt input tree.
|
|
424
|
+
*
|
|
425
|
+
* Built on @tanstack/store for per-slice subscriptions (sub-components
|
|
426
|
+
* re-render only when the fields they read change). The controller exposes
|
|
427
|
+
* two stores:
|
|
428
|
+
*
|
|
429
|
+
* - `request` : outbound state (text, files, mode, model, …extensions). The
|
|
430
|
+
* user edits these; `submit()` ships them to `onSubmit`.
|
|
431
|
+
* - `display` : inbound state (sendStatus, tasks, pendingQuestion, pendingPlan,
|
|
432
|
+
* usage). The harness writes these in; sub-components read them.
|
|
433
|
+
*
|
|
434
|
+
* Consumers never touch the stores directly. They use:
|
|
435
|
+
*
|
|
436
|
+
* const mode = useRequestField('mode'); // read
|
|
437
|
+
* const setMode = useSetRequestField('mode'); // write
|
|
438
|
+
* const status = useDisplayField('sendStatus'); // read
|
|
439
|
+
*
|
|
440
|
+
* Or, for advanced use, subscribe to the whole store with
|
|
441
|
+
* `useStore(controller.request)`.
|
|
442
|
+
*/
|
|
443
|
+
var DEFAULT_REQUEST = {
|
|
444
|
+
text: "",
|
|
445
|
+
files: []
|
|
446
|
+
};
|
|
447
|
+
var DEFAULT_DISPLAY = { sendStatus: "idle" };
|
|
448
|
+
function createPromptInputRequestController(options = {}) {
|
|
449
|
+
const request = new Store({
|
|
450
|
+
...DEFAULT_REQUEST,
|
|
451
|
+
...options.initialRequest
|
|
452
|
+
});
|
|
453
|
+
const display = new Store({
|
|
454
|
+
...DEFAULT_DISPLAY,
|
|
455
|
+
...options.initialDisplay
|
|
456
|
+
});
|
|
457
|
+
return {
|
|
458
|
+
request,
|
|
459
|
+
display,
|
|
460
|
+
setRequestField(key, value) {
|
|
461
|
+
request.setState((prev) => ({
|
|
462
|
+
...prev,
|
|
463
|
+
[key]: value
|
|
464
|
+
}));
|
|
465
|
+
},
|
|
466
|
+
setDisplayField(key, value) {
|
|
467
|
+
display.setState((prev) => ({
|
|
468
|
+
...prev,
|
|
469
|
+
[key]: value
|
|
470
|
+
}));
|
|
471
|
+
},
|
|
472
|
+
addAttachments(files) {
|
|
473
|
+
request.setState((prev) => ({
|
|
474
|
+
...prev,
|
|
475
|
+
files: [...prev.files, ...files]
|
|
476
|
+
}));
|
|
477
|
+
},
|
|
478
|
+
removeAttachment(id) {
|
|
479
|
+
request.setState((prev) => {
|
|
480
|
+
const removed = prev.files.find((f) => f.id === id);
|
|
481
|
+
if (removed?.url.startsWith("blob:")) URL.revokeObjectURL(removed.url);
|
|
482
|
+
return {
|
|
483
|
+
...prev,
|
|
484
|
+
files: prev.files.filter((f) => f.id !== id)
|
|
485
|
+
};
|
|
486
|
+
});
|
|
487
|
+
},
|
|
488
|
+
clearAttachments() {
|
|
489
|
+
request.setState((prev) => {
|
|
490
|
+
for (const f of prev.files) if (f.url.startsWith("blob:")) URL.revokeObjectURL(f.url);
|
|
491
|
+
return {
|
|
492
|
+
...prev,
|
|
493
|
+
files: []
|
|
494
|
+
};
|
|
495
|
+
});
|
|
496
|
+
},
|
|
497
|
+
resetRequest() {
|
|
498
|
+
request.setState((prev) => {
|
|
499
|
+
for (const f of prev.files) if (f.url.startsWith("blob:")) URL.revokeObjectURL(f.url);
|
|
500
|
+
return {
|
|
501
|
+
...prev,
|
|
502
|
+
text: "",
|
|
503
|
+
files: []
|
|
504
|
+
};
|
|
505
|
+
});
|
|
506
|
+
},
|
|
507
|
+
snapshot() {
|
|
508
|
+
return request.state;
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
var PromptInputRequestControllerContext = createContext(null);
|
|
513
|
+
var PromptInputRequestControllerProvider = PromptInputRequestControllerContext.Provider;
|
|
514
|
+
function usePromptInputRequestController() {
|
|
515
|
+
const ctx = useContext(PromptInputRequestControllerContext);
|
|
516
|
+
if (!ctx) throw new Error("usePromptInputRequestController must be used inside <PromptInput> or <PromptInputRequestProvider>");
|
|
517
|
+
return ctx;
|
|
518
|
+
}
|
|
519
|
+
/** Optional variant — returns null if no controller is in context. */
|
|
520
|
+
function useOptionalPromptInputRequestController() {
|
|
521
|
+
return useContext(PromptInputRequestControllerContext);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Subscribe to one field of the request context. Component re-renders only
|
|
525
|
+
* when that field changes.
|
|
526
|
+
*/
|
|
527
|
+
function useRequestField(key) {
|
|
528
|
+
return useStore(usePromptInputRequestController().request, (state) => state[key]);
|
|
529
|
+
}
|
|
530
|
+
/** Returns a stable setter for one field of the request context. */
|
|
531
|
+
function useSetRequestField(key) {
|
|
532
|
+
const ctrl = usePromptInputRequestController();
|
|
533
|
+
return useMemo(() => (value) => {
|
|
534
|
+
ctrl.setRequestField(key, value);
|
|
535
|
+
}, [ctrl, key]);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Subscribe to one field of the display context. Component re-renders only
|
|
539
|
+
* when that field changes.
|
|
540
|
+
*/
|
|
541
|
+
function useDisplayField(key) {
|
|
542
|
+
return useStore(usePromptInputRequestController().display, (state) => state[key]);
|
|
543
|
+
}
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/components/ai-prompt-input/ai-prompt-input.tsx
|
|
546
|
+
var FALLBACK_REQUEST_STORE = new Store({});
|
|
547
|
+
var SF_AI_PROMPT_INPUT_VARIANTS = { status: {
|
|
548
|
+
idle: {
|
|
549
|
+
classes: "",
|
|
550
|
+
description: "Ready to accept input"
|
|
551
|
+
},
|
|
552
|
+
submitted: {
|
|
553
|
+
classes: "",
|
|
554
|
+
description: "Waiting for response (shows spinner)"
|
|
555
|
+
},
|
|
556
|
+
streaming: {
|
|
557
|
+
classes: "",
|
|
558
|
+
description: "Streaming response (shows stop)"
|
|
559
|
+
},
|
|
560
|
+
error: {
|
|
561
|
+
classes: "",
|
|
562
|
+
description: "Error state (shows X)"
|
|
563
|
+
}
|
|
564
|
+
} };
|
|
565
|
+
var SF_AI_PROMPT_INPUT_DEFAULT_VARIANTS = { status: "idle" };
|
|
566
|
+
var PromptInputContext = createContext(null);
|
|
567
|
+
var ProviderAttachmentsContext = createContext(null);
|
|
568
|
+
var usePromptInputController = () => {
|
|
569
|
+
const ctx = useContext(PromptInputContext);
|
|
570
|
+
if (!ctx) throw new Error("Wrap your component inside <PromptInputProvider> to use usePromptInputController().");
|
|
571
|
+
return ctx;
|
|
572
|
+
};
|
|
573
|
+
var useOptionalPromptInputController = () => useContext(PromptInputContext);
|
|
574
|
+
var useProviderAttachments = () => {
|
|
575
|
+
const ctx = useContext(ProviderAttachmentsContext);
|
|
576
|
+
if (!ctx) throw new Error("Wrap your component inside <PromptInputProvider> to use useProviderAttachments().");
|
|
577
|
+
return ctx;
|
|
578
|
+
};
|
|
579
|
+
var useOptionalProviderAttachments = () => useContext(ProviderAttachmentsContext);
|
|
580
|
+
/**
|
|
581
|
+
* Optional global provider that lifts PromptInput state outside of PromptInput.
|
|
582
|
+
* If you don't use it, PromptInput stays fully self-managed.
|
|
583
|
+
*
|
|
584
|
+
* @example
|
|
585
|
+
* ```tsx
|
|
586
|
+
* <PromptInputProvider>
|
|
587
|
+
* <PromptInput onSubmit={handleSubmit}>...</PromptInput>
|
|
588
|
+
* </PromptInputProvider>
|
|
589
|
+
* ```
|
|
590
|
+
*/
|
|
591
|
+
function PromptInputProvider({ initialInput = "", children }) {
|
|
592
|
+
const [textInput, setTextInput] = useState(initialInput);
|
|
593
|
+
const clearInput = useCallback(() => setTextInput(""), []);
|
|
594
|
+
const [attachmentItems, setAttachmentItems] = useState([]);
|
|
595
|
+
const fileInputRef = useRef(null);
|
|
596
|
+
const openRef = useRef(() => {});
|
|
597
|
+
const add = useCallback((files) => {
|
|
598
|
+
const incoming = Array.from(files);
|
|
599
|
+
if (incoming.length === 0) return;
|
|
600
|
+
setAttachmentItems((prev) => prev.concat(incoming.map((file) => ({
|
|
601
|
+
id: `${Date.now()}-${Math.random()}`,
|
|
602
|
+
url: URL.createObjectURL(file),
|
|
603
|
+
mediaType: file.type,
|
|
604
|
+
filename: file.name
|
|
605
|
+
}))));
|
|
606
|
+
}, []);
|
|
607
|
+
const remove = useCallback((id) => {
|
|
608
|
+
setAttachmentItems((prev) => {
|
|
609
|
+
const found = prev.find((f) => f.id === id);
|
|
610
|
+
if (found?.url) URL.revokeObjectURL(found.url);
|
|
611
|
+
return prev.filter((f) => f.id !== id);
|
|
612
|
+
});
|
|
613
|
+
}, []);
|
|
614
|
+
const clear = useCallback(() => {
|
|
615
|
+
setAttachmentItems((prev) => {
|
|
616
|
+
for (const f of prev) if (f.url) URL.revokeObjectURL(f.url);
|
|
617
|
+
return [];
|
|
618
|
+
});
|
|
619
|
+
}, []);
|
|
620
|
+
const openFileDialog = useCallback(() => {
|
|
621
|
+
openRef.current?.();
|
|
622
|
+
}, []);
|
|
623
|
+
const attachments = useMemo(() => ({
|
|
624
|
+
files: attachmentItems,
|
|
625
|
+
add,
|
|
626
|
+
remove,
|
|
627
|
+
clear,
|
|
628
|
+
openFileDialog,
|
|
629
|
+
fileInputRef
|
|
630
|
+
}), [
|
|
631
|
+
attachmentItems,
|
|
632
|
+
add,
|
|
633
|
+
remove,
|
|
634
|
+
clear,
|
|
635
|
+
openFileDialog
|
|
636
|
+
]);
|
|
637
|
+
const __registerFileInput = useCallback((ref, open) => {
|
|
638
|
+
fileInputRef.current = ref.current;
|
|
639
|
+
openRef.current = open;
|
|
640
|
+
}, []);
|
|
641
|
+
const controller = useMemo(() => ({
|
|
642
|
+
textInput: {
|
|
643
|
+
value: textInput,
|
|
644
|
+
setInput: setTextInput,
|
|
645
|
+
clear: clearInput
|
|
646
|
+
},
|
|
647
|
+
attachments,
|
|
648
|
+
__registerFileInput
|
|
649
|
+
}), [
|
|
650
|
+
textInput,
|
|
651
|
+
clearInput,
|
|
652
|
+
attachments,
|
|
653
|
+
__registerFileInput
|
|
654
|
+
]);
|
|
655
|
+
return /* @__PURE__ */ jsx(PromptInputContext.Provider, {
|
|
656
|
+
value: controller,
|
|
657
|
+
children: /* @__PURE__ */ jsx(ProviderAttachmentsContext.Provider, {
|
|
658
|
+
value: attachments,
|
|
659
|
+
children
|
|
660
|
+
})
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
var LocalAttachmentsContext = createContext(null);
|
|
664
|
+
var usePromptInputAttachments = () => {
|
|
665
|
+
const provider = useOptionalProviderAttachments();
|
|
666
|
+
const local = useContext(LocalAttachmentsContext);
|
|
667
|
+
const context = provider ?? local;
|
|
668
|
+
if (!context) throw new Error("usePromptInputAttachments must be used within a PromptInput or PromptInputProvider");
|
|
669
|
+
return context;
|
|
670
|
+
};
|
|
671
|
+
/** Convert a blob URL to a base64 data URL for serialization. */
|
|
672
|
+
async function convertBlobToDataUrl(url) {
|
|
673
|
+
const blob = await (await fetch(url)).blob();
|
|
674
|
+
return new Promise((resolve, reject) => {
|
|
675
|
+
const reader = new FileReader();
|
|
676
|
+
reader.addEventListener("loadend", () => resolve(reader.result));
|
|
677
|
+
reader.addEventListener("error", () => reject(/* @__PURE__ */ new Error("FileReader error")));
|
|
678
|
+
reader.readAsDataURL(blob);
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
function makeAttachmentId() {
|
|
682
|
+
return `${Date.now()}-${Math.random()}`;
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Prompt input form. Can be self-managed or controlled via `PromptInputProvider`.
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```tsx
|
|
689
|
+
* <PromptInput onSubmit={({ text, files }) => send(text, files)}>
|
|
690
|
+
* <PromptInputAttachments>{(f) => <PromptInputAttachment data={f} />}</PromptInputAttachments>
|
|
691
|
+
* <PromptInputTextarea placeholder="Ask anything…" />
|
|
692
|
+
* <PromptInputToolbar>
|
|
693
|
+
* <PromptInputTools>
|
|
694
|
+
* <PromptInputAttachButton />
|
|
695
|
+
* </PromptInputTools>
|
|
696
|
+
* <PromptInputSubmit />
|
|
697
|
+
* </PromptInputToolbar>
|
|
698
|
+
* </PromptInput>
|
|
699
|
+
* ```
|
|
700
|
+
*/
|
|
701
|
+
var PromptInput = ({ className, accept, multiple, globalDrop, maxFiles, maxFileSize, onError, onSubmit, children, backLayer, backLayerTitle = "Context", backLayerOpen, onBackLayerOpenChange, autoOpenBackLayerWhen, ...props }) => {
|
|
702
|
+
const hasBackLayer = backLayer !== void 0;
|
|
703
|
+
const controller = useOptionalPromptInputController();
|
|
704
|
+
const usingProvider = !!controller;
|
|
705
|
+
const inputRef = useRef(null);
|
|
706
|
+
const anchorRef = useRef(null);
|
|
707
|
+
const formRef = useRef(null);
|
|
708
|
+
useEffect(() => {
|
|
709
|
+
const root = anchorRef.current?.closest("form");
|
|
710
|
+
if (root instanceof HTMLFormElement) formRef.current = root;
|
|
711
|
+
}, []);
|
|
712
|
+
useEffect(() => {
|
|
713
|
+
if (autoOpenBackLayerWhen) onBackLayerOpenChange?.(true);
|
|
714
|
+
}, [autoOpenBackLayerWhen, onBackLayerOpenChange]);
|
|
715
|
+
const [items, setItems] = useState([]);
|
|
716
|
+
const files = usingProvider ? controller.attachments.files : items;
|
|
717
|
+
const openFileDialogLocal = useCallback(() => {
|
|
718
|
+
inputRef.current?.click();
|
|
719
|
+
}, []);
|
|
720
|
+
const matchesAccept = useCallback((f) => {
|
|
721
|
+
if (!accept || accept.trim() === "") return true;
|
|
722
|
+
if (accept.includes("image/*")) return f.type.startsWith("image/");
|
|
723
|
+
return true;
|
|
724
|
+
}, [accept]);
|
|
725
|
+
const addLocal = useCallback((fileList) => {
|
|
726
|
+
const incoming = Array.from(fileList);
|
|
727
|
+
const accepted = incoming.filter((f) => matchesAccept(f));
|
|
728
|
+
if (incoming.length && accepted.length === 0) {
|
|
729
|
+
onError?.({
|
|
730
|
+
code: "accept",
|
|
731
|
+
message: "No files match the accepted types."
|
|
732
|
+
});
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
|
|
736
|
+
const sized = accepted.filter(withinSize);
|
|
737
|
+
if (accepted.length > 0 && sized.length === 0) {
|
|
738
|
+
onError?.({
|
|
739
|
+
code: "max_file_size",
|
|
740
|
+
message: "All files exceed the maximum size."
|
|
741
|
+
});
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
setItems((prev) => {
|
|
745
|
+
const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : void 0;
|
|
746
|
+
const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
|
|
747
|
+
if (typeof capacity === "number" && sized.length > capacity) onError?.({
|
|
748
|
+
code: "max_files",
|
|
749
|
+
message: "Too many files. Some were not added."
|
|
750
|
+
});
|
|
751
|
+
const next = capped.map((file) => ({
|
|
752
|
+
id: makeAttachmentId(),
|
|
753
|
+
url: URL.createObjectURL(file),
|
|
754
|
+
mediaType: file.type,
|
|
755
|
+
filename: file.name
|
|
756
|
+
}));
|
|
757
|
+
return prev.concat(next);
|
|
758
|
+
});
|
|
759
|
+
}, [
|
|
760
|
+
matchesAccept,
|
|
761
|
+
maxFiles,
|
|
762
|
+
maxFileSize,
|
|
763
|
+
onError
|
|
764
|
+
]);
|
|
765
|
+
const add = usingProvider ? (f) => controller.attachments.add(f) : addLocal;
|
|
766
|
+
const remove = usingProvider ? (id) => controller.attachments.remove(id) : (id) => setItems((prev) => {
|
|
767
|
+
const found = prev.find((f) => f.id === id);
|
|
768
|
+
if (found?.url) URL.revokeObjectURL(found.url);
|
|
769
|
+
return prev.filter((f) => f.id !== id);
|
|
770
|
+
});
|
|
771
|
+
const clear = usingProvider ? () => controller.attachments.clear() : () => setItems((prev) => {
|
|
772
|
+
for (const f of prev) if (f.url) URL.revokeObjectURL(f.url);
|
|
773
|
+
return [];
|
|
774
|
+
});
|
|
775
|
+
const openFileDialog = usingProvider ? () => controller.attachments.openFileDialog() : openFileDialogLocal;
|
|
776
|
+
useEffect(() => {
|
|
777
|
+
if (!usingProvider) return;
|
|
778
|
+
controller.__registerFileInput(inputRef, () => inputRef.current?.click());
|
|
779
|
+
}, [usingProvider, controller]);
|
|
780
|
+
useEffect(() => {
|
|
781
|
+
const form = formRef.current;
|
|
782
|
+
if (!form) return;
|
|
783
|
+
const onDragOver = (e) => {
|
|
784
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
785
|
+
};
|
|
786
|
+
const onDrop = (e) => {
|
|
787
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
788
|
+
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) add(e.dataTransfer.files);
|
|
789
|
+
};
|
|
790
|
+
form.addEventListener("dragover", onDragOver);
|
|
791
|
+
form.addEventListener("drop", onDrop);
|
|
792
|
+
return () => {
|
|
793
|
+
form.removeEventListener("dragover", onDragOver);
|
|
794
|
+
form.removeEventListener("drop", onDrop);
|
|
795
|
+
};
|
|
796
|
+
}, [add]);
|
|
797
|
+
useEffect(() => {
|
|
798
|
+
if (!globalDrop) return;
|
|
799
|
+
const onDragOver = (e) => {
|
|
800
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
801
|
+
};
|
|
802
|
+
const onDrop = (e) => {
|
|
803
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
804
|
+
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) add(e.dataTransfer.files);
|
|
805
|
+
};
|
|
806
|
+
document.addEventListener("dragover", onDragOver);
|
|
807
|
+
document.addEventListener("drop", onDrop);
|
|
808
|
+
return () => {
|
|
809
|
+
document.removeEventListener("dragover", onDragOver);
|
|
810
|
+
document.removeEventListener("drop", onDrop);
|
|
811
|
+
};
|
|
812
|
+
}, [add, globalDrop]);
|
|
813
|
+
useEffect(() => {
|
|
814
|
+
return () => {
|
|
815
|
+
if (!usingProvider) {
|
|
816
|
+
for (const f of files) if (f.url) URL.revokeObjectURL(f.url);
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
}, [usingProvider, files]);
|
|
820
|
+
const handleFileInputChange = (event) => {
|
|
821
|
+
if (event.currentTarget.files) add(event.currentTarget.files);
|
|
822
|
+
};
|
|
823
|
+
const ctx = useMemo(() => ({
|
|
824
|
+
files,
|
|
825
|
+
add,
|
|
826
|
+
remove,
|
|
827
|
+
clear,
|
|
828
|
+
openFileDialog,
|
|
829
|
+
fileInputRef: inputRef
|
|
830
|
+
}), [
|
|
831
|
+
files,
|
|
832
|
+
add,
|
|
833
|
+
remove,
|
|
834
|
+
clear,
|
|
835
|
+
openFileDialog
|
|
836
|
+
]);
|
|
837
|
+
const handleSubmit = (event) => {
|
|
838
|
+
event.preventDefault();
|
|
839
|
+
const form = event.currentTarget;
|
|
840
|
+
const text = usingProvider ? controller.textInput.value : new FormData(form).get("message") || "";
|
|
841
|
+
if (!usingProvider) form.reset();
|
|
842
|
+
const doSubmit = async () => {
|
|
843
|
+
const convertedFiles = await Promise.all(files.map(async (item) => {
|
|
844
|
+
if (item.url.startsWith("blob:")) return {
|
|
845
|
+
...item,
|
|
846
|
+
url: await convertBlobToDataUrl(item.url)
|
|
847
|
+
};
|
|
848
|
+
return item;
|
|
849
|
+
}));
|
|
850
|
+
try {
|
|
851
|
+
await onSubmit({
|
|
852
|
+
text,
|
|
853
|
+
files: convertedFiles
|
|
854
|
+
}, event);
|
|
855
|
+
clear();
|
|
856
|
+
if (usingProvider) controller.textInput.clear();
|
|
857
|
+
} catch {}
|
|
858
|
+
};
|
|
859
|
+
doSubmit().catch(() => {});
|
|
860
|
+
};
|
|
861
|
+
const isBackLayerOpen = backLayerOpen ?? false;
|
|
862
|
+
const inputShell = hasBackLayer ? /* @__PURE__ */ jsxs("div", {
|
|
863
|
+
className: "flex w-full flex-col rounded-xl bg-sf-elevated ring ring-sf-line/50",
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ jsxs("button", {
|
|
866
|
+
"aria-expanded": isBackLayerOpen,
|
|
867
|
+
"aria-label": isBackLayerOpen ? "Collapse context" : "Expand context",
|
|
868
|
+
className: "flex w-full items-center justify-between gap-2 px-3 py-2 text-left",
|
|
869
|
+
onClick: () => onBackLayerOpenChange?.(!isBackLayerOpen),
|
|
870
|
+
type: "button",
|
|
871
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
872
|
+
className: "text-sm font-medium text-sf-subtle uppercase tracking-wide select-none",
|
|
873
|
+
children: backLayerTitle
|
|
874
|
+
}), /* @__PURE__ */ jsx(CaretDownIcon, { className: cn("size-3.5 text-sf-subtle transition-transform duration-200", isBackLayerOpen && "rotate-180") })]
|
|
875
|
+
}),
|
|
876
|
+
/* @__PURE__ */ jsx(PromptInputBackLayerPanel, {
|
|
877
|
+
open: isBackLayerOpen,
|
|
878
|
+
children: backLayer
|
|
879
|
+
}),
|
|
880
|
+
/* @__PURE__ */ jsx("div", {
|
|
881
|
+
className: "flex flex-col overflow-hidden rounded-xl bg-sf-base ring ring-sf-line/50 focus-within:ring-sf-ring",
|
|
882
|
+
children
|
|
883
|
+
})
|
|
884
|
+
]
|
|
885
|
+
}) : /* @__PURE__ */ jsx(InputGroup, { children });
|
|
886
|
+
const inner = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
887
|
+
/* @__PURE__ */ jsx("span", {
|
|
888
|
+
"aria-hidden": "true",
|
|
889
|
+
className: "hidden",
|
|
890
|
+
ref: anchorRef
|
|
891
|
+
}),
|
|
892
|
+
/* @__PURE__ */ jsx("input", {
|
|
893
|
+
accept,
|
|
894
|
+
"aria-label": "Upload files",
|
|
895
|
+
className: "hidden",
|
|
896
|
+
multiple,
|
|
897
|
+
onChange: handleFileInputChange,
|
|
898
|
+
ref: inputRef,
|
|
899
|
+
type: "file"
|
|
900
|
+
}),
|
|
901
|
+
/* @__PURE__ */ jsx("form", {
|
|
902
|
+
className: cn("w-full", className),
|
|
903
|
+
onSubmit: handleSubmit,
|
|
904
|
+
...props,
|
|
905
|
+
children: inputShell
|
|
906
|
+
})
|
|
907
|
+
] });
|
|
908
|
+
return usingProvider ? inner : /* @__PURE__ */ jsx(LocalAttachmentsContext.Provider, {
|
|
909
|
+
value: ctx,
|
|
910
|
+
children: inner
|
|
911
|
+
});
|
|
912
|
+
};
|
|
913
|
+
var PromptInputBody = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
914
|
+
className: cn("contents", className),
|
|
915
|
+
...props
|
|
916
|
+
});
|
|
917
|
+
function PromptInputBackLayerPanel({ open, children }) {
|
|
918
|
+
const isOpen = open ?? false;
|
|
919
|
+
return /* @__PURE__ */ jsx("div", {
|
|
920
|
+
"aria-hidden": !isOpen,
|
|
921
|
+
className: "grid",
|
|
922
|
+
style: {
|
|
923
|
+
gridTemplateRows: isOpen ? "1fr" : "0fr",
|
|
924
|
+
opacity: isOpen ? 1 : 0,
|
|
925
|
+
transition: "grid-template-rows 200ms ease-out, opacity 200ms ease-out"
|
|
926
|
+
},
|
|
927
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
928
|
+
className: "min-h-0 overflow-hidden",
|
|
929
|
+
children
|
|
930
|
+
})
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Container for back-layer content (HITL approvals, task lists, agent status,
|
|
935
|
+
* etc.). Pass instances of this as the `backLayer` prop on `PromptInput`.
|
|
936
|
+
*
|
|
937
|
+
* @example
|
|
938
|
+
* ```tsx
|
|
939
|
+
* <PromptInput
|
|
940
|
+
* onSubmit={handleSubmit}
|
|
941
|
+
* backLayer={<PromptInputBackLayer>...</PromptInputBackLayer>}
|
|
942
|
+
* backLayerOpen={open}
|
|
943
|
+
* onBackLayerOpenChange={setOpen}
|
|
944
|
+
* >
|
|
945
|
+
* <PromptInputTextarea />
|
|
946
|
+
* <PromptInputToolbar>
|
|
947
|
+
* <PromptInputTools />
|
|
948
|
+
* <PromptInputSubmit />
|
|
949
|
+
* </PromptInputToolbar>
|
|
950
|
+
* </PromptInput>
|
|
951
|
+
* ```
|
|
952
|
+
*/
|
|
953
|
+
function PromptInputBackLayer({ children, className }) {
|
|
954
|
+
return /* @__PURE__ */ jsx("div", {
|
|
955
|
+
className: cn("flex flex-col gap-2 px-3 py-2.5 text-sm text-sf-default", className),
|
|
956
|
+
children
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
var handleTextareaKeyDown = (e) => {
|
|
960
|
+
if (e.key === "Enter") {
|
|
961
|
+
if (e.nativeEvent.isComposing) return;
|
|
962
|
+
if (e.shiftKey) return;
|
|
963
|
+
e.preventDefault();
|
|
964
|
+
e.currentTarget.form?.requestSubmit();
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
/**
|
|
968
|
+
* Auto-resizing textarea for the prompt. Submits on Enter (Shift+Enter for newline).
|
|
969
|
+
* Pastes files into attachments.
|
|
970
|
+
*/
|
|
971
|
+
var PromptInputTextarea = ({ onChange, className, placeholder = "What would you like to know?", ...props }) => {
|
|
972
|
+
const controller = useOptionalPromptInputController();
|
|
973
|
+
const attachments = usePromptInputAttachments();
|
|
974
|
+
const handlePaste = (event) => {
|
|
975
|
+
const items = event.clipboardData?.items;
|
|
976
|
+
if (!items) return;
|
|
977
|
+
const pastedFiles = [];
|
|
978
|
+
for (const item of items) if (item.kind === "file") {
|
|
979
|
+
const file = item.getAsFile();
|
|
980
|
+
if (file) pastedFiles.push(file);
|
|
981
|
+
}
|
|
982
|
+
if (pastedFiles.length > 0) {
|
|
983
|
+
event.preventDefault();
|
|
984
|
+
attachments.add(pastedFiles);
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
const controlledProps = controller ? {
|
|
988
|
+
value: controller.textInput.value,
|
|
989
|
+
onChange: (e) => {
|
|
990
|
+
controller.textInput.setInput(e.currentTarget.value);
|
|
991
|
+
onChange?.(e);
|
|
992
|
+
}
|
|
993
|
+
} : { onChange };
|
|
994
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
995
|
+
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),
|
|
996
|
+
name: "message",
|
|
997
|
+
onKeyDown: handleTextareaKeyDown,
|
|
998
|
+
onPaste: handlePaste,
|
|
999
|
+
placeholder,
|
|
1000
|
+
rows: 1,
|
|
1001
|
+
...props,
|
|
1002
|
+
...controlledProps
|
|
1003
|
+
});
|
|
1004
|
+
};
|
|
1005
|
+
var PromptInputToolbar = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
1006
|
+
className: cn("flex min-w-0 items-center justify-between gap-1 px-2 py-1.5", className),
|
|
1007
|
+
...props
|
|
1008
|
+
});
|
|
1009
|
+
var PromptInputTools = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
1010
|
+
className: cn("flex min-w-0 items-center gap-1", className),
|
|
1011
|
+
...props
|
|
1012
|
+
});
|
|
1013
|
+
var PromptInputButton = ({ variant = "ghost", size = "sm", className, ...props }) => /* @__PURE__ */ jsx(Button, {
|
|
1014
|
+
className: cn("text-sf-subtle hover:text-sf-default", className),
|
|
1015
|
+
size,
|
|
1016
|
+
type: "button",
|
|
1017
|
+
variant,
|
|
1018
|
+
...props
|
|
1019
|
+
});
|
|
1020
|
+
/**
|
|
1021
|
+
* Submit button. Icon changes based on `status`:
|
|
1022
|
+
* - `idle` → send arrow
|
|
1023
|
+
* - `submitted` → spinner
|
|
1024
|
+
* - `streaming` → stop square
|
|
1025
|
+
* - `error` → X
|
|
1026
|
+
*/
|
|
1027
|
+
var PromptInputSubmit = ({ className, variant = "primary", size = "sm", status = "idle", children, ...props }) => {
|
|
1028
|
+
let Icon;
|
|
1029
|
+
if (status === "submitted") Icon = /* @__PURE__ */ jsx(SpinnerGapIcon, { className: "size-4 animate-spin" });
|
|
1030
|
+
else if (status === "streaming") Icon = /* @__PURE__ */ jsx(SquareIcon, { className: "size-4" });
|
|
1031
|
+
else if (status === "error") Icon = /* @__PURE__ */ jsx(XIcon, { className: "size-4" });
|
|
1032
|
+
else Icon = /* @__PURE__ */ jsx(ArrowUpIcon, { className: "size-4" });
|
|
1033
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
1034
|
+
"aria-label": "Submit",
|
|
1035
|
+
className: cn(className),
|
|
1036
|
+
size,
|
|
1037
|
+
type: "submit",
|
|
1038
|
+
variant,
|
|
1039
|
+
...props,
|
|
1040
|
+
children: children ?? Icon
|
|
1041
|
+
});
|
|
1042
|
+
};
|
|
1043
|
+
var DropdownMenu = Menu;
|
|
1044
|
+
var PromptInputActionMenu = (props) => /* @__PURE__ */ jsx(DropdownMenu.Root, { ...props });
|
|
1045
|
+
var PromptInputActionMenuTrigger = ({ className, children, "aria-label": ariaLabel = "More actions", ...props }) => /* @__PURE__ */ jsx(DropdownMenu.Trigger, { render: /* @__PURE__ */ jsx(Button, {
|
|
1046
|
+
"aria-label": ariaLabel,
|
|
1047
|
+
className: cn("text-sf-subtle hover:text-sf-default", className),
|
|
1048
|
+
size: "sm",
|
|
1049
|
+
type: "button",
|
|
1050
|
+
variant: "ghost",
|
|
1051
|
+
...props,
|
|
1052
|
+
children: children ?? /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" })
|
|
1053
|
+
}) });
|
|
1054
|
+
var PromptInputActionMenuContent = ({ className, children, ...props }) => /* @__PURE__ */ jsx(DropdownMenu.Positioner, {
|
|
1055
|
+
className: "z-50",
|
|
1056
|
+
side: "top",
|
|
1057
|
+
align: "start",
|
|
1058
|
+
...props,
|
|
1059
|
+
children: /* @__PURE__ */ jsx(DropdownMenu.Popup, {
|
|
1060
|
+
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),
|
|
1061
|
+
children
|
|
1062
|
+
})
|
|
1063
|
+
});
|
|
1064
|
+
var PromptInputActionMenuItem = ({ className, ...props }) => /* @__PURE__ */ jsx(DropdownMenu.Item, {
|
|
1065
|
+
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),
|
|
1066
|
+
...props
|
|
1067
|
+
});
|
|
1068
|
+
var PromptInputActionAddAttachments = ({ label = "Add photos or files", ...props }) => {
|
|
1069
|
+
const attachments = usePromptInputAttachments();
|
|
1070
|
+
const handleSelect = useCallback(() => {
|
|
1071
|
+
attachments.openFileDialog();
|
|
1072
|
+
}, [attachments]);
|
|
1073
|
+
return /* @__PURE__ */ jsxs(PromptInputActionMenuItem, {
|
|
1074
|
+
...props,
|
|
1075
|
+
onSelect: handleSelect,
|
|
1076
|
+
children: [/* @__PURE__ */ jsx(ImageIcon, { className: "size-4" }), label]
|
|
1077
|
+
});
|
|
1078
|
+
};
|
|
1079
|
+
var PromptInputModeSelector = ({ options, value: valueProp, onChange: onChangeProp, "aria-label": ariaLabel = "Select mode", className }) => {
|
|
1080
|
+
const controller = useOptionalPromptInputRequestController();
|
|
1081
|
+
const ctxMode = useStore(controller?.request ?? FALLBACK_REQUEST_STORE, (s) => s.mode);
|
|
1082
|
+
if (options.length === 0) return null;
|
|
1083
|
+
const isControlled = valueProp !== void 0 || onChangeProp !== void 0;
|
|
1084
|
+
const value = isControlled ? valueProp : ctxMode;
|
|
1085
|
+
const handleSelect = (next) => {
|
|
1086
|
+
if (onChangeProp) {
|
|
1087
|
+
onChangeProp(next);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (!isControlled && controller) controller.setRequestField("mode", next);
|
|
1091
|
+
};
|
|
1092
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1093
|
+
className: cn("flex items-center gap-0.5 rounded-md bg-sf-tint p-0.5", className),
|
|
1094
|
+
role: "group",
|
|
1095
|
+
"aria-label": ariaLabel,
|
|
1096
|
+
children: options.map((opt) => {
|
|
1097
|
+
const isSelected = opt.value === value;
|
|
1098
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
1099
|
+
"aria-pressed": isSelected,
|
|
1100
|
+
disabled: opt.disabled,
|
|
1101
|
+
className: cn("flex cursor-pointer items-center gap-1 rounded-[4px] px-2 py-0.5 text-[11px] font-medium transition-colors", "disabled:cursor-not-allowed disabled:opacity-50", isSelected ? "bg-sf-elevated text-sf-default shadow-sm" : "text-sf-subtle hover:text-sf-default"),
|
|
1102
|
+
onClick: () => handleSelect(opt.value),
|
|
1103
|
+
title: opt.description,
|
|
1104
|
+
type: "button",
|
|
1105
|
+
children: [opt.icon, opt.label]
|
|
1106
|
+
}, opt.value);
|
|
1107
|
+
})
|
|
1108
|
+
});
|
|
1109
|
+
};
|
|
1110
|
+
/**
|
|
1111
|
+
* Compact Select primitive sized for the PromptInput toolbar.
|
|
1112
|
+
*
|
|
1113
|
+
* - 24px-tall trigger with `text-[11px]` label and truncating value
|
|
1114
|
+
* - Matching compact popup with the same text scale
|
|
1115
|
+
*
|
|
1116
|
+
* Build toolbar-specific selectors (mode, model, voice, tone, ...) by
|
|
1117
|
+
* wrapping this and wiring `value`/`onChange` to your state source.
|
|
1118
|
+
*/
|
|
1119
|
+
var PromptInputCompactSelect = ({ options, value, onChange, placeholder = "Select", "aria-label": ariaLabel = "Select", maxWidth = 160, className }) => {
|
|
1120
|
+
if (options.length === 0) return null;
|
|
1121
|
+
const selected = options.find((o) => o.value === value);
|
|
1122
|
+
const triggerMaxWidth = typeof maxWidth === "number" ? `${maxWidth}px` : maxWidth;
|
|
1123
|
+
return /* @__PURE__ */ jsxs(Select.Root, {
|
|
1124
|
+
value: value ?? "",
|
|
1125
|
+
onValueChange: (v) => {
|
|
1126
|
+
if (typeof v === "string") onChange?.(v);
|
|
1127
|
+
},
|
|
1128
|
+
children: [/* @__PURE__ */ jsxs(Select.Trigger, {
|
|
1129
|
+
"aria-label": ariaLabel,
|
|
1130
|
+
style: { maxWidth: triggerMaxWidth },
|
|
1131
|
+
className: cn("flex h-6 min-w-0 shrink cursor-pointer items-center gap-1 rounded-[4px] px-2 text-[11px] leading-none font-medium text-sf-subtle transition-colors", "hover:text-sf-default focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-sf-ring", className),
|
|
1132
|
+
children: [/* @__PURE__ */ jsx(Select.Value, {
|
|
1133
|
+
className: "min-w-0 truncate",
|
|
1134
|
+
children: selected ? selected.label ?? selected.value : placeholder
|
|
1135
|
+
}), /* @__PURE__ */ jsx(Select.Icon, {
|
|
1136
|
+
className: "flex shrink-0 items-center",
|
|
1137
|
+
children: /* @__PURE__ */ jsx(CaretDownIcon, { className: "size-3" })
|
|
1138
|
+
})]
|
|
1139
|
+
}), /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, {
|
|
1140
|
+
sideOffset: 6,
|
|
1141
|
+
children: /* @__PURE__ */ jsx(Select.Popup, {
|
|
1142
|
+
className: cn("overflow-hidden rounded-lg bg-sf-control p-1 text-[11px] text-sf-default shadow-lg ring ring-sf-line", "min-w-[calc(var(--anchor-width)+3px)]"),
|
|
1143
|
+
children: options.map((opt) => /* @__PURE__ */ jsxs(Select.Item, {
|
|
1144
|
+
value: opt.value,
|
|
1145
|
+
disabled: opt.disabled,
|
|
1146
|
+
className: cn("flex cursor-pointer items-center justify-between gap-2 rounded px-2 py-1 text-[11px] leading-tight outline-none", "data-[highlighted]:bg-sf-tint data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"),
|
|
1147
|
+
children: [/* @__PURE__ */ jsx(Select.ItemText, { children: opt.label ?? opt.value }), /* @__PURE__ */ jsx(Select.ItemIndicator, { children: /* @__PURE__ */ jsx(CheckIcon, { className: "size-3" }) })]
|
|
1148
|
+
}, opt.value))
|
|
1149
|
+
})
|
|
1150
|
+
}) })]
|
|
1151
|
+
});
|
|
1152
|
+
};
|
|
1153
|
+
function getModelLabel(option) {
|
|
1154
|
+
if (option.label) return option.label;
|
|
1155
|
+
return option.value.split("/").pop() ?? option.value;
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Compact model selector for the PromptInput toolbar.
|
|
1159
|
+
*
|
|
1160
|
+
* - Controlled: pass `value` + `onChange`.
|
|
1161
|
+
* - Controller-bound: omit both and wrap in `PromptInputRequestControllerProvider`
|
|
1162
|
+
* — the selector reads/writes `requestContext.model` automatically.
|
|
1163
|
+
*/
|
|
1164
|
+
var PromptInputModelSelect = ({ options, value: valueProp, onChange: onChangeProp, "aria-label": ariaLabel = "Select model", className }) => {
|
|
1165
|
+
const controller = useOptionalPromptInputRequestController();
|
|
1166
|
+
const ctxModel = useStore(controller?.request ?? FALLBACK_REQUEST_STORE, (s) => s.model);
|
|
1167
|
+
const isControlled = valueProp !== void 0 || onChangeProp !== void 0;
|
|
1168
|
+
const value = isControlled ? valueProp : ctxModel;
|
|
1169
|
+
const handleChange = (next) => {
|
|
1170
|
+
if (onChangeProp) {
|
|
1171
|
+
onChangeProp(next);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
if (!isControlled && controller) controller.setRequestField("model", next);
|
|
1175
|
+
};
|
|
1176
|
+
return /* @__PURE__ */ jsx(PromptInputCompactSelect, {
|
|
1177
|
+
options: options.map((o) => ({
|
|
1178
|
+
value: o.value,
|
|
1179
|
+
label: getModelLabel(o),
|
|
1180
|
+
disabled: o.disabled
|
|
1181
|
+
})),
|
|
1182
|
+
value,
|
|
1183
|
+
onChange: handleChange,
|
|
1184
|
+
placeholder: "Select model",
|
|
1185
|
+
"aria-label": ariaLabel,
|
|
1186
|
+
className
|
|
1187
|
+
});
|
|
1188
|
+
};
|
|
1189
|
+
/**
|
|
1190
|
+
* Compact mode selector for the PromptInput toolbar — matches
|
|
1191
|
+
* `PromptInputModelSelect` visually. For a segmented button-group look
|
|
1192
|
+
* (multi-mode headers, empty states), use `PromptInputModeSelector` instead.
|
|
1193
|
+
*
|
|
1194
|
+
* - Controlled: pass `value` + `onChange`.
|
|
1195
|
+
* - Controller-bound: omit both and wrap in `PromptInputRequestControllerProvider`
|
|
1196
|
+
* — the selector reads/writes `requestContext.mode` automatically.
|
|
1197
|
+
*/
|
|
1198
|
+
var PromptInputModeSelect = ({ options, value: valueProp, onChange: onChangeProp, "aria-label": ariaLabel = "Select mode", className }) => {
|
|
1199
|
+
const controller = useOptionalPromptInputRequestController();
|
|
1200
|
+
const ctxMode = useStore(controller?.request ?? FALLBACK_REQUEST_STORE, (s) => s.mode);
|
|
1201
|
+
const isControlled = valueProp !== void 0 || onChangeProp !== void 0;
|
|
1202
|
+
const value = isControlled ? valueProp : ctxMode;
|
|
1203
|
+
const handleChange = (next) => {
|
|
1204
|
+
if (onChangeProp) {
|
|
1205
|
+
onChangeProp(next);
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
if (!isControlled && controller) controller.setRequestField("mode", next);
|
|
1209
|
+
};
|
|
1210
|
+
return /* @__PURE__ */ jsx(PromptInputCompactSelect, {
|
|
1211
|
+
options: options.map((o) => ({
|
|
1212
|
+
value: o.value,
|
|
1213
|
+
label: o.label ?? o.value,
|
|
1214
|
+
disabled: o.disabled
|
|
1215
|
+
})),
|
|
1216
|
+
value,
|
|
1217
|
+
onChange: handleChange,
|
|
1218
|
+
placeholder: "Select mode",
|
|
1219
|
+
"aria-label": ariaLabel,
|
|
1220
|
+
className
|
|
1221
|
+
});
|
|
1222
|
+
};
|
|
1223
|
+
/**
|
|
1224
|
+
* Single compact button that cycles through `options` on click.
|
|
1225
|
+
*
|
|
1226
|
+
* - Click / Enter / Space → next option (wraps at end)
|
|
1227
|
+
* - Shift+Click / Shift+Enter → previous option
|
|
1228
|
+
* - Skips `disabled` options
|
|
1229
|
+
*
|
|
1230
|
+
* Useful for binary or 2–3 option toggles where a dropdown feels heavy.
|
|
1231
|
+
*/
|
|
1232
|
+
var PromptInputCompactCycle = ({ options, value, onChange, "aria-label": ariaLabel = "Cycle option", showIcon = true, maxWidth = 160, className }) => {
|
|
1233
|
+
const enabledOptions = options.filter((o) => !o.disabled);
|
|
1234
|
+
if (enabledOptions.length === 0) return null;
|
|
1235
|
+
const currentIndex = options.findIndex((o) => o.value === value);
|
|
1236
|
+
const current = currentIndex !== -1 ? options[currentIndex] : enabledOptions[0];
|
|
1237
|
+
const advance = (direction) => {
|
|
1238
|
+
const start = currentIndex !== -1 ? currentIndex : -direction;
|
|
1239
|
+
const len = options.length;
|
|
1240
|
+
for (let step = 1; step <= len; step++) {
|
|
1241
|
+
const candidate = options[(start + direction * step + len * len) % len];
|
|
1242
|
+
if (candidate && !candidate.disabled) {
|
|
1243
|
+
onChange?.(candidate.value);
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
const triggerMaxWidth = typeof maxWidth === "number" ? `${maxWidth}px` : maxWidth;
|
|
1249
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
1250
|
+
type: "button",
|
|
1251
|
+
"aria-label": ariaLabel,
|
|
1252
|
+
title: `${current?.label ?? current?.value ?? ""} — click to cycle`,
|
|
1253
|
+
style: { maxWidth: triggerMaxWidth },
|
|
1254
|
+
onClick: (e) => advance(e.shiftKey ? -1 : 1),
|
|
1255
|
+
onKeyDown: (e) => {
|
|
1256
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1257
|
+
e.preventDefault();
|
|
1258
|
+
advance(e.shiftKey ? -1 : 1);
|
|
1259
|
+
}
|
|
1260
|
+
},
|
|
1261
|
+
className: cn("flex h-6 min-w-0 shrink cursor-pointer items-center gap-1 rounded-[4px] px-2 text-[11px] leading-none font-medium text-sf-subtle transition-colors", "hover:text-sf-default focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-sf-ring", className),
|
|
1262
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1263
|
+
className: "min-w-0 truncate",
|
|
1264
|
+
children: current?.label ?? current?.value ?? ""
|
|
1265
|
+
}), showIcon && /* @__PURE__ */ jsx(ArrowsClockwiseIcon, { className: "size-3 shrink-0 opacity-70" })]
|
|
1266
|
+
});
|
|
1267
|
+
};
|
|
1268
|
+
/**
|
|
1269
|
+
* Click-to-cycle mode picker. Single compact button that advances through
|
|
1270
|
+
* `options` on each click. Best for 2–3 modes.
|
|
1271
|
+
*
|
|
1272
|
+
* - Controlled: pass `value` + `onChange`.
|
|
1273
|
+
* - Controller-bound: omit both and wrap in `PromptInputRequestControllerProvider`
|
|
1274
|
+
* — reads/writes `requestContext.mode` automatically.
|
|
1275
|
+
*/
|
|
1276
|
+
var PromptInputModeCycle = ({ options, value: valueProp, onChange: onChangeProp, "aria-label": ariaLabel = "Cycle mode", showIcon = true, className }) => {
|
|
1277
|
+
const controller = useOptionalPromptInputRequestController();
|
|
1278
|
+
const ctxMode = useStore(controller?.request ?? FALLBACK_REQUEST_STORE, (s) => s.mode);
|
|
1279
|
+
const isControlled = valueProp !== void 0 || onChangeProp !== void 0;
|
|
1280
|
+
const value = isControlled ? valueProp : ctxMode;
|
|
1281
|
+
const handleChange = (next) => {
|
|
1282
|
+
if (onChangeProp) {
|
|
1283
|
+
onChangeProp(next);
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (!isControlled && controller) controller.setRequestField("mode", next);
|
|
1287
|
+
};
|
|
1288
|
+
return /* @__PURE__ */ jsx(PromptInputCompactCycle, {
|
|
1289
|
+
options: options.map((o) => ({
|
|
1290
|
+
value: o.value,
|
|
1291
|
+
label: o.label ?? o.value,
|
|
1292
|
+
disabled: o.disabled
|
|
1293
|
+
})),
|
|
1294
|
+
value,
|
|
1295
|
+
onChange: handleChange,
|
|
1296
|
+
"aria-label": ariaLabel,
|
|
1297
|
+
showIcon,
|
|
1298
|
+
className
|
|
1299
|
+
});
|
|
1300
|
+
};
|
|
1301
|
+
/**
|
|
1302
|
+
* Click-to-cycle model picker. Single compact button that advances through
|
|
1303
|
+
* `options` on each click. Best for 2–3 models.
|
|
1304
|
+
*
|
|
1305
|
+
* - Controlled: pass `value` + `onChange`.
|
|
1306
|
+
* - Controller-bound: omit both and wrap in `PromptInputRequestControllerProvider`
|
|
1307
|
+
* — reads/writes `requestContext.model` automatically.
|
|
1308
|
+
*/
|
|
1309
|
+
var PromptInputModelCycle = ({ options, value: valueProp, onChange: onChangeProp, "aria-label": ariaLabel = "Cycle model", showIcon = true, className }) => {
|
|
1310
|
+
const controller = useOptionalPromptInputRequestController();
|
|
1311
|
+
const ctxModel = useStore(controller?.request ?? FALLBACK_REQUEST_STORE, (s) => s.model);
|
|
1312
|
+
const isControlled = valueProp !== void 0 || onChangeProp !== void 0;
|
|
1313
|
+
const value = isControlled ? valueProp : ctxModel;
|
|
1314
|
+
const handleChange = (next) => {
|
|
1315
|
+
if (onChangeProp) {
|
|
1316
|
+
onChangeProp(next);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (!isControlled && controller) controller.setRequestField("model", next);
|
|
1320
|
+
};
|
|
1321
|
+
return /* @__PURE__ */ jsx(PromptInputCompactCycle, {
|
|
1322
|
+
options: options.map((o) => ({
|
|
1323
|
+
value: o.value,
|
|
1324
|
+
label: getModelLabel(o),
|
|
1325
|
+
disabled: o.disabled
|
|
1326
|
+
})),
|
|
1327
|
+
value,
|
|
1328
|
+
onChange: handleChange,
|
|
1329
|
+
"aria-label": ariaLabel,
|
|
1330
|
+
showIcon,
|
|
1331
|
+
className
|
|
1332
|
+
});
|
|
1333
|
+
};
|
|
1334
|
+
var IMAGE_EXTS = new Set([
|
|
1335
|
+
"jpg",
|
|
1336
|
+
"jpeg",
|
|
1337
|
+
"png",
|
|
1338
|
+
"gif",
|
|
1339
|
+
"webp",
|
|
1340
|
+
"svg"
|
|
1341
|
+
]);
|
|
1342
|
+
function getFileIcon(filename) {
|
|
1343
|
+
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
1344
|
+
if (IMAGE_EXTS.has(ext)) return /* @__PURE__ */ jsx(ImageIcon, { className: "size-3.5" });
|
|
1345
|
+
if ([
|
|
1346
|
+
"pdf",
|
|
1347
|
+
"doc",
|
|
1348
|
+
"docx",
|
|
1349
|
+
"txt",
|
|
1350
|
+
"md"
|
|
1351
|
+
].includes(ext)) return /* @__PURE__ */ jsx(FileTextIcon, { className: "size-3.5" });
|
|
1352
|
+
return /* @__PURE__ */ jsx(FileIcon, { className: "size-3.5" });
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Single attachment chip for images or files. Includes a remove button.
|
|
1356
|
+
*/
|
|
1357
|
+
function PromptInputAttachment({ data, className, ...props }) {
|
|
1358
|
+
const attachments = usePromptInputAttachments();
|
|
1359
|
+
const isImage = data.mediaType?.startsWith("image/") && data.url;
|
|
1360
|
+
const handleRemove = useCallback(() => {
|
|
1361
|
+
attachments.remove(data.id);
|
|
1362
|
+
}, [attachments, data.id]);
|
|
1363
|
+
if (isImage) return /* @__PURE__ */ jsxs("div", {
|
|
1364
|
+
className: cn("group relative size-14 overflow-hidden rounded-md", className),
|
|
1365
|
+
...props,
|
|
1366
|
+
children: [/* @__PURE__ */ jsx("img", {
|
|
1367
|
+
alt: data.filename || "attachment",
|
|
1368
|
+
className: "size-full object-cover",
|
|
1369
|
+
src: data.url
|
|
1370
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1371
|
+
"aria-label": "Remove attachment",
|
|
1372
|
+
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",
|
|
1373
|
+
onClick: handleRemove,
|
|
1374
|
+
size: "sm",
|
|
1375
|
+
type: "button",
|
|
1376
|
+
variant: "ghost",
|
|
1377
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "size-3" })
|
|
1378
|
+
})]
|
|
1379
|
+
});
|
|
1380
|
+
const ext = data.filename.split(".").pop()?.toUpperCase() ?? "FILE";
|
|
1381
|
+
const name = data.filename.replace(/\.[^/.]+$/, "");
|
|
1382
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1383
|
+
className: cn("group relative inline-flex h-8 items-center gap-2 rounded-md bg-sf-tint/50 px-2 text-sm", className),
|
|
1384
|
+
...props,
|
|
1385
|
+
children: [
|
|
1386
|
+
/* @__PURE__ */ jsx("div", {
|
|
1387
|
+
className: "flex size-5 shrink-0 items-center justify-center text-sf-subtle",
|
|
1388
|
+
children: getFileIcon(data.filename)
|
|
1389
|
+
}),
|
|
1390
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
1391
|
+
content: data.filename,
|
|
1392
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1393
|
+
className: "max-w-[160px] truncate font-medium text-sf-default",
|
|
1394
|
+
children: name || "File"
|
|
1395
|
+
})
|
|
1396
|
+
}),
|
|
1397
|
+
/* @__PURE__ */ jsx("span", {
|
|
1398
|
+
className: "rounded bg-sf-tint px-1 py-0.5 font-semibold text-sf-subtle text-[10px] uppercase",
|
|
1399
|
+
children: ext
|
|
1400
|
+
}),
|
|
1401
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1402
|
+
"aria-label": "Remove attachment",
|
|
1403
|
+
className: "ml-0.5 size-4 shrink-0 rounded-full p-0 opacity-0 transition-opacity group-hover:opacity-100",
|
|
1404
|
+
onClick: handleRemove,
|
|
1405
|
+
size: "sm",
|
|
1406
|
+
type: "button",
|
|
1407
|
+
variant: "ghost",
|
|
1408
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "size-3" })
|
|
1409
|
+
})
|
|
1410
|
+
]
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Renders all attachments using a render prop. Hidden when no files.
|
|
1415
|
+
* Animates height as files are added/removed.
|
|
1416
|
+
*/
|
|
1417
|
+
function PromptInputAttachments({ className, children, ...props }) {
|
|
1418
|
+
const attachments = usePromptInputAttachments();
|
|
1419
|
+
const [height, setHeight] = useState(0);
|
|
1420
|
+
const contentRef = useRef(null);
|
|
1421
|
+
useLayoutEffect(() => {
|
|
1422
|
+
const el = contentRef.current;
|
|
1423
|
+
if (!el) return;
|
|
1424
|
+
const ro = new ResizeObserver(() => {
|
|
1425
|
+
setHeight(el.getBoundingClientRect().height);
|
|
1426
|
+
});
|
|
1427
|
+
ro.observe(el);
|
|
1428
|
+
setHeight(el.getBoundingClientRect().height);
|
|
1429
|
+
return () => ro.disconnect();
|
|
1430
|
+
}, []);
|
|
1431
|
+
useLayoutEffect(() => {
|
|
1432
|
+
const el = contentRef.current;
|
|
1433
|
+
if (el) setHeight(el.getBoundingClientRect().height);
|
|
1434
|
+
}, [attachments.files.length]);
|
|
1435
|
+
if (attachments.files.length === 0) return null;
|
|
1436
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1437
|
+
"aria-live": "polite",
|
|
1438
|
+
className: cn("overflow-hidden px-2 transition-[height] duration-200 ease-out", className),
|
|
1439
|
+
style: { height: attachments.files.length ? height : 0 },
|
|
1440
|
+
...props,
|
|
1441
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1442
|
+
className: "flex flex-wrap gap-2 py-1",
|
|
1443
|
+
ref: contentRef,
|
|
1444
|
+
children: attachments.files.map((file) => /* @__PURE__ */ jsx(Fragment, { children: children(file) }, file.id))
|
|
1445
|
+
})
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Voice input button. Uses the Web Speech API (Chrome/Edge). Hidden on unsupported browsers.
|
|
1450
|
+
* Pulses while listening.
|
|
1451
|
+
*/
|
|
1452
|
+
var PromptInputSpeechButton = ({ className, textareaRef, onTranscriptionChange, "aria-label": ariaLabel = "Voice input", ...props }) => {
|
|
1453
|
+
const [isListening, setIsListening] = useState(false);
|
|
1454
|
+
const [recognition, setRecognition] = useState(null);
|
|
1455
|
+
const recognitionRef = useRef(null);
|
|
1456
|
+
useEffect(() => {
|
|
1457
|
+
if (typeof window === "undefined") return;
|
|
1458
|
+
const SR = window.SpeechRecognition ?? window.webkitSpeechRecognition;
|
|
1459
|
+
if (!SR) return;
|
|
1460
|
+
const sr = new SR();
|
|
1461
|
+
sr.continuous = true;
|
|
1462
|
+
sr.interimResults = true;
|
|
1463
|
+
sr.lang = "en-US";
|
|
1464
|
+
const handleStart = () => setIsListening(true);
|
|
1465
|
+
const handleEnd = () => setIsListening(false);
|
|
1466
|
+
const handleResult = (event) => {
|
|
1467
|
+
const e = event;
|
|
1468
|
+
let final = "";
|
|
1469
|
+
for (const result of Array.from({ length: e.results.length }, (_, i) => e.results[i])) if (result?.isFinal && result[0]?.transcript) final += result[0].transcript;
|
|
1470
|
+
if (final && textareaRef?.current) {
|
|
1471
|
+
const ta = textareaRef.current;
|
|
1472
|
+
const next = ta.value + (ta.value ? " " : "") + final;
|
|
1473
|
+
ta.value = next;
|
|
1474
|
+
ta.dispatchEvent(new Event("input", { bubbles: true }));
|
|
1475
|
+
onTranscriptionChange?.(next);
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
const handleError = (_event) => setIsListening(false);
|
|
1479
|
+
sr.addEventListener("start", handleStart);
|
|
1480
|
+
sr.addEventListener("end", handleEnd);
|
|
1481
|
+
sr.addEventListener("result", handleResult);
|
|
1482
|
+
sr.addEventListener("error", handleError);
|
|
1483
|
+
recognitionRef.current = sr;
|
|
1484
|
+
setRecognition(sr);
|
|
1485
|
+
return () => {
|
|
1486
|
+
sr.removeEventListener("start", handleStart);
|
|
1487
|
+
sr.removeEventListener("end", handleEnd);
|
|
1488
|
+
sr.removeEventListener("result", handleResult);
|
|
1489
|
+
sr.removeEventListener("error", handleError);
|
|
1490
|
+
recognitionRef.current?.stop();
|
|
1491
|
+
};
|
|
1492
|
+
}, [textareaRef, onTranscriptionChange]);
|
|
1493
|
+
const handleToggleListening = useCallback(() => {
|
|
1494
|
+
if (!recognition) return;
|
|
1495
|
+
const sr = recognition;
|
|
1496
|
+
if (isListening) sr.stop();
|
|
1497
|
+
else sr.start();
|
|
1498
|
+
}, [recognition, isListening]);
|
|
1499
|
+
const button = /* @__PURE__ */ jsx(Button, {
|
|
1500
|
+
"aria-label": ariaLabel,
|
|
1501
|
+
className: cn("text-sf-subtle hover:text-sf-default transition-all", isListening && "animate-pulse text-sf-brand", className),
|
|
1502
|
+
disabled: !recognition,
|
|
1503
|
+
onClick: handleToggleListening,
|
|
1504
|
+
size: "sm",
|
|
1505
|
+
type: "button",
|
|
1506
|
+
variant: "ghost",
|
|
1507
|
+
...props,
|
|
1508
|
+
children: /* @__PURE__ */ jsx(MicrophoneIcon, { className: "size-4" })
|
|
1509
|
+
});
|
|
1510
|
+
if (!recognition) return null;
|
|
1511
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
1512
|
+
content: isListening ? "Stop listening" : "Voice input",
|
|
1513
|
+
children: button
|
|
1514
|
+
});
|
|
1515
|
+
};
|
|
1516
|
+
/**
|
|
1517
|
+
* Convenience button that opens the file dialog directly (no dropdown needed).
|
|
1518
|
+
*/
|
|
1519
|
+
var PromptInputAttachButton = ({ "aria-label": ariaLabel = "Attach file", className, ...props }) => {
|
|
1520
|
+
const attachments = usePromptInputAttachments();
|
|
1521
|
+
const handleClick = useCallback(() => {
|
|
1522
|
+
attachments.openFileDialog();
|
|
1523
|
+
}, [attachments]);
|
|
1524
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
1525
|
+
content: ariaLabel,
|
|
1526
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
1527
|
+
"aria-label": ariaLabel,
|
|
1528
|
+
className: cn("text-sf-subtle hover:text-sf-default", className),
|
|
1529
|
+
onClick: handleClick,
|
|
1530
|
+
size: "sm",
|
|
1531
|
+
type: "button",
|
|
1532
|
+
variant: "ghost",
|
|
1533
|
+
...props,
|
|
1534
|
+
children: /* @__PURE__ */ jsx(PaperclipIcon, { className: "size-4" })
|
|
1535
|
+
})
|
|
1536
|
+
});
|
|
1537
|
+
};
|
|
1538
|
+
//#endregion
|
|
1539
|
+
export { useProviderAttachments as A, PromptInputTextarea as C, SF_AI_PROMPT_INPUT_VARIANTS as D, SF_AI_PROMPT_INPUT_DEFAULT_VARIANTS as E, usePromptInputRequestController as F, useRequestField as I, useSetRequestField as L, createPromptInputRequestController as M, useDisplayField as N, usePromptInputAttachments as O, useOptionalPromptInputRequestController as P, PromptInputSubmit as S, PromptInputTools as T, PromptInputModeSelector as _, PromptInputActionMenuItem as a, PromptInputProvider as b, PromptInputAttachment as c, PromptInputBody as d, PromptInputButton as f, PromptInputModeSelect as g, PromptInputModeCycle as h, PromptInputActionMenuContent as i, PromptInputRequestControllerProvider as j, usePromptInputController as k, PromptInputAttachments as l, PromptInputCompactSelect as m, PromptInputActionAddAttachments as n, PromptInputActionMenuTrigger as o, PromptInputCompactCycle as p, PromptInputActionMenu as r, PromptInputAttachButton as s, PromptInput as t, PromptInputBackLayer as u, PromptInputModelCycle as v, PromptInputToolbar as w, PromptInputSpeechButton as x, PromptInputModelSelect as y };
|
|
1540
|
+
|
|
1541
|
+
//# sourceMappingURL=ai-prompt-input-Dy1LfxPk.js.map
|