@artooi/ag-ui-web-component 0.32.0 → 0.33.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/CHANGELOG.md +194 -1
- package/README.md +182 -23
- package/dist/ag-ui-web-component.bundle.js +360 -121
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/index.js +1275 -390
- package/dist/index.js.map +4 -4
- package/dist/ui/clamp_launcher.d.ts +15 -0
- package/dist/ui/clamp_launcher.d.ts.map +1 -0
- package/dist/ui/copy_payload.d.ts +30 -0
- package/dist/ui/copy_payload.d.ts.map +1 -0
- package/dist/ui/launcher_drag.d.ts +43 -0
- package/dist/ui/launcher_drag.d.ts.map +1 -0
- package/dist/ui/launcher_placement.d.ts +64 -0
- package/dist/ui/launcher_placement.d.ts.map +1 -0
- package/dist/ui/message_actions.d.ts +30 -2
- package/dist/ui/message_actions.d.ts.map +1 -1
- package/dist/ui/resize_handle.d.ts +32 -26
- package/dist/ui/resize_handle.d.ts.map +1 -1
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +12 -0
- package/src/core/ag_ui_chat.ts +480 -24
- package/src/ui/clamp_launcher.ts +20 -0
- package/src/ui/copy_payload.ts +156 -0
- package/src/ui/launcher_drag.ts +182 -0
- package/src/ui/launcher_placement.ts +143 -0
- package/src/ui/message_actions.ts +93 -19
- package/src/ui/resize_handle.ts +109 -73
- package/src/ui/styles.ts +299 -82
- package/src/version.ts +1 -1
package/src/ui/styles.ts
CHANGED
|
@@ -45,6 +45,30 @@ export const STYLES = `
|
|
|
45
45
|
--_text: var(--ag-ui-text, var(--_fg));
|
|
46
46
|
--_surface: var(--ag-ui-surface, transparent);
|
|
47
47
|
|
|
48
|
+
/* Message action row: the control box and the mark inside it. The box has a
|
|
49
|
+
floor of 24px so it stays a reliable target at every density. */
|
|
50
|
+
--_action-size: var(--ag-ui-action-size, 28px);
|
|
51
|
+
--_action-icon-size: var(--ag-ui-action-icon-size, 15px);
|
|
52
|
+
--_tooltip-bg: var(--ag-ui-tooltip-bg, #1f2430);
|
|
53
|
+
--_tooltip-fg: var(--ag-ui-tooltip-fg, #f5f6fa);
|
|
54
|
+
|
|
55
|
+
/* Text and marks drawn on top of the accent and danger fills. Separate
|
|
56
|
+
tokens because they are not free: a host that themes the accent to a pale
|
|
57
|
+
colour has white-on-pale with no way to correct it, which is the one
|
|
58
|
+
theming change that makes a control unreadable rather than merely off. */
|
|
59
|
+
--_on-accent: var(--ag-ui-on-accent, #ffffff);
|
|
60
|
+
--_on-danger: var(--ag-ui-on-danger, #ffffff);
|
|
61
|
+
|
|
62
|
+
/* Resize grips: the corner squares and the edge strips between them. Wide
|
|
63
|
+
enough to hit without being wide enough to swallow a click on the content
|
|
64
|
+
underneath, and adjustable for a host with a coarser pointer. */
|
|
65
|
+
--_grip-corner: var(--ag-ui-grip-corner, 14px);
|
|
66
|
+
--_grip-edge: var(--ag-ui-grip-edge, 6px);
|
|
67
|
+
--_grip-edge-docked: var(--ag-ui-grip-edge-docked, 8px);
|
|
68
|
+
/* The mark drawn inside a grip, as opposed to the area it answers to. */
|
|
69
|
+
--_grip-mark-length: var(--ag-ui-grip-mark-length, 28px);
|
|
70
|
+
--_grip-mark-thickness: var(--ag-ui-grip-mark-thickness, 3px);
|
|
71
|
+
|
|
48
72
|
/* Status accents for tool-call cards. */
|
|
49
73
|
--_success: var(--ag-ui-success, #15803d);
|
|
50
74
|
--_danger: var(--ag-ui-danger, #b91c1c);
|
|
@@ -55,6 +79,12 @@ export const STYLES = `
|
|
|
55
79
|
--_tool-icon-done: var(--ag-ui-tool-icon-done, "✓");
|
|
56
80
|
--_tool-icon-error: var(--ag-ui-tool-icon-error, "✕");
|
|
57
81
|
--_tool-icon-declined: var(--ag-ui-tool-icon-declined, "⊘");
|
|
82
|
+
|
|
83
|
+
/* Disclosure marks on every expandable row. Tokenised for the same reason
|
|
84
|
+
the status icons above are: a host re-theming one set and not the other
|
|
85
|
+
ends up with two vocabularies in one transcript. */
|
|
86
|
+
--_disclosure-collapsed: var(--ag-ui-disclosure-collapsed, "▸");
|
|
87
|
+
--_disclosure-expanded: var(--ag-ui-disclosure-expanded, "▾");
|
|
58
88
|
--_tool-spin-duration: var(--ag-ui-tool-spin-duration, 0.7s);
|
|
59
89
|
|
|
60
90
|
/* Answer well (opt-in via data-answer-well) — boxes a whole assistant turn. */
|
|
@@ -335,6 +365,9 @@ export const STYLES = `
|
|
|
335
365
|
font: inherit;
|
|
336
366
|
cursor: pointer;
|
|
337
367
|
pointer-events: auto;
|
|
368
|
+
/* A touch drag on the launcher moves it; without this the page scrolls under
|
|
369
|
+
the finger instead and the launcher never moves at all. */
|
|
370
|
+
touch-action: none;
|
|
338
371
|
opacity: 0;
|
|
339
372
|
transform: scale(0.4);
|
|
340
373
|
visibility: hidden;
|
|
@@ -364,6 +397,15 @@ export const STYLES = `
|
|
|
364
397
|
transform: scale(0.94);
|
|
365
398
|
}
|
|
366
399
|
|
|
400
|
+
/* A drag is a press and a hover at the same time, so both scale rules above are
|
|
401
|
+
live throughout it. Cancelling them is what keeps the launcher the size of
|
|
402
|
+
the thing under the pointer while it travels; the position itself comes from
|
|
403
|
+
inset, which nothing here transitions, so it tracks the pointer exactly. */
|
|
404
|
+
:host([collapsed]) .launcher[data-dragging] {
|
|
405
|
+
transform: none;
|
|
406
|
+
cursor: grabbing;
|
|
407
|
+
}
|
|
408
|
+
|
|
367
409
|
.launcher .icon-holder {
|
|
368
410
|
width: var(--_launcher-icon-size);
|
|
369
411
|
height: var(--_launcher-icon-size);
|
|
@@ -558,6 +600,31 @@ export const STYLES = `
|
|
|
558
600
|
transform-origin: bottom left;
|
|
559
601
|
}
|
|
560
602
|
|
|
603
|
+
/* Once the launcher has been dragged the element places itself, and stamps the
|
|
604
|
+
corner it chose to open away from. The morph has to start at that same
|
|
605
|
+
corner: scaling out of the one the placement originally guessed reads as the
|
|
606
|
+
panel leaping across the screen before it opens.
|
|
607
|
+
|
|
608
|
+
Equal specificity to the placement rule above, so source order is what lets
|
|
609
|
+
the stamped value win -- the same arrangement the resize grip uses, and the
|
|
610
|
+
same trap. Write these with = on the whole hyphenated token; a ~= would match
|
|
611
|
+
whitespace-separated words and so could never match at all. */
|
|
612
|
+
:host([data-expand-corner="top-left"]) .chat {
|
|
613
|
+
transform-origin: top left;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
:host([data-expand-corner="top-right"]) .chat {
|
|
617
|
+
transform-origin: top right;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
:host([data-expand-corner="bottom-left"]) .chat {
|
|
621
|
+
transform-origin: bottom left;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
:host([data-expand-corner="bottom-right"]) .chat {
|
|
625
|
+
transform-origin: bottom right;
|
|
626
|
+
}
|
|
627
|
+
|
|
561
628
|
/* The two in-flow placements keep the original collapse: hide the body, let
|
|
562
629
|
the host shrink to the header bar. */
|
|
563
630
|
:host([collapsed]:is([placement="embedded"], [placement="page"])) {
|
|
@@ -741,7 +808,22 @@ export const STYLES = `
|
|
|
741
808
|
border-radius: var(--_msg-radius);
|
|
742
809
|
line-height: 1.4;
|
|
743
810
|
white-space: pre-wrap;
|
|
744
|
-
word-break
|
|
811
|
+
/* overflow-wrap, not word-break, and the difference is the whole reason a
|
|
812
|
+
markdown table used to be unreadable.
|
|
813
|
+
|
|
814
|
+
Both stop one long unbroken token blowing out the bubble, which is all this
|
|
815
|
+
is for. But word-break: break-word is the legacy spelling of "break
|
|
816
|
+
anywhere", and breaking anywhere drops the *min-content* width of every
|
|
817
|
+
descendant to one character. A table's column algorithm takes min-content
|
|
818
|
+
as an input, so the table always fitted max-width: 100%, the overflow-x:
|
|
819
|
+
auto below it never had anything to scroll, and the columns absorbed the
|
|
820
|
+
pressure by rendering one letter per line instead. A seven-column header
|
|
821
|
+
came out 162px tall.
|
|
822
|
+
|
|
823
|
+
overflow-wrap: break-word breaks a word only when it would otherwise
|
|
824
|
+
overflow its line, and leaves min-content alone -- so the bubble is still
|
|
825
|
+
protected and the table is free to be wider than the panel and scroll. */
|
|
826
|
+
overflow-wrap: break-word;
|
|
745
827
|
}
|
|
746
828
|
|
|
747
829
|
/* ── Incoming-text animations (data-text-animation) ─────────────────────── */
|
|
@@ -983,11 +1065,11 @@ export const STYLES = `
|
|
|
983
1065
|
}
|
|
984
1066
|
|
|
985
1067
|
.thoughts-toggle::before {
|
|
986
|
-
content: "
|
|
1068
|
+
content: var(--_disclosure-expanded) " ";
|
|
987
1069
|
}
|
|
988
1070
|
|
|
989
1071
|
.thoughts-toggle[aria-expanded="false"]::before {
|
|
990
|
-
content: "
|
|
1072
|
+
content: var(--_disclosure-collapsed) " ";
|
|
991
1073
|
}
|
|
992
1074
|
|
|
993
1075
|
/* A gentle pulse on the label while reasoning is still streaming. */
|
|
@@ -1329,14 +1411,14 @@ export const STYLES = `
|
|
|
1329
1411
|
}
|
|
1330
1412
|
|
|
1331
1413
|
.subagent-row::after {
|
|
1332
|
-
content:
|
|
1414
|
+
content: var(--_disclosure-collapsed);
|
|
1333
1415
|
flex: none;
|
|
1334
1416
|
margin-left: auto;
|
|
1335
1417
|
color: var(--_accent);
|
|
1336
1418
|
}
|
|
1337
1419
|
|
|
1338
1420
|
.subagent-row[aria-expanded="true"]::after {
|
|
1339
|
-
content:
|
|
1421
|
+
content: var(--_disclosure-expanded);
|
|
1340
1422
|
}
|
|
1341
1423
|
|
|
1342
1424
|
.subagent-row:disabled::after {
|
|
@@ -1463,16 +1545,23 @@ export const STYLES = `
|
|
|
1463
1545
|
}
|
|
1464
1546
|
|
|
1465
1547
|
.tool-call-toggle::before {
|
|
1466
|
-
content: "
|
|
1548
|
+
content: var(--_disclosure-collapsed) " ";
|
|
1467
1549
|
}
|
|
1468
1550
|
|
|
1469
1551
|
.tool-call-toggle[aria-expanded="true"]::before {
|
|
1470
|
-
content: "
|
|
1552
|
+
content: var(--_disclosure-expanded) " ";
|
|
1471
1553
|
}
|
|
1472
1554
|
|
|
1473
|
-
/* Resize
|
|
1474
|
-
|
|
1475
|
-
nothing to drag.
|
|
1555
|
+
/* Resize grips: every edge and every corner, so the panel can be dragged from
|
|
1556
|
+
whichever side the reader is already near. Absent entirely where the
|
|
1557
|
+
placement is full-bleed, since there is nothing to drag.
|
|
1558
|
+
|
|
1559
|
+
The whole set is always laid out. Which edges the *layout* pins is not a
|
|
1560
|
+
question the stylesheet needs to answer any more -- it decided where the one
|
|
1561
|
+
grip went, and there is no longer one grip. The element still measures and
|
|
1562
|
+
stamps data-resize-anchor, because it decides what a drag on a pinned edge
|
|
1563
|
+
costs in position and which grip carries the tab stop, but no rule here
|
|
1564
|
+
reads it. */
|
|
1476
1565
|
.resize-handle {
|
|
1477
1566
|
position: absolute;
|
|
1478
1567
|
z-index: 2;
|
|
@@ -1485,95 +1574,102 @@ export const STYLES = `
|
|
|
1485
1574
|
outline-offset: -2px;
|
|
1486
1575
|
}
|
|
1487
1576
|
|
|
1488
|
-
/*
|
|
1489
|
-
|
|
1577
|
+
/* Corners first in the file and last in the DOM, so they take the pointer
|
|
1578
|
+
where they overlap an edge strip. */
|
|
1579
|
+
.resize-handle--top-left,
|
|
1580
|
+
.resize-handle--top-right,
|
|
1581
|
+
.resize-handle--bottom-left,
|
|
1582
|
+
.resize-handle--bottom-right {
|
|
1583
|
+
width: var(--_grip-corner);
|
|
1584
|
+
height: var(--_grip-corner);
|
|
1585
|
+
}
|
|
1490
1586
|
|
|
1491
|
-
|
|
1492
|
-
.resize-handle {
|
|
1587
|
+
.resize-handle--top-left {
|
|
1493
1588
|
top: 0;
|
|
1494
1589
|
left: 0;
|
|
1495
|
-
width: 14px;
|
|
1496
|
-
height: 14px;
|
|
1497
1590
|
cursor: nwse-resize;
|
|
1498
1591
|
}
|
|
1499
1592
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
bottom-left is pinned there, so its free corner is top-right. */
|
|
1503
|
-
:host([placement="embedded"]) .resize-handle {
|
|
1504
|
-
top: auto;
|
|
1505
|
-
left: auto;
|
|
1593
|
+
.resize-handle--top-right {
|
|
1594
|
+
top: 0;
|
|
1506
1595
|
right: 0;
|
|
1507
|
-
|
|
1508
|
-
cursor: nwse-resize;
|
|
1596
|
+
cursor: nesw-resize;
|
|
1509
1597
|
}
|
|
1510
1598
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1599
|
+
.resize-handle--bottom-left {
|
|
1600
|
+
bottom: 0;
|
|
1601
|
+
left: 0;
|
|
1514
1602
|
cursor: nesw-resize;
|
|
1515
1603
|
}
|
|
1516
1604
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
anchored bottom-left -- so the element measures them and stamps a single
|
|
1520
|
-
hyphenated "<y>-<x>" token here. Equal specificity to the rules above, so
|
|
1521
|
-
source order is what lets the measured value win.
|
|
1522
|
-
|
|
1523
|
-
Two traps, both of which silently draw the grip on the corner that moves:
|
|
1524
|
-
|
|
1525
|
-
Never write these as [data-resize-anchor~="left"]. The stamped value is one
|
|
1526
|
-
hyphenated token and ~= matches whitespace-separated words, so it can never
|
|
1527
|
-
match.
|
|
1528
|
-
|
|
1529
|
-
Each rule must set both sides of its axis, not only the side it moves, or it
|
|
1530
|
-
cannot undo a placement guess that flipped the other way. */
|
|
1531
|
-
:host([data-resize-anchor$="-left"]) .resize-handle {
|
|
1532
|
-
left: auto;
|
|
1605
|
+
.resize-handle--bottom-right {
|
|
1606
|
+
bottom: 0;
|
|
1533
1607
|
right: 0;
|
|
1608
|
+
cursor: nwse-resize;
|
|
1534
1609
|
}
|
|
1535
1610
|
|
|
1536
|
-
|
|
1611
|
+
/* Edge strips, held clear of the corners at both ends so a corner drag is
|
|
1612
|
+
never swallowed by the edge next to it. */
|
|
1613
|
+
.resize-handle--left,
|
|
1614
|
+
.resize-handle--right {
|
|
1615
|
+
top: var(--_grip-corner);
|
|
1616
|
+
bottom: var(--_grip-corner);
|
|
1617
|
+
width: var(--_grip-edge);
|
|
1618
|
+
cursor: ew-resize;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
.resize-handle--left {
|
|
1537
1622
|
left: 0;
|
|
1538
|
-
right: auto;
|
|
1539
1623
|
}
|
|
1540
1624
|
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
bottom: 0;
|
|
1625
|
+
.resize-handle--right {
|
|
1626
|
+
right: 0;
|
|
1544
1627
|
}
|
|
1545
1628
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1629
|
+
.resize-handle--top,
|
|
1630
|
+
.resize-handle--bottom {
|
|
1631
|
+
left: var(--_grip-corner);
|
|
1632
|
+
right: var(--_grip-corner);
|
|
1633
|
+
height: var(--_grip-edge);
|
|
1634
|
+
cursor: ns-resize;
|
|
1549
1635
|
}
|
|
1550
1636
|
|
|
1551
|
-
|
|
1552
|
-
:
|
|
1553
|
-
:host([data-resize-anchor="bottom-right"]) .resize-handle {
|
|
1554
|
-
cursor: nwse-resize;
|
|
1637
|
+
.resize-handle--top {
|
|
1638
|
+
top: 0;
|
|
1555
1639
|
}
|
|
1556
1640
|
|
|
1557
|
-
|
|
1558
|
-
:
|
|
1559
|
-
cursor: nesw-resize;
|
|
1641
|
+
.resize-handle--bottom {
|
|
1642
|
+
bottom: 0;
|
|
1560
1643
|
}
|
|
1561
1644
|
|
|
1562
|
-
/* Docked: the placement owns the height, so
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1645
|
+
/* Docked: the placement owns the height, so the horizontal edges and every
|
|
1646
|
+
corner are inert and must not advertise a drag that does nothing. The two
|
|
1647
|
+
vertical edges remain, which is the same affordance these placements had
|
|
1648
|
+
when there was one grip -- now on both sides, since either may be the inner
|
|
1649
|
+
one depending on which side the rail is docked to. */
|
|
1650
|
+
:host([placement="sidebar"]) .resize-handle--top,
|
|
1651
|
+
:host([placement="sidebar"]) .resize-handle--bottom,
|
|
1652
|
+
:host([placement="sidebar"]) .resize-handle--top-left,
|
|
1653
|
+
:host([placement="sidebar"]) .resize-handle--top-right,
|
|
1654
|
+
:host([placement="sidebar"]) .resize-handle--bottom-left,
|
|
1655
|
+
:host([placement="sidebar"]) .resize-handle--bottom-right,
|
|
1656
|
+
:host([placement="side"]) .resize-handle--top,
|
|
1657
|
+
:host([placement="side"]) .resize-handle--bottom,
|
|
1658
|
+
:host([placement="side"]) .resize-handle--top-left,
|
|
1659
|
+
:host([placement="side"]) .resize-handle--top-right,
|
|
1660
|
+
:host([placement="side"]) .resize-handle--bottom-left,
|
|
1661
|
+
:host([placement="side"]) .resize-handle--bottom-right {
|
|
1662
|
+
display: none;
|
|
1571
1663
|
}
|
|
1572
1664
|
|
|
1573
|
-
/*
|
|
1574
|
-
:host([placement="sidebar"]
|
|
1575
|
-
|
|
1576
|
-
|
|
1665
|
+
/* The vertical edges run the full height once no corner shares them. */
|
|
1666
|
+
:host([placement="sidebar"]) .resize-handle--left,
|
|
1667
|
+
:host([placement="sidebar"]) .resize-handle--right,
|
|
1668
|
+
:host([placement="side"]) .resize-handle--left,
|
|
1669
|
+
:host([placement="side"]) .resize-handle--right {
|
|
1670
|
+
top: 0;
|
|
1671
|
+
bottom: 0;
|
|
1672
|
+
width: var(--_grip-edge-docked);
|
|
1577
1673
|
}
|
|
1578
1674
|
|
|
1579
1675
|
/* Full-bleed: nothing to drag. */
|
|
@@ -1582,9 +1678,62 @@ export const STYLES = `
|
|
|
1582
1678
|
display: none;
|
|
1583
1679
|
}
|
|
1584
1680
|
|
|
1585
|
-
.
|
|
1681
|
+
/* The visible mark on a grip.
|
|
1682
|
+
|
|
1683
|
+
Filling the whole strip was what the single corner grip did, and at 14px
|
|
1684
|
+
square nobody ever saw it. On a strip running the length of an edge the same
|
|
1685
|
+
fill reads as a border the panel grew -- square ended, stopping short of the
|
|
1686
|
+
corner radius at both ends, and easily mistaken for a rendering fault rather
|
|
1687
|
+
than for something to grab.
|
|
1688
|
+
|
|
1689
|
+
So the hit area stays the full strip and the mark is a short pill centred on
|
|
1690
|
+
it, which cannot be read as an edge of anything and never meets the radius.
|
|
1691
|
+
|
|
1692
|
+
It shows on hover and focus as well as during the drag. With eight grips, a
|
|
1693
|
+
mark that appears only once you are already dragging is a mark that never
|
|
1694
|
+
told anyone the grips were there. */
|
|
1695
|
+
.resize-handle::after {
|
|
1696
|
+
content: "";
|
|
1697
|
+
position: absolute;
|
|
1698
|
+
top: 50%;
|
|
1699
|
+
left: 50%;
|
|
1700
|
+
transform: translate(-50%, -50%);
|
|
1701
|
+
border-radius: 999px;
|
|
1586
1702
|
background: var(--_accent);
|
|
1587
|
-
opacity: 0
|
|
1703
|
+
opacity: 0;
|
|
1704
|
+
transition: opacity var(--_motion) var(--_ease);
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
.resize-handle--top::after,
|
|
1708
|
+
.resize-handle--bottom::after {
|
|
1709
|
+
width: var(--_grip-mark-length);
|
|
1710
|
+
height: var(--_grip-mark-thickness);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
.resize-handle--left::after,
|
|
1714
|
+
.resize-handle--right::after {
|
|
1715
|
+
width: var(--_grip-mark-thickness);
|
|
1716
|
+
height: var(--_grip-mark-length);
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
/* A corner has no length to run along, so it gets a dot instead of a pill. */
|
|
1720
|
+
.resize-handle--top-left::after,
|
|
1721
|
+
.resize-handle--top-right::after,
|
|
1722
|
+
.resize-handle--bottom-left::after,
|
|
1723
|
+
.resize-handle--bottom-right::after {
|
|
1724
|
+
width: var(--_grip-mark-thickness);
|
|
1725
|
+
height: var(--_grip-mark-thickness);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
.resize-handle:hover::after,
|
|
1729
|
+
.resize-handle:focus-visible::after {
|
|
1730
|
+
opacity: 0.5;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
/* Equal specificity to the pair above, so source order is what makes the drag
|
|
1734
|
+
the stronger of the two states. */
|
|
1735
|
+
.resize-handle[data-dragging]::after {
|
|
1736
|
+
opacity: 0.9;
|
|
1588
1737
|
}
|
|
1589
1738
|
|
|
1590
1739
|
/* ── Composer ───────────────────────────────────────────────────────────────
|
|
@@ -1698,7 +1847,7 @@ export const STYLES = `
|
|
|
1698
1847
|
border: none;
|
|
1699
1848
|
border-radius: 50%;
|
|
1700
1849
|
background: var(--_accent);
|
|
1701
|
-
color:
|
|
1850
|
+
color: var(--_on-accent);
|
|
1702
1851
|
font: inherit;
|
|
1703
1852
|
line-height: 1;
|
|
1704
1853
|
cursor: pointer;
|
|
@@ -1772,13 +1921,13 @@ export const STYLES = `
|
|
|
1772
1921
|
/* Recording: a red tint + a gentle pulse so it's clearly "live". */
|
|
1773
1922
|
.voice-btn[data-state="recording"] {
|
|
1774
1923
|
background: var(--_danger);
|
|
1775
|
-
color:
|
|
1924
|
+
color: var(--_on-danger);
|
|
1776
1925
|
animation: ag-ui-voice-pulse 1.2s ease-in-out infinite;
|
|
1777
1926
|
}
|
|
1778
1927
|
|
|
1779
1928
|
.voice-btn[data-state="recording"]:hover {
|
|
1780
1929
|
background: var(--_danger);
|
|
1781
|
-
color:
|
|
1930
|
+
color: var(--_on-danger);
|
|
1782
1931
|
}
|
|
1783
1932
|
|
|
1784
1933
|
@keyframes ag-ui-voice-pulse {
|
|
@@ -2068,7 +2217,7 @@ export const STYLES = `
|
|
|
2068
2217
|
.confirm-btn--confirm {
|
|
2069
2218
|
border-color: var(--_accent);
|
|
2070
2219
|
background: var(--_accent);
|
|
2071
|
-
color:
|
|
2220
|
+
color: var(--_on-accent);
|
|
2072
2221
|
}
|
|
2073
2222
|
|
|
2074
2223
|
/* The session waiver. Deliberately the quietest of the three: it is the widest
|
|
@@ -2136,16 +2285,29 @@ export const STYLES = `
|
|
|
2136
2285
|
margin-top: -6px;
|
|
2137
2286
|
}
|
|
2138
2287
|
|
|
2288
|
+
/* The row's controls are icon-only, so the box is the whole target. Sized from
|
|
2289
|
+
a variable a host can raise, with a floor rather than a fixed height: the
|
|
2290
|
+
compact density shrinks the font, and a target that shrinks with it lands
|
|
2291
|
+
under the 24px minimum that makes a control reliably tappable. It used to,
|
|
2292
|
+
at roughly 20px square. */
|
|
2139
2293
|
.message-action {
|
|
2294
|
+
display: inline-flex;
|
|
2295
|
+
align-items: center;
|
|
2296
|
+
justify-content: center;
|
|
2297
|
+
box-sizing: border-box;
|
|
2298
|
+
min-width: var(--_action-size);
|
|
2299
|
+
min-height: var(--_action-size);
|
|
2300
|
+
padding: 4px;
|
|
2140
2301
|
border: none;
|
|
2141
2302
|
border-radius: 6px;
|
|
2142
|
-
padding: 2px 6px;
|
|
2143
2303
|
font: inherit;
|
|
2144
2304
|
line-height: 1.2;
|
|
2145
2305
|
cursor: pointer;
|
|
2146
2306
|
background: transparent;
|
|
2147
2307
|
color: var(--_muted);
|
|
2148
2308
|
opacity: 0.75;
|
|
2309
|
+
/* The tooltip below is positioned against this box. */
|
|
2310
|
+
position: relative;
|
|
2149
2311
|
}
|
|
2150
2312
|
|
|
2151
2313
|
.message-action:hover,
|
|
@@ -2154,6 +2316,61 @@ export const STYLES = `
|
|
|
2154
2316
|
background: var(--_border);
|
|
2155
2317
|
}
|
|
2156
2318
|
|
|
2319
|
+
.message-action-icon {
|
|
2320
|
+
display: inline-flex;
|
|
2321
|
+
width: var(--_action-icon-size);
|
|
2322
|
+
height: var(--_action-icon-size);
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
.message-action-icon .glyph {
|
|
2326
|
+
width: 100%;
|
|
2327
|
+
height: 100%;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
/* The label, drawn rather than left to the browser.
|
|
2331
|
+
|
|
2332
|
+
A title attribute is the usual answer and covers only half the readers: it
|
|
2333
|
+
never appears on keyboard focus, so tabbing onto an icon-only control shows
|
|
2334
|
+
nothing at all. The attribute stays for the pointer users who expect it, and
|
|
2335
|
+
this shows the same words on hover and on focus alike.
|
|
2336
|
+
|
|
2337
|
+
Left-aligned rather than centred because the row sits at the left edge of an
|
|
2338
|
+
answer inside a scrolling column, and a centred tooltip on the first control
|
|
2339
|
+
is clipped by that column. Growing rightward keeps it inside.
|
|
2340
|
+
|
|
2341
|
+
Not exposed to assistive technology: the button already carries the same
|
|
2342
|
+
string as its accessible name, and this would be a second copy of it. */
|
|
2343
|
+
.message-action::after {
|
|
2344
|
+
content: attr(data-tooltip);
|
|
2345
|
+
position: absolute;
|
|
2346
|
+
bottom: calc(100% + 4px);
|
|
2347
|
+
left: 0;
|
|
2348
|
+
z-index: 3;
|
|
2349
|
+
padding: 3px 6px;
|
|
2350
|
+
border-radius: 4px;
|
|
2351
|
+
background: var(--_tooltip-bg);
|
|
2352
|
+
color: var(--_tooltip-fg);
|
|
2353
|
+
font-size: 11px;
|
|
2354
|
+
line-height: 1.4;
|
|
2355
|
+
white-space: nowrap;
|
|
2356
|
+
pointer-events: none;
|
|
2357
|
+
opacity: 0;
|
|
2358
|
+
transition: opacity var(--_motion) var(--_ease);
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
.message-action:hover::after,
|
|
2362
|
+
.message-action:focus-visible::after {
|
|
2363
|
+
opacity: 1;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
/* Touch has no hover to reveal it, and a tooltip that latches open under a
|
|
2367
|
+
finger covers the answer it belongs to. */
|
|
2368
|
+
@media (hover: none) {
|
|
2369
|
+
.message-action::after {
|
|
2370
|
+
content: none;
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2157
2374
|
.message-action[aria-pressed="true"] {
|
|
2158
2375
|
opacity: 1;
|
|
2159
2376
|
color: var(--_accent);
|
|
@@ -2211,7 +2428,7 @@ export const STYLES = `
|
|
|
2211
2428
|
.approval-btn--approve {
|
|
2212
2429
|
border-color: var(--_accent);
|
|
2213
2430
|
background: var(--_accent);
|
|
2214
|
-
color:
|
|
2431
|
+
color: var(--_on-accent);
|
|
2215
2432
|
}
|
|
2216
2433
|
|
|
2217
2434
|
/* Question card — the built-in ask_user prompt (radios and/or free text). */
|
|
@@ -2277,7 +2494,7 @@ export const STYLES = `
|
|
|
2277
2494
|
font-weight: 600;
|
|
2278
2495
|
cursor: pointer;
|
|
2279
2496
|
background: var(--_accent);
|
|
2280
|
-
color:
|
|
2497
|
+
color: var(--_on-accent);
|
|
2281
2498
|
}
|
|
2282
2499
|
|
|
2283
2500
|
.question-btn:disabled {
|
|
@@ -2546,7 +2763,7 @@ export const STYLES = `
|
|
|
2546
2763
|
font-weight: 600;
|
|
2547
2764
|
border-color: var(--_accent);
|
|
2548
2765
|
background: var(--_accent);
|
|
2549
|
-
color:
|
|
2766
|
+
color: var(--_on-accent);
|
|
2550
2767
|
}
|
|
2551
2768
|
|
|
2552
2769
|
.checkpoint-fork:hover {
|
|
@@ -2721,7 +2938,7 @@ export const STYLES = `
|
|
|
2721
2938
|
border: none;
|
|
2722
2939
|
border-radius: 6px;
|
|
2723
2940
|
background: var(--_danger);
|
|
2724
|
-
color:
|
|
2941
|
+
color: var(--_on-danger);
|
|
2725
2942
|
padding: 3px 10px;
|
|
2726
2943
|
font: inherit;
|
|
2727
2944
|
cursor: pointer;
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION: string = "0.
|
|
1
|
+
export const VERSION: string = "0.33.1";
|