@heyramzi/ui 7.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +28 -17
- package/README.md +6 -2
- package/package.json +25 -15
- package/scripts/sync-shadcn.mjs +17 -13
- package/scripts/sync-transitions.mjs +11 -11
- package/src/agents/agent-avatar.svelte +67 -114
- package/src/agents/agent-cast.svelte +31 -7
- package/src/agents/demo.svelte +55 -8
- package/src/agents/helpers.ts +58 -0
- package/src/agents/page-state.ts +70 -0
- package/src/agents/types.ts +3 -1
- package/src/ai/ai-button/AiButton.svelte +86 -0
- package/src/ai/ai-button/index.ts +4 -0
- package/src/ai/confirmation/Confirmation.svelte +8 -8
- package/src/ai/confirmation/confirmation-context.svelte.ts +2 -2
- package/src/ai/generation-progress/GenerationProgress.svelte +354 -0
- package/src/ai/generation-progress/index.ts +7 -0
- package/src/ai/index.ts +16 -6
- package/src/ai/prompt-input/PromptInput.svelte +19 -1
- package/src/ai/prompt-input/PromptInputSubmit.svelte +9 -3
- package/src/ai/reasoning/Reasoning.svelte +11 -8
- package/src/ambient.d.ts +4 -0
- package/src/animations/README.md +14 -14
- package/src/animations/index.css +3 -14
- package/src/animations/upstream-coverage.test.ts +1 -1
- package/src/components/copy/clipboard.test.ts +206 -0
- package/src/components/copy/clipboard.ts +49 -0
- package/src/components/copy/copied.svelte.ts +39 -0
- package/src/components/copy/copy-button.svelte +175 -0
- package/src/components/copy/copy-button.test.ts +110 -0
- package/src/components/copy/index.ts +3 -0
- package/src/components/data-table/data-table.test.ts +134 -0
- package/src/components/data-table/data-table.ts +77 -0
- package/src/components/data-table/index.ts +20 -0
- package/src/components/data-table/infinite-scroll.svelte +69 -0
- package/src/components/data-table/pagination.svelte +62 -0
- package/src/components/data-table/row-arrow.svelte +28 -0
- package/src/components/data-table/row-context-menu.svelte +106 -0
- package/src/components/data-table/row-context-menu.test.ts +18 -0
- package/src/components/data-table/sortable-head.svelte +56 -0
- package/src/components/data-table/table-skeleton.svelte +83 -0
- package/src/components/hold-to-confirm/hold-to-confirm.svelte +232 -0
- package/src/components/hold-to-confirm/index.ts +1 -0
- package/src/components/icon/icon.svelte +23 -0
- package/src/components/icon/index.ts +1 -0
- package/src/components/language-flag/index.ts +1 -0
- package/src/components/language-flag/language-flag.svelte +60 -0
- package/src/components/list-page/detail-empty.svelte +50 -0
- package/src/components/list-page/detail-header.svelte +107 -0
- package/src/components/list-page/detail-panel.svelte +90 -0
- package/src/components/list-page/detail-row.svelte +36 -0
- package/src/components/list-page/detail-stack.svelte +33 -0
- package/src/components/list-page/empty-state.svelte +91 -0
- package/src/components/list-page/filter-item.svelte +49 -0
- package/src/components/list-page/filter-popover.svelte +88 -0
- package/src/components/list-page/filter-section.ts +12 -0
- package/src/components/list-page/grouped-list.svelte +116 -0
- package/src/components/list-page/index.ts +28 -0
- package/src/components/list-page/list-page-shell.svelte +195 -0
- package/src/components/list-page/list-table.ts +19 -0
- package/src/components/list-page/master-detail.svelte +206 -0
- package/src/components/list-page/master-detail.test.ts +37 -0
- package/src/components/list-page/notice-bar.svelte +68 -0
- package/src/components/list-page/prev-next.svelte +104 -0
- package/src/components/list-page/queue-rail.ts +25 -0
- package/src/components/list-page/rail-body.svelte +29 -0
- package/src/components/list-page/rail-empty.svelte +51 -0
- package/src/components/list-page/rail-head.svelte +49 -0
- package/src/components/list-page/rail-row.svelte +163 -0
- package/src/components/list-page/section-host.ts +24 -0
- package/src/components/list-page/section-label.svelte +39 -0
- package/src/components/list-page/step.test.ts +21 -0
- package/src/components/list-page/step.ts +10 -0
- package/src/components/list-page/toolbar-search.svelte +63 -0
- package/src/components/list-page/toolbar-trigger.ts +23 -0
- package/src/components/list-page/types.ts +1 -0
- package/src/components/locale-switch/index.ts +1 -0
- package/src/components/locale-switch/locale-switch.svelte +59 -0
- package/src/components/mark/index.ts +1 -0
- package/src/components/mark/mark.svelte +70 -0
- package/src/components/open-in/index.ts +6 -0
- package/src/components/open-in/open-in.svelte +106 -0
- package/src/components/picker/index.ts +4 -0
- package/src/components/picker/mark-picker.svelte +84 -0
- package/src/components/picker/picker.svelte +143 -0
- package/src/components/picker/trigger.ts +42 -0
- package/src/components/picker/types.ts +26 -0
- package/src/components/segmented/animated-tabs.svelte +175 -0
- package/src/components/segmented/collapse.test.ts +49 -0
- package/src/components/segmented/collapse.ts +39 -0
- package/src/components/segmented/index.ts +41 -0
- package/src/components/segmented/section-runs.svelte +79 -0
- package/src/components/segmented/segment.svelte +106 -0
- package/src/components/segmented/segmented-filter.svelte +125 -0
- package/src/components/segmented/segmented-nav.svelte +82 -0
- package/src/components/segmented/segmented-overflow.svelte +85 -0
- package/src/components/segmented/segmented-track.svelte +181 -0
- package/src/components/segmented/segmented.test.ts +20 -0
- package/src/components/segmented/segmented.ts +80 -0
- package/src/components/star-rating-input/index.ts +9 -0
- package/src/components/star-rating-input/star-rating-input.svelte +256 -0
- package/src/components/svg/index.ts +1 -0
- package/src/components/svg/svg.svelte +9 -0
- package/src/effects/border-beam/BorderBeam.svelte +1 -1
- package/src/effects/image-comparison/ImageComparison.css +42 -8
- package/src/effects/image-comparison/ImageComparison.svelte +138 -21
- package/src/effects/image-comparison/compare.test.ts +17 -0
- package/src/effects/image-comparison/compare.ts +5 -0
- package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +338 -0
- package/src/effects/magic-card/MagicCard.css +47 -0
- package/src/effects/magic-card/MagicCard.svelte +57 -0
- package/src/effects/marquee/Marquee.css +27 -1
- package/src/effects/marquee/Marquee.svelte +67 -3
- package/src/effects/reveal/index.ts +2 -1
- package/src/effects/reveal/observe.test.ts +60 -0
- package/src/effects/reveal/observe.ts +33 -1
- package/src/effects/reveal/types.ts +7 -0
- package/src/effects/theme-toggle/ThemeToggle.css +199 -6
- package/src/effects/theme-toggle/ThemeToggle.svelte +51 -26
- package/src/gallery.test.ts +52 -0
- package/src/index.ts +5 -1
- package/src/lib/panel.ts +7 -1
- package/src/lib/theme-transition.ts +37 -11
- package/src/lib/tone.ts +48 -0
- package/src/shadcn/accordion/accordion-trigger.svelte +15 -6
- package/src/shadcn/alert/alert.svelte +3 -1
- package/src/shadcn/badge/badge.svelte +8 -7
- package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +2 -2
- package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +2 -2
- package/src/shadcn/calendar/calendar-month-select.svelte +2 -2
- package/src/shadcn/calendar/calendar-next-button.svelte +2 -2
- package/src/shadcn/calendar/calendar-prev-button.svelte +2 -2
- package/src/shadcn/calendar/calendar-year-select.svelte +2 -2
- package/src/shadcn/calendar/calendar.svelte +6 -0
- package/src/shadcn/code-block/code-block-copy-button.svelte +6 -55
- package/src/shadcn/command/index.ts +3 -0
- package/src/shadcn/field/index.ts +3 -0
- package/src/shadcn/field/simple-field.svelte +71 -0
- package/src/shadcn/field/simple-field.test.ts +47 -0
- package/src/shadcn/message-scroller/message-scroller-button.svelte +2 -2
- package/src/shadcn/radio-group/radio-group-item.svelte +3 -2
- package/src/shadcn/sheet/sheet-content.svelte +1 -0
- package/src/shadcn/sheet/sheet.css +48 -0
- package/src/shadcn/sonner/sonner.svelte +7 -10
- package/src/shadcn/toggle-group/toggle-group.svelte +10 -3
- package/src/shadcn/trace/trace-collapsible-trigger.svelte +2 -2
- package/src/ai/composer/Composer.css +0 -108
- package/src/ai/composer/Composer.svelte +0 -111
- package/src/ai/composer/index.ts +0 -10
- package/src/animations/accordion.css +0 -58
- package/src/animations/icon-swap.css +0 -43
- package/src/animations/notification-badge.css +0 -81
- package/src/animations/panel.css +0 -86
- package/src/animations/reasoning-stream.css +0 -49
- package/src/animations/skeleton-reveal.css +0 -97
- package/src/animations/streaming-text.css +0 -31
- package/src/animations/tabs-sliding.css +0 -83
- package/src/animations/texts-reveal.css +0 -70
- package/src/animations/tooltip.css +0 -72
- package/src/animations/view-transition-reveal.ts +0 -74
- package/src/lib/utils.ts +0 -45
- package/src/showcase/AiSection.svelte +0 -150
- package/src/showcase/AnimationsSection.svelte +0 -1658
- package/src/showcase/EffectsSection.svelte +0 -152
- package/src/showcase/ShadcnSection.svelte +0 -27
- package/src/showcase/ShowcaseImport.svelte +0 -126
- package/src/showcase/ShowcaseList.svelte +0 -132
- package/src/showcase/ShowcaseNav.svelte +0 -247
- package/src/showcase/ShowcaseSection.svelte +0 -112
- package/src/showcase/ShowcaseShell.svelte +0 -55
- package/src/showcase/ShowcaseStage.svelte +0 -342
- package/src/showcase/ShowcaseVariant.svelte +0 -109
- package/src/showcase/assets/image-reveal/art-1.jpg +0 -0
- package/src/showcase/assets/image-reveal/art-2.jpg +0 -0
- package/src/showcase/assets/image-reveal/art-3.jpg +0 -0
- package/src/showcase/check.svg +0 -1
- package/src/showcase/chevron-arms.svg +0 -2
- package/src/showcase/chevron-down.svg +0 -1
- package/src/showcase/copy.svg +0 -2
- package/src/showcase/demos/ai/confirmation.svelte +0 -59
- package/src/showcase/demos/ai/loading-state.svelte +0 -19
- package/src/showcase/demos/ai/prompt-input.svelte +0 -57
- package/src/showcase/demos/ai/reasoning.svelte +0 -19
- package/src/showcase/demos/ai/thinking-dots.svelte +0 -15
- package/src/showcase/demos/ai/voice-waveform.svelte +0 -24
- package/src/showcase/demos/effects/animated-beam.svelte +0 -50
- package/src/showcase/demos/effects/animated-group.svelte +0 -47
- package/src/showcase/demos/effects/border-beam-default.svelte +0 -20
- package/src/showcase/demos/effects/border-beam-line.svelte +0 -20
- package/src/showcase/demos/effects/border-beam-mono.svelte +0 -20
- package/src/showcase/demos/effects/border-beam-sunset.svelte +0 -20
- package/src/showcase/demos/effects/image-comparison.svelte +0 -25
- package/src/showcase/demos/effects/image-reveal.svelte +0 -49
- package/src/showcase/demos/effects/in-view.svelte +0 -41
- package/src/showcase/demos/effects/integrations-hub.svelte +0 -82
- package/src/showcase/demos/effects/magnetic.svelte +0 -31
- package/src/showcase/demos/effects/marquee.svelte +0 -33
- package/src/showcase/demos/effects/progressive-blur.svelte +0 -47
- package/src/showcase/demos/effects/spotlight.svelte +0 -40
- package/src/showcase/demos/effects/text-effect.svelte +0 -55
- package/src/showcase/demos/shadcn/accordion.svelte +0 -45
- package/src/showcase/demos/shadcn/alert-dialog.svelte +0 -23
- package/src/showcase/demos/shadcn/alert.svelte +0 -30
- package/src/showcase/demos/shadcn/attachment.svelte +0 -71
- package/src/showcase/demos/shadcn/avatar.svelte +0 -30
- package/src/showcase/demos/shadcn/badge.svelte +0 -26
- package/src/showcase/demos/shadcn/breadcrumb.svelte +0 -34
- package/src/showcase/demos/shadcn/bubble.svelte +0 -36
- package/src/showcase/demos/shadcn/button.svelte +0 -11
- package/src/showcase/demos/shadcn/calendar.svelte +0 -8
- package/src/showcase/demos/shadcn/card.svelte +0 -39
- package/src/showcase/demos/shadcn/checkbox.svelte +0 -39
- package/src/showcase/demos/shadcn/code-block.svelte +0 -80
- package/src/showcase/demos/shadcn/collapsible.svelte +0 -20
- package/src/showcase/demos/shadcn/command.svelte +0 -48
- package/src/showcase/demos/shadcn/context-menu.svelte +0 -54
- package/src/showcase/demos/shadcn/demo-harness.svelte +0 -12
- package/src/showcase/demos/shadcn/demos.test.ts +0 -20
- package/src/showcase/demos/shadcn/dialog.svelte +0 -38
- package/src/showcase/demos/shadcn/dropdown-menu.svelte +0 -59
- package/src/showcase/demos/shadcn/empty.svelte +0 -29
- package/src/showcase/demos/shadcn/field.svelte +0 -115
- package/src/showcase/demos/shadcn/hover-card.svelte +0 -32
- package/src/showcase/demos/shadcn/index.ts +0 -382
- package/src/showcase/demos/shadcn/input-otp.svelte +0 -19
- package/src/showcase/demos/shadcn/input.svelte +0 -5
- package/src/showcase/demos/shadcn/label.svelte +0 -11
- package/src/showcase/demos/shadcn/marker.svelte +0 -30
- package/src/showcase/demos/shadcn/message-scroller.svelte +0 -44
- package/src/showcase/demos/shadcn/message.svelte +0 -75
- package/src/showcase/demos/shadcn/popover.svelte +0 -36
- package/src/showcase/demos/shadcn/progress.svelte +0 -13
- package/src/showcase/demos/shadcn/radio-group.svelte +0 -19
- package/src/showcase/demos/shadcn/scroll-area.svelte +0 -18
- package/src/showcase/demos/shadcn/select.svelte +0 -29
- package/src/showcase/demos/shadcn/separator.svelte +0 -12
- package/src/showcase/demos/shadcn/sheet.svelte +0 -48
- package/src/showcase/demos/shadcn/skeleton.svelte +0 -11
- package/src/showcase/demos/shadcn/slider.svelte +0 -7
- package/src/showcase/demos/shadcn/sonner.svelte +0 -18
- package/src/showcase/demos/shadcn/spinner.svelte +0 -11
- package/src/showcase/demos/shadcn/switch.svelte +0 -9
- package/src/showcase/demos/shadcn/table.svelte +0 -76
- package/src/showcase/demos/shadcn/tabs.svelte +0 -62
- package/src/showcase/demos/shadcn/textarea.svelte +0 -5
- package/src/showcase/demos/shadcn/toggle-group.svelte +0 -18
- package/src/showcase/demos/shadcn/toggle.svelte +0 -14
- package/src/showcase/demos/shadcn/tooltip.svelte +0 -13
- package/src/showcase/demos/shadcn/trace.svelte +0 -51
- package/src/showcase/heart.svg +0 -1
- package/src/showcase/index.ts +0 -38
- package/src/showcase/kit.test.ts +0 -72
- package/src/showcase/kit.ts +0 -16
- package/src/showcase/plus.svg +0 -1
- package/src/showcase/showcase-shell.css +0 -91
- package/src/showcase/stacked-section.css +0 -15
- package/src/showcase/success-check.svg +0 -1
- package/src/showcase/types.ts +0 -29
- package/src/svg-raw.d.ts +0 -7
package/COMPONENTS.md
CHANGED
|
@@ -23,12 +23,12 @@ The style layer lives in [`src/tokens.css`](./src/tokens.css) and the motion CSS
|
|
|
23
23
|
- `checkbox` — Checkbox
|
|
24
24
|
- `code-block` — CodeBlock. Highlight with `useHighlightedCode` from `@heyramzi/ui/shadcn/hooks/use-highlighted-code.svelte` (Shiki, ANSI too)
|
|
25
25
|
- `collapsible` — Collapsible
|
|
26
|
-
- `command` — Command
|
|
26
|
+
- `command` — Command, computeCommandScore (the scorer, for a custom `filter`)
|
|
27
27
|
- `context-menu` — ContextMenu
|
|
28
28
|
- `dialog` — Dialog
|
|
29
29
|
- `dropdown-menu` — CheckboxGroup, CheckboxItem, Content, Portal, DropdownMenu, DropdownMenuCheckboxGroup
|
|
30
30
|
- `empty` — Empty
|
|
31
|
-
- `field` — Field
|
|
31
|
+
- `field` — Field and its parts, plus SimpleField: label, hint or error and the control, with the message linked by `aria-describedby`
|
|
32
32
|
- `hover-card` — HoverCard
|
|
33
33
|
- `input` — Input
|
|
34
34
|
- `input-otp` — InputOtp
|
|
@@ -57,54 +57,66 @@ The style layer lives in [`src/tokens.css`](./src/tokens.css) and the motion CSS
|
|
|
57
57
|
- `tooltip` — Tooltip
|
|
58
58
|
- `trace` — Trace, an agent's steps: reasoning, tool calls and sources as status rows that can expand
|
|
59
59
|
|
|
60
|
+
## Components (`src/components`)
|
|
61
|
+
|
|
62
|
+
Generic pieces built on the primitives, imported from `@heyramzi/ui/components/<name>`.
|
|
63
|
+
|
|
64
|
+
- `copy` — CopyButton, copyText, copyRichText, htmlToPlainText, Copied
|
|
65
|
+
- `data-table` — SortableHead, Pagination, InfiniteScroll, RowContextMenu, TableSkeleton, and the sort, page and filter helpers
|
|
66
|
+
- `hold-to-confirm` — HoldToConfirm, press and hold to confirm
|
|
67
|
+
- `icon` — Icon, a Phosphor component at a size
|
|
68
|
+
- `language-flag` — LanguageFlag
|
|
69
|
+
- `list-page` — ListPageShell, MasterDetail, GroupedList, RailRow and the rest of the master-detail page kit, NoticeBar, EmptyState, FilterPopover, setSectionHost
|
|
70
|
+
- `locale-switch` — LocaleSwitch, a language switch on SegmentedFilter
|
|
71
|
+
- `mark` — Mark, a logo or photo glyph with an initial fallback
|
|
72
|
+
- `open-in` — OpenIn, a link out to a tool that takes the tool's name, label and mark as a prop
|
|
73
|
+
- `picker` — Picker, MarkPicker, pickerTriggerClass, a command palette at popover scale
|
|
74
|
+
- `segmented` — AnimatedTabs (pill or underline), SegmentedFilter, SegmentedNav, SectionRuns, one bordered track with a sliding pill, a badge note and an overflow menu
|
|
75
|
+
- `star-rating-input` — StarRatingInput
|
|
76
|
+
- `svg` — Svg, an SVG file imported with `?raw`
|
|
77
|
+
|
|
60
78
|
## Effects (`src/effects`)
|
|
61
79
|
|
|
80
|
+
- `interactive-hover-button` — InteractiveHoverButton, a dot that grows into the fill
|
|
81
|
+
|
|
62
82
|
- `animated-beam` — AnimatedBeam, AnimatedBeamProps, AnchorPosition
|
|
63
83
|
- `border-beam` — BorderBeam
|
|
64
|
-
- `image-comparison` — ImageComparison
|
|
84
|
+
- `image-comparison` — ImageComparison, a before/after wipe with a bindable position, side badges and a hint sweep
|
|
65
85
|
- `image-reveal` — ImageReveal, ImageRevealProps, ImageRevealHandle
|
|
66
86
|
- `magnetic` — magnetic
|
|
87
|
+
- `magic-card` — MagicCard. Props: `glow`, `radius`, `class`, `contentClass`; the glow colour is `--magic-card-glow` set on `class`.
|
|
67
88
|
- `marquee` — Marquee
|
|
68
89
|
- `progressive-blur` — ProgressiveBlur
|
|
69
90
|
- `reveal` — InView, AnimatedGroup, TextEffect, observeInView, RevealPreset, RevealPer
|
|
70
91
|
- `shader-gradient` — ShaderGradient, ShaderGradientProps, ShaderGradientUniforms
|
|
71
92
|
- `spotlight` — Spotlight
|
|
72
|
-
- `theme-toggle` — ThemeToggle, revealTheme. Props: `isDark`, `mode` (`light`, `dark`, `system`; swaps in the Monitor icon), `onToggle`, `variant` (`outline`, `ghost`, `inverted`; `inverted` boolean is an alias), `size`, `label`, `title`, `skipTransition`, `class`. `revealTheme(event, apply, { skip, duration, easing })` is the circular wipe on its own: `import { revealTheme } from "@heyramzi/ui"` (the `./lib/*` subpath points at `.js` and the source is `.ts`).
|
|
93
|
+
- `theme-toggle` — ThemeToggle, revealTheme. Props: `isDark`, `mode` (`light`, `dark`, `system`; swaps in the Monitor icon), `onToggle`, `variant` (`outline`, `ghost`, `inverted`, `slide`; `inverted` boolean is an alias; `slide` is a pill with a moving thumb, coloured by `--theme-toggle-thumb-light`, `--theme-toggle-thumb-dark`, `--theme-toggle-track`, `--theme-toggle-glow` and friends set on `class`), `size`, `label`, `title`, `skipTransition`, `class`. `revealTheme(event, apply, { skip, duration, easing })` is the circular wipe on its own: `import { revealTheme } from "@heyramzi/ui"` (the `./lib/*` subpath points at `.js` and the source is `.ts`).
|
|
73
94
|
|
|
74
95
|
## Animations (`src/animations`)
|
|
75
96
|
|
|
76
|
-
- `accordion` — keyframe utilities
|
|
77
97
|
- `avatar-group-hover` — keyframe utilities
|
|
78
98
|
- `card-tilt` — keyframe utilities
|
|
79
99
|
- `digit-pop-in` — keyframe utilities
|
|
80
100
|
- `dropdown` — keyframe utilities
|
|
81
|
-
- `icon-swap` — keyframe utilities
|
|
82
101
|
- `input-clear-dissolve` — keyframe utilities
|
|
83
102
|
- `input-shake` — keyframe utilities
|
|
84
103
|
- `learn-more-hover` — keyframe utilities
|
|
85
104
|
- `like-button` — keyframe utilities
|
|
86
105
|
- `modal` — keyframe utilities
|
|
87
|
-
- `notification-badge` — keyframe utilities
|
|
88
106
|
- `page-side-by-side` — keyframe utilities
|
|
89
|
-
- `panel` — keyframe utilities
|
|
90
107
|
- `plus-menu-morph` — keyframe utilities
|
|
91
|
-
- `reasoning-stream` — keyframe utilities
|
|
92
108
|
- `resize` — keyframe utilities
|
|
93
109
|
- `shimmer-text` — keyframe utilities
|
|
94
|
-
- `skeleton-reveal` — keyframe utilities
|
|
95
110
|
- `spinning-counter` — keyframe utilities
|
|
96
|
-
- `streaming-text` — keyframe utilities
|
|
97
111
|
- `success-check` — keyframe utilities
|
|
98
|
-
- `tabs-sliding` — keyframe utilities
|
|
99
112
|
- `text-swap` — keyframe utilities
|
|
100
|
-
- `texts-reveal` — keyframe utilities
|
|
101
113
|
- `thinking-states` — keyframe utilities
|
|
102
|
-
- `tooltip` — keyframe utilities
|
|
103
114
|
|
|
104
115
|
## AI and chat (`src/ai`)
|
|
105
116
|
|
|
106
|
-
- `
|
|
117
|
+
- `ai-button` — AiButton
|
|
107
118
|
- `confirmation` — Confirmation, ConfirmationTitle, ConfirmationRequest, ConfirmationAccepted, ConfirmationRejected, ConfirmationActions, ConfirmationAction
|
|
119
|
+
- `generation-progress` — GenerationProgress
|
|
108
120
|
- `loading-state` — PixelLoader
|
|
109
121
|
- `prompt-input` — PromptInput, PromptInputBody, PromptInputToolbar, PromptInputTextarea, PromptInputSubmit
|
|
110
122
|
- `reasoning` — Reasoning, ReasoningTrigger, ReasoningContent
|
|
@@ -116,7 +128,6 @@ The style layer lives in [`src/tokens.css`](./src/tokens.css) and the motion CSS
|
|
|
116
128
|
|
|
117
129
|
---
|
|
118
130
|
|
|
119
|
-
Skipped on purpose: brand assets (logos, marks, brand renderer,
|
|
120
|
-
locale switch), kanban, list, gantt and status (they live in `@heyramzi/tasks`), and charts
|
|
131
|
+
Skipped on purpose: brand assets (logos, marks, brand renderer), kanban, list, gantt and status (they live in `@heyramzi/tasks`), and charts
|
|
121
132
|
(they live in `@heyramzi/charts`). The bot avatars
|
|
122
133
|
are in `src/agents`, imported from `@heyramzi/ui/agents`.
|
package/README.md
CHANGED
|
@@ -11,11 +11,12 @@ only a convenience.
|
|
|
11
11
|
## What is inside
|
|
12
12
|
|
|
13
13
|
- `src/shadcn` shadcn-svelte primitives, generalized.
|
|
14
|
+
- `src/components` generic pieces on the primitives: segmented control, picker, copy button, data table, list page, hold to confirm and more.
|
|
14
15
|
- `src/effects` animated borders, beams, marquee, spotlight, image reveal,
|
|
15
16
|
image comparison, shimmer gradients and a theme toggle.
|
|
16
17
|
- `src/animations` keyframe CSS for dropdowns, modals, tooltips, text swaps,
|
|
17
18
|
success checks and the rest of the motion vocabulary.
|
|
18
|
-
- `src/ai` chat and AI surfaces:
|
|
19
|
+
- `src/ai` chat and AI surfaces: prompt input, response, reasoning, confirmation and loaders.
|
|
19
20
|
- `src/agents` bot avatars and a cast grid, imported from `@heyramzi/ui/agents`:
|
|
20
21
|
`AgentAvatar` draws an eyes-only face from a name, role, shape and colour, and
|
|
21
22
|
`AgentCast` lays a list of agents out as cards you can pick from.
|
|
@@ -41,6 +42,9 @@ Retheme by overriding any variable on `:root`, for example
|
|
|
41
42
|
`--color-brand-indigo` or `--radius-lg`. Dark mode keys off a `.dark`
|
|
42
43
|
class on `html`.
|
|
43
44
|
|
|
45
|
+
Rich text lives in [`@heyramzi/rich-editor`](https://github.com/heyramzi/ui/tree/main/packages/rich-editor),
|
|
46
|
+
so the kit doesn't pull Tiptap into every app.
|
|
47
|
+
|
|
44
48
|
Charts live in their own package now,
|
|
45
49
|
[`@heyramzi/charts`](https://github.com/heyramzi/ui/tree/main/packages/charts). It reads
|
|
46
50
|
the same tokens, so import `tokens.css` once and both work.
|
|
@@ -125,7 +129,7 @@ Inspired by [shadcn-svelte](https://github.com/huntabyte/shadcn-svelte).
|
|
|
125
129
|
The reveal, marquee and beam effects are inspired by
|
|
126
130
|
[motion-primitives](https://github.com/ibelick/motion-primitives), and the
|
|
127
131
|
motion vocabulary by [transitions-dev](https://transitions.dev). The agent avatars
|
|
128
|
-
|
|
132
|
+
port [bot-avatars](https://github.com/Jakubantalik/Libraries.dev/tree/main/packages/bot-avatars) 0.2.2; its licence is in
|
|
129
133
|
`src/agents/vendor/LICENSE`.
|
|
130
134
|
|
|
131
135
|
## License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyramzi/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Svelte 5 UI primitives: effects, animations, and shared component CSS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,7 +29,16 @@
|
|
|
29
29
|
"default": "./src/index.ts"
|
|
30
30
|
},
|
|
31
31
|
"./tokens.css": "./src/tokens.css",
|
|
32
|
-
"./lib
|
|
32
|
+
"./lib/*.js": {
|
|
33
|
+
"svelte": "./src/lib/*.ts",
|
|
34
|
+
"types": "./src/lib/*.ts",
|
|
35
|
+
"default": "./src/lib/*.ts"
|
|
36
|
+
},
|
|
37
|
+
"./lib/*": {
|
|
38
|
+
"svelte": "./src/lib/*.ts",
|
|
39
|
+
"types": "./src/lib/*.ts",
|
|
40
|
+
"default": "./src/lib/*.ts"
|
|
41
|
+
},
|
|
33
42
|
"./border-beam": {
|
|
34
43
|
"svelte": "./src/effects/border-beam/BorderBeam.svelte",
|
|
35
44
|
"default": "./src/effects/border-beam/BorderBeam.svelte"
|
|
@@ -55,6 +64,10 @@
|
|
|
55
64
|
"svelte": "./src/effects/spotlight/Spotlight.svelte",
|
|
56
65
|
"default": "./src/effects/spotlight/Spotlight.svelte"
|
|
57
66
|
},
|
|
67
|
+
"./effects/magic-card": {
|
|
68
|
+
"svelte": "./src/effects/magic-card/MagicCard.svelte",
|
|
69
|
+
"default": "./src/effects/magic-card/MagicCard.svelte"
|
|
70
|
+
},
|
|
58
71
|
"./effects/marquee": {
|
|
59
72
|
"svelte": "./src/effects/marquee/Marquee.svelte",
|
|
60
73
|
"default": "./src/effects/marquee/Marquee.svelte"
|
|
@@ -78,6 +91,10 @@
|
|
|
78
91
|
"svelte": "./src/effects/progressive-blur/ProgressiveBlur.svelte",
|
|
79
92
|
"default": "./src/effects/progressive-blur/ProgressiveBlur.svelte"
|
|
80
93
|
},
|
|
94
|
+
"./effects/interactive-hover-button": {
|
|
95
|
+
"svelte": "./src/effects/interactive-hover-button/InteractiveHoverButton.svelte",
|
|
96
|
+
"default": "./src/effects/interactive-hover-button/InteractiveHoverButton.svelte"
|
|
97
|
+
},
|
|
81
98
|
"./effects/image-comparison": {
|
|
82
99
|
"svelte": "./src/effects/image-comparison/ImageComparison.svelte",
|
|
83
100
|
"default": "./src/effects/image-comparison/ImageComparison.svelte"
|
|
@@ -115,16 +132,6 @@
|
|
|
115
132
|
"./animations/*": {
|
|
116
133
|
"default": "./src/animations/*"
|
|
117
134
|
},
|
|
118
|
-
"./showcase": {
|
|
119
|
-
"svelte": "./src/showcase/index.ts",
|
|
120
|
-
"types": "./src/showcase/index.ts",
|
|
121
|
-
"default": "./src/showcase/index.ts"
|
|
122
|
-
},
|
|
123
|
-
"./showcase/kit": {
|
|
124
|
-
"svelte": "./src/showcase/kit.ts",
|
|
125
|
-
"types": "./src/showcase/kit.ts",
|
|
126
|
-
"default": "./src/showcase/kit.ts"
|
|
127
|
-
},
|
|
128
135
|
"./shadcn/hooks/*": {
|
|
129
136
|
"svelte": "./src/shadcn/hooks/*.ts",
|
|
130
137
|
"types": "./src/shadcn/hooks/*.ts",
|
|
@@ -135,6 +142,11 @@
|
|
|
135
142
|
"types": "./src/shadcn/*/index.ts",
|
|
136
143
|
"default": "./src/shadcn/*/index.ts"
|
|
137
144
|
},
|
|
145
|
+
"./components/*": {
|
|
146
|
+
"svelte": "./src/components/*/index.ts",
|
|
147
|
+
"types": "./src/components/*/index.ts",
|
|
148
|
+
"default": "./src/components/*/index.ts"
|
|
149
|
+
},
|
|
138
150
|
"./shadcn/utilities.css": {
|
|
139
151
|
"style": "./src/shadcn/utilities.css",
|
|
140
152
|
"default": "./src/shadcn/utilities.css"
|
|
@@ -158,18 +170,16 @@
|
|
|
158
170
|
},
|
|
159
171
|
"dependencies": {
|
|
160
172
|
"@heyramzi/utils": "^1.3.3",
|
|
161
|
-
"@lucide/svelte": "^1.52.0",
|
|
162
173
|
"@shadcn-svelte/primitives": "^0.0.1",
|
|
163
|
-
"clsx": "^2.1.1",
|
|
164
174
|
"marked": "^18.1.0",
|
|
165
175
|
"phosphor-svelte": "^3.1.0",
|
|
166
176
|
"shiki": "^4.5.0",
|
|
167
|
-
"tailwind-merge": "^3.7.0",
|
|
168
177
|
"tailwind-variants": "^3.3.1"
|
|
169
178
|
},
|
|
170
179
|
"devDependencies": {
|
|
171
180
|
"@internationalized/date": "^3.12.4",
|
|
172
181
|
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
182
|
+
"@types/node": "^26.6.4",
|
|
173
183
|
"bits-ui": "^2.19.5",
|
|
174
184
|
"jsdom": "^30.1.2",
|
|
175
185
|
"mode-watcher": "^1.1.0",
|
package/scripts/sync-shadcn.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/* Sync shadcn-svelte into src/shadcn and its demos into the
|
|
2
|
+
/* Sync shadcn-svelte into src/shadcn and its demos into the site, three-way
|
|
3
3
|
* per file against `.upstream/`. The rules and why: ../docs/decisions.md. */
|
|
4
4
|
|
|
5
5
|
// pnpm sync:shadcn fetch, vendor, apply, report
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { execFileSync } from "node:child_process";
|
|
9
9
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
-
import { basename, dirname, resolve } from "node:path";
|
|
11
|
+
import { basename, dirname, relative, resolve } from "node:path";
|
|
12
12
|
import {
|
|
13
13
|
CHECK,
|
|
14
14
|
classify,
|
|
@@ -56,16 +56,16 @@ const REROUTE = {
|
|
|
56
56
|
|
|
57
57
|
const DOCS = "https://raw.githubusercontent.com/huntabyte/shadcn-svelte/main/docs";
|
|
58
58
|
const DEMO_VENDOR = `${PKG}/.upstream/shadcn-demos`;
|
|
59
|
-
const DEMO_LOCAL = `${
|
|
59
|
+
const DEMO_LOCAL = `${ROOT}/site/src/lib/shadcn-demos`;
|
|
60
60
|
const DEMO_MANIFEST = `${DEMO_LOCAL}/index.ts`;
|
|
61
61
|
|
|
62
62
|
/* The docs site's aliases. A demo is a docs file, not a registry payload, so it
|
|
63
63
|
* carries `$lib/...` rather than the registry's `$UI$`. */
|
|
64
64
|
const DEMO_ALIASES = [
|
|
65
|
-
["$lib/registry/ui/", "
|
|
65
|
+
["$lib/registry/ui/", "@heyramzi/ui/shadcn/"],
|
|
66
66
|
// A handful reach their neighbour by relative path instead of the alias.
|
|
67
|
-
["../ui/", "
|
|
68
|
-
["$lib/registry/hooks/", "
|
|
67
|
+
["../ui/", "@heyramzi/ui/shadcn/"],
|
|
68
|
+
["$lib/registry/hooks/", "@heyramzi/ui/shadcn/hooks/"],
|
|
69
69
|
["$lib/utils.js", "@heyramzi/utils"],
|
|
70
70
|
// The peer here is the runes build, published under its own name.
|
|
71
71
|
["@tabler/icons-svelte/", "@tabler/icons-svelte-runes/"],
|
|
@@ -107,12 +107,16 @@ const NO_DEMO = new Set([
|
|
|
107
107
|
]);
|
|
108
108
|
|
|
109
109
|
const adapt = (text) => ALIASES.reduce((acc, [from, to]) => acc.replaceAll(from, to), text);
|
|
110
|
+
/* The site imports the kit the way a consumer does, so the exports map names a
|
|
111
|
+
* primitive without its `/index.js` and a hook without its `.js`. */
|
|
110
112
|
const adaptDemo = (text) =>
|
|
111
|
-
DEMO_ALIASES.reduce((acc, [from, to]) => acc.replaceAll(from, to), text)
|
|
113
|
+
DEMO_ALIASES.reduce((acc, [from, to]) => acc.replaceAll(from, to), text)
|
|
114
|
+
.replace(/(@heyramzi\/ui\/shadcn\/[a-z-]+)\/index\.js/g, "$1")
|
|
115
|
+
.replace(/(@heyramzi\/ui\/shadcn\/hooks\/[a-z-]+(?:\.svelte)?)\.js/g, "$1");
|
|
112
116
|
const readIf = (path) => (existsSync(path) ? readFileSync(path, "utf8") : null);
|
|
113
117
|
const route = (name, target) => `${PKG}/${REROUTE[`${name}/${target}`] ?? `src/shadcn/${target}`}`;
|
|
114
118
|
/** Report a file by where it lives here, not by its upstream target. */
|
|
115
|
-
const label = (path) => path.slice(PKG.length + 1);
|
|
119
|
+
const label = (path) => (path.startsWith(`${PKG}/`) ? path.slice(PKG.length + 1) : relative(ROOT, path));
|
|
116
120
|
|
|
117
121
|
const pascal = (name) => name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
|
|
118
122
|
|
|
@@ -127,10 +131,10 @@ const meta = (doc, name) => {
|
|
|
127
131
|
return { title: field("title") ?? pascal(name), note: field("description") ?? "" };
|
|
128
132
|
};
|
|
129
133
|
|
|
130
|
-
/** The demo's own shadcn imports,
|
|
134
|
+
/** The demo's own shadcn imports, as it spells them for a consumer. */
|
|
131
135
|
const publicImports = (demo) =>
|
|
132
|
-
[...demo.matchAll(/import[^;]*?from\s+"
|
|
133
|
-
.map((m) => m[0]
|
|
136
|
+
[...demo.matchAll(/import[^;]*?from\s+"@heyramzi\/ui\/shadcn\/[^"]+";/g)]
|
|
137
|
+
.map((m) => m[0])
|
|
134
138
|
.join("\n");
|
|
135
139
|
|
|
136
140
|
/* The section's list, generated: a primitive reaches the page by being synced,
|
|
@@ -145,7 +149,7 @@ async function writeManifest(demos) {
|
|
|
145
149
|
" * own, and the sync rewrites this list. */",
|
|
146
150
|
'import type { Component } from "svelte";',
|
|
147
151
|
...demos.map(
|
|
148
|
-
(d) => `import ${pascal(d.name)}Demo from "
|
|
152
|
+
(d) => `import ${pascal(d.name)}Demo from "./${d.name}.svelte";`,
|
|
149
153
|
),
|
|
150
154
|
"",
|
|
151
155
|
"export type ShadcnDemo = {",
|
|
@@ -267,7 +271,7 @@ const ours = readdirSync(LOCAL, { withFileTypes: true })
|
|
|
267
271
|
for (const name of ours) buckets.unported.push(`${name} — in src/shadcn, not in the registry`);
|
|
268
272
|
|
|
269
273
|
/* The docs site's own demo for each primitive, vendored on the same three-way
|
|
270
|
-
* rule. Upstream writes them; the
|
|
274
|
+
* rule. Upstream writes them; the site renders them. */
|
|
271
275
|
const demoNames = [...Object.keys(entries), ...LOCAL_ONLY]
|
|
272
276
|
.filter((name) => !NO_DEMO.has(name))
|
|
273
277
|
.toSorted();
|
|
@@ -46,7 +46,7 @@ const SRC = `${PKG}/src`;
|
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
48
|
/*
|
|
49
|
-
*
|
|
49
|
+
* Fourteen of these name the component that owns the motion instead of a file in src/animations.
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
52
|
/*
|
|
@@ -55,31 +55,31 @@ const SRC = `${PKG}/src`;
|
|
|
55
55
|
const PORTED = {
|
|
56
56
|
"card-resize": "animations/resize.css",
|
|
57
57
|
"number-pop-in": "animations/digit-pop-in.css",
|
|
58
|
-
"notification-badge": "
|
|
58
|
+
"notification-badge": "shadcn/badge/badge.svelte",
|
|
59
59
|
"text-states-swap": "animations/text-swap.css",
|
|
60
60
|
"menu-dropdown": "animations/dropdown.css",
|
|
61
61
|
"modal-open-close": "animations/modal.css",
|
|
62
|
-
"panel-reveal": "
|
|
62
|
+
"panel-reveal": "shadcn/sheet/sheet-content.svelte",
|
|
63
63
|
"page-side-by-side": "animations/page-side-by-side.css",
|
|
64
|
-
"icon-swap": "
|
|
64
|
+
"icon-swap": "effects/theme-toggle/ThemeToggle.svelte",
|
|
65
65
|
"success-check": "animations/success-check.css",
|
|
66
66
|
"avatar-group-hover": "animations/avatar-group-hover.css",
|
|
67
67
|
"error-state-shake": "animations/input-shake.css",
|
|
68
68
|
"input-clear-with-dissolve": "animations/input-clear-dissolve.css",
|
|
69
|
-
"skeleton-loader-and-reveal": "
|
|
70
|
-
"texts-reveal": "
|
|
71
|
-
"tabs-sliding": "
|
|
69
|
+
"skeleton-loader-and-reveal": "shadcn/skeleton/skeleton.svelte",
|
|
70
|
+
"texts-reveal": "effects/reveal/TextEffect.svelte",
|
|
71
|
+
"tabs-sliding": "shadcn/tabs/tabs-list.svelte",
|
|
72
72
|
"shimmer-text": "animations/shimmer-text.css",
|
|
73
|
-
"tooltip-open-close": "
|
|
73
|
+
"tooltip-open-close": "shadcn/tooltip/tooltip-content.svelte",
|
|
74
74
|
"3d-tilt": "animations/card-tilt.css",
|
|
75
75
|
"dropdown-menu-morph": "animations/plus-menu-morph.css",
|
|
76
|
-
accordion: "
|
|
76
|
+
accordion: "shadcn/accordion/accordion-content.svelte",
|
|
77
77
|
"like-button": "animations/like-button.css",
|
|
78
78
|
"learn-more-hover": "animations/learn-more-hover.css",
|
|
79
79
|
"spinning-counter": "animations/spinning-counter.css",
|
|
80
80
|
"thinking-states": "animations/thinking-states.css",
|
|
81
|
-
"reasoning-stream": "
|
|
82
|
-
"streaming-text": "
|
|
81
|
+
"reasoning-stream": "ai/reasoning/ReasoningContent.svelte",
|
|
82
|
+
"streaming-text": "effects/reveal/TextEffect.svelte",
|
|
83
83
|
toggle: "shadcn/switch/switch.svelte",
|
|
84
84
|
"checkbox-check": "shadcn/checkbox/checkbox.svelte",
|
|
85
85
|
"toast-open-close": "shadcn/sonner/sonner.svelte",
|
|
@@ -1,55 +1,5 @@
|
|
|
1
|
-
<script module lang="ts">
|
|
2
|
-
/* The batching shown-gate and the path caches are shared by every avatar
|
|
3
|
-
on the page, so a row of them comes in together. Engine: ./vendor. */
|
|
4
|
-
import { bakesPending as bakesPendingGlobal } from './vendor/plastic';
|
|
5
|
-
|
|
6
|
-
const SHOW_WITHIN = 4000;
|
|
7
|
-
const waiters = new Map<object, { ready: () => boolean; show: () => void }>();
|
|
8
|
-
|
|
9
|
-
function showReady() {
|
|
10
|
-
if (bakesPendingGlobal()) return;
|
|
11
|
-
for (const w of waiters.values()) if (!w.ready()) return;
|
|
12
|
-
const all = [...waiters.values()];
|
|
13
|
-
waiters.clear();
|
|
14
|
-
for (const w of all) w.show();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const pathCache = new Map<string, Path2D>();
|
|
18
|
-
function bodyPath(d: string): Path2D {
|
|
19
|
-
let p = pathCache.get(d);
|
|
20
|
-
if (!p) {
|
|
21
|
-
p = new Path2D(d);
|
|
22
|
-
pathCache.set(d, p);
|
|
23
|
-
}
|
|
24
|
-
return p;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const partsCache = new Map<string, Path2D[]>();
|
|
28
|
-
function partPaths(d: string): Path2D[] {
|
|
29
|
-
let p = partsCache.get(d);
|
|
30
|
-
if (!p) {
|
|
31
|
-
p = d
|
|
32
|
-
.split(/(?=M)/)
|
|
33
|
-
.filter((s) => s.trim())
|
|
34
|
-
.map((s) => new Path2D(s));
|
|
35
|
-
partsCache.set(d, p);
|
|
36
|
-
}
|
|
37
|
-
return p;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
let uidSeq = 0;
|
|
41
|
-
function hashSeed(id: string): number {
|
|
42
|
-
let h = 2166136261;
|
|
43
|
-
for (let i = 0; i < id.length; i++) h = Math.imul(h ^ id.charCodeAt(i), 16777619);
|
|
44
|
-
return ((h >>> 0) % 1000) / 1000;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const reducedMotion = (): boolean =>
|
|
48
|
-
typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
49
|
-
</script>
|
|
50
|
-
|
|
51
1
|
<script lang="ts">
|
|
52
|
-
import { onMount } from 'svelte';
|
|
2
|
+
import { onMount, untrack } from 'svelte';
|
|
53
3
|
import type {
|
|
54
4
|
BotAvatarShading,
|
|
55
5
|
BotAvatarState,
|
|
@@ -61,16 +11,29 @@
|
|
|
61
11
|
import { autoInk, richer, shade } from './vendor/color';
|
|
62
12
|
import { Sim, restPose } from './vendor/engine';
|
|
63
13
|
import { draw, LIGHT_DEFAULTS, OVERSCAN, RISE, type DrawConfig } from './vendor/draw';
|
|
64
|
-
import {
|
|
65
|
-
bakesPending,
|
|
66
|
-
hurry,
|
|
67
|
-
onLanded,
|
|
68
|
-
stockLights,
|
|
69
|
-
takeMissed,
|
|
70
|
-
warmPlastic,
|
|
71
|
-
type FurStyle,
|
|
72
|
-
} from './vendor/plastic';
|
|
14
|
+
import { bakesPending, hurry, onLanded, takeMissed, warmPlastic, type FurStyle } from './vendor/plastic';
|
|
73
15
|
import { subscribe, pointer } from './vendor/ticker';
|
|
16
|
+
import {
|
|
17
|
+
SHOW_WITHIN,
|
|
18
|
+
bodyPath,
|
|
19
|
+
nextUid,
|
|
20
|
+
onThemeChange,
|
|
21
|
+
partPaths,
|
|
22
|
+
showReady,
|
|
23
|
+
waiters,
|
|
24
|
+
} from './page-state';
|
|
25
|
+
import {
|
|
26
|
+
clamp,
|
|
27
|
+
clampOpt,
|
|
28
|
+
deviceDpr,
|
|
29
|
+
hashSeed,
|
|
30
|
+
lightsFor,
|
|
31
|
+
pointerStrength,
|
|
32
|
+
q,
|
|
33
|
+
reducedMotion,
|
|
34
|
+
resolveTheme as themeOf,
|
|
35
|
+
warmIdle,
|
|
36
|
+
} from './helpers';
|
|
74
37
|
|
|
75
38
|
type Props = AgentAvatarProps;
|
|
76
39
|
|
|
@@ -83,7 +46,7 @@
|
|
|
83
46
|
state: botState = 'default',
|
|
84
47
|
size,
|
|
85
48
|
color,
|
|
86
|
-
ink
|
|
49
|
+
ink,
|
|
87
50
|
brightness: brightnessProp,
|
|
88
51
|
saturation: saturationProp,
|
|
89
52
|
speed = 1,
|
|
@@ -140,20 +103,14 @@
|
|
|
140
103
|
class: className,
|
|
141
104
|
style,
|
|
142
105
|
'aria-label': ariaLabel,
|
|
106
|
+
'aria-hidden': ariaHidden,
|
|
143
107
|
onclick: userClick,
|
|
144
108
|
...rest
|
|
145
109
|
}: Props = $props();
|
|
146
110
|
|
|
147
|
-
const clamp = (v: number, lo: number, hi: number) =>
|
|
148
|
-
Math.min(hi, Math.max(lo, Number.isFinite(v) ? v : 1));
|
|
149
|
-
const q = (v: number, lo: number, hi: number) => Math.round(clamp(v, lo, hi) * 20) / 20;
|
|
150
|
-
const clampOpt = (v: number | undefined, d: number, lo: number, hi: number) =>
|
|
151
|
-
clamp(v ?? d, lo, hi);
|
|
152
111
|
const shapePath = (t: BotAvatarType) => SHAPE_PATHS[t] ?? SHAPE_PATHS.clover;
|
|
153
|
-
const deviceDpr = () =>
|
|
154
|
-
Math.min(2, (typeof devicePixelRatio === 'number' && devicePixelRatio) || 1);
|
|
155
112
|
|
|
156
|
-
const uid =
|
|
113
|
+
const uid = nextUid();
|
|
157
114
|
|
|
158
115
|
/* The avatar draws at 75% of its slot so an idle jump has room in the box
|
|
159
116
|
and never clips. A numeric `size` overrides the measurement. */
|
|
@@ -205,6 +162,10 @@
|
|
|
205
162
|
} satisfies FurStyle);
|
|
206
163
|
const material = $derived(shadingMode === 'plastic' || shadingMode === 'fabric');
|
|
207
164
|
const lookKey = $derived(`${shadingMode}|${outlineKey}|${Math.round(drawnSize)}`);
|
|
165
|
+
/* What the bakes depend on, as strings, so the warm effect re-runs only
|
|
166
|
+
when one of them changes, not on every prop or every resized pixel. */
|
|
167
|
+
const warmSize = $derived(Math.round(drawnSize));
|
|
168
|
+
const furKey = $derived(`${fur.length}|${fur.density}|${fur.fuzz}|${fur.clumps}|${fur.curl}|${fur.gravity}`);
|
|
208
169
|
const interactiveNow = $derived(interactive && !heldPose);
|
|
209
170
|
|
|
210
171
|
/* Captured once, on purpose: `shown` never returns to false, so a later
|
|
@@ -278,19 +239,7 @@
|
|
|
278
239
|
let holding: (() => void) | null = null;
|
|
279
240
|
const meRef = {};
|
|
280
241
|
|
|
281
|
-
|
|
282
|
-
if (theme !== 'auto') return theme;
|
|
283
|
-
const host = el?.closest('[data-theme], .dark, .light') as HTMLElement | null;
|
|
284
|
-
if (host) {
|
|
285
|
-
const v = host.getAttribute('data-theme');
|
|
286
|
-
if (v === 'dark' || v === 'light') return v;
|
|
287
|
-
if (host.classList.contains('dark')) return 'dark';
|
|
288
|
-
if (host.classList.contains('light')) return 'light';
|
|
289
|
-
}
|
|
290
|
-
return typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: light)').matches
|
|
291
|
-
? 'light'
|
|
292
|
-
: 'dark';
|
|
293
|
-
}
|
|
242
|
+
const resolveTheme = (el: HTMLElement | null) => themeOf(theme, el);
|
|
294
243
|
|
|
295
244
|
function layoutPx(canvas: HTMLCanvasElement) {
|
|
296
245
|
return canvas.clientWidth / OVERSCAN || cssSize || drawnSize || 64;
|
|
@@ -365,6 +314,9 @@
|
|
|
365
314
|
|
|
366
315
|
function showWhenBaked(paintNow: () => void) {
|
|
367
316
|
showPaint = paintNow;
|
|
317
|
+
/* No canvas means nothing was drawn, so nothing could report a missed
|
|
318
|
+
bake. Calling that ready would skip the fade. */
|
|
319
|
+
if (!canvasEl) return;
|
|
368
320
|
takeMissed();
|
|
369
321
|
paintNow();
|
|
370
322
|
ready = !takeMissed();
|
|
@@ -381,30 +333,13 @@
|
|
|
381
333
|
});
|
|
382
334
|
}
|
|
383
335
|
|
|
384
|
-
function lightsFor(lightAt: number) {
|
|
385
|
-
const a = (lightAt * Math.PI) / 180;
|
|
386
|
-
const b = ((backLight ?? 0) * Math.PI) / 180;
|
|
387
|
-
const lights = stockLights(Math.sin(a), -Math.cos(a));
|
|
388
|
-
if (backLight !== undefined) [lights.bx, lights.by] = [Math.sin(b), -Math.cos(b)];
|
|
389
|
-
if (lightFront !== undefined) lights.front = (Math.min(85, Math.max(0, lightFront)) * Math.PI) / 180;
|
|
390
|
-
return lights;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
336
|
function warm() {
|
|
394
337
|
if (!material || !cfg?.path) return;
|
|
395
338
|
const c = cfg;
|
|
396
339
|
const dev = (drawnSize || 64) * deviceDpr();
|
|
397
340
|
const lightAt = c.light ?? 295;
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
function warmIdle(fn: () => void): () => void {
|
|
402
|
-
if (typeof requestIdleCallback === 'function') {
|
|
403
|
-
const id = requestIdleCallback(fn);
|
|
404
|
-
return () => cancelIdleCallback(id);
|
|
405
|
-
}
|
|
406
|
-
const id = setTimeout(fn, 1);
|
|
407
|
-
return () => clearTimeout(id);
|
|
341
|
+
const lights = lightsFor(lightAt, backLight, lightFront);
|
|
342
|
+
warmPlastic(outlineKey, c.path, dev, depth, shadingMode === 'fabric', fur, lightAt, lights);
|
|
408
343
|
}
|
|
409
344
|
|
|
410
345
|
function hold() {
|
|
@@ -415,7 +350,7 @@
|
|
|
415
350
|
if (!holding) return;
|
|
416
351
|
holding();
|
|
417
352
|
holding = null;
|
|
418
|
-
|
|
353
|
+
applyPose();
|
|
419
354
|
};
|
|
420
355
|
const off = onLanded(() => {
|
|
421
356
|
if (!bakesPending()) release();
|
|
@@ -471,6 +406,8 @@
|
|
|
471
406
|
/* Sync the rig with the props on every meaningful change: this is the
|
|
472
407
|
React `useLayoutEffect` (which ran each render) made reactive. */
|
|
473
408
|
$effect(() => {
|
|
409
|
+
/* Until the slot is measured there's no canvas to draw on. */
|
|
410
|
+
if (drawnSize <= 0) return;
|
|
474
411
|
if (!sim) sim = new Sim(seedValue, stateKey);
|
|
475
412
|
else sim.setState(stateKey);
|
|
476
413
|
sim.setTurn(clamp(turn, 0, 2));
|
|
@@ -513,25 +450,36 @@
|
|
|
513
450
|
};
|
|
514
451
|
});
|
|
515
452
|
|
|
453
|
+
/* Once shown, a paused or reduced-motion avatar draws only when asked, so
|
|
454
|
+
it repaints here and keeps repainting as its bakes land. */
|
|
455
|
+
$effect(() => {
|
|
456
|
+
if (!shown) return;
|
|
457
|
+
untrack(() => {
|
|
458
|
+
if (canvasEl && drawnSize > 0) applyPose();
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
$effect(() => {
|
|
463
|
+
if (theme !== 'auto') return;
|
|
464
|
+
return onThemeChange(() => {
|
|
465
|
+
if (canvasEl && drawnSize > 0) applyPose();
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
516
469
|
/* Start the material's bakes on idle time as soon as the shape is known. */
|
|
517
470
|
$effect(() => {
|
|
518
471
|
void shadingMode;
|
|
519
472
|
void outlineKey;
|
|
520
|
-
void
|
|
473
|
+
void warmSize;
|
|
521
474
|
void depth;
|
|
522
475
|
void light;
|
|
523
476
|
void backLight;
|
|
524
477
|
void lightFront;
|
|
525
|
-
|
|
478
|
+
void furKey;
|
|
479
|
+
if (!material || !warmSize || !untrack(() => cfg?.path)) return;
|
|
526
480
|
return warmIdle(warm);
|
|
527
481
|
});
|
|
528
482
|
|
|
529
|
-
function pointerStrength(d: number) {
|
|
530
|
-
if (d < 1) return 1;
|
|
531
|
-
if (d > 3) return 0;
|
|
532
|
-
return 1 - (d - 1) / 2;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
483
|
function updatePointer(s: Sim, canvas: HTMLCanvasElement) {
|
|
536
484
|
if (!interactiveNow || Number.isNaN(pointer.x)) {
|
|
537
485
|
s.setPointer(0, 0, 0);
|
|
@@ -604,8 +552,10 @@
|
|
|
604
552
|
|
|
605
553
|
onMount(() => {
|
|
606
554
|
if (!hostEl) return;
|
|
555
|
+
/* A hidden parent (a closed tab or panel) reports 0. Keep the last width
|
|
556
|
+
so the canvas, its picture and its gate survive being hidden. */
|
|
607
557
|
const observer = new ResizeObserver(([entry]) => {
|
|
608
|
-
if (entry) hostWidth = entry.contentRect.width;
|
|
558
|
+
if (entry && entry.contentRect.width > 0) hostWidth = entry.contentRect.width;
|
|
609
559
|
});
|
|
610
560
|
observer.observe(hostEl);
|
|
611
561
|
return () => observer.disconnect();
|
|
@@ -628,9 +578,12 @@
|
|
|
628
578
|
data-bot-avatar={type}
|
|
629
579
|
data-face={faceKind}
|
|
630
580
|
data-state={stateKey}
|
|
631
|
-
role=
|
|
632
|
-
aria-
|
|
633
|
-
|
|
581
|
+
role={ariaHidden ? undefined : 'img'}
|
|
582
|
+
aria-hidden={ariaHidden ? 'true' : undefined}
|
|
583
|
+
aria-label={ariaHidden
|
|
584
|
+
? undefined
|
|
585
|
+
: (ariaLabel ??
|
|
586
|
+
(name ? `${name}${role ? `, ${role}` : ''}` : `${preset.label} agent, ${stateLabels[stateKey]}`))}
|
|
634
587
|
style={canvasStyle}
|
|
635
588
|
onclick={handleClick}
|
|
636
589
|
{...rest}
|