@agimon-ai/doompi-web-components 0.0.1-alpha.37 → 0.0.1-alpha.39
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/dist/index.cjs +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +1 -1
- package/dist/src/components/Accordion.cjs +1 -1
- package/dist/src/components/Accordion.cjs.map +1 -1
- package/dist/src/components/Accordion.mjs +1 -1
- package/dist/src/components/Accordion.mjs.map +1 -1
- package/dist/src/components/AnsiText.cjs +3 -3
- package/dist/src/components/AnsiText.cjs.map +1 -1
- package/dist/src/components/AnsiText.mjs +3 -3
- package/dist/src/components/AnsiText.mjs.map +1 -1
- package/dist/src/components/Avatar.cjs +1 -1
- package/dist/src/components/Avatar.cjs.map +1 -1
- package/dist/src/components/Avatar.mjs +1 -1
- package/dist/src/components/Avatar.mjs.map +1 -1
- package/dist/src/components/Badge.cjs +1 -1
- package/dist/src/components/Badge.cjs.map +1 -1
- package/dist/src/components/Badge.mjs +1 -1
- package/dist/src/components/Badge.mjs.map +1 -1
- package/dist/src/components/Breadcrumb.cjs +1 -1
- package/dist/src/components/Breadcrumb.cjs.map +1 -1
- package/dist/src/components/Breadcrumb.mjs +1 -1
- package/dist/src/components/Breadcrumb.mjs.map +1 -1
- package/dist/src/components/Button.cjs +1 -1
- package/dist/src/components/Button.cjs.map +1 -1
- package/dist/src/components/Button.mjs +1 -1
- package/dist/src/components/Button.mjs.map +1 -1
- package/dist/src/components/Checkbox.cjs +1 -1
- package/dist/src/components/Checkbox.cjs.map +1 -1
- package/dist/src/components/Checkbox.mjs +1 -1
- package/dist/src/components/Checkbox.mjs.map +1 -1
- package/dist/src/components/CodeBlock.cjs +1 -1
- package/dist/src/components/CodeBlock.cjs.map +1 -1
- package/dist/src/components/CodeBlock.mjs +1 -1
- package/dist/src/components/CodeBlock.mjs.map +1 -1
- package/dist/src/components/CodeEditor.cjs +1 -1
- package/dist/src/components/CodeEditor.cjs.map +1 -1
- package/dist/src/components/CodeEditor.mjs +1 -1
- package/dist/src/components/CodeEditor.mjs.map +1 -1
- package/dist/src/components/CodeEditorView.cjs +1 -1
- package/dist/src/components/CodeEditorView.cjs.map +1 -1
- package/dist/src/components/CodeEditorView.mjs +1 -1
- package/dist/src/components/CodeEditorView.mjs.map +1 -1
- package/dist/src/components/Collapsible.cjs +1 -1
- package/dist/src/components/Collapsible.cjs.map +1 -1
- package/dist/src/components/Collapsible.mjs +1 -1
- package/dist/src/components/Collapsible.mjs.map +1 -1
- package/dist/src/components/Command.cjs +1 -1
- package/dist/src/components/Command.cjs.map +1 -1
- package/dist/src/components/Command.mjs +1 -1
- package/dist/src/components/Command.mjs.map +1 -1
- package/dist/src/components/CopyButton.cjs +1 -1
- package/dist/src/components/CopyButton.cjs.map +1 -1
- package/dist/src/components/CopyButton.mjs +1 -1
- package/dist/src/components/CopyButton.mjs.map +1 -1
- package/dist/src/components/Dialog.cjs +1 -1
- package/dist/src/components/Dialog.cjs.map +1 -1
- package/dist/src/components/Dialog.mjs +1 -1
- package/dist/src/components/Dialog.mjs.map +1 -1
- package/dist/src/components/Dot.cjs +1 -1
- package/dist/src/components/Dot.cjs.map +1 -1
- package/dist/src/components/Dot.mjs +1 -1
- package/dist/src/components/Dot.mjs.map +1 -1
- package/dist/src/components/Drawer.cjs +2 -0
- package/dist/src/components/Drawer.cjs.map +1 -0
- package/dist/src/components/Drawer.d.mts +17 -0
- package/dist/src/components/Drawer.d.mts.map +1 -0
- package/dist/src/components/Drawer.mjs +2 -0
- package/dist/src/components/Drawer.mjs.map +1 -0
- package/dist/src/components/DropdownMenu.cjs +1 -1
- package/dist/src/components/DropdownMenu.cjs.map +1 -1
- package/dist/src/components/DropdownMenu.mjs +1 -1
- package/dist/src/components/DropdownMenu.mjs.map +1 -1
- package/dist/src/components/EmptyState.cjs +1 -1
- package/dist/src/components/EmptyState.cjs.map +1 -1
- package/dist/src/components/EmptyState.mjs +1 -1
- package/dist/src/components/EmptyState.mjs.map +1 -1
- package/dist/src/components/HashlineLines.cjs +1 -1
- package/dist/src/components/HashlineLines.cjs.map +1 -1
- package/dist/src/components/HashlineLines.mjs +1 -1
- package/dist/src/components/HashlineLines.mjs.map +1 -1
- package/dist/src/components/Input.cjs +1 -1
- package/dist/src/components/Input.cjs.map +1 -1
- package/dist/src/components/Input.mjs +1 -1
- package/dist/src/components/Input.mjs.map +1 -1
- package/dist/src/components/Kbd.cjs +1 -1
- package/dist/src/components/Kbd.cjs.map +1 -1
- package/dist/src/components/Kbd.mjs +1 -1
- package/dist/src/components/Kbd.mjs.map +1 -1
- package/dist/src/components/Label.cjs +1 -1
- package/dist/src/components/Label.cjs.map +1 -1
- package/dist/src/components/Label.mjs +1 -1
- package/dist/src/components/Label.mjs.map +1 -1
- package/dist/src/components/Markdown.cjs +1 -1
- package/dist/src/components/Markdown.cjs.map +1 -1
- package/dist/src/components/Markdown.mjs +1 -1
- package/dist/src/components/Markdown.mjs.map +1 -1
- package/dist/src/components/MediaPreview.cjs +1 -1
- package/dist/src/components/MediaPreview.cjs.map +1 -1
- package/dist/src/components/MediaPreview.mjs +1 -1
- package/dist/src/components/MediaPreview.mjs.map +1 -1
- package/dist/src/components/MermaidDiagram.cjs +1 -1
- package/dist/src/components/MermaidDiagram.cjs.map +1 -1
- package/dist/src/components/MermaidDiagram.mjs +1 -1
- package/dist/src/components/MermaidDiagram.mjs.map +1 -1
- package/dist/src/components/MessageItem.cjs +1 -1
- package/dist/src/components/MessageItem.cjs.map +1 -1
- package/dist/src/components/MessageItem.mjs +1 -1
- package/dist/src/components/MessageItem.mjs.map +1 -1
- package/dist/src/components/MessageLines.cjs +1 -1
- package/dist/src/components/MessageLines.cjs.map +1 -1
- package/dist/src/components/MessageLines.mjs +1 -1
- package/dist/src/components/MessageLines.mjs.map +1 -1
- package/dist/src/components/OptionList.cjs +1 -1
- package/dist/src/components/OptionList.cjs.map +1 -1
- package/dist/src/components/OptionList.mjs +1 -1
- package/dist/src/components/OptionList.mjs.map +1 -1
- package/dist/src/components/Panel.cjs +1 -1
- package/dist/src/components/Panel.cjs.map +1 -1
- package/dist/src/components/Panel.mjs +1 -1
- package/dist/src/components/Panel.mjs.map +1 -1
- package/dist/src/components/PdfPreview.cjs +1 -1
- package/dist/src/components/PdfPreview.cjs.map +1 -1
- package/dist/src/components/PdfPreview.mjs +1 -1
- package/dist/src/components/PdfPreview.mjs.map +1 -1
- package/dist/src/components/Popover.cjs +1 -1
- package/dist/src/components/Popover.cjs.map +1 -1
- package/dist/src/components/Popover.mjs +1 -1
- package/dist/src/components/Popover.mjs.map +1 -1
- package/dist/src/components/Progress.cjs +1 -1
- package/dist/src/components/Progress.cjs.map +1 -1
- package/dist/src/components/Progress.mjs +1 -1
- package/dist/src/components/Progress.mjs.map +1 -1
- package/dist/src/components/RadioGroup.cjs +1 -1
- package/dist/src/components/RadioGroup.cjs.map +1 -1
- package/dist/src/components/RadioGroup.mjs +1 -1
- package/dist/src/components/RadioGroup.mjs.map +1 -1
- package/dist/src/components/ScrollArea.cjs +1 -1
- package/dist/src/components/ScrollArea.cjs.map +1 -1
- package/dist/src/components/ScrollArea.mjs +1 -1
- package/dist/src/components/ScrollArea.mjs.map +1 -1
- package/dist/src/components/SectionLabel.cjs +1 -1
- package/dist/src/components/SectionLabel.cjs.map +1 -1
- package/dist/src/components/SectionLabel.mjs +1 -1
- package/dist/src/components/SectionLabel.mjs.map +1 -1
- package/dist/src/components/Select.cjs +1 -1
- package/dist/src/components/Select.cjs.map +1 -1
- package/dist/src/components/Select.mjs +1 -1
- package/dist/src/components/Select.mjs.map +1 -1
- package/dist/src/components/Separator.cjs +1 -1
- package/dist/src/components/Separator.cjs.map +1 -1
- package/dist/src/components/Separator.mjs +1 -1
- package/dist/src/components/Separator.mjs.map +1 -1
- package/dist/src/components/Sheet.cjs +1 -1
- package/dist/src/components/Sheet.cjs.map +1 -1
- package/dist/src/components/Sheet.mjs +1 -1
- package/dist/src/components/Sheet.mjs.map +1 -1
- package/dist/src/components/Skeleton.cjs +1 -1
- package/dist/src/components/Skeleton.cjs.map +1 -1
- package/dist/src/components/Skeleton.mjs +1 -1
- package/dist/src/components/Skeleton.mjs.map +1 -1
- package/dist/src/components/Spinner.cjs +1 -1
- package/dist/src/components/Spinner.cjs.map +1 -1
- package/dist/src/components/Spinner.mjs +1 -1
- package/dist/src/components/Spinner.mjs.map +1 -1
- package/dist/src/components/StatusBadge.cjs +1 -1
- package/dist/src/components/StatusBadge.cjs.map +1 -1
- package/dist/src/components/StatusBadge.mjs +1 -1
- package/dist/src/components/StatusBadge.mjs.map +1 -1
- package/dist/src/components/StreamCursor.cjs +1 -1
- package/dist/src/components/StreamCursor.cjs.map +1 -1
- package/dist/src/components/StreamCursor.mjs +1 -1
- package/dist/src/components/StreamCursor.mjs.map +1 -1
- package/dist/src/components/Switch.cjs +1 -1
- package/dist/src/components/Switch.cjs.map +1 -1
- package/dist/src/components/Switch.mjs +1 -1
- package/dist/src/components/Switch.mjs.map +1 -1
- package/dist/src/components/SyntaxText.cjs +3 -3
- package/dist/src/components/SyntaxText.cjs.map +1 -1
- package/dist/src/components/SyntaxText.mjs +3 -3
- package/dist/src/components/SyntaxText.mjs.map +1 -1
- package/dist/src/components/Tabs.cjs +1 -1
- package/dist/src/components/Tabs.cjs.map +1 -1
- package/dist/src/components/Tabs.mjs +1 -1
- package/dist/src/components/Tabs.mjs.map +1 -1
- package/dist/src/components/TerminalView.cjs +1 -1
- package/dist/src/components/TerminalView.cjs.map +1 -1
- package/dist/src/components/TerminalView.mjs +1 -1
- package/dist/src/components/TerminalView.mjs.map +1 -1
- package/dist/src/components/Textarea.cjs +1 -1
- package/dist/src/components/Textarea.cjs.map +1 -1
- package/dist/src/components/Textarea.mjs +1 -1
- package/dist/src/components/Textarea.mjs.map +1 -1
- package/dist/src/components/Toast.cjs +1 -1
- package/dist/src/components/Toast.cjs.map +1 -1
- package/dist/src/components/Toast.mjs +1 -1
- package/dist/src/components/Toast.mjs.map +1 -1
- package/dist/src/components/ToolPathLink.cjs +1 -1
- package/dist/src/components/ToolPathLink.cjs.map +1 -1
- package/dist/src/components/ToolPathLink.mjs +1 -1
- package/dist/src/components/ToolPathLink.mjs.map +1 -1
- package/dist/src/components/Tooltip.cjs +1 -1
- package/dist/src/components/Tooltip.cjs.map +1 -1
- package/dist/src/components/Tooltip.mjs +1 -1
- package/dist/src/components/Tooltip.mjs.map +1 -1
- package/package.json +11 -11
- package/dist/components/Accordion.d.cts +0 -11
- package/dist/components/Accordion.d.cts.map +0 -1
- package/dist/components/AnsiText.d.cts +0 -16
- package/dist/components/AnsiText.d.cts.map +0 -1
- package/dist/components/Avatar.d.cts +0 -10
- package/dist/components/Avatar.d.cts.map +0 -1
- package/dist/components/Badge.d.cts +0 -17
- package/dist/components/Badge.d.cts.map +0 -1
- package/dist/components/Breadcrumb.d.cts +0 -36
- package/dist/components/Breadcrumb.d.cts.map +0 -1
- package/dist/components/Button.d.cts +0 -24
- package/dist/components/Button.d.cts.map +0 -1
- package/dist/components/Checkbox.d.cts +0 -8
- package/dist/components/Checkbox.d.cts.map +0 -1
- package/dist/components/CodeEditor.d.cts +0 -5
- package/dist/components/CodeEditor.d.cts.map +0 -1
- package/dist/components/Collapsible.d.cts +0 -10
- package/dist/components/Collapsible.d.cts.map +0 -1
- package/dist/components/Command.d.cts +0 -41
- package/dist/components/Command.d.cts.map +0 -1
- package/dist/components/Dialog.d.cts +0 -39
- package/dist/components/Dialog.d.cts.map +0 -1
- package/dist/components/Dot.d.cts +0 -15
- package/dist/components/Dot.d.cts.map +0 -1
- package/dist/components/DropdownMenu.d.cts +0 -34
- package/dist/components/DropdownMenu.d.cts.map +0 -1
- package/dist/components/EmptyState.d.cts +0 -17
- package/dist/components/EmptyState.d.cts.map +0 -1
- package/dist/components/HashlineLines.d.cts +0 -23
- package/dist/components/HashlineLines.d.cts.map +0 -1
- package/dist/components/Input.d.cts +0 -19
- package/dist/components/Input.d.cts.map +0 -1
- package/dist/components/Kbd.d.cts +0 -6
- package/dist/components/Kbd.d.cts.map +0 -1
- package/dist/components/Label.d.cts +0 -8
- package/dist/components/Label.d.cts.map +0 -1
- package/dist/components/Markdown.d.cts +0 -17
- package/dist/components/Markdown.d.cts.map +0 -1
- package/dist/components/MediaPreview.d.cts +0 -61
- package/dist/components/MediaPreview.d.cts.map +0 -1
- package/dist/components/MessageItem.d.cts +0 -102
- package/dist/components/MessageItem.d.cts.map +0 -1
- package/dist/components/MessageLines.d.cts +0 -23
- package/dist/components/MessageLines.d.cts.map +0 -1
- package/dist/components/OptionList.d.cts +0 -49
- package/dist/components/OptionList.d.cts.map +0 -1
- package/dist/components/Panel.d.cts +0 -14
- package/dist/components/Panel.d.cts.map +0 -1
- package/dist/components/PdfPreview.d.cts +0 -52
- package/dist/components/PdfPreview.d.cts.map +0 -1
- package/dist/components/Popover.d.cts +0 -19
- package/dist/components/Popover.d.cts.map +0 -1
- package/dist/components/Progress.d.cts +0 -8
- package/dist/components/Progress.d.cts.map +0 -1
- package/dist/components/RadioGroup.d.cts +0 -15
- package/dist/components/RadioGroup.d.cts.map +0 -1
- package/dist/components/ScrollArea.d.cts +0 -13
- package/dist/components/ScrollArea.d.cts.map +0 -1
- package/dist/components/SectionLabel.d.cts +0 -6
- package/dist/components/SectionLabel.d.cts.map +0 -1
- package/dist/components/Select.d.cts +0 -19
- package/dist/components/Select.d.cts.map +0 -1
- package/dist/components/Separator.d.cts +0 -13
- package/dist/components/Separator.d.cts.map +0 -1
- package/dist/components/Sheet.d.cts +0 -35
- package/dist/components/Sheet.d.cts.map +0 -1
- package/dist/components/Skeleton.d.cts +0 -10
- package/dist/components/Skeleton.d.cts.map +0 -1
- package/dist/components/Spinner.d.cts +0 -14
- package/dist/components/Spinner.d.cts.map +0 -1
- package/dist/components/StatusBadge.d.cts +0 -23
- package/dist/components/StatusBadge.d.cts.map +0 -1
- package/dist/components/StreamCursor.d.cts +0 -6
- package/dist/components/StreamCursor.d.cts.map +0 -1
- package/dist/components/Switch.d.cts +0 -8
- package/dist/components/Switch.d.cts.map +0 -1
- package/dist/components/SyntaxText.d.cts +0 -50
- package/dist/components/SyntaxText.d.cts.map +0 -1
- package/dist/components/Tabs.d.cts +0 -36
- package/dist/components/Tabs.d.cts.map +0 -1
- package/dist/components/TerminalView.d.cts +0 -31
- package/dist/components/TerminalView.d.cts.map +0 -1
- package/dist/components/Textarea.d.cts +0 -9
- package/dist/components/Textarea.d.cts.map +0 -1
- package/dist/components/Toast.d.cts +0 -24
- package/dist/components/Toast.d.cts.map +0 -1
- package/dist/components/ToolPathLink.d.cts +0 -22
- package/dist/components/ToolPathLink.d.cts.map +0 -1
- package/dist/components/Tooltip.d.cts +0 -11
- package/dist/components/Tooltip.d.cts.map +0 -1
- package/dist/icons/icons.d.cts +0 -2
- package/dist/index.d.cts +0 -59
- package/dist/lib/ansiSpans.d.cts +0 -42
- package/dist/lib/ansiSpans.d.cts.map +0 -1
- package/dist/lib/cn.d.cts +0 -6
- package/dist/lib/cn.d.cts.map +0 -1
- package/dist/lib/collapse.d.cts +0 -14
- package/dist/lib/collapse.d.cts.map +0 -1
- package/dist/lib/editorLanguage.d.cts +0 -21
- package/dist/lib/editorLanguage.d.cts.map +0 -1
- package/dist/lib/editorTheme.d.cts +0 -76
- package/dist/lib/editorTheme.d.cts.map +0 -1
- package/dist/lib/hashlineHighlight.d.cts +0 -30
- package/dist/lib/hashlineHighlight.d.cts.map +0 -1
- package/dist/lib/hashlineView.d.cts +0 -60
- package/dist/lib/hashlineView.d.cts.map +0 -1
- package/dist/lib/media.d.cts +0 -11
- package/dist/lib/media.d.cts.map +0 -1
- package/dist/lib/optionList.d.cts +0 -21
- package/dist/lib/optionList.d.cts.map +0 -1
- package/dist/lib/syntaxHighlight.d.cts +0 -54
- package/dist/lib/syntaxHighlight.d.cts.map +0 -1
- package/dist/lib/tone.d.cts +0 -25
- package/dist/lib/tone.d.cts.map +0 -1
- package/dist/theme/apply.d.cts +0 -25
- package/dist/theme/apply.d.cts.map +0 -1
- package/dist/theme/builtinThemes.d.cts +0 -11
- package/dist/theme/builtinThemes.d.cts.map +0 -1
- package/dist/theme/derive.d.cts +0 -14
- package/dist/theme/derive.d.cts.map +0 -1
- package/dist/theme/parse.d.cts +0 -10
- package/dist/theme/parse.d.cts.map +0 -1
- package/dist/theme/piTheme.d.cts +0 -19
- package/dist/theme/piTheme.d.cts.map +0 -1
- package/dist/theme/preference.d.cts +0 -15
- package/dist/theme/preference.d.cts.map +0 -1
- package/dist/theme.d.cts +0 -8
- package/dist/types/editor.d.cts +0 -78
- package/dist/types/editor.d.cts.map +0 -1
- package/dist/types/theme.d.cts +0 -38
- package/dist/types/theme.d.cts.map +0 -1
- package/dist/types/tone.d.cts +0 -28
- package/dist/types/tone.d.cts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageItem.cjs","names":["createContext","useContext","cva","STATUS_EDGE","useState","cn","StatusBadge","Button","ChevronUpIcon","ChevronDownIcon"],"sources":["../../../src/components/MessageItem.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, createContext, type ReactNode, useContext, useState } from 'react';\n\nimport { ChevronDownIcon, ChevronUpIcon } from '../icons/icons';\nimport { cn } from '../lib/cn';\nimport { Button } from './Button';\nimport { STATUS_EDGE, StatusBadge, type StatusTone } from './StatusBadge';\n\n/**\n * The message item: the timeline card a tool call, a run, or a notice sits\n * in. The item owns its outcome tone and its expand state; the header, body,\n * and status parts read both from it, so a card composes the parts and never\n * threads `expanded` by hand. A tool message in a web plugin is built from\n * this, and so is the cockpit's own fallback item, which is what keeps them\n * looking alike.\n */\n\nexport interface MessageItemState {\n tone: StatusTone;\n /** Whether the header offers the toggle; an item that hides nothing offers none. */\n expandable: boolean;\n expanded: boolean;\n toggle: () => void;\n}\n\nconst MessageItemContext = createContext<MessageItemState>({\n tone: 'neutral',\n expandable: false,\n expanded: false,\n toggle: () => undefined,\n});\n\n/** The enclosing item's state, for a part that lives deeper than the item's render prop. */\nexport function useMessageItem(): MessageItemState {\n return useContext(MessageItemContext);\n}\n\n/**\n * The group an item is a row of, or null for an item that is a card of its\n * own. A row drops the frame the group already draws and keeps its tone as an\n * edge down its left side, and it stops repeating what the group's header has\n * said once: the tool's name, and an outcome the whole run shares.\n */\nconst MessageItemGroupContext = createContext<{ tone: StatusTone } | null>(null);\n\n/** The frame, wearing the edge STATUS_EDGE names for the tone, so the two never drift apart. */\nexport const messageItemVariants = cva('overflow-hidden rounded-md border bg-doom-panel transition-colors', {\n variants: { tone: STATUS_EDGE },\n defaultVariants: { tone: 'neutral' },\n});\n\n/** The same tone as a row: no card, one coloured edge, and the group's separators between rows. */\nexport const messageItemRowVariants = cva('overflow-hidden border-l-2 transition-colors', {\n variants: { tone: STATUS_EDGE },\n defaultVariants: { tone: 'neutral' },\n});\n\nexport interface MessageItemProps\n extends Omit<ComponentProps<'div'>, 'children'>, VariantProps<typeof messageItemVariants> {\n /** Whether the header offers the expand toggle. A toggle that reveals nothing reads as broken, so the card decides. */\n expandable?: boolean;\n defaultExpanded?: boolean;\n /** Controlled mode; pair with onExpandedChange. Omit it and the item keeps its own state. */\n expanded?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n /** Plain content, or a function of the item's state for a card that picks its lines by `expanded`. */\n children?: ReactNode | ((state: MessageItemState) => ReactNode);\n}\n\nexport function MessageItem({\n className,\n tone,\n expandable = false,\n defaultExpanded = false,\n expanded,\n onExpandedChange,\n children,\n ...props\n}: MessageItemProps) {\n const [own, setOwn] = useState(defaultExpanded);\n const group = useContext(MessageItemGroupContext);\n const grouped = group !== null;\n const current = expanded ?? own;\n const state: MessageItemState = {\n tone: tone ?? 'neutral',\n expandable,\n expanded: current,\n toggle: () => {\n const next = !current;\n if (expanded === undefined) setOwn(next);\n onExpandedChange?.(next);\n },\n };\n return (\n <MessageItemContext.Provider value={state}>\n <div\n data-slot=\"message-item\"\n data-expanded={current}\n data-grouped={grouped}\n className={cn(grouped ? messageItemRowVariants({ tone }) : messageItemVariants({ tone }), className)}\n {...props}\n >\n {typeof children === 'function' ? children(state) : children}\n </div>\n </MessageItemContext.Provider>\n );\n}\n\nexport interface MessageItemGroupProps\n extends Omit<ComponentProps<'div'>, 'title'>, VariantProps<typeof messageItemVariants> {\n /** The bold label at the left of the group header: the tool the run belongs to. */\n title: ReactNode;\n /** The line beside it, such as how many calls the group holds. */\n summary?: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n children?: ReactNode;\n}\n\n/**\n * One frame around a run of items that belong together.\n *\n * A transcript that calls the same tool five times in a row is five identical\n * frames, five gutter labels and five gaps, and the repetition reads louder\n * than the commands do. The group draws the frame once and each item inside it\n * becomes a row: same header, same expand, no card of its own. The tone still\n * belongs to the row, as an edge, so a failure in the middle of a run is not\n * flattened into the group's own outcome.\n */\nexport function MessageItemGroup({\n className,\n tone,\n title,\n summary,\n badge,\n children,\n ...props\n}: MessageItemGroupProps) {\n const label = badge === undefined ? STATUS_LABEL[tone ?? 'neutral'] : badge;\n return (\n <MessageItemGroupContext.Provider value={{ tone: tone ?? 'neutral' }}>\n <div data-slot=\"message-item-group\" className={cn(messageItemVariants({ tone }), className)} {...props}>\n <div className=\"flex min-h-8 items-center gap-2 border-doom-border-soft border-b px-[11px] text-sm text-doom-dim\">\n <span className=\"shrink-0 font-bold text-doom-hi\">{title}</span>\n <span className=\"min-w-0 flex-1 truncate text-doom-faint\">{summary}</span>\n {label !== null && label !== '' ? <StatusBadge tone={tone ?? 'neutral'}>{label}</StatusBadge> : null}\n </div>\n <div className=\"flex flex-col divide-y divide-doom-border-soft\">{children}</div>\n </div>\n </MessageItemGroupContext.Provider>\n );\n}\n\n/** The tone a tool call's item wears: running while the tool runs, then its outcome. */\nexport function toolTone(state: { running: boolean; isError: boolean }): StatusTone {\n return state.running ? 'running' : state.isError ? 'error' : 'ok';\n}\n\n/** The badge text a tone reads as when the header is not told otherwise; empty hides the badge. */\nexport const STATUS_LABEL: Readonly<Record<StatusTone, string>> = {\n neutral: '',\n running: 'RUNNING',\n ok: 'OK',\n error: 'ERROR',\n info: 'INFO',\n accent: 'NOTE',\n};\n\nexport interface MessageItemHeaderProps extends Omit<ComponentProps<'div'>, 'title'> {\n /** The bold label at the left: the tool name. */\n title: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n /** The summary beside the title; it gets the remaining width. */\n children?: ReactNode;\n}\n\n/** Inside a group these two are the quiet outcome: the group's header has already said it. */\nconst SILENT_IN_GROUP: ReadonlySet<StatusTone> = new Set<StatusTone>(['ok', 'neutral']);\n\n/**\n * The title row: label, summary, the tone's badge, and the expand toggle when\n * the item is expandable.\n *\n * A row inside a group says less. The group's header has already named the\n * tool and stated the run's outcome, so the row drops its title and speaks up\n * only when something went wrong or is still going: five OK badges down a run\n * of five are noise, and the one ERROR among them is the point.\n */\nexport function MessageItemHeader({ className, title, badge, children, ...props }: MessageItemHeaderProps) {\n const { tone, expandable, expanded, toggle } = useMessageItem();\n const group = useContext(MessageItemGroupContext);\n const defaultLabel = group !== null && SILENT_IN_GROUP.has(tone) ? null : STATUS_LABEL[tone];\n const label = badge === undefined ? defaultLabel : badge;\n return (\n <div\n data-slot=\"message-item-header\"\n className={cn('flex min-h-8 items-center gap-2 px-[11px] text-sm text-doom-dim', className)}\n {...props}\n >\n {group === null ? <span className=\"shrink-0 font-bold text-doom-hi\">{title}</span> : null}\n <div data-slot=\"message-item-summary\" className=\"flex min-w-0 flex-1 items-center gap-2\">\n {children}\n </div>\n {label !== null && label !== '' ? (\n <StatusBadge tone={tone} data-testid=\"tool-status\">\n {label}\n </StatusBadge>\n ) : null}\n {expandable ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid=\"tool-expand\"\n aria-expanded={expanded}\n aria-label={expanded ? 'collapse' : 'expand'}\n onClick={toggle}\n className=\"text-doom-faint hover:text-doom-blue\"\n >\n {expanded ? <ChevronUpIcon className=\"h-3 w-3\" /> : <ChevronDownIcon className=\"h-3 w-3\" />}\n </Button>\n ) : null}\n </div>\n );\n}\n\n/** The recessed body under the header. */\nexport function MessageItemBody({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"message-item-body\"\n className={cn('border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim', className)}\n {...props}\n />\n );\n}\n\n/** The glyph a tone reads as in a status line. */\nexport const STATUS_GLYPH: Readonly<Record<StatusTone, string>> = {\n neutral: '…',\n running: '◐',\n ok: '✓',\n error: '✗',\n info: '●',\n accent: '●',\n};\n\nexport const messageItemStatusVariants = cva('shrink-0', {\n variants: {\n tone: {\n neutral: 'text-doom-faint',\n running: 'text-doom-yellow',\n ok: 'text-doom-green',\n error: 'text-doom-red',\n info: 'text-doom-blue',\n accent: 'text-doom-magenta',\n } satisfies Record<StatusTone, string>,\n },\n defaultVariants: { tone: 'neutral' },\n});\n\nexport interface MessageItemStatusProps extends ComponentProps<'span'>, VariantProps<typeof messageItemStatusVariants> {\n /** Replaces the tone's glyph. */\n glyph?: string;\n /** Renders as a button that expands the item: the \"N more line(s)\" hint. Only className carries over. */\n expands?: boolean;\n}\n\n/** The one-line status under a body: `◐ running`, `✗ failed`, `✓ done`, or the hint that more is hidden. */\nexport function MessageItemStatus({\n className,\n tone,\n glyph,\n expands = false,\n children,\n ...props\n}: MessageItemStatusProps) {\n const { toggle } = useMessageItem();\n const content = (\n <>\n <span className={messageItemStatusVariants({ tone })}>{glyph ?? STATUS_GLYPH[tone ?? 'neutral']}</span>\n <span>{children}</span>\n </>\n );\n if (expands) {\n return (\n <button\n type=\"button\"\n data-slot=\"message-item-status\"\n data-testid=\"tool-more\"\n onClick={toggle}\n className={cn('flex cursor-pointer items-center gap-1.5 text-doom-faint hover:text-doom-blue', className)}\n >\n {content}\n </button>\n );\n }\n return (\n <span\n data-slot=\"message-item-status\"\n className={cn('flex items-center gap-1.5 text-doom-faint', className)}\n {...props}\n >\n {content}\n </span>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MessageItem.cjs","names":["createContext","useContext","cva","STATUS_EDGE","useState","cn","StatusBadge","Button","ChevronUpIcon","ChevronDownIcon"],"sources":["../../../src/components/MessageItem.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, createContext, type ReactNode, useContext, useState } from 'react';\n\nimport { ChevronDownIcon, ChevronUpIcon } from '../icons/icons';\nimport { cn } from '../lib/cn';\nimport { Button } from './Button';\nimport { STATUS_EDGE, StatusBadge, type StatusTone } from './StatusBadge';\n\n/**\n * The message item: the timeline card a tool call, a run, or a notice sits\n * in. The item owns its outcome tone and its expand state; the header, body,\n * and status parts read both from it, so a card composes the parts and never\n * threads `expanded` by hand. A tool message in a web plugin is built from\n * this, and so is the cockpit's own fallback item, which is what keeps them\n * looking alike.\n */\n\nexport interface MessageItemState {\n tone: StatusTone;\n /** Whether the header offers the toggle; an item that hides nothing offers none. */\n expandable: boolean;\n expanded: boolean;\n toggle: () => void;\n}\n\nconst MessageItemContext = createContext<MessageItemState>({\n tone: 'neutral',\n expandable: false,\n expanded: false,\n toggle: () => undefined,\n});\n\n/** The enclosing item's state, for a part that lives deeper than the item's render prop. */\nexport function useMessageItem(): MessageItemState {\n return useContext(MessageItemContext);\n}\n\n/**\n * The group an item is a row of, or null for an item that is a card of its\n * own. A row drops the frame the group already draws and keeps its tone as an\n * edge down its left side, and it stops repeating what the group's header has\n * said once: the tool's name, and an outcome the whole run shares.\n */\nconst MessageItemGroupContext = createContext<{ tone: StatusTone } | null>(null);\n\n/** The frame, wearing the edge STATUS_EDGE names for the tone, so the two never drift apart. */\nexport const messageItemVariants = cva('overflow-hidden rounded-md border bg-doom-panel transition-colors', {\n variants: { tone: STATUS_EDGE },\n defaultVariants: { tone: 'neutral' },\n});\n\n/** The same tone as a row: no card, one coloured edge, and the group's separators between rows. */\nexport const messageItemRowVariants = cva('overflow-hidden border-l-2 transition-colors', {\n variants: { tone: STATUS_EDGE },\n defaultVariants: { tone: 'neutral' },\n});\n\nexport interface MessageItemProps\n extends Omit<ComponentProps<'div'>, 'children'>, VariantProps<typeof messageItemVariants> {\n /** Whether the header offers the expand toggle. A toggle that reveals nothing reads as broken, so the card decides. */\n expandable?: boolean;\n defaultExpanded?: boolean;\n /** Controlled mode; pair with onExpandedChange. Omit it and the item keeps its own state. */\n expanded?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n /** Plain content, or a function of the item's state for a card that picks its lines by `expanded`. */\n children?: ReactNode | ((state: MessageItemState) => ReactNode);\n}\n\nexport function MessageItem({\n className,\n tone,\n expandable = false,\n defaultExpanded = false,\n expanded,\n onExpandedChange,\n children,\n ...props\n}: MessageItemProps) {\n const [own, setOwn] = useState(defaultExpanded);\n const group = useContext(MessageItemGroupContext);\n const grouped = group !== null;\n const current = expanded ?? own;\n const state: MessageItemState = {\n tone: tone ?? 'neutral',\n expandable,\n expanded: current,\n toggle: () => {\n const next = !current;\n if (expanded === undefined) setOwn(next);\n onExpandedChange?.(next);\n },\n };\n return (\n <MessageItemContext.Provider value={state}>\n <div\n data-slot=\"message-item\"\n data-expanded={current}\n data-grouped={grouped}\n className={cn(grouped ? messageItemRowVariants({ tone }) : messageItemVariants({ tone }), className)}\n {...props}\n >\n {typeof children === 'function' ? children(state) : children}\n </div>\n </MessageItemContext.Provider>\n );\n}\n\nexport interface MessageItemGroupProps\n extends Omit<ComponentProps<'div'>, 'title'>, VariantProps<typeof messageItemVariants> {\n /** The bold label at the left of the group header: the tool the run belongs to. */\n title: ReactNode;\n /** The line beside it, such as how many calls the group holds. */\n summary?: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n children?: ReactNode;\n}\n\n/**\n * One frame around a run of items that belong together.\n *\n * A transcript that calls the same tool five times in a row is five identical\n * frames, five gutter labels and five gaps, and the repetition reads louder\n * than the commands do. The group draws the frame once and each item inside it\n * becomes a row: same header, same expand, no card of its own. The tone still\n * belongs to the row, as an edge, so a failure in the middle of a run is not\n * flattened into the group's own outcome.\n */\nexport function MessageItemGroup({\n className,\n tone,\n title,\n summary,\n badge,\n children,\n ...props\n}: MessageItemGroupProps) {\n const label = badge === undefined ? STATUS_LABEL[tone ?? 'neutral'] : badge;\n return (\n <MessageItemGroupContext.Provider value={{ tone: tone ?? 'neutral' }}>\n <div data-slot=\"message-item-group\" className={cn(messageItemVariants({ tone }), className)} {...props}>\n <div className=\"flex min-h-8 items-center gap-2 border-doom-border-soft border-b px-[11px] text-sm text-doom-dim\">\n <span className=\"shrink-0 font-bold text-doom-hi\">{title}</span>\n <span className=\"min-w-0 flex-1 truncate text-doom-faint\">{summary}</span>\n {label !== null && label !== '' ? <StatusBadge tone={tone ?? 'neutral'}>{label}</StatusBadge> : null}\n </div>\n <div className=\"flex flex-col divide-y divide-doom-border-soft\">{children}</div>\n </div>\n </MessageItemGroupContext.Provider>\n );\n}\n\n/** The tone a tool call's item wears: running while the tool runs, then its outcome. */\nexport function toolTone(state: { running: boolean; isError: boolean }): StatusTone {\n return state.running ? 'running' : state.isError ? 'error' : 'ok';\n}\n\n/** The badge text a tone reads as when the header is not told otherwise; empty hides the badge. */\nexport const STATUS_LABEL: Readonly<Record<StatusTone, string>> = {\n neutral: '',\n running: 'RUNNING',\n ok: 'OK',\n error: 'ERROR',\n info: 'INFO',\n accent: 'NOTE',\n};\n\nexport interface MessageItemHeaderProps extends Omit<ComponentProps<'div'>, 'title'> {\n /** The bold label at the left: the tool name. */\n title: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n /** The summary beside the title; it gets the remaining width. */\n children?: ReactNode;\n}\n\n/** Inside a group these two are the quiet outcome: the group's header has already said it. */\nconst SILENT_IN_GROUP: ReadonlySet<StatusTone> = new Set<StatusTone>(['ok', 'neutral']);\n\n/**\n * The title row: label, summary, the tone's badge, and the expand toggle when\n * the item is expandable.\n *\n * A row inside a group says less. The group's header has already named the\n * tool and stated the run's outcome, so the row drops its title and speaks up\n * only when something went wrong or is still going: five OK badges down a run\n * of five are noise, and the one ERROR among them is the point.\n */\nexport function MessageItemHeader({ className, title, badge, children, ...props }: MessageItemHeaderProps) {\n const { tone, expandable, expanded, toggle } = useMessageItem();\n const group = useContext(MessageItemGroupContext);\n const defaultLabel = group !== null && SILENT_IN_GROUP.has(tone) ? null : STATUS_LABEL[tone];\n const label = badge === undefined ? defaultLabel : badge;\n return (\n <div\n data-slot=\"message-item-header\"\n className={cn('flex min-h-8 items-center gap-2 px-[11px] text-sm text-doom-dim', className)}\n {...props}\n >\n {group === null ? <span className=\"shrink-0 font-bold text-doom-hi\">{title}</span> : null}\n <div data-slot=\"message-item-summary\" className=\"flex min-w-0 flex-1 items-center gap-2\">\n {children}\n </div>\n {label !== null && label !== '' ? (\n <StatusBadge tone={tone} data-testid=\"tool-status\">\n {label}\n </StatusBadge>\n ) : null}\n {expandable ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid=\"tool-expand\"\n aria-expanded={expanded}\n aria-label={expanded ? 'collapse' : 'expand'}\n onClick={toggle}\n className=\"text-doom-faint hover:text-doom-blue\"\n >\n {expanded ? <ChevronUpIcon className=\"h-3 w-3\" /> : <ChevronDownIcon className=\"h-3 w-3\" />}\n </Button>\n ) : null}\n </div>\n );\n}\n\n/** The recessed body under the header. */\nexport function MessageItemBody({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"message-item-body\"\n className={cn('border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim', className)}\n {...props}\n />\n );\n}\n\n/** The glyph a tone reads as in a status line. */\nexport const STATUS_GLYPH: Readonly<Record<StatusTone, string>> = {\n neutral: '…',\n running: '◐',\n ok: '✓',\n error: '✗',\n info: '●',\n accent: '●',\n};\n\nexport const messageItemStatusVariants = cva('shrink-0', {\n variants: {\n tone: {\n neutral: 'text-doom-faint',\n running: 'text-doom-yellow',\n ok: 'text-doom-green',\n error: 'text-doom-red',\n info: 'text-doom-blue',\n accent: 'text-doom-magenta',\n } satisfies Record<StatusTone, string>,\n },\n defaultVariants: { tone: 'neutral' },\n});\n\nexport interface MessageItemStatusProps extends ComponentProps<'span'>, VariantProps<typeof messageItemStatusVariants> {\n /** Replaces the tone's glyph. */\n glyph?: string;\n /** Renders as a button that expands the item: the \"N more line(s)\" hint. Only className carries over. */\n expands?: boolean;\n}\n\n/** The one-line status under a body: `◐ running`, `✗ failed`, `✓ done`, or the hint that more is hidden. */\nexport function MessageItemStatus({\n className,\n tone,\n glyph,\n expands = false,\n children,\n ...props\n}: MessageItemStatusProps) {\n const { toggle } = useMessageItem();\n const content = (\n <>\n <span className={messageItemStatusVariants({ tone })}>{glyph ?? STATUS_GLYPH[tone ?? 'neutral']}</span>\n <span>{children}</span>\n </>\n );\n if (expands) {\n return (\n <button\n type=\"button\"\n data-slot=\"message-item-status\"\n data-testid=\"tool-more\"\n onClick={toggle}\n className={cn('flex cursor-pointer items-center gap-1.5 text-doom-faint hover:text-doom-blue', className)}\n >\n {content}\n </button>\n );\n }\n return (\n <span\n data-slot=\"message-item-status\"\n className={cn('flex items-center gap-1.5 text-doom-faint', className)}\n {...props}\n >\n {content}\n </span>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ChevronDownIcon as e,ChevronUpIcon as t}from"../icons/icons.mjs";import{cn as n}from"../lib/cn.mjs";import{Button as r}from"./Button.mjs";import{STATUS_EDGE as i,StatusBadge as a}from"./StatusBadge.mjs";import{Fragment as o,jsx as s,jsxs as c}from"react/jsx-runtime";import{createContext as l,useContext as u,useState as d}from"react";import{cva as f}from"class-variance-authority";const p=l({tone:`neutral`,expandable:!1,expanded:!1,toggle:()=>void 0});function m(){return u(p)}const h=l(null),g=f(`overflow-hidden rounded-md border bg-doom-panel transition-colors`,{variants:{tone:i},defaultVariants:{tone:`neutral`}}),_=f(`overflow-hidden border-l-2 transition-colors`,{variants:{tone:i},defaultVariants:{tone:`neutral`}});function v({className:e,tone:t,expandable:r=!1,defaultExpanded:i=!1,expanded:a,onExpandedChange:o,children:c,...l}){let[f,m]=d(i),v=u(h)!==null,y=a??f,b={tone:t??`neutral`,expandable:r,expanded:y,toggle:()=>{let e=!y;a===void 0&&m(e),o?.(e)}};return s(p.Provider,{value:b,children
|
|
1
|
+
import{ChevronDownIcon as e,ChevronUpIcon as t}from"../icons/icons.mjs";import{cn as n}from"../lib/cn.mjs";import{Button as r}from"./Button.mjs";import{STATUS_EDGE as i,StatusBadge as a}from"./StatusBadge.mjs";import{Fragment as o,jsx as s,jsxs as c}from"react/jsx-runtime";import{createContext as l,useContext as u,useState as d}from"react";import{cva as f}from"class-variance-authority";const p=l({tone:`neutral`,expandable:!1,expanded:!1,toggle:()=>void 0});function m(){return u(p)}const h=l(null),g=f(`overflow-hidden rounded-md border bg-doom-panel transition-colors`,{variants:{tone:i},defaultVariants:{tone:`neutral`}}),_=f(`overflow-hidden border-l-2 transition-colors`,{variants:{tone:i},defaultVariants:{tone:`neutral`}});function v({className:e,tone:t,expandable:r=!1,defaultExpanded:i=!1,expanded:a,onExpandedChange:o,children:c,...l}){let[f,m]=d(i),v=u(h)!==null,y=a??f,b={tone:t??`neutral`,expandable:r,expanded:y,toggle:()=>{let e=!y;a===void 0&&m(e),o?.(e)}};return/* @__PURE__ */ s(p.Provider,{value:b,children:/* @__PURE__ */ s(`div`,{"data-slot":`message-item`,"data-expanded":y,"data-grouped":v,className:n(v?_({tone:t}):g({tone:t}),e),...l,children:typeof c==`function`?c(b):c})})}function y({className:e,tone:t,title:r,summary:i,badge:o,children:l,...u}){let d=o===void 0?x[t??`neutral`]:o;return/* @__PURE__ */ s(h.Provider,{value:{tone:t??`neutral`},children:/* @__PURE__ */ c(`div`,{"data-slot":`message-item-group`,className:n(g({tone:t}),e),...u,children:[/* @__PURE__ */ c(`div`,{className:`flex min-h-8 items-center gap-2 border-doom-border-soft border-b px-[11px] text-sm text-doom-dim`,children:[/* @__PURE__ */ s(`span`,{className:`shrink-0 font-bold text-doom-hi`,children:r}),/* @__PURE__ */ s(`span`,{className:`min-w-0 flex-1 truncate text-doom-faint`,children:i}),d!==null&&d!==``?/* @__PURE__ */ s(a,{tone:t??`neutral`,children:d}):null]}),/* @__PURE__ */ s(`div`,{className:`flex flex-col divide-y divide-doom-border-soft`,children:l})]})})}function b(e){return e.running?`running`:e.isError?`error`:`ok`}const x={neutral:``,running:`RUNNING`,ok:`OK`,error:`ERROR`,info:`INFO`,accent:`NOTE`},S=/* @__PURE__ */ new Set([`ok`,`neutral`]);function C({className:i,title:o,badge:l,children:d,...f}){let{tone:p,expandable:g,expanded:_,toggle:v}=m(),y=u(h),b=y!==null&&S.has(p)?null:x[p],C=l===void 0?b:l;return/* @__PURE__ */ c(`div`,{"data-slot":`message-item-header`,className:n(`flex min-h-8 items-center gap-2 px-[11px] text-sm text-doom-dim`,i),...f,children:[y===null?/* @__PURE__ */ s(`span`,{className:`shrink-0 font-bold text-doom-hi`,children:o}):null,/* @__PURE__ */ s(`div`,{"data-slot":`message-item-summary`,className:`flex min-w-0 flex-1 items-center gap-2`,children:d}),C!==null&&C!==``?/* @__PURE__ */ s(a,{tone:p,"data-testid":`tool-status`,children:C}):null,g?/* @__PURE__ */ s(r,{variant:`ghost`,size:`icon`,"data-testid":`tool-expand`,"aria-expanded":_,"aria-label":_?`collapse`:`expand`,onClick:v,className:`text-doom-faint hover:text-doom-blue`,children:s(_?t:e,{className:`h-3 w-3`})}):null]})}function w({className:e,...t}){return/* @__PURE__ */ s(`div`,{"data-slot":`message-item-body`,className:n(`border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim`,e),...t})}const T={neutral:`…`,running:`◐`,ok:`✓`,error:`✗`,info:`●`,accent:`●`},E=f(`shrink-0`,{variants:{tone:{neutral:`text-doom-faint`,running:`text-doom-yellow`,ok:`text-doom-green`,error:`text-doom-red`,info:`text-doom-blue`,accent:`text-doom-magenta`}},defaultVariants:{tone:`neutral`}});function D({className:e,tone:t,glyph:r,expands:i=!1,children:a,...l}){let{toggle:u}=m(),d=/* @__PURE__ */ c(o,{children:[/* @__PURE__ */ s(`span`,{className:E({tone:t}),children:r??T[t??`neutral`]}),/* @__PURE__ */ s(`span`,{children:a})]});return i?/* @__PURE__ */ s(`button`,{type:`button`,"data-slot":`message-item-status`,"data-testid":`tool-more`,onClick:u,className:n(`flex cursor-pointer items-center gap-1.5 text-doom-faint hover:text-doom-blue`,e),children:d}):/* @__PURE__ */ s(`span`,{"data-slot":`message-item-status`,className:n(`flex items-center gap-1.5 text-doom-faint`,e),...l,children:d})}export{v as MessageItem,w as MessageItemBody,y as MessageItemGroup,C as MessageItemHeader,D as MessageItemStatus,T as STATUS_GLYPH,x as STATUS_LABEL,_ as messageItemRowVariants,E as messageItemStatusVariants,g as messageItemVariants,b as toolTone,m as useMessageItem};
|
|
2
2
|
//# sourceMappingURL=MessageItem.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageItem.mjs","names":[],"sources":["../../../src/components/MessageItem.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, createContext, type ReactNode, useContext, useState } from 'react';\n\nimport { ChevronDownIcon, ChevronUpIcon } from '../icons/icons';\nimport { cn } from '../lib/cn';\nimport { Button } from './Button';\nimport { STATUS_EDGE, StatusBadge, type StatusTone } from './StatusBadge';\n\n/**\n * The message item: the timeline card a tool call, a run, or a notice sits\n * in. The item owns its outcome tone and its expand state; the header, body,\n * and status parts read both from it, so a card composes the parts and never\n * threads `expanded` by hand. A tool message in a web plugin is built from\n * this, and so is the cockpit's own fallback item, which is what keeps them\n * looking alike.\n */\n\nexport interface MessageItemState {\n tone: StatusTone;\n /** Whether the header offers the toggle; an item that hides nothing offers none. */\n expandable: boolean;\n expanded: boolean;\n toggle: () => void;\n}\n\nconst MessageItemContext = createContext<MessageItemState>({\n tone: 'neutral',\n expandable: false,\n expanded: false,\n toggle: () => undefined,\n});\n\n/** The enclosing item's state, for a part that lives deeper than the item's render prop. */\nexport function useMessageItem(): MessageItemState {\n return useContext(MessageItemContext);\n}\n\n/**\n * The group an item is a row of, or null for an item that is a card of its\n * own. A row drops the frame the group already draws and keeps its tone as an\n * edge down its left side, and it stops repeating what the group's header has\n * said once: the tool's name, and an outcome the whole run shares.\n */\nconst MessageItemGroupContext = createContext<{ tone: StatusTone } | null>(null);\n\n/** The frame, wearing the edge STATUS_EDGE names for the tone, so the two never drift apart. */\nexport const messageItemVariants = cva('overflow-hidden rounded-md border bg-doom-panel transition-colors', {\n variants: { tone: STATUS_EDGE },\n defaultVariants: { tone: 'neutral' },\n});\n\n/** The same tone as a row: no card, one coloured edge, and the group's separators between rows. */\nexport const messageItemRowVariants = cva('overflow-hidden border-l-2 transition-colors', {\n variants: { tone: STATUS_EDGE },\n defaultVariants: { tone: 'neutral' },\n});\n\nexport interface MessageItemProps\n extends Omit<ComponentProps<'div'>, 'children'>, VariantProps<typeof messageItemVariants> {\n /** Whether the header offers the expand toggle. A toggle that reveals nothing reads as broken, so the card decides. */\n expandable?: boolean;\n defaultExpanded?: boolean;\n /** Controlled mode; pair with onExpandedChange. Omit it and the item keeps its own state. */\n expanded?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n /** Plain content, or a function of the item's state for a card that picks its lines by `expanded`. */\n children?: ReactNode | ((state: MessageItemState) => ReactNode);\n}\n\nexport function MessageItem({\n className,\n tone,\n expandable = false,\n defaultExpanded = false,\n expanded,\n onExpandedChange,\n children,\n ...props\n}: MessageItemProps) {\n const [own, setOwn] = useState(defaultExpanded);\n const group = useContext(MessageItemGroupContext);\n const grouped = group !== null;\n const current = expanded ?? own;\n const state: MessageItemState = {\n tone: tone ?? 'neutral',\n expandable,\n expanded: current,\n toggle: () => {\n const next = !current;\n if (expanded === undefined) setOwn(next);\n onExpandedChange?.(next);\n },\n };\n return (\n <MessageItemContext.Provider value={state}>\n <div\n data-slot=\"message-item\"\n data-expanded={current}\n data-grouped={grouped}\n className={cn(grouped ? messageItemRowVariants({ tone }) : messageItemVariants({ tone }), className)}\n {...props}\n >\n {typeof children === 'function' ? children(state) : children}\n </div>\n </MessageItemContext.Provider>\n );\n}\n\nexport interface MessageItemGroupProps\n extends Omit<ComponentProps<'div'>, 'title'>, VariantProps<typeof messageItemVariants> {\n /** The bold label at the left of the group header: the tool the run belongs to. */\n title: ReactNode;\n /** The line beside it, such as how many calls the group holds. */\n summary?: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n children?: ReactNode;\n}\n\n/**\n * One frame around a run of items that belong together.\n *\n * A transcript that calls the same tool five times in a row is five identical\n * frames, five gutter labels and five gaps, and the repetition reads louder\n * than the commands do. The group draws the frame once and each item inside it\n * becomes a row: same header, same expand, no card of its own. The tone still\n * belongs to the row, as an edge, so a failure in the middle of a run is not\n * flattened into the group's own outcome.\n */\nexport function MessageItemGroup({\n className,\n tone,\n title,\n summary,\n badge,\n children,\n ...props\n}: MessageItemGroupProps) {\n const label = badge === undefined ? STATUS_LABEL[tone ?? 'neutral'] : badge;\n return (\n <MessageItemGroupContext.Provider value={{ tone: tone ?? 'neutral' }}>\n <div data-slot=\"message-item-group\" className={cn(messageItemVariants({ tone }), className)} {...props}>\n <div className=\"flex min-h-8 items-center gap-2 border-doom-border-soft border-b px-[11px] text-sm text-doom-dim\">\n <span className=\"shrink-0 font-bold text-doom-hi\">{title}</span>\n <span className=\"min-w-0 flex-1 truncate text-doom-faint\">{summary}</span>\n {label !== null && label !== '' ? <StatusBadge tone={tone ?? 'neutral'}>{label}</StatusBadge> : null}\n </div>\n <div className=\"flex flex-col divide-y divide-doom-border-soft\">{children}</div>\n </div>\n </MessageItemGroupContext.Provider>\n );\n}\n\n/** The tone a tool call's item wears: running while the tool runs, then its outcome. */\nexport function toolTone(state: { running: boolean; isError: boolean }): StatusTone {\n return state.running ? 'running' : state.isError ? 'error' : 'ok';\n}\n\n/** The badge text a tone reads as when the header is not told otherwise; empty hides the badge. */\nexport const STATUS_LABEL: Readonly<Record<StatusTone, string>> = {\n neutral: '',\n running: 'RUNNING',\n ok: 'OK',\n error: 'ERROR',\n info: 'INFO',\n accent: 'NOTE',\n};\n\nexport interface MessageItemHeaderProps extends Omit<ComponentProps<'div'>, 'title'> {\n /** The bold label at the left: the tool name. */\n title: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n /** The summary beside the title; it gets the remaining width. */\n children?: ReactNode;\n}\n\n/** Inside a group these two are the quiet outcome: the group's header has already said it. */\nconst SILENT_IN_GROUP: ReadonlySet<StatusTone> = new Set<StatusTone>(['ok', 'neutral']);\n\n/**\n * The title row: label, summary, the tone's badge, and the expand toggle when\n * the item is expandable.\n *\n * A row inside a group says less. The group's header has already named the\n * tool and stated the run's outcome, so the row drops its title and speaks up\n * only when something went wrong or is still going: five OK badges down a run\n * of five are noise, and the one ERROR among them is the point.\n */\nexport function MessageItemHeader({ className, title, badge, children, ...props }: MessageItemHeaderProps) {\n const { tone, expandable, expanded, toggle } = useMessageItem();\n const group = useContext(MessageItemGroupContext);\n const defaultLabel = group !== null && SILENT_IN_GROUP.has(tone) ? null : STATUS_LABEL[tone];\n const label = badge === undefined ? defaultLabel : badge;\n return (\n <div\n data-slot=\"message-item-header\"\n className={cn('flex min-h-8 items-center gap-2 px-[11px] text-sm text-doom-dim', className)}\n {...props}\n >\n {group === null ? <span className=\"shrink-0 font-bold text-doom-hi\">{title}</span> : null}\n <div data-slot=\"message-item-summary\" className=\"flex min-w-0 flex-1 items-center gap-2\">\n {children}\n </div>\n {label !== null && label !== '' ? (\n <StatusBadge tone={tone} data-testid=\"tool-status\">\n {label}\n </StatusBadge>\n ) : null}\n {expandable ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid=\"tool-expand\"\n aria-expanded={expanded}\n aria-label={expanded ? 'collapse' : 'expand'}\n onClick={toggle}\n className=\"text-doom-faint hover:text-doom-blue\"\n >\n {expanded ? <ChevronUpIcon className=\"h-3 w-3\" /> : <ChevronDownIcon className=\"h-3 w-3\" />}\n </Button>\n ) : null}\n </div>\n );\n}\n\n/** The recessed body under the header. */\nexport function MessageItemBody({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"message-item-body\"\n className={cn('border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim', className)}\n {...props}\n />\n );\n}\n\n/** The glyph a tone reads as in a status line. */\nexport const STATUS_GLYPH: Readonly<Record<StatusTone, string>> = {\n neutral: '…',\n running: '◐',\n ok: '✓',\n error: '✗',\n info: '●',\n accent: '●',\n};\n\nexport const messageItemStatusVariants = cva('shrink-0', {\n variants: {\n tone: {\n neutral: 'text-doom-faint',\n running: 'text-doom-yellow',\n ok: 'text-doom-green',\n error: 'text-doom-red',\n info: 'text-doom-blue',\n accent: 'text-doom-magenta',\n } satisfies Record<StatusTone, string>,\n },\n defaultVariants: { tone: 'neutral' },\n});\n\nexport interface MessageItemStatusProps extends ComponentProps<'span'>, VariantProps<typeof messageItemStatusVariants> {\n /** Replaces the tone's glyph. */\n glyph?: string;\n /** Renders as a button that expands the item: the \"N more line(s)\" hint. Only className carries over. */\n expands?: boolean;\n}\n\n/** The one-line status under a body: `◐ running`, `✗ failed`, `✓ done`, or the hint that more is hidden. */\nexport function MessageItemStatus({\n className,\n tone,\n glyph,\n expands = false,\n children,\n ...props\n}: MessageItemStatusProps) {\n const { toggle } = useMessageItem();\n const content = (\n <>\n <span className={messageItemStatusVariants({ tone })}>{glyph ?? STATUS_GLYPH[tone ?? 'neutral']}</span>\n <span>{children}</span>\n </>\n );\n if (expands) {\n return (\n <button\n type=\"button\"\n data-slot=\"message-item-status\"\n data-testid=\"tool-more\"\n onClick={toggle}\n className={cn('flex cursor-pointer items-center gap-1.5 text-doom-faint hover:text-doom-blue', className)}\n >\n {content}\n </button>\n );\n }\n return (\n <span\n data-slot=\"message-item-status\"\n className={cn('flex items-center gap-1.5 text-doom-faint', className)}\n {...props}\n >\n {content}\n </span>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MessageItem.mjs","names":[],"sources":["../../../src/components/MessageItem.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, createContext, type ReactNode, useContext, useState } from 'react';\n\nimport { ChevronDownIcon, ChevronUpIcon } from '../icons/icons';\nimport { cn } from '../lib/cn';\nimport { Button } from './Button';\nimport { STATUS_EDGE, StatusBadge, type StatusTone } from './StatusBadge';\n\n/**\n * The message item: the timeline card a tool call, a run, or a notice sits\n * in. The item owns its outcome tone and its expand state; the header, body,\n * and status parts read both from it, so a card composes the parts and never\n * threads `expanded` by hand. A tool message in a web plugin is built from\n * this, and so is the cockpit's own fallback item, which is what keeps them\n * looking alike.\n */\n\nexport interface MessageItemState {\n tone: StatusTone;\n /** Whether the header offers the toggle; an item that hides nothing offers none. */\n expandable: boolean;\n expanded: boolean;\n toggle: () => void;\n}\n\nconst MessageItemContext = createContext<MessageItemState>({\n tone: 'neutral',\n expandable: false,\n expanded: false,\n toggle: () => undefined,\n});\n\n/** The enclosing item's state, for a part that lives deeper than the item's render prop. */\nexport function useMessageItem(): MessageItemState {\n return useContext(MessageItemContext);\n}\n\n/**\n * The group an item is a row of, or null for an item that is a card of its\n * own. A row drops the frame the group already draws and keeps its tone as an\n * edge down its left side, and it stops repeating what the group's header has\n * said once: the tool's name, and an outcome the whole run shares.\n */\nconst MessageItemGroupContext = createContext<{ tone: StatusTone } | null>(null);\n\n/** The frame, wearing the edge STATUS_EDGE names for the tone, so the two never drift apart. */\nexport const messageItemVariants = cva('overflow-hidden rounded-md border bg-doom-panel transition-colors', {\n variants: { tone: STATUS_EDGE },\n defaultVariants: { tone: 'neutral' },\n});\n\n/** The same tone as a row: no card, one coloured edge, and the group's separators between rows. */\nexport const messageItemRowVariants = cva('overflow-hidden border-l-2 transition-colors', {\n variants: { tone: STATUS_EDGE },\n defaultVariants: { tone: 'neutral' },\n});\n\nexport interface MessageItemProps\n extends Omit<ComponentProps<'div'>, 'children'>, VariantProps<typeof messageItemVariants> {\n /** Whether the header offers the expand toggle. A toggle that reveals nothing reads as broken, so the card decides. */\n expandable?: boolean;\n defaultExpanded?: boolean;\n /** Controlled mode; pair with onExpandedChange. Omit it and the item keeps its own state. */\n expanded?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n /** Plain content, or a function of the item's state for a card that picks its lines by `expanded`. */\n children?: ReactNode | ((state: MessageItemState) => ReactNode);\n}\n\nexport function MessageItem({\n className,\n tone,\n expandable = false,\n defaultExpanded = false,\n expanded,\n onExpandedChange,\n children,\n ...props\n}: MessageItemProps) {\n const [own, setOwn] = useState(defaultExpanded);\n const group = useContext(MessageItemGroupContext);\n const grouped = group !== null;\n const current = expanded ?? own;\n const state: MessageItemState = {\n tone: tone ?? 'neutral',\n expandable,\n expanded: current,\n toggle: () => {\n const next = !current;\n if (expanded === undefined) setOwn(next);\n onExpandedChange?.(next);\n },\n };\n return (\n <MessageItemContext.Provider value={state}>\n <div\n data-slot=\"message-item\"\n data-expanded={current}\n data-grouped={grouped}\n className={cn(grouped ? messageItemRowVariants({ tone }) : messageItemVariants({ tone }), className)}\n {...props}\n >\n {typeof children === 'function' ? children(state) : children}\n </div>\n </MessageItemContext.Provider>\n );\n}\n\nexport interface MessageItemGroupProps\n extends Omit<ComponentProps<'div'>, 'title'>, VariantProps<typeof messageItemVariants> {\n /** The bold label at the left of the group header: the tool the run belongs to. */\n title: ReactNode;\n /** The line beside it, such as how many calls the group holds. */\n summary?: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n children?: ReactNode;\n}\n\n/**\n * One frame around a run of items that belong together.\n *\n * A transcript that calls the same tool five times in a row is five identical\n * frames, five gutter labels and five gaps, and the repetition reads louder\n * than the commands do. The group draws the frame once and each item inside it\n * becomes a row: same header, same expand, no card of its own. The tone still\n * belongs to the row, as an edge, so a failure in the middle of a run is not\n * flattened into the group's own outcome.\n */\nexport function MessageItemGroup({\n className,\n tone,\n title,\n summary,\n badge,\n children,\n ...props\n}: MessageItemGroupProps) {\n const label = badge === undefined ? STATUS_LABEL[tone ?? 'neutral'] : badge;\n return (\n <MessageItemGroupContext.Provider value={{ tone: tone ?? 'neutral' }}>\n <div data-slot=\"message-item-group\" className={cn(messageItemVariants({ tone }), className)} {...props}>\n <div className=\"flex min-h-8 items-center gap-2 border-doom-border-soft border-b px-[11px] text-sm text-doom-dim\">\n <span className=\"shrink-0 font-bold text-doom-hi\">{title}</span>\n <span className=\"min-w-0 flex-1 truncate text-doom-faint\">{summary}</span>\n {label !== null && label !== '' ? <StatusBadge tone={tone ?? 'neutral'}>{label}</StatusBadge> : null}\n </div>\n <div className=\"flex flex-col divide-y divide-doom-border-soft\">{children}</div>\n </div>\n </MessageItemGroupContext.Provider>\n );\n}\n\n/** The tone a tool call's item wears: running while the tool runs, then its outcome. */\nexport function toolTone(state: { running: boolean; isError: boolean }): StatusTone {\n return state.running ? 'running' : state.isError ? 'error' : 'ok';\n}\n\n/** The badge text a tone reads as when the header is not told otherwise; empty hides the badge. */\nexport const STATUS_LABEL: Readonly<Record<StatusTone, string>> = {\n neutral: '',\n running: 'RUNNING',\n ok: 'OK',\n error: 'ERROR',\n info: 'INFO',\n accent: 'NOTE',\n};\n\nexport interface MessageItemHeaderProps extends Omit<ComponentProps<'div'>, 'title'> {\n /** The bold label at the left: the tool name. */\n title: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n /** The summary beside the title; it gets the remaining width. */\n children?: ReactNode;\n}\n\n/** Inside a group these two are the quiet outcome: the group's header has already said it. */\nconst SILENT_IN_GROUP: ReadonlySet<StatusTone> = new Set<StatusTone>(['ok', 'neutral']);\n\n/**\n * The title row: label, summary, the tone's badge, and the expand toggle when\n * the item is expandable.\n *\n * A row inside a group says less. The group's header has already named the\n * tool and stated the run's outcome, so the row drops its title and speaks up\n * only when something went wrong or is still going: five OK badges down a run\n * of five are noise, and the one ERROR among them is the point.\n */\nexport function MessageItemHeader({ className, title, badge, children, ...props }: MessageItemHeaderProps) {\n const { tone, expandable, expanded, toggle } = useMessageItem();\n const group = useContext(MessageItemGroupContext);\n const defaultLabel = group !== null && SILENT_IN_GROUP.has(tone) ? null : STATUS_LABEL[tone];\n const label = badge === undefined ? defaultLabel : badge;\n return (\n <div\n data-slot=\"message-item-header\"\n className={cn('flex min-h-8 items-center gap-2 px-[11px] text-sm text-doom-dim', className)}\n {...props}\n >\n {group === null ? <span className=\"shrink-0 font-bold text-doom-hi\">{title}</span> : null}\n <div data-slot=\"message-item-summary\" className=\"flex min-w-0 flex-1 items-center gap-2\">\n {children}\n </div>\n {label !== null && label !== '' ? (\n <StatusBadge tone={tone} data-testid=\"tool-status\">\n {label}\n </StatusBadge>\n ) : null}\n {expandable ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid=\"tool-expand\"\n aria-expanded={expanded}\n aria-label={expanded ? 'collapse' : 'expand'}\n onClick={toggle}\n className=\"text-doom-faint hover:text-doom-blue\"\n >\n {expanded ? <ChevronUpIcon className=\"h-3 w-3\" /> : <ChevronDownIcon className=\"h-3 w-3\" />}\n </Button>\n ) : null}\n </div>\n );\n}\n\n/** The recessed body under the header. */\nexport function MessageItemBody({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"message-item-body\"\n className={cn('border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim', className)}\n {...props}\n />\n );\n}\n\n/** The glyph a tone reads as in a status line. */\nexport const STATUS_GLYPH: Readonly<Record<StatusTone, string>> = {\n neutral: '…',\n running: '◐',\n ok: '✓',\n error: '✗',\n info: '●',\n accent: '●',\n};\n\nexport const messageItemStatusVariants = cva('shrink-0', {\n variants: {\n tone: {\n neutral: 'text-doom-faint',\n running: 'text-doom-yellow',\n ok: 'text-doom-green',\n error: 'text-doom-red',\n info: 'text-doom-blue',\n accent: 'text-doom-magenta',\n } satisfies Record<StatusTone, string>,\n },\n defaultVariants: { tone: 'neutral' },\n});\n\nexport interface MessageItemStatusProps extends ComponentProps<'span'>, VariantProps<typeof messageItemStatusVariants> {\n /** Replaces the tone's glyph. */\n glyph?: string;\n /** Renders as a button that expands the item: the \"N more line(s)\" hint. Only className carries over. */\n expands?: boolean;\n}\n\n/** The one-line status under a body: `◐ running`, `✗ failed`, `✓ done`, or the hint that more is hidden. */\nexport function MessageItemStatus({\n className,\n tone,\n glyph,\n expands = false,\n children,\n ...props\n}: MessageItemStatusProps) {\n const { toggle } = useMessageItem();\n const content = (\n <>\n <span className={messageItemStatusVariants({ tone })}>{glyph ?? STATUS_GLYPH[tone ?? 'neutral']}</span>\n <span>{children}</span>\n </>\n );\n if (expands) {\n return (\n <button\n type=\"button\"\n data-slot=\"message-item-status\"\n data-testid=\"tool-more\"\n onClick={toggle}\n className={cn('flex cursor-pointer items-center gap-1.5 text-doom-faint hover:text-doom-blue', className)}\n >\n {content}\n </button>\n );\n }\n return (\n <span\n data-slot=\"message-item-status\"\n className={cn('flex items-center gap-1.5 text-doom-faint', className)}\n {...props}\n >\n {content}\n </span>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../lib/cn.cjs");let t=require("react/jsx-runtime");const n=(0,require("class-variance-authority").cva)(`whitespace-pre-wrap break-words`,{variants:{tone:{hi:`text-doom-hi`,text:`text-doom-text`,dim:`text-doom-dim`,muted:`text-doom-faint`,success:`text-doom-green`,error:`text-doom-red`,warning:`text-doom-yellow`,accent:`text-doom-blue`},bold:{true:`font-bold`,false:``},indent:{true:`pl-4`,false:``}},defaultVariants:{tone:`dim`,bold:!1,indent:!1}});function r({className:r,lines:i,...a}){return(0,t.jsx)(`div`,{"data-slot":`message-lines`,className:e.cn(`flex flex-col gap-0.5`,r),...a,children:i.map((e,r)
|
|
1
|
+
const e=require("../lib/cn.cjs");let t=require("react/jsx-runtime");const n=(0,require("class-variance-authority").cva)(`whitespace-pre-wrap break-words`,{variants:{tone:{hi:`text-doom-hi`,text:`text-doom-text`,dim:`text-doom-dim`,muted:`text-doom-faint`,success:`text-doom-green`,error:`text-doom-red`,warning:`text-doom-yellow`,accent:`text-doom-blue`},bold:{true:`font-bold`,false:``},indent:{true:`pl-4`,false:``}},defaultVariants:{tone:`dim`,bold:!1,indent:!1}});function r({className:r,lines:i,...a}){return/* @__PURE__ */ (0,t.jsx)(`div`,{"data-slot":`message-lines`,className:e.cn(`flex flex-col gap-0.5`,r),...a,children:i.map((e,r)=>/* @__PURE__ */ (0,t.jsx)(`div`,{className:n({tone:e.tone,bold:e.bold===!0,indent:e.indent===!0}),children:e.text},`${String(r)}-${e.text}`))})}exports.MessageLines=r,exports.messageLineVariants=n;
|
|
2
2
|
//# sourceMappingURL=MessageLines.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageLines.cjs","names":["cva","cn"],"sources":["../../../src/components/MessageLines.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority';\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '../lib/cn';\nimport type { MessageLineTone as Tone } from '../types/tone';\n\n/** One line of a message body, with the tone the card's pure view logic chose for it. */\nexport type MessageLineTone = Tone;\n\nexport interface MessageLine {\n text: string;\n tone: MessageLineTone;\n bold?: boolean;\n indent?: boolean;\n}\n\nexport const messageLineVariants = cva('whitespace-pre-wrap break-words', {\n variants: {\n tone: {\n hi: 'text-doom-hi',\n text: 'text-doom-text',\n dim: 'text-doom-dim',\n muted: 'text-doom-faint',\n success: 'text-doom-green',\n error: 'text-doom-red',\n warning: 'text-doom-yellow',\n accent: 'text-doom-blue',\n } satisfies Record<Tone, string>,\n bold: { true: 'font-bold', false: '' },\n indent: { true: 'pl-4', false: '' },\n },\n defaultVariants: { tone: 'dim', bold: false, indent: false },\n});\n\nexport interface MessageLinesProps extends ComponentProps<'div'> {\n lines: readonly MessageLine[];\n}\n\n/** A body of toned lines: what a card's view helper computed, drawn one line per row. */\nexport function MessageLines({ className, lines, ...props }: MessageLinesProps) {\n return (\n <div data-slot=\"message-lines\" className={cn('flex flex-col gap-0.5', className)} {...props}>\n {lines.map((line, index) => (\n <div\n key={`${String(index)}-${line.text}`}\n className={messageLineVariants({ tone: line.tone, bold: line.bold === true, indent: line.indent === true })}\n >\n {line.text}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"oEAgBA,MAAa,GAAA,oCAAsBA,CAAAA,CAAAA,IAAAA,CAAI,kCAAmC,CACxE,SAAU,CACR,KAAM,CACJ,GAAI,eACJ,KAAM,iBACN,IAAK,gBACL,MAAO,kBACP,QAAS,kBACT,MAAO,gBACP,QAAS,mBACT,OAAQ,gBACV,EACA,KAAM,CAAE,KAAM,YAAa,MAAO,EAAG,EACrC,OAAQ,CAAE,KAAM,OAAQ,MAAO,EAAG,CACpC,EACA,gBAAiB,CAAE,KAAM,MAAO,KAAM,GAAO,OAAQ,EAAM,CAC7D,CAAC,EAOD,SAAgB,EAAa,CAAE,YAAW,QAAO,GAAG,GAA4B,CAC9E,
|
|
1
|
+
{"version":3,"file":"MessageLines.cjs","names":["cva","cn"],"sources":["../../../src/components/MessageLines.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority';\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '../lib/cn';\nimport type { MessageLineTone as Tone } from '../types/tone';\n\n/** One line of a message body, with the tone the card's pure view logic chose for it. */\nexport type MessageLineTone = Tone;\n\nexport interface MessageLine {\n text: string;\n tone: MessageLineTone;\n bold?: boolean;\n indent?: boolean;\n}\n\nexport const messageLineVariants = cva('whitespace-pre-wrap break-words', {\n variants: {\n tone: {\n hi: 'text-doom-hi',\n text: 'text-doom-text',\n dim: 'text-doom-dim',\n muted: 'text-doom-faint',\n success: 'text-doom-green',\n error: 'text-doom-red',\n warning: 'text-doom-yellow',\n accent: 'text-doom-blue',\n } satisfies Record<Tone, string>,\n bold: { true: 'font-bold', false: '' },\n indent: { true: 'pl-4', false: '' },\n },\n defaultVariants: { tone: 'dim', bold: false, indent: false },\n});\n\nexport interface MessageLinesProps extends ComponentProps<'div'> {\n lines: readonly MessageLine[];\n}\n\n/** A body of toned lines: what a card's view helper computed, drawn one line per row. */\nexport function MessageLines({ className, lines, ...props }: MessageLinesProps) {\n return (\n <div data-slot=\"message-lines\" className={cn('flex flex-col gap-0.5', className)} {...props}>\n {lines.map((line, index) => (\n <div\n key={`${String(index)}-${line.text}`}\n className={messageLineVariants({ tone: line.tone, bold: line.bold === true, indent: line.indent === true })}\n >\n {line.text}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"oEAgBA,MAAa,GAAA,oCAAsBA,CAAAA,CAAAA,IAAAA,CAAI,kCAAmC,CACxE,SAAU,CACR,KAAM,CACJ,GAAI,eACJ,KAAM,iBACN,IAAK,gBACL,MAAO,kBACP,QAAS,kBACT,MAAO,gBACP,QAAS,mBACT,OAAQ,gBACV,EACA,KAAM,CAAE,KAAM,YAAa,MAAO,EAAG,EACrC,OAAQ,CAAE,KAAM,OAAQ,MAAO,EAAG,CACpC,EACA,gBAAiB,CAAE,KAAM,MAAO,KAAM,GAAO,OAAQ,EAAM,CAC7D,CAAC,EAOD,SAAgB,EAAa,CAAE,YAAW,QAAO,GAAG,GAA4B,CAC9E,MACE,iBAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,YAAU,gBAAgB,UAAWC,EAAAA,GAAG,wBAAyB,CAAS,EAAG,GAAI,EACnF,SAAA,EAAM,KAAK,EAAM,IAChB,iBAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CAEE,UAAW,EAAoB,CAAE,KAAM,EAAK,KAAM,KAAM,EAAK,OAAS,GAAM,OAAQ,EAAK,SAAW,EAAK,CAAC,EAEzG,SAAA,EAAK,IACH,EAJE,GAAG,OAAO,CAAK,EAAE,GAAG,EAAK,MAI3B,CACN,CACE,CAAA,CAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{cn as e}from"../lib/cn.mjs";import{jsx as t}from"react/jsx-runtime";import{cva as n}from"class-variance-authority";const r=n(`whitespace-pre-wrap break-words`,{variants:{tone:{hi:`text-doom-hi`,text:`text-doom-text`,dim:`text-doom-dim`,muted:`text-doom-faint`,success:`text-doom-green`,error:`text-doom-red`,warning:`text-doom-yellow`,accent:`text-doom-blue`},bold:{true:`font-bold`,false:``},indent:{true:`pl-4`,false:``}},defaultVariants:{tone:`dim`,bold:!1,indent:!1}});function i({className:n,lines:i,...a}){return t(`div`,{"data-slot":`message-lines`,className:e(`flex flex-col gap-0.5`,n),...a,children:i.map((e,n)
|
|
1
|
+
import{cn as e}from"../lib/cn.mjs";import{jsx as t}from"react/jsx-runtime";import{cva as n}from"class-variance-authority";const r=n(`whitespace-pre-wrap break-words`,{variants:{tone:{hi:`text-doom-hi`,text:`text-doom-text`,dim:`text-doom-dim`,muted:`text-doom-faint`,success:`text-doom-green`,error:`text-doom-red`,warning:`text-doom-yellow`,accent:`text-doom-blue`},bold:{true:`font-bold`,false:``},indent:{true:`pl-4`,false:``}},defaultVariants:{tone:`dim`,bold:!1,indent:!1}});function i({className:n,lines:i,...a}){return/* @__PURE__ */ t(`div`,{"data-slot":`message-lines`,className:e(`flex flex-col gap-0.5`,n),...a,children:i.map((e,n)=>/* @__PURE__ */ t(`div`,{className:r({tone:e.tone,bold:e.bold===!0,indent:e.indent===!0}),children:e.text},`${String(n)}-${e.text}`))})}export{i as MessageLines,r as messageLineVariants};
|
|
2
2
|
//# sourceMappingURL=MessageLines.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageLines.mjs","names":[],"sources":["../../../src/components/MessageLines.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority';\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '../lib/cn';\nimport type { MessageLineTone as Tone } from '../types/tone';\n\n/** One line of a message body, with the tone the card's pure view logic chose for it. */\nexport type MessageLineTone = Tone;\n\nexport interface MessageLine {\n text: string;\n tone: MessageLineTone;\n bold?: boolean;\n indent?: boolean;\n}\n\nexport const messageLineVariants = cva('whitespace-pre-wrap break-words', {\n variants: {\n tone: {\n hi: 'text-doom-hi',\n text: 'text-doom-text',\n dim: 'text-doom-dim',\n muted: 'text-doom-faint',\n success: 'text-doom-green',\n error: 'text-doom-red',\n warning: 'text-doom-yellow',\n accent: 'text-doom-blue',\n } satisfies Record<Tone, string>,\n bold: { true: 'font-bold', false: '' },\n indent: { true: 'pl-4', false: '' },\n },\n defaultVariants: { tone: 'dim', bold: false, indent: false },\n});\n\nexport interface MessageLinesProps extends ComponentProps<'div'> {\n lines: readonly MessageLine[];\n}\n\n/** A body of toned lines: what a card's view helper computed, drawn one line per row. */\nexport function MessageLines({ className, lines, ...props }: MessageLinesProps) {\n return (\n <div data-slot=\"message-lines\" className={cn('flex flex-col gap-0.5', className)} {...props}>\n {lines.map((line, index) => (\n <div\n key={`${String(index)}-${line.text}`}\n className={messageLineVariants({ tone: line.tone, bold: line.bold === true, indent: line.indent === true })}\n >\n {line.text}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"0HAgBA,MAAa,EAAsB,EAAI,kCAAmC,CACxE,SAAU,CACR,KAAM,CACJ,GAAI,eACJ,KAAM,iBACN,IAAK,gBACL,MAAO,kBACP,QAAS,kBACT,MAAO,gBACP,QAAS,mBACT,OAAQ,gBACV,EACA,KAAM,CAAE,KAAM,YAAa,MAAO,EAAG,EACrC,OAAQ,CAAE,KAAM,OAAQ,MAAO,EAAG,CACpC,EACA,gBAAiB,CAAE,KAAM,MAAO,KAAM,GAAO,OAAQ,EAAM,CAC7D,CAAC,EAOD,SAAgB,EAAa,CAAE,YAAW,QAAO,GAAG,GAA4B,CAC9E,
|
|
1
|
+
{"version":3,"file":"MessageLines.mjs","names":[],"sources":["../../../src/components/MessageLines.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority';\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '../lib/cn';\nimport type { MessageLineTone as Tone } from '../types/tone';\n\n/** One line of a message body, with the tone the card's pure view logic chose for it. */\nexport type MessageLineTone = Tone;\n\nexport interface MessageLine {\n text: string;\n tone: MessageLineTone;\n bold?: boolean;\n indent?: boolean;\n}\n\nexport const messageLineVariants = cva('whitespace-pre-wrap break-words', {\n variants: {\n tone: {\n hi: 'text-doom-hi',\n text: 'text-doom-text',\n dim: 'text-doom-dim',\n muted: 'text-doom-faint',\n success: 'text-doom-green',\n error: 'text-doom-red',\n warning: 'text-doom-yellow',\n accent: 'text-doom-blue',\n } satisfies Record<Tone, string>,\n bold: { true: 'font-bold', false: '' },\n indent: { true: 'pl-4', false: '' },\n },\n defaultVariants: { tone: 'dim', bold: false, indent: false },\n});\n\nexport interface MessageLinesProps extends ComponentProps<'div'> {\n lines: readonly MessageLine[];\n}\n\n/** A body of toned lines: what a card's view helper computed, drawn one line per row. */\nexport function MessageLines({ className, lines, ...props }: MessageLinesProps) {\n return (\n <div data-slot=\"message-lines\" className={cn('flex flex-col gap-0.5', className)} {...props}>\n {lines.map((line, index) => (\n <div\n key={`${String(index)}-${line.text}`}\n className={messageLineVariants({ tone: line.tone, bold: line.bold === true, indent: line.indent === true })}\n >\n {line.text}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"0HAgBA,MAAa,EAAsB,EAAI,kCAAmC,CACxE,SAAU,CACR,KAAM,CACJ,GAAI,eACJ,KAAM,iBACN,IAAK,gBACL,MAAO,kBACP,QAAS,kBACT,MAAO,gBACP,QAAS,mBACT,OAAQ,gBACV,EACA,KAAM,CAAE,KAAM,YAAa,MAAO,EAAG,EACrC,OAAQ,CAAE,KAAM,OAAQ,MAAO,EAAG,CACpC,EACA,gBAAiB,CAAE,KAAM,MAAO,KAAM,GAAO,OAAQ,EAAM,CAC7D,CAAC,EAOD,SAAgB,EAAa,CAAE,YAAW,QAAO,GAAG,GAA4B,CAC9E,MACE,kBAAC,MAAD,CAAK,YAAU,gBAAgB,UAAW,EAAG,wBAAyB,CAAS,EAAG,GAAI,EACnF,SAAA,EAAM,KAAK,EAAM,IAChB,kBAAC,MAAD,CAEE,UAAW,EAAoB,CAAE,KAAM,EAAK,KAAM,KAAM,EAAK,OAAS,GAAM,OAAQ,EAAK,SAAW,EAAK,CAAC,EAEzG,SAAA,EAAK,IACH,EAJE,GAAG,OAAO,CAAK,EAAE,GAAG,EAAK,MAI3B,CACN,CACE,CAAA,CAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../lib/cn.cjs"),t=require("../lib/optionList.cjs");let n=require("react/jsx-runtime"),r=require("react"),i=require("class-variance-authority");const a=(0,i.cva)(`flex min-h-0 flex-1 flex-col overflow-y-auto`,{variants:{density:{compact:`gap-0.5 p-1.5`,comfortable:`gap-1.5`}},defaultVariants:{density:`comfortable`}}),o=(0,i.cva)(`flex shrink-0 cursor-pointer items-center text-left outline-none`,{variants:{density:{compact:`gap-2.5 rounded-md px-2 py-[7px]`,comfortable:`min-h-[42px] gap-3 rounded-md border px-[11px] py-2 text-sm text-doom-text transition-colors`},active:{true:``,false:``}},compoundVariants:[{density:`compact`,active:!0,class:`bg-doom-tint-blue`},{density:`comfortable`,active:!0,class:`border-doom-edge-blue bg-doom-tint-blue`},{density:`comfortable`,active:!1,class:`border-doom-border-soft`}],defaultVariants:{density:`comfortable`,active:!1}}),s=(0,i.cva)(`flex shrink-0 items-center justify-center rounded-full font-bold`,{variants:{density:{compact:`h-[15px] w-[15px] border border-doom-border text-2xs text-doom-faint`,comfortable:`h-[18px] w-[18px] bg-doom-deep text-xs text-doom-dim`}},defaultVariants:{density:`comfortable`}});function c({className:t,density:r,active:i,marker:a,children:c,...l}){return(0,n.jsxs)(`button`,{type:`button`,role:`option`,"data-slot":`option-row`,"aria-selected":i??!1,"data-cursor":i??!1,className:e.cn(o({density:r,active:i}),t),...l,children:[a===void 0?null
|
|
1
|
+
const e=require("../lib/cn.cjs"),t=require("../lib/optionList.cjs");let n=require("react/jsx-runtime"),r=require("react"),i=require("class-variance-authority");const a=(0,i.cva)(`flex min-h-0 flex-1 flex-col overflow-y-auto`,{variants:{density:{compact:`gap-0.5 p-1.5`,comfortable:`gap-1.5`}},defaultVariants:{density:`comfortable`}}),o=(0,i.cva)(`flex shrink-0 cursor-pointer items-center text-left outline-none`,{variants:{density:{compact:`gap-2.5 rounded-md px-2 py-[7px]`,comfortable:`min-h-[42px] gap-3 rounded-md border px-[11px] py-2 text-sm text-doom-text transition-colors`},active:{true:``,false:``}},compoundVariants:[{density:`compact`,active:!0,class:`bg-doom-tint-blue`},{density:`comfortable`,active:!0,class:`border-doom-edge-blue bg-doom-tint-blue`},{density:`comfortable`,active:!1,class:`border-doom-border-soft`}],defaultVariants:{density:`comfortable`,active:!1}}),s=(0,i.cva)(`flex shrink-0 items-center justify-center rounded-full font-bold`,{variants:{density:{compact:`h-[15px] w-[15px] border border-doom-border text-2xs text-doom-faint`,comfortable:`h-[18px] w-[18px] bg-doom-deep text-xs text-doom-dim`}},defaultVariants:{density:`comfortable`}});function c({className:t,density:r,active:i,marker:a,children:c,...l}){return/* @__PURE__ */ (0,n.jsxs)(`button`,{type:`button`,role:`option`,"data-slot":`option-row`,"aria-selected":i??!1,"data-cursor":i??!1,className:e.cn(o({density:r,active:i}),t),...l,children:[a===void 0?null:/* @__PURE__ */ (0,n.jsx)(`span`,{className:s({density:r}),children:a}),c]})}function l({className:t,density:r,...i}){return/* @__PURE__ */ (0,n.jsx)(`span`,{"data-slot":`option-label`,className:e.cn(`min-w-0 flex-1`,r===`compact`?`truncate text-sm text-doom-text`:`break-words`,t),...i})}function u({className:i,options:o,cursor:s,onCursorChange:u,onSelect:d,testIdPrefix:f,density:p=`comfortable`,...m}){let h=(0,r.useRef)(null);return(0,r.useEffect)(()=>{h.current?.children[s]?.scrollIntoView({block:`nearest`})},[s]),/* @__PURE__ */ (0,n.jsx)(`div`,{ref:h,role:`listbox`,"aria-label":`options`,"data-slot":`option-list`,"data-testid":`${f}s`,className:e.cn(a({density:p}),i),...m,children:o.map((e,r)=>/* @__PURE__ */ (0,n.jsx)(c,{density:p,active:r===s,marker:t.optionMarker(r),"data-testid":`${f}-${String(r)}`,title:e,onMouseEnter:()=>u(r),onClick:()=>d(e),children:/* @__PURE__ */ (0,n.jsx)(l,{density:p,children:e})},e))})}exports.OptionLabel=l,exports.OptionList=u,exports.OptionRow=c,exports.optionListVariants=a,exports.optionMarkerVariants=s,exports.optionRowVariants=o;
|
|
2
2
|
//# sourceMappingURL=OptionList.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OptionList.cjs","names":["cva","cn","useRef","optionMarker"],"sources":["../../../src/components/OptionList.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, type ReactNode, useEffect, useRef } from 'react';\n\nimport { cn } from '../lib/cn';\nimport { optionMarker } from '../lib/optionList';\n\nexport const optionListVariants = cva('flex min-h-0 flex-1 flex-col overflow-y-auto', {\n variants: {\n /** The compact rows of a bar popover, or the roomier rows of a modal. */\n density: { compact: 'gap-0.5 p-1.5', comfortable: 'gap-1.5' },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nexport const optionRowVariants = cva('flex shrink-0 cursor-pointer items-center text-left outline-none', {\n variants: {\n density: {\n compact: 'gap-2.5 rounded-md px-2 py-[7px]',\n comfortable: 'min-h-[42px] gap-3 rounded-md border px-[11px] py-2 text-sm text-doom-text transition-colors',\n },\n active: { true: '', false: '' },\n },\n compoundVariants: [\n { density: 'compact', active: true, class: 'bg-doom-tint-blue' },\n { density: 'comfortable', active: true, class: 'border-doom-edge-blue bg-doom-tint-blue' },\n { density: 'comfortable', active: false, class: 'border-doom-border-soft' },\n ],\n defaultVariants: { density: 'comfortable', active: false },\n});\n\nexport const optionMarkerVariants = cva('flex shrink-0 items-center justify-center rounded-full font-bold', {\n variants: {\n density: {\n compact: 'h-[15px] w-[15px] border border-doom-border text-2xs text-doom-faint',\n comfortable: 'h-[18px] w-[18px] bg-doom-deep text-xs text-doom-dim',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nexport interface OptionRowProps extends ComponentProps<'button'>, VariantProps<typeof optionRowVariants> {\n /** The digit or bullet in the row's leading circle; omit it for a row with no shortcut. */\n marker?: ReactNode;\n}\n\n/**\n * One row of an option list, exported on its own so a surface with richer\n * rows than a plain string still wears the list's shape instead of restating\n * it. Children render straight into the row, so a row is free to hold a name\n * and a description side by side; OptionLabel is the single-string case.\n * `role=\"option\"` means a row belongs inside a `listbox`.\n */\nexport function OptionRow({ className, density, active, marker, children, ...props }: OptionRowProps) {\n return (\n <button\n type=\"button\"\n role=\"option\"\n data-slot=\"option-row\"\n aria-selected={active ?? false}\n data-cursor={active ?? false}\n className={cn(optionRowVariants({ density, active }), className)}\n {...props}\n >\n {marker === undefined ? null : <span className={optionMarkerVariants({ density })}>{marker}</span>}\n {children}\n </button>\n );\n}\n\n/** A row's single-string label: it takes the remaining width and clips or wraps by density. */\nexport function OptionLabel({\n className,\n density,\n ...props\n}: ComponentProps<'span'> & VariantProps<typeof optionListVariants>) {\n return (\n <span\n data-slot=\"option-label\"\n className={cn(\n 'min-w-0 flex-1',\n density === 'compact' ? 'truncate text-sm text-doom-text' : 'break-words',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface OptionListProps\n extends Omit<ComponentProps<'div'>, 'onSelect'>, VariantProps<typeof optionListVariants> {\n options: readonly string[];\n /** The highlighted row, owned by the surface so its own focused element can drive it. */\n cursor: number;\n onCursorChange: (cursor: number) => void;\n onSelect: (option: string) => void;\n /** Rows are `${testIdPrefix}-${index}`; the list itself is the plural, so a prefix query for a row never catches it. */\n testIdPrefix: string;\n}\n\n/**\n * The answers a select surface offers, wherever it is framed.\n *\n * The agent decides how many options there are, and a real one runs past\n * twenty, so the list cannot rely on the digit shortcuts alone. The cursor is\n * controlled rather than owned here because the surface around it is what the\n * overlay library focuses: keys arrive there, and taking focus away from it to\n * a list would fight the library for it and break the escape and outside-click\n * behaviour that focus carries. Pair it with handleOptionListKey.\n */\nexport function OptionList({\n className,\n options,\n cursor,\n onCursorChange,\n onSelect,\n testIdPrefix,\n density = 'comfortable',\n ...props\n}: OptionListProps) {\n const container = useRef<HTMLDivElement>(null);\n\n // Keep the highlighted row visible when the keyboard moves it past the edge.\n useEffect(() => {\n container.current?.children[cursor]?.scrollIntoView({ block: 'nearest' });\n }, [cursor]);\n\n return (\n <div\n ref={container}\n role=\"listbox\"\n aria-label=\"options\"\n data-slot=\"option-list\"\n data-testid={`${testIdPrefix}s`}\n className={cn(optionListVariants({ density }), className)}\n {...props}\n >\n {options.map((option, index) => (\n <OptionRow\n key={option}\n density={density}\n active={index === cursor}\n marker={optionMarker(index)}\n data-testid={`${testIdPrefix}-${String(index)}`}\n title={option}\n onMouseEnter={() => onCursorChange(index)}\n onClick={() => onSelect(option)}\n >\n <OptionLabel density={density}>{option}</OptionLabel>\n </OptionRow>\n ))}\n </div>\n );\n}\n"],"mappings":"gKAMA,MAAa,GAAA,EAAqBA,EAAAA,IAAAA,CAAI,+CAAgD,CACpF,SAAU,CAER,QAAS,CAAE,QAAS,gBAAiB,YAAa,SAAU,CAC9D,EACA,gBAAiB,CAAE,QAAS,aAAc,CAC5C,CAAC,EAEY,GAAA,EAAoBA,EAAAA,IAAAA,CAAI,mEAAoE,CACvG,SAAU,CACR,QAAS,CACP,QAAS,mCACT,YAAa,8FACf,EACA,OAAQ,CAAE,KAAM,GAAI,MAAO,EAAG,CAChC,EACA,iBAAkB,CAChB,CAAE,QAAS,UAAW,OAAQ,GAAM,MAAO,mBAAoB,EAC/D,CAAE,QAAS,cAAe,OAAQ,GAAM,MAAO,yCAA0C,EACzF,CAAE,QAAS,cAAe,OAAQ,GAAO,MAAO,yBAA0B,CAC5E,EACA,gBAAiB,CAAE,QAAS,cAAe,OAAQ,EAAM,CAC3D,CAAC,EAEY,GAAA,EAAuBA,EAAAA,IAAAA,CAAI,mEAAoE,CAC1G,SAAU,CACR,QAAS,CACP,QAAS,uEACT,YAAa,sDACf,CACF,EACA,gBAAiB,CAAE,QAAS,aAAc,CAC5C,CAAC,EAcD,SAAgB,EAAU,CAAE,YAAW,UAAS,SAAQ,SAAQ,WAAU,GAAG,GAAyB,CACpG,
|
|
1
|
+
{"version":3,"file":"OptionList.cjs","names":["cva","cn","useRef","optionMarker"],"sources":["../../../src/components/OptionList.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, type ReactNode, useEffect, useRef } from 'react';\n\nimport { cn } from '../lib/cn';\nimport { optionMarker } from '../lib/optionList';\n\nexport const optionListVariants = cva('flex min-h-0 flex-1 flex-col overflow-y-auto', {\n variants: {\n /** The compact rows of a bar popover, or the roomier rows of a modal. */\n density: { compact: 'gap-0.5 p-1.5', comfortable: 'gap-1.5' },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nexport const optionRowVariants = cva('flex shrink-0 cursor-pointer items-center text-left outline-none', {\n variants: {\n density: {\n compact: 'gap-2.5 rounded-md px-2 py-[7px]',\n comfortable: 'min-h-[42px] gap-3 rounded-md border px-[11px] py-2 text-sm text-doom-text transition-colors',\n },\n active: { true: '', false: '' },\n },\n compoundVariants: [\n { density: 'compact', active: true, class: 'bg-doom-tint-blue' },\n { density: 'comfortable', active: true, class: 'border-doom-edge-blue bg-doom-tint-blue' },\n { density: 'comfortable', active: false, class: 'border-doom-border-soft' },\n ],\n defaultVariants: { density: 'comfortable', active: false },\n});\n\nexport const optionMarkerVariants = cva('flex shrink-0 items-center justify-center rounded-full font-bold', {\n variants: {\n density: {\n compact: 'h-[15px] w-[15px] border border-doom-border text-2xs text-doom-faint',\n comfortable: 'h-[18px] w-[18px] bg-doom-deep text-xs text-doom-dim',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nexport interface OptionRowProps extends ComponentProps<'button'>, VariantProps<typeof optionRowVariants> {\n /** The digit or bullet in the row's leading circle; omit it for a row with no shortcut. */\n marker?: ReactNode;\n}\n\n/**\n * One row of an option list, exported on its own so a surface with richer\n * rows than a plain string still wears the list's shape instead of restating\n * it. Children render straight into the row, so a row is free to hold a name\n * and a description side by side; OptionLabel is the single-string case.\n * `role=\"option\"` means a row belongs inside a `listbox`.\n */\nexport function OptionRow({ className, density, active, marker, children, ...props }: OptionRowProps) {\n return (\n <button\n type=\"button\"\n role=\"option\"\n data-slot=\"option-row\"\n aria-selected={active ?? false}\n data-cursor={active ?? false}\n className={cn(optionRowVariants({ density, active }), className)}\n {...props}\n >\n {marker === undefined ? null : <span className={optionMarkerVariants({ density })}>{marker}</span>}\n {children}\n </button>\n );\n}\n\n/** A row's single-string label: it takes the remaining width and clips or wraps by density. */\nexport function OptionLabel({\n className,\n density,\n ...props\n}: ComponentProps<'span'> & VariantProps<typeof optionListVariants>) {\n return (\n <span\n data-slot=\"option-label\"\n className={cn(\n 'min-w-0 flex-1',\n density === 'compact' ? 'truncate text-sm text-doom-text' : 'break-words',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface OptionListProps\n extends Omit<ComponentProps<'div'>, 'onSelect'>, VariantProps<typeof optionListVariants> {\n options: readonly string[];\n /** The highlighted row, owned by the surface so its own focused element can drive it. */\n cursor: number;\n onCursorChange: (cursor: number) => void;\n onSelect: (option: string) => void;\n /** Rows are `${testIdPrefix}-${index}`; the list itself is the plural, so a prefix query for a row never catches it. */\n testIdPrefix: string;\n}\n\n/**\n * The answers a select surface offers, wherever it is framed.\n *\n * The agent decides how many options there are, and a real one runs past\n * twenty, so the list cannot rely on the digit shortcuts alone. The cursor is\n * controlled rather than owned here because the surface around it is what the\n * overlay library focuses: keys arrive there, and taking focus away from it to\n * a list would fight the library for it and break the escape and outside-click\n * behaviour that focus carries. Pair it with handleOptionListKey.\n */\nexport function OptionList({\n className,\n options,\n cursor,\n onCursorChange,\n onSelect,\n testIdPrefix,\n density = 'comfortable',\n ...props\n}: OptionListProps) {\n const container = useRef<HTMLDivElement>(null);\n\n // Keep the highlighted row visible when the keyboard moves it past the edge.\n useEffect(() => {\n container.current?.children[cursor]?.scrollIntoView({ block: 'nearest' });\n }, [cursor]);\n\n return (\n <div\n ref={container}\n role=\"listbox\"\n aria-label=\"options\"\n data-slot=\"option-list\"\n data-testid={`${testIdPrefix}s`}\n className={cn(optionListVariants({ density }), className)}\n {...props}\n >\n {options.map((option, index) => (\n <OptionRow\n key={option}\n density={density}\n active={index === cursor}\n marker={optionMarker(index)}\n data-testid={`${testIdPrefix}-${String(index)}`}\n title={option}\n onMouseEnter={() => onCursorChange(index)}\n onClick={() => onSelect(option)}\n >\n <OptionLabel density={density}>{option}</OptionLabel>\n </OptionRow>\n ))}\n </div>\n );\n}\n"],"mappings":"gKAMA,MAAa,GAAA,EAAqBA,EAAAA,IAAAA,CAAI,+CAAgD,CACpF,SAAU,CAER,QAAS,CAAE,QAAS,gBAAiB,YAAa,SAAU,CAC9D,EACA,gBAAiB,CAAE,QAAS,aAAc,CAC5C,CAAC,EAEY,GAAA,EAAoBA,EAAAA,IAAAA,CAAI,mEAAoE,CACvG,SAAU,CACR,QAAS,CACP,QAAS,mCACT,YAAa,8FACf,EACA,OAAQ,CAAE,KAAM,GAAI,MAAO,EAAG,CAChC,EACA,iBAAkB,CAChB,CAAE,QAAS,UAAW,OAAQ,GAAM,MAAO,mBAAoB,EAC/D,CAAE,QAAS,cAAe,OAAQ,GAAM,MAAO,yCAA0C,EACzF,CAAE,QAAS,cAAe,OAAQ,GAAO,MAAO,yBAA0B,CAC5E,EACA,gBAAiB,CAAE,QAAS,cAAe,OAAQ,EAAM,CAC3D,CAAC,EAEY,GAAA,EAAuBA,EAAAA,IAAAA,CAAI,mEAAoE,CAC1G,SAAU,CACR,QAAS,CACP,QAAS,uEACT,YAAa,sDACf,CACF,EACA,gBAAiB,CAAE,QAAS,aAAc,CAC5C,CAAC,EAcD,SAAgB,EAAU,CAAE,YAAW,UAAS,SAAQ,SAAQ,WAAU,GAAG,GAAyB,CACpG,MACE,iBAAA,EAAA,EAAA,KAAA,CAAC,SAAD,CACE,KAAK,SACL,KAAK,SACL,YAAU,aACV,gBAAe,GAAU,GACzB,cAAa,GAAU,GACvB,UAAWC,EAAAA,GAAG,EAAkB,CAAE,UAAS,QAAO,CAAC,EAAG,CAAS,EAC/D,GAAI,EAPN,SAAA,CASG,IAAW,IAAA,GAAY,KAAO,iBAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAqB,CAAE,SAAQ,CAAC,EAAI,SAAA,CAAa,CAAA,EAChG,CACK,GAEZ,CAGA,SAAgB,EAAY,CAC1B,YACA,UACA,GAAG,GACgE,CACnE,MACE,iBAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,YAAU,eACV,UAAWA,EAAAA,GACT,iBACA,IAAY,UAAY,kCAAoC,cAC5D,CACF,EACA,GAAI,CACL,CAAA,CAEL,CAuBA,SAAgB,EAAW,CACzB,YACA,UACA,SACA,iBACA,WACA,eACA,UAAU,cACV,GAAG,GACe,CAClB,IAAM,GAAA,EAAYC,EAAAA,OAAAA,CAAuB,IAAI,EAO7C,OAJA,EAAA,EAAA,UAAA,KAAgB,CACd,EAAU,SAAS,SAAS,EAAO,EAAE,eAAe,CAAE,MAAO,SAAU,CAAC,CAC1E,EAAG,CAAC,CAAM,CAAC,EAGT,iBAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,IAAK,EACL,KAAK,UACL,aAAW,UACX,YAAU,cACV,cAAa,GAAG,EAAa,GAC7B,UAAWD,EAAAA,GAAG,EAAmB,CAAE,SAAQ,CAAC,EAAG,CAAS,EACxD,GAAI,EAEH,SAAA,EAAQ,KAAK,EAAQ,IACpB,iBAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CAEW,UACT,OAAQ,IAAU,EAClB,OAAQE,EAAAA,aAAa,CAAK,EAC1B,cAAa,GAAG,EAAa,GAAG,OAAO,CAAK,IAC5C,MAAO,EACP,iBAAoB,EAAe,CAAK,EACxC,YAAe,EAAS,CAAM,EAE9B,SAAA,iBAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CAAsB,UAAU,SAAA,CAAoB,CAAA,CAC3C,EAVJ,CAUI,CACZ,CACE,CAAA,CAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{cn as e}from"../lib/cn.mjs";import{optionMarker as t}from"../lib/optionList.mjs";import{jsx as n,jsxs as r}from"react/jsx-runtime";import{useEffect as i,useRef as a}from"react";import{cva as o}from"class-variance-authority";const s=o(`flex min-h-0 flex-1 flex-col overflow-y-auto`,{variants:{density:{compact:`gap-0.5 p-1.5`,comfortable:`gap-1.5`}},defaultVariants:{density:`comfortable`}}),c=o(`flex shrink-0 cursor-pointer items-center text-left outline-none`,{variants:{density:{compact:`gap-2.5 rounded-md px-2 py-[7px]`,comfortable:`min-h-[42px] gap-3 rounded-md border px-[11px] py-2 text-sm text-doom-text transition-colors`},active:{true:``,false:``}},compoundVariants:[{density:`compact`,active:!0,class:`bg-doom-tint-blue`},{density:`comfortable`,active:!0,class:`border-doom-edge-blue bg-doom-tint-blue`},{density:`comfortable`,active:!1,class:`border-doom-border-soft`}],defaultVariants:{density:`comfortable`,active:!1}}),l=o(`flex shrink-0 items-center justify-center rounded-full font-bold`,{variants:{density:{compact:`h-[15px] w-[15px] border border-doom-border text-2xs text-doom-faint`,comfortable:`h-[18px] w-[18px] bg-doom-deep text-xs text-doom-dim`}},defaultVariants:{density:`comfortable`}});function u({className:t,density:i,active:a,marker:o,children:s,...u}){return r(`button`,{type:`button`,role:`option`,"data-slot":`option-row`,"aria-selected":a??!1,"data-cursor":a??!1,className:e(c({density:i,active:a}),t),...u,children:[o===void 0?null
|
|
1
|
+
import{cn as e}from"../lib/cn.mjs";import{optionMarker as t}from"../lib/optionList.mjs";import{jsx as n,jsxs as r}from"react/jsx-runtime";import{useEffect as i,useRef as a}from"react";import{cva as o}from"class-variance-authority";const s=o(`flex min-h-0 flex-1 flex-col overflow-y-auto`,{variants:{density:{compact:`gap-0.5 p-1.5`,comfortable:`gap-1.5`}},defaultVariants:{density:`comfortable`}}),c=o(`flex shrink-0 cursor-pointer items-center text-left outline-none`,{variants:{density:{compact:`gap-2.5 rounded-md px-2 py-[7px]`,comfortable:`min-h-[42px] gap-3 rounded-md border px-[11px] py-2 text-sm text-doom-text transition-colors`},active:{true:``,false:``}},compoundVariants:[{density:`compact`,active:!0,class:`bg-doom-tint-blue`},{density:`comfortable`,active:!0,class:`border-doom-edge-blue bg-doom-tint-blue`},{density:`comfortable`,active:!1,class:`border-doom-border-soft`}],defaultVariants:{density:`comfortable`,active:!1}}),l=o(`flex shrink-0 items-center justify-center rounded-full font-bold`,{variants:{density:{compact:`h-[15px] w-[15px] border border-doom-border text-2xs text-doom-faint`,comfortable:`h-[18px] w-[18px] bg-doom-deep text-xs text-doom-dim`}},defaultVariants:{density:`comfortable`}});function u({className:t,density:i,active:a,marker:o,children:s,...u}){return/* @__PURE__ */ r(`button`,{type:`button`,role:`option`,"data-slot":`option-row`,"aria-selected":a??!1,"data-cursor":a??!1,className:e(c({density:i,active:a}),t),...u,children:[o===void 0?null:/* @__PURE__ */ n(`span`,{className:l({density:i}),children:o}),s]})}function d({className:t,density:r,...i}){return/* @__PURE__ */ n(`span`,{"data-slot":`option-label`,className:e(`min-w-0 flex-1`,r===`compact`?`truncate text-sm text-doom-text`:`break-words`,t),...i})}function f({className:r,options:o,cursor:c,onCursorChange:l,onSelect:f,testIdPrefix:p,density:m=`comfortable`,...h}){let g=a(null);return i(()=>{g.current?.children[c]?.scrollIntoView({block:`nearest`})},[c]),/* @__PURE__ */ n(`div`,{ref:g,role:`listbox`,"aria-label":`options`,"data-slot":`option-list`,"data-testid":`${p}s`,className:e(s({density:m}),r),...h,children:o.map((e,r)=>/* @__PURE__ */ n(u,{density:m,active:r===c,marker:t(r),"data-testid":`${p}-${String(r)}`,title:e,onMouseEnter:()=>l(r),onClick:()=>f(e),children:/* @__PURE__ */ n(d,{density:m,children:e})},e))})}export{d as OptionLabel,f as OptionList,u as OptionRow,s as optionListVariants,l as optionMarkerVariants,c as optionRowVariants};
|
|
2
2
|
//# sourceMappingURL=OptionList.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OptionList.mjs","names":[],"sources":["../../../src/components/OptionList.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, type ReactNode, useEffect, useRef } from 'react';\n\nimport { cn } from '../lib/cn';\nimport { optionMarker } from '../lib/optionList';\n\nexport const optionListVariants = cva('flex min-h-0 flex-1 flex-col overflow-y-auto', {\n variants: {\n /** The compact rows of a bar popover, or the roomier rows of a modal. */\n density: { compact: 'gap-0.5 p-1.5', comfortable: 'gap-1.5' },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nexport const optionRowVariants = cva('flex shrink-0 cursor-pointer items-center text-left outline-none', {\n variants: {\n density: {\n compact: 'gap-2.5 rounded-md px-2 py-[7px]',\n comfortable: 'min-h-[42px] gap-3 rounded-md border px-[11px] py-2 text-sm text-doom-text transition-colors',\n },\n active: { true: '', false: '' },\n },\n compoundVariants: [\n { density: 'compact', active: true, class: 'bg-doom-tint-blue' },\n { density: 'comfortable', active: true, class: 'border-doom-edge-blue bg-doom-tint-blue' },\n { density: 'comfortable', active: false, class: 'border-doom-border-soft' },\n ],\n defaultVariants: { density: 'comfortable', active: false },\n});\n\nexport const optionMarkerVariants = cva('flex shrink-0 items-center justify-center rounded-full font-bold', {\n variants: {\n density: {\n compact: 'h-[15px] w-[15px] border border-doom-border text-2xs text-doom-faint',\n comfortable: 'h-[18px] w-[18px] bg-doom-deep text-xs text-doom-dim',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nexport interface OptionRowProps extends ComponentProps<'button'>, VariantProps<typeof optionRowVariants> {\n /** The digit or bullet in the row's leading circle; omit it for a row with no shortcut. */\n marker?: ReactNode;\n}\n\n/**\n * One row of an option list, exported on its own so a surface with richer\n * rows than a plain string still wears the list's shape instead of restating\n * it. Children render straight into the row, so a row is free to hold a name\n * and a description side by side; OptionLabel is the single-string case.\n * `role=\"option\"` means a row belongs inside a `listbox`.\n */\nexport function OptionRow({ className, density, active, marker, children, ...props }: OptionRowProps) {\n return (\n <button\n type=\"button\"\n role=\"option\"\n data-slot=\"option-row\"\n aria-selected={active ?? false}\n data-cursor={active ?? false}\n className={cn(optionRowVariants({ density, active }), className)}\n {...props}\n >\n {marker === undefined ? null : <span className={optionMarkerVariants({ density })}>{marker}</span>}\n {children}\n </button>\n );\n}\n\n/** A row's single-string label: it takes the remaining width and clips or wraps by density. */\nexport function OptionLabel({\n className,\n density,\n ...props\n}: ComponentProps<'span'> & VariantProps<typeof optionListVariants>) {\n return (\n <span\n data-slot=\"option-label\"\n className={cn(\n 'min-w-0 flex-1',\n density === 'compact' ? 'truncate text-sm text-doom-text' : 'break-words',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface OptionListProps\n extends Omit<ComponentProps<'div'>, 'onSelect'>, VariantProps<typeof optionListVariants> {\n options: readonly string[];\n /** The highlighted row, owned by the surface so its own focused element can drive it. */\n cursor: number;\n onCursorChange: (cursor: number) => void;\n onSelect: (option: string) => void;\n /** Rows are `${testIdPrefix}-${index}`; the list itself is the plural, so a prefix query for a row never catches it. */\n testIdPrefix: string;\n}\n\n/**\n * The answers a select surface offers, wherever it is framed.\n *\n * The agent decides how many options there are, and a real one runs past\n * twenty, so the list cannot rely on the digit shortcuts alone. The cursor is\n * controlled rather than owned here because the surface around it is what the\n * overlay library focuses: keys arrive there, and taking focus away from it to\n * a list would fight the library for it and break the escape and outside-click\n * behaviour that focus carries. Pair it with handleOptionListKey.\n */\nexport function OptionList({\n className,\n options,\n cursor,\n onCursorChange,\n onSelect,\n testIdPrefix,\n density = 'comfortable',\n ...props\n}: OptionListProps) {\n const container = useRef<HTMLDivElement>(null);\n\n // Keep the highlighted row visible when the keyboard moves it past the edge.\n useEffect(() => {\n container.current?.children[cursor]?.scrollIntoView({ block: 'nearest' });\n }, [cursor]);\n\n return (\n <div\n ref={container}\n role=\"listbox\"\n aria-label=\"options\"\n data-slot=\"option-list\"\n data-testid={`${testIdPrefix}s`}\n className={cn(optionListVariants({ density }), className)}\n {...props}\n >\n {options.map((option, index) => (\n <OptionRow\n key={option}\n density={density}\n active={index === cursor}\n marker={optionMarker(index)}\n data-testid={`${testIdPrefix}-${String(index)}`}\n title={option}\n onMouseEnter={() => onCursorChange(index)}\n onClick={() => onSelect(option)}\n >\n <OptionLabel density={density}>{option}</OptionLabel>\n </OptionRow>\n ))}\n </div>\n );\n}\n"],"mappings":"uOAMA,MAAa,EAAqB,EAAI,+CAAgD,CACpF,SAAU,CAER,QAAS,CAAE,QAAS,gBAAiB,YAAa,SAAU,CAC9D,EACA,gBAAiB,CAAE,QAAS,aAAc,CAC5C,CAAC,EAEY,EAAoB,EAAI,mEAAoE,CACvG,SAAU,CACR,QAAS,CACP,QAAS,mCACT,YAAa,8FACf,EACA,OAAQ,CAAE,KAAM,GAAI,MAAO,EAAG,CAChC,EACA,iBAAkB,CAChB,CAAE,QAAS,UAAW,OAAQ,GAAM,MAAO,mBAAoB,EAC/D,CAAE,QAAS,cAAe,OAAQ,GAAM,MAAO,yCAA0C,EACzF,CAAE,QAAS,cAAe,OAAQ,GAAO,MAAO,yBAA0B,CAC5E,EACA,gBAAiB,CAAE,QAAS,cAAe,OAAQ,EAAM,CAC3D,CAAC,EAEY,EAAuB,EAAI,mEAAoE,CAC1G,SAAU,CACR,QAAS,CACP,QAAS,uEACT,YAAa,sDACf,CACF,EACA,gBAAiB,CAAE,QAAS,aAAc,CAC5C,CAAC,EAcD,SAAgB,EAAU,CAAE,YAAW,UAAS,SAAQ,SAAQ,WAAU,GAAG,GAAyB,CACpG,
|
|
1
|
+
{"version":3,"file":"OptionList.mjs","names":[],"sources":["../../../src/components/OptionList.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, type ReactNode, useEffect, useRef } from 'react';\n\nimport { cn } from '../lib/cn';\nimport { optionMarker } from '../lib/optionList';\n\nexport const optionListVariants = cva('flex min-h-0 flex-1 flex-col overflow-y-auto', {\n variants: {\n /** The compact rows of a bar popover, or the roomier rows of a modal. */\n density: { compact: 'gap-0.5 p-1.5', comfortable: 'gap-1.5' },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nexport const optionRowVariants = cva('flex shrink-0 cursor-pointer items-center text-left outline-none', {\n variants: {\n density: {\n compact: 'gap-2.5 rounded-md px-2 py-[7px]',\n comfortable: 'min-h-[42px] gap-3 rounded-md border px-[11px] py-2 text-sm text-doom-text transition-colors',\n },\n active: { true: '', false: '' },\n },\n compoundVariants: [\n { density: 'compact', active: true, class: 'bg-doom-tint-blue' },\n { density: 'comfortable', active: true, class: 'border-doom-edge-blue bg-doom-tint-blue' },\n { density: 'comfortable', active: false, class: 'border-doom-border-soft' },\n ],\n defaultVariants: { density: 'comfortable', active: false },\n});\n\nexport const optionMarkerVariants = cva('flex shrink-0 items-center justify-center rounded-full font-bold', {\n variants: {\n density: {\n compact: 'h-[15px] w-[15px] border border-doom-border text-2xs text-doom-faint',\n comfortable: 'h-[18px] w-[18px] bg-doom-deep text-xs text-doom-dim',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nexport interface OptionRowProps extends ComponentProps<'button'>, VariantProps<typeof optionRowVariants> {\n /** The digit or bullet in the row's leading circle; omit it for a row with no shortcut. */\n marker?: ReactNode;\n}\n\n/**\n * One row of an option list, exported on its own so a surface with richer\n * rows than a plain string still wears the list's shape instead of restating\n * it. Children render straight into the row, so a row is free to hold a name\n * and a description side by side; OptionLabel is the single-string case.\n * `role=\"option\"` means a row belongs inside a `listbox`.\n */\nexport function OptionRow({ className, density, active, marker, children, ...props }: OptionRowProps) {\n return (\n <button\n type=\"button\"\n role=\"option\"\n data-slot=\"option-row\"\n aria-selected={active ?? false}\n data-cursor={active ?? false}\n className={cn(optionRowVariants({ density, active }), className)}\n {...props}\n >\n {marker === undefined ? null : <span className={optionMarkerVariants({ density })}>{marker}</span>}\n {children}\n </button>\n );\n}\n\n/** A row's single-string label: it takes the remaining width and clips or wraps by density. */\nexport function OptionLabel({\n className,\n density,\n ...props\n}: ComponentProps<'span'> & VariantProps<typeof optionListVariants>) {\n return (\n <span\n data-slot=\"option-label\"\n className={cn(\n 'min-w-0 flex-1',\n density === 'compact' ? 'truncate text-sm text-doom-text' : 'break-words',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface OptionListProps\n extends Omit<ComponentProps<'div'>, 'onSelect'>, VariantProps<typeof optionListVariants> {\n options: readonly string[];\n /** The highlighted row, owned by the surface so its own focused element can drive it. */\n cursor: number;\n onCursorChange: (cursor: number) => void;\n onSelect: (option: string) => void;\n /** Rows are `${testIdPrefix}-${index}`; the list itself is the plural, so a prefix query for a row never catches it. */\n testIdPrefix: string;\n}\n\n/**\n * The answers a select surface offers, wherever it is framed.\n *\n * The agent decides how many options there are, and a real one runs past\n * twenty, so the list cannot rely on the digit shortcuts alone. The cursor is\n * controlled rather than owned here because the surface around it is what the\n * overlay library focuses: keys arrive there, and taking focus away from it to\n * a list would fight the library for it and break the escape and outside-click\n * behaviour that focus carries. Pair it with handleOptionListKey.\n */\nexport function OptionList({\n className,\n options,\n cursor,\n onCursorChange,\n onSelect,\n testIdPrefix,\n density = 'comfortable',\n ...props\n}: OptionListProps) {\n const container = useRef<HTMLDivElement>(null);\n\n // Keep the highlighted row visible when the keyboard moves it past the edge.\n useEffect(() => {\n container.current?.children[cursor]?.scrollIntoView({ block: 'nearest' });\n }, [cursor]);\n\n return (\n <div\n ref={container}\n role=\"listbox\"\n aria-label=\"options\"\n data-slot=\"option-list\"\n data-testid={`${testIdPrefix}s`}\n className={cn(optionListVariants({ density }), className)}\n {...props}\n >\n {options.map((option, index) => (\n <OptionRow\n key={option}\n density={density}\n active={index === cursor}\n marker={optionMarker(index)}\n data-testid={`${testIdPrefix}-${String(index)}`}\n title={option}\n onMouseEnter={() => onCursorChange(index)}\n onClick={() => onSelect(option)}\n >\n <OptionLabel density={density}>{option}</OptionLabel>\n </OptionRow>\n ))}\n </div>\n );\n}\n"],"mappings":"uOAMA,MAAa,EAAqB,EAAI,+CAAgD,CACpF,SAAU,CAER,QAAS,CAAE,QAAS,gBAAiB,YAAa,SAAU,CAC9D,EACA,gBAAiB,CAAE,QAAS,aAAc,CAC5C,CAAC,EAEY,EAAoB,EAAI,mEAAoE,CACvG,SAAU,CACR,QAAS,CACP,QAAS,mCACT,YAAa,8FACf,EACA,OAAQ,CAAE,KAAM,GAAI,MAAO,EAAG,CAChC,EACA,iBAAkB,CAChB,CAAE,QAAS,UAAW,OAAQ,GAAM,MAAO,mBAAoB,EAC/D,CAAE,QAAS,cAAe,OAAQ,GAAM,MAAO,yCAA0C,EACzF,CAAE,QAAS,cAAe,OAAQ,GAAO,MAAO,yBAA0B,CAC5E,EACA,gBAAiB,CAAE,QAAS,cAAe,OAAQ,EAAM,CAC3D,CAAC,EAEY,EAAuB,EAAI,mEAAoE,CAC1G,SAAU,CACR,QAAS,CACP,QAAS,uEACT,YAAa,sDACf,CACF,EACA,gBAAiB,CAAE,QAAS,aAAc,CAC5C,CAAC,EAcD,SAAgB,EAAU,CAAE,YAAW,UAAS,SAAQ,SAAQ,WAAU,GAAG,GAAyB,CACpG,MACE,kBAAC,SAAD,CACE,KAAK,SACL,KAAK,SACL,YAAU,aACV,gBAAe,GAAU,GACzB,cAAa,GAAU,GACvB,UAAW,EAAG,EAAkB,CAAE,UAAS,QAAO,CAAC,EAAG,CAAS,EAC/D,GAAI,EAPN,SAAA,CASG,IAAW,IAAA,GAAY,KAAO,kBAAC,OAAD,CAAM,UAAW,EAAqB,CAAE,SAAQ,CAAC,EAAI,SAAA,CAAa,CAAA,EAChG,CACK,GAEZ,CAGA,SAAgB,EAAY,CAC1B,YACA,UACA,GAAG,GACgE,CACnE,MACE,kBAAC,OAAD,CACE,YAAU,eACV,UAAW,EACT,iBACA,IAAY,UAAY,kCAAoC,cAC5D,CACF,EACA,GAAI,CACL,CAAA,CAEL,CAuBA,SAAgB,EAAW,CACzB,YACA,UACA,SACA,iBACA,WACA,eACA,UAAU,cACV,GAAG,GACe,CAClB,IAAM,EAAY,EAAuB,IAAI,EAO7C,OAJA,MAAgB,CACd,EAAU,SAAS,SAAS,EAAO,EAAE,eAAe,CAAE,MAAO,SAAU,CAAC,CAC1E,EAAG,CAAC,CAAM,CAAC,EAGT,kBAAC,MAAD,CACE,IAAK,EACL,KAAK,UACL,aAAW,UACX,YAAU,cACV,cAAa,GAAG,EAAa,GAC7B,UAAW,EAAG,EAAmB,CAAE,SAAQ,CAAC,EAAG,CAAS,EACxD,GAAI,EAEH,SAAA,EAAQ,KAAK,EAAQ,IACpB,kBAAC,EAAD,CAEW,UACT,OAAQ,IAAU,EAClB,OAAQ,EAAa,CAAK,EAC1B,cAAa,GAAG,EAAa,GAAG,OAAO,CAAK,IAC5C,MAAO,EACP,iBAAoB,EAAe,CAAK,EACxC,YAAe,EAAS,CAAM,EAE9B,SAAA,kBAAC,EAAD,CAAsB,UAAU,SAAA,CAAoB,CAAA,CAC3C,EAVJ,CAUI,CACZ,CACE,CAAA,CAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../lib/cn.cjs");let t=require("radix-ui"),n=require("react/jsx-runtime");function r({className:r,asChild:i=!1,...a}){let o=i?t.Slot.Root:`div`;return(0,n.jsx)(o,{"data-slot":`panel`,className:e.cn(`overflow-hidden rounded-md border border-doom-border bg-doom-panel`,r),...a})}function i({className:t,...r}){return(0,n.jsx)(`div`,{"data-slot":`panel-header`,className:e.cn(`flex min-h-8 items-center gap-2 px-[11px]`,t),...r})}function a({className:t,...r}){return(0,n.jsx)(`div`,{"data-slot":`panel-body`,className:e.cn(`border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim`,t),...r})}exports.Panel=r,exports.PanelBody=a,exports.PanelHeader=i;
|
|
1
|
+
const e=require("../lib/cn.cjs");let t=require("radix-ui"),n=require("react/jsx-runtime");function r({className:r,asChild:i=!1,...a}){let o=i?t.Slot.Root:`div`;return/* @__PURE__ */ (0,n.jsx)(o,{"data-slot":`panel`,className:e.cn(`overflow-hidden rounded-md border border-doom-border bg-doom-panel`,r),...a})}function i({className:t,...r}){return/* @__PURE__ */ (0,n.jsx)(`div`,{"data-slot":`panel-header`,className:e.cn(`flex min-h-8 items-center gap-2 px-[11px]`,t),...r})}function a({className:t,...r}){return/* @__PURE__ */ (0,n.jsx)(`div`,{"data-slot":`panel-body`,className:e.cn(`border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim`,t),...r})}exports.Panel=r,exports.PanelBody=a,exports.PanelHeader=i;
|
|
2
2
|
//# sourceMappingURL=Panel.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Panel.cjs","names":["Slot","cn"],"sources":["../../../src/components/Panel.tsx"],"sourcesContent":["import { Slot } from 'radix-ui';\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\nexport interface PanelProps extends ComponentProps<'div'> {\n /** Render the child element instead of a <div>, so a list row can be a panel. */\n asChild?: boolean;\n}\n\n/** The bordered card surface: a tool card, a run card, a provider row. */\nexport function Panel({ className, asChild = false, ...props }: PanelProps) {\n const Component = asChild ? Slot.Root : 'div';\n return (\n <Component\n data-slot=\"panel\"\n className={cn('overflow-hidden rounded-md border border-doom-border bg-doom-panel', className)}\n {...props}\n />\n );\n}\n\n/** A panel's title row. */\nexport function PanelHeader({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div data-slot=\"panel-header\" className={cn('flex min-h-8 items-center gap-2 px-[11px]', className)} {...props} />\n );\n}\n\n/** A panel's recessed body: the deep well the header sits above. */\nexport function PanelBody({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"panel-body\"\n className={cn('border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim', className)}\n {...props}\n />\n );\n}\n"],"mappings":"0FAWA,SAAgB,EAAM,CAAE,YAAW,UAAU,GAAO,GAAG,GAAqB,CAC1E,IAAM,EAAY,EAAUA,EAAAA,KAAK,KAAO,MACxC,
|
|
1
|
+
{"version":3,"file":"Panel.cjs","names":["Slot","cn"],"sources":["../../../src/components/Panel.tsx"],"sourcesContent":["import { Slot } from 'radix-ui';\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\nexport interface PanelProps extends ComponentProps<'div'> {\n /** Render the child element instead of a <div>, so a list row can be a panel. */\n asChild?: boolean;\n}\n\n/** The bordered card surface: a tool card, a run card, a provider row. */\nexport function Panel({ className, asChild = false, ...props }: PanelProps) {\n const Component = asChild ? Slot.Root : 'div';\n return (\n <Component\n data-slot=\"panel\"\n className={cn('overflow-hidden rounded-md border border-doom-border bg-doom-panel', className)}\n {...props}\n />\n );\n}\n\n/** A panel's title row. */\nexport function PanelHeader({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div data-slot=\"panel-header\" className={cn('flex min-h-8 items-center gap-2 px-[11px]', className)} {...props} />\n );\n}\n\n/** A panel's recessed body: the deep well the header sits above. */\nexport function PanelBody({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"panel-body\"\n className={cn('border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim', className)}\n {...props}\n />\n );\n}\n"],"mappings":"0FAWA,SAAgB,EAAM,CAAE,YAAW,UAAU,GAAO,GAAG,GAAqB,CAC1E,IAAM,EAAY,EAAUA,EAAAA,KAAK,KAAO,MACxC,MACE,iBAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,YAAU,QACV,UAAWC,EAAAA,GAAG,qEAAsE,CAAS,EAC7F,GAAI,CACL,CAAA,CAEL,CAGA,SAAgB,EAAY,CAAE,YAAW,GAAG,GAAgC,CAC1E,MACE,iBAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,YAAU,eAAe,UAAWA,EAAAA,GAAG,4CAA6C,CAAS,EAAG,GAAI,CAAQ,CAAA,CAErH,CAGA,SAAgB,EAAU,CAAE,YAAW,GAAG,GAAgC,CACxE,MACE,iBAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,YAAU,aACV,UAAWA,EAAAA,GAAG,gFAAiF,CAAS,EACxG,GAAI,CACL,CAAA,CAEL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{cn as e}from"../lib/cn.mjs";import{Slot as t}from"radix-ui";import{jsx as n}from"react/jsx-runtime";function r({className:r,asChild:i=!1,...a}){let o=i?t.Root:`div`;return n(o,{"data-slot":`panel`,className:e(`overflow-hidden rounded-md border border-doom-border bg-doom-panel`,r),...a})}function i({className:t,...r}){return n(`div`,{"data-slot":`panel-header`,className:e(`flex min-h-8 items-center gap-2 px-[11px]`,t),...r})}function a({className:t,...r}){return n(`div`,{"data-slot":`panel-body`,className:e(`border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim`,t),...r})}export{r as Panel,a as PanelBody,i as PanelHeader};
|
|
1
|
+
import{cn as e}from"../lib/cn.mjs";import{Slot as t}from"radix-ui";import{jsx as n}from"react/jsx-runtime";function r({className:r,asChild:i=!1,...a}){let o=i?t.Root:`div`;return/* @__PURE__ */ n(o,{"data-slot":`panel`,className:e(`overflow-hidden rounded-md border border-doom-border bg-doom-panel`,r),...a})}function i({className:t,...r}){return/* @__PURE__ */ n(`div`,{"data-slot":`panel-header`,className:e(`flex min-h-8 items-center gap-2 px-[11px]`,t),...r})}function a({className:t,...r}){return/* @__PURE__ */ n(`div`,{"data-slot":`panel-body`,className:e(`border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim`,t),...r})}export{r as Panel,a as PanelBody,i as PanelHeader};
|
|
2
2
|
//# sourceMappingURL=Panel.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Panel.mjs","names":[],"sources":["../../../src/components/Panel.tsx"],"sourcesContent":["import { Slot } from 'radix-ui';\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\nexport interface PanelProps extends ComponentProps<'div'> {\n /** Render the child element instead of a <div>, so a list row can be a panel. */\n asChild?: boolean;\n}\n\n/** The bordered card surface: a tool card, a run card, a provider row. */\nexport function Panel({ className, asChild = false, ...props }: PanelProps) {\n const Component = asChild ? Slot.Root : 'div';\n return (\n <Component\n data-slot=\"panel\"\n className={cn('overflow-hidden rounded-md border border-doom-border bg-doom-panel', className)}\n {...props}\n />\n );\n}\n\n/** A panel's title row. */\nexport function PanelHeader({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div data-slot=\"panel-header\" className={cn('flex min-h-8 items-center gap-2 px-[11px]', className)} {...props} />\n );\n}\n\n/** A panel's recessed body: the deep well the header sits above. */\nexport function PanelBody({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"panel-body\"\n className={cn('border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim', className)}\n {...props}\n />\n );\n}\n"],"mappings":"2GAWA,SAAgB,EAAM,CAAE,YAAW,UAAU,GAAO,GAAG,GAAqB,CAC1E,IAAM,EAAY,EAAU,EAAK,KAAO,MACxC,
|
|
1
|
+
{"version":3,"file":"Panel.mjs","names":[],"sources":["../../../src/components/Panel.tsx"],"sourcesContent":["import { Slot } from 'radix-ui';\nimport type { ComponentProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\nexport interface PanelProps extends ComponentProps<'div'> {\n /** Render the child element instead of a <div>, so a list row can be a panel. */\n asChild?: boolean;\n}\n\n/** The bordered card surface: a tool card, a run card, a provider row. */\nexport function Panel({ className, asChild = false, ...props }: PanelProps) {\n const Component = asChild ? Slot.Root : 'div';\n return (\n <Component\n data-slot=\"panel\"\n className={cn('overflow-hidden rounded-md border border-doom-border bg-doom-panel', className)}\n {...props}\n />\n );\n}\n\n/** A panel's title row. */\nexport function PanelHeader({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div data-slot=\"panel-header\" className={cn('flex min-h-8 items-center gap-2 px-[11px]', className)} {...props} />\n );\n}\n\n/** A panel's recessed body: the deep well the header sits above. */\nexport function PanelBody({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"panel-body\"\n className={cn('border-t border-doom-border-soft bg-doom-deep px-3 py-2 text-sm text-doom-dim', className)}\n {...props}\n />\n );\n}\n"],"mappings":"2GAWA,SAAgB,EAAM,CAAE,YAAW,UAAU,GAAO,GAAG,GAAqB,CAC1E,IAAM,EAAY,EAAU,EAAK,KAAO,MACxC,MACE,kBAAC,EAAD,CACE,YAAU,QACV,UAAW,EAAG,qEAAsE,CAAS,EAC7F,GAAI,CACL,CAAA,CAEL,CAGA,SAAgB,EAAY,CAAE,YAAW,GAAG,GAAgC,CAC1E,MACE,kBAAC,MAAD,CAAK,YAAU,eAAe,UAAW,EAAG,4CAA6C,CAAS,EAAG,GAAI,CAAQ,CAAA,CAErH,CAGA,SAAgB,EAAU,CAAE,YAAW,GAAG,GAAgC,CACxE,MACE,kBAAC,MAAD,CACE,YAAU,aACV,UAAW,EAAG,gFAAiF,CAAS,EACxG,GAAI,CACL,CAAA,CAEL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../lib/cn.cjs"),t=require("./Button.cjs");let n=require("react/jsx-runtime"),r=require("react");const i={page:1,pageCount:0,sourceWidth:0,sourceHeight:0};function a(e,t){return!Number.isFinite(e)||t<=0?1:Math.min(t,Math.max(1,Math.trunc(e)))}function o(e,t,n){if(t.width<=0||t.height<=0)return null;let r=Math.max(t.left,Math.min(t.right,n.left)),i=Math.max(t.left,Math.min(t.right,n.right)),a=Math.max(t.top,Math.min(t.bottom,n.top)),o=Math.max(t.top,Math.min(t.bottom,n.bottom));return i<=r||o<=a?null:{page:e,rect:{x:(r-t.left)/t.width,y:(a-t.top)/t.height,width:(i-r)/t.width,height:(o-a)/t.height}}}function s(e,t,n){return new Promise(r=>e.toBlob(r,t,n))}function c({src:c,path:l,className:u,controllerRef:d,"data-testid":f}){let p=(0,r.useRef)(null),m=(0,r.useRef)(null),h=(0,r.useRef)(null),[g,_]=(0,r.useState)(1),[v,y]=(0,r.useState)(i),[b,x]=(0,r.useState)();return(0,r.useEffect)(()=>{let e=!1,t;return import(`pdfjs-dist/webpack.mjs`).then(({getDocument:n})=>{if(!e)return t=n({url:c}),t.promise.then(t=>{e||(m.current=t,x(void 0),_(1),y({...i,pageCount:t.numPages}))})}).catch(t=>{e||x(t instanceof Error?t.message:String(t))}),()=>{e=!0,t?.destroy(),h.current?.cancel(),h.current=null,m.current=null}},[c]),(0,r.useEffect)(()=>{let e=m.current,t=p.current;if(e===null||t===null)return;let n=!1;return e.getPage(g).then(r=>{if(n)return;let i=r.getViewport({scale:1}),a=r.getViewport({scale:1.5});t.width=Math.ceil(a.width),t.height=Math.ceil(a.height),y({page:g,pageCount:e.numPages,sourceWidth:i.width,sourceHeight:i.height});let o=t.getContext(`2d`);if(o===null)return;let s=r.render({canvas:t,canvasContext:o,viewport:a});return h.current=s,s.promise}).catch(e=>{!n&&e.name!==`RenderingCancelledException`&&x(e instanceof Error?e.message:String(e))}),()=>{n=!0,h.current?.cancel(),h.current=null}},[g,v.pageCount]),(0,r.useImperativeHandle)(d,()=>({getState:()=>v,setPage:e=>_(a(e,v.pageCount)),resolveViewportRegion:e=>{let t=p.current;return t===null||v.pageCount===0?null:o(v.page,t.getBoundingClientRect(),e)},capturePage:async(e=`image/png`,t)=>{let n=p.current;return n===null?null:await s(n,e,t)}}),[v])
|
|
1
|
+
const e=require("../lib/cn.cjs"),t=require("./Button.cjs");let n=require("react/jsx-runtime"),r=require("react");const i={page:1,pageCount:0,sourceWidth:0,sourceHeight:0};function a(e,t){return!Number.isFinite(e)||t<=0?1:Math.min(t,Math.max(1,Math.trunc(e)))}function o(e,t,n){if(t.width<=0||t.height<=0)return null;let r=Math.max(t.left,Math.min(t.right,n.left)),i=Math.max(t.left,Math.min(t.right,n.right)),a=Math.max(t.top,Math.min(t.bottom,n.top)),o=Math.max(t.top,Math.min(t.bottom,n.bottom));return i<=r||o<=a?null:{page:e,rect:{x:(r-t.left)/t.width,y:(a-t.top)/t.height,width:(i-r)/t.width,height:(o-a)/t.height}}}function s(e,t,n){return new Promise(r=>e.toBlob(r,t,n))}function c({src:c,path:l,className:u,controllerRef:d,"data-testid":f}){let p=(0,r.useRef)(null),m=(0,r.useRef)(null),h=(0,r.useRef)(null),[g,_]=(0,r.useState)(1),[v,y]=(0,r.useState)(i),[b,x]=(0,r.useState)();return(0,r.useEffect)(()=>{let e=!1,t;return import(`pdfjs-dist/webpack.mjs`).then(({getDocument:n})=>{if(!e)return t=n({url:c}),t.promise.then(t=>{e||(m.current=t,x(void 0),_(1),y({...i,pageCount:t.numPages}))})}).catch(t=>{e||x(t instanceof Error?t.message:String(t))}),()=>{e=!0,t?.destroy(),h.current?.cancel(),h.current=null,m.current=null}},[c]),(0,r.useEffect)(()=>{let e=m.current,t=p.current;if(e===null||t===null)return;let n=!1;return e.getPage(g).then(r=>{if(n)return;let i=r.getViewport({scale:1}),a=r.getViewport({scale:1.5});t.width=Math.ceil(a.width),t.height=Math.ceil(a.height),y({page:g,pageCount:e.numPages,sourceWidth:i.width,sourceHeight:i.height});let o=t.getContext(`2d`);if(o===null)return;let s=r.render({canvas:t,canvasContext:o,viewport:a});return h.current=s,s.promise}).catch(e=>{!n&&e.name!==`RenderingCancelledException`&&x(e instanceof Error?e.message:String(e))}),()=>{n=!0,h.current?.cancel(),h.current=null}},[g,v.pageCount]),(0,r.useImperativeHandle)(d,()=>({getState:()=>v,setPage:e=>_(a(e,v.pageCount)),resolveViewportRegion:e=>{let t=p.current;return t===null||v.pageCount===0?null:o(v.page,t.getBoundingClientRect(),e)},capturePage:async(e=`image/png`,t)=>{let n=p.current;return n===null?null:await s(n,e,t)}}),[v]),/* @__PURE__ */ (0,n.jsxs)(`section`,{"data-testid":f,"data-kind":`pdf`,className:e.cn(`flex min-h-0 flex-col gap-2`,u),children:[/* @__PURE__ */ (0,n.jsxs)(`div`,{className:`flex items-center justify-between gap-2 text-xs text-doom-faint`,children:[/* @__PURE__ */ (0,n.jsx)(`span`,{children:l}),/* @__PURE__ */ (0,n.jsxs)(`div`,{className:`flex items-center gap-2`,children:[/* @__PURE__ */ (0,n.jsx)(t.Button,{size:`xs`,variant:`ghost`,disabled:g<=1,onClick:()=>_(e=>Math.max(1,e-1)),children:`previous`}),/* @__PURE__ */ (0,n.jsx)(`span`,{"data-testid":f===void 0?void 0:`${f}-page`,children:v.pageCount===0?`loading`:`${g} / ${v.pageCount}`}),/* @__PURE__ */ (0,n.jsx)(t.Button,{size:`xs`,variant:`ghost`,disabled:v.pageCount===0||g>=v.pageCount,onClick:()=>_(e=>a(e+1,v.pageCount)),children:`next`})]})]}),b===void 0?null:/* @__PURE__ */ (0,n.jsx)(`output`,{className:`text-xs text-doom-red`,children:b}),/* @__PURE__ */ (0,n.jsx)(`div`,{className:`min-h-0 overflow-auto rounded border border-doom-border bg-doom-deep`,children:/* @__PURE__ */ (0,n.jsx)(`canvas`,{ref:p,"aria-label":l,className:`mx-auto block max-w-full`})})]})}exports.PdfPreview=c,exports.resolvePdfViewportRegion=o;
|
|
2
2
|
//# sourceMappingURL=PdfPreview.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PdfPreview.cjs","names":["useRef","useState","cn","Button"],"sources":["../../../src/components/PdfPreview.tsx"],"sourcesContent":["import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist/webpack.mjs';\nimport { useEffect, useImperativeHandle, useRef, useState } from 'react';\n\nimport { cn } from '../lib/cn';\nimport { Button } from './Button';\n\ntype PdfLoadingTask = ReturnType<typeof import('pdfjs-dist/webpack.mjs').getDocument>;\n\nexport interface PdfNormalizedRectangle {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface PdfPageState {\n page: number;\n pageCount: number;\n sourceWidth: number;\n sourceHeight: number;\n}\n\nexport interface PdfPageRegion {\n page: number;\n rect: PdfNormalizedRectangle;\n}\n\nexport interface PdfPreviewController {\n getState: () => PdfPageState;\n setPage: (page: number) => void;\n resolveViewportRegion: (rectangle: {\n left: number;\n top: number;\n right: number;\n bottom: number;\n }) => PdfPageRegion | null;\n capturePage: (type?: 'image/png' | 'image/jpeg', quality?: number) => Promise<Blob | null>;\n}\n\nexport interface PdfPreviewProps {\n src: string;\n path: string;\n className?: string;\n 'data-testid'?: string;\n controllerRef?: import('react').Ref<PdfPreviewController>;\n}\n\nconst EMPTY_STATE: PdfPageState = { page: 1, pageCount: 0, sourceWidth: 0, sourceHeight: 0 };\n\nfunction boundedPage(page: number, pageCount: number): number {\n if (!Number.isFinite(page) || pageCount <= 0) return 1;\n return Math.min(pageCount, Math.max(1, Math.trunc(page)));\n}\n\nexport function resolvePdfViewportRegion(\n page: number,\n bounds: { left: number; top: number; right: number; bottom: number; width: number; height: number },\n rectangle: { left: number; top: number; right: number; bottom: number },\n): PdfPageRegion | null {\n if (bounds.width <= 0 || bounds.height <= 0) return null;\n const left = Math.max(bounds.left, Math.min(bounds.right, rectangle.left));\n const right = Math.max(bounds.left, Math.min(bounds.right, rectangle.right));\n const top = Math.max(bounds.top, Math.min(bounds.bottom, rectangle.top));\n const bottom = Math.max(bounds.top, Math.min(bounds.bottom, rectangle.bottom));\n if (right <= left || bottom <= top) return null;\n return {\n page,\n rect: {\n x: (left - bounds.left) / bounds.width,\n y: (top - bounds.top) / bounds.height,\n width: (right - left) / bounds.width,\n height: (bottom - top) / bounds.height,\n },\n };\n}\nfunction canvasBlob(\n canvas: HTMLCanvasElement,\n type: 'image/png' | 'image/jpeg',\n quality?: number,\n): Promise<Blob | null> {\n return new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n\n/** A page-aware PDF canvas. Coordinates resolve against PDF page geometry rather than an opaque browser iframe. */\nexport function PdfPreview({ src, path, className, controllerRef, 'data-testid': testId }: PdfPreviewProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const documentRef = useRef<PDFDocumentProxy | null>(null);\n const renderTask = useRef<{ cancel(): void } | null>(null);\n const [page, setPage] = useState(1);\n const [pageState, setPageState] = useState<PdfPageState>(EMPTY_STATE);\n const [error, setError] = useState<string | undefined>();\n\n useEffect(() => {\n let disposed = false;\n let loading: PdfLoadingTask | undefined;\n // pdfjs is imported on demand: `pdfjs-dist/webpack.mjs` spins up its worker\n // at module scope, so a static import would make every consumer of this\n // package pay for the worker whether or not it ever shows a PDF.\n void import('pdfjs-dist/webpack.mjs')\n .then(({ getDocument }) => {\n if (disposed) return undefined;\n loading = getDocument({ url: src });\n return loading.promise.then((document) => {\n if (disposed) return;\n documentRef.current = document;\n setError(undefined);\n setPage(1);\n setPageState({ ...EMPTY_STATE, pageCount: document.numPages });\n });\n })\n .catch((reason: unknown) => {\n if (!disposed) setError(reason instanceof Error ? reason.message : String(reason));\n });\n return () => {\n disposed = true;\n void loading?.destroy();\n renderTask.current?.cancel();\n renderTask.current = null;\n documentRef.current = null;\n };\n }, [src]);\n\n useEffect(() => {\n const document = documentRef.current;\n const canvas = canvasRef.current;\n if (document === null || canvas === null) return;\n let disposed = false;\n void document\n .getPage(page)\n .then((pdfPage: PDFPageProxy) => {\n if (disposed) return;\n const source = pdfPage.getViewport({ scale: 1 });\n const viewport = pdfPage.getViewport({ scale: 1.5 });\n canvas.width = Math.ceil(viewport.width);\n canvas.height = Math.ceil(viewport.height);\n setPageState({ page, pageCount: document.numPages, sourceWidth: source.width, sourceHeight: source.height });\n const context = canvas.getContext('2d');\n if (context === null) return;\n const task = pdfPage.render({ canvas, canvasContext: context, viewport });\n renderTask.current = task;\n return task.promise;\n })\n .catch((reason: unknown) => {\n if (!disposed && (reason as { name?: unknown }).name !== 'RenderingCancelledException') {\n setError(reason instanceof Error ? reason.message : String(reason));\n }\n });\n return () => {\n disposed = true;\n renderTask.current?.cancel();\n renderTask.current = null;\n };\n }, [page, pageState.pageCount]);\n\n useImperativeHandle(\n controllerRef,\n () => ({\n getState: () => pageState,\n setPage: (next) => setPage(boundedPage(next, pageState.pageCount)),\n resolveViewportRegion: (rectangle) => {\n const canvas = canvasRef.current;\n if (canvas === null || pageState.pageCount === 0) return null;\n return resolvePdfViewportRegion(pageState.page, canvas.getBoundingClientRect(), rectangle);\n },\n capturePage: async (type = 'image/png', quality) => {\n const canvas = canvasRef.current;\n return canvas === null ? null : await canvasBlob(canvas, type, quality);\n },\n }),\n [pageState],\n );\n\n return (\n <section data-testid={testId} data-kind=\"pdf\" className={cn('flex min-h-0 flex-col gap-2', className)}>\n <div className=\"flex items-center justify-between gap-2 text-xs text-doom-faint\">\n <span>{path}</span>\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={page <= 1}\n onClick={() => setPage((value) => Math.max(1, value - 1))}\n >\n previous\n </Button>\n <span data-testid={testId === undefined ? undefined : `${testId}-page`}>\n {pageState.pageCount === 0 ? 'loading' : `${page} / ${pageState.pageCount}`}\n </span>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={pageState.pageCount === 0 || page >= pageState.pageCount}\n onClick={() => setPage((value) => boundedPage(value + 1, pageState.pageCount))}\n >\n next\n </Button>\n </div>\n </div>\n {error === undefined ? null : <output className=\"text-xs text-doom-red\">{error}</output>}\n <div className=\"min-h-0 overflow-auto rounded border border-doom-border bg-doom-deep\">\n <canvas ref={canvasRef} aria-label={path} className=\"mx-auto block max-w-full\" />\n </div>\n </section>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PdfPreview.cjs","names":["useRef","useState","cn","Button"],"sources":["../../../src/components/PdfPreview.tsx"],"sourcesContent":["import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist/webpack.mjs';\nimport { useEffect, useImperativeHandle, useRef, useState } from 'react';\n\nimport { cn } from '../lib/cn';\nimport { Button } from './Button';\n\ntype PdfLoadingTask = ReturnType<typeof import('pdfjs-dist/webpack.mjs').getDocument>;\n\nexport interface PdfNormalizedRectangle {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface PdfPageState {\n page: number;\n pageCount: number;\n sourceWidth: number;\n sourceHeight: number;\n}\n\nexport interface PdfPageRegion {\n page: number;\n rect: PdfNormalizedRectangle;\n}\n\nexport interface PdfPreviewController {\n getState: () => PdfPageState;\n setPage: (page: number) => void;\n resolveViewportRegion: (rectangle: {\n left: number;\n top: number;\n right: number;\n bottom: number;\n }) => PdfPageRegion | null;\n capturePage: (type?: 'image/png' | 'image/jpeg', quality?: number) => Promise<Blob | null>;\n}\n\nexport interface PdfPreviewProps {\n src: string;\n path: string;\n className?: string;\n 'data-testid'?: string;\n controllerRef?: import('react').Ref<PdfPreviewController>;\n}\n\nconst EMPTY_STATE: PdfPageState = { page: 1, pageCount: 0, sourceWidth: 0, sourceHeight: 0 };\n\nfunction boundedPage(page: number, pageCount: number): number {\n if (!Number.isFinite(page) || pageCount <= 0) return 1;\n return Math.min(pageCount, Math.max(1, Math.trunc(page)));\n}\n\nexport function resolvePdfViewportRegion(\n page: number,\n bounds: { left: number; top: number; right: number; bottom: number; width: number; height: number },\n rectangle: { left: number; top: number; right: number; bottom: number },\n): PdfPageRegion | null {\n if (bounds.width <= 0 || bounds.height <= 0) return null;\n const left = Math.max(bounds.left, Math.min(bounds.right, rectangle.left));\n const right = Math.max(bounds.left, Math.min(bounds.right, rectangle.right));\n const top = Math.max(bounds.top, Math.min(bounds.bottom, rectangle.top));\n const bottom = Math.max(bounds.top, Math.min(bounds.bottom, rectangle.bottom));\n if (right <= left || bottom <= top) return null;\n return {\n page,\n rect: {\n x: (left - bounds.left) / bounds.width,\n y: (top - bounds.top) / bounds.height,\n width: (right - left) / bounds.width,\n height: (bottom - top) / bounds.height,\n },\n };\n}\nfunction canvasBlob(\n canvas: HTMLCanvasElement,\n type: 'image/png' | 'image/jpeg',\n quality?: number,\n): Promise<Blob | null> {\n return new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n\n/** A page-aware PDF canvas. Coordinates resolve against PDF page geometry rather than an opaque browser iframe. */\nexport function PdfPreview({ src, path, className, controllerRef, 'data-testid': testId }: PdfPreviewProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const documentRef = useRef<PDFDocumentProxy | null>(null);\n const renderTask = useRef<{ cancel(): void } | null>(null);\n const [page, setPage] = useState(1);\n const [pageState, setPageState] = useState<PdfPageState>(EMPTY_STATE);\n const [error, setError] = useState<string | undefined>();\n\n useEffect(() => {\n let disposed = false;\n let loading: PdfLoadingTask | undefined;\n // pdfjs is imported on demand: `pdfjs-dist/webpack.mjs` spins up its worker\n // at module scope, so a static import would make every consumer of this\n // package pay for the worker whether or not it ever shows a PDF.\n void import('pdfjs-dist/webpack.mjs')\n .then(({ getDocument }) => {\n if (disposed) return undefined;\n loading = getDocument({ url: src });\n return loading.promise.then((document) => {\n if (disposed) return;\n documentRef.current = document;\n setError(undefined);\n setPage(1);\n setPageState({ ...EMPTY_STATE, pageCount: document.numPages });\n });\n })\n .catch((reason: unknown) => {\n if (!disposed) setError(reason instanceof Error ? reason.message : String(reason));\n });\n return () => {\n disposed = true;\n void loading?.destroy();\n renderTask.current?.cancel();\n renderTask.current = null;\n documentRef.current = null;\n };\n }, [src]);\n\n useEffect(() => {\n const document = documentRef.current;\n const canvas = canvasRef.current;\n if (document === null || canvas === null) return;\n let disposed = false;\n void document\n .getPage(page)\n .then((pdfPage: PDFPageProxy) => {\n if (disposed) return;\n const source = pdfPage.getViewport({ scale: 1 });\n const viewport = pdfPage.getViewport({ scale: 1.5 });\n canvas.width = Math.ceil(viewport.width);\n canvas.height = Math.ceil(viewport.height);\n setPageState({ page, pageCount: document.numPages, sourceWidth: source.width, sourceHeight: source.height });\n const context = canvas.getContext('2d');\n if (context === null) return;\n const task = pdfPage.render({ canvas, canvasContext: context, viewport });\n renderTask.current = task;\n return task.promise;\n })\n .catch((reason: unknown) => {\n if (!disposed && (reason as { name?: unknown }).name !== 'RenderingCancelledException') {\n setError(reason instanceof Error ? reason.message : String(reason));\n }\n });\n return () => {\n disposed = true;\n renderTask.current?.cancel();\n renderTask.current = null;\n };\n }, [page, pageState.pageCount]);\n\n useImperativeHandle(\n controllerRef,\n () => ({\n getState: () => pageState,\n setPage: (next) => setPage(boundedPage(next, pageState.pageCount)),\n resolveViewportRegion: (rectangle) => {\n const canvas = canvasRef.current;\n if (canvas === null || pageState.pageCount === 0) return null;\n return resolvePdfViewportRegion(pageState.page, canvas.getBoundingClientRect(), rectangle);\n },\n capturePage: async (type = 'image/png', quality) => {\n const canvas = canvasRef.current;\n return canvas === null ? null : await canvasBlob(canvas, type, quality);\n },\n }),\n [pageState],\n );\n\n return (\n <section data-testid={testId} data-kind=\"pdf\" className={cn('flex min-h-0 flex-col gap-2', className)}>\n <div className=\"flex items-center justify-between gap-2 text-xs text-doom-faint\">\n <span>{path}</span>\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={page <= 1}\n onClick={() => setPage((value) => Math.max(1, value - 1))}\n >\n previous\n </Button>\n <span data-testid={testId === undefined ? undefined : `${testId}-page`}>\n {pageState.pageCount === 0 ? 'loading' : `${page} / ${pageState.pageCount}`}\n </span>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={pageState.pageCount === 0 || page >= pageState.pageCount}\n onClick={() => setPage((value) => boundedPage(value + 1, pageState.pageCount))}\n >\n next\n </Button>\n </div>\n </div>\n {error === undefined ? null : <output className=\"text-xs text-doom-red\">{error}</output>}\n <div className=\"min-h-0 overflow-auto rounded border border-doom-border bg-doom-deep\">\n <canvas ref={canvasRef} aria-label={path} className=\"mx-auto block max-w-full\" />\n </div>\n </section>\n );\n}\n"],"mappings":"iHA+CA,MAAM,EAA4B,CAAE,KAAM,EAAG,UAAW,EAAG,YAAa,EAAG,aAAc,CAAE,EAE3F,SAAS,EAAY,EAAc,EAA2B,CAE5D,MADI,CAAC,OAAO,SAAS,CAAI,GAAK,GAAa,EAAU,EAC9C,KAAK,IAAI,EAAW,KAAK,IAAI,EAAG,KAAK,MAAM,CAAI,CAAC,CAAC,CAC1D,CAEA,SAAgB,EACd,EACA,EACA,EACsB,CACtB,GAAI,EAAO,OAAS,GAAK,EAAO,QAAU,EAAG,OAAO,KACpD,IAAM,EAAO,KAAK,IAAI,EAAO,KAAM,KAAK,IAAI,EAAO,MAAO,EAAU,IAAI,CAAC,EACnE,EAAQ,KAAK,IAAI,EAAO,KAAM,KAAK,IAAI,EAAO,MAAO,EAAU,KAAK,CAAC,EACrE,EAAM,KAAK,IAAI,EAAO,IAAK,KAAK,IAAI,EAAO,OAAQ,EAAU,GAAG,CAAC,EACjE,EAAS,KAAK,IAAI,EAAO,IAAK,KAAK,IAAI,EAAO,OAAQ,EAAU,MAAM,CAAC,EAE7E,OADI,GAAS,GAAQ,GAAU,EAAY,KACpC,CACL,OACA,KAAM,CACJ,GAAI,EAAO,EAAO,MAAQ,EAAO,MACjC,GAAI,EAAM,EAAO,KAAO,EAAO,OAC/B,OAAQ,EAAQ,GAAQ,EAAO,MAC/B,QAAS,EAAS,GAAO,EAAO,MAClC,CACF,CACF,CACA,SAAS,EACP,EACA,EACA,EACsB,CACtB,OAAO,IAAI,QAAS,GAAY,EAAO,OAAO,EAAS,EAAM,CAAO,CAAC,CACvE,CAGA,SAAgB,EAAW,CAAE,MAAK,OAAM,YAAW,gBAAe,cAAe,GAA2B,CAC1G,IAAM,GAAA,EAAYA,EAAAA,OAAAA,CAA0B,IAAI,EAC1C,GAAA,EAAcA,EAAAA,OAAAA,CAAgC,IAAI,EAClD,GAAA,EAAaA,EAAAA,OAAAA,CAAkC,IAAI,EACnD,CAAC,EAAM,IAAA,EAAWC,EAAAA,SAAAA,CAAS,CAAC,EAC5B,CAAC,EAAW,IAAA,EAAgBA,EAAAA,SAAAA,CAAuB,CAAW,EAC9D,CAAC,EAAO,IAAA,EAAYA,EAAAA,SAAAA,CAA6B,EAkFvD,OAhFA,EAAA,EAAA,UAAA,KAAgB,CACd,IAAI,EAAW,GACX,EAmBJ,OAfA,OAAY,yBAAyB,CAClC,MAAM,CAAE,iBAAkB,CACrB,MAEJ,MADA,GAAU,EAAY,CAAE,IAAK,CAAI,CAAC,EAC3B,EAAQ,QAAQ,KAAM,GAAa,CACpC,IACJ,EAAY,QAAU,EACtB,EAAS,IAAA,EAAS,EAClB,EAAQ,CAAC,EACT,EAAa,CAAE,GAAG,EAAa,UAAW,EAAS,QAAS,CAAC,EAC/D,CAAC,CACH,CAAC,CAAC,CACD,MAAO,GAAoB,CACrB,GAAU,EAAS,aAAkB,MAAQ,EAAO,QAAU,OAAO,CAAM,CAAC,CACnF,CAAC,MACU,CACX,EAAW,GACX,GAAc,QAAQ,EACtB,EAAW,SAAS,OAAO,EAC3B,EAAW,QAAU,KACrB,EAAY,QAAU,IACxB,CACF,EAAG,CAAC,CAAG,CAAC,GAER,EAAA,EAAA,UAAA,KAAgB,CACd,IAAM,EAAW,EAAY,QACvB,EAAS,EAAU,QACzB,GAAI,IAAa,MAAQ,IAAW,KAAM,OAC1C,IAAI,EAAW,GAqBf,OApBA,EACG,QAAQ,CAAI,CAAC,CACb,KAAM,GAA0B,CAC/B,GAAI,EAAU,OACd,IAAM,EAAS,EAAQ,YAAY,CAAE,MAAO,CAAE,CAAC,EACzC,EAAW,EAAQ,YAAY,CAAE,MAAO,GAAI,CAAC,EACnD,EAAO,MAAQ,KAAK,KAAK,EAAS,KAAK,EACvC,EAAO,OAAS,KAAK,KAAK,EAAS,MAAM,EACzC,EAAa,CAAE,OAAM,UAAW,EAAS,SAAU,YAAa,EAAO,MAAO,aAAc,EAAO,MAAO,CAAC,EAC3G,IAAM,EAAU,EAAO,WAAW,IAAI,EACtC,GAAI,IAAY,KAAM,OACtB,IAAM,EAAO,EAAQ,OAAO,CAAE,SAAQ,cAAe,EAAS,UAAS,CAAC,EAExE,MADA,GAAW,QAAU,EACd,EAAK,OACd,CAAC,CAAC,CACD,MAAO,GAAoB,CACtB,CAAC,GAAa,EAA8B,OAAS,+BACvD,EAAS,aAAkB,MAAQ,EAAO,QAAU,OAAO,CAAM,CAAC,CAEtE,CAAC,MACU,CACX,EAAW,GACX,EAAW,SAAS,OAAO,EAC3B,EAAW,QAAU,IACvB,CACF,EAAG,CAAC,EAAM,EAAU,SAAS,CAAC,GAE9B,EAAA,EAAA,oBAAA,CACE,OACO,CACL,aAAgB,EAChB,QAAU,GAAS,EAAQ,EAAY,EAAM,EAAU,SAAS,CAAC,EACjE,sBAAwB,GAAc,CACpC,IAAM,EAAS,EAAU,QAEzB,OADI,IAAW,MAAQ,EAAU,YAAc,EAAU,KAClD,EAAyB,EAAU,KAAM,EAAO,sBAAsB,EAAG,CAAS,CAC3F,EACA,YAAa,MAAO,EAAO,YAAa,IAAY,CAClD,IAAM,EAAS,EAAU,QACzB,OAAO,IAAW,KAAO,KAAO,MAAM,EAAW,EAAQ,EAAM,CAAO,CACxE,CACF,GACA,CAAC,CAAS,CACZ,EAGE,iBAAA,EAAA,EAAA,KAAA,CAAC,UAAD,CAAS,cAAa,EAAQ,YAAU,MAAM,UAAWC,EAAAA,GAAG,8BAA+B,CAAS,EAApG,SAAA,CACE,iBAAA,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,kEAAf,SAAA,CACE,iBAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,CAAW,CAAA,EAClB,iBAAA,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,0BAAf,SAAA,CACE,iBAAA,EAAA,EAAA,IAAA,CAACC,EAAAA,OAAD,CACE,KAAK,KACL,QAAQ,QACR,SAAU,GAAQ,EAClB,YAAe,EAAS,GAAU,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EACzD,SAAA,UAEO,CAAA,EACR,iBAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,cAAa,IAAW,IAAA,GAAY,IAAA,GAAY,GAAG,EAAO,OAC7D,SAAA,EAAU,YAAc,EAAI,UAAY,GAAG,EAAK,KAAK,EAAU,WAC5D,CAAA,EACN,iBAAA,EAAA,EAAA,IAAA,CAACA,EAAAA,OAAD,CACE,KAAK,KACL,QAAQ,QACR,SAAU,EAAU,YAAc,GAAK,GAAQ,EAAU,UACzD,YAAe,EAAS,GAAU,EAAY,EAAQ,EAAG,EAAU,SAAS,CAAC,EAC9E,SAAA,MAEO,CAAA,CACL,CACF,CAAA,CAAA,IACJ,IAAU,IAAA,GAAY,KAAO,iBAAA,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAU,wBAAyB,SAAA,CAAc,CAAA,EACvF,iBAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,uEACb,SAAA,iBAAA,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,IAAK,EAAW,aAAY,EAAM,UAAU,0BAA4B,CAAA,CAC7E,CAAA,CACE,GAEb"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{cn as e}from"../lib/cn.mjs";import{Button as t}from"./Button.mjs";import{jsx as n,jsxs as r}from"react/jsx-runtime";import{useEffect as i,useImperativeHandle as a,useRef as o,useState as s}from"react";const c={page:1,pageCount:0,sourceWidth:0,sourceHeight:0};function l(e,t){return!Number.isFinite(e)||t<=0?1:Math.min(t,Math.max(1,Math.trunc(e)))}function u(e,t,n){if(t.width<=0||t.height<=0)return null;let r=Math.max(t.left,Math.min(t.right,n.left)),i=Math.max(t.left,Math.min(t.right,n.right)),a=Math.max(t.top,Math.min(t.bottom,n.top)),o=Math.max(t.top,Math.min(t.bottom,n.bottom));return i<=r||o<=a?null:{page:e,rect:{x:(r-t.left)/t.width,y:(a-t.top)/t.height,width:(i-r)/t.width,height:(o-a)/t.height}}}function d(e,t,n){return new Promise(r=>e.toBlob(r,t,n))}function f({src:f,path:p,className:m,controllerRef:h,"data-testid":g}){let _=o(null),v=o(null),y=o(null),[b,x]=s(1),[S,C]=s(c),[w,T]=s();return i(()=>{let e=!1,t;return import(`pdfjs-dist/webpack.mjs`).then(({getDocument:n})=>{if(!e)return t=n({url:f}),t.promise.then(t=>{e||(v.current=t,T(void 0),x(1),C({...c,pageCount:t.numPages}))})}).catch(t=>{e||T(t instanceof Error?t.message:String(t))}),()=>{e=!0,t?.destroy(),y.current?.cancel(),y.current=null,v.current=null}},[f]),i(()=>{let e=v.current,t=_.current;if(e===null||t===null)return;let n=!1;return e.getPage(b).then(r=>{if(n)return;let i=r.getViewport({scale:1}),a=r.getViewport({scale:1.5});t.width=Math.ceil(a.width),t.height=Math.ceil(a.height),C({page:b,pageCount:e.numPages,sourceWidth:i.width,sourceHeight:i.height});let o=t.getContext(`2d`);if(o===null)return;let s=r.render({canvas:t,canvasContext:o,viewport:a});return y.current=s,s.promise}).catch(e=>{!n&&e.name!==`RenderingCancelledException`&&T(e instanceof Error?e.message:String(e))}),()=>{n=!0,y.current?.cancel(),y.current=null}},[b,S.pageCount]),a(h,()=>({getState:()=>S,setPage:e=>x(l(e,S.pageCount)),resolveViewportRegion:e=>{let t=_.current;return t===null||S.pageCount===0?null:u(S.page,t.getBoundingClientRect(),e)},capturePage:async(e=`image/png`,t)=>{let n=_.current;return n===null?null:await d(n,e,t)}}),[S])
|
|
1
|
+
import{cn as e}from"../lib/cn.mjs";import{Button as t}from"./Button.mjs";import{jsx as n,jsxs as r}from"react/jsx-runtime";import{useEffect as i,useImperativeHandle as a,useRef as o,useState as s}from"react";const c={page:1,pageCount:0,sourceWidth:0,sourceHeight:0};function l(e,t){return!Number.isFinite(e)||t<=0?1:Math.min(t,Math.max(1,Math.trunc(e)))}function u(e,t,n){if(t.width<=0||t.height<=0)return null;let r=Math.max(t.left,Math.min(t.right,n.left)),i=Math.max(t.left,Math.min(t.right,n.right)),a=Math.max(t.top,Math.min(t.bottom,n.top)),o=Math.max(t.top,Math.min(t.bottom,n.bottom));return i<=r||o<=a?null:{page:e,rect:{x:(r-t.left)/t.width,y:(a-t.top)/t.height,width:(i-r)/t.width,height:(o-a)/t.height}}}function d(e,t,n){return new Promise(r=>e.toBlob(r,t,n))}function f({src:f,path:p,className:m,controllerRef:h,"data-testid":g}){let _=o(null),v=o(null),y=o(null),[b,x]=s(1),[S,C]=s(c),[w,T]=s();return i(()=>{let e=!1,t;return import(`pdfjs-dist/webpack.mjs`).then(({getDocument:n})=>{if(!e)return t=n({url:f}),t.promise.then(t=>{e||(v.current=t,T(void 0),x(1),C({...c,pageCount:t.numPages}))})}).catch(t=>{e||T(t instanceof Error?t.message:String(t))}),()=>{e=!0,t?.destroy(),y.current?.cancel(),y.current=null,v.current=null}},[f]),i(()=>{let e=v.current,t=_.current;if(e===null||t===null)return;let n=!1;return e.getPage(b).then(r=>{if(n)return;let i=r.getViewport({scale:1}),a=r.getViewport({scale:1.5});t.width=Math.ceil(a.width),t.height=Math.ceil(a.height),C({page:b,pageCount:e.numPages,sourceWidth:i.width,sourceHeight:i.height});let o=t.getContext(`2d`);if(o===null)return;let s=r.render({canvas:t,canvasContext:o,viewport:a});return y.current=s,s.promise}).catch(e=>{!n&&e.name!==`RenderingCancelledException`&&T(e instanceof Error?e.message:String(e))}),()=>{n=!0,y.current?.cancel(),y.current=null}},[b,S.pageCount]),a(h,()=>({getState:()=>S,setPage:e=>x(l(e,S.pageCount)),resolveViewportRegion:e=>{let t=_.current;return t===null||S.pageCount===0?null:u(S.page,t.getBoundingClientRect(),e)},capturePage:async(e=`image/png`,t)=>{let n=_.current;return n===null?null:await d(n,e,t)}}),[S]),/* @__PURE__ */ r(`section`,{"data-testid":g,"data-kind":`pdf`,className:e(`flex min-h-0 flex-col gap-2`,m),children:[/* @__PURE__ */ r(`div`,{className:`flex items-center justify-between gap-2 text-xs text-doom-faint`,children:[/* @__PURE__ */ n(`span`,{children:p}),/* @__PURE__ */ r(`div`,{className:`flex items-center gap-2`,children:[/* @__PURE__ */ n(t,{size:`xs`,variant:`ghost`,disabled:b<=1,onClick:()=>x(e=>Math.max(1,e-1)),children:`previous`}),/* @__PURE__ */ n(`span`,{"data-testid":g===void 0?void 0:`${g}-page`,children:S.pageCount===0?`loading`:`${b} / ${S.pageCount}`}),/* @__PURE__ */ n(t,{size:`xs`,variant:`ghost`,disabled:S.pageCount===0||b>=S.pageCount,onClick:()=>x(e=>l(e+1,S.pageCount)),children:`next`})]})]}),w===void 0?null:/* @__PURE__ */ n(`output`,{className:`text-xs text-doom-red`,children:w}),/* @__PURE__ */ n(`div`,{className:`min-h-0 overflow-auto rounded border border-doom-border bg-doom-deep`,children:/* @__PURE__ */ n(`canvas`,{ref:_,"aria-label":p,className:`mx-auto block max-w-full`})})]})}export{f as PdfPreview,u as resolvePdfViewportRegion};
|
|
2
2
|
//# sourceMappingURL=PdfPreview.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PdfPreview.mjs","names":[],"sources":["../../../src/components/PdfPreview.tsx"],"sourcesContent":["import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist/webpack.mjs';\nimport { useEffect, useImperativeHandle, useRef, useState } from 'react';\n\nimport { cn } from '../lib/cn';\nimport { Button } from './Button';\n\ntype PdfLoadingTask = ReturnType<typeof import('pdfjs-dist/webpack.mjs').getDocument>;\n\nexport interface PdfNormalizedRectangle {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface PdfPageState {\n page: number;\n pageCount: number;\n sourceWidth: number;\n sourceHeight: number;\n}\n\nexport interface PdfPageRegion {\n page: number;\n rect: PdfNormalizedRectangle;\n}\n\nexport interface PdfPreviewController {\n getState: () => PdfPageState;\n setPage: (page: number) => void;\n resolveViewportRegion: (rectangle: {\n left: number;\n top: number;\n right: number;\n bottom: number;\n }) => PdfPageRegion | null;\n capturePage: (type?: 'image/png' | 'image/jpeg', quality?: number) => Promise<Blob | null>;\n}\n\nexport interface PdfPreviewProps {\n src: string;\n path: string;\n className?: string;\n 'data-testid'?: string;\n controllerRef?: import('react').Ref<PdfPreviewController>;\n}\n\nconst EMPTY_STATE: PdfPageState = { page: 1, pageCount: 0, sourceWidth: 0, sourceHeight: 0 };\n\nfunction boundedPage(page: number, pageCount: number): number {\n if (!Number.isFinite(page) || pageCount <= 0) return 1;\n return Math.min(pageCount, Math.max(1, Math.trunc(page)));\n}\n\nexport function resolvePdfViewportRegion(\n page: number,\n bounds: { left: number; top: number; right: number; bottom: number; width: number; height: number },\n rectangle: { left: number; top: number; right: number; bottom: number },\n): PdfPageRegion | null {\n if (bounds.width <= 0 || bounds.height <= 0) return null;\n const left = Math.max(bounds.left, Math.min(bounds.right, rectangle.left));\n const right = Math.max(bounds.left, Math.min(bounds.right, rectangle.right));\n const top = Math.max(bounds.top, Math.min(bounds.bottom, rectangle.top));\n const bottom = Math.max(bounds.top, Math.min(bounds.bottom, rectangle.bottom));\n if (right <= left || bottom <= top) return null;\n return {\n page,\n rect: {\n x: (left - bounds.left) / bounds.width,\n y: (top - bounds.top) / bounds.height,\n width: (right - left) / bounds.width,\n height: (bottom - top) / bounds.height,\n },\n };\n}\nfunction canvasBlob(\n canvas: HTMLCanvasElement,\n type: 'image/png' | 'image/jpeg',\n quality?: number,\n): Promise<Blob | null> {\n return new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n\n/** A page-aware PDF canvas. Coordinates resolve against PDF page geometry rather than an opaque browser iframe. */\nexport function PdfPreview({ src, path, className, controllerRef, 'data-testid': testId }: PdfPreviewProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const documentRef = useRef<PDFDocumentProxy | null>(null);\n const renderTask = useRef<{ cancel(): void } | null>(null);\n const [page, setPage] = useState(1);\n const [pageState, setPageState] = useState<PdfPageState>(EMPTY_STATE);\n const [error, setError] = useState<string | undefined>();\n\n useEffect(() => {\n let disposed = false;\n let loading: PdfLoadingTask | undefined;\n // pdfjs is imported on demand: `pdfjs-dist/webpack.mjs` spins up its worker\n // at module scope, so a static import would make every consumer of this\n // package pay for the worker whether or not it ever shows a PDF.\n void import('pdfjs-dist/webpack.mjs')\n .then(({ getDocument }) => {\n if (disposed) return undefined;\n loading = getDocument({ url: src });\n return loading.promise.then((document) => {\n if (disposed) return;\n documentRef.current = document;\n setError(undefined);\n setPage(1);\n setPageState({ ...EMPTY_STATE, pageCount: document.numPages });\n });\n })\n .catch((reason: unknown) => {\n if (!disposed) setError(reason instanceof Error ? reason.message : String(reason));\n });\n return () => {\n disposed = true;\n void loading?.destroy();\n renderTask.current?.cancel();\n renderTask.current = null;\n documentRef.current = null;\n };\n }, [src]);\n\n useEffect(() => {\n const document = documentRef.current;\n const canvas = canvasRef.current;\n if (document === null || canvas === null) return;\n let disposed = false;\n void document\n .getPage(page)\n .then((pdfPage: PDFPageProxy) => {\n if (disposed) return;\n const source = pdfPage.getViewport({ scale: 1 });\n const viewport = pdfPage.getViewport({ scale: 1.5 });\n canvas.width = Math.ceil(viewport.width);\n canvas.height = Math.ceil(viewport.height);\n setPageState({ page, pageCount: document.numPages, sourceWidth: source.width, sourceHeight: source.height });\n const context = canvas.getContext('2d');\n if (context === null) return;\n const task = pdfPage.render({ canvas, canvasContext: context, viewport });\n renderTask.current = task;\n return task.promise;\n })\n .catch((reason: unknown) => {\n if (!disposed && (reason as { name?: unknown }).name !== 'RenderingCancelledException') {\n setError(reason instanceof Error ? reason.message : String(reason));\n }\n });\n return () => {\n disposed = true;\n renderTask.current?.cancel();\n renderTask.current = null;\n };\n }, [page, pageState.pageCount]);\n\n useImperativeHandle(\n controllerRef,\n () => ({\n getState: () => pageState,\n setPage: (next) => setPage(boundedPage(next, pageState.pageCount)),\n resolveViewportRegion: (rectangle) => {\n const canvas = canvasRef.current;\n if (canvas === null || pageState.pageCount === 0) return null;\n return resolvePdfViewportRegion(pageState.page, canvas.getBoundingClientRect(), rectangle);\n },\n capturePage: async (type = 'image/png', quality) => {\n const canvas = canvasRef.current;\n return canvas === null ? null : await canvasBlob(canvas, type, quality);\n },\n }),\n [pageState],\n );\n\n return (\n <section data-testid={testId} data-kind=\"pdf\" className={cn('flex min-h-0 flex-col gap-2', className)}>\n <div className=\"flex items-center justify-between gap-2 text-xs text-doom-faint\">\n <span>{path}</span>\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={page <= 1}\n onClick={() => setPage((value) => Math.max(1, value - 1))}\n >\n previous\n </Button>\n <span data-testid={testId === undefined ? undefined : `${testId}-page`}>\n {pageState.pageCount === 0 ? 'loading' : `${page} / ${pageState.pageCount}`}\n </span>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={pageState.pageCount === 0 || page >= pageState.pageCount}\n onClick={() => setPage((value) => boundedPage(value + 1, pageState.pageCount))}\n >\n next\n </Button>\n </div>\n </div>\n {error === undefined ? null : <output className=\"text-xs text-doom-red\">{error}</output>}\n <div className=\"min-h-0 overflow-auto rounded border border-doom-border bg-doom-deep\">\n <canvas ref={canvasRef} aria-label={path} className=\"mx-auto block max-w-full\" />\n </div>\n </section>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PdfPreview.mjs","names":[],"sources":["../../../src/components/PdfPreview.tsx"],"sourcesContent":["import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist/webpack.mjs';\nimport { useEffect, useImperativeHandle, useRef, useState } from 'react';\n\nimport { cn } from '../lib/cn';\nimport { Button } from './Button';\n\ntype PdfLoadingTask = ReturnType<typeof import('pdfjs-dist/webpack.mjs').getDocument>;\n\nexport interface PdfNormalizedRectangle {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface PdfPageState {\n page: number;\n pageCount: number;\n sourceWidth: number;\n sourceHeight: number;\n}\n\nexport interface PdfPageRegion {\n page: number;\n rect: PdfNormalizedRectangle;\n}\n\nexport interface PdfPreviewController {\n getState: () => PdfPageState;\n setPage: (page: number) => void;\n resolveViewportRegion: (rectangle: {\n left: number;\n top: number;\n right: number;\n bottom: number;\n }) => PdfPageRegion | null;\n capturePage: (type?: 'image/png' | 'image/jpeg', quality?: number) => Promise<Blob | null>;\n}\n\nexport interface PdfPreviewProps {\n src: string;\n path: string;\n className?: string;\n 'data-testid'?: string;\n controllerRef?: import('react').Ref<PdfPreviewController>;\n}\n\nconst EMPTY_STATE: PdfPageState = { page: 1, pageCount: 0, sourceWidth: 0, sourceHeight: 0 };\n\nfunction boundedPage(page: number, pageCount: number): number {\n if (!Number.isFinite(page) || pageCount <= 0) return 1;\n return Math.min(pageCount, Math.max(1, Math.trunc(page)));\n}\n\nexport function resolvePdfViewportRegion(\n page: number,\n bounds: { left: number; top: number; right: number; bottom: number; width: number; height: number },\n rectangle: { left: number; top: number; right: number; bottom: number },\n): PdfPageRegion | null {\n if (bounds.width <= 0 || bounds.height <= 0) return null;\n const left = Math.max(bounds.left, Math.min(bounds.right, rectangle.left));\n const right = Math.max(bounds.left, Math.min(bounds.right, rectangle.right));\n const top = Math.max(bounds.top, Math.min(bounds.bottom, rectangle.top));\n const bottom = Math.max(bounds.top, Math.min(bounds.bottom, rectangle.bottom));\n if (right <= left || bottom <= top) return null;\n return {\n page,\n rect: {\n x: (left - bounds.left) / bounds.width,\n y: (top - bounds.top) / bounds.height,\n width: (right - left) / bounds.width,\n height: (bottom - top) / bounds.height,\n },\n };\n}\nfunction canvasBlob(\n canvas: HTMLCanvasElement,\n type: 'image/png' | 'image/jpeg',\n quality?: number,\n): Promise<Blob | null> {\n return new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n\n/** A page-aware PDF canvas. Coordinates resolve against PDF page geometry rather than an opaque browser iframe. */\nexport function PdfPreview({ src, path, className, controllerRef, 'data-testid': testId }: PdfPreviewProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const documentRef = useRef<PDFDocumentProxy | null>(null);\n const renderTask = useRef<{ cancel(): void } | null>(null);\n const [page, setPage] = useState(1);\n const [pageState, setPageState] = useState<PdfPageState>(EMPTY_STATE);\n const [error, setError] = useState<string | undefined>();\n\n useEffect(() => {\n let disposed = false;\n let loading: PdfLoadingTask | undefined;\n // pdfjs is imported on demand: `pdfjs-dist/webpack.mjs` spins up its worker\n // at module scope, so a static import would make every consumer of this\n // package pay for the worker whether or not it ever shows a PDF.\n void import('pdfjs-dist/webpack.mjs')\n .then(({ getDocument }) => {\n if (disposed) return undefined;\n loading = getDocument({ url: src });\n return loading.promise.then((document) => {\n if (disposed) return;\n documentRef.current = document;\n setError(undefined);\n setPage(1);\n setPageState({ ...EMPTY_STATE, pageCount: document.numPages });\n });\n })\n .catch((reason: unknown) => {\n if (!disposed) setError(reason instanceof Error ? reason.message : String(reason));\n });\n return () => {\n disposed = true;\n void loading?.destroy();\n renderTask.current?.cancel();\n renderTask.current = null;\n documentRef.current = null;\n };\n }, [src]);\n\n useEffect(() => {\n const document = documentRef.current;\n const canvas = canvasRef.current;\n if (document === null || canvas === null) return;\n let disposed = false;\n void document\n .getPage(page)\n .then((pdfPage: PDFPageProxy) => {\n if (disposed) return;\n const source = pdfPage.getViewport({ scale: 1 });\n const viewport = pdfPage.getViewport({ scale: 1.5 });\n canvas.width = Math.ceil(viewport.width);\n canvas.height = Math.ceil(viewport.height);\n setPageState({ page, pageCount: document.numPages, sourceWidth: source.width, sourceHeight: source.height });\n const context = canvas.getContext('2d');\n if (context === null) return;\n const task = pdfPage.render({ canvas, canvasContext: context, viewport });\n renderTask.current = task;\n return task.promise;\n })\n .catch((reason: unknown) => {\n if (!disposed && (reason as { name?: unknown }).name !== 'RenderingCancelledException') {\n setError(reason instanceof Error ? reason.message : String(reason));\n }\n });\n return () => {\n disposed = true;\n renderTask.current?.cancel();\n renderTask.current = null;\n };\n }, [page, pageState.pageCount]);\n\n useImperativeHandle(\n controllerRef,\n () => ({\n getState: () => pageState,\n setPage: (next) => setPage(boundedPage(next, pageState.pageCount)),\n resolveViewportRegion: (rectangle) => {\n const canvas = canvasRef.current;\n if (canvas === null || pageState.pageCount === 0) return null;\n return resolvePdfViewportRegion(pageState.page, canvas.getBoundingClientRect(), rectangle);\n },\n capturePage: async (type = 'image/png', quality) => {\n const canvas = canvasRef.current;\n return canvas === null ? null : await canvasBlob(canvas, type, quality);\n },\n }),\n [pageState],\n );\n\n return (\n <section data-testid={testId} data-kind=\"pdf\" className={cn('flex min-h-0 flex-col gap-2', className)}>\n <div className=\"flex items-center justify-between gap-2 text-xs text-doom-faint\">\n <span>{path}</span>\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={page <= 1}\n onClick={() => setPage((value) => Math.max(1, value - 1))}\n >\n previous\n </Button>\n <span data-testid={testId === undefined ? undefined : `${testId}-page`}>\n {pageState.pageCount === 0 ? 'loading' : `${page} / ${pageState.pageCount}`}\n </span>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={pageState.pageCount === 0 || page >= pageState.pageCount}\n onClick={() => setPage((value) => boundedPage(value + 1, pageState.pageCount))}\n >\n next\n </Button>\n </div>\n </div>\n {error === undefined ? null : <output className=\"text-xs text-doom-red\">{error}</output>}\n <div className=\"min-h-0 overflow-auto rounded border border-doom-border bg-doom-deep\">\n <canvas ref={canvasRef} aria-label={path} className=\"mx-auto block max-w-full\" />\n </div>\n </section>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../lib/cn.cjs");let t=require("radix-ui"),n=require("react/jsx-runtime");function r(e){return(0,n.jsx)(t.Popover.Root,{"data-slot":`popover`,...e})}function i(e){return(0,n.jsx)(t.Popover.Trigger,{"data-slot":`popover-trigger`,...e})}function a(e){return(0,n.jsx)(t.Popover.Anchor,{"data-slot":`popover-anchor`,...e})}function o(e){return(0,n.jsx)(t.Popover.Close,{"data-slot":`popover-close`,...e})}function s({className:r,align:i=`start`,sideOffset:a=6,...o}){return(0,n.jsx)(t.Popover.Portal,{children
|
|
1
|
+
const e=require("../lib/cn.cjs");let t=require("radix-ui"),n=require("react/jsx-runtime");function r(e){return/* @__PURE__ */ (0,n.jsx)(t.Popover.Root,{"data-slot":`popover`,...e})}function i(e){return/* @__PURE__ */ (0,n.jsx)(t.Popover.Trigger,{"data-slot":`popover-trigger`,...e})}function a(e){return/* @__PURE__ */ (0,n.jsx)(t.Popover.Anchor,{"data-slot":`popover-anchor`,...e})}function o(e){return/* @__PURE__ */ (0,n.jsx)(t.Popover.Close,{"data-slot":`popover-close`,...e})}function s({className:r,align:i=`start`,sideOffset:a=6,...o}){return/* @__PURE__ */ (0,n.jsx)(t.Popover.Portal,{children:/* @__PURE__ */ (0,n.jsx)(t.Popover.Content,{"data-slot":`popover-content`,align:i,sideOffset:a,className:e.cn(`z-50 flex max-w-[calc(100vw-24px)] flex-col overflow-hidden rounded-lg border border-doom-border bg-doom-panel font-mono shadow-2xl outline-none data-[state=open]:animate-doom-rise`,r),...o})})}function c({className:t,...r}){return/* @__PURE__ */ (0,n.jsx)(`div`,{"data-slot":`popover-header`,className:e.cn(`flex h-[34px] shrink-0 items-center justify-between gap-3 border-b border-doom-border-soft bg-doom-deep px-3`,t),...r})}function l({className:t,...r}){return/* @__PURE__ */ (0,n.jsx)(`div`,{"data-slot":`popover-footer`,className:e.cn(`flex min-h-[30px] shrink-0 items-center justify-between gap-3 border-t border-doom-border-soft bg-doom-deep px-3 text-2xs text-doom-faint`,t),...r})}exports.Popover=r,exports.PopoverAnchor=a,exports.PopoverClose=o,exports.PopoverContent=s,exports.PopoverFooter=l,exports.PopoverHeader=c,exports.PopoverTrigger=i;
|
|
2
2
|
//# sourceMappingURL=Popover.cjs.map
|