@joewinke/jatui 0.26.0 → 0.27.1

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 (73) hide show
  1. package/CLAUDE.md +106 -1
  2. package/dist/actions/keyboardScope.d.ts +58 -0
  3. package/dist/actions/keyboardScope.js +110 -0
  4. package/dist/actions/listNav.d.ts +48 -3
  5. package/dist/actions/listNav.js +267 -77
  6. package/dist/actions/longpress.d.ts +37 -0
  7. package/dist/actions/longpress.js +105 -0
  8. package/dist/actions/stickToBottom.d.ts +66 -0
  9. package/dist/actions/stickToBottom.js +155 -0
  10. package/dist/actions/swipe.d.ts +64 -0
  11. package/dist/actions/swipe.js +179 -0
  12. package/dist/components/BottomSheet.svelte +280 -0
  13. package/dist/components/BottomSheet.svelte.d.ts +27 -0
  14. package/dist/components/ConfirmDialog.svelte +21 -0
  15. package/dist/components/ConfirmDialog.svelte.d.ts +11 -1
  16. package/dist/components/ConfirmModal.svelte +15 -0
  17. package/dist/components/ConfirmModal.svelte.d.ts +5 -0
  18. package/dist/components/Drawer.svelte +18 -1
  19. package/dist/components/Drawer.svelte.d.ts +9 -1
  20. package/dist/components/InputDialog.svelte +21 -0
  21. package/dist/components/InputDialog.svelte.d.ts +11 -1
  22. package/dist/components/Modal.svelte +18 -1
  23. package/dist/components/Modal.svelte.d.ts +8 -0
  24. package/dist/components/basic/Empty.svelte +173 -9
  25. package/dist/components/basic/Empty.svelte.d.ts +50 -1
  26. package/dist/components/basic/ErrorState.svelte +128 -0
  27. package/dist/components/basic/ErrorState.svelte.d.ts +46 -0
  28. package/dist/components/basic/JumpToLatest.svelte +60 -0
  29. package/dist/components/basic/JumpToLatest.svelte.d.ts +9 -0
  30. package/dist/components/basic/PageHeader.svelte +216 -0
  31. package/dist/components/basic/PageHeader.svelte.d.ts +52 -0
  32. package/dist/components/basic/ProgressButton.svelte +221 -0
  33. package/dist/components/basic/ProgressButton.svelte.d.ts +63 -0
  34. package/dist/components/graph/GraphApiBridge.svelte +36 -0
  35. package/dist/components/graph/GraphApiBridge.svelte.d.ts +9 -0
  36. package/dist/components/graph/GraphCanvas.svelte +212 -27
  37. package/dist/components/graph/GraphCanvas.svelte.d.ts +113 -19
  38. package/dist/components/graph/api.d.ts +52 -0
  39. package/dist/components/graph/api.js +66 -0
  40. package/dist/components/graph/index.d.ts +2 -0
  41. package/dist/components/graph/index.js +1 -0
  42. package/dist/components/graph/theme.js +13 -0
  43. package/dist/components/messaging/MessageItem.svelte +5 -0
  44. package/dist/components/messaging/MessageItem.svelte.d.ts +7 -0
  45. package/dist/components/table/DataTable.svelte +452 -78
  46. package/dist/components/table/DataTable.svelte.d.ts +2 -2
  47. package/dist/components/table/ManageColumnsDropdown.svelte +466 -0
  48. package/dist/components/table/ManageColumnsDropdown.svelte.d.ts +26 -0
  49. package/dist/components/table/columnStorage.d.ts +29 -0
  50. package/dist/components/table/columnStorage.js +61 -0
  51. package/dist/components/table/groupRows.d.ts +72 -0
  52. package/dist/components/table/groupRows.js +118 -0
  53. package/dist/components/table/index.d.ts +7 -1
  54. package/dist/components/table/index.js +3 -0
  55. package/dist/components/table/types.d.ts +121 -20
  56. package/dist/index.d.ts +24 -2
  57. package/dist/index.js +36 -1
  58. package/dist/utils/ansiToHtml.d.ts +83 -0
  59. package/dist/utils/ansiToHtml.js +543 -0
  60. package/dist/utils/keyboardShortcutRegistry.d.ts +332 -11
  61. package/dist/utils/keyboardShortcutRegistry.js +1070 -76
  62. package/dist/utils/pollBackoff.d.ts +83 -0
  63. package/dist/utils/pollBackoff.js +151 -0
  64. package/dist/utils/snapFlick.d.ts +59 -0
  65. package/dist/utils/snapFlick.js +66 -0
  66. package/dist/video/advisors/qr.d.ts +1 -1
  67. package/dist/virtual/createRowWindow.d.ts +75 -0
  68. package/dist/virtual/createRowWindow.js +129 -0
  69. package/dist/virtual/index.d.ts +4 -0
  70. package/dist/virtual/index.js +5 -0
  71. package/dist/virtual/rowWindowCore.d.ts +202 -0
  72. package/dist/virtual/rowWindowCore.js +510 -0
  73. package/package.json +12 -2
@@ -1,17 +1,181 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ /**
5
+ * Empty — the STANDARD "nothing here yet" surface, now at full strength (v2).
6
+ *
7
+ * v1 API (unchanged, still supported): `copy` + `children` render the
8
+ * original thin zero-state block.
9
+ *
10
+ * v2 API (upstreamed from the JAT IDE's ui/EmptyState.svelte, additive):
11
+ * pass `title` to opt into the full hero layout — named glyph or custom
12
+ * `icon` snippet, three sizes, an optional ambient pulse for
13
+ * "listening / waiting" states, CTA `actions` and extra `children` below.
14
+ *
15
+ * Design-system conformance:
16
+ * - Color routes through DaisyUI semantic tokens (`text-base-content`
17
+ * with opacity), so it survives a theme switch. No hardcoded state hues.
18
+ * - Motion (the optional ambient pulse on the icon) is reduced-motion gated
19
+ * and tinted with the theme's `--color-primary` token.
20
+ *
21
+ * Usage — named glyph + CTA:
22
+ * <Empty title="Inbox zero" description="No items need triage.">
23
+ * {#snippet actions()}
24
+ * <button class="btn btn-sm btn-primary" onclick={create}>New task</button>
25
+ * {/snippet}
26
+ * </Empty>
27
+ *
28
+ * Usage — custom icon snippet:
29
+ * <Empty title="No sessions">
30
+ * {#snippet icon()}<MyIcon class="w-12 h-12" />{/snippet}
31
+ * </Empty>
32
+ */
33
+
34
+ /** Named glyph from the curated Heroicons (outline) set. */
35
+ export type EmptyGlyph =
36
+ | 'inbox'
37
+ | 'search'
38
+ | 'tasks'
39
+ | 'folder'
40
+ | 'server'
41
+ | 'sparkles'
42
+ | 'document'
43
+ | 'database'
44
+ | 'bell'
45
+ | 'check'
46
+ | 'radar';
47
+
2
48
  interface Props {
49
+ /** v2 headline — what's missing, in plain words. Opts into the v2 hero layout. */
50
+ title?: string;
51
+ /** v2 optional one-line guidance: what the operator can do about it. */
52
+ description?: string;
53
+ /** v2 named glyph from the curated set. Ignored if an `icon` snippet is passed. */
54
+ glyph?: EmptyGlyph;
55
+ /** v2 visual scale. `sm` for inline panels, `lg` for full-page first-runs. */
56
+ size?: 'sm' | 'md' | 'lg';
57
+ /** v2 subtle ambient pulse on the icon (reduced-motion gated). For "listening / waiting" states. */
58
+ pulse?: boolean;
59
+ /** v2 override the glyph with arbitrary icon markup. */
60
+ icon?: Snippet;
61
+ /** v2 right-aligned CTA buttons rendered under the description. */
62
+ actions?: Snippet;
63
+ /** v1 legacy copy line (ignored when `title` is set). */
3
64
  copy?: string;
65
+ /** Extra class on the outer container (both layouts). */
4
66
  class?: string;
5
- children?: import('svelte').Snippet;
67
+ /** v1 freeform content (v1 body) / v2 extra content rendered below actions. */
68
+ children?: Snippet;
6
69
  }
7
70
 
8
- let { copy = 'Nothing here yet.', class: className = '', children }: Props = $props();
71
+ let {
72
+ title,
73
+ description = '',
74
+ glyph = 'inbox',
75
+ size = 'md',
76
+ pulse = false,
77
+ icon,
78
+ actions,
79
+ copy = 'Nothing here yet.',
80
+ class: className = '',
81
+ children
82
+ }: Props = $props();
83
+
84
+ // Heroicons (outline) — curated to the empty-states the IDE actually shows.
85
+ const GLYPHS: Record<EmptyGlyph, string> = {
86
+ inbox:
87
+ 'M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z',
88
+ search: 'M21 21l-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z',
89
+ tasks:
90
+ 'M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01',
91
+ folder:
92
+ 'M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z',
93
+ server:
94
+ 'M5.25 14.25h13.5m-13.5 0a3 3 0 0 1-3-3m3 3a3 3 0 1 0 0 6h13.5a3 3 0 1 0 0-6m-16.5-3a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3m-19.5 0a4.5 4.5 0 0 1 .9-2.7L5.737 5.1a3.375 3.375 0 0 1 2.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 0 1 .9 2.7m0 0a3 3 0 0 1-3 3m0 3h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Z',
95
+ sparkles:
96
+ 'M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456Z',
97
+ document:
98
+ 'M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z',
99
+ database:
100
+ 'M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75',
101
+ bell: 'M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0',
102
+ check:
103
+ 'M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z',
104
+ radar:
105
+ 'M9.348 14.651a3.75 3.75 0 0 1 0-5.303m5.304-.001a3.75 3.75 0 0 1 0 5.304m-7.425 2.122a6.75 6.75 0 0 1 0-9.546m9.546.001a6.75 6.75 0 0 1 0 9.547m-11.667 2.12a10.5 10.5 0 0 1 0-13.787m13.787.001a10.5 10.5 0 0 1 0 13.787M12 12h.008v.008H12V12Z'
106
+ };
107
+
108
+ const SIZES = {
109
+ sm: { pad: 'py-8 px-4', icon: 'w-9 h-9', box: 'w-14 h-14', title: 'text-sm', desc: 'text-xs', gap: 'gap-1.5' },
110
+ md: { pad: 'py-14 px-6', icon: 'w-12 h-12', box: 'w-20 h-20', title: 'text-base', desc: 'text-sm', gap: 'gap-2' },
111
+ lg: { pad: 'py-20 px-8', icon: 'w-16 h-16', box: 'w-28 h-28', title: 'text-lg', desc: 'text-sm', gap: 'gap-2.5' }
112
+ } as const;
113
+
114
+ const s = $derived(SIZES[size]);
115
+ const path = $derived(GLYPHS[glyph] ?? GLYPHS.inbox);
116
+ const v2 = $derived(title !== undefined);
9
117
  </script>
10
118
 
11
- <div class="text-base-content/45 flex flex-col items-center justify-center gap-2 py-8 text-sm {className}">
12
- {#if children}
13
- {@render children()}
14
- {:else}
15
- <span>{copy}</span>
16
- {/if}
17
- </div>
119
+ {#if v2}
120
+ <div class="flex flex-col items-center justify-center text-center {s.pad} {s.gap} {className}" role="status" aria-live="polite">
121
+ <div
122
+ class="flex items-center justify-center rounded-2xl {s.box} mb-1 bg-base-content/[0.04] border border-base-content/10"
123
+ class:empty-pulse={pulse}
124
+ >
125
+ {#if icon}
126
+ {@render icon()}
127
+ {:else}
128
+ <svg
129
+ xmlns="http://www.w3.org/2000/svg"
130
+ fill="none"
131
+ viewBox="0 0 24 24"
132
+ stroke-width="1.5"
133
+ stroke="currentColor"
134
+ class="{s.icon} text-base-content/40"
135
+ aria-hidden="true"
136
+ >
137
+ <path stroke-linecap="round" stroke-linejoin="round" d={path} />
138
+ </svg>
139
+ {/if}
140
+ </div>
141
+
142
+ <p class="{s.title} font-semibold text-base-content/80 max-w-md">{title}</p>
143
+
144
+ {#if description}
145
+ <p class="{s.desc} text-base-content/50 max-w-md leading-relaxed">{description}</p>
146
+ {/if}
147
+
148
+ {#if actions}
149
+ <div class="flex flex-wrap items-center justify-center gap-2 mt-2">
150
+ {@render actions()}
151
+ </div>
152
+ {/if}
153
+
154
+ {#if children}
155
+ <div class="mt-1 w-full">{@render children()}</div>
156
+ {/if}
157
+ </div>
158
+ {:else}
159
+ <div class="text-base-content/45 flex flex-col items-center justify-center gap-2 py-8 text-sm {className}">
160
+ {#if children}
161
+ {@render children()}
162
+ {:else}
163
+ <span>{copy}</span>
164
+ {/if}
165
+ </div>
166
+ {/if}
167
+
168
+ <style>
169
+ /* Ambient "listening" pulse for waiting/empty states — gated for a11y.
170
+ Tinted with the theme's primary color token, never a hardcoded hue. */
171
+ .empty-pulse {
172
+ animation: empty-pulse 3s ease-in-out infinite;
173
+ }
174
+ @keyframes empty-pulse {
175
+ 0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--color-primary) 18%, transparent); }
176
+ 50% { box-shadow: 0 0 0 8px color-mix(in oklch, var(--color-primary) 0%, transparent); }
177
+ }
178
+ @media (prefers-reduced-motion: reduce) {
179
+ .empty-pulse { animation: none !important; }
180
+ }
181
+ </style>
@@ -1,7 +1,56 @@
1
+ import type { Snippet } from 'svelte';
2
+ /**
3
+ * Empty — the STANDARD "nothing here yet" surface, now at full strength (v2).
4
+ *
5
+ * v1 API (unchanged, still supported): `copy` + `children` render the
6
+ * original thin zero-state block.
7
+ *
8
+ * v2 API (upstreamed from the JAT IDE's ui/EmptyState.svelte, additive):
9
+ * pass `title` to opt into the full hero layout — named glyph or custom
10
+ * `icon` snippet, three sizes, an optional ambient pulse for
11
+ * "listening / waiting" states, CTA `actions` and extra `children` below.
12
+ *
13
+ * Design-system conformance:
14
+ * - Color routes through DaisyUI semantic tokens (`text-base-content`
15
+ * with opacity), so it survives a theme switch. No hardcoded state hues.
16
+ * - Motion (the optional ambient pulse on the icon) is reduced-motion gated
17
+ * and tinted with the theme's `--color-primary` token.
18
+ *
19
+ * Usage — named glyph + CTA:
20
+ * <Empty title="Inbox zero" description="No items need triage.">
21
+ * {#snippet actions()}
22
+ * <button class="btn btn-sm btn-primary" onclick={create}>New task</button>
23
+ * {/snippet}
24
+ * </Empty>
25
+ *
26
+ * Usage — custom icon snippet:
27
+ * <Empty title="No sessions">
28
+ * {#snippet icon()}<MyIcon class="w-12 h-12" />{/snippet}
29
+ * </Empty>
30
+ */
31
+ /** Named glyph from the curated Heroicons (outline) set. */
32
+ export type EmptyGlyph = 'inbox' | 'search' | 'tasks' | 'folder' | 'server' | 'sparkles' | 'document' | 'database' | 'bell' | 'check' | 'radar';
1
33
  interface Props {
34
+ /** v2 headline — what's missing, in plain words. Opts into the v2 hero layout. */
35
+ title?: string;
36
+ /** v2 optional one-line guidance: what the operator can do about it. */
37
+ description?: string;
38
+ /** v2 named glyph from the curated set. Ignored if an `icon` snippet is passed. */
39
+ glyph?: EmptyGlyph;
40
+ /** v2 visual scale. `sm` for inline panels, `lg` for full-page first-runs. */
41
+ size?: 'sm' | 'md' | 'lg';
42
+ /** v2 subtle ambient pulse on the icon (reduced-motion gated). For "listening / waiting" states. */
43
+ pulse?: boolean;
44
+ /** v2 override the glyph with arbitrary icon markup. */
45
+ icon?: Snippet;
46
+ /** v2 right-aligned CTA buttons rendered under the description. */
47
+ actions?: Snippet;
48
+ /** v1 legacy copy line (ignored when `title` is set). */
2
49
  copy?: string;
50
+ /** Extra class on the outer container (both layouts). */
3
51
  class?: string;
4
- children?: import('svelte').Snippet;
52
+ /** v1 freeform content (v1 body) / v2 extra content rendered below actions. */
53
+ children?: Snippet;
5
54
  }
6
55
  declare const Empty: import("svelte").Component<Props, {}, "">;
7
56
  type Empty = ReturnType<typeof Empty>;
@@ -0,0 +1,128 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ /**
5
+ * ErrorState — the STANDARD "this failed to load" surface.
6
+ *
7
+ * Upstreamed from the JAT IDE's ui/ErrorState.svelte so error handling
8
+ * stops being a per-route art project: message, an optional Retry, and an
9
+ * optional collapsible details block for the raw error.
10
+ *
11
+ * Design-system conformance:
12
+ * - The error hue comes from the DaisyUI semantic `error` token
13
+ * (`text-error`, `border-error`, `bg-error`), never an inlined red. This
14
+ * keeps it aligned with the canonical "needs attention" family and
15
+ * themes cleanly.
16
+ * - Centered card layout mirrors `Empty` v2 so the two read as siblings.
17
+ *
18
+ * Usage:
19
+ * {#if error}
20
+ * <ErrorState message={error} onRetry={() => load()} />
21
+ * {/if}
22
+ *
23
+ * <ErrorState
24
+ * title="Couldn't reach the agent relay"
25
+ * message={err.message}
26
+ * details={err.stack}
27
+ * onRetry={reload}
28
+ * />
29
+ */
30
+
31
+ interface Props {
32
+ /** Short headline. Defaults to a generic failure line. */
33
+ title?: string;
34
+ /** Human-readable failure description (the caught error message). */
35
+ message?: string;
36
+ /** Optional raw detail (stack / response body) shown in a collapsible block. */
37
+ details?: string;
38
+ /** Label for the retry button. */
39
+ retryLabel?: string;
40
+ /** When provided, renders a Retry button wired to this callback. */
41
+ onRetry?: () => void;
42
+ /** Visual scale. */
43
+ size?: 'sm' | 'md' | 'lg';
44
+ /** Extra action buttons rendered alongside Retry. */
45
+ actions?: Snippet;
46
+ }
47
+
48
+ let {
49
+ title = 'Something went wrong',
50
+ message = '',
51
+ details = '',
52
+ retryLabel = 'Retry',
53
+ onRetry,
54
+ size = 'md',
55
+ actions
56
+ }: Props = $props();
57
+
58
+ const SIZES = {
59
+ sm: { pad: 'py-8 px-4', icon: 'w-9 h-9', box: 'w-14 h-14', title: 'text-sm', body: 'text-xs', gap: 'gap-1.5' },
60
+ md: { pad: 'py-14 px-6', icon: 'w-12 h-12', box: 'w-20 h-20', title: 'text-base', body: 'text-sm', gap: 'gap-2' },
61
+ lg: { pad: 'py-20 px-8', icon: 'w-16 h-16', box: 'w-28 h-28', title: 'text-lg', body: 'text-sm', gap: 'gap-2.5' }
62
+ } as const;
63
+
64
+ const s = $derived(SIZES[size]);
65
+ </script>
66
+
67
+ <div class="flex flex-col items-center justify-center text-center {s.pad} {s.gap}" role="alert">
68
+ <div class="flex items-center justify-center rounded-2xl {s.box} mb-1 bg-error/10 border border-error/25">
69
+ <svg
70
+ xmlns="http://www.w3.org/2000/svg"
71
+ fill="none"
72
+ viewBox="0 0 24 24"
73
+ stroke-width="1.5"
74
+ stroke="currentColor"
75
+ class="{s.icon} text-error/80"
76
+ aria-hidden="true"
77
+ >
78
+ <path
79
+ stroke-linecap="round"
80
+ stroke-linejoin="round"
81
+ d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
82
+ />
83
+ </svg>
84
+ </div>
85
+
86
+ <p class="{s.title} font-semibold text-base-content/85 max-w-md">{title}</p>
87
+
88
+ {#if message}
89
+ <p class="{s.body} text-base-content/55 max-w-md leading-relaxed break-words">{message}</p>
90
+ {/if}
91
+
92
+ {#if onRetry || actions}
93
+ <div class="flex flex-wrap items-center justify-center gap-2 mt-2">
94
+ {#if onRetry}
95
+ <button type="button" class="btn btn-sm btn-outline btn-error gap-1.5" onclick={() => onRetry?.()}>
96
+ <svg
97
+ xmlns="http://www.w3.org/2000/svg"
98
+ fill="none"
99
+ viewBox="0 0 24 24"
100
+ stroke-width="2"
101
+ stroke="currentColor"
102
+ class="w-4 h-4"
103
+ aria-hidden="true"
104
+ >
105
+ <path
106
+ stroke-linecap="round"
107
+ stroke-linejoin="round"
108
+ d="M16.023 9.348h4.992V4.356m0 4.992-3.181-3.183a8.25 8.25 0 0 0-13.803 3.7M2.985 14.652h4.992m-4.992 0v4.992m0-4.992 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7"
109
+ />
110
+ </svg>
111
+ {retryLabel}
112
+ </button>
113
+ {/if}
114
+ {#if actions}
115
+ {@render actions()}
116
+ {/if}
117
+ </div>
118
+ {/if}
119
+
120
+ {#if details}
121
+ <details class="mt-3 w-full max-w-md text-left">
122
+ <summary class="cursor-pointer text-xs text-base-content/40 hover:text-base-content/70 transition-colors select-none">
123
+ Technical details
124
+ </summary>
125
+ <pre class="mt-2 max-h-48 overflow-auto rounded-lg bg-base-300/60 border border-base-content/10 p-3 text-[0.7rem] leading-relaxed text-base-content/60 whitespace-pre-wrap break-words">{details}</pre>
126
+ </details>
127
+ {/if}
128
+ </div>
@@ -0,0 +1,46 @@
1
+ import type { Snippet } from 'svelte';
2
+ /**
3
+ * ErrorState — the STANDARD "this failed to load" surface.
4
+ *
5
+ * Upstreamed from the JAT IDE's ui/ErrorState.svelte so error handling
6
+ * stops being a per-route art project: message, an optional Retry, and an
7
+ * optional collapsible details block for the raw error.
8
+ *
9
+ * Design-system conformance:
10
+ * - The error hue comes from the DaisyUI semantic `error` token
11
+ * (`text-error`, `border-error`, `bg-error`), never an inlined red. This
12
+ * keeps it aligned with the canonical "needs attention" family and
13
+ * themes cleanly.
14
+ * - Centered card layout mirrors `Empty` v2 so the two read as siblings.
15
+ *
16
+ * Usage:
17
+ * {#if error}
18
+ * <ErrorState message={error} onRetry={() => load()} />
19
+ * {/if}
20
+ *
21
+ * <ErrorState
22
+ * title="Couldn't reach the agent relay"
23
+ * message={err.message}
24
+ * details={err.stack}
25
+ * onRetry={reload}
26
+ * />
27
+ */
28
+ interface Props {
29
+ /** Short headline. Defaults to a generic failure line. */
30
+ title?: string;
31
+ /** Human-readable failure description (the caught error message). */
32
+ message?: string;
33
+ /** Optional raw detail (stack / response body) shown in a collapsible block. */
34
+ details?: string;
35
+ /** Label for the retry button. */
36
+ retryLabel?: string;
37
+ /** When provided, renders a Retry button wired to this callback. */
38
+ onRetry?: () => void;
39
+ /** Visual scale. */
40
+ size?: 'sm' | 'md' | 'lg';
41
+ /** Extra action buttons rendered alongside Retry. */
42
+ actions?: Snippet;
43
+ }
44
+ declare const ErrorState: import("svelte").Component<Props, {}, "">;
45
+ type ErrorState = ReturnType<typeof ErrorState>;
46
+ export default ErrorState;
@@ -0,0 +1,60 @@
1
+ <script lang="ts">
2
+ /**
3
+ * "Jump to latest" affordance for streaming transcripts. Pairs with
4
+ * `createStickToBottom` (`src/lib/actions/stickToBottom`): show it when the
5
+ * reader has scrolled away from the live edge, optionally badge it with the
6
+ * number of messages that arrived off-screen, and on click jump to the bottom
7
+ * and resume following.
8
+ *
9
+ * Theme-token only (no colour literals); motion is transform/opacity only and
10
+ * respects the global reduced-motion guard via Svelte transitions. Upstreamed
11
+ * from the JAT IDE (adoption audit U3, jatui-rl7w6.3).
12
+ *
13
+ * Position it inside a `relative` wrapper that also holds the scroll element.
14
+ */
15
+ import { scale } from 'svelte/transition';
16
+ import { cubicOut } from 'svelte/easing';
17
+
18
+ let {
19
+ visible = false,
20
+ unreadCount = 0,
21
+ label = 'Jump to latest',
22
+ onclick
23
+ }: {
24
+ visible?: boolean;
25
+ unreadCount?: number;
26
+ label?: string;
27
+ onclick?: () => void;
28
+ } = $props();
29
+ </script>
30
+
31
+ {#if visible}
32
+ <div class="pointer-events-none absolute inset-x-0 bottom-3 z-10 flex justify-center">
33
+ <button
34
+ type="button"
35
+ class="btn btn-primary btn-sm pointer-events-auto gap-1 rounded-full"
36
+ aria-label={unreadCount > 0 ? `${label} (${unreadCount} new)` : label}
37
+ onclick={() => onclick?.()}
38
+ transition:scale={{ duration: 150, start: 0.85, easing: cubicOut }}
39
+ >
40
+ {#if unreadCount > 0}
41
+ <span class="font-semibold tabular-nums">{unreadCount}</span>
42
+ <span>new</span>
43
+ {/if}
44
+ <svg
45
+ xmlns="http://www.w3.org/2000/svg"
46
+ class="size-4"
47
+ viewBox="0 0 24 24"
48
+ fill="none"
49
+ stroke="currentColor"
50
+ stroke-width="2"
51
+ stroke-linecap="round"
52
+ stroke-linejoin="round"
53
+ aria-hidden="true"
54
+ >
55
+ <path d="M12 5v14" />
56
+ <path d="m19 12-7 7-7-7" />
57
+ </svg>
58
+ </button>
59
+ </div>
60
+ {/if}
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ visible?: boolean;
3
+ unreadCount?: number;
4
+ label?: string;
5
+ onclick?: () => void;
6
+ };
7
+ declare const JumpToLatest: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type JumpToLatest = ReturnType<typeof JumpToLatest>;
9
+ export default JumpToLatest;