@dowel-ui/react 0.3.0 → 0.4.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/README.md +9 -8
  2. package/dist/components/ai-disclosure/ai-disclosure.d.ts +92 -0
  3. package/dist/components/ai-disclosure/ai-disclosure.d.ts.map +1 -0
  4. package/dist/components/ai-disclosure/ai-disclosure.js +122 -0
  5. package/dist/components/ai-disclosure/ai-disclosure.js.map +1 -0
  6. package/dist/components/ai-disclosure/index.d.ts +2 -0
  7. package/dist/components/ai-disclosure/index.js +2 -0
  8. package/dist/components/ai-disclosure/meta.js +17 -0
  9. package/dist/components/ai-disclosure/meta.js.map +1 -0
  10. package/dist/components/avatar/avatar.d.ts +1 -1
  11. package/dist/components/date-picker/date-picker.js +1 -1
  12. package/dist/components/diff-viewer/diff-model.d.ts +69 -0
  13. package/dist/components/diff-viewer/diff-model.d.ts.map +1 -0
  14. package/dist/components/diff-viewer/diff-model.js +180 -0
  15. package/dist/components/diff-viewer/diff-model.js.map +1 -0
  16. package/dist/components/diff-viewer/diff-viewer.d.ts +42 -0
  17. package/dist/components/diff-viewer/diff-viewer.d.ts.map +1 -0
  18. package/dist/components/diff-viewer/diff-viewer.js +212 -0
  19. package/dist/components/diff-viewer/diff-viewer.js.map +1 -0
  20. package/dist/components/diff-viewer/index.d.ts +3 -0
  21. package/dist/components/diff-viewer/index.js +3 -0
  22. package/dist/components/diff-viewer/meta.js +17 -0
  23. package/dist/components/diff-viewer/meta.js.map +1 -0
  24. package/dist/components/log-viewer/index.d.ts +3 -0
  25. package/dist/components/log-viewer/index.js +3 -0
  26. package/dist/components/log-viewer/log-stream.d.ts +68 -0
  27. package/dist/components/log-viewer/log-stream.d.ts.map +1 -0
  28. package/dist/components/log-viewer/log-stream.js +120 -0
  29. package/dist/components/log-viewer/log-stream.js.map +1 -0
  30. package/dist/components/log-viewer/log-viewer.d.ts +51 -0
  31. package/dist/components/log-viewer/log-viewer.d.ts.map +1 -0
  32. package/dist/components/log-viewer/log-viewer.js +284 -0
  33. package/dist/components/log-viewer/log-viewer.js.map +1 -0
  34. package/dist/components/log-viewer/meta.js +17 -0
  35. package/dist/components/log-viewer/meta.js.map +1 -0
  36. package/dist/components/spinner/spinner.d.ts +1 -1
  37. package/dist/components/time-range-picker/index.d.ts +3 -0
  38. package/dist/components/time-range-picker/index.js +3 -0
  39. package/dist/components/time-range-picker/meta.js +22 -0
  40. package/dist/components/time-range-picker/meta.js.map +1 -0
  41. package/dist/components/time-range-picker/time-expression.d.ts +88 -0
  42. package/dist/components/time-range-picker/time-expression.d.ts.map +1 -0
  43. package/dist/components/time-range-picker/time-expression.js +335 -0
  44. package/dist/components/time-range-picker/time-expression.js.map +1 -0
  45. package/dist/components/time-range-picker/time-range-picker.d.ts +65 -0
  46. package/dist/components/time-range-picker/time-range-picker.d.ts.map +1 -0
  47. package/dist/components/time-range-picker/time-range-picker.js +377 -0
  48. package/dist/components/time-range-picker/time-range-picker.js.map +1 -0
  49. package/dist/index.d.ts +12 -1
  50. package/dist/index.js +9 -2
  51. package/dist/registry/components.d.ts.map +1 -1
  52. package/dist/registry/components.js +58 -50
  53. package/dist/registry/components.js.map +1 -1
  54. package/package.json +6 -4
  55. package/src/components/ai-disclosure/ai-disclosure.tsx +244 -0
  56. package/src/components/ai-disclosure/index.ts +9 -0
  57. package/src/components/ai-disclosure/meta.ts +23 -0
  58. package/src/components/diff-viewer/diff-model.ts +256 -0
  59. package/src/components/diff-viewer/diff-viewer.tsx +339 -0
  60. package/src/components/diff-viewer/index.ts +17 -0
  61. package/src/components/diff-viewer/meta.ts +20 -0
  62. package/src/components/log-viewer/index.ts +21 -0
  63. package/src/components/log-viewer/log-stream.ts +176 -0
  64. package/src/components/log-viewer/log-viewer.tsx +424 -0
  65. package/src/components/log-viewer/meta.ts +21 -0
  66. package/src/components/time-range-picker/index.ts +25 -0
  67. package/src/components/time-range-picker/meta.ts +22 -0
  68. package/src/components/time-range-picker/time-expression.ts +491 -0
  69. package/src/components/time-range-picker/time-range-picker.tsx +522 -0
  70. package/src/index.ts +4 -0
  71. package/src/registry/components.ts +8 -0
@@ -0,0 +1,522 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useId,
8
+ useMemo,
9
+ useState,
10
+ useSyncExternalStore,
11
+ type ComponentPropsWithRef,
12
+ type ReactNode,
13
+ } from "react";
14
+ import type { DateRange } from "react-day-picker";
15
+
16
+ import { Button } from "@/components/button";
17
+ import { Calendar } from "@/components/calendar";
18
+ import { Input } from "@/components/input";
19
+ import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover";
20
+ import { focusRing } from "@/lib/styles";
21
+ import { cn } from "@/lib/utils";
22
+
23
+ import {
24
+ DEFAULT_PRESETS,
25
+ TimeExpressionError,
26
+ absoluteExpression,
27
+ describeTimeRange,
28
+ formatResolvedRange,
29
+ resolveTimeRange,
30
+ type ResolvedRange,
31
+ type TimeRangePreset,
32
+ } from "./time-expression";
33
+
34
+ /**
35
+ * The control every observability product builds for itself.
36
+ *
37
+ * Grafana, Datadog, Sentry, PostHog, Vercel, Honeycomb, Cloudflare and
38
+ * Amplitude each maintain a bespoke one, and no React package ships it, because
39
+ * it looks like a date picker and is not one — see `time-expression.ts` for the
40
+ * value model that makes the difference.
41
+ *
42
+ * Composed rather than reimplemented: Popover supplies the dialog semantics,
43
+ * Calendar the absolute path, Input the expression entry.
44
+ *
45
+ * Two omissions are deliberate. No timezone picker — that is a 400-entry
46
+ * combobox and a decision an app makes once, so `timeZone` is a prop. And no
47
+ * comparison range; a second window belongs to whatever renders the chart.
48
+ */
49
+
50
+ interface TimeRangeContextValue {
51
+ value: string;
52
+ setValue: (expression: string) => void;
53
+ now: Date;
54
+ timeZone?: string;
55
+ locale?: string;
56
+ presets: TimeRangePreset[];
57
+ resolved: ResolvedRange | null;
58
+ error: string | null;
59
+ /** False until the clock is known, which on a server render is never. */
60
+ clockReady: boolean;
61
+ close: () => void;
62
+ labelId: string;
63
+ }
64
+
65
+ /** Stands in until the clock is known. Never shown; see `clockReady`. */
66
+ const EPOCH = new Date(0);
67
+
68
+ /**
69
+ * The clock, for callers who did not supply one.
70
+ *
71
+ * `useSyncExternalStore` rather than an effect, because this is the case it
72
+ * exists for: a value the server cannot know. The server snapshot is null, so a
73
+ * server render and the hydration render both commit to nothing clock-dependent
74
+ * and cannot disagree; the real instant arrives in the re-render after.
75
+ *
76
+ * `getSnapshot` has to be referentially stable, so the instant is created once
77
+ * for the page. That means every picker without a `now` prop shares it and none
78
+ * of them advance — which is the whole reason the prop is there.
79
+ */
80
+ const subscribeToNothing = () => () => undefined;
81
+ let sharedClock: Date | null = null;
82
+ const readSharedClock = () => (sharedClock ??= new Date());
83
+ const readNoClock = () => null;
84
+
85
+ const TimeRangeContext = createContext<TimeRangeContextValue | null>(null);
86
+
87
+ function useTimeRange(component: string): TimeRangeContextValue {
88
+ const context = useContext(TimeRangeContext);
89
+ if (!context) {
90
+ throw new Error(`${component} must be used inside <TimeRange>.`);
91
+ }
92
+ return context;
93
+ }
94
+
95
+ export interface TimeRangeProps {
96
+ /** The expression, e.g. `now-6h..now`. Controlled. */
97
+ value?: string;
98
+ defaultValue?: string;
99
+ onValueChange?: (expression: string) => void;
100
+ /**
101
+ * The instant relative expressions are measured from. Supplied by the
102
+ * consumer so nothing here reads the clock during render. Pass a value that
103
+ * changes when you refresh; a stable one holds the window still between them.
104
+ */
105
+ now?: Date;
106
+ /** IANA zone the snapping happens in. Defaults to the runtime zone. */
107
+ timeZone?: string;
108
+ locale?: string;
109
+ presets?: TimeRangePreset[];
110
+ open?: boolean;
111
+ onOpenChange?: (open: boolean) => void;
112
+ children: ReactNode;
113
+ }
114
+
115
+ export function TimeRange({
116
+ value: valueProp,
117
+ defaultValue = "now-6h..now",
118
+ onValueChange,
119
+ now,
120
+ timeZone,
121
+ locale,
122
+ presets = DEFAULT_PRESETS,
123
+ open: openProp,
124
+ onOpenChange,
125
+ children,
126
+ }: TimeRangeProps) {
127
+ const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
128
+ const value = valueProp ?? uncontrolledValue;
129
+
130
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
131
+ const open = openProp ?? uncontrolledOpen;
132
+
133
+ const labelId = useId();
134
+
135
+ // Reading the clock during render is what the `now` prop exists to avoid: the
136
+ // server would render one instant and hydration another, and React would
137
+ // throw out the tree. Until the fallback arrives, nothing clock-dependent is
138
+ // rendered — the relative label, "Last 6 hours", does not need one, which is
139
+ // why the trigger still says something useful in the meantime.
140
+ const fallbackClock = useSyncExternalStore(subscribeToNothing, readSharedClock, readNoClock);
141
+
142
+ const clock = now ?? fallbackClock;
143
+ // A placeholder only for expressions that never mention `now`; anything that
144
+ // does is gated on `clock` below rather than resolved against a fake instant.
145
+ const effectiveNow = clock ?? EPOCH;
146
+
147
+ const setOpen = useCallback(
148
+ (next: boolean) => {
149
+ if (openProp === undefined) setUncontrolledOpen(next);
150
+ onOpenChange?.(next);
151
+ },
152
+ [openProp, onOpenChange],
153
+ );
154
+
155
+ const setValue = useCallback(
156
+ (expression: string) => {
157
+ if (valueProp === undefined) setUncontrolledValue(expression);
158
+ onValueChange?.(expression);
159
+ },
160
+ [valueProp, onValueChange],
161
+ );
162
+
163
+ const { resolved, error } = useMemo(() => {
164
+ // Claiming neither a window nor an error before the clock is known: an
165
+ // expression cannot be judged against an instant nobody has read yet.
166
+ if (!clock) return { resolved: null, error: null };
167
+ try {
168
+ return { resolved: resolveTimeRange(value, { now: clock, timeZone }), error: null };
169
+ } catch (thrown) {
170
+ return {
171
+ resolved: null,
172
+ error: thrown instanceof TimeExpressionError ? thrown.message : "Invalid time range.",
173
+ };
174
+ }
175
+ }, [value, clock, timeZone]);
176
+
177
+ const context = useMemo<TimeRangeContextValue>(
178
+ () => ({
179
+ value,
180
+ setValue,
181
+ now: effectiveNow,
182
+ timeZone,
183
+ locale,
184
+ presets,
185
+ resolved,
186
+ error,
187
+ clockReady: clock !== null,
188
+ close: () => {
189
+ setOpen(false);
190
+ },
191
+ labelId,
192
+ }),
193
+ [
194
+ value,
195
+ setValue,
196
+ effectiveNow,
197
+ clock,
198
+ timeZone,
199
+ locale,
200
+ presets,
201
+ resolved,
202
+ error,
203
+ setOpen,
204
+ labelId,
205
+ ],
206
+ );
207
+
208
+ return (
209
+ <TimeRangeContext.Provider value={context}>
210
+ <Popover open={open} onOpenChange={setOpen}>
211
+ {children}
212
+ </Popover>
213
+ </TimeRangeContext.Provider>
214
+ );
215
+ }
216
+
217
+ function ClockIcon() {
218
+ return (
219
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="size-4 opacity-70">
220
+ <circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="2" />
221
+ <path d="M12 7v5l3 2" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
222
+ </svg>
223
+ );
224
+ }
225
+
226
+ export interface TimeRangeTriggerProps extends ComponentPropsWithRef<"button"> {
227
+ /** Shows the resolved window under the label. Off by default; it is long. */
228
+ showResolved?: boolean;
229
+ }
230
+
231
+ /**
232
+ * The button, named by the range it holds: "Last 6 hours", not two timestamps.
233
+ * The resolved window stays reachable as the title, and as `showResolved`.
234
+ */
235
+ export function TimeRangeTrigger({
236
+ className,
237
+ showResolved = false,
238
+ ...props
239
+ }: TimeRangeTriggerProps) {
240
+ const { value, now, timeZone, locale, presets, resolved, error } =
241
+ useTimeRange("TimeRangeTrigger");
242
+
243
+ const label = describeTimeRange(value, { now, timeZone, locale, presets });
244
+ const resolvedText = resolved ? formatResolvedRange(resolved, { locale, timeZone }) : null;
245
+
246
+ return (
247
+ <PopoverTrigger asChild>
248
+ <Button
249
+ data-slot="time-range-trigger"
250
+ variant="outline"
251
+ aria-invalid={error ? true : undefined}
252
+ title={resolvedText ?? undefined}
253
+ className={cn("justify-start gap-2 font-normal", className)}
254
+ {...props}
255
+ >
256
+ <ClockIcon />
257
+ <span className="flex flex-col items-start leading-tight">
258
+ <span>{label}</span>
259
+ {showResolved && resolvedText ? (
260
+ <span className="text-2xs font-normal text-muted-foreground">{resolvedText}</span>
261
+ ) : null}
262
+ </span>
263
+ {/* The window the label stands for, for anyone who cannot see the
264
+ title attribute. Read after the label, not instead of it. */}
265
+ {resolvedText && !showResolved ? (
266
+ <span className="sr-only">{`, ${resolvedText}`}</span>
267
+ ) : null}
268
+ </Button>
269
+ </PopoverTrigger>
270
+ );
271
+ }
272
+
273
+ export interface TimeRangeContentProps extends ComponentPropsWithRef<typeof PopoverContent> {
274
+ /** Names the dialog. */
275
+ label?: string;
276
+ }
277
+
278
+ export function TimeRangeContent({
279
+ className,
280
+ label = "Choose a time range",
281
+ children,
282
+ ...props
283
+ }: TimeRangeContentProps) {
284
+ const { labelId } = useTimeRange("TimeRangeContent");
285
+
286
+ return (
287
+ <PopoverContent
288
+ data-slot="time-range-content"
289
+ aria-labelledby={labelId}
290
+ align="start"
291
+ className={cn("w-auto max-w-[min(38rem,calc(100vw-2rem))] p-0", className)}
292
+ {...props}
293
+ >
294
+ <h2 id={labelId} className="sr-only">
295
+ {label}
296
+ </h2>
297
+ <div className="flex flex-col gap-0 sm:flex-row sm:items-stretch">{children}</div>
298
+ </PopoverContent>
299
+ );
300
+ }
301
+
302
+ /**
303
+ * The named ranges, which is how most of these are actually used. Buttons
304
+ * rather than a radiogroup: choosing one both selects and dismisses, which is a
305
+ * command, and `aria-pressed` still carries which one matches.
306
+ */
307
+ export function TimeRangePresets({
308
+ className,
309
+ ...props
310
+ }: Omit<ComponentPropsWithRef<"div">, "children">) {
311
+ const { presets, value, setValue, close } = useTimeRange("TimeRangePresets");
312
+
313
+ const groups = useMemo(() => {
314
+ const collected = new Map<string, TimeRangePreset[]>();
315
+ for (const preset of presets) {
316
+ const existing = collected.get(preset.group);
317
+ if (existing) existing.push(preset);
318
+ else collected.set(preset.group, [preset]);
319
+ }
320
+ return [...collected];
321
+ }, [presets]);
322
+
323
+ return (
324
+ <div
325
+ data-slot="time-range-presets"
326
+ className={cn(
327
+ "max-h-80 min-w-52 overflow-y-auto border-border p-1.5 sm:border-r",
328
+ className,
329
+ )}
330
+ {...props}
331
+ >
332
+ {groups.map(([group, items]) => (
333
+ <div key={group} role="group" aria-label={group} className="pb-1 last:pb-0">
334
+ <p className="px-2 py-1 text-2xs font-medium tracking-wide text-muted-foreground uppercase">
335
+ {group}
336
+ </p>
337
+ {items.map((preset) => (
338
+ <button
339
+ key={preset.expression}
340
+ type="button"
341
+ aria-pressed={preset.expression === value}
342
+ onClick={() => {
343
+ setValue(preset.expression);
344
+ close();
345
+ }}
346
+ className={cn(
347
+ "flex w-full items-center rounded-md px-2 py-1.5 text-left text-sm transition-colors",
348
+ preset.expression === value
349
+ ? "bg-accent font-medium text-accent-foreground"
350
+ : "hover:bg-accent hover:text-accent-foreground",
351
+ focusRing,
352
+ )}
353
+ >
354
+ {preset.label}
355
+ </button>
356
+ ))}
357
+ </div>
358
+ ))}
359
+ </div>
360
+ );
361
+ }
362
+
363
+ /**
364
+ * Raw expression entry, with the resolved window shown as you type — the escape
365
+ * hatch that keeps the presets from being a ceiling.
366
+ *
367
+ * An expression that does not parse says why and is not applied: a chart
368
+ * quietly re-scoping itself to a window nobody asked for is worse than one that
369
+ * refuses.
370
+ */
371
+ export function TimeRangeExpression({
372
+ className,
373
+ ...props
374
+ }: Omit<ComponentPropsWithRef<"div">, "children">) {
375
+ const { value, setValue, now, timeZone, locale, clockReady, close } =
376
+ useTimeRange("TimeRangeExpression");
377
+
378
+ const [draft, setDraft] = useState(value);
379
+ // The expression can change under the input — a preset click, or the
380
+ // consumer setting it. Adjusting during render rather than in an effect keeps
381
+ // the field from showing a stale value for a frame.
382
+ const [lastValue, setLastValue] = useState(value);
383
+ if (value !== lastValue) {
384
+ setLastValue(value);
385
+ setDraft(value);
386
+ }
387
+
388
+ const feedbackId = useId();
389
+ const preview = useMemo(() => {
390
+ // Before the clock is known there is nothing to preview and nothing to
391
+ // reject — saying "invalid" here would be a verdict reached without a fact.
392
+ if (!clockReady) return { text: null, error: null as string | null };
393
+ try {
394
+ return {
395
+ text: formatResolvedRange(resolveTimeRange(draft, { now, timeZone }), {
396
+ locale,
397
+ timeZone,
398
+ }),
399
+ error: null as string | null,
400
+ };
401
+ } catch (thrown) {
402
+ return {
403
+ text: null,
404
+ error: thrown instanceof TimeExpressionError ? thrown.message : "Invalid time range.",
405
+ };
406
+ }
407
+ }, [draft, now, timeZone, locale, clockReady]);
408
+
409
+ const apply = () => {
410
+ if (preview.error || !clockReady) return;
411
+ setValue(draft.trim());
412
+ close();
413
+ };
414
+
415
+ return (
416
+ <div
417
+ data-slot="time-range-expression"
418
+ className={cn(
419
+ "flex flex-col gap-1.5 border-t border-border p-3 sm:border-t-0",
420
+ className,
421
+ )}
422
+ {...props}
423
+ >
424
+ <label htmlFor={feedbackId + "-input"} className="text-xs font-medium">
425
+ Expression
426
+ </label>
427
+ <div className="flex gap-2">
428
+ <Input
429
+ id={feedbackId + "-input"}
430
+ inputSize="sm"
431
+ value={draft}
432
+ spellCheck={false}
433
+ autoComplete="off"
434
+ aria-invalid={preview.error ? true : undefined}
435
+ aria-describedby={feedbackId}
436
+ onChange={(event) => {
437
+ setDraft(event.target.value);
438
+ }}
439
+ onKeyDown={(event) => {
440
+ if (event.key === "Enter") {
441
+ event.preventDefault();
442
+ apply();
443
+ }
444
+ }}
445
+ className="font-mono"
446
+ />
447
+ <Button size="sm" onClick={apply} disabled={Boolean(preview.error) || !clockReady}>
448
+ Apply
449
+ </Button>
450
+ </div>
451
+ {/* One element for both outcomes, so a screen reader hears the error
452
+ replace the preview rather than accumulating two descriptions. */}
453
+ <p
454
+ id={feedbackId}
455
+ className={cn(
456
+ "min-h-4 text-2xs",
457
+ preview.error ? "text-destructive" : "text-muted-foreground",
458
+ )}
459
+ >
460
+ {preview.error ?? preview.text}
461
+ </p>
462
+ </div>
463
+ );
464
+ }
465
+
466
+ /**
467
+ * The absolute path, for a window with no relative meaning. Picking dates
468
+ * writes a concrete expression: "1 to 7 March" does not become relative just
469
+ * because it was chosen here.
470
+ */
471
+ export function TimeRangeCalendar({
472
+ className,
473
+ numberOfMonths = 1,
474
+ ...props
475
+ }: Omit<ComponentPropsWithRef<"div">, "children"> & { numberOfMonths?: number }) {
476
+ const { setValue, resolved, close } = useTimeRange("TimeRangeCalendar");
477
+
478
+ const [selection, setSelection] = useState<DateRange | undefined>(() =>
479
+ resolved ? { from: resolved.from, to: resolved.to } : undefined,
480
+ );
481
+
482
+ return (
483
+ <div
484
+ data-slot="time-range-calendar"
485
+ className={cn("border-t border-border p-1 sm:border-t-0 sm:border-l", className)}
486
+ {...props}
487
+ >
488
+ <Calendar
489
+ mode="range"
490
+ selected={selection}
491
+ defaultMonth={selection?.from}
492
+ numberOfMonths={numberOfMonths}
493
+ onSelect={(next) => {
494
+ setSelection(next);
495
+ // Only once both ends exist. One click is half an answer, and
496
+ // applying it would show a zero-length range mid-gesture.
497
+ if (next?.from && next.to) {
498
+ setValue(absoluteExpression(startOfDay(next.from), endOfDay(next.to)));
499
+ close();
500
+ }
501
+ }}
502
+ />
503
+ </div>
504
+ );
505
+ }
506
+
507
+ function startOfDay(date: Date): Date {
508
+ const start = new Date(date);
509
+ start.setHours(0, 0, 0, 0);
510
+ return start;
511
+ }
512
+
513
+ /**
514
+ * A day picked as the end of a range means the whole of that day. Taking
515
+ * midnight instead silently drops the last day's data, which is the same
516
+ * off-by-one that `now/d..now/d` has to avoid.
517
+ */
518
+ function endOfDay(date: Date): Date {
519
+ const end = new Date(date);
520
+ end.setHours(23, 59, 59, 999);
521
+ return end;
522
+ }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from "./components/ai-agent-plan";
7
7
  export * from "./components/ai-agent-status";
8
8
  export * from "./components/ai-approval-request";
9
9
  export * from "./components/ai-conversation";
10
+ export * from "./components/ai-disclosure";
10
11
  export * from "./components/ai-message";
11
12
  export * from "./components/ai-model-selector";
12
13
  export * from "./components/ai-prompt-input";
@@ -28,6 +29,7 @@ export * from "./components/command";
28
29
  export * from "./components/data-table";
29
30
  export * from "./components/date-picker";
30
31
  export * from "./components/dialog";
32
+ export * from "./components/diff-viewer";
31
33
  export * from "./components/drawer";
32
34
  export * from "./components/dropdown-menu";
33
35
  export * from "./components/empty-state";
@@ -35,6 +37,7 @@ export * from "./components/file-upload";
35
37
  export * from "./components/form";
36
38
  export * from "./components/input";
37
39
  export * from "./components/label";
40
+ export * from "./components/log-viewer";
38
41
  export * from "./components/meter";
39
42
  export * from "./components/metric-delta";
40
43
  export * from "./components/record-diff";
@@ -52,6 +55,7 @@ export * from "./components/switch";
52
55
  export * from "./components/table";
53
56
  export * from "./components/tabs";
54
57
  export * from "./components/tags-input";
58
+ export * from "./components/time-range-picker";
55
59
  export * from "./components/toast";
56
60
  export * from "./components/tooltip";
57
61
 
@@ -7,6 +7,7 @@ import { meta as aiAgentPlanMeta } from "@/components/ai-agent-plan/meta";
7
7
  import { meta as aiAgentStatusMeta } from "@/components/ai-agent-status/meta";
8
8
  import { meta as aiApprovalRequestMeta } from "@/components/ai-approval-request/meta";
9
9
  import { meta as aiConversationMeta } from "@/components/ai-conversation/meta";
10
+ import { meta as aiDisclosureMeta } from "@/components/ai-disclosure/meta";
10
11
  import { meta as aiInlineCompletionMeta } from "@/components/ai-inline-completion/meta";
11
12
  import { meta as aiMessageMeta } from "@/components/ai-message/meta";
12
13
  import { meta as aiModelSelectorMeta } from "@/components/ai-model-selector/meta";
@@ -30,6 +31,7 @@ import { meta as commandMeta } from "@/components/command/meta";
30
31
  import { meta as dataTableMeta } from "@/components/data-table/meta";
31
32
  import { meta as datePickerMeta } from "@/components/date-picker/meta";
32
33
  import { meta as dialogMeta } from "@/components/dialog/meta";
34
+ import { meta as diffViewerMeta } from "@/components/diff-viewer/meta";
33
35
  import { meta as drawerMeta } from "@/components/drawer/meta";
34
36
  import { meta as dropdownMenuMeta } from "@/components/dropdown-menu/meta";
35
37
  import { meta as emptyStateMeta } from "@/components/empty-state/meta";
@@ -37,6 +39,7 @@ import { meta as fileUploadMeta } from "@/components/file-upload/meta";
37
39
  import { meta as formMeta } from "@/components/form/meta";
38
40
  import { meta as inputMeta } from "@/components/input/meta";
39
41
  import { meta as labelMeta } from "@/components/label/meta";
42
+ import { meta as logViewerMeta } from "@/components/log-viewer/meta";
40
43
  import { meta as meterMeta } from "@/components/meter/meta";
41
44
  import { meta as metricDeltaMeta } from "@/components/metric-delta/meta";
42
45
  import { meta as paginationMeta } from "@/components/pagination/meta";
@@ -54,6 +57,7 @@ import { meta as switchMeta } from "@/components/switch/meta";
54
57
  import { meta as tableMeta } from "@/components/table/meta";
55
58
  import { meta as tabsMeta } from "@/components/tabs/meta";
56
59
  import { meta as tagsInputMeta } from "@/components/tags-input/meta";
60
+ import { meta as timeRangePickerMeta } from "@/components/time-range-picker/meta";
57
61
  import { meta as toastMeta } from "@/components/toast/meta";
58
62
  import { meta as tooltipMeta } from "@/components/tooltip/meta";
59
63
 
@@ -76,6 +80,7 @@ export const componentMetas: ComponentMeta[] = [
76
80
  aiAgentStatusMeta,
77
81
  aiApprovalRequestMeta,
78
82
  aiConversationMeta,
83
+ aiDisclosureMeta,
79
84
  aiInlineCompletionMeta,
80
85
  aiMessageMeta,
81
86
  aiModelSelectorMeta,
@@ -99,6 +104,7 @@ export const componentMetas: ComponentMeta[] = [
99
104
  dataTableMeta,
100
105
  datePickerMeta,
101
106
  dialogMeta,
107
+ diffViewerMeta,
102
108
  drawerMeta,
103
109
  dropdownMenuMeta,
104
110
  emptyStateMeta,
@@ -106,6 +112,7 @@ export const componentMetas: ComponentMeta[] = [
106
112
  formMeta,
107
113
  inputMeta,
108
114
  labelMeta,
115
+ logViewerMeta,
109
116
  meterMeta,
110
117
  metricDeltaMeta,
111
118
  paginationMeta,
@@ -123,6 +130,7 @@ export const componentMetas: ComponentMeta[] = [
123
130
  tableMeta,
124
131
  tabsMeta,
125
132
  tagsInputMeta,
133
+ timeRangePickerMeta,
126
134
  toastMeta,
127
135
  tooltipMeta,
128
136
  ];