@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
package/src/tokens/craft.css
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/* ── Radius, by role not by size ─────────────────────────────
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
home so `rounded-lg` on a button already resolves to the control radius and
|
|
5
|
-
no component has to learn a new class. */
|
|
2
|
+
These override Tailwind's own names, so `rounded-lg` on a button already
|
|
3
|
+
resolves to the control radius. */
|
|
6
4
|
@theme {
|
|
7
5
|
--radius-xs: 4px; /* tag, dot, inline code */
|
|
8
6
|
--radius-sm: 6px; /* chip, badge, small pill */
|
|
@@ -14,30 +12,9 @@
|
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
/* ── Elevation ───────────────────────────────────────────────
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
WHY the steps are shadow-only and the hairline is its own token: most boxes
|
|
20
|
-
in a kit already draw a border, and an elevation that bakes a `0 0 0 1px`
|
|
21
|
-
ring into every step gives those boxes two edges a pixel apart. Compose them
|
|
22
|
-
where a component drops its border:
|
|
23
|
-
|
|
24
|
-
box-shadow: var(--elev-hairline), var(--elev-card);
|
|
25
|
-
|
|
26
|
-
WHY each step is stacked rather than one blurred drop: a single shadow reads
|
|
27
|
-
as a sticker. Depth is several offsets falling off together, which is what
|
|
28
|
-
the eye reads as distance. They cost nothing extra because every stop is the
|
|
29
|
-
same colour at a different alpha.
|
|
30
|
-
|
|
31
|
-
WHY the blur radii are large and the alphas small: a 4px blur at 20% draws a
|
|
32
|
-
visible grey edge, which the eye reads as a second border. A 28px blur at 7%
|
|
33
|
-
is barely a colour anywhere and reads as height. Reach for more blur before
|
|
34
|
-
more alpha, every time.
|
|
35
|
-
|
|
36
|
-
`--elev-tint` is the one knob an app turns. Pure black greys a warm palette
|
|
37
|
-
out; the hue of the app's own darkest surface keeps the shadow in the
|
|
38
|
-
family. */
|
|
15
|
+
The steps carry no ring, since a bordered box would get two edges a pixel
|
|
16
|
+
apart; a borderless one adds `var(--elev-hairline)` in front. */
|
|
39
17
|
:root {
|
|
40
|
-
|
|
41
18
|
/* Resting on the page. A button, a chip, a row. */
|
|
42
19
|
--elev-btn:
|
|
43
20
|
0 1px 1px -0.5px color-mix(in oklab, var(--elev-tint) 7%, transparent),
|
|
@@ -61,9 +38,8 @@
|
|
|
61
38
|
0 12px 26px -8px color-mix(in oklab, var(--elev-tint) 12%, transparent),
|
|
62
39
|
0 40px 72px -20px color-mix(in oklab, var(--elev-tint) 20%, transparent);
|
|
63
40
|
|
|
64
|
-
/*
|
|
65
|
-
|
|
66
|
-
read as a physical key rather than a coloured rectangle. */
|
|
41
|
+
/* A hairline separates a surface from what's behind it; a rim is light on the
|
|
42
|
+
top edge, which makes a filled control read as a key. */
|
|
67
43
|
--elev-hairline: 0 0 0 1px var(--color-border, var(--border));
|
|
68
44
|
--elev-rim: inset 0 1px 0 color-mix(in oklab, white 16%, transparent);
|
|
69
45
|
|
|
@@ -71,10 +47,8 @@
|
|
|
71
47
|
--elev-field: inset 0 1px 2px color-mix(in oklab, var(--elev-tint) 7%, transparent);
|
|
72
48
|
}
|
|
73
49
|
|
|
74
|
-
/* A
|
|
75
|
-
hairline does the separating
|
|
76
|
-
dark the hairline is white at a low alpha, which is what reads as a lit edge
|
|
77
|
-
rather than as a drawn border. */
|
|
50
|
+
/* A shadow can't get darker than a dark page, so on dark the low-alpha white
|
|
51
|
+
hairline does the separating. */
|
|
78
52
|
.dark {
|
|
79
53
|
--elev-btn: 0 1px 2px color-mix(in oklab, black 30%, transparent);
|
|
80
54
|
--elev-card:
|
|
@@ -93,25 +67,19 @@
|
|
|
93
67
|
}
|
|
94
68
|
|
|
95
69
|
/* ── Focus ───────────────────────────────────────────────────
|
|
96
|
-
One
|
|
97
|
-
|
|
98
|
-
variable so a component spends one line on it and every control focuses the
|
|
99
|
-
same way. */
|
|
70
|
+
One variable, so every control focuses the same way: a solid accent edge,
|
|
71
|
+
then a soft halo. */
|
|
100
72
|
:root {
|
|
101
73
|
--ring-color: var(--color-ring, var(--ring));
|
|
102
74
|
--ring-halo: color-mix(in oklab, var(--ring-color) 16%, transparent);
|
|
103
|
-
/*
|
|
104
|
-
|
|
105
|
-
is why it is 16% over 4px and not 30% over 2px. */
|
|
75
|
+
/* The halo is 16% over 4px, not 30% over 2px, because a stronger band reads
|
|
76
|
+
as a second border instead of a glow. */
|
|
106
77
|
--ring-focus: 0 0 0 1px var(--ring-color), 0 0 0 5px var(--ring-halo);
|
|
107
78
|
}
|
|
108
79
|
|
|
109
80
|
/* ── Motion ──────────────────────────────────────────────────
|
|
110
|
-
The transitions-dev scale, verbatim, so a
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
The doctrine in one line: an open is an invitation and a close should get out
|
|
114
|
-
of the way, so every close is shorter than its open and no close bounces. */
|
|
81
|
+
The transitions-dev scale, verbatim, so a pasted snippet reads the same
|
|
82
|
+
names. Every close is shorter than its open, and no close bounces. */
|
|
115
83
|
:root {
|
|
116
84
|
--duration-stagger: 40ms;
|
|
117
85
|
--duration-micro: 80ms;
|
|
@@ -163,13 +131,9 @@
|
|
|
163
131
|
|
|
164
132
|
--ease-smooth-out: var(--ease-smooth-out);
|
|
165
133
|
|
|
166
|
-
/*
|
|
167
|
-
`duration
|
|
168
|
-
|
|
169
|
-
compiled to nothing and the element ended up at `transition-duration: 0s`:
|
|
170
|
-
no error, no warning, and a component that simply does not animate. The
|
|
171
|
-
comment at the top of this block had claimed `duration-fast` was a class
|
|
172
|
-
since the block was written. Found 31 Aug 2026 by probing a live page. */
|
|
134
|
+
/* Tailwind's `duration-*` reads `--transition-duration-*`, so without these
|
|
135
|
+
`duration-fast` silently compiles to nothing and the element doesn't animate
|
|
136
|
+
(31 Aug 2026). */
|
|
173
137
|
--transition-duration-micro: var(--duration-micro);
|
|
174
138
|
--transition-duration-quick: var(--duration-quick);
|
|
175
139
|
--transition-duration-fast: var(--duration-fast);
|
package/src/tokens/index.css
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
/* tokens.css
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* @import "tailwindcss";
|
|
6
|
-
* @import "@heyramzi/ui/tokens.css";
|
|
7
|
-
* @import "@heyramzi/ui/animations";
|
|
8
|
-
* @import "@heyramzi/ui/shadcn/utilities.css"; // only if you use Attachment or Message
|
|
9
|
-
*
|
|
10
|
-
* It carries three things: the color palette and semantic tokens, the craft
|
|
11
|
-
* layer (radius, elevation, focus and motion), and the small set of motion
|
|
12
|
-
* primitives the components share. Override any variable on :root to retheme.
|
|
13
|
-
* The parts load in this order because later blocks read earlier variables.
|
|
1
|
+
/* tokens.css: import once, right after `@import "tailwindcss"`. Override any
|
|
2
|
+
* variable on :root to retheme; the parts load in this order because later ones
|
|
3
|
+
* read earlier variables.
|
|
14
4
|
*/
|
|
15
5
|
|
|
16
6
|
@import "./palette.css";
|
package/src/tokens/palette.css
CHANGED
|
@@ -22,9 +22,8 @@
|
|
|
22
22
|
--gray-900: oklch(0.15 0 0); /* Near-black — headings, primary text */
|
|
23
23
|
|
|
24
24
|
/* === Brand roles ===
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
other token below reads from these, so nothing else in the package names a hue. */
|
|
25
|
+
Neutral defaults a brand replaces through `@heyramzi/ui/brand`; every token below reads
|
|
26
|
+
from these, so nothing else in the package names a hue. */
|
|
28
27
|
--brand: oklch(0.28 0 0); /* The signature colour: focus ring, links, active states, the mark */
|
|
29
28
|
--brand-on-dark: oklch(0.92 0 0); /* The same colour lifted for a dark page */
|
|
30
29
|
--brand-highlight: oklch(0.78 0 0); /* Warmth: badges, highlights, soft accents */
|
|
@@ -39,9 +38,9 @@
|
|
|
39
38
|
--brand-surface-light: oklch(0.99 0 0); /* Cards on a light page */
|
|
40
39
|
--brand-line-light: oklch(0.91 0 0); /* Borders on a light page */
|
|
41
40
|
|
|
42
|
-
/* === Tint scales
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
/* === Tint scales ===
|
|
42
|
+
Mixed from the three hues so `--brand` alone yields 100 to 800; 400 is the colour lifted
|
|
43
|
+
for dark, 600 the colour itself. */
|
|
45
44
|
--brand-100: color-mix(in oklab, var(--brand) 12%, white);
|
|
46
45
|
--brand-200: color-mix(in oklab, var(--brand) 28%, white);
|
|
47
46
|
--brand-400: var(--brand-on-dark);
|
|
@@ -117,8 +116,7 @@
|
|
|
117
116
|
--sidebar-ring: var(--brand);
|
|
118
117
|
|
|
119
118
|
/* === Shadow tint ===
|
|
120
|
-
|
|
121
|
-
so the shadow takes the darkest surface's hue. Radius, focus and motion live there. */
|
|
119
|
+
Pure black greys out warm surfaces, so the shadow takes the darkest surface's hue. */
|
|
122
120
|
--elev-tint: var(--brand-ink);
|
|
123
121
|
}
|
|
124
122
|
|
|
@@ -198,14 +196,18 @@
|
|
|
198
196
|
--color-cat-indigo: var(--cat-indigo);
|
|
199
197
|
--color-cat-purple: var(--cat-purple);
|
|
200
198
|
|
|
201
|
-
/* A hue as text
|
|
202
|
-
|
|
199
|
+
/* A hue as text on its own tint mixes 40% toward the foreground to clear AA in both themes,
|
|
200
|
+
where plain destructive text on that tint is 3.6:1. */
|
|
203
201
|
--color-cat-cyan-ink: color-mix(in oklab, var(--color-cat-cyan) 60%, var(--color-foreground));
|
|
204
202
|
--color-cat-teal-ink: color-mix(in oklab, var(--color-cat-teal) 60%, var(--color-foreground));
|
|
205
203
|
--color-cat-purple-ink: color-mix(in oklab, var(--color-cat-purple) 60%, var(--color-foreground));
|
|
206
204
|
--color-cat-amber-ink: color-mix(in oklab, var(--color-cat-amber) 60%, var(--color-foreground));
|
|
207
205
|
--color-success-ink: color-mix(in oklab, var(--color-success) 60%, var(--color-foreground));
|
|
208
|
-
--color-destructive-ink: color-mix(
|
|
206
|
+
--color-destructive-ink: color-mix(
|
|
207
|
+
in oklab,
|
|
208
|
+
var(--color-destructive) 60%,
|
|
209
|
+
var(--color-foreground)
|
|
210
|
+
);
|
|
209
211
|
|
|
210
212
|
/* Light theme color system: the light surfaces and lines of the brand roles */
|
|
211
213
|
--color-background: var(--gray-50);
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
3
|
-
* bits-ui v2 writes `data-checked` / `data-open` and v1 wrote
|
|
4
|
-
* `data-state="checked"`, so each variant matches both and a component does not
|
|
5
|
-
* have to know which it is talking to. They live here rather than in a
|
|
6
|
-
* consumer's stylesheet because the classes that need them ship in this
|
|
7
|
-
* package: an app that defines only the palette would render a switch that
|
|
8
|
-
* never moves. */
|
|
1
|
+
/* Each variant matches both bits-ui v2's `data-checked` and v1's `data-state="checked"`, and
|
|
2
|
+
* ships here because an app that defines only the palette gets a switch that never moves. */
|
|
9
3
|
@custom-variant data-open {
|
|
10
4
|
&:where([data-state="open"]),
|
|
11
5
|
&:where([data-open]:not([data-open="false"])) {
|
|
@@ -80,9 +74,8 @@
|
|
|
80
74
|
background: color-mix(in oklab, var(--ring-color) 24%, transparent);
|
|
81
75
|
}
|
|
82
76
|
|
|
83
|
-
/* A default macOS overlay scrollbar paints a black bar on a dark panel
|
|
84
|
-
transparent
|
|
85
|
-
is over. The border is what insets the thumb without a second element. */
|
|
77
|
+
/* A default macOS overlay scrollbar paints a black bar on a dark panel, so the track stays
|
|
78
|
+
transparent and the thumb takes the line token. */
|
|
86
79
|
:where(html) {
|
|
87
80
|
scrollbar-color: var(--color-border, var(--border)) transparent;
|
|
88
81
|
}
|
|
@@ -111,16 +104,11 @@
|
|
|
111
104
|
}
|
|
112
105
|
|
|
113
106
|
@layer components {
|
|
114
|
-
/*
|
|
115
|
-
|
|
116
|
-
motion guard then lives in one place instead of in every component.
|
|
107
|
+
/* Classes, not tokens, so a component takes one name and the reduced-motion guard lives in
|
|
108
|
+
one place; the `t-` prefix matches the transitions-dev snippets. */
|
|
117
109
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
/* A control that reads as a key: it travels down a pixel under the finger.
|
|
122
|
-
The travel is what sells it, because a colour change alone reads as a hover
|
|
123
|
-
state, not as a press. */
|
|
110
|
+
/* A key-like control travels down a pixel under the finger, because a colour change alone
|
|
111
|
+
reads as a hover, not a press. */
|
|
124
112
|
.t-press {
|
|
125
113
|
transition:
|
|
126
114
|
background-color var(--duration-quick) var(--ease-smooth-out),
|
|
@@ -134,9 +122,8 @@
|
|
|
134
122
|
transform: translateY(1px);
|
|
135
123
|
}
|
|
136
124
|
|
|
137
|
-
/* A card
|
|
138
|
-
|
|
139
|
-
than as its border changing colour. */
|
|
125
|
+
/* A hover card rises a pixel and gains the next elevation step, so it reads as coming
|
|
126
|
+
forward, not as its border changing colour. */
|
|
140
127
|
.t-lift {
|
|
141
128
|
transition:
|
|
142
129
|
box-shadow var(--duration-fast) var(--ease-smooth-out),
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import type { Snippet } from "svelte";
|
|
6
|
-
import { cn } from "../../lib/utils.js";
|
|
7
|
-
import type { PhosphorIcon } from "../../lib/icon-types.js";
|
|
8
|
-
|
|
9
|
-
// =========================
|
|
10
|
-
// TYPES
|
|
11
|
-
// =========================
|
|
12
|
-
interface Props {
|
|
13
|
-
// The one label style for panel, rail group and sub-section headers. Retyped
|
|
14
|
-
// copies drifted to a different tracking value.
|
|
15
|
-
icon?: PhosphorIcon;
|
|
16
|
-
children: Snippet;
|
|
17
|
-
class?: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// =========================
|
|
21
|
-
// PROPS
|
|
22
|
-
// =========================
|
|
23
|
-
let { icon: Icon, children, class: className }: Props = $props();
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
<!-- =========================== -->
|
|
27
|
-
<!-- MARKUP -->
|
|
28
|
-
<!-- =========================== -->
|
|
29
|
-
<span
|
|
30
|
-
class={cn(
|
|
31
|
-
"flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground",
|
|
32
|
-
className,
|
|
33
|
-
)}
|
|
34
|
-
>
|
|
35
|
-
{#if Icon}
|
|
36
|
-
<Icon class="size-4 text-brand" aria-hidden="true" />
|
|
37
|
-
{/if}
|
|
38
|
-
{@render children()}
|
|
39
|
-
</span>
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { StatusDef, StatusId, StatusMap, Statuses } from "./status-types.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Build a status registry from a plain map. The registry keeps the map's keys as
|
|
5
|
-
* a union, so `statuses.ids` and lookups stay typed.
|
|
6
|
-
*
|
|
7
|
-
* ```ts
|
|
8
|
-
* const statuses = defineStatuses({
|
|
9
|
-
* draft: { label: "Draft", tone: "neutral" },
|
|
10
|
-
* sent: { label: "Sent", tone: "info" },
|
|
11
|
-
* });
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
export function defineStatuses<const M extends StatusMap>(
|
|
15
|
-
map: M,
|
|
16
|
-
): Statuses & { ids: StatusId<M>[] } {
|
|
17
|
-
const lookup = map as Record<string, StatusDef | undefined>;
|
|
18
|
-
const find = (id: string): StatusDef | undefined => lookup[id];
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
map,
|
|
22
|
-
ids: Object.keys(map) as StatusId<M>[],
|
|
23
|
-
has: (id) => find(id) !== undefined,
|
|
24
|
-
get: find,
|
|
25
|
-
label: (id) => find(id)?.label ?? id,
|
|
26
|
-
tone: (id) => find(id)?.tone ?? "neutral",
|
|
27
|
-
icon: (id) => find(id)?.icon,
|
|
28
|
-
variant: (id) => find(id)?.variant ?? "open",
|
|
29
|
-
progress: (id) => find(id)?.progress,
|
|
30
|
-
color: (id) => find(id)?.color,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { tick, type Snippet } from "svelte";
|
|
3
|
-
import { Badge } from "../../shadcn/badge/index.js";
|
|
4
|
-
import { Button } from "../../shadcn/button/index.js";
|
|
5
|
-
import * as Empty from "../../shadcn/empty/index.js";
|
|
6
|
-
import { Kbd, KbdGroup } from "../../shadcn/kbd/index.js";
|
|
7
|
-
import { Tabs, TabsList, TabsTrigger } from "../../shadcn/tabs/index.js";
|
|
8
|
-
import { ToggleGroup, ToggleGroupItem } from "../../shadcn/toggle-group/index.js";
|
|
9
|
-
import { cn } from "../../lib/utils.js";
|
|
10
|
-
import StatusDot from "../status-dot.svelte";
|
|
11
|
-
import InboxCard from "./inbox-card.svelte";
|
|
12
|
-
import { flatten, groupByProject, inLane, isTyping, laneOf } from "./inbox-logic.js";
|
|
13
|
-
import { inboxKinds, inboxStates } from "./inbox-statuses.js";
|
|
14
|
-
import type {
|
|
15
|
-
InboxItem,
|
|
16
|
-
InboxKindFilter,
|
|
17
|
-
InboxLane,
|
|
18
|
-
InboxProject,
|
|
19
|
-
InboxVerdict,
|
|
20
|
-
} from "./inbox-types.js";
|
|
21
|
-
|
|
22
|
-
interface Props {
|
|
23
|
-
items: InboxItem[];
|
|
24
|
-
/** The host applies the verdict and passes the updated items back. */
|
|
25
|
-
onverdict: (verdict: InboxVerdict) => void;
|
|
26
|
-
/** Replaces an item's summary lines. */
|
|
27
|
-
renderProposal?: Snippet<[InboxItem]>;
|
|
28
|
-
/** Trailing content in a project's header, such as a link to its timeline. */
|
|
29
|
-
renderGroupAction?: Snippet<[InboxProject]>;
|
|
30
|
-
class?: string;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
let { items, onverdict, renderProposal, renderGroupAction, class: className }: Props = $props();
|
|
34
|
-
|
|
35
|
-
const LANES: { id: InboxLane; label: string }[] = [
|
|
36
|
-
{ id: "needs", label: "Needs you" },
|
|
37
|
-
{ id: "auto", label: "Done by the agent" },
|
|
38
|
-
{ id: "decided", label: "Decided" },
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
let lane = $state<InboxLane>("needs");
|
|
42
|
-
let kind = $state<InboxKindFilter>("all");
|
|
43
|
-
let cursor = $state(0);
|
|
44
|
-
let cards = $state<Record<string, ReturnType<typeof InboxCard> | null>>({});
|
|
45
|
-
|
|
46
|
-
let counts = $derived(
|
|
47
|
-
Object.fromEntries(LANES.map((l) => [l.id, items.filter((i) => laneOf(i) === l.id).length])) as Record<
|
|
48
|
-
InboxLane,
|
|
49
|
-
number
|
|
50
|
-
>,
|
|
51
|
-
);
|
|
52
|
-
let groups = $derived(groupByProject(inLane(items, lane, kind)));
|
|
53
|
-
let visible = $derived(flatten(groups));
|
|
54
|
-
// WHY an index and not an id: when a verdict removes the item under the cursor,
|
|
55
|
-
// the same index lands on the next one, so triage keeps moving down the queue.
|
|
56
|
-
let selected = $derived(visible[Math.min(cursor, visible.length - 1)]);
|
|
57
|
-
|
|
58
|
-
async function move(step: number) {
|
|
59
|
-
if (!visible.length) return;
|
|
60
|
-
cursor = Math.max(0, Math.min(visible.length - 1, cursor + step));
|
|
61
|
-
await tick();
|
|
62
|
-
document.querySelector<HTMLElement>(`[data-inbox-item="${CSS.escape(visible[cursor].id)}"]`)?.focus();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function onkeydown(event: KeyboardEvent) {
|
|
66
|
-
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
67
|
-
if (isTyping(event.target)) return;
|
|
68
|
-
const card = selected ? cards[selected.id] : null;
|
|
69
|
-
const actions: Record<string, (() => void) | undefined> = {
|
|
70
|
-
j: () => move(1),
|
|
71
|
-
k: () => move(-1),
|
|
72
|
-
a: () => card?.approve(),
|
|
73
|
-
d: () => card?.decline(),
|
|
74
|
-
s: () => card?.steer(),
|
|
75
|
-
e: () => card?.edit(),
|
|
76
|
-
u: () => card?.undo(),
|
|
77
|
-
};
|
|
78
|
-
const run = actions[event.key];
|
|
79
|
-
if (!run) return;
|
|
80
|
-
event.preventDefault();
|
|
81
|
-
run();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function setLane(next: string) {
|
|
85
|
-
lane = next as InboxLane;
|
|
86
|
-
cursor = 0;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function setKind(next: string | undefined) {
|
|
90
|
-
kind = (next || "all") as InboxKindFilter;
|
|
91
|
-
cursor = 0;
|
|
92
|
-
}
|
|
93
|
-
</script>
|
|
94
|
-
|
|
95
|
-
<!-- WHY the key handler sits on the region: the shortcuts work wherever focus is inside the inbox and nowhere else on the page. -->
|
|
96
|
-
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
97
|
-
<div
|
|
98
|
-
role="region"
|
|
99
|
-
aria-label="Inbox"
|
|
100
|
-
class={cn("flex h-full min-h-0 flex-col gap-3", className)}
|
|
101
|
-
{onkeydown}
|
|
102
|
-
>
|
|
103
|
-
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
104
|
-
<Tabs value={lane} onValueChange={setLane}>
|
|
105
|
-
<TabsList>
|
|
106
|
-
{#each LANES as l (l.id)}
|
|
107
|
-
<TabsTrigger value={l.id} class="gap-2 px-3">
|
|
108
|
-
{l.label}
|
|
109
|
-
<span class="text-muted-foreground text-xs tabular-nums">{counts[l.id]}</span>
|
|
110
|
-
</TabsTrigger>
|
|
111
|
-
{/each}
|
|
112
|
-
</TabsList>
|
|
113
|
-
</Tabs>
|
|
114
|
-
<ToggleGroup
|
|
115
|
-
type="single"
|
|
116
|
-
variant="outline"
|
|
117
|
-
size="sm"
|
|
118
|
-
value={kind}
|
|
119
|
-
onValueChange={setKind}
|
|
120
|
-
aria-label="Filter by kind"
|
|
121
|
-
>
|
|
122
|
-
<ToggleGroupItem value="all">All</ToggleGroupItem>
|
|
123
|
-
{#each inboxKinds.ids as id (id)}
|
|
124
|
-
<ToggleGroupItem value={id}>{inboxKinds.label(id)}</ToggleGroupItem>
|
|
125
|
-
{/each}
|
|
126
|
-
</ToggleGroup>
|
|
127
|
-
</div>
|
|
128
|
-
|
|
129
|
-
<div class="-mx-1 min-h-0 flex-1 overflow-y-auto px-1 pb-1">
|
|
130
|
-
{#if !visible.length}
|
|
131
|
-
<Empty.Root class="h-full">
|
|
132
|
-
<Empty.Header>
|
|
133
|
-
<Empty.Media>
|
|
134
|
-
<StatusDot statuses={inboxStates} status="approved" size={28} />
|
|
135
|
-
</Empty.Media>
|
|
136
|
-
{#if kind !== "all"}
|
|
137
|
-
<Empty.Title>Nothing of this kind here</Empty.Title>
|
|
138
|
-
<Empty.Description>Show every kind to see the rest of this lane.</Empty.Description>
|
|
139
|
-
{:else if lane === "needs"}
|
|
140
|
-
<Empty.Title>Queue clear</Empty.Title>
|
|
141
|
-
<Empty.Description>The agent handled the rest, and nothing is waiting on you.</Empty.Description>
|
|
142
|
-
{:else if lane === "auto"}
|
|
143
|
-
<Empty.Title>The agent hasn't done anything on its own yet</Empty.Title>
|
|
144
|
-
<Empty.Description>Safe work it finishes by itself lands here, with an undo.</Empty.Description>
|
|
145
|
-
{:else}
|
|
146
|
-
<Empty.Title>No verdicts yet</Empty.Title>
|
|
147
|
-
<Empty.Description>What you approve or decline shows here and on the project timeline.</Empty.Description>
|
|
148
|
-
{/if}
|
|
149
|
-
</Empty.Header>
|
|
150
|
-
<Empty.Content>
|
|
151
|
-
{#if kind !== "all"}
|
|
152
|
-
<Button size="sm" variant="outline" onclick={() => setKind("all")}>Show every kind</Button>
|
|
153
|
-
{:else if lane === "needs" && counts.auto}
|
|
154
|
-
<Button size="sm" variant="outline" onclick={() => setLane("auto")}>See what the agent did</Button>
|
|
155
|
-
{:else if lane !== "needs" && counts.needs}
|
|
156
|
-
<Button size="sm" variant="outline" onclick={() => setLane("needs")}>Go to the queue</Button>
|
|
157
|
-
{/if}
|
|
158
|
-
</Empty.Content>
|
|
159
|
-
</Empty.Root>
|
|
160
|
-
{:else}
|
|
161
|
-
<div class="flex flex-col gap-5">
|
|
162
|
-
{#each groups as group (group.project.id)}
|
|
163
|
-
<section aria-label={group.project.name} class="flex flex-col gap-2">
|
|
164
|
-
<header class="flex items-center gap-2 px-1">
|
|
165
|
-
<h2 class="text-sm font-medium">{group.project.name}</h2>
|
|
166
|
-
{#if group.project.client}
|
|
167
|
-
<span class="text-muted-foreground text-sm">{group.project.client}</span>
|
|
168
|
-
{/if}
|
|
169
|
-
<Badge variant="outline" class="tabular-nums">{group.items.length}</Badge>
|
|
170
|
-
<div class="ml-auto flex items-center gap-1">
|
|
171
|
-
{#if group.project.href}
|
|
172
|
-
<Button size="sm" variant="ghost" href={group.project.href}>Timeline</Button>
|
|
173
|
-
{/if}
|
|
174
|
-
{@render renderGroupAction?.(group.project)}
|
|
175
|
-
</div>
|
|
176
|
-
</header>
|
|
177
|
-
<div class="flex flex-col gap-2">
|
|
178
|
-
{#each group.items as item (item.id)}
|
|
179
|
-
<InboxCard
|
|
180
|
-
bind:this={cards[item.id]}
|
|
181
|
-
{item}
|
|
182
|
-
selected={selected?.id === item.id}
|
|
183
|
-
onselect={() => (cursor = visible.findIndex((v) => v.id === item.id))}
|
|
184
|
-
{onverdict}
|
|
185
|
-
{renderProposal}
|
|
186
|
-
/>
|
|
187
|
-
{/each}
|
|
188
|
-
</div>
|
|
189
|
-
</section>
|
|
190
|
-
{/each}
|
|
191
|
-
</div>
|
|
192
|
-
{/if}
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
<footer class="text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-1 text-xs max-sm:hidden">
|
|
196
|
-
<span class="inline-flex items-center gap-1.5"><KbdGroup><Kbd>j</Kbd><Kbd>k</Kbd></KbdGroup> move</span>
|
|
197
|
-
<span class="inline-flex items-center gap-1.5"><Kbd>a</Kbd> approve</span>
|
|
198
|
-
<span class="inline-flex items-center gap-1.5"><Kbd>d</Kbd> decline</span>
|
|
199
|
-
<span class="inline-flex items-center gap-1.5"><Kbd>s</Kbd> steer</span>
|
|
200
|
-
<span class="inline-flex items-center gap-1.5"><Kbd>e</Kbd> edit draft</span>
|
|
201
|
-
<span class="inline-flex items-center gap-1.5"><Kbd>u</Kbd> undo</span>
|
|
202
|
-
</footer>
|
|
203
|
-
</div>
|