@artooi/ag-ui-web-component 0.47.0 → 0.49.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 +205 -1
- package/README.md +235 -16
- package/dist/ag-ui-web-component.bundle.js +647 -181
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +27 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +44 -5
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/events/bar_height_detail.d.ts +6 -0
- package/dist/core/events/bar_height_detail.d.ts.map +1 -0
- package/dist/dom/highlight_overlay.d.ts +2 -1
- package/dist/dom/highlight_overlay.d.ts.map +1 -1
- package/dist/dom/ring_color.d.ts +8 -0
- package/dist/dom/ring_color.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1184 -69
- package/dist/index.js.map +4 -4
- package/dist/tools/chat_surface_tools.d.ts +8 -1
- package/dist/tools/chat_surface_tools.d.ts.map +1 -1
- package/dist/ui/history/conversation_history.d.ts +16 -3
- package/dist/ui/history/conversation_history.d.ts.map +1 -1
- package/dist/ui/placement/collapse_styles.d.ts.map +1 -1
- package/dist/ui/placement/dock_styles.d.ts +2 -0
- package/dist/ui/placement/dock_styles.d.ts.map +1 -0
- package/dist/ui/placement/panel_placement.d.ts +16 -0
- package/dist/ui/placement/panel_placement.d.ts.map +1 -1
- package/dist/ui/placement/small_viewport_styles.d.ts.map +1 -1
- package/dist/ui/placement/surface_for.d.ts +32 -19
- package/dist/ui/placement/surface_for.d.ts.map +1 -1
- package/dist/ui/progress/tool_group_styles.d.ts.map +1 -1
- package/dist/ui/progress/tool_step_styles.d.ts.map +1 -1
- package/dist/ui/shell/build_launcher.d.ts +15 -4
- package/dist/ui/shell/build_launcher.d.ts.map +1 -1
- package/dist/ui/shell/token_styles.d.ts.map +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/themes/quiet_styles.d.ts.map +1 -1
- package/dist/ui/themes/scheme_styles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +30 -2
- package/src/core/ag_ui_chat.ts +335 -28
- package/src/core/events/bar_height_detail.ts +5 -0
- package/src/dom/highlight_overlay.ts +2 -1
- package/src/dom/ring_color.ts +141 -4
- package/src/index.ts +4 -0
- package/src/tools/chat_surface_tools.ts +13 -5
- package/src/ui/history/conversation_history.ts +51 -7
- package/src/ui/placement/collapse_styles.ts +248 -2
- package/src/ui/placement/dock_styles.ts +95 -0
- package/src/ui/placement/panel_placement.ts +300 -16
- package/src/ui/placement/small_viewport_styles.ts +9 -0
- package/src/ui/placement/surface_for.ts +58 -22
- package/src/ui/progress/tool_group_styles.ts +52 -2
- package/src/ui/progress/tool_step_styles.ts +5 -3
- package/src/ui/shell/build_launcher.ts +32 -5
- package/src/ui/shell/token_styles.ts +58 -1
- package/src/ui/styles.ts +4 -0
- package/src/ui/themes/quiet_styles.ts +19 -3
- package/src/ui/themes/scheme_styles.ts +10 -1
- package/src/version.ts +1 -1
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import type { ResizeAxis } from "./resize_handle.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* What a collapsed panel shrinks to
|
|
5
|
-
*
|
|
6
|
-
* Only `null` versus not-`null` is read today: every caller asks whether the
|
|
7
|
-
* surface can collapse at all, never which target it collapses to. The three
|
|
8
|
-
* names below therefore describe the surface rather than drive it, and nothing
|
|
9
|
-
* relies on them (changing one fails only the test of this record). The
|
|
10
|
-
* bottom-bar collapse is expected to read or replace them, and a reader that
|
|
11
|
-
* does should come with a test outside this record's own.
|
|
4
|
+
* What a collapsed panel shrinks to, stamped on the element as
|
|
5
|
+
* data-collapsed-to so the stylesheet draws it.
|
|
12
6
|
*
|
|
13
7
|
* - `launcher` -- the round button in a corner.
|
|
14
8
|
* - `rail` -- the slim full-height edge the sidebar docks against; the same
|
|
15
9
|
* launcher element, shaped by the stylesheet.
|
|
16
10
|
* - `header` -- the in-flow collapse: the body hides and the header bar stays
|
|
17
11
|
* in the host's layout, so there is no launcher at all.
|
|
12
|
+
* - `bar` -- `collapse-to="bar"` over a placement that would otherwise
|
|
13
|
+
* collapse to `launcher`: a strip along the bottom edge, the panel rising
|
|
14
|
+
* from its right end.
|
|
15
|
+
* - `none` -- `collapse-to="none"` over any collapsible placement: collapsed
|
|
16
|
+
* is hidden, and the host's own control brings the panel back.
|
|
18
17
|
* - `null` -- no collapsed state. `page` is a dedicated route rather than a
|
|
19
18
|
* panel on someone else's page, so there is no "away" for it to go to:
|
|
20
19
|
* collapsing it left a strip of application chrome fixed over a route with
|
|
@@ -25,7 +24,7 @@ import type { ResizeAxis } from "./resize_handle.js";
|
|
|
25
24
|
* those paths asks this record; the stylesheet covers the one path that
|
|
26
25
|
* never does, an attribute written straight onto the element.
|
|
27
26
|
*/
|
|
28
|
-
export type CollapseTarget = "launcher" | "rail" | "header" | null;
|
|
27
|
+
export type CollapseTarget = "launcher" | "rail" | "header" | "bar" | "none" | null;
|
|
29
28
|
|
|
30
29
|
/**
|
|
31
30
|
* Everything the script needs to know about one display surface, in one
|
|
@@ -36,18 +35,22 @@ export type CollapseTarget = "launcher" | "rail" | "header" | null;
|
|
|
36
35
|
* under this directory); this is the part of a surface that code decides.
|
|
37
36
|
*/
|
|
38
37
|
export interface Surface {
|
|
39
|
-
/**
|
|
40
|
-
* What collapsing shrinks the panel to; `null` when it cannot collapse. Only
|
|
41
|
-
* the `null` test is authoritative: the non-null names are descriptive (see
|
|
42
|
-
* {@link CollapseTarget}), and nothing branches on which one a surface has.
|
|
43
|
-
*/
|
|
38
|
+
/** What collapsing shrinks the panel to; `null` when it cannot collapse. */
|
|
44
39
|
readonly collapseTo: CollapseTarget;
|
|
45
40
|
/**
|
|
46
41
|
* Whether a gesture may move the launcher. The rest have nowhere to put it:
|
|
47
|
-
* the rail is a docked edge, `header` and `null` have no launcher,
|
|
48
|
-
* full-bleed panel covers the screen it would be opening into
|
|
42
|
+
* the rail is a docked edge, `header` and `null` have no launcher, a
|
|
43
|
+
* full-bleed panel covers the screen it would be opening into, and the bar
|
|
44
|
+
* and the host's own control are not a launcher at all.
|
|
49
45
|
*/
|
|
50
46
|
readonly launcherDraggable: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the header drag and `moveTo` may move the open panel. The same
|
|
49
|
+
* answer as {@link launcherDraggable} for every placement on its own; the two
|
|
50
|
+
* part under `collapse-to="bar"` and `"none"`, where a corner panel still
|
|
51
|
+
* moves like one but has no launcher left to drag.
|
|
52
|
+
*/
|
|
53
|
+
readonly panelMovable: boolean;
|
|
51
54
|
/**
|
|
52
55
|
* Which axes the resize grips may change. A full-bleed layout is the whole
|
|
53
56
|
* viewport by definition and cannot be resized; a docked panel owns its
|
|
@@ -72,6 +75,7 @@ export interface Surface {
|
|
|
72
75
|
const CORNER: Surface = {
|
|
73
76
|
collapseTo: "launcher",
|
|
74
77
|
launcherDraggable: true,
|
|
78
|
+
panelMovable: true,
|
|
75
79
|
resizeAxis: "both",
|
|
76
80
|
startsCollapsed: true,
|
|
77
81
|
};
|
|
@@ -79,6 +83,7 @@ const CORNER: Surface = {
|
|
|
79
83
|
const DOCKED: Surface = {
|
|
80
84
|
collapseTo: "launcher",
|
|
81
85
|
launcherDraggable: false,
|
|
86
|
+
panelMovable: false,
|
|
82
87
|
resizeAxis: "width",
|
|
83
88
|
startsCollapsed: false,
|
|
84
89
|
};
|
|
@@ -99,17 +104,30 @@ const SURFACES: ReadonlyMap<string | null, Surface> = new Map<string | null, Sur
|
|
|
99
104
|
{
|
|
100
105
|
collapseTo: "launcher",
|
|
101
106
|
launcherDraggable: false,
|
|
107
|
+
panelMovable: false,
|
|
102
108
|
resizeAxis: "none",
|
|
103
109
|
startsCollapsed: false,
|
|
104
110
|
},
|
|
105
111
|
],
|
|
106
112
|
[
|
|
107
113
|
"page",
|
|
108
|
-
{
|
|
114
|
+
{
|
|
115
|
+
collapseTo: null,
|
|
116
|
+
launcherDraggable: false,
|
|
117
|
+
panelMovable: false,
|
|
118
|
+
resizeAxis: "none",
|
|
119
|
+
startsCollapsed: false,
|
|
120
|
+
},
|
|
109
121
|
],
|
|
110
122
|
[
|
|
111
123
|
"embedded",
|
|
112
|
-
{
|
|
124
|
+
{
|
|
125
|
+
collapseTo: "header",
|
|
126
|
+
launcherDraggable: false,
|
|
127
|
+
panelMovable: false,
|
|
128
|
+
resizeAxis: "both",
|
|
129
|
+
startsCollapsed: false,
|
|
130
|
+
},
|
|
113
131
|
],
|
|
114
132
|
]);
|
|
115
133
|
|
|
@@ -122,14 +140,32 @@ const SURFACES: ReadonlyMap<string | null, Surface> = new Map<string | null, Sur
|
|
|
122
140
|
const UNKNOWN: Surface = {
|
|
123
141
|
collapseTo: "launcher",
|
|
124
142
|
launcherDraggable: false,
|
|
143
|
+
panelMovable: false,
|
|
125
144
|
resizeAxis: "both",
|
|
126
145
|
startsCollapsed: false,
|
|
127
146
|
};
|
|
128
147
|
|
|
129
148
|
/**
|
|
130
|
-
* The surface `placement`
|
|
131
|
-
* because
|
|
149
|
+
* The surface `placement` and `collapse-to` describe together. Read per
|
|
150
|
+
* interaction by every caller, because both are live attributes.
|
|
151
|
+
*
|
|
152
|
+
* `collapse-to` only ever replaces the round launcher or the whole collapsed
|
|
153
|
+
* state, never a placement's own shape: `bar` applies where the placement
|
|
154
|
+
* collapses to `launcher`, so the sidebar keeps its rail and an embedded chat
|
|
155
|
+
* its header bar, and `none` applies to every collapsible placement. Absent,
|
|
156
|
+
* `auto` and any other value leave the placement as it was. Both turn the
|
|
157
|
+
* launcher drag off, because there is no launcher left to drag, and keep the
|
|
158
|
+
* placement's own answer for moving the panel: a corner panel rises from the
|
|
159
|
+
* bar or the anchor until the user drags it somewhere else, and a placement
|
|
160
|
+
* that owns its position keeps owning it.
|
|
132
161
|
*/
|
|
133
|
-
export function surfaceFor(placement: string | null): Surface {
|
|
134
|
-
|
|
162
|
+
export function surfaceFor(placement: string | null, collapseTo: string | null = null): Surface {
|
|
163
|
+
const surface = SURFACES.get(placement) ?? UNKNOWN;
|
|
164
|
+
if (collapseTo === "bar" && surface.collapseTo === "launcher") {
|
|
165
|
+
return { ...surface, collapseTo: "bar", launcherDraggable: false };
|
|
166
|
+
}
|
|
167
|
+
if (collapseTo === "none" && surface.collapseTo !== null) {
|
|
168
|
+
return { ...surface, collapseTo: "none", launcherDraggable: false };
|
|
169
|
+
}
|
|
170
|
+
return surface;
|
|
135
171
|
}
|
|
@@ -21,6 +21,18 @@ const GROUPED = `:host(:where(:not(${CARD_MODES})))`;
|
|
|
21
21
|
/** The host in one of the four card modes, where a group drawn earlier is flattened. */
|
|
22
22
|
const CARDS = `:host(:is(${CARD_MODES}))`;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* A card inside an opened group, with the host in grouped mode and the given
|
|
26
|
+
* data-tool-group-display. The inner attribute sits inside the same :host()
|
|
27
|
+
* as the grouped test, since the host is featureless outside it, and being
|
|
28
|
+
* grouped-only is what lets an outer card mode flatten the group into its own
|
|
29
|
+
* cards: none of these rules match there. Full is no rule at all, so an absent
|
|
30
|
+
* or unknown inner value draws the full card a group always drew.
|
|
31
|
+
*/
|
|
32
|
+
function inner(mode: string): string {
|
|
33
|
+
return `:host(:where(:not(${CARD_MODES}))[data-tool-group-display="${mode}"]) .tool-group-list .tool-call`;
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
export const TOOL_GROUP_STYLES: string = `
|
|
25
37
|
/* ── The folded tool-step group ──────────────────────────────────
|
|
26
38
|
One quiet row per run of consecutive calls, the cards one click down as
|
|
@@ -161,7 +173,8 @@ ${GROUPED} .tool-group[data-open="false"] .tool-group-list {
|
|
|
161
173
|
|
|
162
174
|
/* A card inside the group is one line of the list: icon, name, and its
|
|
163
175
|
Details control at the end, with the arguments and result opening under
|
|
164
|
-
the whole row.
|
|
176
|
+
the whole row. Its ink is the second ink in every theme, unless the host
|
|
177
|
+
set the tool token, which then reaches the grouped calls too. */
|
|
165
178
|
${GROUPED} .tool-group .tool-call {
|
|
166
179
|
max-width: 100%;
|
|
167
180
|
align-self: stretch;
|
|
@@ -172,7 +185,7 @@ ${GROUPED} .tool-group .tool-call {
|
|
|
172
185
|
border: 0;
|
|
173
186
|
border-radius: 0;
|
|
174
187
|
background: transparent;
|
|
175
|
-
color: var(--_ink-2);
|
|
188
|
+
color: var(--_tool-fg-set, var(--_ink-2));
|
|
176
189
|
font-family: inherit;
|
|
177
190
|
font-size: var(--_text-sm);
|
|
178
191
|
}
|
|
@@ -205,6 +218,43 @@ ${GROUPED} .tool-group .tool-call-subagent {
|
|
|
205
218
|
flex-basis: 100%;
|
|
206
219
|
}
|
|
207
220
|
|
|
221
|
+
/* The inner display mode: how an opened group draws its calls. The same
|
|
222
|
+
visibility rules the card modes apply to a flat card, so a call inside the
|
|
223
|
+
group shows the regions the flat card of that mode shows. The flat inline
|
|
224
|
+
mode's chrome rule is left out on purpose: a card in the group has already
|
|
225
|
+
lost its box and is laid out as a row of the list, and that rule's padding
|
|
226
|
+
and gap would undo the row. Selected from the host attribute, so changing it
|
|
227
|
+
restyles groups already on screen. */
|
|
228
|
+
${inner("compact")}[data-expanded="false"] .tool-call-section {
|
|
229
|
+
display: none;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
${inner("inline")} .tool-call-section--args {
|
|
233
|
+
display: none;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
${inner("minimal")} .tool-call-toggle,
|
|
237
|
+
${inner("minimal")} .tool-call-body {
|
|
238
|
+
display: none;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* A call waiting on an approval shows its arguments in every inner mode, as it
|
|
242
|
+
does in every card mode, and for the same reason: the arguments are what
|
|
243
|
+
tell three gated calls of one tool apart. These come after the hides above,
|
|
244
|
+
which they tie with on weight. The arguments region only, never every
|
|
245
|
+
section, since the result region carries the hidden attribute until a
|
|
246
|
+
result exists and a display value here would override it. The arguments
|
|
247
|
+
region carries it too when the call has none, so it is excluded while
|
|
248
|
+
hidden rather than framed empty. */
|
|
249
|
+
${inner("minimal")}[data-status="deferred"] .tool-call-body {
|
|
250
|
+
display: flex;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
${inner("compact")}[data-status="deferred"] .tool-call-section--args:not([hidden]),
|
|
254
|
+
${inner("inline")}[data-status="deferred"] .tool-call-section--args:not([hidden]) {
|
|
255
|
+
display: flex;
|
|
256
|
+
}
|
|
257
|
+
|
|
208
258
|
/* Grouping is decided as each card is built, so a card mode set afterwards
|
|
209
259
|
finds groups already drawn. It flattens them: the group and its list stop
|
|
210
260
|
generating boxes, the row and the repeat ordinals go, and every card lays
|
|
@@ -277,9 +277,11 @@ export const TOOL_STEP_STYLES: string = `
|
|
|
277
277
|
|
|
278
278
|
/* The arguments region only, never every section: the result region carries the
|
|
279
279
|
hidden attribute until a result exists, and a display value here overrides it,
|
|
280
|
-
framing an empty RESULT heading under the question.
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
framing an empty RESULT heading under the question. The arguments region is
|
|
281
|
+
hidden too when the call has none, and is left alone then, or it would frame
|
|
282
|
+
an empty ARGUMENTS heading. */
|
|
283
|
+
:host([data-tool-display="compact"]) .tool-call[data-status="deferred"] .tool-call-section--args:not([hidden]),
|
|
284
|
+
:host([data-tool-display="inline"]) .tool-call[data-status="deferred"] .tool-call-section--args:not([hidden]) {
|
|
283
285
|
display: flex;
|
|
284
286
|
}
|
|
285
287
|
|
|
@@ -7,21 +7,32 @@ export interface LauncherOptions {
|
|
|
7
7
|
readonly launcher: HTMLButtonElement;
|
|
8
8
|
readonly badge: HTMLElement;
|
|
9
9
|
readonly railLabel: HTMLElement;
|
|
10
|
+
/** The bottom bar's wrapper, which holds the launcher and the history button. */
|
|
11
|
+
readonly bar: HTMLElement;
|
|
12
|
+
/** The bottom bar's history button, a sibling of the launcher in the bar. */
|
|
13
|
+
readonly barHistory: HTMLButtonElement;
|
|
10
14
|
readonly titleText: string;
|
|
11
15
|
readonly iconUrl: string | null;
|
|
12
16
|
readonly strings: UiStrings;
|
|
13
17
|
readonly signal: AbortSignal;
|
|
14
18
|
readonly onOpen: () => void;
|
|
19
|
+
readonly onHistory: () => void;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
|
-
* Build what a collapsed widget shrinks to: a round floating button,
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* the panel
|
|
23
|
+
* Build what a collapsed widget shrinks to: a round floating button, the slim
|
|
24
|
+
* edge rail under `placement="sidebar"`, or the strip along the bottom edge
|
|
25
|
+
* under `collapse-to="bar"` -- one element, shaped by the stylesheet. The
|
|
26
|
+
* element mounts the bar wrapper as a sibling of the panel, so it survives the
|
|
27
|
+
* panel being hidden.
|
|
28
|
+
*
|
|
29
|
+
* The wrapper is there for the bar's history button: a button cannot nest in
|
|
30
|
+
* a button, so the strip is the launcher and the history button side by side.
|
|
31
|
+
* Outside the bar the wrapper draws no box and the history button is not
|
|
32
|
+
* drawn, so the launcher lays out against the host exactly as it did alone.
|
|
22
33
|
*/
|
|
23
34
|
export function buildLauncher(options: LauncherOptions): void {
|
|
24
|
-
const { launcher, badge, railLabel } = options;
|
|
35
|
+
const { launcher, badge, railLabel, bar, barHistory } = options;
|
|
25
36
|
launcher.className = "launcher";
|
|
26
37
|
launcher.type = "button";
|
|
27
38
|
launcher.setAttribute("part", "launcher");
|
|
@@ -47,4 +58,20 @@ export function buildLauncher(options: LauncherOptions): void {
|
|
|
47
58
|
badge,
|
|
48
59
|
);
|
|
49
60
|
launcher.addEventListener("click", options.onOpen, { signal: options.signal });
|
|
61
|
+
bar.className = "dock-bar";
|
|
62
|
+
bar.setAttribute("part", "bar");
|
|
63
|
+
barHistory.className = "bar-history";
|
|
64
|
+
barHistory.type = "button";
|
|
65
|
+
barHistory.setAttribute("part", "bar-history");
|
|
66
|
+
barHistory.title = options.strings.chatHistory;
|
|
67
|
+
barHistory.setAttribute("aria-label", options.strings.chatHistory);
|
|
68
|
+
// A glyph rather than the header's icon slot: a slot name may appear once
|
|
69
|
+
// per shadow root, and the header's history button already holds it.
|
|
70
|
+
const glyph = document.createElement("span");
|
|
71
|
+
glyph.className = "bar-history-glyph";
|
|
72
|
+
glyph.setAttribute("aria-hidden", "true");
|
|
73
|
+
glyph.textContent = "\u2630";
|
|
74
|
+
barHistory.replaceChildren(glyph);
|
|
75
|
+
barHistory.addEventListener("click", options.onHistory, { signal: options.signal });
|
|
76
|
+
bar.replaceChildren(launcher, barHistory);
|
|
50
77
|
}
|
|
@@ -37,7 +37,15 @@ export const TOKEN_STYLES: string = `
|
|
|
37
37
|
--_line-strong: var(--ag-ui-line-strong, color-mix(in oklch, var(--_fg) 20%, var(--_bg)));
|
|
38
38
|
--_wash: var(--ag-ui-wash, color-mix(in oklch, var(--_fg) 4.5%, var(--_bg)));
|
|
39
39
|
--_accent-ink: var(--ag-ui-accent-ink, color-mix(in oklch, var(--_accent) 82%, var(--_fg)));
|
|
40
|
-
|
|
40
|
+
/* The focus ring is drawn on the panel, so it has to hold 3:1 against
|
|
41
|
+
--_bg whatever accent a host brings (WCAG 2.4.13). Two parts accent to
|
|
42
|
+
three parts ink does that at the worst case, an accent at the far end
|
|
43
|
+
from the ink (white on a light panel, black on a dark one): the ink
|
|
44
|
+
alone sits far above 3:1, and three fifths of it keeps the mix there
|
|
45
|
+
while the hue still reads as the accent. Half and half fell to 2.3:1
|
|
46
|
+
for black on the code theme. The accent over transparent, as before,
|
|
47
|
+
measured only the accent, and a light host accent fell to 1.6:1. */
|
|
48
|
+
--_ring: var(--ag-ui-ring, color-mix(in oklch, var(--_accent) 40%, var(--_fg)));
|
|
41
49
|
/* The user's turn is the accent at a whisper. With Quiet's graphite accent
|
|
42
50
|
that is a neutral wash; once a host sets a brand accent it becomes a tint
|
|
43
51
|
of the brand, which is the one thing Soft does that Quiet keeps. */
|
|
@@ -50,6 +58,23 @@ export const TOKEN_STYLES: string = `
|
|
|
50
58
|
--_tool-fg: var(--ag-ui-tool-fg, var(--_ink-2));
|
|
51
59
|
--_header-bg: var(--ag-ui-header-bg, var(--_bg));
|
|
52
60
|
--_header-fg: var(--ag-ui-header-fg, var(--_fg));
|
|
61
|
+
/* Switches: a public token read with no fallback, named for it with -set.
|
|
62
|
+
With the token unset the switch computes to the guaranteed-invalid value,
|
|
63
|
+
so a rule that reads it with a fallback gets the fallback; that is how a
|
|
64
|
+
rule tells a token the host set from the theme's default for it. The
|
|
65
|
+
default theme's header buttons follow the header text only once a host
|
|
66
|
+
sets it, and a step group's calls follow the tool token only once a host
|
|
67
|
+
sets it (classic defaults --_tool-fg to the accent, which a group never
|
|
68
|
+
drew). */
|
|
69
|
+
--_header-fg-set: var(--ag-ui-header-fg);
|
|
70
|
+
--_tool-fg-set: var(--ag-ui-tool-fg);
|
|
71
|
+
/* The header buttons' hover square, by default, once the header text is
|
|
72
|
+
set. Here it is the text at about a fifth, which every browser can mix;
|
|
73
|
+
where relative colour syntax is supported (see the block at the end) it
|
|
74
|
+
is the text's opposite instead. It reads the switch, so it is unset with
|
|
75
|
+
it, and the square below falls to the panel's hover shade. */
|
|
76
|
+
--_header-btn-hover-auto: color-mix(in srgb, var(--_header-fg-set) 18%, transparent);
|
|
77
|
+
--_header-btn-hover: var(--ag-ui-header-btn-hover, var(--_header-btn-hover-auto, var(--_hover)));
|
|
53
78
|
--_border: var(--ag-ui-border, var(--_line));
|
|
54
79
|
--_radius: var(--ag-ui-radius, 12px);
|
|
55
80
|
/* The panel's own frame, which is the one corner a placement can put against
|
|
@@ -300,6 +325,25 @@ export const TOKEN_STYLES: string = `
|
|
|
300
325
|
grid/flex layout) to embed it in the page flow instead of floating. */
|
|
301
326
|
--_position: var(--ag-ui-position, fixed);
|
|
302
327
|
--_z-index: var(--ag-ui-z-index, 2147483000);
|
|
328
|
+
/* A size the user dragged, written inline on the host by the placement
|
|
329
|
+
code, and read ahead of the host's tokens so the drag wins. There is no
|
|
330
|
+
public token behind it; it is declared initial here only so a chat that
|
|
331
|
+
was never dragged does not inherit one, from a dragged chat it is
|
|
332
|
+
slotted into or from any page rule naming it. The inline value still
|
|
333
|
+
beats this default. */
|
|
334
|
+
--_drag-width: initial;
|
|
335
|
+
--_drag-height: initial;
|
|
336
|
+
/* Where a docked panel stands, written inline the same way by the placement
|
|
337
|
+
code: the bottom bar's height, or the host anchor's distance from the
|
|
338
|
+
bottom and right edges. Read only by the dock rules, each with a 0px
|
|
339
|
+
fallback; declared initial so it never inherits either. */
|
|
340
|
+
--_dock-bottom: initial;
|
|
341
|
+
--_dock-right: initial;
|
|
342
|
+
/* The gap between a panel and the host's own button it rises from under
|
|
343
|
+
collapse-to="none". The button usually sits inside a taller toolbar, so a
|
|
344
|
+
host sets this to the button's distance from the toolbar's top plus the
|
|
345
|
+
clearance it wants, and the panel stands clear of the toolbar. */
|
|
346
|
+
--_anchor-gap: var(--ag-ui-anchor-gap, 8px);
|
|
303
347
|
--_width: var(--_drag-width, var(--ag-ui-width, 380px));
|
|
304
348
|
--_height: var(--_drag-height, var(--ag-ui-height, 560px));
|
|
305
349
|
/* The gutter a resting floating panel keeps between itself and the edge of
|
|
@@ -403,4 +447,17 @@ export const TOKEN_STYLES: string = `
|
|
|
403
447
|
--_on-accent: var(--ag-ui-on-accent, #ffffff);
|
|
404
448
|
}
|
|
405
449
|
}
|
|
450
|
+
|
|
451
|
+
/* The header buttons' default hover square as the header text's opposite,
|
|
452
|
+
so white text gets a darker square and dark text a lighter one. A square
|
|
453
|
+
mixed from the text itself moves the ground toward the glyph: white on a
|
|
454
|
+
mid-blue header fell from 5.1:1 to 3.75:1 under the pointer. Moving away
|
|
455
|
+
from it can only raise what the host's own pair reads. Gated on this
|
|
456
|
+
exact expression, so a browser that cannot compute it keeps the square
|
|
457
|
+
mixed from the text rather than losing the square altogether. */
|
|
458
|
+
@supports (color: rgb(from red calc(255 - r) calc(255 - g) calc(255 - b))) {
|
|
459
|
+
:host {
|
|
460
|
+
--_header-btn-hover-auto: color-mix(in srgb, rgb(from var(--_header-fg-set) calc(255 - r) calc(255 - g) calc(255 - b)) 18%, transparent);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
406
463
|
`;
|
package/src/ui/styles.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { APPROVAL_STYLES } from "./interrupts/approval_styles.js";
|
|
|
23
23
|
import { CONFIRMATION_STYLES } from "./interrupts/confirmation_styles.js";
|
|
24
24
|
import { QUESTION_STYLES } from "./interrupts/question_styles.js";
|
|
25
25
|
import { COLLAPSE_STYLES } from "./placement/collapse_styles.js";
|
|
26
|
+
import { DOCK_STYLES } from "./placement/dock_styles.js";
|
|
26
27
|
import { EMBEDDED_STYLES } from "./placement/embedded_styles.js";
|
|
27
28
|
import { LAUNCHER_STYLES } from "./placement/launcher_styles.js";
|
|
28
29
|
import { PAGE_STYLES } from "./placement/page_styles.js";
|
|
@@ -65,6 +66,9 @@ export const STYLES: string = [
|
|
|
65
66
|
THEME_STYLES,
|
|
66
67
|
PRESET_STYLES,
|
|
67
68
|
PAGE_STYLES,
|
|
69
|
+
// Ahead of the phone block and no heavier than it: a docked panel is still
|
|
70
|
+
// full-bleed on a phone.
|
|
71
|
+
DOCK_STYLES,
|
|
68
72
|
SMALL_VIEWPORT_STYLES,
|
|
69
73
|
SIDEBAR_STYLES,
|
|
70
74
|
LAUNCHER_STYLES,
|
|
@@ -24,14 +24,30 @@ ${THEME_HOST.quiet} .header-title {
|
|
|
24
24
|
letter-spacing: -0.01em;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/* The buttons sit back in the third ink on the panel. Once the host sets the
|
|
28
|
+
header text they draw in it instead, since the header behind them is then
|
|
29
|
+
the host's colour (see the switches in the token defaults). So does their
|
|
30
|
+
rest opacity: the frame's 0.85 lets the header through the glyph, which
|
|
31
|
+
took white on a mid-blue header from 5.1:1 to 4.2:1. With the header text
|
|
32
|
+
set, the opacity below reads a colour, which no opacity can be, so it is
|
|
33
|
+
invalid at computed-value time and falls to the initial 1; unset, it reads
|
|
34
|
+
the fallback, the frame's own 0.85. */
|
|
27
35
|
${THEME_HOST.quiet} .header-btn {
|
|
28
|
-
color: var(--_ink-3);
|
|
36
|
+
color: var(--_header-fg-set, var(--_ink-3));
|
|
37
|
+
opacity: var(--_header-fg-set, 0.85);
|
|
29
38
|
border-radius: 8px;
|
|
30
39
|
}
|
|
31
40
|
|
|
41
|
+
/* The hover square is the host's own when it set one; otherwise it is mixed
|
|
42
|
+
from the header text when the host set that, so it reads on any header the
|
|
43
|
+
host painted; otherwise it is the panel shade.
|
|
44
|
+
The glyph takes the header text, which is the full ink when that is unset.
|
|
45
|
+
The opacity restates the frame's hover value, which the rest rule above
|
|
46
|
+
outweighs. */
|
|
32
47
|
${THEME_HOST.quiet} .header-btn:hover {
|
|
33
|
-
color: var(--
|
|
34
|
-
|
|
48
|
+
color: var(--_header-fg);
|
|
49
|
+
opacity: 1;
|
|
50
|
+
background: var(--_header-btn-hover);
|
|
35
51
|
}
|
|
36
52
|
|
|
37
53
|
${THEME_HOST.quiet} .message--user {
|
|
@@ -6,9 +6,18 @@ export const SCHEME_STYLES: string = `
|
|
|
6
6
|
dark) and beside a theme name (soft dark). auto follows the operating
|
|
7
7
|
system through color-scheme itself, with no media query to keep in step.
|
|
8
8
|
code is a dark-only theme and has no light half. */
|
|
9
|
+
/* Important, so a scheme word holds against a host rule that sets the
|
|
10
|
+
element's color-scheme (a page that follows its own dark mode). A rule
|
|
11
|
+
from outside the shadow tree beats every :host rule at normal weight, and
|
|
12
|
+
the theme toggle writes the word; without this its press changed nothing.
|
|
13
|
+
code stays last, so code light is still dark. */
|
|
14
|
+
:host([theme~="light"]) {
|
|
15
|
+
color-scheme: light !important;
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
:host([theme~="dark"]),
|
|
10
19
|
:host([theme~="code"]) {
|
|
11
|
-
color-scheme: dark;
|
|
20
|
+
color-scheme: dark !important;
|
|
12
21
|
}
|
|
13
22
|
|
|
14
23
|
:host([theme~="auto"]) {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION: string = "0.
|
|
1
|
+
export const VERSION: string = "0.49.0";
|