@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,10 +1,5 @@
|
|
|
1
|
-
/* Thinking states
|
|
2
|
-
|
|
3
|
-
swap: JS exits the old line upward and enters the new one from below, held
|
|
4
|
-
back by --think-gap. The sweep is `.t-shimmer` from shimmer-text.css — put
|
|
5
|
-
both classes on the line and keep data-text in sync with the text, or the
|
|
6
|
-
shimmer paints the wrong words. The hidden sizer carries your longest state
|
|
7
|
-
and is what stops the box resizing mid-swap. */
|
|
1
|
+
/* Thinking states, transitions.dev 28: put `.t-shimmer` on the line too and
|
|
2
|
+
keep data-text in sync with the text, or the shimmer paints the wrong words. */
|
|
8
3
|
:root {
|
|
9
4
|
--think-hold: 2000ms;
|
|
10
5
|
--think-swap: var(--duration-quick, 150ms);
|
package/src/brand/brand.test.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { describe, expect, it } from "vite-plus/test";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
BRAND_COLOR_VARS,
|
|
6
|
+
applyBrand,
|
|
7
|
+
brandCss,
|
|
8
|
+
brandStyle,
|
|
9
|
+
brandVars,
|
|
10
|
+
defineBrand,
|
|
11
|
+
} from "./index.js";
|
|
5
12
|
|
|
6
13
|
const TOKENS = join(import.meta.dirname, "..", "tokens");
|
|
7
14
|
const palette = readFileSync(join(TOKENS, "palette.css"), "utf8");
|
|
@@ -9,7 +16,10 @@ const palette = readFileSync(join(TOKENS, "palette.css"), "utf8");
|
|
|
9
16
|
describe("brandVars", () => {
|
|
10
17
|
it("sets only the roles a brand names", () => {
|
|
11
18
|
const brand = defineBrand({ colors: { brand: "oklch(0.5 0.2 272)", signal: "#f0503d" } });
|
|
12
|
-
expect(brandVars(brand)).toEqual({
|
|
19
|
+
expect(brandVars(brand)).toEqual({
|
|
20
|
+
"--brand": "oklch(0.5 0.2 272)",
|
|
21
|
+
"--brand-signal": "#f0503d",
|
|
22
|
+
});
|
|
13
23
|
});
|
|
14
24
|
|
|
15
25
|
it("maps fonts and extra properties", () => {
|
|
@@ -27,7 +37,9 @@ describe("brandVars", () => {
|
|
|
27
37
|
});
|
|
28
38
|
|
|
29
39
|
it("refuses a value that would break out of its declaration", () => {
|
|
30
|
-
expect(() => brandVars({ colors: { brand: "red; } body { display: none" } })).toThrow(
|
|
40
|
+
expect(() => brandVars({ colors: { brand: "red; } body { display: none" } })).toThrow(
|
|
41
|
+
/--brand/,
|
|
42
|
+
);
|
|
31
43
|
});
|
|
32
44
|
});
|
|
33
45
|
|
|
@@ -36,7 +48,9 @@ describe("the three ways to apply one", () => {
|
|
|
36
48
|
|
|
37
49
|
it("writes a style attribute and a rule", () => {
|
|
38
50
|
expect(brandStyle(brand)).toBe("--brand:blue;--brand-ink:black");
|
|
39
|
-
expect(brandCss(brand, ".client")).toBe(
|
|
51
|
+
expect(brandCss(brand, ".client")).toBe(
|
|
52
|
+
".client {\n --brand: blue;\n --brand-ink: black;\n}\n",
|
|
53
|
+
);
|
|
40
54
|
});
|
|
41
55
|
|
|
42
56
|
it("applies to an element and puts back what it found", () => {
|
|
@@ -61,7 +75,9 @@ describe("the tokens", () => {
|
|
|
61
75
|
it("carry no client palette", () => {
|
|
62
76
|
for (const file of readdirSync(TOKENS).filter((f) => f.endsWith(".css"))) {
|
|
63
77
|
const css = readFileSync(join(TOKENS, file), "utf8");
|
|
64
|
-
expect(css, file).not.toMatch(
|
|
78
|
+
expect(css, file).not.toMatch(
|
|
79
|
+
/brand-(indigo|sand|terra|night|veil|dusk|cream|linen|mist)|--(indigo|sand|terra)-\d/,
|
|
80
|
+
);
|
|
65
81
|
}
|
|
66
82
|
});
|
|
67
83
|
});
|
package/src/brand/index.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A
|
|
3
|
-
*
|
|
4
|
-
* export const brand = defineBrand({ colors: { brand: "oklch(0.5 0.2 272)" } });
|
|
5
|
-
*
|
|
6
|
-
* `@heyramzi/ui/tokens.css` ships a neutral look. A brand replaces only the roles it names:
|
|
7
|
-
* - on the server, put `brandStyle(brand)` on the `<html style>` so the first paint is right;
|
|
8
|
-
* - or call `applyBrand(brand)` once in the browser;
|
|
9
|
-
* - or write `brandCss(brand)` into a stylesheet.
|
|
10
|
-
* Light and dark come from the tokens, which read these roles, so a brand never types both.
|
|
2
|
+
* A brand replaces only the token roles it names. Put `brandStyle(brand)` on the server's
|
|
3
|
+
* `<html style>`, or the first paint shows the neutral look.
|
|
11
4
|
*/
|
|
12
5
|
|
|
13
6
|
/** Any CSS colour: oklch(), hex, a named colour or a var(). */
|
|
@@ -86,7 +79,8 @@ export function defineBrand<const B extends Brand>(brand: B): B {
|
|
|
86
79
|
}
|
|
87
80
|
|
|
88
81
|
function checked(name: string, value: string): string {
|
|
89
|
-
if (/[;{}<>]/.test(value))
|
|
82
|
+
if (/[;{}<>]/.test(value))
|
|
83
|
+
throw new Error(`brand: ${name} holds a character CSS would read as more than a value`);
|
|
90
84
|
return value;
|
|
91
85
|
}
|
|
92
86
|
|
package/src/canvas/README.md
CHANGED
|
@@ -9,7 +9,7 @@ MIT. It runs on any domain and costs nothing per install.
|
|
|
9
9
|
## What's in it
|
|
10
10
|
|
|
11
11
|
- **Pen layer.** Freehand strokes (perfect-freehand), a highlighter, an eraser, plain text, and rectangle, ellipse and diamond shapes. Colours are token names, so a board drawn in light mode reads fine in dark.
|
|
12
|
-
- **Tool bar.** A floating bar with the tools,
|
|
12
|
+
- **Tool bar.** A floating bar with the tools, a colour and a brush size. Pick from 8 colours and 3 brush sizes. Keys: V, H, L, P, M, E, T, R, O, D, F. Undo and redo are yours to wire to `createCanvasDispatcher`.
|
|
13
13
|
- **Plugin API.** Register your own card types, ops and layout passes. One registry builds xyflow's `nodeTypes` for you.
|
|
14
14
|
- **Ops.** 8 core ops (`addNode`, `removeNode`, `patchNode`, `moveNode`, `setParent`, `addEdge`, `removeEdge`, `patchEdge`) as plain functions over nodes and edges. Zod checks every one.
|
|
15
15
|
- **Undo history.** `createCanvasDispatcher` wraps each edit in a transaction. A whole pen stroke is one undo step.
|
|
@@ -68,14 +68,15 @@ Drop this in `src/routes/+page.svelte`.
|
|
|
68
68
|
toolbar: "Canvas tools",
|
|
69
69
|
style: "Style",
|
|
70
70
|
tools: {
|
|
71
|
-
select: "Select", hand: "Hand",
|
|
72
|
-
eraser: "Eraser", text: "Text", rectangle: "Rectangle",
|
|
73
|
-
diamond: "Diamond", shapes: "Shapes",
|
|
71
|
+
select: "Select", hand: "Hand", connect: "Connect", frame: "Frame", pen: "Pen",
|
|
72
|
+
highlighter: "Highlighter", eraser: "Eraser", text: "Text", rectangle: "Rectangle",
|
|
73
|
+
ellipse: "Ellipse", diamond: "Diamond", shapes: "Shapes", note: "Note",
|
|
74
|
+
},
|
|
75
|
+
colors: {
|
|
76
|
+
ink: "Ink", red: "Red", blue: "Blue", green: "Green", amber: "Amber",
|
|
77
|
+
violet: "Violet", indigo: "Indigo", terra: "Terracotta",
|
|
74
78
|
},
|
|
75
|
-
colors: { ink: "Ink", red: "Red", blue: "Blue", green: "Green", amber: "Amber", violet: "Violet" },
|
|
76
79
|
sizes: ["Small", "Medium", "Large"],
|
|
77
|
-
undo: "Undo",
|
|
78
|
-
redo: "Redo",
|
|
79
80
|
} as const;
|
|
80
81
|
</script>
|
|
81
82
|
|
|
@@ -100,10 +101,6 @@ Drop this in `src/routes/+page.svelte`.
|
|
|
100
101
|
bind:color
|
|
101
102
|
bind:level
|
|
102
103
|
{labels}
|
|
103
|
-
canUndo={history.canUndo}
|
|
104
|
-
canRedo={history.canRedo}
|
|
105
|
-
onUndo={history.undo}
|
|
106
|
-
onRedo={history.redo}
|
|
107
104
|
/>
|
|
108
105
|
</SvelteFlow>
|
|
109
106
|
</div>
|
|
@@ -192,7 +189,7 @@ The pen is `PenOverlay` (or `PenLayer` inside xyflow), and it needs 2 callbacks
|
|
|
192
189
|
|
|
193
190
|
The eraser removes whole strokes, never part of one. A swipe across a big shape doesn't wipe it.
|
|
194
191
|
|
|
195
|
-
The pen has
|
|
192
|
+
The pen has 8 colours: `ink`, `red`, `blue`, `green`, `amber`, `violet`, `indigo` and `terra`. `pen.css` maps them to CSS variables with a light and a dark value. Dark mode applies under a `.dark` class on an ancestor or on `.svelte-flow`.
|
|
196
193
|
|
|
197
194
|
## Headless ops
|
|
198
195
|
|
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import type { Snippet } from "svelte";
|
|
6
|
-
import { getDiagramEditorContext } from "../editor/editor-context.js";
|
|
7
|
-
import { isLocked } from "../editor/lock.js";
|
|
8
|
-
import { userHeightOf, type CardResize } from "./resize.js";
|
|
9
|
-
import ResizeHandles from "./resize-handles.svelte";
|
|
10
|
-
import "./card.css";
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
import { getDiagramEditorContext } from "../editor/editor-context.js";
|
|
7
|
+
import { isLocked } from "../editor/lock.js";
|
|
8
|
+
import { userHeightOf, type CardResize } from "./resize.js";
|
|
9
|
+
import ResizeHandles from "./resize-handles.svelte";
|
|
10
|
+
import "./card.css";
|
|
11
11
|
|
|
12
|
-
// The frame every card sits in: it keeps the saved height as a floor and draws the grips
|
|
13
|
-
// on a selected card. A card whose policy is `false` has neither.
|
|
12
|
+
// The frame every card sits in: it keeps the saved height as a floor and draws the grips
|
|
13
|
+
// on a selected card. A card whose policy is `false` has neither.
|
|
14
14
|
|
|
15
|
-
// =========================
|
|
16
|
-
// TYPES
|
|
17
|
-
// =========================
|
|
18
|
-
interface Props {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
15
|
+
// =========================
|
|
16
|
+
// TYPES
|
|
17
|
+
// =========================
|
|
18
|
+
interface Props {
|
|
19
|
+
id: string;
|
|
20
|
+
data?: unknown;
|
|
21
|
+
selected?: boolean;
|
|
22
|
+
/** How this card type resizes, or false when its size isn't the person's to set. */
|
|
23
|
+
resize: CardResize | false;
|
|
24
|
+
children: Snippet;
|
|
25
|
+
}
|
|
26
26
|
|
|
27
|
-
// =========================
|
|
28
|
-
// PROPS
|
|
29
|
-
// =========================
|
|
30
|
-
let { id, data, selected = false, resize, children }: Props = $props();
|
|
27
|
+
// =========================
|
|
28
|
+
// PROPS
|
|
29
|
+
// =========================
|
|
30
|
+
let { id, data, selected = false, resize, children }: Props = $props();
|
|
31
31
|
|
|
32
|
-
// =========================
|
|
33
|
-
// STATE
|
|
34
|
-
// =========================
|
|
35
|
-
const editor = getDiagramEditorContext();
|
|
36
|
-
let frame = $state<HTMLElement>();
|
|
32
|
+
// =========================
|
|
33
|
+
// STATE
|
|
34
|
+
// =========================
|
|
35
|
+
const editor = getDiagramEditorContext();
|
|
36
|
+
let frame = $state<HTMLElement>();
|
|
37
37
|
|
|
38
|
-
// =========================
|
|
39
|
-
// DERIVED
|
|
40
|
-
// =========================
|
|
41
|
-
let floor = $derived(userHeightOf(data));
|
|
42
|
-
// A box card is sized by the node itself, so it needs no frame.
|
|
43
|
-
let framed = $derived(!!resize && resize.store !== "box");
|
|
44
|
-
let grips = $derived(!!resize && !!editor && selected && !isLocked({ data }));
|
|
38
|
+
// =========================
|
|
39
|
+
// DERIVED
|
|
40
|
+
// =========================
|
|
41
|
+
let floor = $derived(userHeightOf(data));
|
|
42
|
+
// A box card is sized by the node itself, so it needs no frame.
|
|
43
|
+
let framed = $derived(!!resize && resize.store !== "box");
|
|
44
|
+
let grips = $derived(!!resize && !!editor && selected && !isLocked({ data }));
|
|
45
45
|
</script>
|
|
46
46
|
|
|
47
47
|
<!-- =========================== -->
|
|
@@ -53,7 +53,11 @@ let grips = $derived(!!resize && !!editor && selected && !isLocked({ data }));
|
|
|
53
53
|
{/if}
|
|
54
54
|
|
|
55
55
|
{#if framed}
|
|
56
|
-
<div
|
|
56
|
+
<div
|
|
57
|
+
class="cv-card-frame"
|
|
58
|
+
bind:this={frame}
|
|
59
|
+
style:--cv-card-h={floor > 0 ? `${floor}px` : undefined}
|
|
60
|
+
>
|
|
57
61
|
{@render children()}
|
|
58
62
|
</div>
|
|
59
63
|
{:else}
|
|
@@ -1,98 +1,103 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import { NodeResizeControl, ResizeControlVariant, useSvelteFlow } from "@xyflow/svelte";
|
|
6
|
-
import { getDiagramEditorContext } from "../editor/editor-context.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "./resize.js";
|
|
14
|
-
import "./card.css";
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import { NodeResizeControl, ResizeControlVariant, useSvelteFlow } from "@xyflow/svelte";
|
|
6
|
+
import { getDiagramEditorContext } from "../editor/editor-context.js";
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_MAX_WIDTH,
|
|
9
|
+
DEFAULT_MIN_HEIGHT,
|
|
10
|
+
DEFAULT_MIN_WIDTH,
|
|
11
|
+
keptHeight,
|
|
12
|
+
type CardResize,
|
|
13
|
+
} from "./resize.js";
|
|
14
|
+
import "./card.css";
|
|
15
15
|
|
|
16
|
-
// The grips of a selected card. One drag is one undo step: width, kept height and extra data close together.
|
|
16
|
+
// The grips of a selected card. One drag is one undo step: width, kept height and extra data close together.
|
|
17
17
|
|
|
18
|
-
// =========================
|
|
19
|
-
// TYPES
|
|
20
|
-
// =========================
|
|
21
|
-
interface Props {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
18
|
+
// =========================
|
|
19
|
+
// TYPES
|
|
20
|
+
// =========================
|
|
21
|
+
interface Props {
|
|
22
|
+
id: string;
|
|
23
|
+
resize: CardResize;
|
|
24
|
+
/** The framed element, read for the height its content needs. */
|
|
25
|
+
frame?: HTMLElement;
|
|
26
|
+
}
|
|
27
27
|
|
|
28
|
-
// =========================
|
|
29
|
-
// CONSTANTS
|
|
30
|
-
// =========================
|
|
31
|
-
const CORNERS = ["top-left", "top-right", "bottom-left", "bottom-right"] as const;
|
|
32
|
-
const EDGES = ["left", "right", "top", "bottom"] as const;
|
|
33
|
-
const SIDES = ["left", "right"] as const;
|
|
28
|
+
// =========================
|
|
29
|
+
// CONSTANTS
|
|
30
|
+
// =========================
|
|
31
|
+
const CORNERS = ["top-left", "top-right", "bottom-left", "bottom-right"] as const;
|
|
32
|
+
const EDGES = ["left", "right", "top", "bottom"] as const;
|
|
33
|
+
const SIDES = ["left", "right"] as const;
|
|
34
34
|
|
|
35
|
-
// =========================
|
|
36
|
-
// PROPS
|
|
37
|
-
// =========================
|
|
38
|
-
let { id, resize, frame }: Props = $props();
|
|
35
|
+
// =========================
|
|
36
|
+
// PROPS
|
|
37
|
+
// =========================
|
|
38
|
+
let { id, resize, frame }: Props = $props();
|
|
39
39
|
|
|
40
|
-
// =========================
|
|
41
|
-
// STATE
|
|
42
|
-
// =========================
|
|
43
|
-
const editor = getDiagramEditorContext();
|
|
44
|
-
const { updateNode } = useSvelteFlow();
|
|
45
|
-
let endGesture: (() => void) | null = null;
|
|
46
|
-
// The height the content needs, read when a drag starts.
|
|
47
|
-
let contentHeight = $state(DEFAULT_MIN_HEIGHT);
|
|
40
|
+
// =========================
|
|
41
|
+
// STATE
|
|
42
|
+
// =========================
|
|
43
|
+
const editor = getDiagramEditorContext();
|
|
44
|
+
const { updateNode } = useSvelteFlow();
|
|
45
|
+
let endGesture: (() => void) | null = null;
|
|
46
|
+
// The height the content needs, read when a drag starts.
|
|
47
|
+
let contentHeight = $state(DEFAULT_MIN_HEIGHT);
|
|
48
48
|
|
|
49
|
-
// =========================
|
|
50
|
-
// DERIVED
|
|
51
|
-
// =========================
|
|
52
|
-
let both = $derived(resize.axes === "both");
|
|
53
|
-
let ratio = $derived(resize.axes === "ratio");
|
|
54
|
-
let direction = $derived(both || ratio ? undefined : ("horizontal" as const));
|
|
55
|
-
let box = $derived(resize.store === "box");
|
|
56
|
-
let minWidth = $derived(resize.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
57
|
-
let maxWidth = $derived(resize.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
58
|
-
let minHeight = $derived(both && !box ? contentHeight : (resize.minHeight ?? DEFAULT_MIN_HEIGHT));
|
|
49
|
+
// =========================
|
|
50
|
+
// DERIVED
|
|
51
|
+
// =========================
|
|
52
|
+
let both = $derived(resize.axes === "both");
|
|
53
|
+
let ratio = $derived(resize.axes === "ratio");
|
|
54
|
+
let direction = $derived(both || ratio ? undefined : ("horizontal" as const));
|
|
55
|
+
let box = $derived(resize.store === "box");
|
|
56
|
+
let minWidth = $derived(resize.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
57
|
+
let maxWidth = $derived(resize.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
58
|
+
let minHeight = $derived(both && !box ? contentHeight : (resize.minHeight ?? DEFAULT_MIN_HEIGHT));
|
|
59
59
|
|
|
60
|
-
// =========================
|
|
61
|
-
// FUNCTIONS
|
|
62
|
-
// =========================
|
|
63
|
-
function measureContent() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
60
|
+
// =========================
|
|
61
|
+
// FUNCTIONS
|
|
62
|
+
// =========================
|
|
63
|
+
function measureContent() {
|
|
64
|
+
if (!frame) return;
|
|
65
|
+
// With the saved floor dropped, the frame is as tall as the content needs.
|
|
66
|
+
frame.setAttribute("data-resizing", "");
|
|
67
|
+
contentHeight = Math.max(DEFAULT_MIN_HEIGHT, Math.ceil(frame.offsetHeight));
|
|
68
|
+
}
|
|
69
69
|
|
|
70
|
-
function start() {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
70
|
+
function start() {
|
|
71
|
+
endGesture = editor?.startGesture() ?? null;
|
|
72
|
+
if (both && !box) measureContent();
|
|
73
|
+
}
|
|
74
74
|
|
|
75
|
-
function finish(size: { width: number; height: number }) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
function finish(size: { width: number; height: number }) {
|
|
76
|
+
frame?.removeAttribute("data-resizing");
|
|
77
|
+
const width = Math.round(size.width);
|
|
78
|
+
if (!box) {
|
|
79
|
+
editor?.setNodeWidth(id, width);
|
|
80
|
+
const patch: Record<string, unknown> = {
|
|
81
|
+
...resize.extraData?.({ width, height: size.height }),
|
|
82
|
+
};
|
|
83
|
+
// A text card keeps growing with its words, and an image's height follows its width.
|
|
84
|
+
if (both) patch.userHeight = keptHeight(size.height, contentHeight);
|
|
85
|
+
if (Object.keys(patch).length > 0) editor?.patchNodeData(id, patch);
|
|
86
|
+
// The drag gave the node a height; the content and the saved floor size it from here.
|
|
87
|
+
updateNode(id, { height: undefined });
|
|
88
|
+
}
|
|
89
|
+
endGesture?.();
|
|
90
|
+
endGesture = null;
|
|
86
91
|
}
|
|
87
|
-
endGesture?.();
|
|
88
|
-
endGesture = null;
|
|
89
|
-
}
|
|
90
92
|
</script>
|
|
91
93
|
|
|
92
94
|
<!-- =========================== -->
|
|
93
95
|
<!-- MARKUP -->
|
|
94
96
|
<!-- =========================== -->
|
|
95
|
-
{#snippet control(
|
|
97
|
+
{#snippet control(
|
|
98
|
+
position: (typeof CORNERS)[number] | (typeof EDGES)[number],
|
|
99
|
+
variant: ResizeControlVariant,
|
|
100
|
+
)}
|
|
96
101
|
<NodeResizeControl
|
|
97
102
|
nodeId={id}
|
|
98
103
|
{position}
|
|
@@ -102,7 +107,11 @@ function finish(size: { width: number; height: number }) {
|
|
|
102
107
|
{minHeight}
|
|
103
108
|
keepAspectRatio={ratio}
|
|
104
109
|
resizeDirection={direction}
|
|
105
|
-
class={variant === ResizeControlVariant.Line
|
|
110
|
+
class={variant === ResizeControlVariant.Line
|
|
111
|
+
? "cv-edge"
|
|
112
|
+
: direction
|
|
113
|
+
? "cv-grip cv-grip-x"
|
|
114
|
+
: "cv-grip"}
|
|
106
115
|
onResizeStart={start}
|
|
107
116
|
onResizeEnd={(_, size) => finish(size)}
|
|
108
117
|
/>
|