@heyramzi/ui 2.1.0 → 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 +7 -8
- 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/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 +5 -1
- 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/section-header.test.ts +7 -1
- package/src/components/section-header/section-label.svelte +20 -20
- 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/toolbar-search.svelte +25 -25
- package/src/components/toolbar/toolbar.svelte +11 -7
- 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 +1 -3
- package/src/inbox/inbox.test.ts +181 -138
- package/src/inbox/index.ts +20 -20
- package/src/inbox/logic.ts +114 -111
- package/src/inbox/thread-inbox.svelte +227 -151
- package/src/inbox/types.ts +130 -108
- 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 +79 -69
- 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/side-panel.svelte +94 -93
- package/src/record/demo-data.ts +291 -225
- package/src/record/field-section.svelte +38 -34
- package/src/record/fields.ts +148 -96
- package/src/record/format.ts +63 -56
- package/src/record/index.ts +49 -35
- package/src/record/info-list.svelte +145 -114
- package/src/record/kinds.ts +31 -31
- package/src/record/logic.ts +216 -214
- package/src/record/next-up.svelte +45 -34
- package/src/record/project-list.svelte +56 -50
- package/src/record/record-demo.svelte +130 -110
- package/src/record/record-header.svelte +37 -28
- package/src/record/record-shell.svelte +54 -54
- package/src/record/record-stats.svelte +25 -25
- package/src/record/record-timeline.svelte +134 -114
- package/src/record/record.test.ts +430 -309
- package/src/record/steps-rail.svelte +225 -216
- package/src/record/types.ts +234 -215
- package/src/record/where-we-are.svelte +62 -58
- 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/define-statuses.ts +17 -24
- package/src/status/index.ts +5 -5
- package/src/status/status-badge.svelte +3 -1
- package/src/status/status-dot.svelte +1 -3
- package/src/status/status-icon.svelte +1 -1
- package/src/status/status-types.ts +21 -21
- package/src/tasks/board-order.ts +2 -4
- package/src/tasks/demo.svelte +189 -31
- package/src/tasks/detail/detail-demo.svelte +73 -13
- 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 +3 -1
- package/src/tasks/gantt/logic.test.ts +110 -20
- package/src/tasks/gantt/logic.ts +42 -6
- package/src/tasks/inbox/inbox-card.svelte +13 -5
- 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 +23 -23
- 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 +49 -52
- package/src/tasks/kanban-board.svelte +16 -12
- package/src/tasks/kanban-types.ts +2 -3
- package/src/tasks/presets/sales.ts +6 -6
- 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/index.ts +7 -7
- package/src/thread/logic.ts +36 -33
- package/src/thread/thread.svelte +82 -16
- package/src/thread/thread.test.ts +58 -47
- package/src/thread/types.ts +68 -68
- 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/tasks/inbox/inbox.svelte +0 -203
package/src/slides/export/pdf.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Print a URL or HTML to PDF at a page size, and assemble captured PNGs into one.
|
|
2
2
|
|
|
3
|
-
import { readFile } from
|
|
4
|
-
import type { PageLike, ResponseLike, Size, WaitUntil } from
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import type { PageLike, ResponseLike, Size, WaitUntil } from "./types.js";
|
|
5
5
|
|
|
6
6
|
/** Chromium drops backgrounds in its print pipeline without the color-adjust line. */
|
|
7
7
|
export const RESET_CSS = `*{margin:0;padding:0;box-sizing:border-box;}
|
|
@@ -15,14 +15,14 @@ export function pageRule(size: Size): string {
|
|
|
15
15
|
/** The reset, the page size and one full-bleed `.page` box per image. */
|
|
16
16
|
export function imagePagesCss(size: Size, background?: string): string {
|
|
17
17
|
return `${RESET_CSS}
|
|
18
|
-
${background ? `body{background:${background};}\n` :
|
|
18
|
+
${background ? `body{background:${background};}\n` : ""}${pageRule(size)}
|
|
19
19
|
.page{display:block;width:${size.width}px;height:${size.height}px;break-after:page;}
|
|
20
20
|
.page:last-child{break-after:auto;}`;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/** One `<img class="page">` per data URL, laid out so each prints on its own page. */
|
|
24
24
|
export function imagePagesHtml(dataUrls: string[], size: Size, background?: string): string {
|
|
25
|
-
const pages = dataUrls.map((dataUrl) => `<img class="page" src="${dataUrl}" alt="">`).join(
|
|
25
|
+
const pages = dataUrls.map((dataUrl) => `<img class="page" src="${dataUrl}" alt="">`).join("");
|
|
26
26
|
return `<!doctype html><html><head><meta charset="utf-8"><style>
|
|
27
27
|
${imagePagesCss(size, background)}
|
|
28
28
|
</style></head><body>${pages}</body></html>`;
|
|
@@ -30,7 +30,11 @@ ${imagePagesCss(size, background)}
|
|
|
30
30
|
|
|
31
31
|
/** The page count of a PDF, read off its page objects. */
|
|
32
32
|
export function countPdfPages(pdf: Uint8Array): number {
|
|
33
|
-
return (
|
|
33
|
+
return (
|
|
34
|
+
Buffer.from(pdf)
|
|
35
|
+
.toString("latin1")
|
|
36
|
+
.match(/\/Type\s*\/Page[^s]/g) ?? []
|
|
37
|
+
).length;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export interface PrintOptions {
|
|
@@ -59,13 +63,12 @@ export interface PrintResult {
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* and the images that never loaded, so a caller can refuse a half-painted sheet.
|
|
66
|
+
* Prints the page once fonts, images and the caller's signal settle, returning the images
|
|
67
|
+
* that never loaded so a caller can refuse a half-painted sheet.
|
|
65
68
|
*/
|
|
66
69
|
export async function printPage(page: PageLike, options: PrintOptions = {}): Promise<PrintResult> {
|
|
67
70
|
const { ready } = options;
|
|
68
|
-
if (typeof ready ===
|
|
71
|
+
if (typeof ready === "string") {
|
|
69
72
|
await page.waitForSelector(ready, { timeout: options.readyTimeout ?? 60_000 });
|
|
70
73
|
} else if (ready) {
|
|
71
74
|
await ready(page);
|
|
@@ -73,7 +76,9 @@ export async function printPage(page: PageLike, options: PrintOptions = {}): Pro
|
|
|
73
76
|
await page.evaluate(() => document.fonts.ready.then(() => undefined));
|
|
74
77
|
if (options.settle) await page.waitForTimeout(options.settle);
|
|
75
78
|
const brokenImages = await page.evaluate(() =>
|
|
76
|
-
[...document.images]
|
|
79
|
+
[...document.images]
|
|
80
|
+
.filter((img) => !img.complete || img.naturalWidth === 0)
|
|
81
|
+
.map((img) => img.src),
|
|
77
82
|
);
|
|
78
83
|
const { size } = options;
|
|
79
84
|
const pdf = await page.pdf({
|
|
@@ -100,7 +105,7 @@ export async function printUrl(
|
|
|
100
105
|
url: string,
|
|
101
106
|
options: PrintNavigation = {},
|
|
102
107
|
): Promise<PrintResult> {
|
|
103
|
-
const response = await page.goto(url, { waitUntil: options.waitUntil ??
|
|
108
|
+
const response = await page.goto(url, { waitUntil: options.waitUntil ?? "load" });
|
|
104
109
|
options.check?.(response, page);
|
|
105
110
|
return printPage(page, options);
|
|
106
111
|
}
|
|
@@ -111,7 +116,7 @@ export async function printHtml(
|
|
|
111
116
|
html: string,
|
|
112
117
|
options: PrintNavigation = {},
|
|
113
118
|
): Promise<PrintResult> {
|
|
114
|
-
await page.setContent(html, { waitUntil: options.waitUntil ??
|
|
119
|
+
await page.setContent(html, { waitUntil: options.waitUntil ?? "load" });
|
|
115
120
|
return printPage(page, options);
|
|
116
121
|
}
|
|
117
122
|
|
|
@@ -123,30 +128,28 @@ export interface ImagesToPdfOptions {
|
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* Printing the original document would run it through Chromium's print
|
|
129
|
-
* pipeline, which re-rasterises effects instead of compositing them: a blurred
|
|
130
|
-
* backdrop lost 47% of its chroma that way. Handing the pipeline flat bitmaps
|
|
131
|
-
* removes the whole class of drift, so the PDF shows what the PNG shows.
|
|
132
|
-
*
|
|
133
|
-
* Each image is a file path or the bytes themselves.
|
|
131
|
+
* Prints PNGs one per page, never the original document, because Chromium's print pipeline
|
|
132
|
+
* re-rasterises effects: a blurred backdrop lost 47% of its chroma.
|
|
134
133
|
*/
|
|
135
134
|
export async function imagesToPdf(
|
|
136
135
|
page: PageLike,
|
|
137
136
|
images: Array<string | Uint8Array>,
|
|
138
137
|
options: ImagesToPdfOptions,
|
|
139
138
|
): Promise<Uint8Array> {
|
|
140
|
-
if (images.length === 0) throw new Error(
|
|
139
|
+
if (images.length === 0) throw new Error("imagesToPdf needs at least one image.");
|
|
141
140
|
const dataUrls = await Promise.all(
|
|
142
141
|
images.map(async (image) => {
|
|
143
|
-
const bytes = typeof image ===
|
|
144
|
-
return `data:image/png;base64,${Buffer.from(bytes).toString(
|
|
142
|
+
const bytes = typeof image === "string" ? await readFile(image) : image;
|
|
143
|
+
return `data:image/png;base64,${Buffer.from(bytes).toString("base64")}`;
|
|
145
144
|
}),
|
|
146
145
|
);
|
|
147
|
-
const { pdf } = await printHtml(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
const { pdf } = await printHtml(
|
|
147
|
+
page,
|
|
148
|
+
imagePagesHtml(dataUrls, options.size, options.background),
|
|
149
|
+
{
|
|
150
|
+
size: options.size,
|
|
151
|
+
pageRanges: `1-${images.length}`,
|
|
152
|
+
},
|
|
153
|
+
);
|
|
151
154
|
return pdf;
|
|
152
155
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
// The slice of
|
|
2
|
-
//
|
|
3
|
-
// Browser satisfy these as they are, whichever version the caller has installed.
|
|
1
|
+
// The slice of Playwright the export helpers touch, declared here so the package
|
|
2
|
+
// takes no Playwright dependency and any installed version fits.
|
|
4
3
|
|
|
5
4
|
export interface Size {
|
|
6
5
|
width: number;
|
|
7
6
|
height: number;
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
export type WaitUntil =
|
|
9
|
+
export type WaitUntil = "load" | "domcontentloaded" | "networkidle";
|
|
11
10
|
|
|
12
11
|
export interface ResponseLike {
|
|
13
12
|
ok(): boolean;
|
|
@@ -18,19 +17,22 @@ export interface LocatorLike {
|
|
|
18
17
|
first(): LocatorLike;
|
|
19
18
|
nth(index: number): LocatorLike;
|
|
20
19
|
count(): Promise<number>;
|
|
21
|
-
waitFor(options?: { state?:
|
|
22
|
-
screenshot(options?: { path?: string; type?:
|
|
20
|
+
waitFor(options?: { state?: "visible"; timeout?: number }): Promise<void>;
|
|
21
|
+
screenshot(options?: { path?: string; type?: "png" }): Promise<Uint8Array>;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
export interface PageLike {
|
|
26
|
-
goto(
|
|
25
|
+
goto(
|
|
26
|
+
url: string,
|
|
27
|
+
options?: { waitUntil?: WaitUntil; timeout?: number },
|
|
28
|
+
): Promise<ResponseLike | null>;
|
|
27
29
|
setContent(html: string, options?: { waitUntil?: WaitUntil; timeout?: number }): Promise<void>;
|
|
28
30
|
addStyleTag(options: { content: string }): Promise<unknown>;
|
|
29
31
|
evaluate<R>(pageFunction: () => R | Promise<R>): Promise<R>;
|
|
30
32
|
waitForTimeout(ms: number): Promise<void>;
|
|
31
33
|
waitForSelector(selector: string, options?: { timeout?: number }): Promise<unknown>;
|
|
32
34
|
locator(selector: string): LocatorLike;
|
|
33
|
-
screenshot(options?: { type?:
|
|
35
|
+
screenshot(options?: { type?: "png"; fullPage?: boolean }): Promise<Uint8Array>;
|
|
34
36
|
pdf(options: {
|
|
35
37
|
path?: string;
|
|
36
38
|
width?: string;
|
package/src/slides/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export { default as Deck } from
|
|
2
|
-
export { default as Slide } from
|
|
3
|
-
export { default as Fragment } from
|
|
4
|
-
export { default as Presenter } from
|
|
5
|
-
export { DeckController } from
|
|
6
|
-
export type { SlideEntry, Source } from
|
|
7
|
-
export { defaultLabels } from
|
|
8
|
-
export type { DeckLabels } from
|
|
9
|
-
export type { Position } from
|
|
1
|
+
export { default as Deck } from "./Deck.svelte";
|
|
2
|
+
export { default as Slide } from "./Slide.svelte";
|
|
3
|
+
export { default as Fragment } from "./Fragment.svelte";
|
|
4
|
+
export { default as Presenter } from "./Presenter.svelte";
|
|
5
|
+
export { DeckController } from "./deck-controller.svelte.js";
|
|
6
|
+
export type { SlideEntry, Source } from "./deck-controller.svelte.js";
|
|
7
|
+
export { defaultLabels } from "./labels.js";
|
|
8
|
+
export type { DeckLabels } from "./labels.js";
|
|
9
|
+
export type { Position } from "./navigation.js";
|
|
10
10
|
export type {
|
|
11
11
|
BridgeOptions,
|
|
12
12
|
Canvas,
|
|
@@ -15,5 +15,5 @@ export type {
|
|
|
15
15
|
SlideAlign,
|
|
16
16
|
SlideNotes,
|
|
17
17
|
UrlSync,
|
|
18
|
-
} from
|
|
19
|
-
export { DEFAULT_CANVAS } from
|
|
18
|
+
} from "./types.js";
|
|
19
|
+
export { DEFAULT_CANVAS } from "./types.js";
|
package/src/slides/labels.ts
CHANGED
|
@@ -23,26 +23,29 @@ export type DeckLabels = {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
export const defaultLabels: DeckLabels = {
|
|
26
|
-
deck:
|
|
27
|
-
deckRole:
|
|
28
|
-
slideRole:
|
|
29
|
-
previous:
|
|
30
|
-
next:
|
|
31
|
-
fullscreen:
|
|
32
|
-
overview:
|
|
33
|
-
presenter:
|
|
34
|
-
progress:
|
|
26
|
+
deck: "Slide deck",
|
|
27
|
+
deckRole: "presentation",
|
|
28
|
+
slideRole: "slide",
|
|
29
|
+
previous: "Previous slide",
|
|
30
|
+
next: "Next slide",
|
|
31
|
+
fullscreen: "Toggle fullscreen",
|
|
32
|
+
overview: "Slide overview",
|
|
33
|
+
presenter: "Open presenter view",
|
|
34
|
+
progress: "Deck progress",
|
|
35
35
|
slide: (n, of) => `Slide ${n} of ${of}`,
|
|
36
|
-
upcoming:
|
|
37
|
-
current:
|
|
38
|
-
notes:
|
|
39
|
-
noNotes:
|
|
40
|
-
endOfDeck:
|
|
41
|
-
timer:
|
|
42
|
-
startTimer:
|
|
43
|
-
pauseTimer:
|
|
44
|
-
resetTimer:
|
|
36
|
+
upcoming: "Next",
|
|
37
|
+
current: "Now",
|
|
38
|
+
notes: "Notes",
|
|
39
|
+
noNotes: "No notes for this slide.",
|
|
40
|
+
endOfDeck: "End of deck.",
|
|
41
|
+
timer: "Elapsed time",
|
|
42
|
+
startTimer: "Start timer",
|
|
43
|
+
pauseTimer: "Pause timer",
|
|
44
|
+
resetTimer: "Reset timer",
|
|
45
45
|
unknownType: (type) => `Unknown slide type: ${type}`,
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
export const resolveLabels = (labels?: Partial<DeckLabels>): DeckLabels => ({
|
|
48
|
+
export const resolveLabels = (labels?: Partial<DeckLabels>): DeckLabels => ({
|
|
49
|
+
...defaultLabels,
|
|
50
|
+
...labels,
|
|
51
|
+
});
|
package/src/slides/layout.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Where a slide sits inside the stage: centred to fit, or in the overview grid. */
|
|
2
2
|
|
|
3
|
-
import type { Canvas } from
|
|
3
|
+
import type { Canvas } from "./types.js";
|
|
4
4
|
|
|
5
5
|
export type Size = { w: number; h: number };
|
|
6
6
|
|
|
@@ -15,7 +15,14 @@ export function fitBox(view: Size, canvas: Canvas): Box {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/** The scale at which `cols` columns of `count` canvases fit the view. */
|
|
18
|
-
function scaleFor(
|
|
18
|
+
function scaleFor(
|
|
19
|
+
cols: number,
|
|
20
|
+
count: number,
|
|
21
|
+
view: Size,
|
|
22
|
+
canvas: Canvas,
|
|
23
|
+
pad: number,
|
|
24
|
+
gap: number,
|
|
25
|
+
): number {
|
|
19
26
|
const rows = Math.ceil(count / cols);
|
|
20
27
|
const byWidth = (view.w - 2 * pad - (cols - 1) * gap) / cols / canvas.w;
|
|
21
28
|
const byHeight = (view.h - 2 * pad - (rows - 1) * gap) / rows / canvas.h;
|
package/src/slides/model.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Data-driven decks: a deck is JSON, and a registry says how each slide type draws itself. */
|
|
2
2
|
|
|
3
|
-
import type { Component } from
|
|
4
|
-
import type { Canvas, DeckTheme } from
|
|
3
|
+
import type { Component } from "svelte";
|
|
4
|
+
import type { Canvas, DeckTheme } from "./types.js";
|
|
5
5
|
|
|
6
6
|
export type SlideInstance<Content = unknown> = {
|
|
7
7
|
id: string;
|
|
@@ -33,7 +33,8 @@ export type SlideType<Content = any> = {
|
|
|
33
33
|
export type SlideRegistry = Readonly<Record<string, SlideType>>;
|
|
34
34
|
|
|
35
35
|
/** Identity function that fixes `Content` from the layout. */
|
|
36
|
-
export const defineSlideType = <Content>(definition: SlideType<Content>): SlideType<Content> =>
|
|
36
|
+
export const defineSlideType = <Content>(definition: SlideType<Content>): SlideType<Content> =>
|
|
37
|
+
definition;
|
|
37
38
|
|
|
38
39
|
/** Keys the types by their `type`. Two with the same name is a mistake worth failing on. */
|
|
39
40
|
export function defineRegistry(types: readonly SlideType[]): SlideRegistry {
|
|
@@ -44,4 +45,3 @@ export function defineRegistry(types: readonly SlideType[]): SlideRegistry {
|
|
|
44
45
|
}
|
|
45
46
|
return registry;
|
|
46
47
|
}
|
|
47
|
-
|
package/src/slides/navigation.ts
CHANGED
|
@@ -6,17 +6,17 @@ export type Position = { index: number; step: number };
|
|
|
6
6
|
export type SlideState = { steps: number; hidden: boolean };
|
|
7
7
|
|
|
8
8
|
export type KeyAction =
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
9
|
+
| "next"
|
|
10
|
+
| "prev"
|
|
11
|
+
| "up"
|
|
12
|
+
| "down"
|
|
13
|
+
| "first"
|
|
14
|
+
| "last"
|
|
15
|
+
| "select"
|
|
16
|
+
| "overview"
|
|
17
|
+
| "close"
|
|
18
|
+
| "fullscreen"
|
|
19
|
+
| "presenter";
|
|
20
20
|
|
|
21
21
|
export const visibleIndexes = (slides: readonly SlideState[]): number[] =>
|
|
22
22
|
slides.flatMap((slide, index) => (slide.hidden ? [] : [index]));
|
|
@@ -32,7 +32,11 @@ export function settle(slides: readonly SlideState[], position: Position): Posit
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/** The position after one forward press, or null at the end of a deck that doesn't loop. */
|
|
35
|
-
export function nextPosition(
|
|
35
|
+
export function nextPosition(
|
|
36
|
+
slides: readonly SlideState[],
|
|
37
|
+
from: Position,
|
|
38
|
+
loop = false,
|
|
39
|
+
): Position | null {
|
|
36
40
|
const visible = visibleIndexes(slides);
|
|
37
41
|
if (visible.length === 0) return null;
|
|
38
42
|
if (from.step < slides[from.index].steps) return { index: from.index, step: from.step + 1 };
|
|
@@ -42,12 +46,20 @@ export function nextPosition(slides: readonly SlideState[], from: Position, loop
|
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
/** The position after one back press. Landing on an earlier slide shows it fully revealed. */
|
|
45
|
-
export function prevPosition(
|
|
49
|
+
export function prevPosition(
|
|
50
|
+
slides: readonly SlideState[],
|
|
51
|
+
from: Position,
|
|
52
|
+
loop = false,
|
|
53
|
+
): Position | null {
|
|
46
54
|
const visible = visibleIndexes(slides);
|
|
47
55
|
if (visible.length === 0) return null;
|
|
48
56
|
if (from.step > 0) return { index: from.index, step: from.step - 1 };
|
|
49
57
|
const before = visible.filter((i) => i < from.index);
|
|
50
|
-
const index = before.length
|
|
58
|
+
const index = before.length
|
|
59
|
+
? before[before.length - 1]
|
|
60
|
+
: loop
|
|
61
|
+
? visible[visible.length - 1]
|
|
62
|
+
: undefined;
|
|
51
63
|
return index === undefined ? null : { index, step: slides[index].steps };
|
|
52
64
|
}
|
|
53
65
|
|
|
@@ -75,26 +87,26 @@ export function parseSlideParam(raw: string | null | undefined, total: number):
|
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
const KEYS: Record<string, KeyAction> = {
|
|
78
|
-
ArrowRight:
|
|
79
|
-
PageDown:
|
|
80
|
-
ArrowLeft:
|
|
81
|
-
PageUp:
|
|
82
|
-
ArrowUp:
|
|
83
|
-
ArrowDown:
|
|
84
|
-
Home:
|
|
85
|
-
End:
|
|
86
|
-
Enter:
|
|
87
|
-
Escape:
|
|
88
|
-
o:
|
|
89
|
-
O:
|
|
90
|
-
f:
|
|
91
|
-
F:
|
|
92
|
-
p:
|
|
93
|
-
P:
|
|
90
|
+
ArrowRight: "next",
|
|
91
|
+
PageDown: "next",
|
|
92
|
+
ArrowLeft: "prev",
|
|
93
|
+
PageUp: "prev",
|
|
94
|
+
ArrowUp: "up",
|
|
95
|
+
ArrowDown: "down",
|
|
96
|
+
Home: "first",
|
|
97
|
+
End: "last",
|
|
98
|
+
Enter: "select",
|
|
99
|
+
Escape: "close",
|
|
100
|
+
o: "overview",
|
|
101
|
+
O: "overview",
|
|
102
|
+
f: "fullscreen",
|
|
103
|
+
F: "fullscreen",
|
|
104
|
+
p: "presenter",
|
|
105
|
+
P: "presenter",
|
|
94
106
|
};
|
|
95
107
|
|
|
96
108
|
/** Maps a key to what the deck does with it. Space moves forward, Shift+Space back. */
|
|
97
109
|
export function keyAction(key: string, shift = false): KeyAction | null {
|
|
98
|
-
if (key ===
|
|
110
|
+
if (key === " ") return shift ? "prev" : "next";
|
|
99
111
|
return KEYS[key] ?? null;
|
|
100
112
|
}
|
package/src/slides/theme.css
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Everything global to a deck
|
|
3
|
-
*
|
|
4
|
-
* because the whole canvas scales, so a 28px heading is 28px on a 960px canvas at any zoom.
|
|
5
|
-
* Colours are flat hex: Chromium's PDF pipeline shifts oklch.
|
|
2
|
+
* Everything global to a deck, in design px since the whole canvas scales, and in flat
|
|
3
|
+
* hex because Chromium's PDF pipeline shifts oklch.
|
|
6
4
|
*/
|
|
7
5
|
|
|
8
6
|
.deck,
|
|
@@ -119,13 +117,13 @@
|
|
|
119
117
|
transform: scale(var(--scale-large, 0.96));
|
|
120
118
|
}
|
|
121
119
|
}
|
|
122
|
-
.deck[data-transition=
|
|
120
|
+
.deck[data-transition="fade"] .slide[data-active="true"] .slide__surface {
|
|
123
121
|
animation-name: deck-fade;
|
|
124
122
|
}
|
|
125
|
-
.deck[data-transition=
|
|
123
|
+
.deck[data-transition="slide"] .slide[data-active="true"] .slide__surface {
|
|
126
124
|
animation-name: deck-slide;
|
|
127
125
|
}
|
|
128
|
-
.deck[data-transition=
|
|
126
|
+
.deck[data-transition="zoom"] .slide[data-active="true"] .slide__surface {
|
|
129
127
|
animation-name: deck-zoom;
|
|
130
128
|
}
|
|
131
129
|
.deck[data-morphing] .slide__surface {
|
package/src/slides/types.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { Snippet } from
|
|
2
|
-
import type { DeckController } from
|
|
3
|
-
import type { DeckLabels } from
|
|
4
|
-
import type { SlideInstance, SlideRegistry } from
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { DeckController } from "./deck-controller.svelte.js";
|
|
3
|
+
import type { DeckLabels } from "./labels.js";
|
|
4
|
+
import type { SlideInstance, SlideRegistry } from "./model.js";
|
|
5
5
|
|
|
6
|
-
export const DECK_KEY = Symbol(
|
|
7
|
-
export const SLIDE_KEY = Symbol(
|
|
6
|
+
export const DECK_KEY = Symbol("heyramzi.deck");
|
|
7
|
+
export const SLIDE_KEY = Symbol("heyramzi.slide");
|
|
8
8
|
|
|
9
9
|
/** `ink` is the dark default, `paper` a light surface, `auto` follows the system. */
|
|
10
|
-
export type DeckTheme =
|
|
10
|
+
export type DeckTheme = "ink" | "paper" | "auto";
|
|
11
11
|
|
|
12
12
|
/** How a slide enters. `morph` uses the View Transitions API and falls back to a cut. */
|
|
13
|
-
export type DeckTransition =
|
|
13
|
+
export type DeckTransition = "fade" | "slide" | "zoom" | "morph" | "none";
|
|
14
14
|
|
|
15
15
|
/** The design size in px. Slides lay out inside it and the whole canvas scales to fit. */
|
|
16
16
|
export type Canvas = { w: number; h: number };
|
|
@@ -21,7 +21,7 @@ export const DEFAULT_CANVAS: Canvas = { w: 960, h: 540 };
|
|
|
21
21
|
export type SlideNotes = string | Snippet;
|
|
22
22
|
|
|
23
23
|
/** How a Slide lays out its own content. `bare` leaves the whole canvas to the author. */
|
|
24
|
-
export type SlideAlign =
|
|
24
|
+
export type SlideAlign = "center" | "start" | "bare";
|
|
25
25
|
|
|
26
26
|
/** Where a deck keeps `?s=`. `write` replaces the default `history.replaceState`, so SvelteKit apps can use its own. */
|
|
27
27
|
export type UrlSync = { param?: string; write?: (url: URL) => void };
|
|
@@ -83,4 +83,3 @@ export type DeckProps = {
|
|
|
83
83
|
onchange?: (index: number, step: number) => void;
|
|
84
84
|
children?: Snippet;
|
|
85
85
|
};
|
|
86
|
-
|
|
@@ -1,32 +1,25 @@
|
|
|
1
1
|
import type { StatusDef, StatusId, StatusMap, Statuses } from "./status-types.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* ```ts
|
|
8
|
-
* const statuses = defineStatuses({
|
|
9
|
-
* draft: { label: "Draft", tone: "neutral" },
|
|
10
|
-
* sent: { label: "Sent", tone: "info" },
|
|
11
|
-
* });
|
|
12
|
-
* ```
|
|
4
|
+
* Builds a status registry from a map, keeping its keys as a union so lookups stay typed:
|
|
5
|
+
* `defineStatuses({ draft: { label: "Draft", tone: "neutral" } })`.
|
|
13
6
|
*/
|
|
14
7
|
export function defineStatuses<const M extends StatusMap>(
|
|
15
|
-
|
|
8
|
+
map: M,
|
|
16
9
|
): Statuses & { ids: StatusId<M>[] } {
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
const lookup = map as Record<string, StatusDef | undefined>;
|
|
11
|
+
const find = (id: string): StatusDef | undefined => lookup[id];
|
|
19
12
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
13
|
+
return {
|
|
14
|
+
map,
|
|
15
|
+
ids: Object.keys(map) as StatusId<M>[],
|
|
16
|
+
has: (id) => find(id) !== undefined,
|
|
17
|
+
get: find,
|
|
18
|
+
label: (id) => find(id)?.label ?? id,
|
|
19
|
+
tone: (id) => find(id)?.tone ?? "neutral",
|
|
20
|
+
icon: (id) => find(id)?.icon,
|
|
21
|
+
variant: (id) => find(id)?.variant ?? "open",
|
|
22
|
+
progress: (id) => find(id)?.progress,
|
|
23
|
+
color: (id) => find(id)?.color,
|
|
24
|
+
};
|
|
32
25
|
}
|
package/src/status/index.ts
CHANGED
|
@@ -3,9 +3,9 @@ export { default as StatusDot } from "./status-dot.svelte";
|
|
|
3
3
|
export { default as StatusIcon } from "./status-icon.svelte";
|
|
4
4
|
export { defineStatuses } from "./define-statuses.js";
|
|
5
5
|
export type {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
StatusDef,
|
|
7
|
+
StatusDotVariant,
|
|
8
|
+
StatusId,
|
|
9
|
+
StatusMap,
|
|
10
|
+
Statuses,
|
|
11
11
|
} from "./status-types.js";
|
|
@@ -41,7 +41,9 @@
|
|
|
41
41
|
}: Props = $props();
|
|
42
42
|
|
|
43
43
|
let resolvedTone = $derived(tone ?? (statuses && status ? statuses.tone(status) : "neutral"));
|
|
44
|
-
let resolvedLabel = $derived(
|
|
44
|
+
let resolvedLabel = $derived(
|
|
45
|
+
label ?? (statuses && status ? statuses.label(status) : (status ?? "")),
|
|
46
|
+
);
|
|
45
47
|
</script>
|
|
46
48
|
|
|
47
49
|
<Badge tone={resolvedTone} class={cn(capitalize && "capitalize", className)} {...restProps}>
|
|
@@ -52,9 +52,7 @@
|
|
|
52
52
|
: undefined,
|
|
53
53
|
);
|
|
54
54
|
let dasharray = $derived(
|
|
55
|
-
fraction != null
|
|
56
|
-
? `${INNER_CIRC * fraction} ${INNER_CIRC * (1 - fraction)}`
|
|
57
|
-
: undefined,
|
|
55
|
+
fraction != null ? `${INNER_CIRC * fraction} ${INNER_CIRC * (1 - fraction)}` : undefined,
|
|
58
56
|
);
|
|
59
57
|
let dashoffset = $derived(fraction != null ? INNER_CIRC * 0.25 : undefined);
|
|
60
58
|
</script>
|
|
@@ -8,16 +8,16 @@ import type { Tone } from "../lib/tone.js";
|
|
|
8
8
|
export type StatusDotVariant = "open" | "dashed" | "progress" | "filled" | "completed" | "lost";
|
|
9
9
|
|
|
10
10
|
export type StatusDef = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
label: string;
|
|
12
|
+
tone?: Tone;
|
|
13
|
+
/** Any Svelte component, for example a Phosphor icon. Renders inside StatusIcon. */
|
|
14
|
+
icon?: Component;
|
|
15
|
+
variant?: StatusDotVariant;
|
|
16
|
+
/** 0 to 1. Only used by the `progress` variant. */
|
|
17
|
+
progress?: number;
|
|
18
|
+
/** Optional CSS color. Overrides the tone color for the dot and icon. */
|
|
19
|
+
color?: string;
|
|
20
|
+
description?: string;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
export type StatusMap = Record<string, StatusDef>;
|
|
@@ -26,15 +26,15 @@ export type StatusId<M extends StatusMap> = keyof M & string;
|
|
|
26
26
|
|
|
27
27
|
/** What the components accept. `defineStatuses` returns this plus a typed `ids`. */
|
|
28
28
|
export type Statuses = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
/** The raw map. Kept loose so a registry with specific keys is assignable here. */
|
|
30
|
+
map: StatusMap;
|
|
31
|
+
ids: string[];
|
|
32
|
+
has(id: string): boolean;
|
|
33
|
+
get(id: string): StatusDef | undefined;
|
|
34
|
+
label(id: string): string;
|
|
35
|
+
tone(id: string): Tone;
|
|
36
|
+
icon(id: string): Component | undefined;
|
|
37
|
+
variant(id: string): StatusDotVariant;
|
|
38
|
+
progress(id: string): number | undefined;
|
|
39
|
+
color(id: string): string | undefined;
|
|
40
40
|
};
|
package/src/tasks/board-order.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
// Fractional indexing
|
|
2
|
-
//
|
|
3
|
-
// the CC0 one from https://observablehq.com/@dgreensp/implementing-fractional-indexing
|
|
4
|
-
// (see the README credit). Kept inline so this package carries no runtime deps.
|
|
1
|
+
// Fractional indexing, so a move writes one row instead of renumbering a column; it's the
|
|
2
|
+
// CC0 algorithm from https://observablehq.com/@dgreensp/implementing-fractional-indexing.
|
|
5
3
|
const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
6
4
|
const INT_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
7
5
|
const ZERO = DIGITS.charAt(0);
|