@artooi/ag-ui-web-component 0.50.0 → 0.50.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 +54 -1
- package/README.md +12 -1
- package/dist/ag-ui-web-component.bundle.js +64 -34
- package/dist/ag-ui-web-component.bundle.js.map +3 -3
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/index.js +177 -25
- package/dist/index.js.map +2 -2
- package/dist/ui/placement/collapse_styles.d.ts.map +1 -1
- package/dist/ui/placement/panel_placement.d.ts +2 -0
- package/dist/ui/placement/panel_placement.d.ts.map +1 -1
- package/dist/ui/placement/resize_styles.d.ts.map +1 -1
- package/dist/ui/transcript/transcript_styles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/ag_ui_chat.ts +29 -4
- package/src/ui/placement/collapse_styles.ts +8 -0
- package/src/ui/placement/panel_placement.ts +143 -19
- package/src/ui/placement/resize_styles.ts +9 -0
- package/src/ui/transcript/transcript_styles.ts +14 -1
- package/src/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collapse_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/collapse_styles.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"collapse_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/collapse_styles.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,EAAE,MAmY7B,CAAC"}
|
|
@@ -141,6 +141,8 @@ export declare class PanelPlacement {
|
|
|
141
141
|
* reopen to happen: a line typed in the composer is one. A position waiting to
|
|
142
142
|
* be handed back under data-launcher-drag="false" waits on, because a panel that leaves for the
|
|
143
143
|
* host's corner as the reader starts typing is a jump with no cause they can see.
|
|
144
|
+
* It waits no longer than the panel stays on the screen: a host's size can stand
|
|
145
|
+
* it off an edge, where its header is out of reach until something else happens.
|
|
144
146
|
*/
|
|
145
147
|
settleLauncherPosition(): void;
|
|
146
148
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panel_placement.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/panel_placement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAsJlD;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAClC,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC;IACpC,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,SAAS,CAAC;IAClC,qDAAqD;IACrD,QAAQ,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACzD,kDAAkD;IAClD,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,iDAAiD;IACjD,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,uEAAuE;IACvE,QAAQ,CAAC,qBAAqB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CAC1E;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc;;IAGzB,YAAY,IAAI,EAAE,aAAa,EAE9B;IAED;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAQzC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAS5C;IAED,mFAAmF;IACnF,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CA+BxC;IAED,sDAAsD;IACtD,WAAW,IAAI,IAAI,CAElB;IAED;;;;;;;;;OASG;IACH,SAAS,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"panel_placement.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/panel_placement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAsJlD;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAClC,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC;IACpC,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,SAAS,CAAC;IAClC,qDAAqD;IACrD,QAAQ,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACzD,kDAAkD;IAClD,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,iDAAiD;IACjD,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,uEAAuE;IACvE,QAAQ,CAAC,qBAAqB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CAC1E;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc;;IAGzB,YAAY,IAAI,EAAE,aAAa,EAE9B;IAED;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAQzC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAS5C;IAED,mFAAmF;IACnF,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CA+BxC;IAED,sDAAsD;IACtD,WAAW,IAAI,IAAI,CAElB;IAED;;;;;;;;;OASG;IACH,SAAS,IAAI,IAAI,CAoBhB;IA2CD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,iBAAiC;IAsI/C;;;;;;;;OAQG;IACH,QAAQ,IAAI,OAAO,CAOlB;IAED,+EAA+E;IAC/E,eAAe,IAAI,iBAAiB,CA8BnC;IAED,oEAAoE;IACpE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAmDjF;IA2JD,sEAAsE;IACtE,gBAAgB,IAAI,IAAI,CAavB;IA+ED;;;;;;OAMG;IACH,gBAAgB,IAAI,IAAI,CAQvB;IAiBD;;;;;;;;;OASG;IACH,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,CAkCzD;IA6PD;;;;;;;;;;;;OAYG;IACH,qBAAqB,IAAI,IAAI,CAgD5B;IAyQD;;;;;;;;OAQG;IACH,uBAAuB,IAAI,IAAI,CAG9B;IAED;;;;;;;OAOG;IACH,sBAAsB,IAAI,IAAI,CAG7B;IAgKD;;;;OAIG;IACH,uBAAuB,IAAI,IAAI,CAY9B;CAyJF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resize_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/resize_styles.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"resize_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/resize_styles.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,aAAa,EAAE,MAiM3B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/transcript/transcript_styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"transcript_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/transcript/transcript_styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,EAAE,MAsJ/B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@artooi/ag-ui-web-component",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.1",
|
|
4
4
|
"description": "Framework-free <ag-ui-chat> Web Component over the AG-UI protocol. Drop-in chat sidebar with a pluggable client-side tool registry, DOM driver primitives, animations, and destructive-action confirmation modal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/src/core/ag_ui_chat.ts
CHANGED
|
@@ -665,7 +665,27 @@ export class AgUiChat extends HTMLElement {
|
|
|
665
665
|
// sent me looking -- that was an inset measured from the wrong box -- so it
|
|
666
666
|
// is a guard against a fight that had not been observed rather than a fix
|
|
667
667
|
// for one that had.
|
|
668
|
-
|
|
668
|
+
//
|
|
669
|
+
// Nor while this element has no box, which is what hiding it reports to the
|
|
670
|
+
// transcript's observer: placing measures a rect of nothing and writes an inset
|
|
671
|
+
// from it. Showing it reports again, so nothing waits on this.
|
|
672
|
+
return !this.#placement.dragging() && this.#hasBox();
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Whether this element generates a box at all. Hidden on itself or on any ancestor
|
|
677
|
+
* it does not; a host collapsed to its launcher does, whatever its size, and that
|
|
678
|
+
* launcher still has to be placed, which is why this is not a test of the size.
|
|
679
|
+
*/
|
|
680
|
+
#hasBox(): boolean {
|
|
681
|
+
return this.getClientRects().length > 0;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** Restore for a moment that is not a viewport's, which showing this reports anyway. */
|
|
685
|
+
#restoreIfBoxed(): void {
|
|
686
|
+
if (this.#hasBox()) {
|
|
687
|
+
this.#placement.restoreLauncherPosition();
|
|
688
|
+
}
|
|
669
689
|
}
|
|
670
690
|
|
|
671
691
|
/** Mic button mount point (input row); the control mounts on connect when enabled. */
|
|
@@ -1567,7 +1587,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1567
1587
|
requestAnimationFrame(() => {
|
|
1568
1588
|
// Before the anchor, which the placement stamps itself once a dragged
|
|
1569
1589
|
// position exists.
|
|
1570
|
-
this.#
|
|
1590
|
+
this.#restoreIfBoxed();
|
|
1571
1591
|
this.#placement.syncResizeAnchor();
|
|
1572
1592
|
});
|
|
1573
1593
|
// Resolve the string table before rendering any chrome (defaults are the
|
|
@@ -1924,7 +1944,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1924
1944
|
// Re-decide which way to open before opening: the viewport may have
|
|
1925
1945
|
// changed since the launcher was dropped, and this corner is what the
|
|
1926
1946
|
// panel grows from.
|
|
1927
|
-
this.#
|
|
1947
|
+
this.#restoreIfBoxed();
|
|
1928
1948
|
}
|
|
1929
1949
|
if (collapsed) {
|
|
1930
1950
|
this.setAttribute("collapsed", "");
|
|
@@ -2036,7 +2056,12 @@ export class AgUiChat extends HTMLElement {
|
|
|
2036
2056
|
if (!this.isConnected) {
|
|
2037
2057
|
return;
|
|
2038
2058
|
}
|
|
2039
|
-
|
|
2059
|
+
// With no box there is no bar drawn, which the host still hears of, and nothing to
|
|
2060
|
+
// measure: syncing would place a dragged panel from a rect of nothing. Showing this
|
|
2061
|
+
// element resizes the bar or the transcript, and their observers sync it then.
|
|
2062
|
+
const height = this.#hasBox()
|
|
2063
|
+
? this.#placement.syncDock(this.#dockBar, this.#resolveAnchor())
|
|
2064
|
+
: 0;
|
|
2040
2065
|
if (height === this.#barHeight) {
|
|
2041
2066
|
return;
|
|
2042
2067
|
}
|
|
@@ -300,6 +300,14 @@ export const COLLAPSE_STYLES: string = `
|
|
|
300
300
|
display: none;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
/* A panel a host sized to the whole window stands on the strip, whose fixed box would
|
|
304
|
+
draw over the composer's foot. So it goes as it does on a phone, by display, so the
|
|
305
|
+
bar measures 0 and the host's padding goes with it, and the header's minimise
|
|
306
|
+
control is the way back. Collapsed, the strip is the way back, so it stays. */
|
|
307
|
+
:host([data-collapsed-to="bar"][data-fills-window]:not([collapsed])) .dock-bar {
|
|
308
|
+
display: none;
|
|
309
|
+
}
|
|
310
|
+
|
|
303
311
|
/* History from the bar: the panel's own drawer, moved beside the bar while
|
|
304
312
|
the panel is away, drawn as a popover standing on the bar's end. There is
|
|
305
313
|
no backdrop; a press outside the bar closes it, as Escape does.
|
|
@@ -288,6 +288,9 @@ export class PanelPlacement {
|
|
|
288
288
|
}
|
|
289
289
|
this.#dockPos = null;
|
|
290
290
|
this.#storeDockPosition(null);
|
|
291
|
+
// A size the host holds does not change here, so no observer is coming to
|
|
292
|
+
// stand a panel that fills the window back on the screen.
|
|
293
|
+
this.#placeDocked();
|
|
291
294
|
} else {
|
|
292
295
|
this.#carrySpotWithSize(sized);
|
|
293
296
|
}
|
|
@@ -387,9 +390,10 @@ export class PanelPlacement {
|
|
|
387
390
|
#gripInset: string | null = null;
|
|
388
391
|
|
|
389
392
|
/**
|
|
390
|
-
* The inset standing a panel
|
|
391
|
-
*
|
|
392
|
-
* lasts only as long as that
|
|
393
|
+
* The inset standing a panel at the start of a window a host's size fills, or
|
|
394
|
+
* null: a corner panel with no spot, or a docked one with or without one. Told
|
|
395
|
+
* apart from a host's own for the same reason, and it lasts only as long as that
|
|
396
|
+
* size does.
|
|
393
397
|
*/
|
|
394
398
|
#fillInset: string | null = null;
|
|
395
399
|
|
|
@@ -411,6 +415,13 @@ export class PanelPlacement {
|
|
|
411
415
|
* syncDock, which runs on every change either can make.
|
|
412
416
|
*/
|
|
413
417
|
#dockTop = Number.POSITIVE_INFINITY;
|
|
418
|
+
/** The bar's height, as last measured: what a fill takes away when it stamps. */
|
|
419
|
+
#barHeight = 0;
|
|
420
|
+
/**
|
|
421
|
+
* What the usable box and the window measured when a fill took the bar away, and what
|
|
422
|
+
* the bar was. Null while no fill holds it. See {@link #fillRoom}.
|
|
423
|
+
*/
|
|
424
|
+
#barTaken: { readonly room: number; readonly window: number; readonly bar: number } | null = null;
|
|
414
425
|
|
|
415
426
|
/**
|
|
416
427
|
* The corner the panel opens away from, once this element is placing itself.
|
|
@@ -490,7 +501,7 @@ export class PanelPlacement {
|
|
|
490
501
|
describeSurface(): ChatSurfaceReport {
|
|
491
502
|
const box = this.#host.element.getBoundingClientRect();
|
|
492
503
|
const viewport = this.#viewport();
|
|
493
|
-
const fullBleed = coversViewport(box,
|
|
504
|
+
const fullBleed = coversViewport(box, this.#fillRoom());
|
|
494
505
|
return {
|
|
495
506
|
placement: this.#host.element.getAttribute("placement"),
|
|
496
507
|
collapseTo: this.#surface().collapseTo,
|
|
@@ -527,7 +538,7 @@ export class PanelPlacement {
|
|
|
527
538
|
const restore = options.announce === true ? this.#captureGeometry() : null;
|
|
528
539
|
const viewport = this.#viewport();
|
|
529
540
|
const box = this.#host.element.getBoundingClientRect();
|
|
530
|
-
if (coversViewport(box,
|
|
541
|
+
if (coversViewport(box, this.#fillRoom())) {
|
|
531
542
|
return false;
|
|
532
543
|
}
|
|
533
544
|
const [edgeY, edgeX] = corner.split("-");
|
|
@@ -581,7 +592,9 @@ export class PanelPlacement {
|
|
|
581
592
|
* corner is what the panel grows from, so restoring a position without it
|
|
582
593
|
* puts the box back and animates it out of the wrong side. Absent values are
|
|
583
594
|
* captured as absent and removed on the way back, rather than written as
|
|
584
|
-
* empty strings that would outrank the placement.
|
|
595
|
+
* empty strings that would outrank the placement. And what the inset is, because
|
|
596
|
+
* one put back with no record of having come from a grip or a fill reads as a
|
|
597
|
+
* host's own, and a phone would leave it holding the panel off the screen.
|
|
585
598
|
*
|
|
586
599
|
* The surface may have changed by the time of the undo, so the two halves are
|
|
587
600
|
* put back apart. Both records are, always, each to its own store, so the
|
|
@@ -598,6 +611,8 @@ export class PanelPlacement {
|
|
|
598
611
|
const panelPos = this.#panelPos;
|
|
599
612
|
const dockPos = this.#dockPos ?? this.#readDockPosition();
|
|
600
613
|
const expandCorner = this.#expandCorner;
|
|
614
|
+
const gripInset = this.#gripInset;
|
|
615
|
+
const fillInset = this.#fillInset;
|
|
601
616
|
const cornerSpot = this.#cornerSpot ?? this.#readLauncherPosition();
|
|
602
617
|
const owner = this.#positionOwner();
|
|
603
618
|
return () => {
|
|
@@ -624,6 +639,8 @@ export class PanelPlacement {
|
|
|
624
639
|
this.#launcherPos = launcherPos;
|
|
625
640
|
this.#panelPos = panelPos;
|
|
626
641
|
this.#expandCorner = expandCorner;
|
|
642
|
+
this.#gripInset = gripInset;
|
|
643
|
+
this.#fillInset = fillInset;
|
|
627
644
|
} else {
|
|
628
645
|
this.#forgetCornerPosition();
|
|
629
646
|
}
|
|
@@ -746,6 +763,31 @@ export class PanelPlacement {
|
|
|
746
763
|
*/
|
|
747
764
|
#stampHeader(): void {
|
|
748
765
|
this.#header?.toggleAttribute("data-fills-screen", this.#fillsScreen());
|
|
766
|
+
// And for the bar and the grips, which go while it does. The next frame, because
|
|
767
|
+
// this runs inside the transcript's observer, and the bar it takes away sits
|
|
768
|
+
// above that in the tree: its own observer would be left undelivered, which the
|
|
769
|
+
// browser reports on the window, where a host's error tracking counts it.
|
|
770
|
+
const element = this.#host.element;
|
|
771
|
+
if (this.#fillsWindow() !== element.hasAttribute("data-fills-window")) {
|
|
772
|
+
requestAnimationFrame(() => {
|
|
773
|
+
const fills = this.#fillsWindow();
|
|
774
|
+
if (fills !== element.hasAttribute("data-fills-window")) {
|
|
775
|
+
this.#barTaken = fills
|
|
776
|
+
? { room: this.#viewport().height, window: this.#screen().height, bar: this.#barHeight }
|
|
777
|
+
: null;
|
|
778
|
+
element.toggleAttribute("data-fills-window", fills);
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Whether the panel fills the window in a placement it can move in. Not one that owns
|
|
786
|
+
* its position: full stops at the bar, so taking the bar away would make it fill the
|
|
787
|
+
* window, and that would take the bar away for good.
|
|
788
|
+
*/
|
|
789
|
+
#fillsWindow(): boolean {
|
|
790
|
+
return this.#fillsScreen() && this.#surface().panelMovable;
|
|
749
791
|
}
|
|
750
792
|
|
|
751
793
|
/** Put a custom property back to a previous value, or remove it if there was none. */
|
|
@@ -848,6 +890,10 @@ export class PanelPlacement {
|
|
|
848
890
|
element.style.removeProperty(DOCK_RIGHT);
|
|
849
891
|
}
|
|
850
892
|
element.toggleAttribute("data-dock-anchored", target === "none" && anchor !== null);
|
|
893
|
+
this.#barHeight = height;
|
|
894
|
+
if (target !== "bar") {
|
|
895
|
+
this.#barTaken = null;
|
|
896
|
+
}
|
|
851
897
|
this.#dockTop =
|
|
852
898
|
target === "bar" && height > 0
|
|
853
899
|
? bar.getBoundingClientRect().top
|
|
@@ -881,19 +927,43 @@ export class PanelPlacement {
|
|
|
881
927
|
* block reads the host's inset before its own, so a dragged spot written
|
|
882
928
|
* there would hold the panel off the screen's edges. The spot itself stays,
|
|
883
929
|
* for the next time there is room for it.
|
|
930
|
+
*
|
|
931
|
+
* A host's size that makes the panel the window hangs it off the resting inset's
|
|
932
|
+
* far side, past the top of the window with the header on it under the bar, so
|
|
933
|
+
* it stands at the start of the room instead, moved or not, as a corner panel
|
|
934
|
+
* does, and the stylesheet takes the bar away for as long as it does. A placement
|
|
935
|
+
* that owns its position keeps it, whatever the host's size.
|
|
936
|
+
*
|
|
937
|
+
* Under data-launcher-drag="false" a spot from before the attribute is left alone
|
|
938
|
+
* while the panel is on the screen, as a corner panel's is, and the record stays
|
|
939
|
+
* for the day the attribute comes off.
|
|
884
940
|
*/
|
|
885
941
|
#placeDocked(): void {
|
|
886
|
-
if (!this.#docked() || !this.#
|
|
942
|
+
if (!this.#docked() || !this.#surface().panelMovable || this.dragging()) {
|
|
887
943
|
return;
|
|
888
944
|
}
|
|
889
|
-
const
|
|
945
|
+
const element = this.#host.element;
|
|
946
|
+
const at = this.#panelMovable() ? (this.#dockPos ?? this.#readDockPosition()) : null;
|
|
890
947
|
if (at === null) {
|
|
948
|
+
if (this.#dockPos !== null) {
|
|
949
|
+
const filling = element.style.getPropertyValue("--ag-ui-inset") === this.#fillInset;
|
|
950
|
+
if (!filling && !this.#offScreen()) {
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
this.#dockPos = null;
|
|
954
|
+
element.style.removeProperty("--ag-ui-inset");
|
|
955
|
+
}
|
|
956
|
+
this.#placeUnstated();
|
|
891
957
|
return;
|
|
892
958
|
}
|
|
893
959
|
this.#dockPos = at;
|
|
894
|
-
|
|
895
|
-
const rest =
|
|
896
|
-
|
|
960
|
+
element.style.removeProperty("--ag-ui-inset");
|
|
961
|
+
const rest = element.getBoundingClientRect();
|
|
962
|
+
const viewport = this.#viewport();
|
|
963
|
+
if (coversViewport(rest, this.#fillRoom())) {
|
|
964
|
+
if (hangsOff(rest, viewport, this.#screen())) {
|
|
965
|
+
this.#standAtStart(rest);
|
|
966
|
+
}
|
|
897
967
|
return;
|
|
898
968
|
}
|
|
899
969
|
// Measured at the dock, so the inset below is written from the corner the
|
|
@@ -960,7 +1030,49 @@ export class PanelPlacement {
|
|
|
960
1030
|
|
|
961
1031
|
/** Whether the panel covers the usable viewport, as it stands now. */
|
|
962
1032
|
#fillsScreen(): boolean {
|
|
963
|
-
return coversViewport(this.#host.element.getBoundingClientRect(), this.#
|
|
1033
|
+
return coversViewport(this.#host.element.getBoundingClientRect(), this.#fillRoom());
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* The box a fill is judged against: the usable one, unless taking the bar away is
|
|
1038
|
+
* what grew it.
|
|
1039
|
+
*
|
|
1040
|
+
* A host can write the bar's height into the inset it reserves. The panel fills the
|
|
1041
|
+
* box that leaves, so the bar goes, the height says 0, the host writes that, the box
|
|
1042
|
+
* grows by what the bar took, and the panel no longer fills it. Judged against that,
|
|
1043
|
+
* the bar comes back and it all goes round again every frame without end. So on the
|
|
1044
|
+
* same window, a box grown by the bar's height is the box it was, and the panel
|
|
1045
|
+
* stands where it stood with the bar gone. Grown by anything else, or on a window of
|
|
1046
|
+
* another size, it is a change of the host's or the reader's and is judged as one.
|
|
1047
|
+
* The slack is the pixel a host rounding the height up can add.
|
|
1048
|
+
*/
|
|
1049
|
+
#fillRoom(): ViewportBox {
|
|
1050
|
+
const viewport = this.#viewport();
|
|
1051
|
+
const taken = this.#barTaken;
|
|
1052
|
+
if (
|
|
1053
|
+
taken !== null &&
|
|
1054
|
+
taken.window === this.#screen().height &&
|
|
1055
|
+
Math.abs(viewport.height - taken.room - taken.bar) <= 1
|
|
1056
|
+
) {
|
|
1057
|
+
return { ...viewport, height: taken.room };
|
|
1058
|
+
}
|
|
1059
|
+
return viewport;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* Whether the panel stands off an edge of the screen, as it stands now.
|
|
1064
|
+
*
|
|
1065
|
+
* The screen rather than the usable box: a panel under chrome the host reserved
|
|
1066
|
+
* has its controls where the host can still bring them back, and is not what a
|
|
1067
|
+
* position kept under data-launcher-drag="false" has to give way to.
|
|
1068
|
+
*/
|
|
1069
|
+
#offScreen(): boolean {
|
|
1070
|
+
const screen = this.#screen();
|
|
1071
|
+
return hangsOff(
|
|
1072
|
+
this.#host.element.getBoundingClientRect(),
|
|
1073
|
+
{ left: 0, top: 0, ...screen },
|
|
1074
|
+
screen,
|
|
1075
|
+
);
|
|
964
1076
|
}
|
|
965
1077
|
|
|
966
1078
|
/**
|
|
@@ -1392,6 +1504,8 @@ export class PanelPlacement {
|
|
|
1392
1504
|
* reopen to happen: a line typed in the composer is one. A position waiting to
|
|
1393
1505
|
* be handed back under data-launcher-drag="false" waits on, because a panel that leaves for the
|
|
1394
1506
|
* host's corner as the reader starts typing is a jump with no cause they can see.
|
|
1507
|
+
* It waits no longer than the panel stays on the screen: a host's size can stand
|
|
1508
|
+
* it off an edge, where its header is out of reach until something else happens.
|
|
1395
1509
|
*/
|
|
1396
1510
|
settleLauncherPosition(): void {
|
|
1397
1511
|
this.#placeFromRecord(false);
|
|
@@ -1417,7 +1531,7 @@ export class PanelPlacement {
|
|
|
1417
1531
|
// interaction, like every other answer here.
|
|
1418
1532
|
const draggable = this.#launcherDraggable();
|
|
1419
1533
|
if (!draggable && this.#launcherPos !== null) {
|
|
1420
|
-
if (!handBack) {
|
|
1534
|
+
if (!handBack && !this.#offScreen()) {
|
|
1421
1535
|
return;
|
|
1422
1536
|
}
|
|
1423
1537
|
this.releaseLauncherPosition();
|
|
@@ -1485,8 +1599,9 @@ export class PanelPlacement {
|
|
|
1485
1599
|
}
|
|
1486
1600
|
|
|
1487
1601
|
/**
|
|
1488
|
-
* Place a
|
|
1489
|
-
* with nothing stated to keep and no launcher of its own
|
|
1602
|
+
* Place a panel that has no spot, a corner one or a docked one, the half of
|
|
1603
|
+
* {@link #standFullBleed} with nothing stated to keep and no launcher of its own
|
|
1604
|
+
* to measure.
|
|
1490
1605
|
*
|
|
1491
1606
|
* Only an inset this element wrote comes off or goes back. One the host wrote
|
|
1492
1607
|
* inline is a token a host may state on a phone as anywhere else, and with no
|
|
@@ -1496,7 +1611,7 @@ export class PanelPlacement {
|
|
|
1496
1611
|
const element = this.#host.element;
|
|
1497
1612
|
const inline = element.style.getPropertyValue("--ag-ui-inset");
|
|
1498
1613
|
const viewport = this.#viewport();
|
|
1499
|
-
if (!coversViewport(element.getBoundingClientRect(),
|
|
1614
|
+
if (!coversViewport(element.getBoundingClientRect(), this.#fillRoom())) {
|
|
1500
1615
|
if (inline === "" || inline === this.#fillInset) {
|
|
1501
1616
|
this.#restoreProperty("--ag-ui-inset", this.#gripInset ?? "");
|
|
1502
1617
|
this.syncResizeAnchor();
|
|
@@ -1513,12 +1628,21 @@ export class PanelPlacement {
|
|
|
1513
1628
|
element.style.getPropertyValue("--ag-ui-inset") === "" &&
|
|
1514
1629
|
hangsOff(rest, viewport, screen)
|
|
1515
1630
|
) {
|
|
1516
|
-
this.#
|
|
1517
|
-
element.style.setProperty("--ag-ui-inset", this.#fillInset);
|
|
1518
|
-
this.syncResizeAnchor();
|
|
1631
|
+
this.#standAtStart(rest);
|
|
1519
1632
|
}
|
|
1520
1633
|
}
|
|
1521
1634
|
|
|
1635
|
+
/** Stand a box a host's size fills at the start of the room the host left free. */
|
|
1636
|
+
#standAtStart(rest: PanelRect): void {
|
|
1637
|
+
this.#fillInset = insetFrom(
|
|
1638
|
+
{ x: "left", y: "top" },
|
|
1639
|
+
atStartOf(this.#viewport(), rest),
|
|
1640
|
+
this.#screen(),
|
|
1641
|
+
);
|
|
1642
|
+
this.#host.element.style.setProperty("--ag-ui-inset", this.#fillInset);
|
|
1643
|
+
this.syncResizeAnchor();
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1522
1646
|
/** The persisted position for this instance, or null. */
|
|
1523
1647
|
#readLauncherPosition(): CornerSpot | null {
|
|
1524
1648
|
const raw = this.#host.readPreference(LAUNCHER_KEY);
|
|
@@ -90,6 +90,15 @@ export const RESIZE_STYLES: string = `
|
|
|
90
90
|
bottom: 0;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/* A docked panel a host sized to the whole window has nothing to give a grip: its size
|
|
94
|
+
is the host's, and a place a grip left would outlast that size with the bar back
|
|
95
|
+
under it. They go as they do on a phone. A corner panel keeps them, since it has no
|
|
96
|
+
bar to stand clear of and a grip's place is its own. */
|
|
97
|
+
:host([data-collapsed-to="bar"][data-fills-window]) .resize-handle,
|
|
98
|
+
:host([data-collapsed-to="none"][data-fills-window]) .resize-handle {
|
|
99
|
+
display: none;
|
|
100
|
+
}
|
|
101
|
+
|
|
93
102
|
/* Docked: the placement owns the height, so the horizontal edges and every
|
|
94
103
|
corner are inert and must not advertise a drag that does nothing. The two
|
|
95
104
|
vertical edges remain, which is the same affordance these placements had
|
|
@@ -97,7 +97,18 @@ export const TRANSCRIPT_STYLES: string = `
|
|
|
97
97
|
|
|
98
98
|
visibility rather than display: none, because a box that generates no
|
|
99
99
|
layout has no used values to read. Zero-sized, absolutely positioned and
|
|
100
|
-
inert, so it costs nothing but the read.
|
|
100
|
+
inert, so it costs nothing but the read.
|
|
101
|
+
|
|
102
|
+
The padding gives that box the size of the tokens, which a card narrower than
|
|
103
|
+
one of them would scroll to reach. Scrollable overflow counts a box as its
|
|
104
|
+
transform draws it, so a scale of nothing takes it out of that count on every
|
|
105
|
+
side and in either direction, and leaves what is read: the padding's used values,
|
|
106
|
+
and the containing block a percentage resolves against. Position fixed would add
|
|
107
|
+
no scroll either, and would change that block.
|
|
108
|
+
|
|
109
|
+
From its own corner, which is inside whatever holds it where the centre of the
|
|
110
|
+
padding need not be: kept for an engine that counts a point with no area. Chromium
|
|
111
|
+
does not, so the suite cannot pin the origin. */
|
|
101
112
|
.viewport-probe {
|
|
102
113
|
position: absolute;
|
|
103
114
|
top: 0;
|
|
@@ -106,6 +117,8 @@ export const TRANSCRIPT_STYLES: string = `
|
|
|
106
117
|
height: 0;
|
|
107
118
|
visibility: hidden;
|
|
108
119
|
pointer-events: none;
|
|
120
|
+
transform: scale(0);
|
|
121
|
+
transform-origin: 0 0;
|
|
109
122
|
padding: var(--_viewport-inset-top) var(--_viewport-inset-right)
|
|
110
123
|
var(--_viewport-inset-bottom) var(--_viewport-inset-left);
|
|
111
124
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION: string = "0.50.
|
|
1
|
+
export const VERSION: string = "0.50.1";
|