@diegopluna/narvi 0.0.0-stage → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -2
- package/dist/components/activity/ActivityContent.svelte +55 -0
- package/dist/components/activity/ActivityContent.svelte.d.ts +11 -0
- package/dist/components/activity/ActivityRoot.svelte +66 -0
- package/dist/components/activity/ActivityRoot.svelte.d.ts +17 -0
- package/dist/components/activity/ActivityStep.svelte +157 -0
- package/dist/components/activity/ActivityStep.svelte.d.ts +26 -0
- package/dist/components/activity/ActivityThought.svelte +60 -0
- package/dist/components/activity/ActivityThought.svelte.d.ts +10 -0
- package/dist/components/activity/ActivityTrigger.svelte +103 -0
- package/dist/components/activity/ActivityTrigger.svelte.d.ts +20 -0
- package/dist/components/activity/context.d.ts +7 -0
- package/dist/components/activity/context.js +11 -0
- package/dist/components/activity/index.d.ts +11 -0
- package/dist/components/activity/index.js +6 -0
- package/dist/components/announcer/Announcer.svelte +84 -0
- package/dist/components/announcer/Announcer.svelte.d.ts +13 -0
- package/dist/components/app-shell/AppShellHeader.svelte +54 -0
- package/dist/components/app-shell/AppShellHeader.svelte.d.ts +15 -0
- package/dist/components/app-shell/AppShellMain.svelte +49 -0
- package/dist/components/app-shell/AppShellMain.svelte.d.ts +11 -0
- package/dist/components/app-shell/AppShellPanel.svelte +107 -0
- package/dist/components/app-shell/AppShellPanel.svelte.d.ts +18 -0
- package/dist/components/app-shell/AppShellRoot.svelte +136 -0
- package/dist/components/app-shell/AppShellRoot.svelte.d.ts +28 -0
- package/dist/components/app-shell/AppShellSidebar.svelte +192 -0
- package/dist/components/app-shell/AppShellSidebar.svelte.d.ts +17 -0
- package/dist/components/app-shell/AppShellToggle.svelte +50 -0
- package/dist/components/app-shell/AppShellToggle.svelte.d.ts +10 -0
- package/dist/components/app-shell/context.d.ts +15 -0
- package/dist/components/app-shell/context.js +15 -0
- package/dist/components/app-shell/index.d.ts +15 -0
- package/dist/components/app-shell/index.js +8 -0
- package/dist/components/approval/Approval.svelte +213 -0
- package/dist/components/approval/Approval.svelte.d.ts +38 -0
- package/dist/components/avatar/AvatarFallback.svelte +73 -0
- package/dist/components/avatar/AvatarFallback.svelte.d.ts +11 -0
- package/dist/components/avatar/AvatarImage.svelte +75 -0
- package/dist/components/avatar/AvatarImage.svelte.d.ts +11 -0
- package/dist/components/avatar/AvatarRoot.svelte +108 -0
- package/dist/components/avatar/AvatarRoot.svelte.d.ts +19 -0
- package/dist/components/avatar/context.d.ts +11 -0
- package/dist/components/avatar/context.js +11 -0
- package/dist/components/avatar/index.d.ts +7 -0
- package/dist/components/avatar/index.js +6 -0
- package/dist/components/badge/Badge.svelte +78 -0
- package/dist/components/badge/Badge.svelte.d.ts +15 -0
- package/dist/components/button/Button.svelte +270 -0
- package/dist/components/button/Button.svelte.d.ts +22 -0
- package/dist/components/card/Card.svelte +65 -0
- package/dist/components/card/Card.svelte.d.ts +15 -0
- package/dist/components/change-stats/ChangeStats.svelte +52 -0
- package/dist/components/change-stats/ChangeStats.svelte.d.ts +14 -0
- package/dist/components/changes-summary/ChangesSummary.svelte +152 -0
- package/dist/components/changes-summary/ChangesSummary.svelte.d.ts +20 -0
- package/dist/components/checkbox/Checkbox.svelte +178 -0
- package/dist/components/checkbox/Checkbox.svelte.d.ts +19 -0
- package/dist/components/code/Code.svelte +33 -0
- package/dist/components/code/Code.svelte.d.ts +10 -0
- package/dist/components/command/CommandDialog.svelte +37 -0
- package/dist/components/command/CommandDialog.svelte.d.ts +12 -0
- package/dist/components/command/CommandEmpty.svelte +20 -0
- package/dist/components/command/CommandEmpty.svelte.d.ts +11 -0
- package/dist/components/command/CommandFooter.svelte +25 -0
- package/dist/components/command/CommandFooter.svelte.d.ts +11 -0
- package/dist/components/command/CommandGroup.svelte +25 -0
- package/dist/components/command/CommandGroup.svelte.d.ts +12 -0
- package/dist/components/command/CommandInput.svelte +33 -0
- package/dist/components/command/CommandInput.svelte.d.ts +8 -0
- package/dist/components/command/CommandItem.svelte +49 -0
- package/dist/components/command/CommandItem.svelte.d.ts +16 -0
- package/dist/components/command/CommandList.svelte +21 -0
- package/dist/components/command/CommandList.svelte.d.ts +10 -0
- package/dist/components/command/CommandRoot.svelte +20 -0
- package/dist/components/command/CommandRoot.svelte.d.ts +11 -0
- package/dist/components/command/index.d.ts +20 -0
- package/dist/components/command/index.js +12 -0
- package/dist/components/command/styles.d.ts +117 -0
- package/dist/components/command/styles.js +121 -0
- package/dist/components/composer/ComposerAttachButton.svelte +55 -0
- package/dist/components/composer/ComposerAttachButton.svelte.d.ts +14 -0
- package/dist/components/composer/ComposerAttachment.svelte +173 -0
- package/dist/components/composer/ComposerAttachment.svelte.d.ts +13 -0
- package/dist/components/composer/ComposerAttachments.svelte +63 -0
- package/dist/components/composer/ComposerAttachments.svelte.d.ts +13 -0
- package/dist/components/composer/ComposerModelPicker.svelte +475 -0
- package/dist/components/composer/ComposerModelPicker.svelte.d.ts +27 -0
- package/dist/components/composer/ComposerRoot.svelte +315 -0
- package/dist/components/composer/ComposerRoot.svelte.d.ts +42 -0
- package/dist/components/composer/ComposerSubmit.svelte +131 -0
- package/dist/components/composer/ComposerSubmit.svelte.d.ts +13 -0
- package/dist/components/composer/ComposerTextarea.svelte +109 -0
- package/dist/components/composer/ComposerTextarea.svelte.d.ts +9 -0
- package/dist/components/composer/ComposerToggle.svelte +106 -0
- package/dist/components/composer/ComposerToggle.svelte.d.ts +14 -0
- package/dist/components/composer/ComposerToolbar.svelte +29 -0
- package/dist/components/composer/ComposerToolbar.svelte.d.ts +11 -0
- package/dist/components/composer/context.d.ts +53 -0
- package/dist/components/composer/context.js +32 -0
- package/dist/components/composer/index.d.ts +21 -0
- package/dist/components/composer/index.js +13 -0
- package/dist/components/conversation/ConversationContent.svelte +94 -0
- package/dist/components/conversation/ConversationContent.svelte.d.ts +15 -0
- package/dist/components/conversation/ConversationEmpty.svelte +89 -0
- package/dist/components/conversation/ConversationEmpty.svelte.d.ts +16 -0
- package/dist/components/conversation/ConversationRoot.svelte +237 -0
- package/dist/components/conversation/ConversationRoot.svelte.d.ts +20 -0
- package/dist/components/conversation/ConversationScrollButton.svelte +114 -0
- package/dist/components/conversation/ConversationScrollButton.svelte.d.ts +14 -0
- package/dist/components/conversation/context.d.ts +28 -0
- package/dist/components/conversation/context.js +15 -0
- package/dist/components/conversation/index.d.ts +11 -0
- package/dist/components/conversation/index.js +8 -0
- package/dist/components/conversation/scrolling.d.ts +25 -0
- package/dist/components/conversation/scrolling.js +60 -0
- package/dist/components/dev-styles/DevStyles.svelte +21 -0
- package/dist/components/dev-styles/DevStyles.svelte.d.ts +3 -0
- package/dist/components/dialog/ConfirmDialog.svelte +157 -0
- package/dist/components/dialog/ConfirmDialog.svelte.d.ts +31 -0
- package/dist/components/dialog/DialogBody.svelte +19 -0
- package/dist/components/dialog/DialogBody.svelte.d.ts +10 -0
- package/dist/components/dialog/DialogContent.svelte +55 -0
- package/dist/components/dialog/DialogContent.svelte.d.ts +17 -0
- package/dist/components/dialog/DialogDescription.svelte +23 -0
- package/dist/components/dialog/DialogDescription.svelte.d.ts +10 -0
- package/dist/components/dialog/DialogFooter.svelte +19 -0
- package/dist/components/dialog/DialogFooter.svelte.d.ts +10 -0
- package/dist/components/dialog/DialogHeader.svelte +19 -0
- package/dist/components/dialog/DialogHeader.svelte.d.ts +10 -0
- package/dist/components/dialog/DialogTitle.svelte +20 -0
- package/dist/components/dialog/DialogTitle.svelte.d.ts +10 -0
- package/dist/components/dialog/index.d.ts +17 -0
- package/dist/components/dialog/index.js +12 -0
- package/dist/components/dialog/styles.d.ts +120 -0
- package/dist/components/dialog/styles.js +136 -0
- package/dist/components/divider/Divider.svelte +42 -0
- package/dist/components/divider/Divider.svelte.d.ts +11 -0
- package/dist/components/empty-state/EmptyState.svelte +82 -0
- package/dist/components/empty-state/EmptyState.svelte.d.ts +15 -0
- package/dist/components/field/Field.svelte +132 -0
- package/dist/components/field/Field.svelte.d.ts +23 -0
- package/dist/components/field/context.d.ts +12 -0
- package/dist/components/field/context.js +9 -0
- package/dist/components/frame/Frame.svelte +88 -0
- package/dist/components/frame/Frame.svelte.d.ts +19 -0
- package/dist/components/heading/Heading.svelte +82 -0
- package/dist/components/heading/Heading.svelte.d.ts +16 -0
- package/dist/components/input/Input.svelte +47 -0
- package/dist/components/input/Input.svelte.d.ts +14 -0
- package/dist/components/kbd/Kbd.svelte +40 -0
- package/dist/components/kbd/Kbd.svelte.d.ts +11 -0
- package/dist/components/link/Link.svelte +68 -0
- package/dist/components/link/Link.svelte.d.ts +16 -0
- package/dist/components/menu/ContextMenuContent.svelte +33 -0
- package/dist/components/menu/ContextMenuContent.svelte.d.ts +11 -0
- package/dist/components/menu/ContextMenuTrigger.svelte +44 -0
- package/dist/components/menu/ContextMenuTrigger.svelte.d.ts +11 -0
- package/dist/components/menu/MenuCheckboxItem.svelte +43 -0
- package/dist/components/menu/MenuCheckboxItem.svelte.d.ts +11 -0
- package/dist/components/menu/MenuContent.svelte +36 -0
- package/dist/components/menu/MenuContent.svelte.d.ts +10 -0
- package/dist/components/menu/MenuItem.svelte +41 -0
- package/dist/components/menu/MenuItem.svelte.d.ts +16 -0
- package/dist/components/menu/MenuLabel.svelte +24 -0
- package/dist/components/menu/MenuLabel.svelte.d.ts +10 -0
- package/dist/components/menu/MenuRadioItem.svelte +54 -0
- package/dist/components/menu/MenuRadioItem.svelte.d.ts +12 -0
- package/dist/components/menu/MenuSeparator.svelte +19 -0
- package/dist/components/menu/MenuSeparator.svelte.d.ts +8 -0
- package/dist/components/menu/MenuSubContent.svelte +34 -0
- package/dist/components/menu/MenuSubContent.svelte.d.ts +10 -0
- package/dist/components/menu/MenuSubTrigger.svelte +35 -0
- package/dist/components/menu/MenuSubTrigger.svelte.d.ts +11 -0
- package/dist/components/menu/context-menu.d.ts +11 -0
- package/dist/components/menu/context-menu.js +10 -0
- package/dist/components/menu/index.js +16 -0
- package/dist/components/message/MessageAction.svelte +62 -0
- package/dist/components/message/MessageAction.svelte.d.ts +16 -0
- package/dist/components/message/MessageActions.svelte +77 -0
- package/dist/components/message/MessageActions.svelte.d.ts +19 -0
- package/dist/components/message/MessageContent.svelte +68 -0
- package/dist/components/message/MessageContent.svelte.d.ts +12 -0
- package/dist/components/message/MessageCopy.svelte +106 -0
- package/dist/components/message/MessageCopy.svelte.d.ts +16 -0
- package/dist/components/message/MessageHeader.svelte +74 -0
- package/dist/components/message/MessageHeader.svelte.d.ts +15 -0
- package/dist/components/message/MessagePending.svelte +44 -0
- package/dist/components/message/MessagePending.svelte.d.ts +10 -0
- package/dist/components/message/MessageRoot.svelte +79 -0
- package/dist/components/message/MessageRoot.svelte.d.ts +20 -0
- package/dist/components/message/MessageTime.svelte +58 -0
- package/dist/components/message/MessageTime.svelte.d.ts +12 -0
- package/dist/components/message/context.d.ts +7 -0
- package/dist/components/message/context.js +11 -0
- package/dist/components/message/index.d.ts +18 -0
- package/dist/components/message/index.js +11 -0
- package/dist/components/notice/Notice.svelte +111 -0
- package/dist/components/notice/Notice.svelte.d.ts +26 -0
- package/dist/components/plan/Plan.svelte +198 -0
- package/dist/components/plan/Plan.svelte.d.ts +18 -0
- package/dist/components/popover/PopoverContent.svelte +42 -0
- package/dist/components/popover/PopoverContent.svelte.d.ts +10 -0
- package/dist/components/popover/index.d.ts +7 -0
- package/dist/components/popover/index.js +7 -0
- package/dist/components/progress/Progress.svelte +113 -0
- package/dist/components/progress/Progress.svelte.d.ts +11 -0
- package/dist/components/questionnaire/Questionnaire.svelte +315 -0
- package/dist/components/questionnaire/Questionnaire.svelte.d.ts +33 -0
- package/dist/components/queued-message/QueuedMessage.svelte +133 -0
- package/dist/components/queued-message/QueuedMessage.svelte.d.ts +23 -0
- package/dist/components/rail/RailItem.svelte +170 -0
- package/dist/components/rail/RailItem.svelte.d.ts +21 -0
- package/dist/components/rail/RailRoot.svelte +46 -0
- package/dist/components/rail/RailRoot.svelte.d.ts +13 -0
- package/dist/components/rail/RailSpacer.svelte +2 -0
- package/dist/components/rail/RailSpacer.svelte.d.ts +26 -0
- package/dist/components/rail/index.d.ts +6 -0
- package/dist/components/rail/index.js +4 -0
- package/dist/components/reasoning/ReasoningContent.svelte +56 -0
- package/dist/components/reasoning/ReasoningContent.svelte.d.ts +12 -0
- package/dist/components/reasoning/ReasoningRoot.svelte +102 -0
- package/dist/components/reasoning/ReasoningRoot.svelte.d.ts +25 -0
- package/dist/components/reasoning/ReasoningTrigger.svelte +106 -0
- package/dist/components/reasoning/ReasoningTrigger.svelte.d.ts +18 -0
- package/dist/components/reasoning/context.d.ts +8 -0
- package/dist/components/reasoning/context.js +11 -0
- package/dist/components/reasoning/index.d.ts +7 -0
- package/dist/components/reasoning/index.js +6 -0
- package/dist/components/resize-handle/ResizeHandle.svelte +177 -0
- package/dist/components/resize-handle/ResizeHandle.svelte.d.ts +29 -0
- package/dist/components/rolling-text/RollingText.svelte +51 -0
- package/dist/components/rolling-text/RollingText.svelte.d.ts +11 -0
- package/dist/components/segmented/SegmentedControl.svelte +171 -0
- package/dist/components/segmented/SegmentedControl.svelte.d.ts +20 -0
- package/dist/components/select/Select.svelte +146 -0
- package/dist/components/select/Select.svelte.d.ts +31 -0
- package/dist/components/separator/Separator.svelte +48 -0
- package/dist/components/separator/Separator.svelte.d.ts +11 -0
- package/dist/components/settings-row/SettingsRow.svelte +97 -0
- package/dist/components/settings-row/SettingsRow.svelte.d.ts +21 -0
- package/dist/components/shimmer/Shimmer.svelte +66 -0
- package/dist/components/shimmer/Shimmer.svelte.d.ts +12 -0
- package/dist/components/sidebar/SidebarContent.svelte +70 -0
- package/dist/components/sidebar/SidebarContent.svelte.d.ts +13 -0
- package/dist/components/sidebar/SidebarFooter.svelte +33 -0
- package/dist/components/sidebar/SidebarFooter.svelte.d.ts +10 -0
- package/dist/components/sidebar/SidebarGroup.svelte +123 -0
- package/dist/components/sidebar/SidebarGroup.svelte.d.ts +19 -0
- package/dist/components/sidebar/SidebarHeader.svelte +39 -0
- package/dist/components/sidebar/SidebarHeader.svelte.d.ts +12 -0
- package/dist/components/sidebar/SidebarItem.svelte +236 -0
- package/dist/components/sidebar/SidebarItem.svelte.d.ts +27 -0
- package/dist/components/sidebar/index.d.ts +11 -0
- package/dist/components/sidebar/index.js +6 -0
- package/dist/components/skeleton/Skeleton.svelte +61 -0
- package/dist/components/skeleton/Skeleton.svelte.d.ts +13 -0
- package/dist/components/spinner/Spinner.svelte +57 -0
- package/dist/components/spinner/Spinner.svelte.d.ts +12 -0
- package/dist/components/stack/Stack.svelte +99 -0
- package/dist/components/stack/Stack.svelte.d.ts +19 -0
- package/dist/components/status-indicator/StatusIndicator.svelte +97 -0
- package/dist/components/status-indicator/StatusIndicator.svelte.d.ts +14 -0
- package/dist/components/suggestions/SuggestionsItem.svelte +104 -0
- package/dist/components/suggestions/SuggestionsItem.svelte.d.ts +14 -0
- package/dist/components/suggestions/SuggestionsRoot.svelte +80 -0
- package/dist/components/suggestions/SuggestionsRoot.svelte.d.ts +20 -0
- package/dist/components/suggestions/context.d.ts +7 -0
- package/dist/components/suggestions/context.js +8 -0
- package/dist/components/suggestions/index.d.ts +5 -0
- package/dist/components/suggestions/index.js +5 -0
- package/dist/components/switch/Switch.svelte +175 -0
- package/dist/components/switch/Switch.svelte.d.ts +17 -0
- package/dist/components/tabs/Tabs.svelte +170 -0
- package/dist/components/tabs/Tabs.svelte.d.ts +22 -0
- package/dist/components/text/Text.svelte +88 -0
- package/dist/components/text/Text.svelte.d.ts +20 -0
- package/dist/components/textarea/Textarea.svelte +64 -0
- package/dist/components/textarea/Textarea.svelte.d.ts +13 -0
- package/dist/components/theme/Theme.svelte +49 -0
- package/dist/components/theme/Theme.svelte.d.ts +25 -0
- package/dist/components/toast/Toaster.svelte +223 -0
- package/dist/components/toast/Toaster.svelte.d.ts +9 -0
- package/dist/components/toast/toasts.svelte.d.ts +55 -0
- package/dist/components/toast/toasts.svelte.js +66 -0
- package/dist/components/tool/ToolContent.svelte +51 -0
- package/dist/components/tool/ToolContent.svelte.d.ts +11 -0
- package/dist/components/tool/ToolInput.svelte +21 -0
- package/dist/components/tool/ToolInput.svelte.d.ts +14 -0
- package/dist/components/tool/ToolOutput.svelte +33 -0
- package/dist/components/tool/ToolOutput.svelte.d.ts +16 -0
- package/dist/components/tool/ToolRoot.svelte +94 -0
- package/dist/components/tool/ToolRoot.svelte.d.ts +23 -0
- package/dist/components/tool/ToolSection.svelte +102 -0
- package/dist/components/tool/ToolSection.svelte.d.ts +16 -0
- package/dist/components/tool/ToolTrigger.svelte +191 -0
- package/dist/components/tool/ToolTrigger.svelte.d.ts +14 -0
- package/dist/components/tool/context.d.ts +18 -0
- package/dist/components/tool/context.js +11 -0
- package/dist/components/tool/index.d.ts +12 -0
- package/dist/components/tool/index.js +8 -0
- package/dist/components/tooltip/TooltipContent.svelte +76 -0
- package/dist/components/tooltip/TooltipContent.svelte.d.ts +12 -0
- package/dist/components/tooltip/TooltipRoot.svelte +37 -0
- package/dist/components/tooltip/TooltipRoot.svelte.d.ts +10 -0
- package/dist/components/tooltip/TooltipTrigger.svelte +25 -0
- package/dist/components/tooltip/TooltipTrigger.svelte.d.ts +5 -0
- package/dist/components/tooltip/index.d.ts +13 -0
- package/dist/components/tooltip/index.js +10 -0
- package/dist/components/tooltip/intent.d.ts +2 -0
- package/dist/components/tooltip/intent.js +29 -0
- package/dist/components/work-fold/WorkFold.svelte +100 -0
- package/dist/components/work-fold/WorkFold.svelte.d.ts +15 -0
- package/dist/index.d.ts +127 -0
- package/dist/index.js +93 -0
- package/dist/internal/Chevron.svelte +28 -0
- package/dist/internal/Chevron.svelte.d.ts +7 -0
- package/dist/internal/Code.svelte +70 -0
- package/dist/internal/Code.svelte.d.ts +10 -0
- package/dist/internal/Collapse.svelte +57 -0
- package/dist/internal/Collapse.svelte.d.ts +12 -0
- package/dist/internal/Glyph.svelte +89 -0
- package/dist/internal/Glyph.svelte.d.ts +37 -0
- package/dist/internal/Tile.svelte +86 -0
- package/dist/internal/Tile.svelte.d.ts +15 -0
- package/dist/internal/control.d.ts +58 -0
- package/dist/internal/control.js +74 -0
- package/dist/internal/disclosure.d.ts +7 -0
- package/dist/internal/disclosure.js +1 -0
- package/dist/internal/json.d.ts +13 -0
- package/dist/internal/json.js +51 -0
- package/dist/internal/kinds.d.ts +9 -0
- package/dist/internal/kinds.js +25 -0
- package/dist/internal/layer.d.ts +103 -0
- package/dist/internal/layer.js +129 -0
- package/dist/internal/loop.d.ts +35 -0
- package/dist/internal/loop.js +72 -0
- package/dist/internal/markers.stylex.d.ts +14 -0
- package/dist/internal/markers.stylex.js +10 -0
- package/dist/internal/roving.d.ts +17 -0
- package/dist/internal/roving.js +68 -0
- package/dist/internal/shared.d.ts +36 -0
- package/dist/internal/shared.js +44 -0
- package/dist/internal/sx.d.ts +14 -0
- package/dist/internal/sx.js +11 -0
- package/dist/preset.d.ts +102 -0
- package/dist/preset.js +334 -0
- package/dist/tokens.stylex.d.ts +104 -0
- package/dist/tokens.stylex.js +125 -0
- package/dist/vite.d.ts +16 -0
- package/dist/vite.js +58 -0
- package/package.json +123 -5
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import * as stylex from '@stylexjs/stylex';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import type { HTMLFormAttributes } from 'svelte/elements';
|
|
5
|
+
import { shared } from '../../internal/shared.js';
|
|
6
|
+
import { attrs, type Sx } from '../../internal/sx.js';
|
|
7
|
+
import { colors, font, motion, radius } from '../../tokens.stylex.js';
|
|
8
|
+
import {
|
|
9
|
+
accepts,
|
|
10
|
+
setComposerContext,
|
|
11
|
+
type ComposerMessage,
|
|
12
|
+
type ComposerRejection,
|
|
13
|
+
type ComposerStatus
|
|
14
|
+
} from './context.js';
|
|
15
|
+
|
|
16
|
+
export type ComposerRootProps = Omit<HTMLFormAttributes, 'children' | 'onsubmit'> & {
|
|
17
|
+
/** What the person has typed. Bindable. */
|
|
18
|
+
value?: string;
|
|
19
|
+
/** The files attached so far. Bindable. */
|
|
20
|
+
files?: File[];
|
|
21
|
+
status?: ComposerStatus;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** File types to take, as in an input's `accept`, such as `image/*,.pdf`. */
|
|
24
|
+
accept?: string;
|
|
25
|
+
/** The most files at once. */
|
|
26
|
+
maxFiles?: number;
|
|
27
|
+
/** The largest file, in bytes. */
|
|
28
|
+
maxSize?: number;
|
|
29
|
+
/** What is shown over the composer while files are dragged onto it. */
|
|
30
|
+
dropLabel?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Called with the trimmed text and the files when the person sends. The
|
|
33
|
+
* composer is cleared afterwards, unless this returns `false`.
|
|
34
|
+
*/
|
|
35
|
+
onsubmit?: (message: ComposerMessage, event: SubmitEvent) => unknown;
|
|
36
|
+
/** Called when the person stops a reply that is on its way. */
|
|
37
|
+
onstop?: () => void;
|
|
38
|
+
/**
|
|
39
|
+
* Called with the message when the person sends while a reply is under
|
|
40
|
+
* way. Without it, nothing can be sent until the reply ends. Show what is
|
|
41
|
+
* queued with a QueuedMessage; the composer is cleared unless this returns `false`.
|
|
42
|
+
*/
|
|
43
|
+
onqueue?: (message: ComposerMessage, event: SubmitEvent) => unknown;
|
|
44
|
+
/** Called with the files that were turned away, and why. */
|
|
45
|
+
onreject?: (rejections: ComposerRejection[]) => void;
|
|
46
|
+
sx?: Sx;
|
|
47
|
+
ref?: HTMLFormElement | null;
|
|
48
|
+
/** Composer.Attachments, a Composer.Textarea and a Composer.Toolbar. */
|
|
49
|
+
children?: Snippet;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const FORCED = '@media (forced-colors: active)';
|
|
53
|
+
const MOTION = '@media (prefers-reduced-motion: no-preference)';
|
|
54
|
+
|
|
55
|
+
const styles = stylex.create({
|
|
56
|
+
root: {
|
|
57
|
+
position: 'relative',
|
|
58
|
+
boxSizing: 'border-box',
|
|
59
|
+
display: 'flex',
|
|
60
|
+
flexDirection: 'column',
|
|
61
|
+
gap: '0.25rem',
|
|
62
|
+
minWidth: 0,
|
|
63
|
+
margin: 0,
|
|
64
|
+
padding: '0.5rem',
|
|
65
|
+
// The controls' radius plus this padding, so the corners sit concentric.
|
|
66
|
+
// Both follow the radius scale: 1rem and 1.5rem by default.
|
|
67
|
+
borderRadius: `calc(${radius.xl} * 4 / 3 + 0.5rem)`,
|
|
68
|
+
backgroundColor: colors.bgRaised,
|
|
69
|
+
color: colors.text,
|
|
70
|
+
fontFamily: font.body,
|
|
71
|
+
cursor: 'text',
|
|
72
|
+
boxShadow: {
|
|
73
|
+
default: `inset 0 0 0 1px ${colors.line}, inset 0 1px 0 ${colors.highlight}, 0 1px 2px ${colors.shade}, 0 16px 32px -20px ${colors.shade}`,
|
|
74
|
+
':focus-within': `inset 0 0 0 1px ${colors.edge}, inset 0 1px 0 ${colors.highlight}, 0 1px 2px ${colors.shade}, 0 16px 32px -20px ${colors.shade}`
|
|
75
|
+
},
|
|
76
|
+
// In forced colours the shadow is gone; the outline is the edge, and thickens with focus
|
|
77
|
+
outlineWidth: { default: '1px', [FORCED]: { default: '1px', ':focus-within': '2px' } },
|
|
78
|
+
transitionProperty: 'box-shadow',
|
|
79
|
+
transitionDuration: motion.base,
|
|
80
|
+
transitionTimingFunction: 'ease'
|
|
81
|
+
},
|
|
82
|
+
disabled: {
|
|
83
|
+
opacity: 0.6,
|
|
84
|
+
cursor: 'not-allowed'
|
|
85
|
+
},
|
|
86
|
+
// Dragging files over: the whole composer lights up as the place to let go
|
|
87
|
+
drop: {
|
|
88
|
+
position: 'absolute',
|
|
89
|
+
inset: 0,
|
|
90
|
+
zIndex: 2,
|
|
91
|
+
display: 'grid',
|
|
92
|
+
placeItems: 'center',
|
|
93
|
+
borderRadius: 'inherit',
|
|
94
|
+
backgroundColor: `color-mix(in oklab, ${colors.bgRaised} 86%, transparent)`,
|
|
95
|
+
backdropFilter: 'blur(2px)',
|
|
96
|
+
boxShadow: `inset 0 0 0 1.5px ${colors.ithil}, 0 0 24px ${colors.glow}`,
|
|
97
|
+
color: colors.ithil,
|
|
98
|
+
fontSize: font.sizeMd,
|
|
99
|
+
fontWeight: font.weightMedium,
|
|
100
|
+
pointerEvents: 'none',
|
|
101
|
+
opacity: { default: 1, '@starting-style': 0 },
|
|
102
|
+
scale: { default: null, [MOTION]: { default: 1, '@starting-style': 0.98 } },
|
|
103
|
+
transitionProperty: { default: 'opacity', [MOTION]: 'opacity, scale' },
|
|
104
|
+
transitionDuration: motion.base,
|
|
105
|
+
transitionTimingFunction: motion.easeOut
|
|
106
|
+
},
|
|
107
|
+
input: { display: 'none' },
|
|
108
|
+
status: {
|
|
109
|
+
position: 'absolute',
|
|
110
|
+
width: '1px',
|
|
111
|
+
height: '1px',
|
|
112
|
+
overflow: 'hidden',
|
|
113
|
+
clipPath: 'inset(50%)',
|
|
114
|
+
whiteSpace: 'nowrap'
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<script lang="ts">
|
|
120
|
+
import { untrack } from 'svelte';
|
|
121
|
+
|
|
122
|
+
let {
|
|
123
|
+
value = $bindable(''),
|
|
124
|
+
files = $bindable([]),
|
|
125
|
+
status = 'ready',
|
|
126
|
+
disabled = false,
|
|
127
|
+
accept,
|
|
128
|
+
maxFiles,
|
|
129
|
+
maxSize,
|
|
130
|
+
dropLabel = 'Drop files to attach',
|
|
131
|
+
onsubmit,
|
|
132
|
+
onstop,
|
|
133
|
+
onqueue,
|
|
134
|
+
onreject,
|
|
135
|
+
sx,
|
|
136
|
+
class: className,
|
|
137
|
+
ref = $bindable(null),
|
|
138
|
+
children,
|
|
139
|
+
onclick,
|
|
140
|
+
...rest
|
|
141
|
+
}: ComposerRootProps = $props();
|
|
142
|
+
|
|
143
|
+
let textarea: HTMLTextAreaElement | null = null;
|
|
144
|
+
let picker = $state<HTMLInputElement | null>(null);
|
|
145
|
+
let attachments = $state(0);
|
|
146
|
+
// dragenter and dragleave fire for every child crossed; count them to know when the drag has left
|
|
147
|
+
let depth = $state(0);
|
|
148
|
+
let announcement = $state('');
|
|
149
|
+
|
|
150
|
+
const busy = $derived(status === 'submitted' || status === 'streaming');
|
|
151
|
+
const attachable = $derived(attachments > 0 && !disabled);
|
|
152
|
+
|
|
153
|
+
function addFiles(incoming: Iterable<File>) {
|
|
154
|
+
const taken: File[] = [];
|
|
155
|
+
const rejected: ComposerRejection[] = [];
|
|
156
|
+
for (const file of incoming) {
|
|
157
|
+
if (!accepts(file, accept)) rejected.push({ file, reason: 'type' });
|
|
158
|
+
else if (maxSize !== undefined && file.size > maxSize)
|
|
159
|
+
rejected.push({ file, reason: 'size' });
|
|
160
|
+
else if (maxFiles !== undefined && files.length + taken.length >= maxFiles)
|
|
161
|
+
rejected.push({ file, reason: 'count' });
|
|
162
|
+
else taken.push(file);
|
|
163
|
+
}
|
|
164
|
+
if (taken.length) {
|
|
165
|
+
files = [...files, ...taken];
|
|
166
|
+
announcement =
|
|
167
|
+
taken.length === 1 ? `Attached ${taken[0].name}` : `Attached ${taken.length} files`;
|
|
168
|
+
}
|
|
169
|
+
if (rejected.length) onreject?.(rejected);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function handleSubmit(event: SubmitEvent) {
|
|
173
|
+
event.preventDefault();
|
|
174
|
+
if (disabled) return;
|
|
175
|
+
const text = value.trim();
|
|
176
|
+
const empty = !text && files.length === 0;
|
|
177
|
+
if (busy) {
|
|
178
|
+
// The Stop button stops; Enter in the text, with something written, queues
|
|
179
|
+
const stopping = !!(event.submitter as HTMLElement | null)?.closest(
|
|
180
|
+
'[data-slot="composer-submit"]'
|
|
181
|
+
);
|
|
182
|
+
if (stopping || !onqueue || empty) {
|
|
183
|
+
if (stopping) onstop?.();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (onqueue({ text, files: [...files] }, event) !== false) {
|
|
187
|
+
value = '';
|
|
188
|
+
files = [];
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (empty) return;
|
|
193
|
+
if (onsubmit?.({ text, files: [...files] }, event) !== false) {
|
|
194
|
+
value = '';
|
|
195
|
+
files = [];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const carriesFiles = (event: DragEvent) => event.dataTransfer?.types.includes('Files') ?? false;
|
|
200
|
+
|
|
201
|
+
setComposerContext({
|
|
202
|
+
get value() {
|
|
203
|
+
return value;
|
|
204
|
+
},
|
|
205
|
+
set value(next) {
|
|
206
|
+
value = next;
|
|
207
|
+
},
|
|
208
|
+
get files() {
|
|
209
|
+
return files;
|
|
210
|
+
},
|
|
211
|
+
get status() {
|
|
212
|
+
return status;
|
|
213
|
+
},
|
|
214
|
+
get disabled() {
|
|
215
|
+
return disabled;
|
|
216
|
+
},
|
|
217
|
+
get busy() {
|
|
218
|
+
return busy;
|
|
219
|
+
},
|
|
220
|
+
get canSend() {
|
|
221
|
+
const something = value.trim().length > 0 || files.length > 0;
|
|
222
|
+
return !disabled && something && (!busy || !!onqueue);
|
|
223
|
+
},
|
|
224
|
+
get attachable() {
|
|
225
|
+
return attachable;
|
|
226
|
+
},
|
|
227
|
+
submit() {
|
|
228
|
+
ref?.requestSubmit();
|
|
229
|
+
},
|
|
230
|
+
addFiles,
|
|
231
|
+
removeFile(file) {
|
|
232
|
+
files = files.filter((f) => f !== file);
|
|
233
|
+
},
|
|
234
|
+
browse() {
|
|
235
|
+
picker?.click();
|
|
236
|
+
},
|
|
237
|
+
focus() {
|
|
238
|
+
textarea?.focus();
|
|
239
|
+
},
|
|
240
|
+
announce(message) {
|
|
241
|
+
announcement = message;
|
|
242
|
+
},
|
|
243
|
+
setTextarea(el) {
|
|
244
|
+
textarea = el;
|
|
245
|
+
},
|
|
246
|
+
// Called from an effect: the count is read here, so it must not become a dependency
|
|
247
|
+
registerAttachments() {
|
|
248
|
+
untrack(() => attachments++);
|
|
249
|
+
return () => untrack(() => attachments--);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
</script>
|
|
253
|
+
|
|
254
|
+
<form
|
|
255
|
+
bind:this={ref}
|
|
256
|
+
data-slot="composer"
|
|
257
|
+
data-status={status}
|
|
258
|
+
data-disabled={disabled || undefined}
|
|
259
|
+
data-dragging={depth > 0 || undefined}
|
|
260
|
+
{...rest}
|
|
261
|
+
{...attrs([shared.edged, styles.root, disabled && styles.disabled, sx], className)}
|
|
262
|
+
onsubmit={handleSubmit}
|
|
263
|
+
onclick={(event) => {
|
|
264
|
+
onclick?.(event);
|
|
265
|
+
// A click on the frame, not on a control inside it, goes to the text
|
|
266
|
+
const target = event.target as HTMLElement;
|
|
267
|
+
if (target === event.currentTarget || target.dataset.slot === 'composer-toolbar')
|
|
268
|
+
textarea?.focus();
|
|
269
|
+
}}
|
|
270
|
+
ondragenter={(event) => {
|
|
271
|
+
if (!attachable || !carriesFiles(event)) return;
|
|
272
|
+
event.preventDefault();
|
|
273
|
+
depth++;
|
|
274
|
+
}}
|
|
275
|
+
ondragover={(event) => {
|
|
276
|
+
if (!attachable || !carriesFiles(event)) return;
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy';
|
|
279
|
+
}}
|
|
280
|
+
ondragleave={() => {
|
|
281
|
+
if (depth > 0) depth--;
|
|
282
|
+
}}
|
|
283
|
+
ondrop={(event) => {
|
|
284
|
+
if (!attachable || !carriesFiles(event)) return;
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
depth = 0;
|
|
287
|
+
addFiles(event.dataTransfer?.files ?? []);
|
|
288
|
+
textarea?.focus();
|
|
289
|
+
}}
|
|
290
|
+
>
|
|
291
|
+
{@render children?.()}
|
|
292
|
+
|
|
293
|
+
<!-- One hidden picker, shared by every Composer.AttachButton -->
|
|
294
|
+
<input
|
|
295
|
+
bind:this={picker}
|
|
296
|
+
type="file"
|
|
297
|
+
multiple={maxFiles !== 1}
|
|
298
|
+
{accept}
|
|
299
|
+
tabindex="-1"
|
|
300
|
+
aria-hidden="true"
|
|
301
|
+
{...stylex.attrs(styles.input)}
|
|
302
|
+
onchange={(event) => {
|
|
303
|
+
const input = event.currentTarget;
|
|
304
|
+
addFiles(input.files ?? []);
|
|
305
|
+
input.value = '';
|
|
306
|
+
textarea?.focus();
|
|
307
|
+
}}
|
|
308
|
+
/>
|
|
309
|
+
|
|
310
|
+
<span role="status" {...stylex.attrs(styles.status)}>{announcement}</span>
|
|
311
|
+
|
|
312
|
+
{#if depth > 0}
|
|
313
|
+
<div aria-hidden="true" {...stylex.attrs(styles.drop)}>{dropLabel}</div>
|
|
314
|
+
{/if}
|
|
315
|
+
</form>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLFormAttributes } from 'svelte/elements';
|
|
3
|
+
import { type Sx } from '../../internal/sx.js';
|
|
4
|
+
import { type ComposerMessage, type ComposerRejection, type ComposerStatus } from './context.js';
|
|
5
|
+
export type ComposerRootProps = Omit<HTMLFormAttributes, 'children' | 'onsubmit'> & {
|
|
6
|
+
/** What the person has typed. Bindable. */
|
|
7
|
+
value?: string;
|
|
8
|
+
/** The files attached so far. Bindable. */
|
|
9
|
+
files?: File[];
|
|
10
|
+
status?: ComposerStatus;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** File types to take, as in an input's `accept`, such as `image/*,.pdf`. */
|
|
13
|
+
accept?: string;
|
|
14
|
+
/** The most files at once. */
|
|
15
|
+
maxFiles?: number;
|
|
16
|
+
/** The largest file, in bytes. */
|
|
17
|
+
maxSize?: number;
|
|
18
|
+
/** What is shown over the composer while files are dragged onto it. */
|
|
19
|
+
dropLabel?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Called with the trimmed text and the files when the person sends. The
|
|
22
|
+
* composer is cleared afterwards, unless this returns `false`.
|
|
23
|
+
*/
|
|
24
|
+
onsubmit?: (message: ComposerMessage, event: SubmitEvent) => unknown;
|
|
25
|
+
/** Called when the person stops a reply that is on its way. */
|
|
26
|
+
onstop?: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Called with the message when the person sends while a reply is under
|
|
29
|
+
* way. Without it, nothing can be sent until the reply ends. Show what is
|
|
30
|
+
* queued with a QueuedMessage; the composer is cleared unless this returns `false`.
|
|
31
|
+
*/
|
|
32
|
+
onqueue?: (message: ComposerMessage, event: SubmitEvent) => unknown;
|
|
33
|
+
/** Called with the files that were turned away, and why. */
|
|
34
|
+
onreject?: (rejections: ComposerRejection[]) => void;
|
|
35
|
+
sx?: Sx;
|
|
36
|
+
ref?: HTMLFormElement | null;
|
|
37
|
+
/** Composer.Attachments, a Composer.Textarea and a Composer.Toolbar. */
|
|
38
|
+
children?: Snippet;
|
|
39
|
+
};
|
|
40
|
+
declare const ComposerRoot: import("svelte").Component<ComposerRootProps, {}, "value" | "ref" | "files">;
|
|
41
|
+
type ComposerRoot = ReturnType<typeof ComposerRoot>;
|
|
42
|
+
export default ComposerRoot;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import * as stylex from '@stylexjs/stylex';
|
|
3
|
+
import Glyph from '../../internal/Glyph.svelte';
|
|
4
|
+
import { loop } from '../../internal/loop.js';
|
|
5
|
+
import type { Sx } from '../../internal/sx.js';
|
|
6
|
+
import { colors, motion, radius } from '../../tokens.stylex.js';
|
|
7
|
+
import Button, { type ButtonProps } from '../button/Button.svelte';
|
|
8
|
+
import { useComposer } from './context.js';
|
|
9
|
+
|
|
10
|
+
export type ComposerSubmitProps = Omit<
|
|
11
|
+
ButtonProps,
|
|
12
|
+
'children' | 'type' | 'variant' | 'iconOnly' | 'href' | 'ref' | 'sx'
|
|
13
|
+
> & {
|
|
14
|
+
/** Accessible name while it sends. */
|
|
15
|
+
sendLabel?: string;
|
|
16
|
+
/** Accessible name while a reply is on its way and pressing it stops the reply. */
|
|
17
|
+
stopLabel?: string;
|
|
18
|
+
sx?: Sx;
|
|
19
|
+
ref?: HTMLButtonElement | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const MOTION = '@media (prefers-reduced-motion: no-preference)';
|
|
23
|
+
|
|
24
|
+
// While the reply has not started, an arc circles the square, on the shared clock
|
|
25
|
+
const spin = loop(
|
|
26
|
+
(reduced) => (reduced ? 2400 : 900),
|
|
27
|
+
(element, { progress }) => (element.style.rotate = `${progress}turn`),
|
|
28
|
+
(element) => (element.style.rotate = '')
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const styles = stylex.create({
|
|
32
|
+
button: {
|
|
33
|
+
marginInlineStart: 'auto',
|
|
34
|
+
// Round by default; squarer when the radius scale is sharp
|
|
35
|
+
borderRadius: `calc(${radius.xl} * 4 / 3)`
|
|
36
|
+
},
|
|
37
|
+
// Nothing to send yet: a quiet socket in the stone, not a greyed-out button.
|
|
38
|
+
// It turns to mithril as soon as there is something to send.
|
|
39
|
+
idle: {
|
|
40
|
+
opacity: 1,
|
|
41
|
+
backgroundImage: 'none',
|
|
42
|
+
backgroundColor: colors.well,
|
|
43
|
+
boxShadow: `inset 0 0 0 1px ${colors.line}`,
|
|
44
|
+
color: colors.textSubtle,
|
|
45
|
+
'::before': { display: 'none' }
|
|
46
|
+
},
|
|
47
|
+
stack: {
|
|
48
|
+
display: 'grid',
|
|
49
|
+
placeItems: 'center'
|
|
50
|
+
},
|
|
51
|
+
// Send and Stop share one cell and trade places, with a little blur so the
|
|
52
|
+
// arrow reads as turning into the square
|
|
53
|
+
icon: {
|
|
54
|
+
gridArea: '1 / 1',
|
|
55
|
+
display: 'grid',
|
|
56
|
+
placeItems: 'center',
|
|
57
|
+
transitionProperty: { default: 'opacity', [MOTION]: 'opacity, scale, filter' },
|
|
58
|
+
transitionDuration: motion.slow,
|
|
59
|
+
transitionTimingFunction: motion.easeOut
|
|
60
|
+
},
|
|
61
|
+
gone: {
|
|
62
|
+
opacity: 0,
|
|
63
|
+
scale: { default: null, [MOTION]: 0.5 },
|
|
64
|
+
filter: { default: null, [MOTION]: 'blur(2px)' }
|
|
65
|
+
},
|
|
66
|
+
// While the reply has not started, an arc circles the square
|
|
67
|
+
ring: {
|
|
68
|
+
gridArea: '1 / 1',
|
|
69
|
+
opacity: { default: 1, '@starting-style': 0 },
|
|
70
|
+
transitionProperty: 'opacity',
|
|
71
|
+
transitionDuration: motion.base
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<script lang="ts">
|
|
77
|
+
let {
|
|
78
|
+
sendLabel = 'Send',
|
|
79
|
+
stopLabel = 'Stop',
|
|
80
|
+
disabled,
|
|
81
|
+
sx,
|
|
82
|
+
ref = $bindable(null),
|
|
83
|
+
...rest
|
|
84
|
+
}: ComposerSubmitProps = $props();
|
|
85
|
+
|
|
86
|
+
const composer = useComposer('Submit');
|
|
87
|
+
const busy = $derived(composer.busy);
|
|
88
|
+
const idle = $derived(!busy && !composer.canSend);
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<Button
|
|
92
|
+
bind:ref={() => ref, (el) => (ref = el as HTMLButtonElement | null)}
|
|
93
|
+
type="submit"
|
|
94
|
+
variant="primary"
|
|
95
|
+
iconOnly
|
|
96
|
+
aria-label={busy ? stopLabel : sendLabel}
|
|
97
|
+
disabled={disabled ?? (composer.disabled || (!busy && !composer.canSend))}
|
|
98
|
+
data-slot="composer-submit"
|
|
99
|
+
data-status={composer.status}
|
|
100
|
+
{...rest}
|
|
101
|
+
sx={[styles.button, idle && styles.idle, sx]}
|
|
102
|
+
>
|
|
103
|
+
<span {...stylex.attrs(styles.stack)}>
|
|
104
|
+
<span {...stylex.attrs(styles.icon, busy && styles.gone)}>
|
|
105
|
+
<Glyph name="arrowUp" stroke={2} />
|
|
106
|
+
</span>
|
|
107
|
+
<span {...stylex.attrs(styles.icon, !busy && styles.gone)}>
|
|
108
|
+
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
|
|
109
|
+
<rect x="4.5" y="4.5" width="7" height="7" rx="1.75" fill="currentColor" />
|
|
110
|
+
</svg>
|
|
111
|
+
</span>
|
|
112
|
+
{#if composer.status === 'submitted'}
|
|
113
|
+
<svg
|
|
114
|
+
width="26"
|
|
115
|
+
height="26"
|
|
116
|
+
viewBox="0 0 26 26"
|
|
117
|
+
fill="none"
|
|
118
|
+
aria-hidden="true"
|
|
119
|
+
{...stylex.attrs(styles.ring)}
|
|
120
|
+
{@attach spin}
|
|
121
|
+
>
|
|
122
|
+
<path
|
|
123
|
+
d="M13 2.5A10.5 10.5 0 0 1 23.5 13"
|
|
124
|
+
stroke="currentColor"
|
|
125
|
+
stroke-width="1.75"
|
|
126
|
+
stroke-linecap="round"
|
|
127
|
+
/>
|
|
128
|
+
</svg>
|
|
129
|
+
{/if}
|
|
130
|
+
</span>
|
|
131
|
+
</Button>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Sx } from '../../internal/sx.js';
|
|
2
|
+
import { type ButtonProps } from '../button/Button.svelte';
|
|
3
|
+
export type ComposerSubmitProps = Omit<ButtonProps, 'children' | 'type' | 'variant' | 'iconOnly' | 'href' | 'ref' | 'sx'> & {
|
|
4
|
+
/** Accessible name while it sends. */
|
|
5
|
+
sendLabel?: string;
|
|
6
|
+
/** Accessible name while a reply is on its way and pressing it stops the reply. */
|
|
7
|
+
stopLabel?: string;
|
|
8
|
+
sx?: Sx;
|
|
9
|
+
ref?: HTMLButtonElement | null;
|
|
10
|
+
};
|
|
11
|
+
declare const ComposerSubmit: import("svelte").Component<ComposerSubmitProps, {}, "ref">;
|
|
12
|
+
type ComposerSubmit = ReturnType<typeof ComposerSubmit>;
|
|
13
|
+
export default ComposerSubmit;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import * as stylex from '@stylexjs/stylex';
|
|
3
|
+
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
|
4
|
+
import { attrs, type Sx } from '../../internal/sx.js';
|
|
5
|
+
import { colors, font } from '../../tokens.stylex.js';
|
|
6
|
+
import { useComposer } from './context.js';
|
|
7
|
+
|
|
8
|
+
export type ComposerTextareaProps = Omit<HTMLTextareaAttributes, 'value'> & {
|
|
9
|
+
sx?: Sx;
|
|
10
|
+
ref?: HTMLTextAreaElement | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const styles = stylex.create({
|
|
14
|
+
textarea: {
|
|
15
|
+
boxSizing: 'border-box',
|
|
16
|
+
display: 'block',
|
|
17
|
+
width: '100%',
|
|
18
|
+
// One line to start; it grows with what is typed, then scrolls
|
|
19
|
+
minHeight: 'calc(1lh + 12px)',
|
|
20
|
+
maxHeight: '14rem',
|
|
21
|
+
margin: 0,
|
|
22
|
+
paddingBlock: '0.375rem',
|
|
23
|
+
paddingInline: '0.5rem',
|
|
24
|
+
borderWidth: 0,
|
|
25
|
+
backgroundColor: 'transparent',
|
|
26
|
+
color: colors.text,
|
|
27
|
+
fontFamily: 'inherit',
|
|
28
|
+
// The size messages are read at. At 16px, iOS also leaves the page unzoomed on focus.
|
|
29
|
+
fontSize: font.sizeLg,
|
|
30
|
+
lineHeight: font.leadingNormal,
|
|
31
|
+
fieldSizing: 'content',
|
|
32
|
+
resize: 'none',
|
|
33
|
+
overflowY: 'auto',
|
|
34
|
+
scrollbarWidth: 'thin',
|
|
35
|
+
// The frame around it shows the focus
|
|
36
|
+
outlineStyle: 'none',
|
|
37
|
+
cursor: { default: 'text', ':disabled': 'not-allowed' },
|
|
38
|
+
'::placeholder': {
|
|
39
|
+
color: colors.textSubtle,
|
|
40
|
+
opacity: 1
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Phones and tablets type with an on-screen keyboard, where Return is for new lines
|
|
46
|
+
const TOUCH = '(hover: none) and (pointer: coarse)';
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<script lang="ts">
|
|
50
|
+
let {
|
|
51
|
+
rows = 1,
|
|
52
|
+
disabled,
|
|
53
|
+
sx,
|
|
54
|
+
class: className,
|
|
55
|
+
ref = $bindable(null),
|
|
56
|
+
onkeydown,
|
|
57
|
+
onpaste,
|
|
58
|
+
...rest
|
|
59
|
+
}: ComposerTextareaProps = $props();
|
|
60
|
+
|
|
61
|
+
const composer = useComposer('Textarea');
|
|
62
|
+
|
|
63
|
+
$effect(() => {
|
|
64
|
+
composer.setTextarea(ref);
|
|
65
|
+
return () => composer.setTextarea(null);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Browsers without `field-sizing` are sized by hand
|
|
69
|
+
$effect(() => {
|
|
70
|
+
void composer.value;
|
|
71
|
+
if (!ref || CSS.supports('field-sizing', 'content')) return;
|
|
72
|
+
ref.style.height = 'auto';
|
|
73
|
+
ref.style.height = `${ref.scrollHeight}px`;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
function handleKeydown(event: KeyboardEvent & { currentTarget: HTMLTextAreaElement }) {
|
|
77
|
+
onkeydown?.(event);
|
|
78
|
+
if (event.defaultPrevented || event.key !== 'Enter') return;
|
|
79
|
+
// Shift+Enter is a new line. So is Enter while an input method is composing a character.
|
|
80
|
+
if (event.shiftKey || event.isComposing || event.keyCode === 229) return;
|
|
81
|
+
// On touch keyboards Return types a new line, unless a modifier asks to send
|
|
82
|
+
if (!(event.metaKey || event.ctrlKey) && matchMedia(TOUCH).matches) return;
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
// Enter never stops a reply. That takes a deliberate press of the Stop button.
|
|
85
|
+
// While a reply is under way, it queues, if the app takes queued messages
|
|
86
|
+
if (composer.canSend) composer.submit();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// A pasted screenshot or file becomes an attachment. Pasted text is left to the browser.
|
|
90
|
+
function handlePaste(event: ClipboardEvent & { currentTarget: HTMLTextAreaElement }) {
|
|
91
|
+
onpaste?.(event);
|
|
92
|
+
const files = event.clipboardData?.files;
|
|
93
|
+
if (event.defaultPrevented || !composer.attachable || !files?.length) return;
|
|
94
|
+
if (!event.clipboardData?.getData('text/plain')) event.preventDefault();
|
|
95
|
+
composer.addFiles(files);
|
|
96
|
+
}
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<textarea
|
|
100
|
+
bind:this={ref}
|
|
101
|
+
bind:value={composer.value}
|
|
102
|
+
{rows}
|
|
103
|
+
disabled={disabled ?? composer.disabled}
|
|
104
|
+
enterkeyhint="send"
|
|
105
|
+
data-slot="composer-textarea"
|
|
106
|
+
{...rest}
|
|
107
|
+
{...attrs([styles.textarea, sx], className)}
|
|
108
|
+
onkeydown={handleKeydown}
|
|
109
|
+
onpaste={handlePaste}></textarea>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
|
2
|
+
import { type Sx } from '../../internal/sx.js';
|
|
3
|
+
export type ComposerTextareaProps = Omit<HTMLTextareaAttributes, 'value'> & {
|
|
4
|
+
sx?: Sx;
|
|
5
|
+
ref?: HTMLTextAreaElement | null;
|
|
6
|
+
};
|
|
7
|
+
declare const ComposerTextarea: import("svelte").Component<ComposerTextareaProps, {}, "ref">;
|
|
8
|
+
type ComposerTextarea = ReturnType<typeof ComposerTextarea>;
|
|
9
|
+
export default ComposerTextarea;
|