@joewinke/jatui 0.22.0 → 0.24.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 (71) hide show
  1. package/CLAUDE.md +72 -2
  2. package/dist/actions/reveal.d.ts +37 -0
  3. package/dist/actions/reveal.js +42 -0
  4. package/dist/components/AudioRecorder.svelte +529 -0
  5. package/dist/components/AudioRecorder.svelte.d.ts +30 -0
  6. package/dist/components/BulkActionBar.svelte +40 -0
  7. package/dist/components/BulkActionBar.svelte.d.ts +9 -0
  8. package/dist/components/ConfirmDialog.svelte +39 -35
  9. package/dist/components/CopyButton.svelte +5 -2
  10. package/dist/components/FloatingActionBar.svelte +55 -1
  11. package/dist/components/FloatingActionBar.svelte.d.ts +8 -1
  12. package/dist/components/IconSelector.svelte +417 -0
  13. package/dist/components/IconSelector.svelte.d.ts +55 -0
  14. package/dist/components/InfiniteGrid.svelte +305 -0
  15. package/dist/components/InfiniteGrid.svelte.d.ts +71 -0
  16. package/dist/components/InfiniteSearchGrid.svelte +131 -0
  17. package/dist/components/InfiniteSearchGrid.svelte.d.ts +67 -0
  18. package/dist/components/InputDialog.svelte +49 -38
  19. package/dist/components/KeyboardShortcutsOverlay.svelte +94 -3
  20. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +61 -16
  21. package/dist/components/MediaCaptureIcon.svelte +23 -0
  22. package/dist/components/MediaCaptureIcon.svelte.d.ts +1 -1
  23. package/dist/components/ThumbnailGrid.svelte +137 -0
  24. package/dist/components/ThumbnailGrid.svelte.d.ts +42 -0
  25. package/dist/components/batch/BatchJobTable.svelte +549 -0
  26. package/dist/components/batch/BatchJobTable.svelte.d.ts +114 -0
  27. package/dist/components/batch/QuotaMeter.svelte +78 -0
  28. package/dist/components/batch/QuotaMeter.svelte.d.ts +16 -0
  29. package/dist/components/batch/batchQueue.d.ts +56 -0
  30. package/dist/components/batch/batchQueue.js +81 -0
  31. package/dist/components/batch/elapsedEstimate.d.ts +24 -0
  32. package/dist/components/batch/elapsedEstimate.js +25 -0
  33. package/dist/components/batch/pollOperation.d.ts +45 -0
  34. package/dist/components/batch/pollOperation.js +80 -0
  35. package/dist/components/batch/useElapsedEstimate.d.ts +10 -0
  36. package/dist/components/batch/useElapsedEstimate.js +38 -0
  37. package/dist/components/bulkActionBarKeys.d.ts +40 -0
  38. package/dist/components/bulkActionBarKeys.js +38 -0
  39. package/dist/components/infiniteGridLoader.d.ts +70 -0
  40. package/dist/components/infiniteGridLoader.js +141 -0
  41. package/dist/components/linked-columns/LinkedColumns.svelte +10 -22
  42. package/dist/components/linked-columns/LinkedColumns.svelte.d.ts +1 -9
  43. package/dist/components/linked-columns/types.d.ts +9 -0
  44. package/dist/components/linked-columns/types.js +1 -0
  45. package/dist/components/replay/ChapterTimeline.svelte +43 -25
  46. package/dist/components/replay/ChapterTimeline.svelte.d.ts +26 -1
  47. package/dist/components/table/columnResize.d.ts +38 -0
  48. package/dist/components/table/columnResize.js +117 -0
  49. package/dist/components/table/index.d.ts +2 -0
  50. package/dist/components/table/index.js +1 -0
  51. package/dist/components/toast/ToastItem.svelte +47 -2
  52. package/dist/components/variant/VariantMatrixPicker.svelte +181 -0
  53. package/dist/components/variant/VariantMatrixPicker.svelte.d.ts +37 -0
  54. package/dist/components/variant/variantCombinations.d.ts +75 -0
  55. package/dist/components/variant/variantCombinations.js +100 -0
  56. package/dist/index.d.ts +34 -3
  57. package/dist/index.js +30 -1
  58. package/dist/stores/toast.d.ts +8 -0
  59. package/dist/stores/toast.js +2 -0
  60. package/dist/styles/motion.css +100 -0
  61. package/dist/utils/audioRecorder.d.ts +28 -0
  62. package/dist/utils/audioRecorder.js +58 -0
  63. package/dist/utils/audioRecorderCore.d.ts +132 -0
  64. package/dist/utils/audioRecorderCore.js +447 -0
  65. package/dist/utils/clipboard.d.ts +25 -0
  66. package/dist/utils/clipboard.js +59 -0
  67. package/dist/utils/dateFormatters.d.ts +11 -2
  68. package/dist/utils/dateFormatters.js +22 -2
  69. package/dist/utils/keyboardShortcutRegistry.d.ts +152 -0
  70. package/dist/utils/keyboardShortcutRegistry.js +365 -0
  71. package/package.json +1 -1
@@ -151,7 +151,11 @@
151
151
  }
152
152
  }
153
153
 
154
- // DaisyUI alert class per type
154
+ // Collapsible details (toast.details)
155
+ let detailsOpen = $state(false);
156
+ const detailsId = $derived(`toast-details-${toast.id}`);
157
+
158
+ // DaisyUI alert class per type
155
159
  const alertClass: Record<string, string> = {
156
160
  info: 'alert-info',
157
161
  success: 'alert-success',
@@ -223,7 +227,40 @@
223
227
  ></span>
224
228
  {/if}
225
229
 
226
- <span class="flex-1 text-sm">{toast.message}</span>
230
+ <div class="flex-1 min-w-0">
231
+ <span class="text-sm">{toast.message}</span>
232
+
233
+ {#if toast.details}
234
+ <button
235
+ type="button"
236
+ class="toast-details-toggle btn btn-ghost btn-xs mt-0.5 h-auto min-h-0 gap-1 px-0 font-semibold opacity-70"
237
+ aria-expanded={detailsOpen}
238
+ aria-controls={detailsId}
239
+ onclick={() => (detailsOpen = !detailsOpen)}
240
+ >
241
+ <svg
242
+ xmlns="http://www.w3.org/2000/svg"
243
+ class="size-3"
244
+ class:rotate-90={detailsOpen}
245
+ viewBox="0 0 24 24"
246
+ fill="none"
247
+ stroke="currentColor"
248
+ stroke-width="2.5"
249
+ stroke-linecap="round"
250
+ stroke-linejoin="round"
251
+ >
252
+ <path d="m9 18 6-6-6-6" />
253
+ </svg>
254
+ Details
255
+ </button>
256
+ {#if detailsOpen}
257
+ <pre
258
+ id={detailsId}
259
+ class="toast-details mt-1 max-h-40 overflow-auto rounded border border-current/20 bg-base-content/5 p-2 font-mono text-xs whitespace-pre-wrap break-words"
260
+ >{toast.details}</pre>
261
+ {/if}
262
+ {/if}
263
+ </div>
227
264
 
228
265
  {#if toast.onUndo}
229
266
  <button
@@ -274,7 +311,15 @@
274
311
  to { transform: scaleX(0); }
275
312
  }
276
313
 
314
+ .toast-details-toggle svg {
315
+ transition: transform var(--motion-fast) var(--motion-glide);
316
+ }
317
+
277
318
  @media (prefers-reduced-motion: reduce) {
319
+ .toast-details-toggle svg {
320
+ transition: none;
321
+ }
322
+
278
323
  .toast-progress {
279
324
  animation: none;
280
325
  }
@@ -0,0 +1,181 @@
1
+ <script lang="ts">
2
+ /**
3
+ * VariantMatrixPicker Component
4
+ *
5
+ * Category/option chip grid for building a combinatorial variant matrix
6
+ * (jatui-m4uz3.8, ported from vid-gen's FrameVariantGenerator selection UI,
7
+ * lines 72–197 / 500–620, with the hardcoded presets stripped).
8
+ *
9
+ * Every category renders as a tab with its options as toggleable chips.
10
+ * Each category carries an `additive`/`multiplicative` badge:
11
+ * - multiplicative: each selection multiplies the total combination count
12
+ * (cartesian product across multiplicative categories);
13
+ * - additive: selections stack flat onto every combination.
14
+ * A live combination count renders in the footer via `countCombinations`.
15
+ *
16
+ * The algebra lives in `./variantCombinations.ts` (pure, unit-tested);
17
+ * the host passes `categories` and reads/binds `selected`.
18
+ */
19
+ import {
20
+ countCombinations,
21
+ variantCategoryMode,
22
+ type VariantCategory,
23
+ type VariantSelection,
24
+ type VariantSelectionMode
25
+ } from './variantCombinations';
26
+
27
+ interface Props {
28
+ /** Category/option matrix — caller-supplied, no presets inside the component. */
29
+ categories: VariantCategory[];
30
+ /** categoryId → selected option ids. Bind to drive the matrix externally. */
31
+ selected: VariantSelection;
32
+ /** Force every category to one mode (`null` respects each category's own). */
33
+ forceMode?: VariantSelectionMode | null;
34
+ /** Show the live combination count footer (default true). */
35
+ showCount?: boolean;
36
+ /** Optional label for the count footer (default "combinations"). */
37
+ countLabel?: string;
38
+ /** Fired after every selection change with the new selection. */
39
+ onChange?: (selected: VariantSelection) => void;
40
+ /** Additional CSS class for the container. */
41
+ class?: string;
42
+ }
43
+
44
+ let {
45
+ categories,
46
+ selected = $bindable({}),
47
+ forceMode = null,
48
+ showCount = true,
49
+ countLabel = 'combinations',
50
+ onChange,
51
+ class: className = ''
52
+ }: Props = $props();
53
+
54
+ let activeTab = $state('');
55
+
56
+ // Follow the category list if it changes identity.
57
+ $effect(() => {
58
+ if (!categories.some((c) => c.id === activeTab)) {
59
+ activeTab = categories[0]?.id ?? '';
60
+ }
61
+ });
62
+
63
+ const combinationCount = $derived(countCombinations(categories, selected, forceMode));
64
+ const selectedCount = (categoryId: string) => selected[categoryId]?.length ?? 0;
65
+ const activeCategory = $derived(categories.find((c) => c.id === activeTab));
66
+
67
+ function toggleOption(categoryId: string, optionId: string) {
68
+ const current = selected[categoryId] ?? [];
69
+ const next = current.includes(optionId)
70
+ ? current.filter((id) => id !== optionId)
71
+ : [...current, optionId];
72
+ selected = { ...selected, [categoryId]: next };
73
+ onChange?.(selected);
74
+ }
75
+
76
+ function selectAll(categoryId: string) {
77
+ const category = categories.find((c) => c.id === categoryId);
78
+ if (!category) return;
79
+ selected = { ...selected, [categoryId]: category.options.map((o) => o.id) };
80
+ onChange?.(selected);
81
+ }
82
+
83
+ function clearCategory(categoryId: string) {
84
+ selected = { ...selected, [categoryId]: [] };
85
+ onChange?.(selected);
86
+ }
87
+
88
+ function clearAll() {
89
+ const next: VariantSelection = {};
90
+ for (const c of categories) next[c.id] = [];
91
+ selected = next;
92
+ onChange?.(next);
93
+ }
94
+ </script>
95
+
96
+ <div class="{className}">
97
+ <!-- Category tabs: icon, label, live selection count -->
98
+ <div role="tablist" aria-label="Variant categories" class="flex flex-wrap gap-1 mb-3">
99
+ {#each categories as category (category.id)}
100
+ {@const count = selectedCount(category.id)}
101
+ <button
102
+ role="tab"
103
+ aria-selected={activeTab === category.id}
104
+ class="btn btn-sm gap-2 {activeTab === category.id ? 'btn-primary' : 'btn-ghost'}"
105
+ onclick={() => (activeTab = category.id)}
106
+ >
107
+ {#if category.icon}<span class="text-base">{category.icon}</span>{/if}
108
+ <span>{category.label}</span>
109
+ {#if count > 0}<span class="badge badge-sm">{count}</span>{/if}
110
+ </button>
111
+ {/each}
112
+ </div>
113
+
114
+ {#if activeCategory}
115
+ {@const count = selectedCount(activeCategory.id)}
116
+ {@const isAdditive = variantCategoryMode(activeCategory, forceMode) === 'additive'}
117
+ <div class="card bg-base-200 shadow-sm">
118
+ <div class="card-body p-4">
119
+ <!-- Category header: mode badge + bulk actions -->
120
+ <div class="flex items-center justify-between gap-2 mb-3">
121
+ <div class="flex items-center gap-2">
122
+ {#if activeCategory.icon}<span class="text-xl">{activeCategory.icon}</span>{/if}
123
+ <h3 class="font-bold">{activeCategory.label}</h3>
124
+ <span
125
+ class="badge badge-sm {isAdditive ? 'badge-success' : 'badge-info'}"
126
+ title={isAdditive
127
+ ? 'Additive — selections stack onto every combination'
128
+ : 'Multiplicative — each selection multiplies the combination count'}
129
+ >
130
+ {isAdditive ? '➕ Additive' : '✖️ Multiplicative'}
131
+ </span>
132
+ {#if count > 0}<span class="badge badge-sm badge-primary">{count}</span>{/if}
133
+ </div>
134
+ <div class="flex items-center gap-1">
135
+ <button
136
+ class="btn btn-xs"
137
+ onclick={() => selectAll(activeCategory.id)}
138
+ disabled={activeCategory.options.length === 0}
139
+ >
140
+ Select all
141
+ </button>
142
+ {#if count > 0}
143
+ <button class="btn btn-xs btn-ghost" onclick={() => clearCategory(activeCategory.id)}>
144
+ Clear
145
+ </button>
146
+ {/if}
147
+ </div>
148
+ </div>
149
+
150
+ <!-- Option chip grid -->
151
+ <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
152
+ {#each activeCategory.options as option (option.id)}
153
+ {@const isSelected = selected[activeCategory.id]?.includes(option.id)}
154
+ <button
155
+ class="btn btn-sm normal-case justify-start gap-1 {isSelected
156
+ ? 'btn-info'
157
+ : 'btn-outline'}"
158
+ aria-pressed={isSelected}
159
+ onclick={() => toggleOption(activeCategory.id, option.id)}
160
+ >
161
+ {isSelected ? '✓' : '➕'}
162
+ <span class="truncate">{option.label}</span>
163
+ </button>
164
+ {/each}
165
+ </div>
166
+ </div>
167
+ </div>
168
+ {/if}
169
+
170
+ <!-- Footer: live combination count -->
171
+ {#if showCount}
172
+ <div class="flex items-center justify-between mt-3 text-sm">
173
+ <span class="text-base-content/60">
174
+ <strong class="text-base-content">{combinationCount}</strong>
175
+ {countLabel}
176
+ {#if combinationCount === 0}<span class="text-base-content/40"> — select an option to start</span>{/if}
177
+ </span>
178
+ <button class="btn btn-xs btn-ghost" onclick={clearAll}>Clear all</button>
179
+ </div>
180
+ {/if}
181
+ </div>
@@ -0,0 +1,37 @@
1
+ /**
2
+ * VariantMatrixPicker Component
3
+ *
4
+ * Category/option chip grid for building a combinatorial variant matrix
5
+ * (jatui-m4uz3.8, ported from vid-gen's FrameVariantGenerator selection UI,
6
+ * lines 72–197 / 500–620, with the hardcoded presets stripped).
7
+ *
8
+ * Every category renders as a tab with its options as toggleable chips.
9
+ * Each category carries an `additive`/`multiplicative` badge:
10
+ * - multiplicative: each selection multiplies the total combination count
11
+ * (cartesian product across multiplicative categories);
12
+ * - additive: selections stack flat onto every combination.
13
+ * A live combination count renders in the footer via `countCombinations`.
14
+ *
15
+ * The algebra lives in `./variantCombinations.ts` (pure, unit-tested);
16
+ * the host passes `categories` and reads/binds `selected`.
17
+ */
18
+ import { type VariantCategory, type VariantSelection, type VariantSelectionMode } from './variantCombinations';
19
+ interface Props {
20
+ /** Category/option matrix — caller-supplied, no presets inside the component. */
21
+ categories: VariantCategory[];
22
+ /** categoryId → selected option ids. Bind to drive the matrix externally. */
23
+ selected: VariantSelection;
24
+ /** Force every category to one mode (`null` respects each category's own). */
25
+ forceMode?: VariantSelectionMode | null;
26
+ /** Show the live combination count footer (default true). */
27
+ showCount?: boolean;
28
+ /** Optional label for the count footer (default "combinations"). */
29
+ countLabel?: string;
30
+ /** Fired after every selection change with the new selection. */
31
+ onChange?: (selected: VariantSelection) => void;
32
+ /** Additional CSS class for the container. */
33
+ class?: string;
34
+ }
35
+ declare const VariantMatrixPicker: import("svelte").Component<Props, {}, "selected">;
36
+ type VariantMatrixPicker = ReturnType<typeof VariantMatrixPicker>;
37
+ export default VariantMatrixPicker;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Pure combination algebra behind `VariantMatrixPicker` (jatui-m4uz3.8).
3
+ *
4
+ * Ported from vid-gen's `variantPresets.js` (generateCombinations + friends),
5
+ * with the hardcoded apparel/background/lighting preset map stripped out:
6
+ * every category and option is caller-supplied, so the algebra works for any
7
+ * combinatorial domain (image variants, A/B matrices, test permutations…).
8
+ *
9
+ * Two category modes drive the math:
10
+ * - `multiplicative`: each selected option pairs with every selection in the
11
+ * other multiplicative categories → cartesian product, one new variant
12
+ * per pick.
13
+ * - `additive`: all selected options stack flat into every combination —
14
+ * they ride along instead of multiplying.
15
+ *
16
+ * No DOM, no runes — kept separate so the math is unit-testable under plain
17
+ * node:test (mirrors the `batch/` module split from jatui-m4uz3.7).
18
+ */
19
+ export type VariantSelectionMode = 'multiplicative' | 'additive';
20
+ export interface VariantOption {
21
+ id: string;
22
+ label: string;
23
+ /** Optional host payload — vid-gen used image-edit prompts; joined with ', ' into `VariantCombination.prompt`. */
24
+ prompt?: string;
25
+ }
26
+ export interface VariantCategory {
27
+ id: string;
28
+ label: string;
29
+ /** Emoji or short glyph shown on the category tab. */
30
+ icon?: string;
31
+ /** Defaults to `multiplicative`. */
32
+ mode?: VariantSelectionMode;
33
+ options: VariantOption[];
34
+ }
35
+ /** categoryId → selected option ids (mirror of vid-gen's selectedModifiers). */
36
+ export type VariantSelection = Record<string, string[]>;
37
+ export interface VariantCombination {
38
+ /** Flat list of every option id in the combination (multiplicative picks + stacked additive picks). */
39
+ modifierIds: string[];
40
+ /** All `prompt`s joined with ', ' — empty string when no option carries a prompt. */
41
+ prompt: string;
42
+ /** Resolved option objects, in category order. */
43
+ options: VariantOption[];
44
+ }
45
+ export interface GenerateCombinationsOptions {
46
+ /** Override every category's mode. `null` (default) respects each category's own mode. */
47
+ forceMode?: VariantSelectionMode | null;
48
+ }
49
+ /** Resolve a category's effective mode: forceMode wins, then the category's own, then multiplicative. */
50
+ export declare function variantCategoryMode(category: VariantCategory, forceMode?: VariantSelectionMode | null): VariantSelectionMode;
51
+ /** Look up an option by category + id across a caller-supplied category list. */
52
+ export declare function findVariantOption(categories: VariantCategory[], categoryId: string, optionId: string): VariantOption | undefined;
53
+ /**
54
+ * Number of combinations `generateCombinations` would produce — the live count
55
+ * the picker footer renders. Multiplicative categories multiply; a selection
56
+ * with only additive categories counts as exactly 1 stacked combination.
57
+ */
58
+ export declare function countCombinations(categories: VariantCategory[], selected: VariantSelection, forceMode?: VariantSelectionMode | null): number;
59
+ /**
60
+ * Generate all combinations from the current selection.
61
+ *
62
+ * Mirrors vid-gen's `generateCombinations(selectedModifiers, options)`:
63
+ * - categories with nothing selected are skipped entirely (all-empty → []);
64
+ * - multiplicative categories contribute one option per combination via
65
+ * cartesian product; additive selections stack flat onto every combination;
66
+ * - with no multiplicative selections at all, a single additive-only
67
+ * combination is produced.
68
+ */
69
+ export declare function generateCombinations(categories: VariantCategory[], selected: VariantSelection, options?: GenerateCombinationsOptions): VariantCombination[];
70
+ /**
71
+ * Build a variant filename from an original filename and modifier ids
72
+ * (vid-gen's `generateVariantFilename`, generic — no preset knowledge):
73
+ * `photo.png` + ['a', 'b'] → `photo_a_b.png`, ids sorted for consistency.
74
+ */
75
+ export declare function generateVariantFilename(originalFilename: string, modifierIds: string[]): string;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Pure combination algebra behind `VariantMatrixPicker` (jatui-m4uz3.8).
3
+ *
4
+ * Ported from vid-gen's `variantPresets.js` (generateCombinations + friends),
5
+ * with the hardcoded apparel/background/lighting preset map stripped out:
6
+ * every category and option is caller-supplied, so the algebra works for any
7
+ * combinatorial domain (image variants, A/B matrices, test permutations…).
8
+ *
9
+ * Two category modes drive the math:
10
+ * - `multiplicative`: each selected option pairs with every selection in the
11
+ * other multiplicative categories → cartesian product, one new variant
12
+ * per pick.
13
+ * - `additive`: all selected options stack flat into every combination —
14
+ * they ride along instead of multiplying.
15
+ *
16
+ * No DOM, no runes — kept separate so the math is unit-testable under plain
17
+ * node:test (mirrors the `batch/` module split from jatui-m4uz3.7).
18
+ */
19
+ /** Resolve a category's effective mode: forceMode wins, then the category's own, then multiplicative. */
20
+ export function variantCategoryMode(category, forceMode = null) {
21
+ return forceMode ?? category.mode ?? 'multiplicative';
22
+ }
23
+ /** Look up an option by category + id across a caller-supplied category list. */
24
+ export function findVariantOption(categories, categoryId, optionId) {
25
+ return categories.find((c) => c.id === categoryId)?.options.find((o) => o.id === optionId);
26
+ }
27
+ /**
28
+ * Number of combinations `generateCombinations` would produce — the live count
29
+ * the picker footer renders. Multiplicative categories multiply; a selection
30
+ * with only additive categories counts as exactly 1 stacked combination.
31
+ */
32
+ export function countCombinations(categories, selected, forceMode = null) {
33
+ const active = categories.filter((c) => (selected[c.id]?.length ?? 0) > 0);
34
+ if (active.length === 0)
35
+ return 0;
36
+ let count = 1;
37
+ let multiplicativeSeen = false;
38
+ for (const category of active) {
39
+ const n = selected[category.id].length;
40
+ if (variantCategoryMode(category, forceMode) === 'multiplicative') {
41
+ multiplicativeSeen = true;
42
+ count *= n;
43
+ }
44
+ }
45
+ // Only-additive selections produce a single stacked combination, not zero.
46
+ return multiplicativeSeen ? count : 1;
47
+ }
48
+ /** Cartesian product of arrays (vid-gen's helper, verbatim semantics). */
49
+ function cartesian(...arrays) {
50
+ return arrays.reduce((acc, array) => acc.flatMap((x) => array.map((y) => [...x, y])), [[]]);
51
+ }
52
+ /**
53
+ * Generate all combinations from the current selection.
54
+ *
55
+ * Mirrors vid-gen's `generateCombinations(selectedModifiers, options)`:
56
+ * - categories with nothing selected are skipped entirely (all-empty → []);
57
+ * - multiplicative categories contribute one option per combination via
58
+ * cartesian product; additive selections stack flat onto every combination;
59
+ * - with no multiplicative selections at all, a single additive-only
60
+ * combination is produced.
61
+ */
62
+ export function generateCombinations(categories, selected, options = {}) {
63
+ const { forceMode = null } = options;
64
+ const active = categories.filter((c) => (selected[c.id]?.length ?? 0) > 0);
65
+ if (active.length === 0)
66
+ return [];
67
+ const multiplicativeArrays = active
68
+ .filter((c) => variantCategoryMode(c, forceMode) === 'multiplicative')
69
+ .map((c) => selected[c.id].map((optionId) => ({ categoryId: c.id, optionId })));
70
+ const additivePicks = active
71
+ .filter((c) => variantCategoryMode(c, forceMode) === 'additive')
72
+ .flatMap((c) => selected[c.id].map((optionId) => ({ categoryId: c.id, optionId })));
73
+ const multiplicativeCombos = cartesian(...multiplicativeArrays);
74
+ const combos = multiplicativeArrays.length
75
+ ? multiplicativeCombos.map((combo) => [...combo, ...additivePicks])
76
+ : [additivePicks];
77
+ return combos.map((combo) => {
78
+ const resolved = combo
79
+ .map(({ categoryId, optionId }) => findVariantOption(categories, categoryId, optionId))
80
+ .filter((o) => o !== undefined);
81
+ return {
82
+ modifierIds: combo.map(({ optionId }) => optionId),
83
+ prompt: resolved
84
+ .map((o) => o.prompt)
85
+ .filter((p) => p !== undefined)
86
+ .join(', '),
87
+ options: resolved
88
+ };
89
+ });
90
+ }
91
+ /**
92
+ * Build a variant filename from an original filename and modifier ids
93
+ * (vid-gen's `generateVariantFilename`, generic — no preset knowledge):
94
+ * `photo.png` + ['a', 'b'] → `photo_a_b.png`, ids sorted for consistency.
95
+ */
96
+ export function generateVariantFilename(originalFilename, modifierIds) {
97
+ const stem = originalFilename.replace(/\.[^/.]+$/, '');
98
+ const suffix = [...modifierIds].sort().join('_');
99
+ return suffix ? `${stem}_${suffix}.png` : `${stem}.png`;
100
+ }
package/dist/index.d.ts CHANGED
@@ -6,9 +6,9 @@ export { collapse } from './actions/collapse';
6
6
  export type { CollapseOptions } from './actions/collapse';
7
7
  export { default as Collapse } from './components/Collapse.svelte';
8
8
  export { default as LinkedColumns } from './components/linked-columns/LinkedColumns.svelte';
9
- export type { ColumnLink } from './components/linked-columns/LinkedColumns.svelte';
9
+ export type { ColumnLink } from './components/linked-columns/types.js';
10
10
  export { default as ChapterTimeline } from './components/replay/ChapterTimeline.svelte';
11
- export type { ChapterBlock } from './components/replay/ChapterTimeline.svelte';
11
+ export type { ChapterBlock, ChapterStateVisual } from './components/replay/ChapterTimeline.svelte';
12
12
  export { default as TranscriptPlayer } from './components/TranscriptPlayer.svelte';
13
13
  export type { TranscriptSegment, TranscriptBookmark, TranscriptChapter, TranscriptSpeakerTalkTime, } from './components/TranscriptPlayer.svelte';
14
14
  export { default as VideoPlayer } from './components/VideoPlayer.svelte';
@@ -56,6 +56,10 @@ export { createDirtyGuard } from './utils/dirtyGuard';
56
56
  export type { DirtyGuard, DirtyGuardOptions, BeforeNavigateFn } from './utils/dirtyGuard';
57
57
  export { default as MediaLightbox } from './components/MediaLightbox.svelte';
58
58
  export type { MediaItem } from './components/MediaLightbox.svelte';
59
+ export { default as InfiniteGrid } from './components/InfiniteGrid.svelte';
60
+ export { default as InfiniteSearchGrid } from './components/InfiniteSearchGrid.svelte';
61
+ export { createInfiniteLoader } from './components/infiniteGridLoader';
62
+ export type { InfinitePage, InfiniteLoadContext, InfiniteLoadFn, InfiniteLoader, InfiniteLoaderState, InfiniteStatus } from './components/infiniteGridLoader';
59
63
  export { default as Button } from './components/Button.svelte';
60
64
  export { default as Badge } from './components/Badge.svelte';
61
65
  export { default as Card } from './components/Card.svelte';
@@ -78,6 +82,10 @@ export { default as LazyImage } from './components/LazyImage.svelte';
78
82
  export { default as ResizablePanel } from './components/ResizablePanel.svelte';
79
83
  export { default as VoicePlayer } from './components/VoicePlayer.svelte';
80
84
  export { default as SpeechForm } from './components/SpeechForm.svelte';
85
+ export { default as AudioRecorder } from './components/AudioRecorder.svelte';
86
+ export type { AudioRecordingResult } from './utils/audioRecorderCore';
87
+ export { createAudioRecorder } from './utils/audioRecorder';
88
+ export type { AudioRecorder, AudioRecorderCore, AudioRecorderError, AudioRecorderErrorKind, AudioRecorderFrame, AudioRecorderMode, AudioRecorderOptions, AudioRecorderPhase, AudioRecorderSnapshot } from './utils/audioRecorderCore';
81
89
  export { default as ThemeSelector } from './components/ThemeSelector.svelte';
82
90
  export { default as Sparkline } from './components/Sparkline.svelte';
83
91
  export { default as BurndownChart } from './components/BurndownChart.svelte';
@@ -93,6 +101,8 @@ export type { FileDropzoneFile } from './components/FileDropzone.svelte';
93
101
  export { default as VariablePicker } from './components/VariablePicker.svelte';
94
102
  export type { TemplateVar, TemplateVarGroup } from './types/templateVars';
95
103
  export { default as EmojiPicker } from './components/EmojiPicker.svelte';
104
+ export { default as IconSelector } from './components/IconSelector.svelte';
105
+ export type { IconSelectorOption, IconComponentProps } from './components/IconSelector.svelte';
96
106
  export { EMOJI_DATA, ALL_EMOJIS, searchEmojis } from './data/emojis';
97
107
  export type { EmojiEntry, EmojiGroup } from './data/emojis';
98
108
  export { default as LinkShortener } from './components/LinkShortener.svelte';
@@ -120,7 +130,7 @@ export type { Milestone, MilestoneStatus } from './types/milestone';
120
130
  export type { LocationType, BookingStatus, OverrideType, Host, EventType, WeeklyAvailability, DateOverride, Booking, BookingAttendee, TimeSlot, BookingFormData, AvailabilityCounts } from './types/booking';
121
131
  export { DAYS_OF_WEEK, DAYS_SHORT, LOCATION_LABELS, LOCATION_ICONS } from './types/booking';
122
132
  export type { ImageVariantSpec, ImageProcessingSpec, VariantResult, VariantState, } from './types/smartImageEditor';
123
- export { normalizeTimestamp, parseTimestamp, formatRelativeTime, formatFullDate, formatShortDate, formatLastActivity, formatDate, getTimeSinceMs, getTimeSinceMinutes, isWithinMinutes, formatDateTime, taskAge, pacingColor, type PacingLevel, type PacingResult, } from './utils/dateFormatters';
133
+ export { normalizeTimestamp, parseTimestamp, formatRelativeTime, formatFullDate, formatShortDate, formatLastActivity, formatDate, getTimeSinceMs, getTimeSinceMinutes, isWithinMinutes, formatDateTime, taskAge, pacingColor, formatPacingDuration, type PacingLevel, type PacingResult, type PacingOptions, } from './utils/dateFormatters';
124
134
  export { statusColor, priorityColor, typeIcon, statusLabel, } from './utils/taskUtils';
125
135
  export { stripPhoneNumber, formatPhoneNumber, isCompletePhoneNumber, isValidPhoneNumber, updatePhoneInput } from './utils/phoneFormat';
126
136
  export { toE164, formatPhoneForDisplay, isValidPhone, listCountries, DEFAULT_COUNTRY, PRIORITY_COUNTRIES, type CountryOption, } from './utils/phone';
@@ -166,6 +176,8 @@ export type { MarkupTool, Point, MarkupShape, ArrowShape, RectangleShape, Ellips
166
176
  export type { Annotation, AnnotationProfile, AnnotationThread, AnnotationCallbacks, } from './types/annotation';
167
177
  export { default as KeyboardShortcutsOverlay } from './components/KeyboardShortcutsOverlay.svelte';
168
178
  export type { KeyboardShortcut, SectionEntry as KeyboardShortcutSectionEntry, ShortcutSection as KeyboardShortcutSection, } from './components/KeyboardShortcutsOverlay.svelte';
179
+ export { createShortcutRegistry, getShortcutRegistry, formatShortcut, parseCombo as parseShortcutCombo, isTypingTarget, detectPlatform as detectShortcutPlatform, SIDEBAR_TOGGLE_KEY, } from './utils/keyboardShortcutRegistry';
180
+ export type { ShortcutRegistry, ShortcutDefinition, ShortcutModifier, ShortcutPlatform, PushContextOptions as ShortcutContextOptions, RegistrySection as ShortcutRegistrySection, ToSectionsOptions as ShortcutSectionsOptions, ShortcutRegistryOptions, ShortcutEventTarget, } from './utils/keyboardShortcutRegistry';
169
181
  export { default as MapView } from './components/MapView.svelte';
170
182
  export { default as LocationMap } from './components/LocationMap.svelte';
171
183
  export { default as GPSTracker } from './components/GPSTracker.svelte';
@@ -177,6 +189,10 @@ export { staggerEntrance } from './actions/staggerEntrance';
177
189
  export type { StaggerEntranceOptions } from './actions/staggerEntrance';
178
190
  export { attention } from './actions/attention';
179
191
  export type { AttentionEffect, AttentionOptions } from './actions/attention';
192
+ export { reveal, REVEAL_HIDDEN_CLASS, REVEAL_BASE_CLASS } from './actions/reveal';
193
+ export type { RevealOptions, RevealVariant } from './actions/reveal';
194
+ export { default as ThumbnailGrid } from './components/ThumbnailGrid.svelte';
195
+ export type { ThumbnailItem } from './components/ThumbnailGrid.svelte';
180
196
  export { floating, makeVirtualAnchor } from './actions/floating';
181
197
  export type { FloatingOptions, Placement } from './actions/floating';
182
198
  export { glide } from './transitions/glide';
@@ -184,6 +200,7 @@ export type { GlideOrigin, GlideParams } from './transitions/glide';
184
200
  export { themeWipe } from './utils/themeWipe';
185
201
  export { viewTransition } from './utils/viewTransition';
186
202
  export { play as playSound, registerSound, isSoundEnabled, setSoundEnabled } from './utils/sound';
203
+ export { copyText, readClipboardText } from './utils/clipboard';
187
204
  export { splitText } from './utils/splitText';
188
205
  export type { SplitBy, SplitTextOptions, SplitTextResult } from './utils/splitText';
189
206
  export { default as ToastHost } from './components/toast/ToastHost.svelte';
@@ -227,3 +244,17 @@ export { default as Slider } from './components/basic/Slider.svelte';
227
244
  export { default as Pagination } from './components/basic/Pagination.svelte';
228
245
  export { MAX_UNDO_DEPTH, createHistory, pushState, beginChange, endChange, untracked, undo, redo, clearHistory } from './history';
229
246
  export type { History } from './history';
247
+ export { default as BatchJobTable } from './components/batch/BatchJobTable.svelte';
248
+ export type { JobContext, RowControls, BatchControls, BatchPhase } from './components/batch/BatchJobTable.svelte';
249
+ export { default as QuotaMeter } from './components/batch/QuotaMeter.svelte';
250
+ export { toJobRows, nextJobsToStart, isProcessLimitReached, moveJob, summarizeJobs, formatElapsed } from './components/batch/batchQueue';
251
+ export type { JobRow, JobStatus, BatchSummary, DispatchLimits } from './components/batch/batchQueue';
252
+ export { rollingAverage, estimateRemainingMs } from './components/batch/elapsedEstimate';
253
+ export type { EtaInput } from './components/batch/elapsedEstimate';
254
+ export { useElapsedEstimate } from './components/batch/useElapsedEstimate';
255
+ export type { ElapsedEstimate } from './components/batch/useElapsedEstimate';
256
+ export { pollOperation, PollTimeoutError } from './components/batch/pollOperation';
257
+ export type { PollStep, PollContext, PollOptions } from './components/batch/pollOperation';
258
+ export { default as VariantMatrixPicker } from './components/variant/VariantMatrixPicker.svelte';
259
+ export { countCombinations, generateCombinations, generateVariantFilename, findVariantOption, variantCategoryMode } from './components/variant/variantCombinations';
260
+ export type { VariantCategory, VariantOption, VariantSelection, VariantSelectionMode, VariantCombination } from './components/variant/variantCombinations';
package/dist/index.js CHANGED
@@ -73,6 +73,10 @@ export { default as PresenceStack } from './components/PresenceStack.svelte';
73
73
  export { default as DirtyFormHost } from './components/DirtyFormHost.svelte';
74
74
  export { createDirtyGuard } from './utils/dirtyGuard';
75
75
  export { default as MediaLightbox } from './components/MediaLightbox.svelte';
76
+ // Cursor-paged infinite-scroll grids, harvested from chimaro (jatui-m4uz3.4).
77
+ export { default as InfiniteGrid } from './components/InfiniteGrid.svelte';
78
+ export { default as InfiniteSearchGrid } from './components/InfiniteSearchGrid.svelte';
79
+ export { createInfiniteLoader } from './components/infiniteGridLoader';
76
80
  // Components — from Flush
77
81
  export { default as Button } from './components/Button.svelte';
78
82
  export { default as Badge } from './components/Badge.svelte';
@@ -97,6 +101,9 @@ export { default as LazyImage } from './components/LazyImage.svelte';
97
101
  export { default as ResizablePanel } from './components/ResizablePanel.svelte';
98
102
  export { default as VoicePlayer } from './components/VoicePlayer.svelte';
99
103
  export { default as SpeechForm } from './components/SpeechForm.svelte';
104
+ export { default as AudioRecorder } from './components/AudioRecorder.svelte';
105
+ // Headless audio capture — the factory behind AudioRecorder (jatui-is2ow.8)
106
+ export { createAudioRecorder } from './utils/audioRecorder';
100
107
  // Components — from Headcount
101
108
  export { default as ThemeSelector } from './components/ThemeSelector.svelte';
102
109
  export { default as Sparkline } from './components/Sparkline.svelte';
@@ -112,6 +119,8 @@ export { default as EntityPreviewCard } from './components/EntityPreviewCard.sve
112
119
  export { default as VariablePicker } from './components/VariablePicker.svelte';
113
120
  // Components — Emoji
114
121
  export { default as EmojiPicker } from './components/EmojiPicker.svelte';
122
+ // Components — Icon selection
123
+ export { default as IconSelector } from './components/IconSelector.svelte';
115
124
  // Emoji data
116
125
  export { EMOJI_DATA, ALL_EMOJIS, searchEmojis } from './data/emojis';
117
126
  // Components — from Meadow
@@ -130,7 +139,7 @@ export { default as AvailabilityModal } from './components/AvailabilityModal.sve
130
139
  export { default as BookingForm } from './components/BookingForm.svelte';
131
140
  export { DAYS_OF_WEEK, DAYS_SHORT, LOCATION_LABELS, LOCATION_ICONS } from './types/booking';
132
141
  // Utilities — date formatting
133
- export { normalizeTimestamp, parseTimestamp, formatRelativeTime, formatFullDate, formatShortDate, formatLastActivity, formatDate, getTimeSinceMs, getTimeSinceMinutes, isWithinMinutes, formatDateTime, taskAge, pacingColor, } from './utils/dateFormatters';
142
+ export { normalizeTimestamp, parseTimestamp, formatRelativeTime, formatFullDate, formatShortDate, formatLastActivity, formatDate, getTimeSinceMs, getTimeSinceMinutes, isWithinMinutes, formatDateTime, taskAge, pacingColor, formatPacingDuration, } from './utils/dateFormatters';
134
143
  // Utilities — task display helpers
135
144
  export { statusColor, priorityColor, typeIcon, statusLabel, } from './utils/taskUtils';
136
145
  // Utilities — phone formatting (legacy US-only)
@@ -183,6 +192,7 @@ export { default as AnnotationPanel } from './components/AnnotationPanel.svelte'
183
192
  export { MARKUP_COLORS, DEFAULT_STROKE_WIDTH, renderShape, renderAllShapes, nextShapeId, } from './components/markup';
184
193
  // Components — Keyboard Navigation
185
194
  export { default as KeyboardShortcutsOverlay } from './components/KeyboardShortcutsOverlay.svelte';
195
+ export { createShortcutRegistry, getShortcutRegistry, formatShortcut, parseCombo as parseShortcutCombo, isTypingTarget, detectPlatform as detectShortcutPlatform, SIDEBAR_TOGGLE_KEY, } from './utils/keyboardShortcutRegistry';
186
196
  // Components — Google Maps (DispatchMap fork + location primitives)
187
197
  export { default as MapView } from './components/MapView.svelte';
188
198
  export { default as LocationMap } from './components/LocationMap.svelte';
@@ -202,6 +212,13 @@ export { loadGoogleMapsAPI, isGoogleMapsLoaded, isPlacesLoaded, isGeometryLoaded
202
212
  export { tooltip } from './actions/tooltip';
203
213
  export { staggerEntrance } from './actions/staggerEntrance';
204
214
  export { attention } from './actions/attention';
215
+ // Scroll-reveal (jatui-m4uz3.3, chimaro port): IntersectionObserver
216
+ // scroll-reveal whose variants live in styles/motion.css as jatui-reveal-*
217
+ // classes on the --motion-* tokens — no shipped animista.css.
218
+ export { reveal, REVEAL_HIDDEN_CLASS, REVEAL_BASE_CLASS } from './actions/reveal';
219
+ // Thumbnail grid (jatui-m4uz3.3, chimaro port): generic {id,url,alt} strip
220
+ // with overflow counter, remove/add affordances and a cell snippet override.
221
+ export { default as ThumbnailGrid } from './components/ThumbnailGrid.svelte';
205
222
  export { floating, makeVirtualAnchor } from './actions/floating';
206
223
  export { glide } from './transitions/glide';
207
224
  // View Transitions kit
@@ -209,6 +226,8 @@ export { themeWipe } from './utils/themeWipe';
209
226
  export { viewTransition } from './utils/viewTransition';
210
227
  // Sound kit (functional feedback, opt-in)
211
228
  export { play as playSound, registerSound, isSoundEnabled, setSoundEnabled } from './utils/sound';
229
+ // Clipboard (secure-context aware: execCommand fallback for plain-HTTP origins)
230
+ export { copyText, readClipboardText } from './utils/clipboard';
212
231
  // Text utilities
213
232
  export { splitText } from './utils/splitText';
214
233
  // Toast system
@@ -262,3 +281,13 @@ export { default as Slider } from './components/basic/Slider.svelte';
262
281
  export { default as Pagination } from './components/basic/Pagination.svelte';
263
282
  // Snapshot undo/redo history (wayflow-core) — also importable as 'jatui/history' (jatui-00jfh).
264
283
  export { MAX_UNDO_DEPTH, createHistory, pushState, beginChange, endChange, untracked, undo, redo, clearHistory } from './history';
284
+ // Batch queue runner (harvested from vid-gen BatchGenerator — jatui-m4uz3.7).
285
+ export { default as BatchJobTable } from './components/batch/BatchJobTable.svelte';
286
+ export { default as QuotaMeter } from './components/batch/QuotaMeter.svelte';
287
+ export { toJobRows, nextJobsToStart, isProcessLimitReached, moveJob, summarizeJobs, formatElapsed } from './components/batch/batchQueue';
288
+ export { rollingAverage, estimateRemainingMs } from './components/batch/elapsedEstimate';
289
+ export { useElapsedEstimate } from './components/batch/useElapsedEstimate';
290
+ export { pollOperation, PollTimeoutError } from './components/batch/pollOperation';
291
+ // Variant matrix picker + combination algebra (harvested from vid-gen FrameVariantGenerator — jatui-m4uz3.8).
292
+ export { default as VariantMatrixPicker } from './components/variant/VariantMatrixPicker.svelte';
293
+ export { countCombinations, generateCombinations, generateVariantFilename, findVariantOption, variantCategoryMode } from './components/variant/variantCombinations';