@joewinke/jatui 0.38.5 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +32 -1
- package/THIRD_PARTY_NOTICES.md +51 -0
- package/dist/components/BulkActionBar.svelte +32 -2
- package/dist/components/BulkActionBar.svelte.d.ts +5 -1
- package/dist/components/FloatingActionBar.svelte +102 -34
- package/dist/components/FloatingActionBar.svelte.d.ts +9 -0
- package/dist/components/MediaExplorer.svelte +249 -0
- package/dist/components/MediaExplorer.svelte.d.ts +37 -0
- package/dist/components/NotificationCenter.svelte +481 -0
- package/dist/components/NotificationCenter.svelte.d.ts +29 -0
- package/dist/components/SearchDropdown.svelte +54 -8
- package/dist/components/SearchDropdown.svelte.d.ts +12 -0
- package/dist/components/VideoPlayer.svelte +269 -3
- package/dist/components/agent/AgentLog.svelte +217 -0
- package/dist/components/agent/AgentLog.svelte.d.ts +72 -0
- package/dist/components/agent/AgentLogRow.svelte +281 -0
- package/dist/components/agent/AgentLogRow.svelte.d.ts +21 -0
- package/dist/components/agent/AgentTaskList.svelte +607 -0
- package/dist/components/agent/AgentTaskList.svelte.d.ts +40 -0
- package/dist/components/agent/AgentThinking.svelte +280 -0
- package/dist/components/agent/AgentThinking.svelte.d.ts +22 -0
- package/dist/components/agent/ComposerGlow.svelte +265 -0
- package/dist/components/agent/ComposerGlow.svelte.d.ts +18 -0
- package/dist/components/agent/ContextMeter.svelte +277 -0
- package/dist/components/agent/ContextMeter.svelte.d.ts +21 -0
- package/dist/components/agent/Questionnaire.svelte +767 -0
- package/dist/components/agent/Questionnaire.svelte.d.ts +29 -0
- package/dist/components/agent/ShimmerText.svelte +100 -0
- package/dist/components/agent/ShimmerText.svelte.d.ts +19 -0
- package/dist/components/agent/StreamedText.svelte +135 -0
- package/dist/components/agent/StreamedText.svelte.d.ts +13 -0
- package/dist/components/agent/agentLog.d.ts +22 -0
- package/dist/components/agent/agentLog.js +26 -0
- package/dist/components/agent/agentTaskList.d.ts +90 -0
- package/dist/components/agent/agentTaskList.js +93 -0
- package/dist/components/agent/agentThinking.d.ts +46 -0
- package/dist/components/agent/agentThinking.js +78 -0
- package/dist/components/agent/contextMeter.d.ts +97 -0
- package/dist/components/agent/contextMeter.js +156 -0
- package/dist/components/agent/questionnaire.d.ts +104 -0
- package/dist/components/agent/questionnaire.js +225 -0
- package/dist/components/agent/revealCore.d.ts +74 -0
- package/dist/components/agent/revealCore.js +160 -0
- package/dist/components/agent/revealTicker.d.ts +34 -0
- package/dist/components/agent/revealTicker.js +73 -0
- package/dist/components/agent/streamedText.d.ts +28 -0
- package/dist/components/agent/streamedText.js +59 -0
- package/dist/components/messaging/MessageInput.svelte +34 -4
- package/dist/components/messaging/MessageInput.svelte.d.ts +7 -0
- package/dist/components/messaging/MessageItem.svelte +2 -2
- package/dist/components/notificationCenter.d.ts +65 -0
- package/dist/components/notificationCenter.js +110 -0
- package/dist/components/progress/Steps.svelte +269 -5
- package/dist/components/progress/Steps.svelte.d.ts +16 -1
- package/dist/components/progress/stepsSummary.d.ts +14 -0
- package/dist/components/progress/stepsSummary.js +32 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +29 -0
- package/dist/selection/clickSelection.d.ts +89 -0
- package/dist/selection/clickSelection.js +152 -0
- package/dist/selection/createClickSelection.d.ts +79 -0
- package/dist/selection/createClickSelection.js +134 -0
- package/dist/video/keys.d.ts +5 -5
- package/dist/video/keys.js +9 -5
- package/dist/video/transcriptSearch.d.ts +80 -0
- package/dist/video/transcriptSearch.js +153 -0
- package/package.json +3 -2
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* MediaExplorer — a two-pane source browser shell for files, images, or any
|
|
4
|
+
* per-source collection. The LEFT RAIL lists the sources (grouped, keyboard
|
|
5
|
+
* navigable); the RIGHT PANE renders whatever the host shows for the active
|
|
6
|
+
* source via the `pane` snippet. MediaExplorer owns the shell only — it is
|
|
7
|
+
* deliberately decoupled from any backend, media type, or selection model:
|
|
8
|
+
* the host keeps its own selection state in the pane and the footer.
|
|
9
|
+
*
|
|
10
|
+
* <MediaExplorer {sources} bind:activeId title="Add references" onClose={...}>
|
|
11
|
+
* {#snippet pane(source)}
|
|
12
|
+
* <MyBrowser source={source.id} />
|
|
13
|
+
* {/snippet}
|
|
14
|
+
* {#snippet footer()}…{/snippet}
|
|
15
|
+
* </MediaExplorer>
|
|
16
|
+
*
|
|
17
|
+
* Rail keyboard contract: the rail is one tab stop (roving tabindex).
|
|
18
|
+
* ArrowUp/ArrowDown/Home/End move between enabled sources and ACTIVATE them
|
|
19
|
+
* (the pane follows focus — arrowing through sources is the browsing gesture,
|
|
20
|
+
* no Enter needed). Click activates. Disabled sources are skipped; their
|
|
21
|
+
* `hint` explains why (rendered as the row's title).
|
|
22
|
+
*
|
|
23
|
+
* Dialog conventions follow Modal.svelte: native <dialog>, focus moved in on
|
|
24
|
+
* open and restored on close, Escape + backdrop close, body scroll locked,
|
|
25
|
+
* reduced-motion skips the entrance transitions.
|
|
26
|
+
*/
|
|
27
|
+
import { fade, fly } from "svelte/transition"
|
|
28
|
+
import { cubicOut } from "svelte/easing"
|
|
29
|
+
import type { Snippet } from "svelte"
|
|
30
|
+
|
|
31
|
+
export interface ExplorerSource {
|
|
32
|
+
/** Stable source id, e.g. "refs", "out", "catalogs/costume". */
|
|
33
|
+
id: string
|
|
34
|
+
/** Rail label. */
|
|
35
|
+
label: string
|
|
36
|
+
/** Small mono suffix after the label, e.g. "refs/". */
|
|
37
|
+
hint?: string
|
|
38
|
+
/** Right-aligned count badge. */
|
|
39
|
+
badge?: string | number
|
|
40
|
+
/** Rail section heading; ungrouped sources render above every group. */
|
|
41
|
+
group?: string
|
|
42
|
+
/** Not clickable and skipped by the arrows; `hint` should say why. */
|
|
43
|
+
disabled?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface Props {
|
|
47
|
+
sources: ExplorerSource[]
|
|
48
|
+
/** The active source id. Falls back to the first enabled source when unset or stale. */
|
|
49
|
+
activeId?: string
|
|
50
|
+
title: string
|
|
51
|
+
onClose: () => void
|
|
52
|
+
/** Content of the active source. */
|
|
53
|
+
pane: Snippet<[ExplorerSource]>
|
|
54
|
+
/** Rendered beside the title (filters, view toggles). */
|
|
55
|
+
toolbar?: Snippet
|
|
56
|
+
/** Below the pane (selection counts, actions). */
|
|
57
|
+
footer?: Snippet
|
|
58
|
+
/** Accessible label for the rail. Default "Sources". */
|
|
59
|
+
railLabel?: string
|
|
60
|
+
/** Accessible label for the pane column. Default "Contents". */
|
|
61
|
+
paneLabel?: string
|
|
62
|
+
/** Max width of the dialog as a CSS length. Default "72rem". */
|
|
63
|
+
maxWidth?: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let {
|
|
67
|
+
sources,
|
|
68
|
+
activeId = $bindable(),
|
|
69
|
+
title,
|
|
70
|
+
onClose,
|
|
71
|
+
pane,
|
|
72
|
+
toolbar,
|
|
73
|
+
footer,
|
|
74
|
+
railLabel = "Sources",
|
|
75
|
+
paneLabel = "Contents",
|
|
76
|
+
maxWidth = "72rem",
|
|
77
|
+
}: Props = $props()
|
|
78
|
+
|
|
79
|
+
const enabled = $derived(sources.filter((s) => !s.disabled))
|
|
80
|
+
|
|
81
|
+
// An unset or stale activeId falls to the first enabled source, so a caller
|
|
82
|
+
// can bind a plain variable and read it back.
|
|
83
|
+
const active = $derived(sources.find((s) => s.id === activeId) ?? enabled[0])
|
|
84
|
+
$effect(() => {
|
|
85
|
+
if (active && active.id !== activeId) activeId = active.id
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Rail sections: ungrouped first, then groups in first-seen order.
|
|
89
|
+
const sections = $derived.by(() => {
|
|
90
|
+
const ungrouped = sources.filter((s) => !s.group)
|
|
91
|
+
const groups: { name: string; sources: ExplorerSource[] }[] = []
|
|
92
|
+
for (const s of sources) {
|
|
93
|
+
if (!s.group) continue
|
|
94
|
+
let g = groups.find((x) => x.name === s.group)
|
|
95
|
+
if (!g) groups.push((g = { name: s.group, sources: [] }))
|
|
96
|
+
g.sources.push(s)
|
|
97
|
+
}
|
|
98
|
+
return [...(ungrouped.length ? [{ name: "", sources: ungrouped }] : []), ...groups]
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
function activate(s: ExplorerSource) {
|
|
102
|
+
if (s.disabled) return
|
|
103
|
+
activeId = s.id
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Roving tabindex: one tab stop, arrows both move and activate.
|
|
107
|
+
let railEl = $state<HTMLElement | null>(null)
|
|
108
|
+
let dialogEl = $state<HTMLDialogElement | null>(null)
|
|
109
|
+
let previousFocus: HTMLElement | null = null
|
|
110
|
+
|
|
111
|
+
const reducedMotion =
|
|
112
|
+
typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
|
|
113
|
+
|
|
114
|
+
const FOCUSABLE = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
115
|
+
|
|
116
|
+
$effect(() => {
|
|
117
|
+
if (typeof document === "undefined") return
|
|
118
|
+
previousFocus = document.activeElement as HTMLElement
|
|
119
|
+
document.body.style.overflow = "hidden"
|
|
120
|
+
requestAnimationFrame(() => dialogEl?.focus())
|
|
121
|
+
return () => {
|
|
122
|
+
document.body.style.overflow = ""
|
|
123
|
+
previousFocus?.focus?.()
|
|
124
|
+
previousFocus = null
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
function focusRow(id: string) {
|
|
129
|
+
railEl?.querySelector<HTMLElement>(`[data-source-id="${CSS.escape(id)}"]`)?.focus()
|
|
130
|
+
}
|
|
131
|
+
function onRailKey(e: KeyboardEvent) {
|
|
132
|
+
if (e.key !== "ArrowDown" && e.key !== "ArrowUp" && e.key !== "Home" && e.key !== "End") return
|
|
133
|
+
e.preventDefault()
|
|
134
|
+
if (!enabled.length) return
|
|
135
|
+
const i = enabled.findIndex((s) => s.id === active?.id)
|
|
136
|
+
const next =
|
|
137
|
+
e.key === "Home" ? 0 : e.key === "End" ? enabled.length - 1 : (i + (e.key === "ArrowDown" ? 1 : enabled.length - 1)) % enabled.length
|
|
138
|
+
const target = enabled[next]!
|
|
139
|
+
activate(target)
|
|
140
|
+
focusRow(target.id)
|
|
141
|
+
}
|
|
142
|
+
function onDialogKey(e: KeyboardEvent) {
|
|
143
|
+
if (e.key !== "Escape") return
|
|
144
|
+
e.preventDefault()
|
|
145
|
+
e.stopPropagation()
|
|
146
|
+
onClose()
|
|
147
|
+
}
|
|
148
|
+
function trapFocus(e: KeyboardEvent) {
|
|
149
|
+
if (e.key !== "Tab" || !dialogEl) return
|
|
150
|
+
const focusable = dialogEl.querySelectorAll<HTMLElement>(FOCUSABLE)
|
|
151
|
+
if (!focusable.length) return
|
|
152
|
+
const first = focusable[0]!
|
|
153
|
+
const last = focusable[focusable.length - 1]!
|
|
154
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
155
|
+
e.preventDefault()
|
|
156
|
+
last.focus()
|
|
157
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
158
|
+
e.preventDefault()
|
|
159
|
+
first.focus()
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
<svelte:window onkeydown={onDialogKey} />
|
|
165
|
+
|
|
166
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
167
|
+
<dialog
|
|
168
|
+
bind:this={dialogEl}
|
|
169
|
+
class="modal"
|
|
170
|
+
aria-modal="true"
|
|
171
|
+
aria-label={title}
|
|
172
|
+
open
|
|
173
|
+
tabindex="-1"
|
|
174
|
+
onkeydown={(e) => {
|
|
175
|
+
trapFocus(e)
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
179
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
180
|
+
<div
|
|
181
|
+
class="modal-backdrop"
|
|
182
|
+
onclick={(e) => {
|
|
183
|
+
// Only a direct click on the backdrop closes; clicks inside the sheet bubble
|
|
184
|
+
// here and must not (same guard as Modal.svelte).
|
|
185
|
+
if (e.target === e.currentTarget) onClose()
|
|
186
|
+
}}
|
|
187
|
+
transition:fade={{ duration: reducedMotion ? 0 : 150 }}
|
|
188
|
+
>
|
|
189
|
+
<div
|
|
190
|
+
class="bg-base-100 text-base-content relative mx-auto my-auto flex max-h-[92vh] flex-col rounded-lg shadow-2xl"
|
|
191
|
+
style="width: min(calc(100% - 2rem), {maxWidth})"
|
|
192
|
+
in:fly={{ y: reducedMotion ? 0 : 12, duration: reducedMotion ? 0 : 200, easing: cubicOut }}
|
|
193
|
+
out:fade={{ duration: reducedMotion ? 0 : 120 }}
|
|
194
|
+
>
|
|
195
|
+
<header class="flex items-center gap-3 px-4 pt-4 pb-2">
|
|
196
|
+
<h2 class="text-lg font-semibold">{title}</h2>
|
|
197
|
+
<span class="min-w-0 flex-1"></span>
|
|
198
|
+
{@render toolbar?.()}
|
|
199
|
+
<button type="button" class="btn btn-sm btn-circle btn-ghost" onclick={onClose} aria-label="Close">✕</button>
|
|
200
|
+
</header>
|
|
201
|
+
<div class="grid min-h-0 flex-1 sm:grid-cols-[13rem_1fr]" style="height: min(70vh, 620px)">
|
|
202
|
+
<nav class="border-base-content/10 overflow-y-auto border-r pr-1" aria-label={railLabel}>
|
|
203
|
+
<div
|
|
204
|
+
class="flex flex-col gap-0.5 py-1"
|
|
205
|
+
role="tablist"
|
|
206
|
+
aria-orientation="vertical"
|
|
207
|
+
aria-label={railLabel}
|
|
208
|
+
bind:this={railEl}
|
|
209
|
+
onkeydown={onRailKey}
|
|
210
|
+
>
|
|
211
|
+
{#each sections as section (section.name)}
|
|
212
|
+
{#if section.name}
|
|
213
|
+
<p class="text-base-content/50 mt-2 px-2 text-[10px] font-semibold tracking-widest uppercase">{section.name}</p>
|
|
214
|
+
{/if}
|
|
215
|
+
{#each section.sources as s (s.id)}
|
|
216
|
+
<button
|
|
217
|
+
data-source-id={s.id}
|
|
218
|
+
role="tab"
|
|
219
|
+
aria-selected={active?.id === s.id}
|
|
220
|
+
tabindex={active?.id === s.id ? 0 : -1}
|
|
221
|
+
disabled={s.disabled}
|
|
222
|
+
title={s.disabled ? s.hint : undefined}
|
|
223
|
+
class="flex items-center gap-2 rounded-lg px-2 py-1.5 text-left text-sm
|
|
224
|
+
{active?.id === s.id ? 'bg-base-content/10 font-semibold' : 'hover:bg-base-content/5'}
|
|
225
|
+
{s.disabled ? 'opacity-50' : ''}"
|
|
226
|
+
onclick={() => activate(s)}
|
|
227
|
+
>
|
|
228
|
+
<span class="min-w-0 flex-1 truncate">{s.label}</span>
|
|
229
|
+
{#if s.hint}<span class="text-base-content/40 shrink-0 font-mono text-[10px]">{s.hint}</span>{/if}
|
|
230
|
+
{#if s.badge !== undefined}<span class="text-base-content/50 shrink-0 font-mono text-[10px] tnum">{s.badge}</span>{/if}
|
|
231
|
+
</button>
|
|
232
|
+
{/each}
|
|
233
|
+
{/each}
|
|
234
|
+
</div>
|
|
235
|
+
</nav>
|
|
236
|
+
<section class="min-h-0 overflow-y-auto p-4" aria-label={paneLabel}>
|
|
237
|
+
{#if active}
|
|
238
|
+
{@render pane(active)}
|
|
239
|
+
{/if}
|
|
240
|
+
</section>
|
|
241
|
+
</div>
|
|
242
|
+
{#if footer}
|
|
243
|
+
<div class="border-base-content/10 flex flex-wrap items-center gap-2 border-t px-4 py-3">
|
|
244
|
+
{@render footer()}
|
|
245
|
+
</div>
|
|
246
|
+
{/if}
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</dialog>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
export interface ExplorerSource {
|
|
3
|
+
/** Stable source id, e.g. "refs", "out", "catalogs/costume". */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Rail label. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Small mono suffix after the label, e.g. "refs/". */
|
|
8
|
+
hint?: string;
|
|
9
|
+
/** Right-aligned count badge. */
|
|
10
|
+
badge?: string | number;
|
|
11
|
+
/** Rail section heading; ungrouped sources render above every group. */
|
|
12
|
+
group?: string;
|
|
13
|
+
/** Not clickable and skipped by the arrows; `hint` should say why. */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface Props {
|
|
17
|
+
sources: ExplorerSource[];
|
|
18
|
+
/** The active source id. Falls back to the first enabled source when unset or stale. */
|
|
19
|
+
activeId?: string;
|
|
20
|
+
title: string;
|
|
21
|
+
onClose: () => void;
|
|
22
|
+
/** Content of the active source. */
|
|
23
|
+
pane: Snippet<[ExplorerSource]>;
|
|
24
|
+
/** Rendered beside the title (filters, view toggles). */
|
|
25
|
+
toolbar?: Snippet;
|
|
26
|
+
/** Below the pane (selection counts, actions). */
|
|
27
|
+
footer?: Snippet;
|
|
28
|
+
/** Accessible label for the rail. Default "Sources". */
|
|
29
|
+
railLabel?: string;
|
|
30
|
+
/** Accessible label for the pane column. Default "Contents". */
|
|
31
|
+
paneLabel?: string;
|
|
32
|
+
/** Max width of the dialog as a CSS length. Default "72rem". */
|
|
33
|
+
maxWidth?: string;
|
|
34
|
+
}
|
|
35
|
+
declare const MediaExplorer: import("svelte").Component<Props, {}, "activeId">;
|
|
36
|
+
type MediaExplorer = ReturnType<typeof MediaExplorer>;
|
|
37
|
+
export default MediaExplorer;
|