@haikit/client 0.23.0 → 0.24.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/README.md +2 -1
- package/package.json +1 -1
- package/src/app.js +22 -2
- package/src/hai.css +25 -2
- package/src/icons.js +1 -0
package/README.md
CHANGED
|
@@ -48,7 +48,8 @@ Every surface has a **Dock** button on its bar. Docked, it waits on a shelf
|
|
|
48
48
|
above the message box and opens from there as a window, one at a time; **Put
|
|
49
49
|
back** or **Remove from dock** returns it to its place. It's moved, not
|
|
50
50
|
mounted again, so your component keeps its state. A docked question stays
|
|
51
|
-
amber on the shelf,
|
|
51
|
+
amber on the shelf, when the shelf is full the oldest go into a pile, and
|
|
52
|
+
**Remove all** at the shelf's end puts every docked surface back at once.
|
|
52
53
|
`dock: false` leaves it out.
|
|
53
54
|
|
|
54
55
|
It is light or dark with the system. To pin it, put `data-hai-theme="dark"` (or
|
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -436,6 +436,7 @@ export function mountChat({
|
|
|
436
436
|
*
|
|
437
437
|
* When the shelf's row is full, the oldest go into a pile at its end, which
|
|
438
438
|
* lists them. A surface waiting on the reader, or open, stays in the row.
|
|
439
|
+
* Remove all, at the shelf's end, puts every docked surface back at once.
|
|
439
440
|
*/
|
|
440
441
|
function createDock({ chat, id, shell, chatPane, transcript, composer, statusEl, wide, wideQuery, drawerOpen, onChange }) {
|
|
441
442
|
/** Docked surfaces, oldest first. */
|
|
@@ -464,7 +465,13 @@ function createDock({ chat, id, shell, chatPane, transcript, composer, statusEl,
|
|
|
464
465
|
list.id = `${id}-pile`;
|
|
465
466
|
list.hidden = true;
|
|
466
467
|
pile.append(pileBtn, list);
|
|
467
|
-
|
|
468
|
+
// Remove all, at the shelf's end: every docked surface back in the chat
|
|
469
|
+
const clear = h("button", "hai-dock-clear");
|
|
470
|
+
clear.type = "button";
|
|
471
|
+
clear.title = "Remove all from dock";
|
|
472
|
+
clear.setAttribute("aria-label", "Remove all from the dock");
|
|
473
|
+
clear.append(icon("undockAll"));
|
|
474
|
+
shelf.append(count, row, pile, clear);
|
|
468
475
|
composer.prepend(shelf);
|
|
469
476
|
|
|
470
477
|
// ── the window ───────────────────────────────────────────────────────
|
|
@@ -591,6 +598,19 @@ function createDock({ chat, id, shell, chatPane, transcript, composer, statusEl,
|
|
|
591
598
|
remove.onclick = () => open && undock(open, true);
|
|
592
599
|
minimise.onclick = () => close(true);
|
|
593
600
|
|
|
601
|
+
// Every docked surface back in its card, oldest first. The shelf goes with
|
|
602
|
+
// them, so the reader goes to the message box under it, or, once the
|
|
603
|
+
// conversation has expired and the box is disabled, to the button that
|
|
604
|
+
// starts a new one.
|
|
605
|
+
clear.onclick = () => {
|
|
606
|
+
const n = entries.length;
|
|
607
|
+
for (const entry of [...entries]) undock(entry, false);
|
|
608
|
+
const input = composer.querySelector(".hai-input");
|
|
609
|
+
if (input && !input.disabled) input.focus();
|
|
610
|
+
else transcript.querySelector(".hai-expired .hai-new")?.focus();
|
|
611
|
+
live.textContent = n === 1 ? "1 surface is back in the chat" : `${n} surfaces are back in the chat`;
|
|
612
|
+
};
|
|
613
|
+
|
|
594
614
|
// ── keeping up ───────────────────────────────────────────────────────
|
|
595
615
|
// After every event the chat renders, and after every change here.
|
|
596
616
|
function sync() {
|
|
@@ -751,7 +771,7 @@ function createDock({ chat, id, shell, chatPane, transcript, composer, statusEl,
|
|
|
751
771
|
if (!keys.includes(e.key)) return;
|
|
752
772
|
const buttons = inList
|
|
753
773
|
? [...list.querySelectorAll("button")]
|
|
754
|
-
: [...shelf.querySelectorAll(".hai-dock-item")].filter((b) => !b.hidden && !b.closest("[hidden]"));
|
|
774
|
+
: [...shelf.querySelectorAll(".hai-dock-item, .hai-dock-clear")].filter((b) => !b.hidden && !b.closest("[hidden]"));
|
|
755
775
|
const at = buttons.indexOf(e.target);
|
|
756
776
|
if (at < 0) return;
|
|
757
777
|
e.preventDefault();
|
package/src/hai.css
CHANGED
|
@@ -659,6 +659,8 @@
|
|
|
659
659
|
/* The composer card's top row while anything is docked: one card, one
|
|
660
660
|
shadow. Not positioned, so the box below covers the shelf's own shadow. */
|
|
661
661
|
.hai-dock {
|
|
662
|
+
/* Remove all's size: the pile's list lines up past it, with the shelf's end */
|
|
663
|
+
--hai-dock-clear: 36px;
|
|
662
664
|
display: flex;
|
|
663
665
|
align-items: center;
|
|
664
666
|
gap: 10px;
|
|
@@ -766,10 +768,29 @@
|
|
|
766
768
|
transform: translate(8px, -8px);
|
|
767
769
|
}
|
|
768
770
|
.hai-dock-pile::after { transform: translate(4px, -4px); }
|
|
771
|
+
/* Remove all, at the shelf's end: a quiet icon until the pointer's on it */
|
|
772
|
+
.hai-dock-clear {
|
|
773
|
+
flex: none;
|
|
774
|
+
display: grid;
|
|
775
|
+
place-items: center;
|
|
776
|
+
width: var(--hai-dock-clear);
|
|
777
|
+
height: var(--hai-dock-clear);
|
|
778
|
+
padding: 0;
|
|
779
|
+
border: 2px solid transparent;
|
|
780
|
+
border-radius: var(--hai-radius);
|
|
781
|
+
background: none;
|
|
782
|
+
color: var(--hai-dim);
|
|
783
|
+
cursor: pointer;
|
|
784
|
+
}
|
|
785
|
+
.hai-dock-clear .hai-icon { width: 16px; height: 16px; stroke-width: 2.5; }
|
|
786
|
+
.hai-dock-clear:hover { border-color: var(--hai-line); background: var(--hai-hover); color: var(--hai-fg); }
|
|
769
787
|
/* what's in it, oldest first, above the shelf */
|
|
788
|
+
/* Its right edge lines up with the shelf's: past the pile's margin, the gap,
|
|
789
|
+
Remove all, the shelf's padding and its border. So it opens leftward from
|
|
790
|
+
the shelf's end, and fits whatever the screen fits. */
|
|
770
791
|
.hai-pile-list {
|
|
771
792
|
position: absolute;
|
|
772
|
-
right: -8px;
|
|
793
|
+
right: calc(-1 * (8px + 10px + var(--hai-dock-clear, 36px) + 14px + 3px));
|
|
773
794
|
bottom: calc(100% + 18px);
|
|
774
795
|
z-index: 6;
|
|
775
796
|
width: 280px;
|
|
@@ -993,6 +1014,7 @@
|
|
|
993
1014
|
/* a finger's worth, wherever the pointer is coarse */
|
|
994
1015
|
@media (pointer: coarse) {
|
|
995
1016
|
.hai-dock-item { height: 44px; }
|
|
1017
|
+
.hai-dock { --hai-dock-clear: 44px; }
|
|
996
1018
|
.hai-surface-bar { min-height: 44px; }
|
|
997
1019
|
.hai-docked { min-height: 44px; }
|
|
998
1020
|
.hai-window-bar { min-height: 44px; }
|
|
@@ -1014,7 +1036,8 @@
|
|
|
1014
1036
|
.hai-tool summary:focus-visible { outline: 2px solid currentColor; outline-offset: -5px; }
|
|
1015
1037
|
/* the dock's items and its pile take the buttons' ring; a bar's cells and the
|
|
1016
1038
|
pile's rows, where a card would clip it, a ring inside */
|
|
1017
|
-
.hai-dock-item:focus-visible
|
|
1039
|
+
.hai-dock-item:focus-visible,
|
|
1040
|
+
.hai-dock-clear:focus-visible { outline: 3px solid var(--hai-fg); outline-offset: 2px; }
|
|
1018
1041
|
.hai-bar-btn:focus-visible,
|
|
1019
1042
|
.hai-pile-row:focus-visible { outline: 2px solid currentColor; outline-offset: -6px; }
|
|
1020
1043
|
|
package/src/icons.js
CHANGED
|
@@ -21,6 +21,7 @@ const PATHS = {
|
|
|
21
21
|
window: "M4 5h16v14H4zM4 9h16",
|
|
22
22
|
dock: "M12 4v10M7.5 9.5 12 14l4.5-4.5M4 20h16",
|
|
23
23
|
undock: "M12 16V4M7.5 8.5 12 4l4.5 4.5M4 20h16",
|
|
24
|
+
undockAll: "M12 13V3M7.5 7.5 12 3l4.5 4.5M4 17h16M4 21h16",
|
|
24
25
|
stack: "M3 8h13v13H3zM8 8V3h13v13h-5",
|
|
25
26
|
};
|
|
26
27
|
|