@signal9/era-ui 23.3.0 → 24.0.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 +16 -0
- package/dist/ai/index.d.ts +0 -1
- package/dist/ai/index.js +0 -1
- package/dist/dev/audit/audits/clipped-glyphs.js +18 -0
- package/dist/ui/command-bar/command-bar.svelte +9 -2
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +1 -0
- package/dist/{ai → ui}/timeline/context.svelte.d.ts +10 -1
- package/dist/{ai → ui}/timeline/context.svelte.js +11 -2
- package/dist/{ai → ui}/timeline/timeline-step.svelte +6 -0
- package/dist/{ai → ui}/timeline/timeline.svelte +7 -3
- package/package.json +1 -1
- /package/dist/{ai → ui}/timeline/index.d.ts +0 -0
- /package/dist/{ai → ui}/timeline/index.js +0 -0
- /package/dist/{ai → ui}/timeline/timeline-step.svelte.d.ts +0 -0
- /package/dist/{ai → ui}/timeline/timeline.svelte.d.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [24.0.0](https://github.com/sig-nine/era-ui/compare/v23.3.0...v24.0.0) (2026-08-11)
|
|
2
|
+
|
|
3
|
+
### ⚠ BREAKING CHANGES
|
|
4
|
+
|
|
5
|
+
* **timeline:** `Timeline` moves from `@sig-nine/era-ui/ai` to
|
|
6
|
+
`@sig-nine/era-ui/timeline`. `Todo` is unchanged at `@sig-nine/era-ui/ai`. The
|
|
7
|
+
`/agent-progress` docs route is now `/timeline` and `/todo`. Verified against
|
|
8
|
+
the built artifact: dist/ui/timeline/ resolves, dist/ai/timeline/ is gone, and
|
|
9
|
+
the ai barrel exports Todo and not Timeline.
|
|
10
|
+
|
|
11
|
+
toggle-group also leaves DEMO_OPTIONAL — it has a real demo page now.
|
|
12
|
+
|
|
13
|
+
### Code Refactoring
|
|
14
|
+
|
|
15
|
+
* **timeline:** Timeline is a ui primitive, not an AI one ([8093e9d](https://github.com/sig-nine/era-ui/commit/8093e9dac6133b180f5290827e124a2309c6adbd))
|
|
16
|
+
|
|
1
17
|
## [23.3.0](https://github.com/sig-nine/era-ui/compare/v23.2.0...v23.3.0) (2026-08-10)
|
|
2
18
|
|
|
3
19
|
### Features
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ export * as Actions from './actions/index.js';
|
|
|
10
10
|
export * as Artifact from './artifact/index.js';
|
|
11
11
|
export * as Plan from './plan/index.js';
|
|
12
12
|
export * as Todo from './todo/index.js';
|
|
13
|
-
export * as Timeline from './timeline/index.js';
|
|
14
13
|
export * as Queue from './queue/index.js';
|
|
15
14
|
export * as Confirmation from './confirmation/index.js';
|
|
16
15
|
export * as InlineCitation from './inline-citation/index.js';
|
package/dist/ai/index.js
CHANGED
|
@@ -15,7 +15,6 @@ export * as Actions from './actions/index.js';
|
|
|
15
15
|
export * as Artifact from './artifact/index.js';
|
|
16
16
|
export * as Plan from './plan/index.js';
|
|
17
17
|
export * as Todo from './todo/index.js';
|
|
18
|
-
export * as Timeline from './timeline/index.js';
|
|
19
18
|
export * as Queue from './queue/index.js';
|
|
20
19
|
export * as Confirmation from './confirmation/index.js';
|
|
21
20
|
export * as InlineCitation from './inline-citation/index.js';
|
|
@@ -36,6 +36,24 @@ export const clippedGlyphs = {
|
|
|
36
36
|
const s = getComputedStyle(el);
|
|
37
37
|
if (!/hidden|clip|auto|scroll/.test(s.overflowX + s.overflowY))
|
|
38
38
|
return null;
|
|
39
|
+
/*
|
|
40
|
+
* VISUALLY HIDDEN TEXT IS SUPPOSED TO BE CLIPPED — that is the technique.
|
|
41
|
+
* `sr-only` is a 1x1 absolutely-positioned box with overflow:hidden, which
|
|
42
|
+
* is precisely the shape this rule hunts, so it reports every one of them
|
|
43
|
+
* as "a 1px box holding 16px text" and is right about the geometry and
|
|
44
|
+
* wrong about the defect.
|
|
45
|
+
*
|
|
46
|
+
* It had never fired because era's only sr-only spans were conditional
|
|
47
|
+
* (Button's loading label) and the board never rendered one. The first
|
|
48
|
+
* component to announce state unconditionally lit up four errors.
|
|
49
|
+
*
|
|
50
|
+
* Detected by SHAPE, not by the class name: a consumer's own
|
|
51
|
+
* visually-hidden helper is the same technique under a different name, and
|
|
52
|
+
* matching on `.sr-only` would flag theirs while excusing ours.
|
|
53
|
+
*/
|
|
54
|
+
const r = el.getBoundingClientRect();
|
|
55
|
+
if (s.position === 'absolute' && r.width <= 1.5 && r.height <= 1.5)
|
|
56
|
+
return null;
|
|
39
57
|
const fontSize = parseFloat(s.fontSize) || 14;
|
|
40
58
|
const h = el.getBoundingClientRect().height;
|
|
41
59
|
if (h === 0)
|
|
@@ -187,9 +187,16 @@
|
|
|
187
187
|
<!-- The terminal command bar: a bare prompt line, not a floating palette. The
|
|
188
188
|
dropdown anchors under it with no radius (a full-width sheet, like the shell
|
|
189
189
|
it mirrors), but its chrome is surface-adaptive via --era-shadow-lg +
|
|
190
|
-
glass-blur: a
|
|
190
|
+
glass-blur: a 1px hairline at the divider colour on flat, the chisel frame
|
|
191
191
|
on bevel, frost on glass — so it reads as "dedicated" on every surface, not
|
|
192
|
-
an undifferentiated rectangle.
|
|
192
|
+
an undifferentiated rectangle.
|
|
193
|
+
|
|
194
|
+
This said "a flat sheet on flat (shadow resolves to none)" until flat's
|
|
195
|
+
--era-shadow-lg stopped resolving to none. It is a RING (0 0 0 1px), not a
|
|
196
|
+
border, so it costs no layout — but it is visible, and describing it as
|
|
197
|
+
nothing is how a reader concludes a real edge is a bug. Note the token: the
|
|
198
|
+
resting --era-shadow IS still none on flat, and that is the one every
|
|
199
|
+
comment about a flat control's lack of chrome refers to. -->
|
|
193
200
|
<div class={cn('relative flex h-control w-full min-w-0 items-center font-mono', className)}>
|
|
194
201
|
<ChevronRight class="mx-gutter size-icon shrink-0 text-fg" />
|
|
195
202
|
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export * as TimeField from './time-field/index.js';
|
|
|
40
40
|
export * as TimeRangeField from './time-range-field/index.js';
|
|
41
41
|
export * as Tooltip from './tooltip/index.js';
|
|
42
42
|
export * as ToggleGroup from './toggle-group/index.js';
|
|
43
|
+
export * as Timeline from './timeline/index.js';
|
|
43
44
|
export * as Toolbar from './toolbar/index.js';
|
|
44
45
|
export { Bar } from './bar/index.js';
|
|
45
46
|
export { Button } from './button/index.js';
|
package/dist/ui/index.js
CHANGED
|
@@ -40,6 +40,7 @@ export * as TimeField from './time-field/index.js';
|
|
|
40
40
|
export * as TimeRangeField from './time-range-field/index.js';
|
|
41
41
|
export * as Tooltip from './tooltip/index.js';
|
|
42
42
|
export * as ToggleGroup from './toggle-group/index.js';
|
|
43
|
+
export * as Timeline from './timeline/index.js';
|
|
43
44
|
export * as Toolbar from './toolbar/index.js';
|
|
44
45
|
export { Bar } from './bar/index.js';
|
|
45
46
|
export { Button } from './button/index.js';
|
|
@@ -29,7 +29,16 @@ export declare class TimelineData {
|
|
|
29
29
|
steps: TimelineStep[];
|
|
30
30
|
/** Live clock, ticked by the root while any step is running. */
|
|
31
31
|
now: number;
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* A step's elapsed ms: its final duration, or live if still running.
|
|
34
|
+
*
|
|
35
|
+
* `now === 0` means the clock never started — `tick={0}`, or a server render.
|
|
36
|
+
* A running step then falls back to its given `durationMs` instead of
|
|
37
|
+
* measuring against the wall clock, because measuring against a clock that
|
|
38
|
+
* was never started is how a frozen board rendered "1440215m 53s": the step's
|
|
39
|
+
* startedAt was a fixed epoch, and Date.now() minus a 2023 timestamp is a
|
|
40
|
+
* thousand days. A frozen timeline should show the durations it was handed.
|
|
41
|
+
*/
|
|
33
42
|
elapsed(step: TimelineStep): number;
|
|
34
43
|
get total(): number;
|
|
35
44
|
get running(): boolean;
|
|
@@ -20,9 +20,18 @@ export class TimelineData {
|
|
|
20
20
|
steps = $state([]);
|
|
21
21
|
/** Live clock, ticked by the root while any step is running. */
|
|
22
22
|
now = $state(0);
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* A step's elapsed ms: its final duration, or live if still running.
|
|
25
|
+
*
|
|
26
|
+
* `now === 0` means the clock never started — `tick={0}`, or a server render.
|
|
27
|
+
* A running step then falls back to its given `durationMs` instead of
|
|
28
|
+
* measuring against the wall clock, because measuring against a clock that
|
|
29
|
+
* was never started is how a frozen board rendered "1440215m 53s": the step's
|
|
30
|
+
* startedAt was a fixed epoch, and Date.now() minus a 2023 timestamp is a
|
|
31
|
+
* thousand days. A frozen timeline should show the durations it was handed.
|
|
32
|
+
*/
|
|
24
33
|
elapsed(step) {
|
|
25
|
-
if (step.state === 'running' && step.startedAt) {
|
|
34
|
+
if (step.state === 'running' && step.startedAt && this.now > 0) {
|
|
26
35
|
return Math.max(0, this.now - step.startedAt);
|
|
27
36
|
}
|
|
28
37
|
return step.durationMs ?? 0;
|
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
WHAT, the bar's width is HOW LONG relative to the rest, and the caption is
|
|
48
48
|
WHEN in wall-clock, which is the one you need when correlating against logs.
|
|
49
49
|
|
|
50
|
+
role="listitem" sits on the step itself rather than on a wrapper. The wrapper
|
|
51
|
+
was `display: contents` — exactly a box that ISN'T — so anything measuring
|
|
52
|
+
this row read zeros from it (layout/wall-matches-gap reported a −48px wall).
|
|
53
|
+
A listitem outside a list is ignored, so a Step used standalone is fine.
|
|
54
|
+
|
|
50
55
|
flex-grow IS the duration, with a zero basis, so width is strictly
|
|
51
56
|
proportional. The min-width keeps a 200ms step readable beside a 60s one —
|
|
52
57
|
flexbox reserves it before distributing the rest, so the short step stays
|
|
@@ -60,6 +65,7 @@
|
|
|
60
65
|
fits `16:20:14.538`.
|
|
61
66
|
-->
|
|
62
67
|
<div
|
|
68
|
+
role="listitem"
|
|
63
69
|
class={cn('flex flex-col gap-gutter', showClock ? 'min-w-[12ch]' : 'min-w-[7ch]')}
|
|
64
70
|
style="flex: {grow} 1 0"
|
|
65
71
|
>
|
|
@@ -41,8 +41,12 @@
|
|
|
41
41
|
if (!shouldRun) {
|
|
42
42
|
if (timer) clearInterval(timer);
|
|
43
43
|
timer = null;
|
|
44
|
-
// Settle on a final reading
|
|
45
|
-
|
|
44
|
+
// Settle on a final reading ONLY if the clock was ever running. With
|
|
45
|
+
// tick={0} — documented as "the counter freezes" — writing Date.now()
|
|
46
|
+
// here is the opposite of freezing: it hands every running step the
|
|
47
|
+
// wall clock to measure against, and a step whose startedAt is a fixed
|
|
48
|
+
// epoch reads a thousand days.
|
|
49
|
+
if (tick > 0) data.now = Date.now();
|
|
46
50
|
return;
|
|
47
51
|
}
|
|
48
52
|
data.now = Date.now();
|
|
@@ -78,7 +82,7 @@
|
|
|
78
82
|
{@render children()}
|
|
79
83
|
{:else}
|
|
80
84
|
{#each data.steps as step, i (step.id ?? i)}
|
|
81
|
-
<
|
|
85
|
+
<Step {step} {showClock} />
|
|
82
86
|
{/each}
|
|
83
87
|
{/if}
|
|
84
88
|
</div>
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|