@artooi/ag-ui-web-component 0.49.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 +175 -1
- package/README.md +25 -14
- package/dist/ag-ui-web-component.bundle.js +168 -125
- 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 +530 -110
- package/dist/index.js.map +2 -2
- package/dist/tools/chat_surface_tools.d.ts +8 -5
- package/dist/tools/chat_surface_tools.d.ts.map +1 -1
- package/dist/ui/placement/collapse_styles.d.ts.map +1 -1
- package/dist/ui/placement/panel_placement.d.ts +15 -1
- 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/shell/frame_styles.d.ts.map +1 -1
- package/dist/ui/shell/token_styles.d.ts.map +1 -1
- package/dist/ui/themes/classic_tokens.d.ts.map +1 -1
- package/dist/ui/themes/ledger_styles.d.ts.map +1 -1
- package/dist/ui/themes/ledger_tokens.d.ts.map +1 -1
- package/dist/ui/themes/quiet_styles.d.ts.map +1 -1
- package/dist/ui/themes/soft_styles.d.ts.map +1 -1
- package/dist/ui/themes/soft_tokens.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 +73 -7
- package/src/tools/chat_surface_tools.ts +8 -5
- package/src/ui/placement/collapse_styles.ts +8 -0
- package/src/ui/placement/panel_placement.ts +512 -99
- package/src/ui/placement/resize_styles.ts +9 -0
- package/src/ui/shell/frame_styles.ts +22 -2
- package/src/ui/shell/token_styles.ts +7 -6
- package/src/ui/themes/classic_tokens.ts +1 -0
- package/src/ui/themes/ledger_styles.ts +4 -3
- package/src/ui/themes/ledger_tokens.ts +1 -0
- package/src/ui/themes/quiet_styles.ts +2 -15
- package/src/ui/themes/soft_styles.ts +4 -3
- package/src/ui/themes/soft_tokens.ts +1 -0
- package/src/ui/transcript/transcript_styles.ts +14 -1
- package/src/version.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -5895,6 +5895,18 @@ function asPoint(value) {
|
|
|
5895
5895
|
function coversViewport(box, viewport) {
|
|
5896
5896
|
return box.width >= viewport.width - 1 && box.height >= viewport.height - 1;
|
|
5897
5897
|
}
|
|
5898
|
+
function hangsOff(box, viewport, screen) {
|
|
5899
|
+
const slack = 0.5;
|
|
5900
|
+
return box.left < viewport.left - slack || box.top < viewport.top - slack || box.right > screen.width + slack || box.bottom > screen.height + slack;
|
|
5901
|
+
}
|
|
5902
|
+
function atStartOf(viewport, box) {
|
|
5903
|
+
return {
|
|
5904
|
+
left: viewport.left,
|
|
5905
|
+
top: viewport.top,
|
|
5906
|
+
right: viewport.left + (box.right - box.left),
|
|
5907
|
+
bottom: viewport.top + (box.bottom - box.top)
|
|
5908
|
+
};
|
|
5909
|
+
}
|
|
5898
5910
|
function insetFrom(corner, box, screen) {
|
|
5899
5911
|
const side = (value) => `${Math.round(value)}px`;
|
|
5900
5912
|
return [
|
|
@@ -5924,6 +5936,7 @@ var PanelPlacement = class {
|
|
|
5924
5936
|
* header on screen, and the launcher is the handle then.
|
|
5925
5937
|
*/
|
|
5926
5938
|
enablePanelDrag(header) {
|
|
5939
|
+
this.#header = header;
|
|
5927
5940
|
enablePanelDrag(header, {
|
|
5928
5941
|
enabled: () => !this.#host.collapsed() && this.#canDragPanel(),
|
|
5929
5942
|
rect: () => this.#host.element.getBoundingClientRect(),
|
|
@@ -5948,12 +5961,19 @@ var PanelPlacement = class {
|
|
|
5948
5961
|
}
|
|
5949
5962
|
/** Build the eight resize grips into `chat`, with one of them in the tab order. */
|
|
5950
5963
|
mountResizeGrips(chat) {
|
|
5964
|
+
this.#resizing = false;
|
|
5951
5965
|
for (const grip of RESIZE_GRIPS) {
|
|
5952
5966
|
const handle = createResizeHandle(grip, {
|
|
5953
5967
|
axis: () => this.#resizeAxis(),
|
|
5954
5968
|
rect: () => this.#host.element.getBoundingClientRect(),
|
|
5955
|
-
apply: (box) =>
|
|
5956
|
-
|
|
5969
|
+
apply: (box) => {
|
|
5970
|
+
this.#resizing = true;
|
|
5971
|
+
this.#applyResize(grip, box);
|
|
5972
|
+
},
|
|
5973
|
+
commit: (box) => {
|
|
5974
|
+
this.#resizing = false;
|
|
5975
|
+
this.#commitResize(grip, box);
|
|
5976
|
+
},
|
|
5957
5977
|
label: this.#host.strings().resizePanel
|
|
5958
5978
|
});
|
|
5959
5979
|
handle.tabIndex = -1;
|
|
@@ -5978,6 +5998,7 @@ var PanelPlacement = class {
|
|
|
5978
5998
|
* it always has.
|
|
5979
5999
|
*/
|
|
5980
6000
|
resetSize() {
|
|
6001
|
+
const sized = this.#host.element.getBoundingClientRect();
|
|
5981
6002
|
this.#host.element.style.removeProperty(DRAG_WIDTH);
|
|
5982
6003
|
this.#host.element.style.removeProperty(DRAG_HEIGHT);
|
|
5983
6004
|
this.#host.clearPreference(SIZE_KEY);
|
|
@@ -5987,9 +6008,47 @@ var PanelPlacement = class {
|
|
|
5987
6008
|
}
|
|
5988
6009
|
this.#dockPos = null;
|
|
5989
6010
|
this.#storeDockPosition(null);
|
|
6011
|
+
this.#placeDocked();
|
|
6012
|
+
} else {
|
|
6013
|
+
this.#carrySpotWithSize(sized);
|
|
5990
6014
|
}
|
|
5991
6015
|
this.syncResizeAnchor();
|
|
5992
6016
|
}
|
|
6017
|
+
/**
|
|
6018
|
+
* Carry a stated corner spot along with a dragged size coming off.
|
|
6019
|
+
*
|
|
6020
|
+
* The size comes off toward the corner the layout holds still, so the top-left
|
|
6021
|
+
* the spot is kept as moves with it, and a restore from the old one would swing
|
|
6022
|
+
* the panel away from its launcher. Only that movement goes onto the spot, not
|
|
6023
|
+
* where the box stands: in a window narrower than the spot the box is held
|
|
6024
|
+
* short of it, and that is not something the user stated.
|
|
6025
|
+
*
|
|
6026
|
+
* Left alone while a host's own size is in force, because that cannot be told
|
|
6027
|
+
* from the dragged size coming off, and it may be a state the host is about to
|
|
6028
|
+
* leave: counting it would throw the spot off by as much as that state is
|
|
6029
|
+
* bigger. A panel that is the screen needs no case of its own: the small-viewport
|
|
6030
|
+
* block ignores a dragged size, so its box does not move.
|
|
6031
|
+
*
|
|
6032
|
+
* Left alone too under data-launcher-drag="false", where a spot left over from
|
|
6033
|
+
* before the attribute is a record kept for the day it comes off, and nothing
|
|
6034
|
+
* that happens to the box now is a statement about it.
|
|
6035
|
+
*/
|
|
6036
|
+
#carrySpotWithSize(sized) {
|
|
6037
|
+
const element = this.#host.element;
|
|
6038
|
+
const box = element.getBoundingClientRect();
|
|
6039
|
+
const spot = this.#cornerSpot ?? this.#readLauncherPosition();
|
|
6040
|
+
const style = getComputedStyle(element);
|
|
6041
|
+
if (spot?.panel === void 0 || !this.#launcherDraggable() || style.getPropertyValue("--ag-ui-width") !== "" || style.getPropertyValue("--ag-ui-height") !== "") {
|
|
6042
|
+
return;
|
|
6043
|
+
}
|
|
6044
|
+
this.#writeCornerSpot({
|
|
6045
|
+
...spot,
|
|
6046
|
+
panel: {
|
|
6047
|
+
left: spot.panel.left + box.left - sized.left,
|
|
6048
|
+
top: spot.panel.top + box.top - sized.top
|
|
6049
|
+
}
|
|
6050
|
+
});
|
|
6051
|
+
}
|
|
5993
6052
|
/**
|
|
5994
6053
|
* A zero-sized box carrying the host's viewport insets as padding, so they
|
|
5995
6054
|
* can be read back as used pixel lengths. See the `.viewport-probe` rule for
|
|
@@ -6014,6 +6073,33 @@ var PanelPlacement = class {
|
|
|
6014
6073
|
* from the launcher would move the panel the user just placed.
|
|
6015
6074
|
*/
|
|
6016
6075
|
#panelPos = null;
|
|
6076
|
+
/**
|
|
6077
|
+
* The spot #launcherPos and #panelPos were last stated as, or null while
|
|
6078
|
+
* nothing has been stated since this element connected.
|
|
6079
|
+
*
|
|
6080
|
+
* Those two say where the launcher and the panel stand now, held inside the
|
|
6081
|
+
* viewport, because that is where a press finds them and a drag starts from.
|
|
6082
|
+
* This is what the user asked for, kept apart so that a window narrower than
|
|
6083
|
+
* the spot, or a phone that took the panel full-bleed, gives it back unchanged
|
|
6084
|
+
* once there is room again. Only a gesture, a move or a dragged size coming off
|
|
6085
|
+
* changes it; re-applying it against a viewport never does, which is the difference from a clamped
|
|
6086
|
+
* position quietly becoming the record one resize at a time.
|
|
6087
|
+
*/
|
|
6088
|
+
#cornerSpot = null;
|
|
6089
|
+
/**
|
|
6090
|
+
* The inset a grip wrote on a corner panel with no spot, or null. A grip on an
|
|
6091
|
+
* edge the layout holds moves the panel without stating a spot, so this is the
|
|
6092
|
+
* only way to tell its inline inset from one a host wrote there, which stays,
|
|
6093
|
+
* and to give it back once a phone has taken the panel full-bleed.
|
|
6094
|
+
*/
|
|
6095
|
+
#gripInset = null;
|
|
6096
|
+
/**
|
|
6097
|
+
* The inset standing a panel at the start of a window a host's size fills, or
|
|
6098
|
+
* null: a corner panel with no spot, or a docked one with or without one. Told
|
|
6099
|
+
* apart from a host's own for the same reason, and it lasts only as long as that
|
|
6100
|
+
* size does.
|
|
6101
|
+
*/
|
|
6102
|
+
#fillInset = null;
|
|
6017
6103
|
/**
|
|
6018
6104
|
* Where the user dragged a docked panel, in viewport coordinates, or null
|
|
6019
6105
|
* while it stands on the bar or the anchor.
|
|
@@ -6031,6 +6117,13 @@ var PanelPlacement = class {
|
|
|
6031
6117
|
* syncDock, which runs on every change either can make.
|
|
6032
6118
|
*/
|
|
6033
6119
|
#dockTop = Number.POSITIVE_INFINITY;
|
|
6120
|
+
/** The bar's height, as last measured: what a fill takes away when it stamps. */
|
|
6121
|
+
#barHeight = 0;
|
|
6122
|
+
/**
|
|
6123
|
+
* What the usable box and the window measured when a fill took the bar away, and what
|
|
6124
|
+
* the bar was. Null while no fill holds it. See {@link #fillRoom}.
|
|
6125
|
+
*/
|
|
6126
|
+
#barTaken = null;
|
|
6034
6127
|
/**
|
|
6035
6128
|
* The corner the panel opens away from, once this element is placing itself.
|
|
6036
6129
|
* Null means the host's layout still decides, and the anchor is measured.
|
|
@@ -6042,6 +6135,10 @@ var PanelPlacement = class {
|
|
|
6042
6135
|
* second while the panel is already being laid out on every one of them.
|
|
6043
6136
|
*/
|
|
6044
6137
|
#anchor = { x: "right", y: "bottom" };
|
|
6138
|
+
/** The header the panel drag listens on, stamped by #stampHeader. */
|
|
6139
|
+
#header = null;
|
|
6140
|
+
/** Whether a grip has applied a box it has not committed yet. */
|
|
6141
|
+
#resizing = false;
|
|
6045
6142
|
/** The eight grips, by name, so the keyboard-reachable one can be moved. */
|
|
6046
6143
|
#resizeHandles = /* @__PURE__ */ new Map();
|
|
6047
6144
|
/**
|
|
@@ -6076,27 +6173,30 @@ var PanelPlacement = class {
|
|
|
6076
6173
|
*
|
|
6077
6174
|
* Read from the stamp the drag helpers already set, rather than tracked
|
|
6078
6175
|
* separately: one source of truth, and it clears on `pointercancel` as well
|
|
6079
|
-
* as `pointerup`, which is the end a touch gesture usually gets.
|
|
6176
|
+
* as `pointerup`, which is the end a touch gesture usually gets. The one
|
|
6177
|
+
* gesture that stamps nothing is an arrow key on a grip, which applies a box
|
|
6178
|
+
* per key and states it only when the key comes up, hence #resizing.
|
|
6080
6179
|
*/
|
|
6081
6180
|
dragging() {
|
|
6082
|
-
return this.#host.launcher.hasAttribute("data-dragging") || this.#host.root.querySelector(".header[data-dragging]") !== null;
|
|
6181
|
+
return this.#resizing || this.#host.launcher.hasAttribute("data-dragging") || this.#host.root.querySelector(".header[data-dragging], .resize-handle[data-dragging]") !== null;
|
|
6083
6182
|
}
|
|
6084
6183
|
/** The report behind `AgUiChat.describeSurface`, whose doc is the contract. */
|
|
6085
6184
|
describeSurface() {
|
|
6086
6185
|
const box = this.#host.element.getBoundingClientRect();
|
|
6087
6186
|
const viewport = this.#viewport();
|
|
6088
|
-
const fullBleed = coversViewport(box,
|
|
6187
|
+
const fullBleed = coversViewport(box, this.#fillRoom());
|
|
6089
6188
|
return {
|
|
6090
6189
|
placement: this.#host.element.getAttribute("placement"),
|
|
6091
6190
|
collapseTo: this.#surface().collapseTo,
|
|
6092
6191
|
collapsed: this.#host.collapsed(),
|
|
6093
6192
|
collapsible: this.#host.collapsible(),
|
|
6094
6193
|
movable: this.#panelMovable() && !fullBleed,
|
|
6095
|
-
//
|
|
6096
|
-
//
|
|
6097
|
-
//
|
|
6098
|
-
//
|
|
6099
|
-
|
|
6194
|
+
// The page's setting alone: true for every placement unless the host set
|
|
6195
|
+
// `data-launcher-drag="false"`. Whether this panel moves is `movable`,
|
|
6196
|
+
// and reading the launcher or the dock in here made the field change
|
|
6197
|
+
// with `collapse-to`, so a docked sidebar reported that the page had
|
|
6198
|
+
// turned moving off when the placement owned its position.
|
|
6199
|
+
draggable: this.#host.element.getAttribute("data-launcher-drag") !== "false",
|
|
6100
6200
|
fullBleed,
|
|
6101
6201
|
box: {
|
|
6102
6202
|
left: Math.round(box.left),
|
|
@@ -6120,7 +6220,7 @@ var PanelPlacement = class {
|
|
|
6120
6220
|
const restore = options.announce === true ? this.#captureGeometry() : null;
|
|
6121
6221
|
const viewport = this.#viewport();
|
|
6122
6222
|
const box = this.#host.element.getBoundingClientRect();
|
|
6123
|
-
if (coversViewport(box,
|
|
6223
|
+
if (coversViewport(box, this.#fillRoom())) {
|
|
6124
6224
|
return false;
|
|
6125
6225
|
}
|
|
6126
6226
|
const [edgeY, edgeX] = corner.split("-");
|
|
@@ -6162,7 +6262,16 @@ var PanelPlacement = class {
|
|
|
6162
6262
|
* corner is what the panel grows from, so restoring a position without it
|
|
6163
6263
|
* puts the box back and animates it out of the wrong side. Absent values are
|
|
6164
6264
|
* captured as absent and removed on the way back, rather than written as
|
|
6165
|
-
* empty strings that would outrank the placement.
|
|
6265
|
+
* empty strings that would outrank the placement. And what the inset is, because
|
|
6266
|
+
* one put back with no record of having come from a grip or a fill reads as a
|
|
6267
|
+
* host's own, and a phone would leave it holding the panel off the screen.
|
|
6268
|
+
*
|
|
6269
|
+
* The surface may have changed by the time of the undo, so the two halves are
|
|
6270
|
+
* put back apart. Both records are, always, each to its own store, so the
|
|
6271
|
+
* memory and the storage cannot disagree and the surface they belong to finds
|
|
6272
|
+
* them when it comes back. The inline values only mean something to the surface
|
|
6273
|
+
* they were read from: written on any other, they pin a sidebar to where a
|
|
6274
|
+
* floating panel used to be.
|
|
6166
6275
|
*/
|
|
6167
6276
|
#captureGeometry() {
|
|
6168
6277
|
const inset2 = this.#host.element.style.getPropertyValue("--ag-ui-inset");
|
|
@@ -6170,33 +6279,48 @@ var PanelPlacement = class {
|
|
|
6170
6279
|
const corner = this.#host.element.getAttribute("data-expand-corner");
|
|
6171
6280
|
const launcherPos = this.#launcherPos;
|
|
6172
6281
|
const panelPos = this.#panelPos;
|
|
6173
|
-
const dockPos = this.#dockPos;
|
|
6282
|
+
const dockPos = this.#dockPos ?? this.#readDockPosition();
|
|
6174
6283
|
const expandCorner = this.#expandCorner;
|
|
6284
|
+
const gripInset = this.#gripInset;
|
|
6285
|
+
const fillInset = this.#fillInset;
|
|
6286
|
+
const cornerSpot = this.#cornerSpot ?? this.#readLauncherPosition();
|
|
6287
|
+
const owner = this.#positionOwner();
|
|
6175
6288
|
return () => {
|
|
6176
|
-
this.#restoreProperty("--ag-ui-inset", inset2);
|
|
6177
|
-
this.#restoreProperty("--ag-ui-launcher-inset", launcherInset);
|
|
6178
|
-
if (corner === null) {
|
|
6179
|
-
this.#host.element.removeAttribute("data-expand-corner");
|
|
6180
|
-
} else {
|
|
6181
|
-
this.#host.element.setAttribute("data-expand-corner", corner);
|
|
6182
|
-
}
|
|
6183
|
-
this.#launcherPos = launcherPos;
|
|
6184
|
-
this.#panelPos = panelPos;
|
|
6185
6289
|
this.#dockPos = dockPos;
|
|
6186
|
-
this.#
|
|
6187
|
-
if (
|
|
6188
|
-
this.#
|
|
6189
|
-
this.syncResizeAnchor();
|
|
6190
|
-
return;
|
|
6191
|
-
}
|
|
6192
|
-
if (launcherPos === null) {
|
|
6290
|
+
this.#storeDockPosition(dockPos);
|
|
6291
|
+
if (cornerSpot === null) {
|
|
6292
|
+
this.#cornerSpot = null;
|
|
6193
6293
|
this.#host.clearPreference(LAUNCHER_KEY);
|
|
6194
6294
|
} else {
|
|
6195
|
-
this.#
|
|
6295
|
+
this.#writeCornerSpot(cornerSpot);
|
|
6296
|
+
}
|
|
6297
|
+
if (owner === this.#positionOwner()) {
|
|
6298
|
+
this.#restoreProperty("--ag-ui-inset", inset2);
|
|
6299
|
+
this.#restoreProperty("--ag-ui-launcher-inset", launcherInset);
|
|
6300
|
+
if (corner === null) {
|
|
6301
|
+
this.#host.element.removeAttribute("data-expand-corner");
|
|
6302
|
+
} else {
|
|
6303
|
+
this.#host.element.setAttribute("data-expand-corner", corner);
|
|
6304
|
+
}
|
|
6305
|
+
this.#launcherPos = launcherPos;
|
|
6306
|
+
this.#panelPos = panelPos;
|
|
6307
|
+
this.#expandCorner = expandCorner;
|
|
6308
|
+
this.#gripInset = gripInset;
|
|
6309
|
+
this.#fillInset = fillInset;
|
|
6310
|
+
} else {
|
|
6311
|
+
this.#forgetCornerPosition();
|
|
6196
6312
|
}
|
|
6313
|
+
this.restoreLauncherPosition();
|
|
6197
6314
|
this.syncResizeAnchor();
|
|
6198
6315
|
};
|
|
6199
6316
|
}
|
|
6317
|
+
/** Which of the three kinds of surface decides where the panel stands. */
|
|
6318
|
+
#positionOwner() {
|
|
6319
|
+
if (this.#docked()) {
|
|
6320
|
+
return "dock";
|
|
6321
|
+
}
|
|
6322
|
+
return this.#surface().launcherDraggable ? "corner" : "placement";
|
|
6323
|
+
}
|
|
6200
6324
|
/**
|
|
6201
6325
|
* Which axes the current placement allows.
|
|
6202
6326
|
*
|
|
@@ -6257,6 +6381,36 @@ var PanelPlacement = class {
|
|
|
6257
6381
|
this.#anchor = anchor;
|
|
6258
6382
|
this.#host.element.setAttribute("data-resize-anchor", `${anchor.y}-${anchor.x}`);
|
|
6259
6383
|
this.#focusableGrip();
|
|
6384
|
+
this.#stampHeader();
|
|
6385
|
+
}
|
|
6386
|
+
/**
|
|
6387
|
+
* Tell the stylesheet whether the panel is the screen, for the header's cursor.
|
|
6388
|
+
*
|
|
6389
|
+
* Only this half of what refuses a header drag: the resting rules already answer
|
|
6390
|
+
* for the placement and for data-launcher-drag="false", and neither of those
|
|
6391
|
+
* re-applies anything when it changes, so a stamp taking them in would go on
|
|
6392
|
+
* refusing, in the cursor, a drag the header would take.
|
|
6393
|
+
*/
|
|
6394
|
+
#stampHeader() {
|
|
6395
|
+
this.#header?.toggleAttribute("data-fills-screen", this.#fillsScreen());
|
|
6396
|
+
const element = this.#host.element;
|
|
6397
|
+
if (this.#fillsWindow() !== element.hasAttribute("data-fills-window")) {
|
|
6398
|
+
requestAnimationFrame(() => {
|
|
6399
|
+
const fills = this.#fillsWindow();
|
|
6400
|
+
if (fills !== element.hasAttribute("data-fills-window")) {
|
|
6401
|
+
this.#barTaken = fills ? { room: this.#viewport().height, window: this.#screen().height, bar: this.#barHeight } : null;
|
|
6402
|
+
element.toggleAttribute("data-fills-window", fills);
|
|
6403
|
+
}
|
|
6404
|
+
});
|
|
6405
|
+
}
|
|
6406
|
+
}
|
|
6407
|
+
/**
|
|
6408
|
+
* Whether the panel fills the window in a placement it can move in. Not one that owns
|
|
6409
|
+
* its position: full stops at the bar, so taking the bar away would make it fill the
|
|
6410
|
+
* window, and that would take the bar away for good.
|
|
6411
|
+
*/
|
|
6412
|
+
#fillsWindow() {
|
|
6413
|
+
return this.#fillsScreen() && this.#surface().panelMovable;
|
|
6260
6414
|
}
|
|
6261
6415
|
/** Put a custom property back to a previous value, or remove it if there was none. */
|
|
6262
6416
|
#restoreProperty(name, value) {
|
|
@@ -6346,6 +6500,10 @@ var PanelPlacement = class {
|
|
|
6346
6500
|
element.style.removeProperty(DOCK_RIGHT);
|
|
6347
6501
|
}
|
|
6348
6502
|
element.toggleAttribute("data-dock-anchored", target === "none" && anchor !== null);
|
|
6503
|
+
this.#barHeight = height;
|
|
6504
|
+
if (target !== "bar") {
|
|
6505
|
+
this.#barTaken = null;
|
|
6506
|
+
}
|
|
6349
6507
|
this.#dockTop = target === "bar" && height > 0 ? bar.getBoundingClientRect().top : target === "none" && anchor !== null ? anchor.getBoundingClientRect().top : Number.POSITIVE_INFINITY;
|
|
6350
6508
|
this.#placeDocked();
|
|
6351
6509
|
return height;
|
|
@@ -6368,19 +6526,43 @@ var PanelPlacement = class {
|
|
|
6368
6526
|
* block reads the host's inset before its own, so a dragged spot written
|
|
6369
6527
|
* there would hold the panel off the screen's edges. The spot itself stays,
|
|
6370
6528
|
* for the next time there is room for it.
|
|
6529
|
+
*
|
|
6530
|
+
* A host's size that makes the panel the window hangs it off the resting inset's
|
|
6531
|
+
* far side, past the top of the window with the header on it under the bar, so
|
|
6532
|
+
* it stands at the start of the room instead, moved or not, as a corner panel
|
|
6533
|
+
* does, and the stylesheet takes the bar away for as long as it does. A placement
|
|
6534
|
+
* that owns its position keeps it, whatever the host's size.
|
|
6535
|
+
*
|
|
6536
|
+
* Under data-launcher-drag="false" a spot from before the attribute is left alone
|
|
6537
|
+
* while the panel is on the screen, as a corner panel's is, and the record stays
|
|
6538
|
+
* for the day the attribute comes off.
|
|
6371
6539
|
*/
|
|
6372
6540
|
#placeDocked() {
|
|
6373
|
-
if (!this.#docked() || !this.#
|
|
6541
|
+
if (!this.#docked() || !this.#surface().panelMovable || this.dragging()) {
|
|
6374
6542
|
return;
|
|
6375
6543
|
}
|
|
6376
|
-
const
|
|
6544
|
+
const element = this.#host.element;
|
|
6545
|
+
const at2 = this.#panelMovable() ? this.#dockPos ?? this.#readDockPosition() : null;
|
|
6377
6546
|
if (at2 === null) {
|
|
6547
|
+
if (this.#dockPos !== null) {
|
|
6548
|
+
const filling = element.style.getPropertyValue("--ag-ui-inset") === this.#fillInset;
|
|
6549
|
+
if (!filling && !this.#offScreen()) {
|
|
6550
|
+
return;
|
|
6551
|
+
}
|
|
6552
|
+
this.#dockPos = null;
|
|
6553
|
+
element.style.removeProperty("--ag-ui-inset");
|
|
6554
|
+
}
|
|
6555
|
+
this.#placeUnstated();
|
|
6378
6556
|
return;
|
|
6379
6557
|
}
|
|
6380
6558
|
this.#dockPos = at2;
|
|
6381
|
-
|
|
6382
|
-
const rest =
|
|
6383
|
-
|
|
6559
|
+
element.style.removeProperty("--ag-ui-inset");
|
|
6560
|
+
const rest = element.getBoundingClientRect();
|
|
6561
|
+
const viewport = this.#viewport();
|
|
6562
|
+
if (coversViewport(rest, this.#fillRoom())) {
|
|
6563
|
+
if (hangsOff(rest, viewport, this.#screen())) {
|
|
6564
|
+
this.#standAtStart(rest);
|
|
6565
|
+
}
|
|
6384
6566
|
return;
|
|
6385
6567
|
}
|
|
6386
6568
|
this.syncResizeAnchor();
|
|
@@ -6427,15 +6609,52 @@ var PanelPlacement = class {
|
|
|
6427
6609
|
return this.#host.element.getAttribute("data-launcher-drag") !== "false" && this.#surface().panelMovable;
|
|
6428
6610
|
}
|
|
6429
6611
|
/**
|
|
6430
|
-
* Whether a header drag may start now. A
|
|
6431
|
-
*
|
|
6432
|
-
*
|
|
6612
|
+
* Whether a header drag may start now. A panel that is the screen, on a phone
|
|
6613
|
+
* or by a host's own sizing, has nowhere to go, and a drag there would pull it
|
|
6614
|
+
* off the edges it fills.
|
|
6433
6615
|
*/
|
|
6434
6616
|
#canDragPanel() {
|
|
6435
|
-
|
|
6436
|
-
|
|
6617
|
+
return this.#panelMovable() && !this.#fillsScreen();
|
|
6618
|
+
}
|
|
6619
|
+
/** Whether the panel covers the usable viewport, as it stands now. */
|
|
6620
|
+
#fillsScreen() {
|
|
6621
|
+
return coversViewport(this.#host.element.getBoundingClientRect(), this.#fillRoom());
|
|
6622
|
+
}
|
|
6623
|
+
/**
|
|
6624
|
+
* The box a fill is judged against: the usable one, unless taking the bar away is
|
|
6625
|
+
* what grew it.
|
|
6626
|
+
*
|
|
6627
|
+
* A host can write the bar's height into the inset it reserves. The panel fills the
|
|
6628
|
+
* box that leaves, so the bar goes, the height says 0, the host writes that, the box
|
|
6629
|
+
* grows by what the bar took, and the panel no longer fills it. Judged against that,
|
|
6630
|
+
* the bar comes back and it all goes round again every frame without end. So on the
|
|
6631
|
+
* same window, a box grown by the bar's height is the box it was, and the panel
|
|
6632
|
+
* stands where it stood with the bar gone. Grown by anything else, or on a window of
|
|
6633
|
+
* another size, it is a change of the host's or the reader's and is judged as one.
|
|
6634
|
+
* The slack is the pixel a host rounding the height up can add.
|
|
6635
|
+
*/
|
|
6636
|
+
#fillRoom() {
|
|
6637
|
+
const viewport = this.#viewport();
|
|
6638
|
+
const taken = this.#barTaken;
|
|
6639
|
+
if (taken !== null && taken.window === this.#screen().height && Math.abs(viewport.height - taken.room - taken.bar) <= 1) {
|
|
6640
|
+
return { ...viewport, height: taken.room };
|
|
6437
6641
|
}
|
|
6438
|
-
return
|
|
6642
|
+
return viewport;
|
|
6643
|
+
}
|
|
6644
|
+
/**
|
|
6645
|
+
* Whether the panel stands off an edge of the screen, as it stands now.
|
|
6646
|
+
*
|
|
6647
|
+
* The screen rather than the usable box: a panel under chrome the host reserved
|
|
6648
|
+
* has its controls where the host can still bring them back, and is not what a
|
|
6649
|
+
* position kept under data-launcher-drag="false" has to give way to.
|
|
6650
|
+
*/
|
|
6651
|
+
#offScreen() {
|
|
6652
|
+
const screen = this.#screen();
|
|
6653
|
+
return hangsOff(
|
|
6654
|
+
this.#host.element.getBoundingClientRect(),
|
|
6655
|
+
{ left: 0, top: 0, ...screen },
|
|
6656
|
+
screen
|
|
6657
|
+
);
|
|
6439
6658
|
}
|
|
6440
6659
|
/**
|
|
6441
6660
|
* Whether this placement lets the launcher be dragged, read per interaction
|
|
@@ -6568,9 +6787,6 @@ var PanelPlacement = class {
|
|
|
6568
6787
|
* back rather than leaving a stale inline one behind.
|
|
6569
6788
|
*/
|
|
6570
6789
|
#applyLauncherPlacement(at2) {
|
|
6571
|
-
if (!this.#launcherDraggable()) {
|
|
6572
|
-
return;
|
|
6573
|
-
}
|
|
6574
6790
|
this.#launcherPos = at2;
|
|
6575
6791
|
this.#panelPos = null;
|
|
6576
6792
|
const panel = this.#host.element.getBoundingClientRect();
|
|
@@ -6591,6 +6807,7 @@ var PanelPlacement = class {
|
|
|
6591
6807
|
}
|
|
6592
6808
|
/** Move the launcher live during a drag, without persisting. */
|
|
6593
6809
|
#moveLauncher(left, top) {
|
|
6810
|
+
this.#cornerSpot = { left, top };
|
|
6594
6811
|
this.#applyLauncherPlacement({ left, top });
|
|
6595
6812
|
}
|
|
6596
6813
|
/** Move the launcher and remember where, per tab, like the dragged size. */
|
|
@@ -6695,9 +6912,6 @@ var PanelPlacement = class {
|
|
|
6695
6912
|
* its own right.
|
|
6696
6913
|
*/
|
|
6697
6914
|
#restorePanelPosition(at2) {
|
|
6698
|
-
if (!this.#launcherDraggable()) {
|
|
6699
|
-
return;
|
|
6700
|
-
}
|
|
6701
6915
|
const rect = this.#host.element.getBoundingClientRect();
|
|
6702
6916
|
const held = clampPanel(
|
|
6703
6917
|
{ left: at2.left, top: at2.top, right: at2.left + rect.width, bottom: at2.top + rect.height },
|
|
@@ -6723,6 +6937,9 @@ var PanelPlacement = class {
|
|
|
6723
6937
|
* with it, the panel goes back where it was put; without it, the panel is
|
|
6724
6938
|
* re-derived from the launcher and opens into whatever room the viewport has
|
|
6725
6939
|
* now.
|
|
6940
|
+
*
|
|
6941
|
+
* Every caller is a gesture ending or a move being made, so where the two
|
|
6942
|
+
* stand now is what was stated. A restore never calls this.
|
|
6726
6943
|
*/
|
|
6727
6944
|
#storeLauncherPosition() {
|
|
6728
6945
|
const position = this.#launcherPos;
|
|
@@ -6730,38 +6947,151 @@ var PanelPlacement = class {
|
|
|
6730
6947
|
return;
|
|
6731
6948
|
}
|
|
6732
6949
|
const panel = this.#panelPos;
|
|
6733
|
-
this.#
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6950
|
+
this.#writeCornerSpot(panel === null ? position : { ...position, panel });
|
|
6951
|
+
}
|
|
6952
|
+
/** Make `spot` the stated one, and remember it. */
|
|
6953
|
+
#writeCornerSpot(spot) {
|
|
6954
|
+
this.#cornerSpot = spot;
|
|
6955
|
+
this.#host.writePreference(LAUNCHER_KEY, JSON.stringify(spot));
|
|
6737
6956
|
}
|
|
6738
6957
|
/**
|
|
6739
6958
|
* Re-apply the dragged position against the current viewport: on connect,
|
|
6740
6959
|
* whenever the window resizes, and before an expand. The viewport that
|
|
6741
6960
|
* stored a position may since have shrunk, and a launcher past the edge is
|
|
6742
6961
|
* unreachable -- it is the only way back to a collapsed conversation.
|
|
6962
|
+
*
|
|
6963
|
+
* The one gate on whether a stored position is this element's to honour: the
|
|
6964
|
+
* gestures check their own before they ever reach the placing below.
|
|
6743
6965
|
*/
|
|
6744
6966
|
restoreLauncherPosition() {
|
|
6967
|
+
this.#placeFromRecord(true);
|
|
6968
|
+
this.#stampHeader();
|
|
6969
|
+
}
|
|
6970
|
+
/**
|
|
6971
|
+
* Re-apply it for a change in the panel's own layout, which needs no window or
|
|
6972
|
+
* reopen to happen: a line typed in the composer is one. A position waiting to
|
|
6973
|
+
* be handed back under data-launcher-drag="false" waits on, because a panel that leaves for the
|
|
6974
|
+
* host's corner as the reader starts typing is a jump with no cause they can see.
|
|
6975
|
+
* It waits no longer than the panel stays on the screen: a host's size can stand
|
|
6976
|
+
* it off an edge, where its header is out of reach until something else happens.
|
|
6977
|
+
*/
|
|
6978
|
+
settleLauncherPosition() {
|
|
6979
|
+
this.#placeFromRecord(false);
|
|
6980
|
+
this.#stampHeader();
|
|
6981
|
+
}
|
|
6982
|
+
/** The placing behind {@link restoreLauncherPosition}. */
|
|
6983
|
+
#placeFromRecord(handBack) {
|
|
6745
6984
|
if (this.#docked()) {
|
|
6746
6985
|
this.#placeDocked();
|
|
6747
6986
|
return;
|
|
6748
6987
|
}
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6988
|
+
if (!this.#surface().launcherDraggable) {
|
|
6989
|
+
return;
|
|
6990
|
+
}
|
|
6991
|
+
const draggable = this.#launcherDraggable();
|
|
6992
|
+
if (!draggable && this.#launcherPos !== null) {
|
|
6993
|
+
if (!handBack && !this.#offScreen()) {
|
|
6994
|
+
return;
|
|
6995
|
+
}
|
|
6996
|
+
this.releaseLauncherPosition();
|
|
6997
|
+
}
|
|
6998
|
+
const spot = draggable ? this.#cornerSpot ?? this.#readLauncherPosition() : null;
|
|
6999
|
+
if (spot === null) {
|
|
7000
|
+
this.#placeUnstated();
|
|
6752
7001
|
return;
|
|
6753
7002
|
}
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
7003
|
+
if (this.#standFullBleed(spot)) {
|
|
7004
|
+
return;
|
|
7005
|
+
}
|
|
7006
|
+
if (spot.panel !== void 0) {
|
|
7007
|
+
this.#launcherPos = { left: spot.left, top: spot.top };
|
|
7008
|
+
this.#restorePanelPosition(spot.panel);
|
|
6758
7009
|
return;
|
|
6759
7010
|
}
|
|
6760
7011
|
const box = this.#launcherBox();
|
|
6761
7012
|
this.#applyLauncherPlacement(
|
|
6762
|
-
clampLauncher({ ...box, left:
|
|
7013
|
+
clampLauncher({ ...box, left: spot.left, top: spot.top }, this.#viewport())
|
|
6763
7014
|
);
|
|
6764
7015
|
}
|
|
7016
|
+
/**
|
|
7017
|
+
* Leave a corner panel full-bleed if it has become the screen, and say whether
|
|
7018
|
+
* it has.
|
|
7019
|
+
*
|
|
7020
|
+
* The inline inset comes off, because the small-viewport block reads the
|
|
7021
|
+
* host's inset before its own: a spot written there holds the panel off the
|
|
7022
|
+
* edges it should fill, and past them by the same amount, since its size is
|
|
7023
|
+
* the screen's. What the panel covers is read before that, from the size
|
|
7024
|
+
* alone, which no inset changes.
|
|
7025
|
+
*
|
|
7026
|
+
* The launcher stays a place of its own. It is positioned from the host box,
|
|
7027
|
+
* so it is measured again from the box that has just moved, and it is held on
|
|
7028
|
+
* the screen, because it is the way back from a collapsed conversation. The
|
|
7029
|
+
* spot itself is untouched, for the next time there is room for it.
|
|
7030
|
+
*
|
|
7031
|
+
* Above the breakpoint nothing in the stylesheet takes the panel to the edges.
|
|
7032
|
+
* A host's own size makes it the screen while the resting inset still spends a
|
|
7033
|
+
* gutter on one corner, which hangs the box off the opposite one, so there it
|
|
7034
|
+
* stands at the start of the room the host left free and keeps that inset. A
|
|
7035
|
+
* box the placement already holds on the screen has no inset to keep, which is
|
|
7036
|
+
* every phone, and a host's rule that placed the box itself.
|
|
7037
|
+
*/
|
|
7038
|
+
#standFullBleed(spot) {
|
|
7039
|
+
const element = this.#host.element;
|
|
7040
|
+
const viewport = this.#viewport();
|
|
7041
|
+
if (!coversViewport(element.getBoundingClientRect(), viewport)) {
|
|
7042
|
+
return false;
|
|
7043
|
+
}
|
|
7044
|
+
element.style.removeProperty("--ag-ui-inset");
|
|
7045
|
+
const rest = element.getBoundingClientRect();
|
|
7046
|
+
const hanging = hangsOff(rest, viewport, this.#screen());
|
|
7047
|
+
const box = hanging ? atStartOf(viewport, rest) : rest;
|
|
7048
|
+
const launcher = { ...this.#launcherBox(), left: spot.left, top: spot.top };
|
|
7049
|
+
this.#placePanelAndLauncher(box, { ...launcher, ...clampLauncher(launcher, viewport) });
|
|
7050
|
+
if (!hanging) {
|
|
7051
|
+
element.style.removeProperty("--ag-ui-inset");
|
|
7052
|
+
}
|
|
7053
|
+
return true;
|
|
7054
|
+
}
|
|
7055
|
+
/**
|
|
7056
|
+
* Place a panel that has no spot, a corner one or a docked one, the half of
|
|
7057
|
+
* {@link #standFullBleed} with nothing stated to keep and no launcher of its own
|
|
7058
|
+
* to measure.
|
|
7059
|
+
*
|
|
7060
|
+
* Only an inset this element wrote comes off or goes back. One the host wrote
|
|
7061
|
+
* inline is a token a host may state on a phone as anywhere else, and with no
|
|
7062
|
+
* spot it never came from here.
|
|
7063
|
+
*/
|
|
7064
|
+
#placeUnstated() {
|
|
7065
|
+
const element = this.#host.element;
|
|
7066
|
+
const inline = element.style.getPropertyValue("--ag-ui-inset");
|
|
7067
|
+
const viewport = this.#viewport();
|
|
7068
|
+
if (!coversViewport(element.getBoundingClientRect(), this.#fillRoom())) {
|
|
7069
|
+
if (inline === "" || inline === this.#fillInset) {
|
|
7070
|
+
this.#restoreProperty("--ag-ui-inset", this.#gripInset ?? "");
|
|
7071
|
+
this.syncResizeAnchor();
|
|
7072
|
+
}
|
|
7073
|
+
this.#fillInset = null;
|
|
7074
|
+
return;
|
|
7075
|
+
}
|
|
7076
|
+
if (inline === this.#gripInset || inline === this.#fillInset) {
|
|
7077
|
+
element.style.removeProperty("--ag-ui-inset");
|
|
7078
|
+
}
|
|
7079
|
+
const rest = element.getBoundingClientRect();
|
|
7080
|
+
const screen = this.#screen();
|
|
7081
|
+
if (element.style.getPropertyValue("--ag-ui-inset") === "" && hangsOff(rest, viewport, screen)) {
|
|
7082
|
+
this.#standAtStart(rest);
|
|
7083
|
+
}
|
|
7084
|
+
}
|
|
7085
|
+
/** Stand a box a host's size fills at the start of the room the host left free. */
|
|
7086
|
+
#standAtStart(rest) {
|
|
7087
|
+
this.#fillInset = insetFrom(
|
|
7088
|
+
{ x: "left", y: "top" },
|
|
7089
|
+
atStartOf(this.#viewport(), rest),
|
|
7090
|
+
this.#screen()
|
|
7091
|
+
);
|
|
7092
|
+
this.#host.element.style.setProperty("--ag-ui-inset", this.#fillInset);
|
|
7093
|
+
this.syncResizeAnchor();
|
|
7094
|
+
}
|
|
6765
7095
|
/** The persisted position for this instance, or null. */
|
|
6766
7096
|
#readLauncherPosition() {
|
|
6767
7097
|
const raw = this.#host.readPreference(LAUNCHER_KEY);
|
|
@@ -6796,9 +7126,15 @@ var PanelPlacement = class {
|
|
|
6796
7126
|
if (this.#launcherDraggable()) {
|
|
6797
7127
|
return;
|
|
6798
7128
|
}
|
|
7129
|
+
this.#forgetCornerPosition();
|
|
7130
|
+
}
|
|
7131
|
+
/** Take everything off that placed a corner panel, leaving its record alone. */
|
|
7132
|
+
#forgetCornerPosition() {
|
|
6799
7133
|
this.#launcherPos = null;
|
|
6800
7134
|
this.#panelPos = null;
|
|
6801
7135
|
this.#expandCorner = null;
|
|
7136
|
+
this.#gripInset = null;
|
|
7137
|
+
this.#fillInset = null;
|
|
6802
7138
|
this.#host.element.style.removeProperty("--ag-ui-inset");
|
|
6803
7139
|
this.#host.element.style.removeProperty("--ag-ui-launcher-inset");
|
|
6804
7140
|
this.#host.element.removeAttribute("data-expand-corner");
|
|
@@ -6834,26 +7170,32 @@ var PanelPlacement = class {
|
|
|
6834
7170
|
if (this.#docked()) {
|
|
6835
7171
|
this.#dockPos = { left: box.left, top: box.top };
|
|
6836
7172
|
}
|
|
6837
|
-
if (grip.x
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
7173
|
+
if (grip.x === this.#anchor.x || grip.y === this.#anchor.y) {
|
|
7174
|
+
const anchor = this.#anchor;
|
|
7175
|
+
const inset2 = insetFrom(anchor, box, this.#screen());
|
|
7176
|
+
this.#host.element.style.setProperty("--ag-ui-inset", inset2);
|
|
7177
|
+
this.#gripInset = this.#launcherPos === null && !this.#docked() ? inset2 : null;
|
|
7178
|
+
if (this.#launcherPos !== null) {
|
|
7179
|
+
const size = this.#host.launcher.offsetWidth;
|
|
7180
|
+
this.#launcherPos = {
|
|
7181
|
+
left: anchor.x === "left" ? box.left : box.right - size,
|
|
7182
|
+
top: anchor.y === "top" ? box.top : box.bottom - size
|
|
7183
|
+
};
|
|
7184
|
+
}
|
|
6848
7185
|
}
|
|
6849
|
-
if (this.#panelPos !== null) {
|
|
6850
|
-
|
|
7186
|
+
if (this.#panelPos !== null && !this.#docked()) {
|
|
7187
|
+
const drawn = this.#host.element.getBoundingClientRect();
|
|
7188
|
+
this.#panelPos = { left: drawn.left, top: drawn.top };
|
|
6851
7189
|
}
|
|
6852
7190
|
return box;
|
|
6853
7191
|
}
|
|
6854
7192
|
/** Finish a resize: keep the box, remember it, and re-read the pinned edges. */
|
|
6855
7193
|
#commitResize(grip, box) {
|
|
6856
7194
|
const held = this.#applyResize(grip, box);
|
|
7195
|
+
if (this.#host.element.style.getPropertyValue("--ag-ui-inset") === this.#fillInset) {
|
|
7196
|
+
this.#gripInset = this.#fillInset;
|
|
7197
|
+
this.#fillInset = null;
|
|
7198
|
+
}
|
|
6857
7199
|
this.#persistSize({ width: held.right - held.left, height: held.bottom - held.top });
|
|
6858
7200
|
this.#storeLauncherPosition();
|
|
6859
7201
|
if (this.#dockPos !== null) {
|
|
@@ -9001,6 +9343,14 @@ var COLLAPSE_STYLES = `
|
|
|
9001
9343
|
display: none;
|
|
9002
9344
|
}
|
|
9003
9345
|
|
|
9346
|
+
/* A panel a host sized to the whole window stands on the strip, whose fixed box would
|
|
9347
|
+
draw over the composer's foot. So it goes as it does on a phone, by display, so the
|
|
9348
|
+
bar measures 0 and the host's padding goes with it, and the header's minimise
|
|
9349
|
+
control is the way back. Collapsed, the strip is the way back, so it stays. */
|
|
9350
|
+
:host([data-collapsed-to="bar"][data-fills-window]:not([collapsed])) .dock-bar {
|
|
9351
|
+
display: none;
|
|
9352
|
+
}
|
|
9353
|
+
|
|
9004
9354
|
/* History from the bar: the panel's own drawer, moved beside the bar while
|
|
9005
9355
|
the panel is away, drawn as a popover standing on the bar's end. There is
|
|
9006
9356
|
no backdrop; a press outside the bar closes it, as Escape does.
|
|
@@ -9553,6 +9903,15 @@ var RESIZE_STYLES = `
|
|
|
9553
9903
|
bottom: 0;
|
|
9554
9904
|
}
|
|
9555
9905
|
|
|
9906
|
+
/* A docked panel a host sized to the whole window has nothing to give a grip: its size
|
|
9907
|
+
is the host's, and a place a grip left would outlast that size with the bar back
|
|
9908
|
+
under it. They go as they do on a phone. A corner panel keeps them, since it has no
|
|
9909
|
+
bar to stand clear of and a grip's place is its own. */
|
|
9910
|
+
:host([data-collapsed-to="bar"][data-fills-window]) .resize-handle,
|
|
9911
|
+
:host([data-collapsed-to="none"][data-fills-window]) .resize-handle {
|
|
9912
|
+
display: none;
|
|
9913
|
+
}
|
|
9914
|
+
|
|
9556
9915
|
/* Docked: the placement owns the height, so the horizontal edges and every
|
|
9557
9916
|
corner are inert and must not advertise a drag that does nothing. The two
|
|
9558
9917
|
vertical edges remain, which is the same affordance these placements had
|
|
@@ -10706,6 +11065,15 @@ var FRAME_STYLES = `
|
|
|
10706
11065
|
touch-action: auto;
|
|
10707
11066
|
}
|
|
10708
11067
|
|
|
11068
|
+
/* A panel that is the screen has nowhere to go, so its header refuses the drag
|
|
11069
|
+
and must not offer it. No selector can see that a host has sized the panel to
|
|
11070
|
+
the window, so the placement stamps this from the measurement that refuses the
|
|
11071
|
+
drag. :host is carried for the specificity, not the scope, as below. Before the
|
|
11072
|
+
dragging rule, so a drag the header did take keeps its grabbing cursor. */
|
|
11073
|
+
:host .header[data-fills-screen] {
|
|
11074
|
+
cursor: auto;
|
|
11075
|
+
}
|
|
11076
|
+
|
|
10709
11077
|
/* :host is carried for the specificity, not the scope: the placement rules
|
|
10710
11078
|
above are a host selector plus a class plus an attribute, and a bare
|
|
10711
11079
|
.header[data-dragging] loses to them -- silently, because the drag still
|
|
@@ -10755,6 +11123,14 @@ var FRAME_STYLES = `
|
|
|
10755
11123
|
flex: none;
|
|
10756
11124
|
}
|
|
10757
11125
|
|
|
11126
|
+
/* Under every theme: once the host sets the header text, the header behind the
|
|
11127
|
+
buttons is the host's colour, so their rest opacity goes. The frame's 0.85
|
|
11128
|
+
lets the header through the glyph, which took white on a mid-blue header from
|
|
11129
|
+
5.1:1 to 4.2:1. With the header text set, the opacity below reads a colour,
|
|
11130
|
+
which no opacity can be, so it is invalid at computed-value time and falls to
|
|
11131
|
+
the initial 1; unset, it reads the fallback. The glyph is each theme's own
|
|
11132
|
+
business: this inherits the header text, and a theme that draws the buttons
|
|
11133
|
+
in another ink reads the same switch to yield. */
|
|
10758
11134
|
.header-btn {
|
|
10759
11135
|
display: inline-flex;
|
|
10760
11136
|
flex: 0 0 auto;
|
|
@@ -10771,12 +11147,15 @@ var FRAME_STYLES = `
|
|
|
10771
11147
|
padding: 0;
|
|
10772
11148
|
border-radius: 6px;
|
|
10773
11149
|
cursor: pointer;
|
|
10774
|
-
opacity: 0.85;
|
|
11150
|
+
opacity: var(--_header-fg-set, 0.85);
|
|
10775
11151
|
}
|
|
10776
11152
|
|
|
11153
|
+
/* The square is the host's own when it set one; otherwise it is mixed from the
|
|
11154
|
+
header text when the host set that, so it reads on any header the host
|
|
11155
|
+
painted; otherwise it is the theme's own. See the alias in the token defaults. */
|
|
10777
11156
|
.header-btn:hover {
|
|
10778
11157
|
opacity: 1;
|
|
10779
|
-
background:
|
|
11158
|
+
background: var(--_header-btn-hover);
|
|
10780
11159
|
}
|
|
10781
11160
|
`;
|
|
10782
11161
|
|
|
@@ -10854,18 +11233,19 @@ var TOKEN_STYLES = `
|
|
|
10854
11233
|
/* Switches: a public token read with no fallback, named for it with -set.
|
|
10855
11234
|
With the token unset the switch computes to the guaranteed-invalid value,
|
|
10856
11235
|
so a rule that reads it with a fallback gets the fallback; that is how a
|
|
10857
|
-
rule tells a token the host set from the theme's default for it.
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
drew). */
|
|
11236
|
+
rule tells a token the host set from the theme's default for it. Every
|
|
11237
|
+
theme's header buttons follow the header text only once a host sets it,
|
|
11238
|
+
and a step group's calls follow the tool token only once a host sets it
|
|
11239
|
+
(classic defaults --_tool-fg to the accent, which a group never drew). */
|
|
10862
11240
|
--_header-fg-set: var(--ag-ui-header-fg);
|
|
10863
11241
|
--_tool-fg-set: var(--ag-ui-tool-fg);
|
|
10864
11242
|
/* The header buttons' hover square, by default, once the header text is
|
|
10865
11243
|
set. Here it is the text at about a fifth, which every browser can mix;
|
|
10866
11244
|
where relative colour syntax is supported (see the block at the end) it
|
|
10867
11245
|
is the text's opposite instead. It reads the switch, so it is unset with
|
|
10868
|
-
it, and the square below falls to the panel's hover shade
|
|
11246
|
+
it, and the square below falls to the theme's own: the panel's hover shade
|
|
11247
|
+
here, restated by each theme that draws another, so one rule in the frame
|
|
11248
|
+
paints them all. */
|
|
10869
11249
|
--_header-btn-hover-auto: color-mix(in srgb, var(--_header-fg-set) 18%, transparent);
|
|
10870
11250
|
--_header-btn-hover: var(--ag-ui-header-btn-hover, var(--_header-btn-hover-auto, var(--_hover)));
|
|
10871
11251
|
--_border: var(--ag-ui-border, var(--_line));
|
|
@@ -11306,6 +11686,7 @@ var CLASSIC_TOKENS = `
|
|
|
11306
11686
|
--_tool-fg: var(--ag-ui-tool-fg, var(--_accent));
|
|
11307
11687
|
--_header-bg: var(--ag-ui-header-bg, light-dark(var(--_accent), #1f1f30));
|
|
11308
11688
|
--_header-fg: var(--ag-ui-header-fg, light-dark(#ffffff, #e8e8f2));
|
|
11689
|
+
--_header-btn-hover: var(--ag-ui-header-btn-hover, var(--_header-btn-hover-auto, rgba(255, 255, 255, 0.18)));
|
|
11309
11690
|
--_border: var(--ag-ui-border, light-dark(#e2e2ec, #33334a));
|
|
11310
11691
|
--_muted: var(--ag-ui-muted, light-dark(#6b7280, #9aa0b4));
|
|
11311
11692
|
--_success: var(--ag-ui-success, #15803d);
|
|
@@ -11392,14 +11773,15 @@ var LEDGER_STYLES = `
|
|
|
11392
11773
|
vertical-align: 1px;
|
|
11393
11774
|
}
|
|
11394
11775
|
|
|
11776
|
+
/* Both inks yield once the host sets the header text, since the header behind
|
|
11777
|
+
them is then the host's colour (see the switches in the token defaults). */
|
|
11395
11778
|
:host([theme~="ledger"]) .header-btn {
|
|
11396
|
-
color: var(--_ink-3);
|
|
11779
|
+
color: var(--_header-fg-set, var(--_ink-3));
|
|
11397
11780
|
border-radius: 3px;
|
|
11398
11781
|
}
|
|
11399
11782
|
|
|
11400
11783
|
:host([theme~="ledger"]) .header-btn:hover {
|
|
11401
|
-
|
|
11402
|
-
color: var(--_fg);
|
|
11784
|
+
color: var(--_header-fg-set, var(--_fg));
|
|
11403
11785
|
}
|
|
11404
11786
|
|
|
11405
11787
|
:host([theme~="ledger"]) .message--user {
|
|
@@ -11549,6 +11931,7 @@ var LEDGER_TOKENS = `
|
|
|
11549
11931
|
--_msg-radius: var(--ag-ui-msg-radius, 0px);
|
|
11550
11932
|
--_composer-radius: var(--ag-ui-composer-radius, 4px);
|
|
11551
11933
|
--_user-bg: var(--ag-ui-user-bg, transparent);
|
|
11934
|
+
--_header-btn-hover: var(--ag-ui-header-btn-hover, var(--_header-btn-hover-auto, var(--_hover)));
|
|
11552
11935
|
--_frame-shadow: var(--ag-ui-frame-shadow, 0 0 0 1px var(--_line-strong), 0 20px 40px -28px rgb(0 0 0 / 0.45));
|
|
11553
11936
|
--_launcher-bg: var(--ag-ui-launcher-bg, var(--_fg));
|
|
11554
11937
|
--_launcher-fg: var(--ag-ui-launcher-fg, var(--_bg));
|
|
@@ -11669,28 +12052,15 @@ ${THEME_HOST.quiet} .header-title {
|
|
|
11669
12052
|
|
|
11670
12053
|
/* The buttons sit back in the third ink on the panel. Once the host sets the
|
|
11671
12054
|
header text they draw in it instead, since the header behind them is then
|
|
11672
|
-
the host's colour (see the switches in the token defaults).
|
|
11673
|
-
rest opacity: the frame's 0.85 lets the header through the glyph, which
|
|
11674
|
-
took white on a mid-blue header from 5.1:1 to 4.2:1. With the header text
|
|
11675
|
-
set, the opacity below reads a colour, which no opacity can be, so it is
|
|
11676
|
-
invalid at computed-value time and falls to the initial 1; unset, it reads
|
|
11677
|
-
the fallback, the frame's own 0.85. */
|
|
12055
|
+
the host's colour (see the switches in the token defaults). */
|
|
11678
12056
|
${THEME_HOST.quiet} .header-btn {
|
|
11679
12057
|
color: var(--_header-fg-set, var(--_ink-3));
|
|
11680
|
-
opacity: var(--_header-fg-set, 0.85);
|
|
11681
12058
|
border-radius: 8px;
|
|
11682
12059
|
}
|
|
11683
12060
|
|
|
11684
|
-
/* The
|
|
11685
|
-
from the header text when the host set that, so it reads on any header the
|
|
11686
|
-
host painted; otherwise it is the panel shade.
|
|
11687
|
-
The glyph takes the header text, which is the full ink when that is unset.
|
|
11688
|
-
The opacity restates the frame's hover value, which the rest rule above
|
|
11689
|
-
outweighs. */
|
|
12061
|
+
/* The glyph takes the header text, which is the full ink when that is unset. */
|
|
11690
12062
|
${THEME_HOST.quiet} .header-btn:hover {
|
|
11691
12063
|
color: var(--_header-fg);
|
|
11692
|
-
opacity: 1;
|
|
11693
|
-
background: var(--_header-btn-hover);
|
|
11694
12064
|
}
|
|
11695
12065
|
|
|
11696
12066
|
${THEME_HOST.quiet} .message--user {
|
|
@@ -11833,14 +12203,15 @@ var SOFT_STYLES = `
|
|
|
11833
12203
|
background: linear-gradient(140deg, color-mix(in oklch, var(--_accent) 55%, var(--_bg)), var(--_accent));
|
|
11834
12204
|
}
|
|
11835
12205
|
|
|
12206
|
+
/* Both inks yield once the host sets the header text, since the header behind
|
|
12207
|
+
them is then the host's colour (see the switches in the token defaults). */
|
|
11836
12208
|
:host([theme~="soft"]) .header-btn {
|
|
11837
|
-
color: var(--_ink-3);
|
|
12209
|
+
color: var(--_header-fg-set, var(--_ink-3));
|
|
11838
12210
|
border-radius: 10px;
|
|
11839
12211
|
}
|
|
11840
12212
|
|
|
11841
12213
|
:host([theme~="soft"]) .header-btn:hover {
|
|
11842
|
-
|
|
11843
|
-
color: var(--_accent-ink);
|
|
12214
|
+
color: var(--_header-fg-set, var(--_accent-ink));
|
|
11844
12215
|
}
|
|
11845
12216
|
|
|
11846
12217
|
:host([theme~="soft"]) .messages {
|
|
@@ -11961,6 +12332,7 @@ var SOFT_TOKENS = `
|
|
|
11961
12332
|
--_tint: var(--ag-ui-tint, color-mix(in oklch, var(--_accent) 5%, var(--_bg)));
|
|
11962
12333
|
--_tint-2: var(--ag-ui-tint-2, color-mix(in oklch, var(--_accent) 13%, var(--_bg)));
|
|
11963
12334
|
--_header-bg: var(--ag-ui-header-bg, var(--_tint));
|
|
12335
|
+
--_header-btn-hover: var(--ag-ui-header-btn-hover, var(--_header-btn-hover-auto, var(--_tint-2)));
|
|
11964
12336
|
--_user-bg: var(--ag-ui-user-bg, var(--_tint-2));
|
|
11965
12337
|
--_border: var(--ag-ui-border, color-mix(in oklch, var(--_accent) 14%, var(--_bg)));
|
|
11966
12338
|
--_radius: var(--ag-ui-radius, 16px);
|
|
@@ -12778,7 +13150,18 @@ var TRANSCRIPT_STYLES = `
|
|
|
12778
13150
|
|
|
12779
13151
|
visibility rather than display: none, because a box that generates no
|
|
12780
13152
|
layout has no used values to read. Zero-sized, absolutely positioned and
|
|
12781
|
-
inert, so it costs nothing but the read.
|
|
13153
|
+
inert, so it costs nothing but the read.
|
|
13154
|
+
|
|
13155
|
+
The padding gives that box the size of the tokens, which a card narrower than
|
|
13156
|
+
one of them would scroll to reach. Scrollable overflow counts a box as its
|
|
13157
|
+
transform draws it, so a scale of nothing takes it out of that count on every
|
|
13158
|
+
side and in either direction, and leaves what is read: the padding's used values,
|
|
13159
|
+
and the containing block a percentage resolves against. Position fixed would add
|
|
13160
|
+
no scroll either, and would change that block.
|
|
13161
|
+
|
|
13162
|
+
From its own corner, which is inside whatever holds it where the centre of the
|
|
13163
|
+
padding need not be: kept for an engine that counts a point with no area. Chromium
|
|
13164
|
+
does not, so the suite cannot pin the origin. */
|
|
12782
13165
|
.viewport-probe {
|
|
12783
13166
|
position: absolute;
|
|
12784
13167
|
top: 0;
|
|
@@ -12787,6 +13170,8 @@ var TRANSCRIPT_STYLES = `
|
|
|
12787
13170
|
height: 0;
|
|
12788
13171
|
visibility: hidden;
|
|
12789
13172
|
pointer-events: none;
|
|
13173
|
+
transform: scale(0);
|
|
13174
|
+
transform-origin: 0 0;
|
|
12790
13175
|
padding: var(--_viewport-inset-top) var(--_viewport-inset-right)
|
|
12791
13176
|
var(--_viewport-inset-bottom) var(--_viewport-inset-left);
|
|
12792
13177
|
}
|
|
@@ -19847,13 +20232,40 @@ var AgUiChat = class extends HTMLElement {
|
|
|
19847
20232
|
* a field so `removeEventListener` on disconnect gets the same reference.
|
|
19848
20233
|
*/
|
|
19849
20234
|
#onViewportResize = () => {
|
|
20235
|
+
if (this.#readyToPlace()) {
|
|
20236
|
+
this.#placement.restoreLauncherPosition();
|
|
20237
|
+
}
|
|
20238
|
+
};
|
|
20239
|
+
/**
|
|
20240
|
+
* The same for a change only the panel's own layout shows: a host's size, a
|
|
20241
|
+
* viewport inset token, a line typed in the composer. Not a moment to hand a
|
|
20242
|
+
* position back, so a panel under data-launcher-drag="false" stays put.
|
|
20243
|
+
*/
|
|
20244
|
+
#onLayoutChange = () => {
|
|
20245
|
+
if (this.#readyToPlace()) {
|
|
20246
|
+
this.#placement.settleLauncherPosition();
|
|
20247
|
+
}
|
|
20248
|
+
};
|
|
20249
|
+
/** Bring the viewport's readings up to date, and say whether placing may follow. */
|
|
20250
|
+
#readyToPlace() {
|
|
19850
20251
|
this.#placement.publishVisualViewport();
|
|
19851
20252
|
this.#syncDock();
|
|
19852
|
-
|
|
19853
|
-
|
|
20253
|
+
return !this.#placement.dragging() && this.#hasBox();
|
|
20254
|
+
}
|
|
20255
|
+
/**
|
|
20256
|
+
* Whether this element generates a box at all. Hidden on itself or on any ancestor
|
|
20257
|
+
* it does not; a host collapsed to its launcher does, whatever its size, and that
|
|
20258
|
+
* launcher still has to be placed, which is why this is not a test of the size.
|
|
20259
|
+
*/
|
|
20260
|
+
#hasBox() {
|
|
20261
|
+
return this.getClientRects().length > 0;
|
|
20262
|
+
}
|
|
20263
|
+
/** Restore for a moment that is not a viewport's, which showing this reports anyway. */
|
|
20264
|
+
#restoreIfBoxed() {
|
|
20265
|
+
if (this.#hasBox()) {
|
|
20266
|
+
this.#placement.restoreLauncherPosition();
|
|
19854
20267
|
}
|
|
19855
|
-
|
|
19856
|
-
};
|
|
20268
|
+
}
|
|
19857
20269
|
/** Mic button mount point (input row); the control mounts on connect when enabled. */
|
|
19858
20270
|
#voiceSlot;
|
|
19859
20271
|
/** The composer's mic, mounted on connect when transcription is available. */
|
|
@@ -20624,7 +21036,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
20624
21036
|
this.#chatRegistration.join();
|
|
20625
21037
|
this.#placement.restoreSize();
|
|
20626
21038
|
requestAnimationFrame(() => {
|
|
20627
|
-
this.#
|
|
21039
|
+
this.#restoreIfBoxed();
|
|
20628
21040
|
this.#placement.syncResizeAnchor();
|
|
20629
21041
|
});
|
|
20630
21042
|
this.#strings = mergeUiStrings({ ...this.#readStringOverrides(), ...this.strings });
|
|
@@ -20646,6 +21058,12 @@ var AgUiChat = class extends HTMLElement {
|
|
|
20646
21058
|
const dockObserver = new ResizeObserver(() => this.#syncDock());
|
|
20647
21059
|
dockObserver.observe(this.#dockBar);
|
|
20648
21060
|
this.#connection.signal.addEventListener("abort", () => dockObserver.disconnect());
|
|
21061
|
+
const layoutObserver = new ResizeObserver(this.#onLayoutChange);
|
|
21062
|
+
layoutObserver.observe(this.#messages);
|
|
21063
|
+
this.#connection.signal.addEventListener("abort", () => layoutObserver.disconnect());
|
|
21064
|
+
this.#placement.probe.addEventListener("transitionrun", this.#onLayoutChange, {
|
|
21065
|
+
signal: this.#connection.signal
|
|
21066
|
+
});
|
|
20649
21067
|
this.#syncDock();
|
|
20650
21068
|
if (this.#surface().collapseTo === "none") {
|
|
20651
21069
|
this.dispatchEvent(
|
|
@@ -20868,7 +21286,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
20868
21286
|
this.#announceSurfaceChange(this.#strings.chatMinimised, null);
|
|
20869
21287
|
}
|
|
20870
21288
|
if (!collapsed) {
|
|
20871
|
-
this.#
|
|
21289
|
+
this.#restoreIfBoxed();
|
|
20872
21290
|
}
|
|
20873
21291
|
if (collapsed) {
|
|
20874
21292
|
this.setAttribute("collapsed", "");
|
|
@@ -20964,7 +21382,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
20964
21382
|
if (!this.isConnected) {
|
|
20965
21383
|
return;
|
|
20966
21384
|
}
|
|
20967
|
-
const height = this.#placement.syncDock(this.#dockBar, this.#resolveAnchor());
|
|
21385
|
+
const height = this.#hasBox() ? this.#placement.syncDock(this.#dockBar, this.#resolveAnchor()) : 0;
|
|
20968
21386
|
if (height === this.#barHeight) {
|
|
20969
21387
|
return;
|
|
20970
21388
|
}
|
|
@@ -21578,6 +21996,8 @@ var AgUiChat = class extends HTMLElement {
|
|
|
21578
21996
|
const probe = this.#placement.probe;
|
|
21579
21997
|
probe.className = "viewport-probe";
|
|
21580
21998
|
probe.setAttribute("aria-hidden", "true");
|
|
21999
|
+
probe.style.borderRadius = "var(--_viewport-inset-top) var(--_viewport-inset-right) var(--_viewport-inset-bottom) var(--_viewport-inset-left)";
|
|
22000
|
+
probe.style.transition = "border-radius 1ms";
|
|
21581
22001
|
this.#root.replaceChildren(probe, this.#announcer.region, this.#chat, this.#dockBar);
|
|
21582
22002
|
}
|
|
21583
22003
|
/**
|
|
@@ -22225,7 +22645,7 @@ function setControlValue(el2, value) {
|
|
|
22225
22645
|
}
|
|
22226
22646
|
|
|
22227
22647
|
// src/version.ts
|
|
22228
|
-
var VERSION = "0.
|
|
22648
|
+
var VERSION = "0.50.1";
|
|
22229
22649
|
export {
|
|
22230
22650
|
ATTACHMENT_EVENT,
|
|
22231
22651
|
AgUiChat,
|