@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.
- package/CHANGELOG.md +14 -0
- package/dist/ai/index.d.ts +2 -0
- package/dist/ai/index.js +2 -0
- package/dist/ai/timeline/context.svelte.d.ts +67 -0
- package/dist/ai/timeline/context.svelte.js +99 -0
- package/dist/ai/timeline/index.d.ts +3 -0
- package/dist/ai/timeline/index.js +3 -0
- package/dist/ai/timeline/timeline-step.svelte +98 -0
- package/dist/ai/timeline/timeline-step.svelte.d.ts +9 -0
- package/dist/ai/timeline/timeline.svelte +84 -0
- package/dist/ai/timeline/timeline.svelte.d.ts +13 -0
- package/dist/ai/todo/context.svelte.d.ts +43 -0
- package/dist/ai/todo/context.svelte.js +53 -0
- package/dist/ai/todo/index.d.ts +5 -0
- package/dist/ai/todo/index.js +5 -0
- package/dist/ai/todo/todo-header.svelte +56 -0
- package/dist/ai/todo/todo-header.svelte.d.ts +9 -0
- package/dist/ai/todo/todo-item.svelte +97 -0
- package/dist/ai/todo/todo-item.svelte.d.ts +10 -0
- package/dist/ai/todo/todo-list.svelte +40 -0
- package/dist/ai/todo/todo-list.svelte.d.ts +10 -0
- package/dist/ai/todo/todo.svelte +49 -0
- package/dist/ai/todo/todo.svelte.d.ts +12 -0
- package/dist/era-ui.css +1 -1
- package/dist/eslint/index.js +3 -0
- package/dist/ui/avatar/avatar-fallback.svelte +4 -4
- package/dist/ui/calendar/calendar-day.svelte +8 -1
- package/dist/ui/date-field/date-field-segment.svelte +11 -1
- package/dist/ui/date-picker/date-picker-segment.svelte +11 -1
- package/dist/ui/date-range-field/date-range-field-segment.svelte +11 -1
- package/dist/ui/date-range-picker/date-range-picker-segment.svelte +11 -1
- package/dist/ui/time-field/time-field-segment.svelte +11 -1
- package/dist/ui/time-range-field/time-range-field-segment.svelte +11 -1
- 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;
|