@heyramzi/ui 2.0.1 → 2.2.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/package.json +48 -24
- package/scripts/sync-shadcn.mjs +3 -4
- package/src/agents/agent-avatar.svelte +604 -575
- package/src/agents/agent-cast.svelte +179 -183
- package/src/agents/demo.svelte +125 -113
- package/src/agents/helpers.ts +23 -17
- package/src/agents/index.ts +4 -4
- package/src/agents/page-state.ts +6 -6
- package/src/agents/types.ts +6 -6
- package/src/ai/ai-button/AiButton.svelte +4 -12
- package/src/ai/generation-progress/GenerationProgress.svelte +10 -3
- package/src/ai/prompt-input/PromptInputTextarea.svelte +1 -2
- package/src/ai/response/Response.css +2 -3
- package/src/animations/avatar-group-hover.css +2 -16
- package/src/animations/card-tilt.css +3 -6
- package/src/animations/digit-pop-in.css +2 -15
- package/src/animations/dropdown.css +4 -17
- package/src/animations/input-clear-dissolve.css +5 -18
- package/src/animations/input-shake.css +9 -41
- package/src/animations/learn-more-hover.css +17 -9
- package/src/animations/like-button.css +2 -4
- package/src/animations/page-side-by-side.css +2 -10
- package/src/animations/plus-menu-morph.css +2 -5
- package/src/animations/shimmer-text.css +5 -18
- package/src/animations/spinning-counter.css +2 -6
- package/src/animations/success-check.css +2 -15
- package/src/animations/thinking-states.css +2 -7
- package/src/brand/brand.test.ts +21 -5
- package/src/brand/index.ts +4 -10
- package/src/canvas/README.md +9 -12
- package/src/canvas/card/card-frame.svelte +43 -39
- package/src/canvas/card/resize-handles.svelte +89 -80
- package/src/canvas/chrome/chrome-zoom.svelte +101 -100
- package/src/canvas/chrome/lock-badge.svelte +8 -8
- package/src/canvas/chrome/side-handles.svelte +24 -19
- package/src/canvas/chrome/size-chip.svelte +30 -30
- package/src/canvas/chrome/snap-guides.svelte +20 -20
- package/src/canvas/components/icons/ToolIcon.svelte +8 -8
- package/src/canvas/components/shared/svg/PreviewSvg.svelte +19 -19
- package/src/canvas/components/shared/svg/ShapeOutline.svelte +71 -48
- package/src/canvas/components/shared/svg/StrokeNode.svelte +21 -27
- package/src/canvas/components/shared/svg/TextRule.svelte +25 -17
- package/src/canvas/edges/bend-handle.svelte +46 -46
- package/src/canvas/edges/edge-gradient.svelte +10 -10
- package/src/canvas/edges/gradient-edge.svelte +45 -29
- package/src/canvas/editor/lock.test.ts +6 -1
- package/src/canvas/editor/style.ts +7 -1
- package/src/canvas/headless/svg-figure.test.ts +5 -1
- package/src/canvas/lod/lod-switch.svelte +111 -106
- package/src/canvas/pen/PenField.svelte +87 -81
- package/src/canvas/pen/PenOverlay.svelte +127 -127
- package/src/canvas/pen/ShapeNode.svelte +31 -32
- package/src/canvas/pen/TextNode.svelte +54 -53
- package/src/canvas/pen/host-field.test.svelte +7 -7
- package/src/canvas/pen/shapes.ts +7 -1
- package/src/canvas/screen/GlyphIcon.svelte +22 -17
- package/src/canvas/screen/PartView.svelte +138 -72
- package/src/canvas/screen/ScreenBody.svelte +81 -60
- package/src/canvas/screen/ScreenCard.svelte +20 -20
- package/src/canvas/ui/CanvasToolbar.svelte +173 -85
- package/src/canvas/ui/FrameLayer.svelte +59 -59
- package/src/canvas/ui/PenLayer.svelte +39 -30
- package/src/canvas/ui/StylePanel.svelte +119 -102
- package/src/canvas/ui/canvas-grid.svelte +137 -129
- package/src/charts/animated-value.svelte +1 -1
- package/src/charts/area-chart.css +2 -5
- package/src/charts/area-chart.svelte +1 -1
- package/src/charts/bar-chart.css +2 -3
- package/src/charts/bar-chart.svelte +1 -1
- package/src/charts/charts.css +5 -17
- package/src/charts/crosshair.svelte +1 -1
- package/src/charts/dashboard/dashboard-empty.svelte +25 -25
- package/src/charts/dashboard/dashboard-section.svelte +24 -24
- package/src/charts/dashboard/data-panel.svelte +50 -50
- package/src/charts/dashboard/data-sort-head.svelte +56 -56
- package/src/charts/dashboard/data-table.svelte +138 -138
- package/src/charts/dashboard/panel-grid.svelte +30 -30
- package/src/charts/dashboard/period-filter.svelte +25 -25
- package/src/charts/dashboard/rating-chip.svelte +19 -19
- package/src/charts/dashboard/stat-band.svelte +32 -32
- package/src/charts/dashboard/stat-tile.svelte +106 -99
- package/src/charts/donut-chart.svelte +1 -1
- package/src/charts/edge-fade.svelte +1 -1
- package/src/charts/figure.svelte +1 -1
- package/src/charts/gauge-chart.css +4 -7
- package/src/charts/gauge-chart.svelte +1 -1
- package/src/charts/hover-focus.svelte +1 -1
- package/src/charts/legend.css +1 -3
- package/src/charts/legend.svelte +1 -1
- package/src/charts/line-chart.svelte +1 -1
- package/src/charts/number-chart.css +6 -9
- package/src/charts/number-chart.svelte +1 -1
- package/src/charts/reference-line.svelte +19 -19
- package/src/charts/reveal.svelte +1 -1
- package/src/charts/ring-chart.css +2 -4
- package/src/charts/ring-chart.svelte +1 -1
- package/src/charts/sparkline.svelte +5 -2
- package/src/charts/stat-card.svelte +1 -1
- package/src/charts/time-series-chart.css +2 -5
- package/src/charts/trend-pill.css +2 -4
- package/src/charts/trend-pill.svelte +1 -1
- package/src/components/copy/copy-button.svelte +93 -98
- package/src/components/copy/copy-button.test.ts +10 -2
- package/src/components/data-table/infinite-scroll.svelte +42 -42
- package/src/components/data-table/pagination.svelte +34 -22
- package/src/components/data-table/row-arrow.svelte +19 -16
- package/src/components/data-table/row-context-menu.svelte +52 -52
- package/src/components/data-table/sortable-head.svelte +25 -25
- package/src/components/data-table/table-skeleton.svelte +41 -36
- package/src/components/docs/doc-page.svelte +18 -9
- package/src/components/docs/on-this-page.svelte +16 -3
- package/src/components/docs/scroll-spy.svelte.ts +2 -3
- package/src/components/field-row/field-row.svelte +10 -8
- package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
- package/src/components/icon/icon.svelte +14 -14
- package/src/components/language-flag/language-flag.svelte +3 -1
- package/src/components/list-page/detail-empty.svelte +34 -34
- package/src/components/list-page/detail-header.svelte +55 -47
- package/src/components/list-page/detail-panel.svelte +51 -59
- package/src/components/list-page/detail-row.svelte +19 -19
- package/src/components/list-page/detail-stack.svelte +21 -21
- package/src/components/list-page/empty-state.svelte +30 -30
- package/src/components/list-page/filter-item.svelte +20 -20
- package/src/components/list-page/filter-popover.svelte +60 -55
- package/src/components/list-page/grouped-list.svelte +4 -2
- package/src/components/list-page/index.ts +8 -4
- package/src/components/list-page/list-page-shell.svelte +2 -5
- package/src/components/list-page/master-detail.svelte +87 -88
- package/src/components/list-page/notice-bar.svelte +38 -38
- package/src/components/list-page/prev-next.svelte +63 -63
- package/src/components/list-page/rail-body.svelte +18 -18
- package/src/components/list-page/rail-empty.svelte +23 -30
- package/src/components/list-page/rail-head.svelte +24 -24
- package/src/components/list-page/rail-row.svelte +77 -73
- package/src/components/list-page/section-host.ts +2 -3
- package/src/components/locale-switch/locale-switch.svelte +41 -41
- package/src/components/mark/mark.svelte +34 -38
- package/src/components/open-in/index.ts +1 -5
- package/src/components/open-in/open-in.svelte +40 -40
- package/src/components/picker/mark-picker.svelte +49 -49
- package/src/components/picker/picker.svelte +5 -3
- package/src/components/section-header/index.ts +1 -0
- package/src/components/section-header/section-header.svelte +1 -1
- package/src/components/section-header/section-header.test.ts +7 -1
- package/src/components/section-header/section-label.svelte +39 -0
- package/src/components/segmented/animated-tabs.svelte +31 -2
- package/src/components/segmented/collapse.test.ts +2 -1
- package/src/components/segmented/index.ts +2 -0
- package/src/components/segmented/segment.svelte +52 -49
- package/src/components/segmented/segmented-filter.svelte +79 -80
- package/src/components/segmented/segmented-nav.svelte +34 -40
- package/src/components/segmented/segmented-overflow.svelte +53 -50
- package/src/components/segmented/segmented-track.svelte +62 -46
- package/src/components/segmented/segmented.ts +2 -3
- package/src/components/star-rating-input/star-rating-input.svelte +103 -103
- package/src/components/svg/svg.svelte +4 -4
- package/src/components/table-view/table-view.svelte +17 -2
- package/src/components/table-view/table-view.test.ts +7 -1
- package/src/components/table-view/types.ts +11 -11
- package/src/components/tags-input/index.ts +13 -0
- package/src/components/tags-input/tags-input.svelte +305 -0
- package/src/components/tags-input/tags.test.ts +64 -0
- package/src/components/tags-input/tags.ts +142 -0
- package/src/components/theme/index.ts +2 -4
- package/src/components/theme/theme-provider.svelte +1 -3
- package/src/components/toolbar/index.ts +2 -0
- package/src/components/{list-page → toolbar}/toolbar-search.svelte +25 -25
- package/src/components/toolbar/toolbar.svelte +13 -9
- package/src/components/toolbar/toolbar.test.ts +10 -1
- package/src/design/index.css +21 -78
- package/src/design/polish.css +26 -35
- package/src/editor/editor-toolbar.svelte +104 -99
- package/src/editor/editor.svelte +296 -266
- package/src/editor/image-toolbar.svelte +20 -22
- package/src/editor/index.ts +1 -4
- package/src/editor/inline-editor.svelte +280 -238
- package/src/editor/markdown.ts +15 -15
- package/src/editor/renderer.svelte +71 -71
- package/src/editor/slash-menu.svelte +24 -27
- package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
- package/src/effects/image-comparison/ImageComparison.svelte +11 -1
- package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +59 -46
- package/src/effects/magic-card/MagicCard.css +14 -7
- package/src/effects/marquee/Marquee.svelte +3 -5
- package/src/effects/reveal/observe.ts +0 -3
- package/src/effects/spotlight/Spotlight.svelte +1 -1
- package/src/effects/theme-toggle/ThemeToggle.css +25 -22
- package/src/effects/theme-toggle/ThemeToggle.svelte +6 -7
- package/src/gallery.test.ts +14 -4
- package/src/inbox/README.md +27 -0
- package/src/inbox/inbox.test.ts +214 -0
- package/src/inbox/index.ts +31 -0
- package/src/inbox/logic.ts +169 -0
- package/src/inbox/thread-inbox.svelte +439 -0
- package/src/inbox/types.ts +161 -0
- package/src/lib/theme-transition.ts +2 -4
- package/src/lib/tone.ts +2 -4
- package/src/lib/utils.ts +2 -2
- package/src/modules.test.ts +105 -0
- package/src/onboarding/define-tours.test.ts +55 -40
- package/src/onboarding/define-tours.ts +23 -23
- package/src/onboarding/demo.svelte +24 -5
- package/src/onboarding/index.ts +1 -8
- package/src/onboarding/onboarding-provider.svelte +19 -7
- package/src/onboarding/onboarding.svelte.ts +16 -16
- package/src/onboarding/pick.test.ts +61 -52
- package/src/onboarding/pick.ts +16 -13
- package/src/onboarding/stores.test.ts +80 -80
- package/src/onboarding/stores.ts +41 -39
- package/src/onboarding/tour-card.svelte +30 -5
- package/src/onboarding/tour-runner.svelte +12 -3
- package/src/onboarding/types.ts +20 -12
- package/src/onboarding/wait.ts +22 -22
- package/src/panel/README.md +5 -0
- package/src/panel/index.ts +2 -0
- package/src/panel/side-panel.svelte +194 -0
- package/src/record/README.md +3 -0
- package/src/record/demo-data.ts +352 -0
- package/src/record/field-section.svelte +57 -0
- package/src/record/fields.ts +194 -0
- package/src/record/format.ts +94 -0
- package/src/{tasks/record → record}/index.ts +49 -35
- package/src/{tasks/record → record}/info-list.svelte +145 -114
- package/src/record/kinds.ts +42 -0
- package/src/record/logic.ts +326 -0
- package/src/record/next-up.svelte +81 -0
- package/src/{tasks/record → record}/project-list.svelte +56 -50
- package/src/{tasks/record → record}/record-demo.svelte +130 -110
- package/src/{tasks/record → record}/record-header.svelte +37 -28
- package/src/{tasks/record → record}/record-shell.svelte +54 -54
- package/src/record/record-stats.svelte +48 -0
- package/src/record/record-timeline.svelte +251 -0
- package/src/record/record.test.ts +471 -0
- package/src/{tasks/record → record}/steps-rail.svelte +225 -216
- package/src/record/types.ts +299 -0
- package/src/record/where-we-are.svelte +125 -0
- package/src/shadcn/button/button.svelte +1 -1
- package/src/shadcn/card/card-title.svelte +1 -5
- package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
- package/src/shadcn/sheet/sheet-content.svelte +2 -4
- package/src/shadcn/table/table-cell.svelte +2 -3
- package/src/shadcn/utilities.css +3 -8
- package/src/slides/Controls.svelte +150 -124
- package/src/slides/Deck.svelte +243 -238
- package/src/slides/Fragment.svelte +64 -57
- package/src/slides/Overview.svelte +106 -101
- package/src/slides/Presenter.svelte +300 -266
- package/src/slides/Slide.svelte +185 -170
- package/src/slides/SlideList.svelte +16 -12
- package/src/slides/ambient.d.ts +1 -1
- package/src/slides/bridges.ts +42 -23
- package/src/slides/deck-controller.svelte.ts +47 -35
- package/src/slides/demo-slides.svelte +330 -306
- package/src/slides/demo.svelte +164 -160
- package/src/slides/export/capture.ts +20 -16
- package/src/slides/export/ffmpeg.ts +43 -38
- package/src/slides/export/index.ts +5 -5
- package/src/slides/export/launch.ts +3 -1
- package/src/slides/export/pdf.ts +30 -27
- package/src/slides/export/types.ts +10 -8
- package/src/slides/index.ts +11 -11
- package/src/slides/labels.ts +22 -19
- package/src/slides/layout.ts +9 -2
- package/src/slides/model.ts +4 -4
- package/src/slides/navigation.ts +43 -31
- package/src/slides/theme.css +5 -7
- package/src/slides/types.ts +9 -10
- package/src/status/README.md +18 -0
- package/src/status/define-statuses.ts +25 -0
- package/src/status/index.ts +11 -0
- package/src/{tasks → status}/status-badge.svelte +3 -1
- package/src/{tasks → status}/status-dot.svelte +1 -3
- package/src/{tasks → status}/status-icon.svelte +1 -1
- package/src/status/status-types.ts +40 -0
- package/src/tasks/README.md +1 -1
- package/src/tasks/board-order.ts +2 -4
- package/src/tasks/demo.svelte +192 -34
- package/src/tasks/detail/detail-demo.svelte +75 -15
- package/src/tasks/detail/detail-types.ts +45 -45
- package/src/tasks/detail/task-detail.svelte +40 -10
- package/src/tasks/detail/task-property.svelte +8 -2
- package/src/tasks/detail/task-thread.svelte +53 -12
- package/src/tasks/gantt/gantt-bar.svelte +17 -6
- package/src/tasks/gantt/gantt-grid.svelte +40 -9
- package/src/tasks/gantt/gantt-group-row.svelte +8 -2
- package/src/tasks/gantt/gantt-header.svelte +9 -3
- package/src/tasks/gantt/gantt-task-row.svelte +26 -6
- package/src/tasks/gantt/gantt.css +2 -1
- package/src/tasks/gantt/gantt.test.ts +4 -2
- package/src/tasks/gantt/logic.test.ts +110 -20
- package/src/tasks/gantt/logic.ts +42 -6
- package/src/tasks/inbox/inbox-card.svelte +15 -7
- package/src/tasks/inbox/inbox-draft.svelte +1 -1
- package/src/tasks/inbox/inbox-logic.ts +6 -40
- package/src/tasks/inbox/inbox-statuses.ts +24 -24
- package/src/tasks/inbox/inbox-trace.svelte +9 -3
- package/src/tasks/inbox/inbox-types.ts +54 -58
- package/src/tasks/inbox/index.ts +22 -21
- package/src/tasks/index.ts +52 -53
- package/src/tasks/kanban-board.svelte +16 -12
- package/src/tasks/kanban-types.ts +2 -3
- package/src/tasks/presets/sales.ts +7 -7
- package/src/tasks/task-gantt.svelte +28 -8
- package/src/tasks/task-list.svelte +41 -17
- package/src/tasks/time/duration.ts +40 -40
- package/src/tasks/time/time-chip.svelte +9 -2
- package/src/tasks/time/time-dot.svelte +1 -1
- package/src/tasks/time/time-ring.svelte +11 -3
- package/src/tasks/time/time-tracker.svelte +57 -15
- package/src/tasks/time/time-types.ts +57 -57
- package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
- package/src/thread/README.md +31 -0
- package/src/thread/index.ts +12 -0
- package/src/thread/logic.ts +61 -0
- package/src/thread/thread.svelte +432 -0
- package/src/thread/thread.test.ts +73 -0
- package/src/thread/types.ts +93 -0
- package/src/tokens/craft.css +17 -53
- package/src/tokens/index.css +3 -13
- package/src/tokens/palette.css +13 -11
- package/src/tokens/primitives.css +10 -23
- package/src/components/list-page/section-label.svelte +0 -39
- package/src/tasks/define-statuses.ts +0 -32
- package/src/tasks/inbox/inbox.svelte +0 -203
- package/src/tasks/record/demo-data.ts +0 -286
- package/src/tasks/record/field-section.svelte +0 -53
- package/src/tasks/record/fields.ts +0 -142
- package/src/tasks/record/format.ts +0 -87
- package/src/tasks/record/kinds.ts +0 -42
- package/src/tasks/record/logic.ts +0 -324
- package/src/tasks/record/next-up.svelte +0 -70
- package/src/tasks/record/record-stats.svelte +0 -48
- package/src/tasks/record/record-timeline.svelte +0 -231
- package/src/tasks/record/record.test.ts +0 -350
- package/src/tasks/record/types.ts +0 -280
- package/src/tasks/record/where-we-are.svelte +0 -121
- package/src/tasks/side-panel.svelte +0 -193
- package/src/tasks/status-types.ts +0 -40
- /package/src/components/{list-page → toolbar}/toolbar-trigger.ts +0 -0
- /package/src/{tasks → panel}/side-panel-types.ts +0 -0
- /package/src/{tasks/record → record}/gutter.ts +0 -0
|
@@ -1,87 +1,87 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import "./content.css";
|
|
6
|
-
import { renderTiptapContent } from "./serializer.js";
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import "./content.css";
|
|
6
|
+
import { renderTiptapContent } from "./serializer.js";
|
|
7
7
|
|
|
8
|
-
// =========================
|
|
9
|
-
// TYPES
|
|
10
|
-
// =========================
|
|
11
|
-
interface Props {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
8
|
+
// =========================
|
|
9
|
+
// TYPES
|
|
10
|
+
// =========================
|
|
11
|
+
interface Props {
|
|
12
|
+
content: Record<string, unknown>;
|
|
13
|
+
/** Optional URL prefix to rewrite in rendered HTML (e.g. storage CDN -> local proxy). */
|
|
14
|
+
storagePrefix?: string;
|
|
15
|
+
/** Replacement path for storagePrefix rewrites. */
|
|
16
|
+
storageReplacement?: string;
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
// =========================
|
|
20
|
-
// PROPS
|
|
21
|
-
// =========================
|
|
22
|
-
let { content, storagePrefix, storageReplacement = "" }: Props = $props();
|
|
19
|
+
// =========================
|
|
20
|
+
// PROPS
|
|
21
|
+
// =========================
|
|
22
|
+
let { content, storagePrefix, storageReplacement = "" }: Props = $props();
|
|
23
23
|
|
|
24
|
-
// =========================
|
|
25
|
-
// STATE
|
|
26
|
-
// =========================
|
|
27
|
-
let root = $state<HTMLDivElement | null>(null);
|
|
24
|
+
// =========================
|
|
25
|
+
// STATE
|
|
26
|
+
// =========================
|
|
27
|
+
let root = $state<HTMLDivElement | null>(null);
|
|
28
28
|
|
|
29
|
-
// =========================
|
|
30
|
-
// DERIVED
|
|
31
|
-
// =========================
|
|
29
|
+
// =========================
|
|
30
|
+
// DERIVED
|
|
31
|
+
// =========================
|
|
32
32
|
|
|
33
|
-
// WHY {@html}: the serializer only reads trusted admin CMS JSON, and unlike the
|
|
34
|
-
// old client-only action it renders in SSR, so crawlers see the body. Issue #48.
|
|
35
|
-
let html = $derived.by(() => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
33
|
+
// WHY {@html}: the serializer only reads trusted admin CMS JSON, and unlike the
|
|
34
|
+
// old client-only action it renders in SSR, so crawlers see the body. Issue #48.
|
|
35
|
+
let html = $derived.by(() => {
|
|
36
|
+
if (!content) return "";
|
|
37
|
+
let rendered = renderTiptapContent(content);
|
|
38
|
+
if (storagePrefix && storageReplacement !== undefined) {
|
|
39
|
+
rendered = rendered.replaceAll(storagePrefix, storageReplacement);
|
|
40
|
+
}
|
|
41
|
+
return rendered;
|
|
42
|
+
});
|
|
43
43
|
|
|
44
|
-
// =========================
|
|
45
|
-
// EFFECTS
|
|
46
|
-
// =========================
|
|
44
|
+
// =========================
|
|
45
|
+
// EFFECTS
|
|
46
|
+
// =========================
|
|
47
47
|
|
|
48
|
-
// WHY bound here, not onclick on the div: the facade's real <button> already
|
|
49
|
-
// handles Enter and Space. onclick on the div makes the compiler demand a role.
|
|
50
|
-
$effect(() => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
});
|
|
48
|
+
// WHY bound here, not onclick on the div: the facade's real <button> already
|
|
49
|
+
// handles Enter and Space. onclick on the div makes the compiler demand a role.
|
|
50
|
+
$effect(() => {
|
|
51
|
+
const el = root;
|
|
52
|
+
if (!el) return;
|
|
53
|
+
el.addEventListener("click", handleClick);
|
|
54
|
+
return () => el.removeEventListener("click", handleClick);
|
|
55
|
+
});
|
|
56
56
|
|
|
57
|
-
// =========================
|
|
58
|
-
// FUNCTIONS
|
|
59
|
-
// =========================
|
|
57
|
+
// =========================
|
|
58
|
+
// FUNCTIONS
|
|
59
|
+
// =========================
|
|
60
60
|
|
|
61
|
-
// WHY: the {@html} facade has no Svelte handlers, so this delegated listener swaps
|
|
62
|
-
// a clicked facade for a youtube-nocookie.com iframe: no cookies, no cost until play.
|
|
63
|
-
function handleClick(event: MouseEvent) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
// WHY: the {@html} facade has no Svelte handlers, so this delegated listener swaps
|
|
62
|
+
// a clicked facade for a youtube-nocookie.com iframe: no cookies, no cost until play.
|
|
63
|
+
function handleClick(event: MouseEvent) {
|
|
64
|
+
const target = event.target as HTMLElement | null;
|
|
65
|
+
const facade = target?.closest<HTMLElement>(".tiptap-youtube");
|
|
66
|
+
if (!facade) return;
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
const videoId = facade.dataset.videoId;
|
|
69
|
+
if (!videoId) return;
|
|
70
|
+
const start = facade.dataset.start || "0";
|
|
71
|
+
const title = facade.querySelector(".tiptap-youtube-title")?.textContent ?? "";
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
const iframe = document.createElement("iframe");
|
|
74
|
+
iframe.src = `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1&start=${start}&rel=0`;
|
|
75
|
+
iframe.title = title;
|
|
76
|
+
iframe.setAttribute("frameborder", "0");
|
|
77
|
+
iframe.setAttribute(
|
|
78
|
+
"allow",
|
|
79
|
+
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
|
|
80
|
+
);
|
|
81
|
+
iframe.allowFullscreen = true;
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
}
|
|
83
|
+
facade.replaceChildren(iframe);
|
|
84
|
+
}
|
|
85
85
|
</script>
|
|
86
86
|
|
|
87
87
|
<!-- =========================== -->
|
|
@@ -1,43 +1,38 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import type { SlashItem } from "./slash-items.js";
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import type { SlashItem } from "./slash-items.js";
|
|
6
6
|
|
|
7
|
-
// =========================
|
|
8
|
-
// TYPES
|
|
9
|
-
// =========================
|
|
10
|
-
interface Props {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
7
|
+
// =========================
|
|
8
|
+
// TYPES
|
|
9
|
+
// =========================
|
|
10
|
+
interface Props {
|
|
11
|
+
items: SlashItem[];
|
|
12
|
+
pos: { top: number; left: number };
|
|
13
|
+
selectedIndex: number;
|
|
14
|
+
/** The menu element, so the parent can close it on an outside click. */
|
|
15
|
+
el?: HTMLDivElement;
|
|
16
|
+
onselect: (item: SlashItem) => void;
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
// =========================
|
|
20
|
-
// PROPS
|
|
21
|
-
// =========================
|
|
22
|
-
let { items, pos, selectedIndex = $bindable(), el = $bindable(), onselect }: Props = $props();
|
|
19
|
+
// =========================
|
|
20
|
+
// PROPS
|
|
21
|
+
// =========================
|
|
22
|
+
let { items, pos, selectedIndex = $bindable(), el = $bindable(), onselect }: Props = $props();
|
|
23
23
|
</script>
|
|
24
24
|
|
|
25
25
|
<!-- =========================== -->
|
|
26
26
|
<!-- MARKUP -->
|
|
27
27
|
<!-- =========================== -->
|
|
28
|
-
<div
|
|
29
|
-
bind:this={el}
|
|
30
|
-
class="slash-menu"
|
|
31
|
-
style="top: {pos.top}px; left: {pos.left}px;"
|
|
32
|
-
role="listbox"
|
|
33
|
-
>
|
|
28
|
+
<div bind:this={el} class="slash-menu" style="top: {pos.top}px; left: {pos.left}px;" role="listbox">
|
|
34
29
|
{#each items as item, i}
|
|
35
30
|
<button
|
|
36
31
|
type="button"
|
|
37
32
|
class="slash-item"
|
|
38
33
|
class:selected={i === selectedIndex}
|
|
39
34
|
onclick={() => onselect(item)}
|
|
40
|
-
onpointerenter={() => selectedIndex = i}
|
|
35
|
+
onpointerenter={() => (selectedIndex = i)}
|
|
41
36
|
role="option"
|
|
42
37
|
aria-selected={i === selectedIndex}
|
|
43
38
|
>
|
|
@@ -117,7 +112,9 @@ let { items, pos, selectedIndex = $bindable(), el = $bindable(), onselect }: Pro
|
|
|
117
112
|
background: var(--color-muted);
|
|
118
113
|
color: var(--color-muted-foreground);
|
|
119
114
|
flex-shrink: 0;
|
|
120
|
-
transition:
|
|
115
|
+
transition:
|
|
116
|
+
background-color var(--duration-micro) var(--ease-smooth-out),
|
|
117
|
+
color var(--duration-micro) var(--ease-smooth-out);
|
|
121
118
|
}
|
|
122
119
|
|
|
123
120
|
.slash-item.selected .slash-icon {
|
|
@@ -200,7 +200,17 @@
|
|
|
200
200
|
/>
|
|
201
201
|
<div class="hr-compare__handle">
|
|
202
202
|
<span class="hr-compare__grip">
|
|
203
|
-
<svg
|
|
203
|
+
<svg
|
|
204
|
+
viewBox="0 0 24 24"
|
|
205
|
+
width="16"
|
|
206
|
+
height="16"
|
|
207
|
+
fill="none"
|
|
208
|
+
stroke="currentColor"
|
|
209
|
+
stroke-width="2.5"
|
|
210
|
+
stroke-linecap="round"
|
|
211
|
+
stroke-linejoin="round"
|
|
212
|
+
aria-hidden="true"
|
|
213
|
+
>
|
|
204
214
|
<polyline points="10 6 4 12 10 18"></polyline>
|
|
205
215
|
<polyline points="14 6 20 12 14 18"></polyline>
|
|
206
216
|
</svg>
|
|
@@ -1,47 +1,58 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import { ArrowRight } from "phosphor-svelte";
|
|
6
|
-
|
|
7
|
-
// =========================
|
|
8
|
-
// TYPES
|
|
9
|
-
// =========================
|
|
10
|
-
interface Props {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// =========================
|
|
25
|
-
// PROPS
|
|
26
|
-
// =========================
|
|
27
|
-
let {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import { ArrowRight } from "phosphor-svelte";
|
|
6
|
+
|
|
7
|
+
// =========================
|
|
8
|
+
// TYPES
|
|
9
|
+
// =========================
|
|
10
|
+
interface Props {
|
|
11
|
+
href?: string;
|
|
12
|
+
/** `submit` posts the form the button sits in. */
|
|
13
|
+
type?: "button" | "submit";
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
size?: "default" | "md" | "sm";
|
|
16
|
+
tone?: "ink" | "coral";
|
|
17
|
+
beam?: boolean;
|
|
18
|
+
dot?: boolean;
|
|
19
|
+
class?: string;
|
|
20
|
+
onclick?: (event: MouseEvent) => void;
|
|
21
|
+
children: import("svelte").Snippet;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// =========================
|
|
25
|
+
// PROPS
|
|
26
|
+
// =========================
|
|
27
|
+
let {
|
|
28
|
+
href,
|
|
29
|
+
type = "button",
|
|
30
|
+
disabled = false,
|
|
31
|
+
size = "default",
|
|
32
|
+
tone = "ink",
|
|
33
|
+
beam = true,
|
|
34
|
+
dot = true,
|
|
35
|
+
class: className,
|
|
36
|
+
onclick,
|
|
37
|
+
children,
|
|
38
|
+
}: Props = $props();
|
|
39
|
+
|
|
40
|
+
// =========================
|
|
41
|
+
// DERIVED
|
|
42
|
+
// =========================
|
|
43
|
+
let classes = $derived(
|
|
44
|
+
[
|
|
45
|
+
"cta-hover",
|
|
46
|
+
size === "md" && "cta-hover--md",
|
|
47
|
+
size === "sm" && "cta-hover--sm",
|
|
48
|
+
tone === "coral" && "cta-hover--coral",
|
|
49
|
+
!beam && "cta-hover--no-beam",
|
|
50
|
+
!dot && "cta-hover--no-dot",
|
|
51
|
+
className,
|
|
52
|
+
]
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.join(" "),
|
|
55
|
+
);
|
|
45
56
|
</script>
|
|
46
57
|
|
|
47
58
|
<!-- =========================== -->
|
|
@@ -123,8 +134,8 @@ let classes = $derived(
|
|
|
123
134
|
background: conic-gradient(
|
|
124
135
|
transparent 0deg,
|
|
125
136
|
transparent 220deg,
|
|
126
|
-
oklch(0.65 0.
|
|
127
|
-
oklch(0.82 0.
|
|
137
|
+
oklch(0.65 0.2 30) 280deg,
|
|
138
|
+
oklch(0.82 0.1 30) 320deg,
|
|
128
139
|
transparent 360deg
|
|
129
140
|
);
|
|
130
141
|
transform: translate(-50%, -50%);
|
|
@@ -230,7 +241,9 @@ let classes = $derived(
|
|
|
230
241
|
justify-content: center;
|
|
231
242
|
gap: 8px;
|
|
232
243
|
width: 100%;
|
|
233
|
-
transition:
|
|
244
|
+
transition:
|
|
245
|
+
opacity var(--duration-fast) var(--ease-smooth-out),
|
|
246
|
+
transform var(--duration-fast) var(--ease-smooth-out);
|
|
234
247
|
}
|
|
235
248
|
|
|
236
249
|
/* signal-600, not signal-400: the dot becomes the hover fill under a paper
|
|
@@ -6,9 +6,20 @@
|
|
|
6
6
|
box-shadow 300ms ease;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
@media (hover: hover) and (pointer: fine) {
|
|
10
|
+
.magic-card:hover {
|
|
11
|
+
border-color: color-mix(
|
|
12
|
+
in oklch,
|
|
13
|
+
var(--magic-card-glow, var(--color-primary)) 30%,
|
|
14
|
+
var(--color-border)
|
|
15
|
+
);
|
|
16
|
+
box-shadow: 0 0 0 1px
|
|
17
|
+
color-mix(in oklch, var(--magic-card-glow, var(--color-primary)) 8%, transparent);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.magic-card:hover .magic-card__glow {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
}
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
.magic-card:focus-within {
|
|
@@ -35,10 +46,6 @@
|
|
|
35
46
|
);
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
.magic-card:hover .magic-card__glow {
|
|
39
|
-
opacity: 1;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
49
|
@media (prefers-reduced-motion: reduce) {
|
|
43
50
|
.magic-card,
|
|
44
51
|
.magic-card__glow {
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
/** Waits this many ms before the first scroll starts. */
|
|
26
26
|
delay?: number;
|
|
27
27
|
/**
|
|
28
|
-
* Scrolls only while the content
|
|
29
|
-
*
|
|
28
|
+
* Scrolls only while the content overflows the box; when it fits, the box shrinks to it,
|
|
29
|
+
* drops its padding and shows one still copy.
|
|
30
30
|
*/
|
|
31
31
|
overflowOnly?: boolean;
|
|
32
32
|
style?: string;
|
|
@@ -73,9 +73,7 @@
|
|
|
73
73
|
let still = $derived(overflowOnly && !overflowing);
|
|
74
74
|
// WHY: two copies loop seamlessly once one copy is wider than the box.
|
|
75
75
|
let copies = $derived(overflowOnly ? (overflowing ? 2 : 1) : repeat);
|
|
76
|
-
let loopSeconds = $derived(
|
|
77
|
-
speed && trackSize > 0 ? (trackSize + gapSize) / speed : undefined,
|
|
78
|
-
);
|
|
76
|
+
let loopSeconds = $derived(speed && trackSize > 0 ? (trackSize + gapSize) / speed : undefined);
|
|
79
77
|
|
|
80
78
|
// WHY: only emit the CSS vars that were passed as props, so a consumer can
|
|
81
79
|
// still override timing or spacing with a `[--duration:24s]` utility class.
|
|
@@ -36,9 +36,6 @@ export function observeInView(
|
|
|
36
36
|
/**
|
|
37
37
|
* Svelte action that calls back when the node enters or leaves the viewport.
|
|
38
38
|
* Options are read at call time, so a `use:onInView={...}` update swaps the callbacks.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* <section use:onInView={{ amount: 0.2, onEnter: () => (visible = true) }}>
|
|
42
39
|
*/
|
|
43
40
|
export const onInView: Action<HTMLElement, InViewActionOptions | undefined> = (node, initial) => {
|
|
44
41
|
let options = initial ?? {};
|
|
@@ -8,17 +8,16 @@
|
|
|
8
8
|
color: color-mix(in oklch, var(--color-background) 70%, transparent);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
@media (hover: hover) and (pointer: fine) {
|
|
12
|
+
.theme-toggle--inverted:hover {
|
|
13
|
+
border-color: color-mix(in oklch, var(--color-background) 40%, transparent);
|
|
14
|
+
background: color-mix(in oklch, var(--color-background) 12%, transparent);
|
|
15
|
+
color: var(--color-background);
|
|
16
|
+
}
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
/* The slide variant
|
|
18
|
-
|
|
19
|
-
--theme-toggle-thumb-light, --theme-toggle-thumb-dark, --theme-toggle-track,
|
|
20
|
-
--theme-toggle-track-hover, --theme-toggle-sun, --theme-toggle-moon,
|
|
21
|
-
--theme-toggle-thumb-icon and --theme-toggle-glow. */
|
|
19
|
+
/* The slide variant, a pill track with a moving thumb; a caller sets brand
|
|
20
|
+
colours through the `--theme-toggle-*` custom properties on this class. */
|
|
22
21
|
.theme-toggle-slide {
|
|
23
22
|
--tt-thumb: 1.5rem;
|
|
24
23
|
--tt-pad: 0.25rem;
|
|
@@ -46,12 +45,18 @@
|
|
|
46
45
|
--tt-thumb: 2rem;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
@media (hover: hover) and (pointer: fine) {
|
|
49
|
+
.theme-toggle-slide:hover {
|
|
50
|
+
background: var(
|
|
51
|
+
--theme-toggle-track-hover,
|
|
52
|
+
color-mix(in oklch, var(--color-foreground) 20%, transparent)
|
|
53
|
+
);
|
|
54
|
+
box-shadow: 0 0 15px color-mix(in oklch, var(--theme-toggle-glow, transparent) 30%, transparent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.theme-toggle-slide:hover .theme-toggle-slide__glow {
|
|
58
|
+
opacity: 1;
|
|
59
|
+
}
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
.theme-toggle-slide:active {
|
|
@@ -77,10 +82,6 @@
|
|
|
77
82
|
transition: opacity 500ms;
|
|
78
83
|
}
|
|
79
84
|
|
|
80
|
-
.theme-toggle-slide:hover .theme-toggle-slide__glow {
|
|
81
|
-
opacity: 1;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
85
|
.theme-toggle-slide[data-state="dark"] {
|
|
85
86
|
--tt-glow-x: 75%;
|
|
86
87
|
}
|
|
@@ -130,7 +131,9 @@
|
|
|
130
131
|
border-radius: 9999px;
|
|
131
132
|
background: var(--tt-thumb-color);
|
|
132
133
|
color: var(--theme-toggle-thumb-icon, var(--color-primary-foreground));
|
|
133
|
-
box-shadow:
|
|
134
|
+
box-shadow:
|
|
135
|
+
0 10px 15px -3px rgb(0 0 0 / 0.1),
|
|
136
|
+
0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
134
137
|
transition:
|
|
135
138
|
transform 500ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
136
139
|
background-color 500ms;
|
|
@@ -164,12 +167,12 @@
|
|
|
164
167
|
|
|
165
168
|
.theme-toggle-slide__mark--moon {
|
|
166
169
|
opacity: 0;
|
|
167
|
-
transform: scale(0) rotate(-90deg);
|
|
170
|
+
transform: scale(0.9) rotate(-90deg);
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
.theme-toggle-slide[data-state="dark"] .theme-toggle-slide__mark--sun {
|
|
171
174
|
opacity: 0;
|
|
172
|
-
transform: scale(0) rotate(90deg);
|
|
175
|
+
transform: scale(0.9) rotate(90deg);
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
.theme-toggle-slide[data-state="dark"] .theme-toggle-slide__mark--moon {
|
|
@@ -53,12 +53,7 @@
|
|
|
53
53
|
let active = $derived(mode ?? (isDark ? "dark" : "light"));
|
|
54
54
|
let style = $derived(inverted ? "inverted" : variant);
|
|
55
55
|
let text = $derived(
|
|
56
|
-
label ??
|
|
57
|
-
(mode
|
|
58
|
-
? `Theme: ${mode}`
|
|
59
|
-
: isDark
|
|
60
|
-
? "Switch to light mode"
|
|
61
|
-
: "Switch to dark mode"),
|
|
56
|
+
label ?? (mode ? `Theme: ${mode}` : isDark ? "Switch to light mode" : "Switch to dark mode"),
|
|
62
57
|
);
|
|
63
58
|
|
|
64
59
|
// =========================
|
|
@@ -97,7 +92,11 @@
|
|
|
97
92
|
<span class="theme-toggle-slide__thumb" aria-hidden="true">
|
|
98
93
|
<span class="theme-toggle-slide__ring"></span>
|
|
99
94
|
<Sun size={14} weight="fill" class="theme-toggle-slide__mark theme-toggle-slide__mark--sun" />
|
|
100
|
-
<Moon
|
|
95
|
+
<Moon
|
|
96
|
+
size={14}
|
|
97
|
+
weight="fill"
|
|
98
|
+
class="theme-toggle-slide__mark theme-toggle-slide__mark--moon"
|
|
99
|
+
/>
|
|
101
100
|
</span>
|
|
102
101
|
<span class="sr-only">{text}</span>
|
|
103
102
|
</button>
|
package/src/gallery.test.ts
CHANGED
|
@@ -51,21 +51,31 @@ describe("site gallery coverage", () => {
|
|
|
51
51
|
it("gives every component, AI part and effect a place on the site", () => {
|
|
52
52
|
const walk = (dir: string): string[] =>
|
|
53
53
|
readdirSync(dir, { withFileTypes: true }).flatMap((d) =>
|
|
54
|
-
d.isDirectory()
|
|
54
|
+
d.isDirectory()
|
|
55
|
+
? walk(join(dir, d.name))
|
|
56
|
+
: /\.(svelte|ts)$/.test(d.name)
|
|
57
|
+
? [join(dir, d.name)]
|
|
58
|
+
: [],
|
|
55
59
|
);
|
|
56
|
-
const site = walk(join(SITE, ".."))
|
|
60
|
+
const site = walk(join(SITE, ".."))
|
|
61
|
+
.map((file) => readFileSync(file, "utf8"))
|
|
62
|
+
.join("\n");
|
|
57
63
|
const parts = ["components", "ai", "effects"].flatMap((top) =>
|
|
58
64
|
readdirSync(join(PKG, "src", top), { withFileTypes: true })
|
|
59
65
|
.filter((d) => d.isDirectory())
|
|
60
66
|
.map((d) => `${top}/${d.name}`),
|
|
61
67
|
);
|
|
62
|
-
expect(
|
|
68
|
+
expect(
|
|
69
|
+
parts.filter((part) => !site.includes(`${part}/`) && !site.includes(`${part}"`)),
|
|
70
|
+
).toEqual([]);
|
|
63
71
|
});
|
|
64
72
|
|
|
65
73
|
it("keeps a demo file behind every listed primitive", () => {
|
|
66
74
|
const manifest = readFileSync(join(SITE, "shadcn-demos", "index.ts"), "utf8");
|
|
67
75
|
const listed = [...manifest.matchAll(/name: "([a-z-]+)"/g)].map((m) => m[1]);
|
|
68
76
|
expect(listed.length).toBeGreaterThan(0);
|
|
69
|
-
expect(
|
|
77
|
+
expect(
|
|
78
|
+
listed.filter((name) => !existsSync(join(SITE, "shadcn-demos", `${name}.svelte`))),
|
|
79
|
+
).toEqual([]);
|
|
70
80
|
});
|
|
71
81
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @heyramzi/ui/inbox
|
|
2
|
+
|
|
3
|
+
One threaded inbox. Entries from any source (a comment, a status change, an assignment, a mention, an agent reply, an agent's ask) group into one thread per subject, and the screen shows the thread list on the left and the selected thread on the right. The inbox never fetches. Everything leaves through callbacks.
|
|
4
|
+
|
|
5
|
+
```svelte
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import { ThreadInbox, type InboxEntry } from "@heyramzi/ui/inbox";
|
|
8
|
+
|
|
9
|
+
let entries = $state<InboxEntry[]>([]);
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<ThreadInbox
|
|
13
|
+
{entries}
|
|
14
|
+
unreadFirst
|
|
15
|
+
onRead={(thread, read) => mark(thread.key, read)}
|
|
16
|
+
onSnooze={({ thread, until }) => snooze(thread.key, until)}
|
|
17
|
+
onDecide={(verdict, thread) => decide(verdict)}
|
|
18
|
+
/>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **Grouping.** `threadsOf(entries, { unreadFirst })` keys on `subject.type` plus `subject.id`, sorts threads by their newest entry and entries oldest first. An entry with no subject id is dropped. A malformed timestamp sorts last. Each thread carries `unread`, `latestAt` and `pending`.
|
|
22
|
+
- **Needs you.** A `proposal` entry whose `proposal` (an `InboxItem`) is still open pins to the top of its thread, drawn with `InboxCard`. The "Needs you" filter keeps threads that hold one. Verdicts come out of `onDecide`.
|
|
23
|
+
- **Filters.** By subject type, by actor name and by Needs you. `filterThreads` runs them without the component.
|
|
24
|
+
- **Keys.** `j` and `k` move, `u` marks the thread read (or unread when nothing is unread), `h` opens the snooze row (`1`, `2`, `3` pick a time), and `a`, `d`, `s`, `e` act on the pinned proposal. A key does nothing while focus is in an input, a textarea, a select or an editable element, or while a modifier is held. `inboxKeyOf(event)` is the same check.
|
|
25
|
+
- **Snooze.** `onSnooze({ thread, until })` gets a `Date`: 3 hours on, tomorrow 9:00 or next Monday 9:00 (`snoozeChoices(now)`). The host decides what snoozed means and stops passing the entries.
|
|
26
|
+
- **Read.** `onRead(thread, read)` is true when the thread had unread entries, so the host marks them all read.
|
|
27
|
+
- **Words.** `labels` is a partial of `InboxLabels`. Status and assignment lines have `{from}`, `{to}` and `{assignee}` slots. `typeLabels` names the subject types. The `avatar` and `status` snippets let the app draw its own portrait and status badge.
|