@contraagent/elements 0.7.2 → 0.8.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/custom-elements.json +31 -6
- package/dist/graph/layered-graph.d.ts +7 -1
- package/dist/graph/layered-graph.d.ts.map +1 -1
- package/dist/graph/layered-graph.js +6 -2
- package/dist/graph/layered-graph.js.map +1 -1
- package/dist/graph/strings.d.ts +3 -1
- package/dist/graph/strings.d.ts.map +1 -1
- package/dist/graph/strings.js.map +1 -1
- package/dist/graph/styles.d.ts.map +1 -1
- package/dist/graph/styles.js +2 -0
- package/dist/graph/styles.js.map +1 -1
- package/dist/graph/types.d.ts +1 -1
- package/dist/graph/types.d.ts.map +1 -1
- package/dist/graph/views/dag.js +4 -4
- package/dist/graph/views/dag.js.map +1 -1
- package/dist/graph/views/foot.d.ts +4 -3
- package/dist/graph/views/foot.d.ts.map +1 -1
- package/dist/graph/views/foot.js +8 -23
- package/dist/graph/views/foot.js.map +1 -1
- package/dist/graph/views/node-table.js +2 -2
- package/dist/graph/views/node-table.js.map +1 -1
- package/dist/lanes/lanes.d.ts +3 -3
- package/dist/lanes/lanes.d.ts.map +1 -1
- package/dist/lanes/lanes.js +7 -4
- package/dist/lanes/lanes.js.map +1 -1
- package/dist/primitives/share-bar.d.ts +4 -1
- package/dist/primitives/share-bar.d.ts.map +1 -1
- package/dist/primitives/share-bar.js +8 -5
- package/dist/primitives/share-bar.js.map +1 -1
- package/dist/tiles/stat-tiles.d.ts +3 -2
- package/dist/tiles/stat-tiles.d.ts.map +1 -1
- package/dist/tiles/stat-tiles.js +5 -4
- package/dist/tiles/stat-tiles.js.map +1 -1
- package/dist/tiles/styles.d.ts.map +1 -1
- package/dist/tiles/styles.js +4 -1
- package/dist/tiles/styles.js.map +1 -1
- package/dist/tiles/types.d.ts +1 -1
- package/dist/tiles/types.d.ts.map +1 -1
- package/dist/transcript/styles.d.ts.map +1 -1
- package/dist/transcript/styles.js +0 -1
- package/dist/transcript/styles.js.map +1 -1
- package/dist/transcript/transcript.d.ts +1 -0
- package/dist/transcript/transcript.d.ts.map +1 -1
- package/dist/transcript/transcript.js +10 -7
- package/dist/transcript/transcript.js.map +1 -1
- package/dist/transcript/types.d.ts +2 -1
- package/dist/transcript/types.d.ts.map +1 -1
- package/dist/transcript/types.js.map +1 -1
- package/docs/graph.md +5 -1
- package/docs/lanes.md +2 -1
- package/docs/tiles.md +6 -4
- package/docs/transcript.md +5 -3
- package/package.json +1 -1
- package/src/graph/layered-graph.ts +6 -2
- package/src/graph/strings.ts +3 -1
- package/src/graph/styles.ts +2 -0
- package/src/graph/types.ts +1 -1
- package/src/graph/views/dag.ts +4 -4
- package/src/graph/views/foot.ts +8 -23
- package/src/graph/views/node-table.ts +2 -2
- package/src/lanes/lanes.ts +6 -4
- package/src/primitives/share-bar.ts +12 -7
- package/src/tiles/stat-tiles.ts +5 -4
- package/src/tiles/styles.ts +4 -1
- package/src/tiles/types.ts +1 -1
- package/src/transcript/styles.ts +0 -1
- package/src/transcript/transcript.ts +10 -7
- package/src/transcript/types.ts +2 -1
package/docs/transcript.md
CHANGED
|
@@ -27,7 +27,9 @@ a list (errors) or a note.
|
|
|
27
27
|
::code{file="../src/transcript/types.ts" from="export interface TranscriptSection {" to="/** Where the reader was in a transcript. */"}
|
|
28
28
|
|
|
29
29
|
An item is a prompt, a message or reasoning (markdown), an event (a glyph, a
|
|
30
|
-
title and a detail), a notice, or a tool card
|
|
30
|
+
title and a detail), a notice, or a tool card. A prompt and reasoning show a
|
|
31
|
+
head over their bubble; the agent's messages show none, and their avatar
|
|
32
|
+
carries the name (`strings.assistant`) as its accessible name and tooltip:
|
|
31
33
|
|
|
32
34
|
::code{file="../src/transcript/types.ts" from="/** One tool call:" to="/** A labelled value in a section's head (who ran it). */"}
|
|
33
35
|
|
|
@@ -69,5 +71,5 @@ Markdown is turned into HTML by `markdown` (`md` of
|
|
|
69
71
|
reasoning show as plain text. `renderDetail` draws an open prose detail. The
|
|
70
72
|
parts are `picker`, `section`, `tag`, `bubble`, `notice`, `tool`, `tool-body`
|
|
71
73
|
and `verdict`; `tagChips` and `tagStyles` draw tags elsewhere in the same
|
|
72
|
-
look. The words are `strings` ([Strings](strings.md)): the picker's
|
|
73
|
-
tooltip, the bubbles' heads, the tool cards' statuses, the counts.
|
|
74
|
+
look. The words are `strings` ([Strings](strings.md)): the picker's accessible
|
|
75
|
+
name (not shown) and tooltip, the bubbles' heads and the messages' name, the tool cards' statuses, the counts.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contraagent/elements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Generic web components (Lit) and their parts: design tokens, themes, status pills, legends, a segmented control, a data table, a layered graph, a mark matrix, stat tiles, time lanes, charts (columns, lines, a heatmap, horizontal bars), a check table, a transcript, a JSON tree, markdown, highlighted code, a tree of items and counters. No contraagent data, no localization library",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -51,7 +51,8 @@ const EMPTY: GraphInput = { series: [], nodes: [], clusters: [], edges: [], stub
|
|
|
51
51
|
* graph; Ctrl (or ⌘) and the wheel, or the buttons, zoom; dragging pans; "Show all"
|
|
52
52
|
* goes back. "Graph | Table" changes the view (`cae-view-change`): the table is a row
|
|
53
53
|
* per node, with its status and the nodes it is joined to. With `matrix`, the frame
|
|
54
|
-
* shows that `<cae-mark-matrix>` as the table instead.
|
|
54
|
+
* shows that `<cae-mark-matrix>` as the table instead. With `controls` false, the
|
|
55
|
+
* buttons and "Graph | Table" are not drawn.
|
|
55
56
|
*
|
|
56
57
|
* Size: the element is as wide as its container. Without a height set on it, the
|
|
57
58
|
* frame is as tall as the whole drawing at that width (never scaled up), up to 70% of
|
|
@@ -103,6 +104,7 @@ export class CaeLayeredGraph extends LitElement {
|
|
|
103
104
|
view: {},
|
|
104
105
|
matrix: { attribute: false },
|
|
105
106
|
barAt: { type: Number },
|
|
107
|
+
controls: { type: Boolean },
|
|
106
108
|
related: { attribute: false },
|
|
107
109
|
strings: { attribute: false },
|
|
108
110
|
matrixStrings: { attribute: false },
|
|
@@ -128,6 +130,8 @@ export class CaeLayeredGraph extends LitElement {
|
|
|
128
130
|
direction: 'auto' | 'across' | 'down' = 'auto';
|
|
129
131
|
/** The node table instead of the drawing. */
|
|
130
132
|
view: 'graph' | 'table' = 'graph';
|
|
133
|
+
/** The zoom buttons and "Graph | Table" under the frame; false for a graph that is only shown (the wheel with Ctrl or ⌘, and dragging, still zoom and pan). */
|
|
134
|
+
controls = true;
|
|
131
135
|
/** A mark matrix shown in the frame as the table, or null. */
|
|
132
136
|
matrix: MarkMatrixInput | null = null;
|
|
133
137
|
/** Above this many marks, a node shows a bar of its counts. */
|
|
@@ -370,7 +374,7 @@ export class CaeLayeredGraph extends LitElement {
|
|
|
370
374
|
return html`<div class="col">
|
|
371
375
|
<slot name="crumbs"></slot>
|
|
372
376
|
<div class="main">${body}${card}<div class="sr" role="status">${this.announced}</div></div>
|
|
373
|
-
${foot(h, mx)}
|
|
377
|
+
${foot(h, mx, this.controls)}
|
|
374
378
|
</div>`;
|
|
375
379
|
}
|
|
376
380
|
|
package/src/graph/strings.ts
CHANGED
|
@@ -26,11 +26,13 @@ export interface LayeredGraphStrings {
|
|
|
26
26
|
more: (n: number) => string;
|
|
27
27
|
/** A node's count without `progress`: its marks of the tone `done`, of all its marks ("2 of 3 done"). */
|
|
28
28
|
progress: (done: number, total: number) => string;
|
|
29
|
-
/**
|
|
29
|
+
/** The label of a mark's badge in its tooltip ("count: 3"). */
|
|
30
30
|
badge: string;
|
|
31
|
+
/** Legend words: the three edge styles. */
|
|
31
32
|
solidEdge: string;
|
|
32
33
|
dottedEdge: string;
|
|
33
34
|
dashedEdge: string;
|
|
35
|
+
/** Tooltips: the mark of a cluster's return, and of an item with an inner view. */
|
|
34
36
|
cycle: string;
|
|
35
37
|
drill: string;
|
|
36
38
|
/** The open and close controls of an item that can be drawn open ("Show inside", "Hide inside"). */
|
package/src/graph/styles.ts
CHANGED
|
@@ -41,6 +41,8 @@ export const graphStyles = css`
|
|
|
41
41
|
.edges path.ret-tip, .legend svg path.ret-head { fill: var(--text-2); stroke: none; opacity: 0.8; }
|
|
42
42
|
.ic { display: block; flex: none; }
|
|
43
43
|
.loop-ic { color: var(--text-2); }
|
|
44
|
+
/* an icon that names itself in a tooltip */
|
|
45
|
+
.tip-ic { display: inline-flex; line-height: 0; }
|
|
44
46
|
.in-ic { color: var(--text-muted); }
|
|
45
47
|
|
|
46
48
|
.stub { z-index: 1; font-family: var(--mono); font-size: 11.5px; color: var(--text-muted); padding: 3px 9px;
|
package/src/graph/types.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface GraphMark {
|
|
|
23
23
|
label: string;
|
|
24
24
|
/** The tooltip. */
|
|
25
25
|
tip?: string;
|
|
26
|
-
/** A short text in the mark: a count. */
|
|
26
|
+
/** A short text in the mark: a count. The tooltip adds it after `strings.badge` ("count: 3"). */
|
|
27
27
|
badge?: string;
|
|
28
28
|
/** A short name, as a chip shows it (a node drawn as a bar lists its failed marks by it); default: its series id. */
|
|
29
29
|
name?: string;
|
package/src/graph/views/dag.ts
CHANGED
|
@@ -109,7 +109,7 @@ function toggleControl(h: GraphHost, id: string, label: string, open: boolean) {
|
|
|
109
109
|
function clusterName(h: GraphHost, b: GraphClusterInput, sel: boolean) {
|
|
110
110
|
const first = b.path?.[0];
|
|
111
111
|
const name = html`<button type="button" class="name" title=${(first?.tip ?? b.tip) || nothing} aria-pressed=${sel}
|
|
112
|
-
@focus=${() => h.focusSelect(b.id)} @keydown=${(e: KeyboardEvent) => onNameKey(h, e, b.id)}>${b.cycle ? cycleIcon() : nothing}${first?.label ?? b.label}</button>`;
|
|
112
|
+
@focus=${() => h.focusSelect(b.id)} @keydown=${(e: KeyboardEvent) => onNameKey(h, e, b.id)}>${b.cycle ? html`<span class="tip-ic" title=${h.words.cycle}>${cycleIcon()}</span>` : nothing}${first?.label ?? b.label}</button>`;
|
|
113
113
|
if (!b.path || b.path.length < 2) return name;
|
|
114
114
|
return html`${name}${b.path.slice(1).map(
|
|
115
115
|
(p) => html`<span class="psep" aria-hidden="true">›</span><button type="button" class="part" title=${p.tip || nothing}
|
|
@@ -139,7 +139,7 @@ function cluster(h: GraphHost, b: GraphClusterInput, parent: Rect | null, item:
|
|
|
139
139
|
${b.toggle
|
|
140
140
|
? html`<span class="cue">${toggleControl(h, b.id, b.path?.map((p) => p.label).join(' › ') ?? b.label, true)}</span>`
|
|
141
141
|
: b.drill
|
|
142
|
-
? html`<span class="cue" aria-hidden="true">${innerIcon()}</span>`
|
|
142
|
+
? html`<span class="cue" aria-hidden="true" title=${h.words.drill}>${innerIcon()}</span>`
|
|
143
143
|
: nothing}
|
|
144
144
|
<div class="in">${repeat(kids, (id) => id, (id) => item(id, r))}</div>
|
|
145
145
|
</section>`;
|
|
@@ -169,7 +169,7 @@ function node(h: GraphHost, n: GraphNode, parent: Rect | null) {
|
|
|
169
169
|
<span class="right"><span class="count" aria-hidden="true" title=${n.progress.label}>${n.progress.done}/${n.progress.total}</span>${n.toggle
|
|
170
170
|
? toggleControl(h, n.id, n.label, false)
|
|
171
171
|
: n.drill
|
|
172
|
-
? html`<span class="cue" aria-hidden="true">${innerIcon()}</span>`
|
|
172
|
+
? html`<span class="cue" aria-hidden="true" title=${h.words.drill}>${innerIcon()}</span>`
|
|
173
173
|
: nothing}</span>
|
|
174
174
|
</div>
|
|
175
175
|
${bar ? statusBar(h, n, group) : marks(h, group)}
|
|
@@ -230,7 +230,7 @@ function mark(h: GraphHost, g: MarkGroup, s: Slot, i: number, tabStop: boolean,
|
|
|
230
230
|
const look = h.states[m.state];
|
|
231
231
|
return html`<button type="button" class="mark ${lit ? 'on' : ''} ${sel}" part="mark" data-tone=${toneAttr(look?.tone)} data-shape=${look?.shape ?? nothing}
|
|
232
232
|
data-link=${link ?? nothing} data-i=${i} data-n=${m.badge ?? nothing} tabindex=${tabStop ? 0 : -1}
|
|
233
|
-
title=${m.tip
|
|
233
|
+
title=${[m.tip, m.badge ? `${h.words.badge}: ${m.badge}` : ''].filter(Boolean).join('\n') || nothing}
|
|
234
234
|
aria-label=${m.label} aria-pressed=${!!sel}
|
|
235
235
|
@mouseenter=${() => (h.hover = hlKey)}
|
|
236
236
|
@focus=${focus}
|
package/src/graph/views/foot.ts
CHANGED
|
@@ -5,17 +5,17 @@ import { legend as legendRow, type LegendItem } from '../../primitives/legend.js
|
|
|
5
5
|
import type { MarkMatrixInput } from '../../matrix/types.js';
|
|
6
6
|
import { asBar } from '../marks.js';
|
|
7
7
|
import { slotsOf, type GraphHost } from '../host.js';
|
|
8
|
-
import { innerIcon } from '../icons.js';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
|
-
* The legend on the left, the tools at the right end of the first line
|
|
12
|
-
* under them: their line keeps its room, and shows its
|
|
10
|
+
* The legend on the left, the tools at the right end of the first line (with
|
|
11
|
+
* `controls`), and the keys under them: their line keeps its room, and shows its
|
|
12
|
+
* words while the graph has the focus.
|
|
13
13
|
*/
|
|
14
|
-
export function foot(h: GraphHost, matrix: MarkMatrixInput | null) {
|
|
14
|
+
export function foot(h: GraphHost, matrix: MarkMatrixInput | null, controls: boolean) {
|
|
15
15
|
const table = !!matrix || h.asTable;
|
|
16
16
|
return html`<div class="foot">
|
|
17
17
|
${legend(h, matrix)}
|
|
18
|
-
|
|
18
|
+
${controls ? html`<div class="tools">${table ? nothing : zoomTools(h)}${viewSwitch(h, table)}</div>` : nothing}
|
|
19
19
|
<div class="keys" part="keys">${keyHints(h, matrix).map((k, i) => html`${i ? ' | ' : ''}<span>${k}</span>`)}</div>
|
|
20
20
|
</div>`;
|
|
21
21
|
}
|
|
@@ -58,9 +58,9 @@ const legendMark = (h: GraphHost, state: string, badge?: string) => {
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
* What the view shows that needs a word: the states of the marks shown,
|
|
62
|
-
*
|
|
63
|
-
*
|
|
61
|
+
* What the view shows that needs a word: the states of the marks shown, the icons the
|
|
62
|
+
* host names, and the edge styles when another than solid is drawn. A badge, a cluster's
|
|
63
|
+
* return and the mark of an item with an inner view name themselves in their tooltips.
|
|
64
64
|
*/
|
|
65
65
|
function legend(h: GraphHost, mx: MarkMatrixInput | null) {
|
|
66
66
|
const g = h.drawn;
|
|
@@ -73,13 +73,9 @@ function legend(h: GraphHost, mx: MarkMatrixInput | null) {
|
|
|
73
73
|
: g.nodes.flatMap((n) => slotsOf(n, g.series).map((s) => s && { state: s.m.state, badge: asBar(n, h.barAt) ? undefined : s.m.badge }));
|
|
74
74
|
const shown = marks.filter((m): m is { state: string; badge: string | undefined } => m !== null);
|
|
75
75
|
const present = new Set(shown.map((m) => m.state));
|
|
76
|
-
const badge = shown.some((m) => m.badge);
|
|
77
76
|
const styles = new Set(!mx && !flat ? [...g.edges, ...g.stubs].map((e) => e.style) : []);
|
|
78
77
|
const edgeStyles = styles.size > 1 || (styles.size === 1 && !styles.has('solid')) ? (['solid', 'dotted', 'dashed'] as const).filter((s) => styles.has(s)) : [];
|
|
79
78
|
const icons = new Set(mx ? mx.head.flat().flatMap((c) => c.icons ?? []) : g.nodes.flatMap((n) => n.icons ?? []));
|
|
80
|
-
const cycles = !mx && !flat && g.clusters.some((c) => c.cycle);
|
|
81
|
-
const inner = !mx && ((!flat && g.clusters.some((c) => c.drill)) || g.nodes.some((n) => n.drill));
|
|
82
|
-
const first = Object.keys(h.states)[0] ?? '';
|
|
83
79
|
const line = (style: 'solid' | 'dotted' | 'dashed') =>
|
|
84
80
|
svg`<svg width="22" height="8" aria-hidden="true"><path class=${style} d="M1,4 H21" stroke-dasharray=${style === 'dashed' ? '5 4' : style === 'dotted' ? '0.1 4' : 'none'}></path></svg>`;
|
|
85
81
|
const edgeWord = { solid: h.words.solidEdge, dotted: h.words.dottedEdge, dashed: h.words.dashedEdge };
|
|
@@ -87,21 +83,10 @@ function legend(h: GraphHost, mx: MarkMatrixInput | null) {
|
|
|
87
83
|
...Object.entries(h.states)
|
|
88
84
|
.filter(([s]) => present.has(s))
|
|
89
85
|
.map(([s, look]) => ({ label: look.label, icon: legendMark(h, s), decorative: true })),
|
|
90
|
-
...(badge ? [{ label: h.words.badge, icon: legendMark(h, first, '2'), decorative: true }] : []),
|
|
91
86
|
...Object.entries(h.words.iconNames)
|
|
92
87
|
.filter(([name]) => icons.has(name))
|
|
93
88
|
.map(([name, label]) => ({ label, icon: h.renderIcon(name) })),
|
|
94
89
|
...edgeStyles.map((s) => ({ label: edgeWord[s], icon: line(s) })),
|
|
95
|
-
...(cycles
|
|
96
|
-
? [
|
|
97
|
-
{
|
|
98
|
-
label: h.words.cycle,
|
|
99
|
-
icon: html`<svg width="24" height="12" aria-hidden="true"><path class="ret" d="M20,1.5 V5.5 Q20,9.5 16,9.5 H8 Q4,9.5 4,5.5 V5"></path>
|
|
100
|
-
<path class="ret-head" d="M4,0.8 L1.4,5 L6.6,5 z"></path></svg>`,
|
|
101
|
-
},
|
|
102
|
-
]
|
|
103
|
-
: []),
|
|
104
|
-
...(inner ? [{ label: h.words.drill, icon: innerIcon() }] : []),
|
|
105
90
|
];
|
|
106
91
|
return legendRow(items, { cls: 'big' });
|
|
107
92
|
}
|
|
@@ -57,7 +57,7 @@ export function nodeTable(h: GraphHost) {
|
|
|
57
57
|
const nb = neighbours(n, g, h.nodes, w.inCluster);
|
|
58
58
|
const box = n.cluster ? h.clusters.get(n.cluster) : undefined;
|
|
59
59
|
const on = h.selected === n.id;
|
|
60
|
-
const icons = html`${(n.icons ?? []).map((x) => h.renderIcon(x))}${n.drill ? innerIcon() : nothing}`;
|
|
60
|
+
const icons = html`${(n.icons ?? []).map((x) => h.renderIcon(x))}${n.drill ? html`<span class="tip-ic" title=${w.drill}>${innerIcon()}</span>` : nothing}`;
|
|
61
61
|
return html`<tr class=${on ? 'sel' : ''} part="row" data-id=${n.id}
|
|
62
62
|
@click=${(e: MouseEvent) => !(e.target as Element).closest('.loopchip') && e.detail < 2 && h.select(n.id)}
|
|
63
63
|
@dblclick=${(e: MouseEvent) => !(e.target as Element).closest('.loopchip') && h.act(n.id)}>
|
|
@@ -69,7 +69,7 @@ export function nodeTable(h: GraphHost) {
|
|
|
69
69
|
h.act(n.id);
|
|
70
70
|
}}>${n.label}</button>${box?.drill
|
|
71
71
|
? html`<button type="button" class="taskchip loopchip" title=${box.chipTip ?? nothing} aria-label=${box.chipLabel ?? nothing}
|
|
72
|
-
@click=${() => h.drill(box.id)}>${box.cycle ? cycleIcon(12) : nothing}${box.note ?? box.label}</button>`
|
|
72
|
+
@click=${() => h.drill(box.id)}>${box.cycle ? html`<span class="tip-ic" title=${w.cycle}>${cycleIcon(12)}</span>` : nothing}${box.note ?? box.label}</button>`
|
|
73
73
|
: nothing}</span>
|
|
74
74
|
</th>
|
|
75
75
|
<td class="fit st">${statusLine(n)}</td>
|
package/src/lanes/lanes.ts
CHANGED
|
@@ -14,8 +14,8 @@ const SECONDS_BELOW_MS = 10 * 60e3;
|
|
|
14
14
|
/**
|
|
15
15
|
* Bars on a time axis, a lane per group: each bar as long as it took, the
|
|
16
16
|
* overlapping bars of a lane stacked, a running bar (`end` null) growing until
|
|
17
|
-
* now and a line at now. A bar's look is its style in `barStyles
|
|
18
|
-
*
|
|
17
|
+
* now and a line at now. A bar's look is its style in `barStyles`; the labels
|
|
18
|
+
* of the styles the bars use are the legend (a row per `group`). The bars grow in when they are
|
|
19
19
|
* first drawn.
|
|
20
20
|
*
|
|
21
21
|
* Its look: `--cae-lanes-label-width` (the lanes' labels; 112px, 78px on a
|
|
@@ -117,10 +117,12 @@ export class CaeLanes extends LitElement {
|
|
|
117
117
|
</div>`;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/** A legend row per group of styles, in key order; the note of omitted bars ends the last. */
|
|
120
|
+
/** A legend row per group of the styles the bars use, in key order; the note of omitted bars ends the last. */
|
|
121
121
|
private legends(s: LanesStrings): TemplateResult[] {
|
|
122
122
|
const groups: LegendItem[][] = [];
|
|
123
|
-
|
|
123
|
+
const used = new Set(this.lanes.flatMap((l) => l.bars.map((b) => b.style)));
|
|
124
|
+
for (const [key, look] of Object.entries(this.barStyles)) {
|
|
125
|
+
if (!used.has(key)) continue;
|
|
124
126
|
const g = look.group ?? 0;
|
|
125
127
|
(groups[g] ??= []).push({
|
|
126
128
|
label: look.label,
|
|
@@ -20,6 +20,8 @@ export interface ShareBarOptions {
|
|
|
20
20
|
label?: string;
|
|
21
21
|
/** The smallest share (0..1) whose part is named under the bar; default 0.15. */
|
|
22
22
|
labelAt?: number;
|
|
23
|
+
/** Names the parts under the bar; default true. False when a table beside the bar names each part with its share. */
|
|
24
|
+
names?: boolean;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/**
|
|
@@ -42,7 +44,8 @@ export const sharePercent = (pct: number, value: number): string => (pct === 0 &
|
|
|
42
44
|
/**
|
|
43
45
|
* The parts of a whole as one bar (`.share`, part `share`): each part a segment as
|
|
44
46
|
* wide as its share, 2px apart, in the order given (so a part keeps its place when
|
|
45
|
-
* the amounts change), with its name and share under it when it is wide enough
|
|
47
|
+
* the amounts change), with its name and share under it when it is wide enough
|
|
48
|
+
* (unless `names` is false).
|
|
46
49
|
* Hovering or focusing a segment shows its name, share and amount; each segment is
|
|
47
50
|
* a tab stop with the same words. Name every part beside the bar too (a legend or a
|
|
48
51
|
* table): a colour alone never says which part is which.
|
|
@@ -62,12 +65,14 @@ export function shareBar(parts: readonly SharePart[], o: ShareBarOptions = {}):
|
|
|
62
65
|
><span class="share-tip" aria-hidden="true">${words(p, i)}</span></span>`,
|
|
63
66
|
)}
|
|
64
67
|
</div>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
${o.names === false
|
|
69
|
+
? nothing
|
|
70
|
+
: html`<div class="share-names" aria-hidden="true">
|
|
71
|
+
${shown.map(
|
|
72
|
+
(p, i) => html`<span style=${styleMap({ flexGrow: String(p.value / total) })}
|
|
73
|
+
>${p.value / total >= labelAt ? html`<b>${p.label}</b> ${sharePercent(pcts[i]!, p.value)}` : nothing}</span>`,
|
|
74
|
+
)}
|
|
75
|
+
</div>`}
|
|
71
76
|
</div>`;
|
|
72
77
|
}
|
|
73
78
|
|
package/src/tiles/stat-tiles.ts
CHANGED
|
@@ -13,7 +13,8 @@ const reducedMotion = () => globalThis.matchMedia?.('(prefers-reduced-motion: re
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Figures as a grid of tiles: each its label, its value (a number counts up in
|
|
16
|
-
* `<cae-count>`)
|
|
16
|
+
* `<cae-count>`) and the lines of facts under it. A tile with an action is a
|
|
17
|
+
* link as a whole, named by the action's label. A tile whose number
|
|
17
18
|
* changes bumps, unless `bump` is off or the reader asks for reduced motion.
|
|
18
19
|
*
|
|
19
20
|
* Its look: `columns` (fewer in a narrow element: 4 below 1100px of its
|
|
@@ -26,7 +27,7 @@ const reducedMotion = () => globalThis.matchMedia?.('(prefers-reduced-motion: re
|
|
|
26
27
|
* @csspart label - a tile's label
|
|
27
28
|
* @csspart value - a tile's value
|
|
28
29
|
* @csspart sub - each line under the value
|
|
29
|
-
* @csspart action - a tile's link
|
|
30
|
+
* @csspart action - a tile's link, over the whole tile
|
|
30
31
|
*/
|
|
31
32
|
export class CaeStatTiles extends LitElement {
|
|
32
33
|
static override styles = [base, statTilesStyles];
|
|
@@ -63,10 +64,10 @@ export class CaeStatTiles extends LitElement {
|
|
|
63
64
|
<div class="value" part="value">${typeof t.value === 'number' ? html`<cae-count .value=${t.value}></cae-count>` : t.value}</div>
|
|
64
65
|
${(t.sub ?? []).map((line) => html`<div class="sub words" part="sub">${factLine(line)}</div>`)}
|
|
65
66
|
${t.action
|
|
66
|
-
? html`<
|
|
67
|
+
? html`<a class="action" part="action" href="#" @click=${(e: Event) => {
|
|
67
68
|
e.preventDefault();
|
|
68
69
|
if (t.action) emit(this, 'cae-action', { id: t.id, action: t.action.id });
|
|
69
|
-
}}>${t.action.label}</a
|
|
70
|
+
}}><span class="sr">${t.action.label}</span></a>`
|
|
70
71
|
: nothing}
|
|
71
72
|
</div>`;
|
|
72
73
|
}
|
package/src/tiles/styles.ts
CHANGED
|
@@ -16,7 +16,10 @@ export const statTilesStyles = css`
|
|
|
16
16
|
.label { font-size: 12px; font-weight: 600; color: var(--text-muted); }
|
|
17
17
|
.value { overflow-wrap: anywhere; font-size: 28px; font-weight: 700; line-height: 1.15; margin-top: 6px; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; color: var(--text); }
|
|
18
18
|
.sub { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
|
|
19
|
-
|
|
19
|
+
/* a tile with an action is a link as a whole: the link covers the tile */
|
|
20
|
+
.action { position: absolute; inset: 0; border-radius: inherit; }
|
|
21
|
+
.action:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
22
|
+
.tile:has(.action):hover { border-color: var(--accent); }
|
|
20
23
|
.tile[data-tone] { --edge: var(--tone); }
|
|
21
24
|
.tile[data-tone] .value, .tile[data-tone] .label::before { color: var(--tone); }
|
|
22
25
|
.label[data-mark]::before { display: inline-block; }
|
package/src/tiles/types.ts
CHANGED
|
@@ -15,6 +15,6 @@ export interface StatTile {
|
|
|
15
15
|
tip?: string;
|
|
16
16
|
/** The lines under the value, each a row of facts (`factLine`); an empty line keeps its place. */
|
|
17
17
|
sub?: readonly (readonly (string | Fact)[])[];
|
|
18
|
-
/**
|
|
18
|
+
/** Makes the whole tile a link named `label`: a click sends `cae-action` with the tile's id and this id. */
|
|
19
19
|
action?: { id: string; label: string };
|
|
20
20
|
}
|
package/src/transcript/styles.ts
CHANGED
|
@@ -7,7 +7,6 @@ export const transcriptStyles = css`
|
|
|
7
7
|
:host([mode='tabs']) .scroller, :host([mode='tabs']) .newpill { display: none; }
|
|
8
8
|
|
|
9
9
|
.rounds { flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 10px; padding-left: 2px; }
|
|
10
|
-
.rounds-lbl { color: var(--text-muted); font-size: 11.5px; font-weight: 600; margin-right: 4px; cursor: help; }
|
|
11
10
|
.stab { padding: 3px 11px; border: 1px solid var(--line); border-radius: 999px; background: var(--card); color: var(--text-muted);
|
|
12
11
|
cursor: pointer; font-size: 12px; display: inline-flex; align-items: center; }
|
|
13
12
|
.stab:hover { border-color: var(--accent); }
|
|
@@ -254,8 +254,8 @@ export class CaeTranscript extends LitElement {
|
|
|
254
254
|
|
|
255
255
|
private renderPicker(sections: readonly TranscriptSection[], shown: number, s: TranscriptStrings) {
|
|
256
256
|
if (sections.length < 2) return nothing;
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
// The buttons say what they pick: the picker's name is for screen readers, its tooltip on the group.
|
|
258
|
+
return html`<div class="rounds" role="group" aria-label=${s.sections} title=${s.sectionsTip || nothing} part="picker">
|
|
259
259
|
${sections.map((sec, i) => {
|
|
260
260
|
const v = sec.verdict;
|
|
261
261
|
return html`<button type="button" class="stab ${i === shown ? 'sel' : ''}" title=${v?.tip || nothing} aria-pressed=${i === shown}
|
|
@@ -320,10 +320,13 @@ export class CaeTranscript extends LitElement {
|
|
|
320
320
|
return unsafeHTML(out);
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
|
|
323
|
+
/** A bubble under its head; `named`: no head, the avatar carries the name (the agent's own messages). */
|
|
324
|
+
private bubble(av: string, avCls: string, head: string, body: TemplateResult | string, title?: string, named = false) {
|
|
324
325
|
return html`<div class="msg" part="bubble" title=${title ?? nothing}>
|
|
325
|
-
|
|
326
|
-
|
|
326
|
+
${named
|
|
327
|
+
? html`<div class="av ${avCls}" role="img" aria-label=${head} title=${head}>${av}</div>`
|
|
328
|
+
: html`<div class="av ${avCls}" aria-hidden="true">${av}</div>`}
|
|
329
|
+
<div class="bd">${named ? nothing : html`<div class="hd">${head}</div>`}${body}</div>
|
|
327
330
|
</div>`;
|
|
328
331
|
}
|
|
329
332
|
|
|
@@ -332,7 +335,7 @@ export class CaeTranscript extends LitElement {
|
|
|
332
335
|
case 'prompt':
|
|
333
336
|
return this.bubble('▸', 'av-l', s.prompt, html`<div class="tx meta">${s.characters(it.text.length)}</div>`, it.text.slice(0, 600));
|
|
334
337
|
case 'message':
|
|
335
|
-
return this.bubble('A', 'av-a', s.assistant, html`<div class="tx">${this.md(it.id, it.text)}</div
|
|
338
|
+
return this.bubble('A', 'av-a', s.assistant, html`<div class="tx">${this.md(it.id, it.text)}</div>`, undefined, true);
|
|
336
339
|
case 'reasoning':
|
|
337
340
|
return this.bubble('✦', 'av-t', s.reasoning, html`<div class="tx">${this.md(it.id, it.text)}</div>`);
|
|
338
341
|
case 'event':
|
|
@@ -347,7 +350,7 @@ export class CaeTranscript extends LitElement {
|
|
|
347
350
|
}
|
|
348
351
|
|
|
349
352
|
private typing(s: TranscriptStrings) {
|
|
350
|
-
return html`<div class="typing"><span class="av av-a">A</span><span class="dots"><i></i><i></i><i></i></span><span class="meta">${s.running}</span></div>`;
|
|
353
|
+
return html`<div class="typing"><span class="av av-a" aria-hidden="true">A</span><span class="dots"><i></i><i></i><i></i></span><span class="meta">${s.running}</span></div>`;
|
|
351
354
|
}
|
|
352
355
|
}
|
|
353
356
|
|
package/src/transcript/types.ts
CHANGED
|
@@ -106,7 +106,7 @@ export interface TranscriptMemory {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
export interface TranscriptStrings {
|
|
109
|
-
/** The section picker's name. */
|
|
109
|
+
/** The section picker's accessible name (not shown). */
|
|
110
110
|
sections: string;
|
|
111
111
|
/** The section picker's tooltip. */
|
|
112
112
|
sectionsTip: string;
|
|
@@ -117,6 +117,7 @@ export interface TranscriptStrings {
|
|
|
117
117
|
/** As `newBelow`, while another section than the latest is shown. */
|
|
118
118
|
newInLatest: (n: number) => string;
|
|
119
119
|
prompt: string;
|
|
120
|
+
/** The name of the agent's messages: their avatar's accessible name and tooltip (no head is shown). */
|
|
120
121
|
assistant: string;
|
|
121
122
|
reasoning: string;
|
|
122
123
|
characters: (n: number) => string;
|