@signal9/era-ui 4.6.1 → 4.7.1
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/dist/ai/error-panel/error-panel.svelte +1 -30
- package/dist/ai/index.d.ts +1 -0
- package/dist/ai/index.js +1 -0
- package/dist/ai/runtime-feed/runtime-feed-item.svelte +4 -1
- package/dist/ai/swarm/context.d.ts +26 -0
- package/dist/ai/swarm/context.js +14 -0
- package/dist/ai/swarm/index.d.ts +16 -0
- package/dist/ai/swarm/index.js +15 -0
- package/dist/ai/swarm/swarm-agent.svelte +132 -0
- package/dist/ai/swarm/swarm-agent.svelte.d.ts +16 -0
- package/dist/ai/swarm/swarm-artifact.svelte +45 -0
- package/dist/ai/swarm/swarm-artifact.svelte.d.ts +8 -0
- package/dist/ai/swarm/swarm-artifacts.svelte +42 -0
- package/dist/ai/swarm/swarm-artifacts.svelte.d.ts +15 -0
- package/dist/ai/swarm/swarm-barrier.svelte +63 -0
- package/dist/ai/swarm/swarm-barrier.svelte.d.ts +10 -0
- package/dist/ai/swarm/swarm-counts.svelte +52 -0
- package/dist/ai/swarm/swarm-counts.svelte.d.ts +13 -0
- package/dist/ai/swarm/swarm-dot.svelte +52 -0
- package/dist/ai/swarm/swarm-dot.svelte.d.ts +23 -0
- package/dist/ai/swarm/swarm-footer.svelte +63 -0
- package/dist/ai/swarm/swarm-footer.svelte.d.ts +16 -0
- package/dist/ai/swarm/swarm-grid.svelte +51 -0
- package/dist/ai/swarm/swarm-grid.svelte.d.ts +14 -0
- package/dist/ai/swarm/swarm-header.svelte +64 -0
- package/dist/ai/swarm/swarm-header.svelte.d.ts +14 -0
- package/dist/ai/swarm/swarm-node.svelte +258 -0
- package/dist/ai/swarm/swarm-node.svelte.d.ts +32 -0
- package/dist/ai/swarm/swarm-role.svelte +71 -0
- package/dist/ai/swarm/swarm-role.svelte.d.ts +17 -0
- package/dist/ai/swarm/swarm-root.svelte +55 -0
- package/dist/ai/swarm/swarm-root.svelte.d.ts +16 -0
- package/dist/ai/swarm/swarm-tree.svelte +201 -0
- package/dist/ai/swarm/swarm-tree.svelte.d.ts +25 -0
- package/dist/ai/swarm/swarm.d.ts +218 -0
- package/dist/ai/swarm/swarm.js +187 -0
- package/dist/ai/swarm/swarm.svelte.d.ts +59 -0
- package/dist/ai/swarm/swarm.svelte.js +125 -0
- package/dist/apps/llm-shell/index.d.ts +1 -1
- package/dist/apps/llm-shell/llm-shell.svelte +65 -1
- package/dist/apps/llm-shell/llm-shell.svelte.d.ts +7 -0
- package/dist/apps/llm-shell/types.d.ts +22 -0
- package/dist/apps/notes/notes.svelte +10 -5
- package/dist/era-ui.css +1 -1
- package/dist/generated-docs/llm-shell.md +1 -1
- package/dist/generated-docs/llms-full.txt +36 -1
- package/dist/generated-docs/llms.txt +1 -0
- package/dist/generated-docs/manifest.json +14 -3
- package/dist/generated-docs/utilities.json +10 -0
- package/dist/generated-docs/utilities.md +34 -0
- package/dist/styles/index.css +47 -0
- package/dist/styles/themes.css +27 -15
- package/dist/ui/step/step-content.svelte +5 -34
- package/package.json +1 -1
|
@@ -92,39 +92,10 @@
|
|
|
92
92
|
{/if}
|
|
93
93
|
|
|
94
94
|
{#if showDetail}
|
|
95
|
-
<Collapsible.Content
|
|
95
|
+
<Collapsible.Content class="era-collapse">
|
|
96
96
|
<div class="pt-(--era-gap)">
|
|
97
97
|
<CodeBlock code={rawDetail} />
|
|
98
98
|
</div>
|
|
99
99
|
</Collapsible.Content>
|
|
100
100
|
{/if}
|
|
101
101
|
</Collapsible.Root>
|
|
102
|
-
|
|
103
|
-
<style>
|
|
104
|
-
:global([data-era-error-detail][data-state='open']) {
|
|
105
|
-
animation: era-error-expand var(--era-duration) var(--era-ease);
|
|
106
|
-
}
|
|
107
|
-
:global([data-era-error-detail][data-state='closed']) {
|
|
108
|
-
animation: era-error-collapse var(--era-duration) var(--era-ease);
|
|
109
|
-
}
|
|
110
|
-
@keyframes -global-era-error-expand {
|
|
111
|
-
from {
|
|
112
|
-
height: 0;
|
|
113
|
-
opacity: 0;
|
|
114
|
-
}
|
|
115
|
-
to {
|
|
116
|
-
height: var(--bits-collapsible-content-height);
|
|
117
|
-
opacity: 1;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
@keyframes -global-era-error-collapse {
|
|
121
|
-
from {
|
|
122
|
-
height: var(--bits-collapsible-content-height);
|
|
123
|
-
opacity: 1;
|
|
124
|
-
}
|
|
125
|
-
to {
|
|
126
|
-
height: 0;
|
|
127
|
-
opacity: 0;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
</style>
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * as StructuredValue from './structured-value/index.js';
|
|
|
17
17
|
export * as RuntimeFeed from './runtime-feed/index.js';
|
|
18
18
|
export * as ErrorPanel from './error-panel/index.js';
|
|
19
19
|
export * as RunStatus from './run-status/index.js';
|
|
20
|
+
export * as Swarm from './swarm/index.js';
|
|
20
21
|
export { Response, repairStreamingMarkdown } from './response/index.js';
|
|
21
22
|
export { default as ContextGauge } from './context/context.svelte';
|
|
22
23
|
export { default as ModelSelector } from './model-selector/model-selector.svelte';
|
package/dist/ai/index.js
CHANGED
|
@@ -22,6 +22,7 @@ export * as StructuredValue from './structured-value/index.js';
|
|
|
22
22
|
export * as RuntimeFeed from './runtime-feed/index.js';
|
|
23
23
|
export * as ErrorPanel from './error-panel/index.js';
|
|
24
24
|
export * as RunStatus from './run-status/index.js';
|
|
25
|
+
export * as Swarm from './swarm/index.js';
|
|
25
26
|
export { Response, repairStreamingMarkdown } from './response/index.js';
|
|
26
27
|
export { default as ContextGauge } from './context/context.svelte';
|
|
27
28
|
export { default as ModelSelector } from './model-selector/model-selector.svelte';
|
|
@@ -54,7 +54,10 @@
|
|
|
54
54
|
class={cn('flex items-center gap-(--era-gap) text-body', TONE[tone], className)}
|
|
55
55
|
{...restProps}
|
|
56
56
|
>
|
|
57
|
-
|
|
57
|
+
<!-- size-2, the library's dot size (conversations row, run status, swarm) —
|
|
58
|
+
NOT a tier height: h-xxs is the pill tier (18-26px), which rendered this
|
|
59
|
+
as a bullet taller than the line it marks. -->
|
|
60
|
+
<span class="size-2 shrink-0 rounded-full bg-current" aria-hidden="true"></span>
|
|
58
61
|
<span class="min-w-0 flex-1 truncate">
|
|
59
62
|
{#if children}{@render children()}{:else}{text}{/if}
|
|
60
63
|
</span>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SwarmAgent } from './swarm.js';
|
|
2
|
+
/**
|
|
3
|
+
* What `Swarm.Root` shares with every part below it.
|
|
4
|
+
*
|
|
5
|
+
* The two callbacks are here rather than on the parts because a swarm is a
|
|
6
|
+
* TREE: a node six levels down would otherwise have to be handed the same
|
|
7
|
+
* `onOpen` through six intermediaries, and the recursion is generated by the
|
|
8
|
+
* Tree, not written by the consumer, so there is nowhere to thread it. Any part
|
|
9
|
+
* may still take its own handler and win over the context.
|
|
10
|
+
*/
|
|
11
|
+
export interface SwarmContext {
|
|
12
|
+
/**
|
|
13
|
+
* Epoch ms every elapsed readout measures against.
|
|
14
|
+
*
|
|
15
|
+
* The library never reads the clock itself — same input, same render (which
|
|
16
|
+
* is what makes it testable and SSR-safe). A host that ticks this in `$state`
|
|
17
|
+
* gets live counters on running agents for free.
|
|
18
|
+
*/
|
|
19
|
+
readonly now: number;
|
|
20
|
+
/** Open an agent's own transcript. Omitted = no open affordance. */
|
|
21
|
+
readonly onOpen?: (agent: SwarmAgent) => void;
|
|
22
|
+
/** Cancel an agent (the host decides whether that means its subtree too). */
|
|
23
|
+
readonly onCancel?: (agent: SwarmAgent) => void;
|
|
24
|
+
}
|
|
25
|
+
export declare function setSwarmContext(ctx: SwarmContext): void;
|
|
26
|
+
export declare function getSwarmContext(): SwarmContext;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
const KEY = Symbol('era-swarm');
|
|
3
|
+
/** Standalone parts (a `Swarm.Agent` card outside a Root) still need a clock. */
|
|
4
|
+
const FALLBACK = {
|
|
5
|
+
get now() {
|
|
6
|
+
return Date.now();
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
export function setSwarmContext(ctx) {
|
|
10
|
+
setContext(KEY, ctx);
|
|
11
|
+
}
|
|
12
|
+
export function getSwarmContext() {
|
|
13
|
+
return getContext(KEY) ?? FALLBACK;
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { default as Root } from './swarm-root.svelte';
|
|
2
|
+
export { default as Header } from './swarm-header.svelte';
|
|
3
|
+
export { default as Counts } from './swarm-counts.svelte';
|
|
4
|
+
export { default as Tree } from './swarm-tree.svelte';
|
|
5
|
+
export { default as Node } from './swarm-node.svelte';
|
|
6
|
+
export { default as Grid } from './swarm-grid.svelte';
|
|
7
|
+
export { default as Agent } from './swarm-agent.svelte';
|
|
8
|
+
export { default as Artifacts } from './swarm-artifacts.svelte';
|
|
9
|
+
export { default as Artifact } from './swarm-artifact.svelte';
|
|
10
|
+
export { default as Barrier } from './swarm-barrier.svelte';
|
|
11
|
+
export { default as Footer } from './swarm-footer.svelte';
|
|
12
|
+
export { default as Dot, SWARM_DOT } from './swarm-dot.svelte';
|
|
13
|
+
export { default as Role, SWARM_ROLE } from './swarm-role.svelte';
|
|
14
|
+
export { SwarmController } from './swarm.svelte.js';
|
|
15
|
+
export { buildSwarmTree, flattenSwarmTree, summarizeSwarm, summarizeBarrier, swarmElapsed, formatSwarmAge, formatSwarmDuration, type SwarmAgent, type SwarmAgentStatus, type SwarmArtifact, type SwarmBarrier, type SwarmBarrierStatus, type SwarmConnection, type SwarmCounts, type SwarmEvent, type SwarmNode, type SwarmPlan, type SwarmPlanStatus, type SwarmRole, type SwarmSandbox, type SwarmSnapshot } from './swarm.js';
|
|
16
|
+
export type { SwarmContext } from './context.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { default as Root } from './swarm-root.svelte';
|
|
2
|
+
export { default as Header } from './swarm-header.svelte';
|
|
3
|
+
export { default as Counts } from './swarm-counts.svelte';
|
|
4
|
+
export { default as Tree } from './swarm-tree.svelte';
|
|
5
|
+
export { default as Node } from './swarm-node.svelte';
|
|
6
|
+
export { default as Grid } from './swarm-grid.svelte';
|
|
7
|
+
export { default as Agent } from './swarm-agent.svelte';
|
|
8
|
+
export { default as Artifacts } from './swarm-artifacts.svelte';
|
|
9
|
+
export { default as Artifact } from './swarm-artifact.svelte';
|
|
10
|
+
export { default as Barrier } from './swarm-barrier.svelte';
|
|
11
|
+
export { default as Footer } from './swarm-footer.svelte';
|
|
12
|
+
export { default as Dot, SWARM_DOT } from './swarm-dot.svelte';
|
|
13
|
+
export { default as Role, SWARM_ROLE } from './swarm-role.svelte';
|
|
14
|
+
export { SwarmController } from './swarm.svelte.js';
|
|
15
|
+
export { buildSwarmTree, flattenSwarmTree, summarizeSwarm, summarizeBarrier, swarmElapsed, formatSwarmAge, formatSwarmDuration } from './swarm.js';
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import Box from '@lucide/svelte/icons/box';
|
|
5
|
+
import X from '@lucide/svelte/icons/x';
|
|
6
|
+
import PanelRightOpen from '@lucide/svelte/icons/panel-right-open';
|
|
7
|
+
import { Button } from '../../ui/button/index.js';
|
|
8
|
+
import { Card } from '../../ui/card/index.js';
|
|
9
|
+
import { cn } from '../../utils/index.js';
|
|
10
|
+
import { getSwarmContext } from './context.js';
|
|
11
|
+
import Dot from './swarm-dot.svelte';
|
|
12
|
+
import Role from './swarm-role.svelte';
|
|
13
|
+
import { formatSwarmDuration, swarmElapsed, type SwarmAgent } from './swarm.js';
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
agent,
|
|
17
|
+
depth,
|
|
18
|
+
children,
|
|
19
|
+
onOpen,
|
|
20
|
+
onCancel,
|
|
21
|
+
class: className,
|
|
22
|
+
...restProps
|
|
23
|
+
}: Omit<HTMLAttributes<HTMLElement>, 'children'> & {
|
|
24
|
+
agent: SwarmAgent;
|
|
25
|
+
/** Distance from the root, shown as a `d2` tag. `buildSwarmTree` computes it. */
|
|
26
|
+
depth?: number;
|
|
27
|
+
/** Extra card body under the result — a chip row, a mini feed. */
|
|
28
|
+
children?: Snippet<[SwarmAgent]>;
|
|
29
|
+
/** Override the Root's handlers for this card. */
|
|
30
|
+
onOpen?: (agent: SwarmAgent) => void;
|
|
31
|
+
onCancel?: (agent: SwarmAgent) => void;
|
|
32
|
+
} = $props();
|
|
33
|
+
|
|
34
|
+
const ctx = getSwarmContext();
|
|
35
|
+
const openAgent = $derived(onOpen ?? ctx.onOpen);
|
|
36
|
+
const cancelAgent = $derived(onCancel ?? ctx.onCancel);
|
|
37
|
+
const cancellable = $derived(
|
|
38
|
+
cancelAgent != null &&
|
|
39
|
+
(agent.status === 'running' || agent.status === 'queued' || agent.status === 'blocked')
|
|
40
|
+
);
|
|
41
|
+
// See `Swarm.Node`: the open affordance is gated on the agent actually having
|
|
42
|
+
// a transcript to open.
|
|
43
|
+
const openable = $derived(openAgent != null && agent.conversationId != null);
|
|
44
|
+
const elapsed = $derived(swarmElapsed(agent, ctx.now));
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<!-- One agent as a card, for the console grid — the same agent the tree renders
|
|
48
|
+
as a row, sized for scanning forty of them at once.
|
|
49
|
+
|
|
50
|
+
A well, not an elevated card: the grid sits INSIDE the swarm panel, and on
|
|
51
|
+
the flat surface `elevated` resolves to the same fill the panel already has
|
|
52
|
+
— forty invisible boxes. The recessed tier is the one that reads as content
|
|
53
|
+
put into a container on every surface.
|
|
54
|
+
|
|
55
|
+
The status dot and the sandbox chip stay SEPARATE signals: an agent can be
|
|
56
|
+
running with a terminated VM (reaped, or never created), and collapsing the
|
|
57
|
+
two into one indicator hides precisely the state you opened a console to
|
|
58
|
+
find. A failure rings the whole card, because in a grid of forty the one
|
|
59
|
+
that broke has to be findable without reading any of them. -->
|
|
60
|
+
<Card
|
|
61
|
+
tone="well"
|
|
62
|
+
data-status={agent.status}
|
|
63
|
+
class={cn(
|
|
64
|
+
'flex min-w-0 flex-col gap-(--era-gap) p-(--era-pad-sm)',
|
|
65
|
+
agent.status === 'failed' && 'outline -outline-offset-1 outline-destructive/60',
|
|
66
|
+
className
|
|
67
|
+
)}
|
|
68
|
+
{...restProps}
|
|
69
|
+
>
|
|
70
|
+
<div class="flex min-w-0 items-center gap-(--era-gap) text-body era-text-trim">
|
|
71
|
+
<Dot status={agent.status} />
|
|
72
|
+
<Role role={agent.role ?? 'worker'} label={agent.label} class="min-w-0" />
|
|
73
|
+
{#if agent.key}
|
|
74
|
+
<span class="shrink-0 font-mono text-body text-muted">#{agent.key}</span>
|
|
75
|
+
{/if}
|
|
76
|
+
<span class="flex-1"></span>
|
|
77
|
+
{#if depth != null}
|
|
78
|
+
<span class="shrink-0 font-mono text-[0.625rem] text-muted tabular-nums">d{depth}</span>
|
|
79
|
+
{/if}
|
|
80
|
+
{#if cancellable}
|
|
81
|
+
<Button
|
|
82
|
+
icon
|
|
83
|
+
size="xxs"
|
|
84
|
+
tone="destructive"
|
|
85
|
+
aria-label="Cancel {agent.task}"
|
|
86
|
+
onclick={() => cancelAgent?.(agent)}
|
|
87
|
+
>
|
|
88
|
+
<X class="size-(--era-h-xs)" />
|
|
89
|
+
</Button>
|
|
90
|
+
{/if}
|
|
91
|
+
{#if openable}
|
|
92
|
+
<Button icon size="xxs" aria-label="Open {agent.task}" onclick={() => openAgent?.(agent)}>
|
|
93
|
+
<PanelRightOpen class="size-(--era-h-xs)" />
|
|
94
|
+
</Button>
|
|
95
|
+
{/if}
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- Two lines of task, one of result: enough to tell two workers apart,
|
|
99
|
+
bounded so one verbose subtask can't make its card twice the height of
|
|
100
|
+
its neighbours and shred the grid. -->
|
|
101
|
+
<span class="line-clamp-2 text-body text-fg" title={agent.task}>{agent.task}</span>
|
|
102
|
+
{#if agent.result}
|
|
103
|
+
<span class="line-clamp-1 text-body text-muted" title={agent.result}>{agent.result}</span>
|
|
104
|
+
{/if}
|
|
105
|
+
|
|
106
|
+
{@render children?.(agent)}
|
|
107
|
+
|
|
108
|
+
{#if agent.sandbox || elapsed != null}
|
|
109
|
+
<div class="flex min-w-0 items-center gap-(--era-gap) text-body era-text-trim text-muted">
|
|
110
|
+
{#if agent.sandbox}
|
|
111
|
+
<span
|
|
112
|
+
class="flex min-w-0 items-center gap-(--era-gap) font-mono"
|
|
113
|
+
title="sandbox {agent.sandbox.id ?? ''}"
|
|
114
|
+
>
|
|
115
|
+
<Box
|
|
116
|
+
class={cn(
|
|
117
|
+
'size-(--era-h-xs) shrink-0',
|
|
118
|
+
agent.sandbox.status === 'running' && 'text-success',
|
|
119
|
+
agent.sandbox.status === 'paused' && 'text-warning'
|
|
120
|
+
)}
|
|
121
|
+
aria-hidden="true"
|
|
122
|
+
/>
|
|
123
|
+
<span class="truncate">{agent.sandbox.status}</span>
|
|
124
|
+
</span>
|
|
125
|
+
{/if}
|
|
126
|
+
<span class="flex-1"></span>
|
|
127
|
+
{#if elapsed != null}
|
|
128
|
+
<span class="shrink-0 font-mono tabular-nums">{formatSwarmDuration(elapsed)}</span>
|
|
129
|
+
{/if}
|
|
130
|
+
</div>
|
|
131
|
+
{/if}
|
|
132
|
+
</Card>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { type SwarmAgent } from './swarm.js';
|
|
4
|
+
type $$ComponentProps = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
|
|
5
|
+
agent: SwarmAgent;
|
|
6
|
+
/** Distance from the root, shown as a `d2` tag. `buildSwarmTree` computes it. */
|
|
7
|
+
depth?: number;
|
|
8
|
+
/** Extra card body under the result — a chip row, a mini feed. */
|
|
9
|
+
children?: Snippet<[SwarmAgent]>;
|
|
10
|
+
/** Override the Root's handlers for this card. */
|
|
11
|
+
onOpen?: (agent: SwarmAgent) => void;
|
|
12
|
+
onCancel?: (agent: SwarmAgent) => void;
|
|
13
|
+
};
|
|
14
|
+
declare const SwarmAgent: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
15
|
+
type SwarmAgent = ReturnType<typeof SwarmAgent>;
|
|
16
|
+
export default SwarmAgent;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { Card } from '../../ui/card/index.js';
|
|
4
|
+
import { cn } from '../../utils/index.js';
|
|
5
|
+
import { getSwarmContext } from './context.js';
|
|
6
|
+
import { formatSwarmAge, type SwarmArtifact } from './swarm.js';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
artifact,
|
|
10
|
+
class: className,
|
|
11
|
+
...restProps
|
|
12
|
+
}: HTMLAttributes<HTMLElement> & {
|
|
13
|
+
artifact: SwarmArtifact;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
const ctx = getSwarmContext();
|
|
17
|
+
const age = $derived(
|
|
18
|
+
artifact.createdAt != null ? formatSwarmAge(artifact.createdAt, ctx.now) : null
|
|
19
|
+
);
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<!-- What a branch actually produced: the merged finding, the report, the diff.
|
|
23
|
+
A well (recessed) rather than a card: an artifact is payload the swarm put
|
|
24
|
+
INTO the panel, the same tier a tool's output pane uses. -->
|
|
25
|
+
<Card
|
|
26
|
+
tone="well"
|
|
27
|
+
data-kind={artifact.kind}
|
|
28
|
+
class={cn('flex min-w-0 flex-col gap-(--era-gap) p-(--era-pad-sm)', className)}
|
|
29
|
+
{...restProps}
|
|
30
|
+
>
|
|
31
|
+
<div class="flex min-w-0 items-center gap-(--era-gap) text-body era-text-trim">
|
|
32
|
+
<span class="shrink-0 font-mono text-[0.625rem] tracking-[0.12em] text-muted uppercase">
|
|
33
|
+
{artifact.kind}
|
|
34
|
+
</span>
|
|
35
|
+
{#if artifact.title}
|
|
36
|
+
<span class="min-w-0 flex-1 truncate text-bright">{artifact.title}</span>
|
|
37
|
+
{:else}
|
|
38
|
+
<span class="flex-1"></span>
|
|
39
|
+
{/if}
|
|
40
|
+
{#if age}
|
|
41
|
+
<span class="shrink-0 font-mono text-body text-muted tabular-nums">{age}</span>
|
|
42
|
+
{/if}
|
|
43
|
+
</div>
|
|
44
|
+
<p class="text-body leading-relaxed text-muted">{artifact.summary}</p>
|
|
45
|
+
</Card>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
import { type SwarmArtifact } from './swarm.js';
|
|
3
|
+
type $$ComponentProps = HTMLAttributes<HTMLElement> & {
|
|
4
|
+
artifact: SwarmArtifact;
|
|
5
|
+
};
|
|
6
|
+
declare const SwarmArtifact: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type SwarmArtifact = ReturnType<typeof SwarmArtifact>;
|
|
8
|
+
export default SwarmArtifact;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import * as Step from '../../ui/step/index.js';
|
|
5
|
+
import { cn } from '../../utils/index.js';
|
|
6
|
+
import Artifact from './swarm-artifact.svelte';
|
|
7
|
+
import type { SwarmArtifact } from './swarm.js';
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
artifacts = [],
|
|
11
|
+
label = 'artifacts',
|
|
12
|
+
limit,
|
|
13
|
+
children,
|
|
14
|
+
class: className,
|
|
15
|
+
...restProps
|
|
16
|
+
}: Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
17
|
+
artifacts?: SwarmArtifact[];
|
|
18
|
+
/** Section heading. Set to `undefined` for a bare list. */
|
|
19
|
+
label?: string;
|
|
20
|
+
/** Show at most this many, newest first by position. Omit for all of them. */
|
|
21
|
+
limit?: number;
|
|
22
|
+
/** Replaces the default `Swarm.Artifact` rendering for each item. */
|
|
23
|
+
children?: Snippet<[SwarmArtifact]>;
|
|
24
|
+
} = $props();
|
|
25
|
+
|
|
26
|
+
const shown = $derived(limit == null ? artifacts : artifacts.slice(0, limit));
|
|
27
|
+
const hidden = $derived(artifacts.length - shown.length);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<!-- Renders nothing at all when there is nothing to show: an empty "ARTIFACTS"
|
|
31
|
+
heading over blank space is worse than silence, and this sits inside rows
|
|
32
|
+
that are mostly empty until a branch lands. -->
|
|
33
|
+
{#if artifacts.length > 0}
|
|
34
|
+
<Step.Section {label} class={cn('min-w-0', className)} {...restProps}>
|
|
35
|
+
{#each shown as artifact (artifact.id)}
|
|
36
|
+
{#if children}{@render children(artifact)}{:else}<Artifact {artifact} />{/if}
|
|
37
|
+
{/each}
|
|
38
|
+
{#if hidden > 0}
|
|
39
|
+
<span class="text-body text-muted">+{hidden} more</span>
|
|
40
|
+
{/if}
|
|
41
|
+
</Step.Section>
|
|
42
|
+
{/if}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { SwarmArtifact } from './swarm.js';
|
|
4
|
+
type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
5
|
+
artifacts?: SwarmArtifact[];
|
|
6
|
+
/** Section heading. Set to `undefined` for a bare list. */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Show at most this many, newest first by position. Omit for all of them. */
|
|
9
|
+
limit?: number;
|
|
10
|
+
/** Replaces the default `Swarm.Artifact` rendering for each item. */
|
|
11
|
+
children?: Snippet<[SwarmArtifact]>;
|
|
12
|
+
};
|
|
13
|
+
declare const SwarmArtifacts: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
|
+
type SwarmArtifacts = ReturnType<typeof SwarmArtifacts>;
|
|
15
|
+
export default SwarmArtifacts;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { SwarmBarrierStatus } from './swarm.js';
|
|
3
|
+
|
|
4
|
+
const BARRIER_TONE: Record<SwarmBarrierStatus, 'default' | 'success' | 'destructive'> = {
|
|
5
|
+
open: 'default',
|
|
6
|
+
done: 'success',
|
|
7
|
+
failed: 'destructive',
|
|
8
|
+
cancelled: 'default'
|
|
9
|
+
};
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
14
|
+
import GitMerge from '@lucide/svelte/icons/git-merge';
|
|
15
|
+
import { Badge } from '../../ui/badge/index.js';
|
|
16
|
+
import { Progress } from '../../ui/progress/index.js';
|
|
17
|
+
import { cn } from '../../utils/index.js';
|
|
18
|
+
import { summarizeBarrier, type SwarmAgent, type SwarmBarrier } from './swarm.js';
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
barrier,
|
|
22
|
+
agents = [],
|
|
23
|
+
class: className,
|
|
24
|
+
...restProps
|
|
25
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
26
|
+
barrier: SwarmBarrier;
|
|
27
|
+
/** The swarm's agents — members are the ones pointing at this barrier. */
|
|
28
|
+
agents?: SwarmAgent[];
|
|
29
|
+
} = $props();
|
|
30
|
+
|
|
31
|
+
const state = $derived(summarizeBarrier(barrier, agents));
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<!-- A fan-in gate: the point where parallel branches have to rejoin before the
|
|
35
|
+
run can move on. It is rendered as PROGRESS rather than as a status word
|
|
36
|
+
because the only question anyone asks of a barrier is "how much longer",
|
|
37
|
+
and 3/5 answers it where "open" does not. -->
|
|
38
|
+
<div
|
|
39
|
+
data-status={state.status}
|
|
40
|
+
class={cn('flex min-w-0 flex-col gap-(--era-gap)', className)}
|
|
41
|
+
{...restProps}
|
|
42
|
+
>
|
|
43
|
+
<div class="flex min-w-0 items-center gap-(--era-gap) text-body era-text-trim">
|
|
44
|
+
<GitMerge class="size-(--era-h-xs) shrink-0 text-muted" aria-hidden="true" />
|
|
45
|
+
<span class="min-w-0 flex-1 truncate text-fg">{barrier.title}</span>
|
|
46
|
+
{#if barrier.key}
|
|
47
|
+
<span class="shrink-0 font-mono text-body text-muted">#{barrier.key}</span>
|
|
48
|
+
{/if}
|
|
49
|
+
{#if state.failed > 0}
|
|
50
|
+
<Badge tone="destructive">{state.failed} failed</Badge>
|
|
51
|
+
{/if}
|
|
52
|
+
<Badge tone={BARRIER_TONE[state.status]}>
|
|
53
|
+
{state.done}/{state.total}
|
|
54
|
+
</Badge>
|
|
55
|
+
</div>
|
|
56
|
+
{#if state.total > 0}
|
|
57
|
+
<Progress
|
|
58
|
+
value={state.done}
|
|
59
|
+
max={state.total}
|
|
60
|
+
aria-label="{barrier.title} — {state.done} of {state.total} agents done"
|
|
61
|
+
/>
|
|
62
|
+
{/if}
|
|
63
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
import { type SwarmAgent, type SwarmBarrier } from './swarm.js';
|
|
3
|
+
type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
barrier: SwarmBarrier;
|
|
5
|
+
/** The swarm's agents — members are the ones pointing at this barrier. */
|
|
6
|
+
agents?: SwarmAgent[];
|
|
7
|
+
};
|
|
8
|
+
declare const SwarmBarrier: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
9
|
+
type SwarmBarrier = ReturnType<typeof SwarmBarrier>;
|
|
10
|
+
export default SwarmBarrier;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import Box from '@lucide/svelte/icons/box';
|
|
4
|
+
import { Badge } from '../../ui/badge/index.js';
|
|
5
|
+
import { cn } from '../../utils/index.js';
|
|
6
|
+
import { summarizeSwarm, type SwarmAgent, type SwarmCounts } from './swarm.js';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
agents = [],
|
|
10
|
+
counts,
|
|
11
|
+
sandboxes = true,
|
|
12
|
+
class: className,
|
|
13
|
+
...restProps
|
|
14
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
15
|
+
/** The swarm's agents — counted here so the caller never tallies by hand. */
|
|
16
|
+
agents?: SwarmAgent[];
|
|
17
|
+
/** Pre-computed counts (a controller's `counts`), used instead of `agents`. */
|
|
18
|
+
counts?: SwarmCounts;
|
|
19
|
+
/** Show the live-VM tally. Off for swarms that do not run sandboxes. */
|
|
20
|
+
sandboxes?: boolean;
|
|
21
|
+
} = $props();
|
|
22
|
+
|
|
23
|
+
const tally = $derived(counts ?? summarizeSwarm(agents));
|
|
24
|
+
// blocked and queued are two different waits (see SwarmAgentStatus) but they
|
|
25
|
+
// are one number to someone reading the header: work that has not started.
|
|
26
|
+
const waiting = $derived(tally.blocked + tally.queued);
|
|
27
|
+
// Nothing to report is itself worth reporting — a just-created swarm with no
|
|
28
|
+
// agents should read "0 agents", not render an empty strip.
|
|
29
|
+
const quiet = $derived(
|
|
30
|
+
tally.running === 0 && waiting === 0 && tally.done === 0 && tally.failed === 0
|
|
31
|
+
);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<!-- Live tally. Each badge appears only while its number is non-zero, so the
|
|
35
|
+
strip shrinks as a run lands instead of trailing a row of zeroes — and the
|
|
36
|
+
one that matters (failures) is never hidden behind the ones that don't. -->
|
|
37
|
+
<div class={cn('flex shrink-0 items-center gap-(--era-gap)', className)} {...restProps}>
|
|
38
|
+
{#if quiet}
|
|
39
|
+
<Badge>{tally.total} agents</Badge>
|
|
40
|
+
{:else}
|
|
41
|
+
{#if tally.running > 0}<Badge tone="accent">{tally.running} running</Badge>{/if}
|
|
42
|
+
{#if waiting > 0}<Badge>{waiting} waiting</Badge>{/if}
|
|
43
|
+
{#if tally.done > 0}<Badge tone="success">{tally.done} done</Badge>{/if}
|
|
44
|
+
{#if tally.failed > 0}<Badge tone="destructive">{tally.failed} failed</Badge>{/if}
|
|
45
|
+
{/if}
|
|
46
|
+
{#if sandboxes && tally.sandboxes > 0}
|
|
47
|
+
<Badge>
|
|
48
|
+
<Box class="size-(--era-h-xs)" aria-hidden="true" />
|
|
49
|
+
{tally.sandboxes}
|
|
50
|
+
</Badge>
|
|
51
|
+
{/if}
|
|
52
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
import { type SwarmAgent, type SwarmCounts } from './swarm.js';
|
|
3
|
+
type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
/** The swarm's agents — counted here so the caller never tallies by hand. */
|
|
5
|
+
agents?: SwarmAgent[];
|
|
6
|
+
/** Pre-computed counts (a controller's `counts`), used instead of `agents`. */
|
|
7
|
+
counts?: SwarmCounts;
|
|
8
|
+
/** Show the live-VM tally. Off for swarms that do not run sandboxes. */
|
|
9
|
+
sandboxes?: boolean;
|
|
10
|
+
};
|
|
11
|
+
declare const SwarmCounts: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type SwarmCounts = ReturnType<typeof SwarmCounts>;
|
|
13
|
+
export default SwarmCounts;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { SwarmAgentStatus } from './swarm.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Status → tone. Colour is reserved for LIFECYCLE across the whole family
|
|
6
|
+
* (a role is a category, and categories get glyphs, not hues), so this map is
|
|
7
|
+
* the single place any part says what a state looks like.
|
|
8
|
+
*
|
|
9
|
+
* `running` pulses, which is the one animation here — it rides the pulse
|
|
10
|
+
* keyframes, so `prefers-reduced-motion` and data-motion="instant" settle it
|
|
11
|
+
* to a solid dot rather than leaving a strobe in a 40-agent grid.
|
|
12
|
+
*/
|
|
13
|
+
export const SWARM_DOT: Record<SwarmAgentStatus, { class: string; label: string }> = {
|
|
14
|
+
blocked: { class: 'bg-warning', label: 'Blocked' },
|
|
15
|
+
queued: { class: 'bg-muted', label: 'Queued' },
|
|
16
|
+
running: { class: 'bg-primary animate-pulse', label: 'Running' },
|
|
17
|
+
done: { class: 'bg-success', label: 'Done' },
|
|
18
|
+
failed: { class: 'bg-destructive', label: 'Failed' },
|
|
19
|
+
cancelled: { class: 'bg-muted opacity-50', label: 'Cancelled' }
|
|
20
|
+
};
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<script lang="ts">
|
|
24
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
25
|
+
import { cn } from '../../utils/index.js';
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
status,
|
|
29
|
+
label,
|
|
30
|
+
class: className,
|
|
31
|
+
...restProps
|
|
32
|
+
}: HTMLAttributes<HTMLSpanElement> & {
|
|
33
|
+
status: SwarmAgentStatus;
|
|
34
|
+
/** Overrides the announced/hover word (defaults to the status). */
|
|
35
|
+
label?: string;
|
|
36
|
+
} = $props();
|
|
37
|
+
|
|
38
|
+
const tone = $derived(SWARM_DOT[status]);
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<!-- role="img" rather than aria-hidden: in a swarm the dot is not decoration
|
|
42
|
+
next to a spelled-out state, it IS the state, and a card that only paints
|
|
43
|
+
it says nothing to a screen reader. The name also flows into the enclosing
|
|
44
|
+
treeitem's name-from-contents, so a tree row announces its status too. -->
|
|
45
|
+
<span
|
|
46
|
+
role="img"
|
|
47
|
+
aria-label={label ?? tone.label}
|
|
48
|
+
title={label ?? tone.label}
|
|
49
|
+
data-status={status}
|
|
50
|
+
class={cn('size-2 shrink-0 rounded-full', tone.class, className)}
|
|
51
|
+
{...restProps}
|
|
52
|
+
></span>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SwarmAgentStatus } from './swarm.js';
|
|
2
|
+
/**
|
|
3
|
+
* Status → tone. Colour is reserved for LIFECYCLE across the whole family
|
|
4
|
+
* (a role is a category, and categories get glyphs, not hues), so this map is
|
|
5
|
+
* the single place any part says what a state looks like.
|
|
6
|
+
*
|
|
7
|
+
* `running` pulses, which is the one animation here — it rides the pulse
|
|
8
|
+
* keyframes, so `prefers-reduced-motion` and data-motion="instant" settle it
|
|
9
|
+
* to a solid dot rather than leaving a strobe in a 40-agent grid.
|
|
10
|
+
*/
|
|
11
|
+
export declare const SWARM_DOT: Record<SwarmAgentStatus, {
|
|
12
|
+
class: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}>;
|
|
15
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
16
|
+
type $$ComponentProps = HTMLAttributes<HTMLSpanElement> & {
|
|
17
|
+
status: SwarmAgentStatus;
|
|
18
|
+
/** Overrides the announced/hover word (defaults to the status). */
|
|
19
|
+
label?: string;
|
|
20
|
+
};
|
|
21
|
+
declare const SwarmDot: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
22
|
+
type SwarmDot = ReturnType<typeof SwarmDot>;
|
|
23
|
+
export default SwarmDot;
|