@urbicon-ui/blocks 6.35.0 → 6.37.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.
Files changed (124) hide show
  1. package/dist/components/Chat/Chat/Chat.svelte +53 -0
  2. package/dist/components/Chat/Chat/Chat.svelte.d.ts +4 -0
  3. package/dist/components/Chat/Chat/chat.variants.d.ts +20 -0
  4. package/dist/components/Chat/Chat/chat.variants.js +15 -0
  5. package/dist/components/Chat/Chat/index.d.ts +47 -0
  6. package/dist/components/Chat/Chat/index.js +2 -0
  7. package/dist/components/Chat/ChatMessage/ChatMessage.svelte +342 -0
  8. package/dist/components/Chat/ChatMessage/ChatMessage.svelte.d.ts +4 -0
  9. package/dist/components/Chat/ChatMessage/chat-message.variants.d.ts +138 -0
  10. package/dist/components/Chat/ChatMessage/chat-message.variants.js +118 -0
  11. package/dist/components/Chat/ChatMessage/index.d.ts +102 -0
  12. package/dist/components/Chat/ChatMessage/index.js +2 -0
  13. package/dist/components/Chat/ChatMessageList/ChatMessageList.svelte +301 -0
  14. package/dist/components/Chat/ChatMessageList/ChatMessageList.svelte.d.ts +6 -0
  15. package/dist/components/Chat/ChatMessageList/chat-message-list.variants.d.ts +23 -0
  16. package/dist/components/Chat/ChatMessageList/chat-message-list.variants.js +32 -0
  17. package/dist/components/Chat/ChatMessageList/chat-scroll.d.ts +45 -0
  18. package/dist/components/Chat/ChatMessageList/chat-scroll.js +52 -0
  19. package/dist/components/Chat/ChatMessageList/index.d.ts +93 -0
  20. package/dist/components/Chat/ChatMessageList/index.js +3 -0
  21. package/dist/components/Chat/CitationChip/CitationChip.svelte +107 -0
  22. package/dist/components/Chat/CitationChip/CitationChip.svelte.d.ts +4 -0
  23. package/dist/components/Chat/CitationChip/citation-chip.variants.d.ts +40 -0
  24. package/dist/components/Chat/CitationChip/citation-chip.variants.js +42 -0
  25. package/dist/components/Chat/CitationChip/index.d.ts +69 -0
  26. package/dist/components/Chat/CitationChip/index.js +2 -0
  27. package/dist/components/Chat/CodeBlock/CodeBlock.svelte +120 -0
  28. package/dist/components/Chat/CodeBlock/CodeBlock.svelte.d.ts +4 -0
  29. package/dist/components/Chat/CodeBlock/code-block.variants.d.ts +40 -0
  30. package/dist/components/Chat/CodeBlock/code-block.variants.js +51 -0
  31. package/dist/components/Chat/CodeBlock/index.d.ts +55 -0
  32. package/dist/components/Chat/CodeBlock/index.js +2 -0
  33. package/dist/components/Chat/PromptInput/PromptInput.svelte +432 -0
  34. package/dist/components/Chat/PromptInput/PromptInput.svelte.d.ts +4 -0
  35. package/dist/components/Chat/PromptInput/index.d.ts +165 -0
  36. package/dist/components/Chat/PromptInput/index.js +2 -0
  37. package/dist/components/Chat/PromptInput/prompt-input.variants.d.ts +90 -0
  38. package/dist/components/Chat/PromptInput/prompt-input.variants.js +102 -0
  39. package/dist/components/Chat/ReasoningDisclosure/ReasoningDisclosure.svelte +107 -0
  40. package/dist/components/Chat/ReasoningDisclosure/ReasoningDisclosure.svelte.d.ts +4 -0
  41. package/dist/components/Chat/ReasoningDisclosure/index.d.ts +85 -0
  42. package/dist/components/Chat/ReasoningDisclosure/index.js +2 -0
  43. package/dist/components/Chat/ReasoningDisclosure/reasoning-disclosure.variants.d.ts +20 -0
  44. package/dist/components/Chat/ReasoningDisclosure/reasoning-disclosure.variants.js +31 -0
  45. package/dist/components/Chat/StreamingMarkdown/MdBlock.svelte +99 -0
  46. package/dist/components/Chat/StreamingMarkdown/MdBlock.svelte.d.ts +9 -0
  47. package/dist/components/Chat/StreamingMarkdown/MdInline.svelte +82 -0
  48. package/dist/components/Chat/StreamingMarkdown/MdInline.svelte.d.ts +9 -0
  49. package/dist/components/Chat/StreamingMarkdown/StreamingMarkdown.svelte +118 -0
  50. package/dist/components/Chat/StreamingMarkdown/StreamingMarkdown.svelte.d.ts +4 -0
  51. package/dist/components/Chat/StreamingMarkdown/index.d.ts +86 -0
  52. package/dist/components/Chat/StreamingMarkdown/index.js +2 -0
  53. package/dist/components/Chat/StreamingMarkdown/md-context.d.ts +58 -0
  54. package/dist/components/Chat/StreamingMarkdown/md-context.js +1 -0
  55. package/dist/components/Chat/StreamingMarkdown/streaming-markdown.variants.d.ts +145 -0
  56. package/dist/components/Chat/StreamingMarkdown/streaming-markdown.variants.js +73 -0
  57. package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte +174 -0
  58. package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte.d.ts +4 -0
  59. package/dist/components/Chat/ToolCallCard/index.d.ts +84 -0
  60. package/dist/components/Chat/ToolCallCard/index.js +2 -0
  61. package/dist/components/Chat/ToolCallCard/tool-call-card.variants.d.ts +38 -0
  62. package/dist/components/Chat/ToolCallCard/tool-call-card.variants.js +38 -0
  63. package/dist/components/Chat/chat.types.d.ts +82 -0
  64. package/dist/components/Chat/chat.types.js +7 -0
  65. package/dist/components/Chat/index.d.ts +22 -0
  66. package/dist/components/Chat/index.js +14 -0
  67. package/dist/components/Chat/markdown/blocks.d.ts +54 -0
  68. package/dist/components/Chat/markdown/blocks.js +462 -0
  69. package/dist/components/Chat/markdown/inline.d.ts +31 -0
  70. package/dist/components/Chat/markdown/inline.js +446 -0
  71. package/dist/components/Chat/markdown/repair.d.ts +23 -0
  72. package/dist/components/Chat/markdown/repair.js +337 -0
  73. package/dist/components/Chat/markdown/streaming-harness.d.ts +23 -0
  74. package/dist/components/Chat/markdown/streaming-harness.js +62 -0
  75. package/dist/components/Chat/markdown/types.d.ts +139 -0
  76. package/dist/components/Chat/markdown/types.js +13 -0
  77. package/dist/components/Chat/markdown/url-policy.d.ts +33 -0
  78. package/dist/components/Chat/markdown/url-policy.js +109 -0
  79. package/dist/components/FileUpload/FileUpload.svelte +35 -123
  80. package/dist/components/FileUpload/FileUpload.svelte.d.ts +1 -1
  81. package/dist/components/FileUpload/index.d.ts +2 -28
  82. package/dist/components/PinInput/PinInput.svelte +321 -0
  83. package/dist/components/PinInput/PinInput.svelte.d.ts +4 -0
  84. package/dist/components/PinInput/index.d.ts +98 -0
  85. package/dist/components/PinInput/index.js +2 -0
  86. package/dist/components/PinInput/pin-input.variants.d.ts +96 -0
  87. package/dist/components/PinInput/pin-input.variants.js +111 -0
  88. package/dist/components/QRCode/QRCode.svelte +107 -0
  89. package/dist/components/QRCode/QRCode.svelte.d.ts +4 -0
  90. package/dist/components/QRCode/index.d.ts +74 -0
  91. package/dist/components/QRCode/index.js +3 -0
  92. package/dist/components/QRCode/qr-code.variants.d.ts +25 -0
  93. package/dist/components/QRCode/qr-code.variants.js +24 -0
  94. package/dist/components/QRCode/qr-encode.d.ts +22 -0
  95. package/dist/components/QRCode/qr-encode.js +528 -0
  96. package/dist/components/TimeInput/TimeInput.svelte +521 -0
  97. package/dist/components/TimeInput/TimeInput.svelte.d.ts +4 -0
  98. package/dist/components/TimeInput/index.d.ts +93 -0
  99. package/dist/components/TimeInput/index.js +2 -0
  100. package/dist/components/TimeInput/time-input.variants.d.ts +131 -0
  101. package/dist/components/TimeInput/time-input.variants.js +120 -0
  102. package/dist/components/index.d.ts +7 -0
  103. package/dist/components/index.js +4 -0
  104. package/dist/i18n/index.d.ts +18 -0
  105. package/dist/primitives/Collapsible/Collapsible.svelte +5 -0
  106. package/dist/primitives/SplitPane/SplitPane.svelte +293 -0
  107. package/dist/primitives/SplitPane/SplitPane.svelte.d.ts +4 -0
  108. package/dist/primitives/SplitPane/index.d.ts +88 -0
  109. package/dist/primitives/SplitPane/index.js +2 -0
  110. package/dist/primitives/SplitPane/split-pane.utils.d.ts +73 -0
  111. package/dist/primitives/SplitPane/split-pane.utils.js +94 -0
  112. package/dist/primitives/SplitPane/split-pane.variants.d.ts +40 -0
  113. package/dist/primitives/SplitPane/split-pane.variants.js +56 -0
  114. package/dist/primitives/index.d.ts +2 -0
  115. package/dist/primitives/index.js +1 -0
  116. package/dist/translations/de.d.ts +6 -0
  117. package/dist/translations/de.js +6 -0
  118. package/dist/translations/en.d.ts +6 -0
  119. package/dist/translations/en.js +6 -0
  120. package/dist/utils/file-intake.d.ts +78 -0
  121. package/dist/utils/file-intake.js +144 -0
  122. package/dist/utils/index.d.ts +1 -0
  123. package/dist/utils/index.js +1 -0
  124. package/package.json +3 -3
@@ -0,0 +1,88 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { MintProp } from '../../mint/index.js';
4
+ import type { SplitPaneLimit } from './split-pane.utils.js';
5
+ import type { SplitPaneSlots, SplitPaneVariants } from './split-pane.variants.js';
6
+ /**
7
+ * @description Resizable two-pane layout. A draggable divider (ARIA "window
8
+ * splitter", role="separator") splits the container into a `start` and an `end`
9
+ * pane; the first pane's share is controlled by `ratio` (0–1) and supports
10
+ * `bind:ratio`. Resize by pointer drag or keyboard (Arrow / Home / End), with
11
+ * optional collapse-to-zero. Panes clip their own overflow so their content
12
+ * scrolls independently. For an application shell with a permanent nav rail
13
+ * prefer SidebarLayout / Sidebar; reach for SplitPane when both regions are
14
+ * primary content the user should be able to rebalance.
15
+ *
16
+ * @tag layout
17
+ * @related SidebarLayout
18
+ * @related Sidebar
19
+ * @stability experimental
20
+ *
21
+ * @example
22
+ * ```svelte
23
+ * <SplitPane bind:ratio={split} min="20%" max="80%">
24
+ * {#snippet start()}<nav class="p-4">…</nav>{/snippet}
25
+ * {#snippet end()}<main class="p-4">…</main>{/snippet}
26
+ * </SplitPane>
27
+ * ```
28
+ *
29
+ * @example
30
+ * ```svelte
31
+ * <SplitPane orientation="vertical" collapsible defaultRatio={0.3}>
32
+ * {#snippet start()}<section class="overflow-auto">Editor</section>{/snippet}
33
+ * {#snippet end()}<section class="overflow-auto">Preview</section>{/snippet}
34
+ * </SplitPane>
35
+ * ```
36
+ */
37
+ export interface SplitPaneProps extends Omit<SplitPaneVariants, 'dragging' | 'orientation' | 'disabled'>, Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'class'> {
38
+ /** First pane. In `horizontal` orientation it is the leading (left) pane; in `vertical` the top pane. Required. */
39
+ start: Snippet;
40
+ /** Second pane — fills the space the first pane leaves. Required. */
41
+ end: Snippet;
42
+ /** Custom content rendered inside the divider (e.g. a grip icon). Replaces the default line; the divider stays the focusable separator. */
43
+ handle?: Snippet;
44
+ /** Layout axis. `horizontal` places panes side by side (vertical divider); `vertical` stacks them (horizontal divider). @default 'horizontal' */
45
+ orientation?: 'horizontal' | 'vertical';
46
+ /** First pane's share of the container, `0`–`1`. Supports `bind:ratio`. When omitted, starts at `defaultRatio` and is managed internally. */
47
+ ratio?: number;
48
+ /** Initial ratio for uncontrolled use; also the target of a double-click reset. @default 0.5 */
49
+ defaultRatio?: number;
50
+ /** Lower bound for the first pane — px (`number`) or percentage string (`'20%'`). @default '10%' */
51
+ min?: SplitPaneLimit;
52
+ /** Upper bound for the first pane — px (`number`) or percentage string (`'90%'`). @default '90%' */
53
+ max?: SplitPaneLimit;
54
+ /** Allow the first pane to collapse to zero: dragging below `collapseThreshold`, or pressing Enter on the divider, snaps it shut. @default false */
55
+ collapsible?: boolean;
56
+ /** Pixel width/height of the first pane below which a drag snaps it collapsed. Only used when `collapsible`. @default 48 */
57
+ collapseThreshold?: number;
58
+ /** Disable resizing — the divider becomes inert (not focusable, no pointer/keyboard response). Panes stay visible. @default false */
59
+ disabled?: boolean;
60
+ /** Fires after a drag/keyboard interaction changes the ratio. Receives the new ratio (`0`–`1`). Does not fire for consumer-driven `bind:ratio` writes. */
61
+ onRatioChange?: (ratio: number) => void;
62
+ /** Fires when the first pane collapses (`true`) or re-expands (`false`) via drag or Enter. */
63
+ onCollapsedChange?: (collapsed: boolean) => void;
64
+ /** Accessible name for the divider (its `aria-label`). @default 'Resize panes' */
65
+ handleLabel?: string;
66
+ /** Micro-interaction preset applied to the divider. @default 'none' */
67
+ mint?: MintProp;
68
+ /** Extra classes merged onto the root container (the flex box). */
69
+ class?: string;
70
+ /** Remove all default tv() classes; combine with `slotClasses` to rebuild the look. */
71
+ unstyled?: boolean;
72
+ /**
73
+ * Per-slot class overrides merged with tv() styles. Slots: root (the flex
74
+ * container — what `class` also targets) | startPane | endPane | handle (the
75
+ * draggable divider / separator).
76
+ */
77
+ slotClasses?: Partial<Record<SplitPaneSlots, string>>;
78
+ /**
79
+ * Apply a named preset registered via `<BlocksProvider presets={{ SplitPane: {...} }}>`.
80
+ * Prefer this over `class` overrides when the requested look falls outside the
81
+ * semantic intent palette — presets keep hover/active/dark-mode logic coherent
82
+ * and make the custom look reusable across the project.
83
+ */
84
+ preset?: string;
85
+ }
86
+ export { default as SplitPane } from './SplitPane.svelte';
87
+ export type { SplitPaneLimit, SplitPaneOrientation } from './split-pane.utils.js';
88
+ export { type SplitPaneVariants, splitPaneVariants } from './split-pane.variants.js';
@@ -0,0 +1,2 @@
1
+ export { default as SplitPane } from './SplitPane.svelte';
2
+ export { splitPaneVariants } from './split-pane.variants.js';
@@ -0,0 +1,73 @@
1
+ /** Layout axis. `horizontal` = panes side by side; `vertical` = stacked. */
2
+ export type SplitPaneOrientation = 'horizontal' | 'vertical';
3
+ /**
4
+ * A min/max boundary for the first pane. A raw `number` is pixels; a string
5
+ * ending in `%` (e.g. `'20%'`) is a percentage of the container. A bare numeric
6
+ * string (`'200'`) is also treated as pixels.
7
+ */
8
+ export type SplitPaneLimit = number | string;
9
+ /** The four rect fields the pointer math reads — a structural subset of DOMRect. */
10
+ export interface RectLike {
11
+ left: number;
12
+ top: number;
13
+ width: number;
14
+ height: number;
15
+ }
16
+ /**
17
+ * Resolve a min/max limit to a ratio in `[0, 1]`.
18
+ *
19
+ * - Percentage strings (`'20%'`) convert directly and need no measurement.
20
+ * - Pixel values (number or bare numeric string) are divided by `containerPx`.
21
+ *
22
+ * Returns `null` when a pixel limit cannot be resolved because the container has
23
+ * not been measured yet (`containerPx <= 0`). Callers treat a `null` minimum as
24
+ * `0` and a `null` maximum as `1`, so an unmeasured px limit degrades to "no
25
+ * constraint" until the first layout pass, then clamps at runtime.
26
+ */
27
+ export declare function parseLimit(limit: SplitPaneLimit, containerPx: number): number | null;
28
+ /**
29
+ * Clamp a ratio into the `[min, max]` window, resolving px/percent limits
30
+ * against `containerPx`. Unresolved px limits fall back to `0` (min) / `1`
31
+ * (max). A reversed window (`min > max`) is tolerated by swapping the bounds so
32
+ * the result is always well-defined. A non-finite input ratio snaps to the
33
+ * lower bound.
34
+ */
35
+ export declare function clampRatio(ratio: number, min: SplitPaneLimit, max: SplitPaneLimit, containerPx: number): number;
36
+ /**
37
+ * Convert a pointer coordinate to the first-pane ratio (`[0, 1]`), before
38
+ * min/max clamping.
39
+ *
40
+ * - `horizontal`: uses `clientX` against `rect.left` / `rect.width`. When `rtl`
41
+ * is true the axis is mirrored (the first pane grows from the right edge).
42
+ * - `vertical`: uses `clientY` against `rect.top` / `rect.height`; `rtl` has no
43
+ * effect on the vertical axis.
44
+ *
45
+ * A zero-sized container yields `0` rather than dividing by zero.
46
+ */
47
+ export declare function ratioFromPointer(clientPos: number, rect: RectLike, orientation: SplitPaneOrientation, rtl?: boolean): number;
48
+ /** Limits + collapse config consumed by {@link resolveDragRatio}. */
49
+ export interface DragResolveOptions {
50
+ min: SplitPaneLimit;
51
+ max: SplitPaneLimit;
52
+ collapsible: boolean;
53
+ collapseThreshold: number;
54
+ }
55
+ /**
56
+ * Dead-band added on top of `collapseThreshold` before a collapsed pane
57
+ * re-expands during a drag. Without it, pointer jitter around the threshold
58
+ * flip-flops ratio between `0` and `min` and bursts the change callbacks.
59
+ */
60
+ export declare const COLLAPSE_HYSTERESIS_PX = 12;
61
+ /**
62
+ * Decide the resting ratio and collapsed flag for a raw pointer ratio.
63
+ *
64
+ * Collapse wins over the min clamp: while `collapsible`, dragging the first pane
65
+ * below `collapseThreshold` px snaps it fully shut (`ratio: 0`, `collapsed:
66
+ * true`). While `currentlyCollapsed`, re-expanding requires clearing the
67
+ * threshold plus {@link COLLAPSE_HYSTERESIS_PX} (drag hysteresis). Otherwise the
68
+ * ratio is clamped into the `[min, max]` window and `collapsed` is `false`.
69
+ */
70
+ export declare function resolveDragRatio(raw: number, containerPx: number, { min, max, collapsible, collapseThreshold }: DragResolveOptions, currentlyCollapsed?: boolean): {
71
+ ratio: number;
72
+ collapsed: boolean;
73
+ };
@@ -0,0 +1,94 @@
1
+ // Pure geometry for the SplitPane primitive. Every function here is
2
+ // side-effect-free and DOM-free so the resize math can be unit-tested in the
3
+ // node environment (no jsdom) and the `.svelte` file can stay a thin shell over
4
+ // it — exactly the split Slider draws between its keyboard math and its markup.
5
+ const clamp01 = (n) => Math.min(1, Math.max(0, n));
6
+ /**
7
+ * Resolve a min/max limit to a ratio in `[0, 1]`.
8
+ *
9
+ * - Percentage strings (`'20%'`) convert directly and need no measurement.
10
+ * - Pixel values (number or bare numeric string) are divided by `containerPx`.
11
+ *
12
+ * Returns `null` when a pixel limit cannot be resolved because the container has
13
+ * not been measured yet (`containerPx <= 0`). Callers treat a `null` minimum as
14
+ * `0` and a `null` maximum as `1`, so an unmeasured px limit degrades to "no
15
+ * constraint" until the first layout pass, then clamps at runtime.
16
+ */
17
+ export function parseLimit(limit, containerPx) {
18
+ if (typeof limit === 'string') {
19
+ const trimmed = limit.trim();
20
+ if (trimmed.endsWith('%')) {
21
+ const pct = Number.parseFloat(trimmed.slice(0, -1));
22
+ return Number.isFinite(pct) ? clamp01(pct / 100) : null;
23
+ }
24
+ const px = Number.parseFloat(trimmed);
25
+ if (!Number.isFinite(px))
26
+ return null;
27
+ return containerPx > 0 ? clamp01(px / containerPx) : null;
28
+ }
29
+ if (!Number.isFinite(limit))
30
+ return null;
31
+ return containerPx > 0 ? clamp01(limit / containerPx) : null;
32
+ }
33
+ /**
34
+ * Clamp a ratio into the `[min, max]` window, resolving px/percent limits
35
+ * against `containerPx`. Unresolved px limits fall back to `0` (min) / `1`
36
+ * (max). A reversed window (`min > max`) is tolerated by swapping the bounds so
37
+ * the result is always well-defined. A non-finite input ratio snaps to the
38
+ * lower bound.
39
+ */
40
+ export function clampRatio(ratio, min, max, containerPx) {
41
+ const lo = parseLimit(min, containerPx) ?? 0;
42
+ const hi = parseLimit(max, containerPx) ?? 1;
43
+ const low = Math.min(lo, hi);
44
+ const high = Math.max(lo, hi);
45
+ const r = Number.isFinite(ratio) ? ratio : low;
46
+ return Math.min(high, Math.max(low, r));
47
+ }
48
+ /**
49
+ * Convert a pointer coordinate to the first-pane ratio (`[0, 1]`), before
50
+ * min/max clamping.
51
+ *
52
+ * - `horizontal`: uses `clientX` against `rect.left` / `rect.width`. When `rtl`
53
+ * is true the axis is mirrored (the first pane grows from the right edge).
54
+ * - `vertical`: uses `clientY` against `rect.top` / `rect.height`; `rtl` has no
55
+ * effect on the vertical axis.
56
+ *
57
+ * A zero-sized container yields `0` rather than dividing by zero.
58
+ */
59
+ export function ratioFromPointer(clientPos, rect, orientation, rtl = false) {
60
+ if (orientation === 'vertical') {
61
+ if (!(rect.height > 0))
62
+ return 0;
63
+ return clamp01((clientPos - rect.top) / rect.height);
64
+ }
65
+ if (!(rect.width > 0))
66
+ return 0;
67
+ const raw = (clientPos - rect.left) / rect.width;
68
+ return clamp01(rtl ? 1 - raw : raw);
69
+ }
70
+ /**
71
+ * Dead-band added on top of `collapseThreshold` before a collapsed pane
72
+ * re-expands during a drag. Without it, pointer jitter around the threshold
73
+ * flip-flops ratio between `0` and `min` and bursts the change callbacks.
74
+ */
75
+ export const COLLAPSE_HYSTERESIS_PX = 12;
76
+ /**
77
+ * Decide the resting ratio and collapsed flag for a raw pointer ratio.
78
+ *
79
+ * Collapse wins over the min clamp: while `collapsible`, dragging the first pane
80
+ * below `collapseThreshold` px snaps it fully shut (`ratio: 0`, `collapsed:
81
+ * true`). While `currentlyCollapsed`, re-expanding requires clearing the
82
+ * threshold plus {@link COLLAPSE_HYSTERESIS_PX} (drag hysteresis). Otherwise the
83
+ * ratio is clamped into the `[min, max]` window and `collapsed` is `false`.
84
+ */
85
+ export function resolveDragRatio(raw, containerPx, { min, max, collapsible, collapseThreshold }, currentlyCollapsed = false) {
86
+ if (collapsible && containerPx > 0) {
87
+ const releasePx = currentlyCollapsed
88
+ ? collapseThreshold + COLLAPSE_HYSTERESIS_PX
89
+ : collapseThreshold;
90
+ if (raw * containerPx < releasePx)
91
+ return { ratio: 0, collapsed: true };
92
+ }
93
+ return { ratio: clampRatio(raw, min, max, containerPx), collapsed: false };
94
+ }
@@ -0,0 +1,40 @@
1
+ import { type SlotNames, type VariantProps } from '../../utils/variants.js';
2
+ export declare const splitPaneVariants: ((props?: {
3
+ orientation?: "horizontal" | "vertical" | undefined;
4
+ dragging?: boolean | undefined;
5
+ disabled?: boolean | undefined;
6
+ } | undefined) => {
7
+ root: (props?: ({
8
+ orientation?: "horizontal" | "vertical" | undefined;
9
+ dragging?: boolean | undefined;
10
+ disabled?: boolean | undefined;
11
+ } & {
12
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
13
+ }) | undefined) => string;
14
+ startPane: (props?: ({
15
+ orientation?: "horizontal" | "vertical" | undefined;
16
+ dragging?: boolean | undefined;
17
+ disabled?: boolean | undefined;
18
+ } & {
19
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
20
+ }) | undefined) => string;
21
+ endPane: (props?: ({
22
+ orientation?: "horizontal" | "vertical" | undefined;
23
+ dragging?: boolean | undefined;
24
+ disabled?: boolean | undefined;
25
+ } & {
26
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
27
+ }) | undefined) => string;
28
+ handle: (props?: ({
29
+ orientation?: "horizontal" | "vertical" | undefined;
30
+ dragging?: boolean | undefined;
31
+ disabled?: boolean | undefined;
32
+ } & {
33
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
34
+ }) | undefined) => string;
35
+ }) & {
36
+ readonly config: import("../../utils/variants.js").TVConfig;
37
+ };
38
+ export type SplitPaneVariants = VariantProps<typeof splitPaneVariants>;
39
+ /** Slot names derived from the `tv()` config above — single source of truth for `slotClasses`. */
40
+ export type SplitPaneSlots = SlotNames<typeof splitPaneVariants>;
@@ -0,0 +1,56 @@
1
+ import { tv } from '../../utils/variants.js';
2
+ export const splitPaneVariants = tv({
3
+ slots: {
4
+ // Flex container. `h-full`/`w-full` inherit the consumer's box; the panes
5
+ // size via flex-basis so the divider can sit at any ratio.
6
+ root: ['relative flex w-full h-full min-w-0 min-h-0'],
7
+ // Panes clip their own overflow and let long content scroll internally —
8
+ // min-w-0/min-h-0 defeats flexbox's default min-content floor so a pane can
9
+ // actually shrink below its content size.
10
+ startPane: ['overflow-hidden min-w-0 min-h-0'],
11
+ endPane: ['overflow-hidden min-w-0 min-h-0'],
12
+ // The divider is a thin visible line (bg-border-default) that is itself the
13
+ // role="separator". It is positioned (`relative`) with no z-index, so — like
14
+ // Slider's rail thumb — it and its transparent `::before` hit-expander paint
15
+ // above the statically-flowed panes. The `::before` swells the grab target
16
+ // to ≥24px (WCAG 2.5.8 AA) without widening the visible gutter.
17
+ handle: [
18
+ 'relative shrink-0 touch-none select-none',
19
+ 'bg-border-default',
20
+ 'transition-[background-color] duration-[var(--blocks-duration-fast)]',
21
+ 'hover:bg-primary',
22
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-1 focus-visible:ring-offset-surface-base',
23
+ "before:content-[''] before:absolute"
24
+ ]
25
+ },
26
+ variants: {
27
+ orientation: {
28
+ horizontal: {
29
+ root: 'flex-row',
30
+ // Vertical rule between side-by-side panes. Hit area: 4px + 2×10px = 24px.
31
+ handle: 'self-stretch w-1 cursor-col-resize before:inset-y-0 before:-inset-x-2.5'
32
+ },
33
+ vertical: {
34
+ root: 'flex-col',
35
+ // Horizontal rule between stacked panes.
36
+ handle: 'w-full h-1 cursor-row-resize before:inset-x-0 before:-inset-y-2.5'
37
+ }
38
+ },
39
+ // State axes last (house order): a state must dominate the resting look.
40
+ dragging: {
41
+ true: { handle: 'bg-primary' }
42
+ },
43
+ // Declared after `dragging` so its hover-neutralising override wins the
44
+ // `hover:bg` bucket even if a stale `dragging` were ever set together.
45
+ disabled: {
46
+ true: {
47
+ handle: 'cursor-default pointer-events-none hover:bg-border-default'
48
+ }
49
+ }
50
+ },
51
+ defaultVariants: {
52
+ orientation: 'horizontal',
53
+ dragging: false,
54
+ disabled: false
55
+ }
56
+ });
@@ -55,6 +55,8 @@ export type { SliderMark, SliderProps } from './Slider/index.js';
55
55
  export * from './Slider/index.js';
56
56
  export type { SpinnerProps } from './Spinner/index.js';
57
57
  export * from './Spinner/index.js';
58
+ export type { SplitPaneProps } from './SplitPane/index.js';
59
+ export * from './SplitPane/index.js';
58
60
  export type { StepperProps, StepperStepProps } from './Stepper/index.js';
59
61
  export * from './Stepper/index.js';
60
62
  export type { TabItemProps, TabPanelProps, TabProps } from './Tab/index.js';
@@ -29,6 +29,7 @@ export * from './Sidebar/index.js';
29
29
  export * from './Skeleton/index.js';
30
30
  export * from './Slider/index.js';
31
31
  export * from './Spinner/index.js';
32
+ export * from './SplitPane/index.js';
32
33
  export * from './Stepper/index.js';
33
34
  export * from './Tab/index.js';
34
35
  export * from './Textarea/index.js';
@@ -14,8 +14,14 @@ declare const _default: {
14
14
  readonly maximum: "Maximum";
15
15
  readonly minimum: "Minimum";
16
16
  readonly pagination: "Seitennavigation";
17
+ readonly pinInputCell: "Zeichen {{index}} von {{total}}";
17
18
  readonly progress: "Fortschritt";
19
+ readonly qrCode: "QR-Code";
18
20
  readonly slider: "Schieberegler";
21
+ readonly timeHours: "Stunden";
22
+ readonly timeMinutes: "Minuten";
23
+ readonly timeSeconds: "Sekunden";
24
+ readonly timeMeridiem: "AM oder PM";
19
25
  readonly toggle: "Umschalter";
20
26
  readonly toggleOptions: "Optionen umschalten";
21
27
  readonly removableBadge: "Entfernbarer Badge";
@@ -14,8 +14,14 @@ export default {
14
14
  maximum: 'Maximum',
15
15
  minimum: 'Minimum',
16
16
  pagination: 'Seitennavigation',
17
+ pinInputCell: 'Zeichen {{index}} von {{total}}',
17
18
  progress: 'Fortschritt',
19
+ qrCode: 'QR-Code',
18
20
  slider: 'Schieberegler',
21
+ timeHours: 'Stunden',
22
+ timeMinutes: 'Minuten',
23
+ timeSeconds: 'Sekunden',
24
+ timeMeridiem: 'AM oder PM',
19
25
  toggle: 'Umschalter',
20
26
  toggleOptions: 'Optionen umschalten',
21
27
  removableBadge: 'Entfernbarer Badge',
@@ -14,8 +14,14 @@ declare const _default: {
14
14
  readonly maximum: "Maximum";
15
15
  readonly minimum: "Minimum";
16
16
  readonly pagination: "Page navigation";
17
+ readonly pinInputCell: "Character {{index}} of {{total}}";
17
18
  readonly progress: "Progress";
19
+ readonly qrCode: "QR code";
18
20
  readonly slider: "Slider";
21
+ readonly timeHours: "Hours";
22
+ readonly timeMinutes: "Minutes";
23
+ readonly timeSeconds: "Seconds";
24
+ readonly timeMeridiem: "AM or PM";
19
25
  readonly toggle: "Toggle";
20
26
  readonly toggleOptions: "Toggle options";
21
27
  readonly removableBadge: "Removable badge";
@@ -14,8 +14,14 @@ export default {
14
14
  maximum: 'Maximum',
15
15
  minimum: 'Minimum',
16
16
  pagination: 'Page navigation',
17
+ pinInputCell: 'Character {{index}} of {{total}}',
17
18
  progress: 'Progress',
19
+ qrCode: 'QR code',
18
20
  slider: 'Slider',
21
+ timeHours: 'Hours',
22
+ timeMinutes: 'Minutes',
23
+ timeSeconds: 'Seconds',
24
+ timeMeridiem: 'AM or PM',
19
25
  toggle: 'Toggle',
20
26
  toggleOptions: 'Toggle options',
21
27
  removableBadge: 'Removable badge',
@@ -0,0 +1,78 @@
1
+ export type FileIntakeErrorCode = 'FILE_INVALID_TYPE' | 'FILE_TOO_LARGE' | 'FILE_TOO_SMALL' | 'TOO_MANY_FILES' | 'FILE_EXISTS' | 'CUSTOM';
2
+ export interface FileIntakeError {
3
+ /** Machine-readable error code. */
4
+ code: FileIntakeErrorCode;
5
+ /** Human-readable error message. */
6
+ message: string;
7
+ }
8
+ export type FileIntakeStatus = 'pending' | 'uploading' | 'complete' | 'error';
9
+ export interface FileIntakeEntry {
10
+ /** Unique identifier for this file entry. */
11
+ id: string;
12
+ /** The native File object. */
13
+ file: File;
14
+ /** Object URL for image previews (auto-generated, auto-revoked). */
15
+ preview?: string;
16
+ /** Upload progress 0–100. Undefined when not tracking. */
17
+ progress?: number;
18
+ /** Current lifecycle status. */
19
+ status: FileIntakeStatus;
20
+ /** Validation or upload errors. */
21
+ errors: FileIntakeError[];
22
+ }
23
+ export interface FileIntakeRejection {
24
+ /** The rejected file. */
25
+ file: File;
26
+ /** Why it was rejected. */
27
+ errors: FileIntakeError[];
28
+ }
29
+ /** Validation limits shared by every intake surface. */
30
+ export interface FileIntakeConstraints {
31
+ /** Accepted MIME types or file extensions (e.g. 'image/*', '.pdf'). */
32
+ accept?: string | string[];
33
+ /** Maximum number of files across the whole list. */
34
+ maxFiles?: number;
35
+ /** Maximum file size in bytes. */
36
+ maxFileSize?: number;
37
+ /** Minimum file size in bytes. */
38
+ minFileSize?: number;
39
+ /** Custom validation function. Return errors array or null. */
40
+ validate?: (file: File) => FileIntakeError[] | null;
41
+ }
42
+ /** Error-text factories — components inject their i18n/label source here. */
43
+ export interface FileIntakeMessages {
44
+ invalidType: (type: string) => string;
45
+ tooLarge: (formattedSize: string) => string;
46
+ tooSmall: (formattedSize: string) => string;
47
+ exists: () => string;
48
+ tooMany: (count: number) => string;
49
+ }
50
+ /** Does a picked/dropped file match the accept spec (`.ext`, `type/*`, exact MIME)? */
51
+ export declare function matchesAccept(file: File, accept: string | string[] | undefined): boolean;
52
+ /**
53
+ * Dragged-over feedback: do ALL items look acceptable? Extension patterns
54
+ * (`.pdf`) match permissively — file names are unavailable during dragenter,
55
+ * so a definitive verdict only exists for MIME patterns.
56
+ */
57
+ export declare function dragItemsMatchAccept(items: Iterable<DataTransferItem>, accept: string | string[] | undefined): boolean;
58
+ /** Validate one file against the constraints and the existing list (duplicates). */
59
+ export declare function validateIntakeFile(file: File, existing: readonly FileIntakeEntry[], constraints: FileIntakeConstraints, messages: FileIntakeMessages): FileIntakeError[];
60
+ /**
61
+ * Split incoming files into accepted entries (id + pending status + image
62
+ * preview object-URL) and rejections. Files beyond the remaining `maxFiles`
63
+ * budget are rejected with `TOO_MANY_FILES` before any validation runs.
64
+ * The caller owns the returned entries — including revoking their previews
65
+ * (`revokeIntakePreviews`) when they leave the list.
66
+ */
67
+ export declare function partitionIntake(incoming: readonly File[], existing: readonly FileIntakeEntry[], constraints: FileIntakeConstraints, messages: FileIntakeMessages, idPrefix?: string): {
68
+ accepted: FileIntakeEntry[];
69
+ rejected: FileIntakeRejection[];
70
+ };
71
+ /** Wrap a (pre-validated) file as a pending entry with id and image preview. */
72
+ export declare function createIntakeEntry(file: File, idPrefix?: string): FileIntakeEntry;
73
+ /** Revoke the preview object-URLs of the given entries (removal/teardown). */
74
+ export declare function revokeIntakePreviews(entries: Iterable<FileIntakeEntry>): void;
75
+ /** Human-readable file size (`1.5 MB`). Empty string for non-finite values. */
76
+ export declare function formatFileSize(bytes: number): string;
77
+ /** Is this file an image (drives preview object-URL creation)? */
78
+ export declare function isImageFile(file: File): boolean;