@signal9/era-ui 23.1.0 → 23.2.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 (34) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/ai/index.d.ts +2 -0
  3. package/dist/ai/index.js +2 -0
  4. package/dist/ai/timeline/context.svelte.d.ts +67 -0
  5. package/dist/ai/timeline/context.svelte.js +99 -0
  6. package/dist/ai/timeline/index.d.ts +3 -0
  7. package/dist/ai/timeline/index.js +3 -0
  8. package/dist/ai/timeline/timeline-step.svelte +98 -0
  9. package/dist/ai/timeline/timeline-step.svelte.d.ts +9 -0
  10. package/dist/ai/timeline/timeline.svelte +84 -0
  11. package/dist/ai/timeline/timeline.svelte.d.ts +13 -0
  12. package/dist/ai/todo/context.svelte.d.ts +43 -0
  13. package/dist/ai/todo/context.svelte.js +53 -0
  14. package/dist/ai/todo/index.d.ts +5 -0
  15. package/dist/ai/todo/index.js +5 -0
  16. package/dist/ai/todo/todo-header.svelte +56 -0
  17. package/dist/ai/todo/todo-header.svelte.d.ts +9 -0
  18. package/dist/ai/todo/todo-item.svelte +97 -0
  19. package/dist/ai/todo/todo-item.svelte.d.ts +10 -0
  20. package/dist/ai/todo/todo-list.svelte +40 -0
  21. package/dist/ai/todo/todo-list.svelte.d.ts +10 -0
  22. package/dist/ai/todo/todo.svelte +49 -0
  23. package/dist/ai/todo/todo.svelte.d.ts +12 -0
  24. package/dist/era-ui.css +1 -1
  25. package/dist/eslint/index.js +3 -0
  26. package/dist/ui/avatar/avatar-fallback.svelte +4 -4
  27. package/dist/ui/calendar/calendar-day.svelte +8 -1
  28. package/dist/ui/date-field/date-field-segment.svelte +11 -1
  29. package/dist/ui/date-picker/date-picker-segment.svelte +11 -1
  30. package/dist/ui/date-range-field/date-range-field-segment.svelte +11 -1
  31. package/dist/ui/date-range-picker/date-range-picker-segment.svelte +11 -1
  32. package/dist/ui/time-field/time-field-segment.svelte +11 -1
  33. package/dist/ui/time-range-field/time-range-field-segment.svelte +11 -1
  34. package/package.json +1 -1
@@ -0,0 +1,97 @@
1
+ <script lang="ts" module>
2
+ import Circle from '@lucide/svelte/icons/circle';
3
+ import CircleCheck from '@lucide/svelte/icons/circle-check';
4
+ import CircleSlash from '@lucide/svelte/icons/circle-slash-2';
5
+ import LoaderCircle from '@lucide/svelte/icons/loader-circle';
6
+ import type { Component } from 'svelte';
7
+ import type { IconProps } from '@lucide/svelte';
8
+ import type { TodoStatus } from './context.svelte.js';
9
+
10
+ /*
11
+ * GLYPHS, NOT UNICODE. The obvious implementation writes ○ ◐ ● ⊘ into a
12
+ * span, and it is what agent TUIs do because a terminal has nothing else.
13
+ * In a browser it goes wrong three ways: the four characters come from
14
+ * different Unicode blocks with different metrics, so they do not sit on one
15
+ * optical line; none of them respond to `size-icon`, so they do not scale
16
+ * with the density axis; and a screen reader reads them aloud as their
17
+ * character names. Lucide glyphs are one family, take the tier, and are
18
+ * aria-hidden with the status carried in real text.
19
+ */
20
+ const GLYPH: Record<TodoStatus, Component<IconProps>> = {
21
+ pending: Circle,
22
+ in_progress: LoaderCircle,
23
+ completed: CircleCheck,
24
+ cancelled: CircleSlash
25
+ };
26
+
27
+ /** Semantic tokens, never a palette literal — these follow theme and surface. */
28
+ const TONE: Record<TodoStatus, { icon: string; text: string }> = {
29
+ pending: { icon: 'text-muted', text: 'text-muted' },
30
+ in_progress: { icon: 'text-primary', text: 'text-bright' },
31
+ completed: { icon: 'text-success', text: 'text-fg' },
32
+ cancelled: { icon: 'text-muted', text: 'text-muted line-through' }
33
+ };
34
+
35
+ /** What a screen reader hears; the glyph is decorative. */
36
+ const LABEL: Record<TodoStatus, string> = {
37
+ pending: 'to do',
38
+ in_progress: 'in progress',
39
+ completed: 'completed',
40
+ cancelled: 'cancelled'
41
+ };
42
+ </script>
43
+
44
+ <script lang="ts">
45
+ import type { HTMLLiAttributes } from 'svelte/elements';
46
+ import { cn } from '../../utils/index.js';
47
+
48
+ let {
49
+ status = 'pending',
50
+ content,
51
+ class: className,
52
+ ...restProps
53
+ }: HTMLLiAttributes & {
54
+ status?: TodoStatus;
55
+ content?: string;
56
+ } = $props();
57
+
58
+ const Icon = $derived(GLYPH[status]);
59
+ const tone = $derived(TONE[status]);
60
+ </script>
61
+
62
+ <!--
63
+ ONE FACT, AND IT IS THE ACCESSIBLE ONE — the same rule Nav.Item follows.
64
+ `aria-current="step"` marks the item being worked on, and it is what a screen
65
+ reader announces when it reaches this row. `data-status` carries the full
66
+ four-way state for styling and for a consumer to key off. Neither is a
67
+ styling flag the caller sets separately, so the row cannot look active while
68
+ announcing nothing.
69
+
70
+ The row is on the control tier with the concentric icon inset, so a plan
71
+ nests inside a panel the same way every other era list does.
72
+ -->
73
+ <li
74
+ data-status={status}
75
+ aria-current={status === 'in_progress' ? 'step' : undefined}
76
+ class={cn(
77
+ 'flex h-control items-center gap-inset-control rounded-control px-inset-control text-body',
78
+ tone.text,
79
+ className
80
+ )}
81
+ {...restProps}
82
+ >
83
+ <Icon
84
+ class={cn(
85
+ 'size-icon shrink-0',
86
+ tone.icon,
87
+ // The spinner is the ONE thing here that moves, and it takes the motion
88
+ // axis rather than a literal — at data-motion="instant", which is also
89
+ // what prefers-reduced-motion resolves to, it holds still.
90
+ status === 'in_progress' &&
91
+ '[animation-duration:calc(var(--era-duration)*8)] motion-safe:animate-spin'
92
+ )}
93
+ aria-hidden="true"
94
+ />
95
+ <span class="min-w-0 flex-1 truncate">{content}</span>
96
+ <span class="sr-only">{LABEL[status]}</span>
97
+ </li>
@@ -0,0 +1,10 @@
1
+ import type { Component } from 'svelte';
2
+ import type { TodoStatus } from './context.svelte.js';
3
+ import type { HTMLLiAttributes } from 'svelte/elements';
4
+ type $$ComponentProps = HTMLLiAttributes & {
5
+ status?: TodoStatus;
6
+ content?: string;
7
+ };
8
+ declare const TodoItem: Component<$$ComponentProps, {}, "">;
9
+ type TodoItem = ReturnType<typeof TodoItem>;
10
+ export default TodoItem;
@@ -0,0 +1,40 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { Collapsible } from 'bits-ui';
4
+ import { cn } from '../../utils/index.js';
5
+ import { getTodoState, type TodoItem } from './context.svelte.js';
6
+ import TodoItemRow from './todo-item.svelte';
7
+
8
+ let {
9
+ item,
10
+ class: className
11
+ }: {
12
+ /** Render your own row. Omitted, the default row is used. */
13
+ item?: Snippet<[TodoItem, number]>;
14
+ class?: string;
15
+ } = $props();
16
+
17
+ const state = getTodoState();
18
+ </script>
19
+
20
+ <Collapsible.Content class="overflow-hidden">
21
+ <!--
22
+ <ol>, not a stack of divs. A plan is an ordered thing: a screen reader
23
+ should say "list, 7 items, item 3 of 7", and that is free from the right
24
+ element and impossible to retrofit onto divs with ARIA without restating
25
+ every index by hand.
26
+
27
+ max-h + overflow-y with scrollbar-none: a long plan must not push the
28
+ conversation off screen, and era hides the bar because a scrollbar on one
29
+ panel and not another reads as a defect.
30
+ -->
31
+ <ol class={cn('scrollbar-none flex max-h-64 flex-col overflow-y-auto pt-gutter', className)}>
32
+ {#each state?.todos ?? [] as todo, i (todo.id ?? i)}
33
+ {#if item}
34
+ {@render item(todo, i)}
35
+ {:else}
36
+ <TodoItemRow status={todo.status} content={todo.content} />
37
+ {/if}
38
+ {/each}
39
+ </ol>
40
+ </Collapsible.Content>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type TodoItem } from './context.svelte.js';
3
+ type $$ComponentProps = {
4
+ /** Render your own row. Omitted, the default row is used. */
5
+ item?: Snippet<[TodoItem, number]>;
6
+ class?: string;
7
+ };
8
+ declare const TodoList: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type TodoList = ReturnType<typeof TodoList>;
10
+ export default TodoList;
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { Collapsible } from 'bits-ui';
4
+ import { cn } from '../../utils/index.js';
5
+ import { TodoState, setTodoState, type TodoItem } from './context.svelte.js';
6
+
7
+ let {
8
+ todos = [],
9
+ open = $bindable(true),
10
+ children,
11
+ class: className
12
+ }: {
13
+ /** The plan. Root owns it; Header and List both read THIS array. */
14
+ todos?: TodoItem[];
15
+ open?: boolean;
16
+ children?: Snippet;
17
+ class?: string;
18
+ } = $props();
19
+
20
+ const state = setTodoState(new TodoState());
21
+ $effect(() => {
22
+ state.todos = todos;
23
+ });
24
+ </script>
25
+
26
+ <!--
27
+ A plan board: a collapsible panel of the agent's own to-do list.
28
+ Ported from term's TodoBoard, which is the shape that works — a collapsed
29
+ header that still shows what is being worked on is the reason it can live
30
+ permanently above a chat without costing anything.
31
+
32
+ What changed on the way in, and why:
33
+ - Palette literals (text-sky-300, text-green-400, bg-9/5) became semantic
34
+ tokens, so the board follows theme, surface and mode instead of pinning
35
+ one dark palette.
36
+ - Fixed pixel rows (h-6, text-[10px]) became tier heights, so it scales
37
+ with the density axis like everything else.
38
+ - The list became an <ol> of <li>, so it announces as a list with a
39
+ position — a plan is an ORDERED thing and the DOM should say so.
40
+ - The counts moved into context, so the header cannot disagree with the
41
+ rows underneath it.
42
+ -->
43
+ <Collapsible.Root
44
+ bind:open
45
+ data-complete={state.complete || undefined}
46
+ class={cn('flex flex-col rounded-bar bg-well p-panel shadow-sm', className)}
47
+ >
48
+ {@render children?.()}
49
+ </Collapsible.Root>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type TodoItem } from './context.svelte.js';
3
+ type $$ComponentProps = {
4
+ /** The plan. Root owns it; Header and List both read THIS array. */
5
+ todos?: TodoItem[];
6
+ open?: boolean;
7
+ children?: Snippet;
8
+ class?: string;
9
+ };
10
+ declare const Todo: import("svelte").Component<$$ComponentProps, {}, "open">;
11
+ type Todo = ReturnType<typeof Todo>;
12
+ export default Todo;