@artooi/ag-ui-web-component 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -1
- package/README.md +70 -7
- package/dist/ag-ui-web-component.bundle.js +157 -106
- package/dist/ag-ui-web-component.bundle.js.map +3 -3
- package/dist/core/ag_ui_chat.d.ts +8 -2
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/index.js +264 -23
- package/dist/index.js.map +3 -3
- package/dist/tools/chat_surface_tools.d.ts.map +1 -1
- package/dist/ui/placement/dock_styles.d.ts.map +1 -1
- package/dist/ui/placement/panel_placement.d.ts +5 -0
- package/dist/ui/placement/panel_placement.d.ts.map +1 -1
- package/dist/ui/placement/surface_for.d.ts +15 -6
- package/dist/ui/placement/surface_for.d.ts.map +1 -1
- package/dist/ui/progress/tool_group_styles.d.ts.map +1 -1
- package/dist/ui/shell/token_styles.d.ts.map +1 -1
- package/dist/ui/themes/quiet_styles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/ag_ui_chat.ts +10 -3
- package/src/tools/chat_surface_tools.ts +3 -5
- package/src/ui/placement/dock_styles.ts +5 -1
- package/src/ui/placement/panel_placement.ts +232 -16
- package/src/ui/placement/surface_for.ts +33 -8
- package/src/ui/progress/tool_group_styles.ts +3 -2
- package/src/ui/shell/token_styles.ts +30 -0
- package/src/ui/themes/quiet_styles.ts +19 -3
- package/src/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat_surface_tools.d.ts","sourceRoot":"","sources":["../../src/tools/chat_surface_tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,kFAAkF;AAClF,eAAO,MAAM,YAAY,YAAI,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,CAAU,CAAC;AAE9F,yCAAyC;AACzC,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,GAAG,EAAE;QACZ,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;IACF;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,eAAe,IAAI,iBAAiB,CAAC;IACrC;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;IAC/E,YAAY,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACnF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"chat_surface_tools.d.ts","sourceRoot":"","sources":["../../src/tools/chat_surface_tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,kFAAkF;AAClF,eAAO,MAAM,YAAY,YAAI,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,CAAU,CAAC;AAE9F,yCAAyC;AACzC,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,GAAG,EAAE;QACZ,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;IACF;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,eAAe,IAAI,iBAAiB,CAAC;IACrC;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;IAC/E,YAAY,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACnF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,EAAE,CA4GzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dock_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/dock_styles.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"dock_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/dock_styles.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,EAAE,MA0FzB,CAAC"}
|
|
@@ -62,6 +62,11 @@ export declare class PanelPlacement {
|
|
|
62
62
|
* Drop the dragged size and its stored copy, so the placement's own size and
|
|
63
63
|
* the host's tokens apply again. For a host entering a sized state of its
|
|
64
64
|
* own, which a user's drag would otherwise outrank.
|
|
65
|
+
*
|
|
66
|
+
* A docked panel the user dragged off its dock goes back to it as well, and
|
|
67
|
+
* the stored spot with it: there the position is part of the same resting
|
|
68
|
+
* state the host is asking for. A corner panel keeps a dragged position, as
|
|
69
|
+
* it always has.
|
|
65
70
|
*/
|
|
66
71
|
resetSize(): void;
|
|
67
72
|
/**
|
|
@@ -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;
|
|
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;AAgHlD;;;;;;;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,CAOzC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAS5C;IAED,mFAAmF;IACnF,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAqBxC;IAED,sDAAsD;IACtD,WAAW,IAAI,IAAI,CAElB;IAED;;;;;;;;;OASG;IACH,SAAS,IAAI,IAAI,CAchB;IAED;;;;OAIG;IACH,QAAQ,CAAC,KAAK,iBAAiC;IA2F/C;;;;;;OAMG;IACH,QAAQ,IAAI,OAAO,CAKlB;IAED,+EAA+E;IAC/E,eAAe,IAAI,iBAAiB,CA+BnC;IAED,oEAAoE;IACpE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAmDjF;IAmID,sEAAsE;IACtE,gBAAgB,IAAI,IAAI,CAYvB;IA0CD;;;;;;OAMG;IACH,gBAAgB,IAAI,IAAI,CAQvB;IAiBD;;;;;;;;;OASG;IACH,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,CA8BzD;IA4LD;;;;;;;;;;;;OAYG;IACH,qBAAqB,IAAI,IAAI,CAgD5B;IAyQD;;;;;OAKG;IACH,uBAAuB,IAAI,IAAI,CA0B9B;IAiCD;;;;OAIG;IACH,uBAAuB,IAAI,IAAI,CAiB9B;CAgIF"}
|
|
@@ -37,10 +37,18 @@ export interface Surface {
|
|
|
37
37
|
readonly collapseTo: CollapseTarget;
|
|
38
38
|
/**
|
|
39
39
|
* Whether a gesture may move the launcher. The rest have nowhere to put it:
|
|
40
|
-
* the rail is a docked edge, `header` and `null` have no launcher,
|
|
41
|
-
* full-bleed panel covers the screen it would be opening into
|
|
40
|
+
* the rail is a docked edge, `header` and `null` have no launcher, a
|
|
41
|
+
* full-bleed panel covers the screen it would be opening into, and the bar
|
|
42
|
+
* and the host's own control are not a launcher at all.
|
|
42
43
|
*/
|
|
43
44
|
readonly launcherDraggable: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the header drag and `moveTo` may move the open panel. The same
|
|
47
|
+
* answer as {@link launcherDraggable} for every placement on its own; the two
|
|
48
|
+
* part under `collapse-to="bar"` and `"none"`, where a corner panel still
|
|
49
|
+
* moves like one but has no launcher left to drag.
|
|
50
|
+
*/
|
|
51
|
+
readonly panelMovable: boolean;
|
|
44
52
|
/**
|
|
45
53
|
* Which axes the resize grips may change. A full-bleed layout is the whole
|
|
46
54
|
* viewport by definition and cannot be resized; a docked panel owns its
|
|
@@ -69,10 +77,11 @@ export interface Surface {
|
|
|
69
77
|
* state, never a placement's own shape: `bar` applies where the placement
|
|
70
78
|
* collapses to `launcher`, so the sidebar keeps its rail and an embedded chat
|
|
71
79
|
* its header bar, and `none` applies to every collapsible placement. Absent,
|
|
72
|
-
* `auto` and any other value leave the placement as it was.
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* the
|
|
80
|
+
* `auto` and any other value leave the placement as it was. Both turn the
|
|
81
|
+
* launcher drag off, because there is no launcher left to drag, and keep the
|
|
82
|
+
* placement's own answer for moving the panel: a corner panel rises from the
|
|
83
|
+
* bar or the anchor until the user drags it somewhere else, and a placement
|
|
84
|
+
* that owns its position keeps owning it.
|
|
76
85
|
*/
|
|
77
86
|
export declare function surfaceFor(placement: string | null, collapseTo?: string | null): Surface;
|
|
78
87
|
//# sourceMappingURL=surface_for.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface_for.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/surface_for.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;AAEpF;;;;;;;GAOG;AACH,MAAM,WAAW,OAAO;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC
|
|
1
|
+
{"version":3,"file":"surface_for.d.ts","sourceRoot":"","sources":["../../../src/ui/placement/surface_for.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;AAEpF;;;;;;;GAOG;AACH,MAAM,WAAW,OAAO;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AA2ED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAS9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool_group_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/progress/tool_group_styles.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"tool_group_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/progress/tool_group_styles.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,iBAAiB,EAAE,MAsP/B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/shell/token_styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"token_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/shell/token_styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,EAAE,MA4c1B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quiet_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/themes/quiet_styles.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"quiet_styles.d.ts","sourceRoot":"","sources":["../../../src/ui/themes/quiet_styles.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,EAAE,MA8H1B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@artooi/ag-ui-web-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
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
|
@@ -1128,7 +1128,8 @@ export class AgUiChat extends HTMLElement {
|
|
|
1128
1128
|
return;
|
|
1129
1129
|
}
|
|
1130
1130
|
// collapse-to changes the surface the way placement does -- a docked panel
|
|
1131
|
-
//
|
|
1131
|
+
// has no launcher to drag, and keeps its own dragged spot apart from the
|
|
1132
|
+
// corner's, so the same handover applies -- and is live the same way.
|
|
1132
1133
|
if (name === "placement" || name === "collapse-to") {
|
|
1133
1134
|
// A placement owns the axes it fixes, so hand those back before anything
|
|
1134
1135
|
// else: a size dragged under the previous placement would otherwise sit
|
|
@@ -2076,7 +2077,9 @@ export class AgUiChat extends HTMLElement {
|
|
|
2076
2077
|
* `movable` folds two separate reasons into the one answer a caller needs:
|
|
2077
2078
|
* a placement that places itself owns its position, and a panel that fills
|
|
2078
2079
|
* the screen has nowhere to go. Reporting them apart would make every caller
|
|
2079
|
-
* re-derive the same conjunction.
|
|
2080
|
+
* re-derive the same conjunction. A corner panel docked under
|
|
2081
|
+
* `collapse-to="bar"` or `"none"` is movable on a wide viewport, like the
|
|
2082
|
+
* corner panel it is.
|
|
2080
2083
|
*/
|
|
2081
2084
|
describeSurface(): ChatSurfaceReport {
|
|
2082
2085
|
return this.#placement.describeSurface();
|
|
@@ -2091,7 +2094,8 @@ export class AgUiChat extends HTMLElement {
|
|
|
2091
2094
|
* the corner it opens from is re-picked, and switching placement hands
|
|
2092
2095
|
* everything back. What it must not do is claim a move it did not make --
|
|
2093
2096
|
* hence the boolean, and hence {@link describeSurface} existing so a caller
|
|
2094
|
-
* can ask first.
|
|
2097
|
+
* can ask first. A docked panel moves too, within the room above its bar or
|
|
2098
|
+
* anchor, and remembers the spot as a header drag would.
|
|
2095
2099
|
*/
|
|
2096
2100
|
moveTo(corner: ChatCorner, options: { readonly announce?: boolean } = {}): boolean {
|
|
2097
2101
|
return this.#placement.moveTo(corner, options);
|
|
@@ -2100,6 +2104,9 @@ export class AgUiChat extends HTMLElement {
|
|
|
2100
2104
|
/**
|
|
2101
2105
|
* Drop a size the user dragged, and its stored copy, so the placement's
|
|
2102
2106
|
* default and the host's `--ag-ui-width` / `--ag-ui-height` apply again.
|
|
2107
|
+
* Under `collapse-to="bar"` or `"none"` it also returns a panel the user
|
|
2108
|
+
* dragged off its dock to the dock, and forgets the stored spot; a corner
|
|
2109
|
+
* panel keeps a dragged position.
|
|
2103
2110
|
*/
|
|
2104
2111
|
resetSize(): void {
|
|
2105
2112
|
this.#placement.resetSize();
|
|
@@ -161,11 +161,9 @@ export function createChatSurfaceTools(surface: ChatSurface): ClientTool[] {
|
|
|
161
161
|
moved: false,
|
|
162
162
|
reason: report.fullBleed
|
|
163
163
|
? "the panel fills the screen, so there is nowhere to move it to"
|
|
164
|
-
: report.
|
|
165
|
-
? "
|
|
166
|
-
: report.
|
|
167
|
-
? "this page has turned off moving the panel"
|
|
168
|
-
: `the "${report.placement ?? ""}" placement owns the panel's position`,
|
|
164
|
+
: report.draggable === false
|
|
165
|
+
? "this page has turned off moving the panel"
|
|
166
|
+
: `the "${report.placement ?? ""}" placement owns the panel's position`,
|
|
169
167
|
suggestion: report.collapsible ? "minimise_chat" : null,
|
|
170
168
|
};
|
|
171
169
|
}
|
|
@@ -16,7 +16,11 @@ export const DOCK_STYLES: string = `
|
|
|
16
16
|
The cap follows the token block's rule: what the host left free less the
|
|
17
17
|
one gutter the anchored edge spends, so a panel grown to the cap runs from
|
|
18
18
|
just above the bar to the far edge. A host therefore needs no inset to keep
|
|
19
|
-
the panel off its bar; --ag-ui-inset still wins when set.
|
|
19
|
+
the panel off its bar; --ag-ui-inset still wins when set. A panel the user
|
|
20
|
+
dragged off its dock is written there inline, which is how the dragged spot
|
|
21
|
+
outranks every rule below, the anchor gap included; the element holds that
|
|
22
|
+
spot above the bar or the anchor itself, and drops it on a phone, where the
|
|
23
|
+
small-viewport block reads the same property first.
|
|
20
24
|
|
|
21
25
|
These blocks restate tokens and nothing else, and sit ahead of the phone
|
|
22
26
|
block at no more weight than it has, so on a phone the panel is the screen
|
|
@@ -50,6 +50,15 @@ const DOCK_RIGHT = "--_dock-right";
|
|
|
50
50
|
/** Per-tab persistence key for a dragged launcher position. */
|
|
51
51
|
const LAUNCHER_KEY = "ag-ui-chat:launcher";
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Persistence key for where the user dragged a docked panel. Its own key, not
|
|
55
|
+
* the launcher's: a spot stored under the corner launcher describes another
|
|
56
|
+
* surface, and reading it here would open every panel away from the bar for a
|
|
57
|
+
* host that switched to the bar after its users had dragged their corner
|
|
58
|
+
* panels.
|
|
59
|
+
*/
|
|
60
|
+
const DOCK_KEY = "ag-ui-chat:dock";
|
|
61
|
+
|
|
53
62
|
/** A stored `{ left, top }` pair, or null for anything that is not one. */
|
|
54
63
|
function asPoint(value: unknown): { readonly left: number; readonly top: number } | null {
|
|
55
64
|
if (typeof value !== "object" || value === null) {
|
|
@@ -157,7 +166,7 @@ export class PanelPlacement {
|
|
|
157
166
|
*/
|
|
158
167
|
enablePanelDrag(header: HTMLElement): void {
|
|
159
168
|
enablePanelDrag(header, {
|
|
160
|
-
enabled: () => !this.#host.collapsed() && this.#
|
|
169
|
+
enabled: () => !this.#host.collapsed() && this.#canDragPanel(),
|
|
161
170
|
rect: () => this.#host.element.getBoundingClientRect(),
|
|
162
171
|
apply: (box, from) => this.#movePanel(box, from),
|
|
163
172
|
commit: (box, from) => this.#commitPanel(box, from),
|
|
@@ -213,11 +222,23 @@ export class PanelPlacement {
|
|
|
213
222
|
* Drop the dragged size and its stored copy, so the placement's own size and
|
|
214
223
|
* the host's tokens apply again. For a host entering a sized state of its
|
|
215
224
|
* own, which a user's drag would otherwise outrank.
|
|
225
|
+
*
|
|
226
|
+
* A docked panel the user dragged off its dock goes back to it as well, and
|
|
227
|
+
* the stored spot with it: there the position is part of the same resting
|
|
228
|
+
* state the host is asking for. A corner panel keeps a dragged position, as
|
|
229
|
+
* it always has.
|
|
216
230
|
*/
|
|
217
231
|
resetSize(): void {
|
|
218
232
|
this.#host.element.style.removeProperty(DRAG_WIDTH);
|
|
219
233
|
this.#host.element.style.removeProperty(DRAG_HEIGHT);
|
|
220
234
|
this.#host.clearPreference(SIZE_KEY);
|
|
235
|
+
if (this.#docked()) {
|
|
236
|
+
if (this.#dockPos !== null) {
|
|
237
|
+
this.#host.element.style.removeProperty("--ag-ui-inset");
|
|
238
|
+
}
|
|
239
|
+
this.#dockPos = null;
|
|
240
|
+
this.#storeDockPosition(null);
|
|
241
|
+
}
|
|
221
242
|
// The anchor was measured against the dragged box; the resting one may
|
|
222
243
|
// pin different edges.
|
|
223
244
|
this.syncResizeAnchor();
|
|
@@ -250,6 +271,25 @@ export class PanelPlacement {
|
|
|
250
271
|
*/
|
|
251
272
|
#panelPos: { readonly left: number; readonly top: number } | null = null;
|
|
252
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Where the user dragged a docked panel, in viewport coordinates, or null
|
|
276
|
+
* while it stands on the bar or the anchor.
|
|
277
|
+
*
|
|
278
|
+
* Apart from the two corner positions above because it moves nothing else:
|
|
279
|
+
* a docked panel has no launcher to carry, and the corner it opens from is
|
|
280
|
+
* the dock's. Held as the user stated it and never as clamped, so a bar that
|
|
281
|
+
* grew, or a phone that took the panel full-bleed, gives the spot back
|
|
282
|
+
* unchanged once there is room for it again.
|
|
283
|
+
*/
|
|
284
|
+
#dockPos: { readonly left: number; readonly top: number } | null = null;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* The line a docked panel stays above, in viewport coordinates: the bar's
|
|
288
|
+
* top, or the anchor's. Infinite where neither is drawn. Measured by
|
|
289
|
+
* syncDock, which runs on every change either can make.
|
|
290
|
+
*/
|
|
291
|
+
#dockTop = Number.POSITIVE_INFINITY;
|
|
292
|
+
|
|
253
293
|
/**
|
|
254
294
|
* The corner the panel opens away from, once this element is placing itself.
|
|
255
295
|
* Null means the host's layout still decides, and the anchor is measured.
|
|
@@ -281,7 +321,10 @@ export class PanelPlacement {
|
|
|
281
321
|
* rather than collapsed to nothing.
|
|
282
322
|
*/
|
|
283
323
|
#withinViewport(box: PanelRect): PanelRect {
|
|
284
|
-
|
|
324
|
+
// A docked panel that moves has its room stop at the dock, so no grip
|
|
325
|
+
// pulls it over the bar it minimises to. A placement that owns its
|
|
326
|
+
// position keeps the bound it always had.
|
|
327
|
+
const viewport = this.#docked() && this.#panelMovable() ? this.#dockRoom() : this.#viewport();
|
|
285
328
|
// The same bound a drag stops at, so a grip pulled to the edge and a panel
|
|
286
329
|
// dragged to it come to rest on the same line. The inner Math.max/min pair
|
|
287
330
|
// keeps an already-inverted box from turning inside out.
|
|
@@ -321,8 +364,14 @@ export class PanelPlacement {
|
|
|
321
364
|
collapseTo: this.#surface().collapseTo,
|
|
322
365
|
collapsed: this.#host.collapsed(),
|
|
323
366
|
collapsible: this.#host.collapsible(),
|
|
324
|
-
movable: this.#
|
|
325
|
-
|
|
367
|
+
movable: this.#panelMovable() && !fullBleed,
|
|
368
|
+
// Whether the page left moving on, which is not the same question as
|
|
369
|
+
// whether this panel moves: a docked sidebar's position is owned by its
|
|
370
|
+
// placement, and reporting that as the host having turned moving off is
|
|
371
|
+
// the sentence the move refusal would then get wrong.
|
|
372
|
+
draggable:
|
|
373
|
+
this.#host.element.getAttribute("data-launcher-drag") !== "false" &&
|
|
374
|
+
(this.#surface().launcherDraggable || this.#docked()),
|
|
326
375
|
fullBleed,
|
|
327
376
|
box: {
|
|
328
377
|
left: Math.round(box.left),
|
|
@@ -341,7 +390,7 @@ export class PanelPlacement {
|
|
|
341
390
|
|
|
342
391
|
/** The move behind `AgUiChat.moveTo`, whose doc is the contract. */
|
|
343
392
|
moveTo(corner: ChatCorner, options: { readonly announce?: boolean } = {}): boolean {
|
|
344
|
-
if (!this.#
|
|
393
|
+
if (!this.#panelMovable()) {
|
|
345
394
|
return false;
|
|
346
395
|
}
|
|
347
396
|
const restore = options.announce === true ? this.#captureGeometry() : null;
|
|
@@ -351,23 +400,31 @@ export class PanelPlacement {
|
|
|
351
400
|
return false;
|
|
352
401
|
}
|
|
353
402
|
const [edgeY, edgeX] = corner.split("-");
|
|
403
|
+
// A docked panel's bottom corners are above its dock, not on the bar.
|
|
404
|
+
const docked = this.#docked();
|
|
405
|
+
const room = docked ? this.#dockRoom() : viewport;
|
|
354
406
|
// Every term is an absolute screen coordinate, because that is what the
|
|
355
407
|
// clamps and the insets both speak. The usable box carries an origin, so
|
|
356
408
|
// its near edge is `viewport.left`, not zero, and its far edge is
|
|
357
409
|
// `viewport.left + viewport.width` -- a margin applied to the extents
|
|
358
410
|
// alone would send the agent's own move under the chrome the host
|
|
359
411
|
// reserved, which is the failure the usable box exists to prevent.
|
|
360
|
-
const nearX =
|
|
361
|
-
const nearY =
|
|
412
|
+
const nearX = room.left + EDGE_MARGIN;
|
|
413
|
+
const nearY = room.top + EDGE_MARGIN;
|
|
362
414
|
const left =
|
|
363
|
-
edgeX === "left"
|
|
364
|
-
? nearX
|
|
365
|
-
: Math.max(nearX, viewport.left + viewport.width - EDGE_MARGIN - box.width);
|
|
415
|
+
edgeX === "left" ? nearX : Math.max(nearX, room.left + room.width - EDGE_MARGIN - box.width);
|
|
366
416
|
const top =
|
|
367
|
-
edgeY === "top"
|
|
368
|
-
? nearY
|
|
369
|
-
: Math.max(nearY, viewport.top + viewport.height - EDGE_MARGIN - box.height);
|
|
417
|
+
edgeY === "top" ? nearY : Math.max(nearY, room.top + room.height - EDGE_MARGIN - box.height);
|
|
370
418
|
const host = { left, top, right: left + box.width, bottom: top + box.height };
|
|
419
|
+
if (docked) {
|
|
420
|
+
this.#dockPos = { left, top };
|
|
421
|
+
this.#writeDockInset(host);
|
|
422
|
+
this.#storeDockPosition(this.#dockPos);
|
|
423
|
+
if (restore !== null) {
|
|
424
|
+
this.#host.announceSurfaceChange(this.#host.strings().chatMoved, restore);
|
|
425
|
+
}
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
371
428
|
// Both axes measured, rather than one read twice: a host can restyle the
|
|
372
429
|
// launcher as a pill, and squaring it here would put it off the corner.
|
|
373
430
|
const launcherWidth = this.#host.launcher.offsetWidth;
|
|
@@ -401,6 +458,7 @@ export class PanelPlacement {
|
|
|
401
458
|
const corner = this.#host.element.getAttribute("data-expand-corner");
|
|
402
459
|
const launcherPos = this.#launcherPos;
|
|
403
460
|
const panelPos = this.#panelPos;
|
|
461
|
+
const dockPos = this.#dockPos;
|
|
404
462
|
const expandCorner = this.#expandCorner;
|
|
405
463
|
return () => {
|
|
406
464
|
this.#restoreProperty("--ag-ui-inset", inset);
|
|
@@ -412,7 +470,15 @@ export class PanelPlacement {
|
|
|
412
470
|
}
|
|
413
471
|
this.#launcherPos = launcherPos;
|
|
414
472
|
this.#panelPos = panelPos;
|
|
473
|
+
this.#dockPos = dockPos;
|
|
415
474
|
this.#expandCorner = expandCorner;
|
|
475
|
+
if (this.#docked()) {
|
|
476
|
+
// The dock's own record, and only that: the launcher's belongs to the
|
|
477
|
+
// corner surface and has nothing to undo here.
|
|
478
|
+
this.#storeDockPosition(dockPos);
|
|
479
|
+
this.syncResizeAnchor();
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
416
482
|
// Erased rather than rewritten when there was nothing to go back to.
|
|
417
483
|
// #storeLauncherPosition returns early for a null position, which would
|
|
418
484
|
// leave the move this is undoing sitting in storage -- and since that
|
|
@@ -583,7 +649,10 @@ export class PanelPlacement {
|
|
|
583
649
|
return surfaceFor(element.getAttribute("placement"), element.getAttribute("collapse-to"));
|
|
584
650
|
}
|
|
585
651
|
|
|
586
|
-
/**
|
|
652
|
+
/**
|
|
653
|
+
* Whether the panel rises from the bottom bar or the host's anchor, until the
|
|
654
|
+
* user drags it somewhere else.
|
|
655
|
+
*/
|
|
587
656
|
#docked(): boolean {
|
|
588
657
|
const target = this.#surface().collapseTo;
|
|
589
658
|
return target === "bar" || target === "none";
|
|
@@ -617,9 +686,122 @@ export class PanelPlacement {
|
|
|
617
686
|
element.style.removeProperty(DOCK_RIGHT);
|
|
618
687
|
}
|
|
619
688
|
element.toggleAttribute("data-dock-anchored", target === "none" && anchor !== null);
|
|
689
|
+
this.#dockTop =
|
|
690
|
+
target === "bar" && height > 0
|
|
691
|
+
? bar.getBoundingClientRect().top
|
|
692
|
+
: target === "none" && anchor !== null
|
|
693
|
+
? anchor.getBoundingClientRect().top
|
|
694
|
+
: Number.POSITIVE_INFINITY;
|
|
695
|
+
// Every change the dock can make passes through here -- the bar's height
|
|
696
|
+
// arrives by a ResizeObserver after the first restore, and a bar that grew
|
|
697
|
+
// since the drag would otherwise cover the panel's foot -- so the dragged
|
|
698
|
+
// spot is held against it here rather than only when it is restored.
|
|
699
|
+
this.#placeDocked();
|
|
620
700
|
return height;
|
|
621
701
|
}
|
|
622
702
|
|
|
703
|
+
/**
|
|
704
|
+
* The part of the usable viewport a docked panel may stand in: all of it
|
|
705
|
+
* above the dock's line. The bar is what it minimises to, and the anchor is
|
|
706
|
+
* the host's own way back, so a dragged panel never covers either.
|
|
707
|
+
*/
|
|
708
|
+
#dockRoom(): ViewportBox {
|
|
709
|
+
const viewport = this.#viewport();
|
|
710
|
+
const bottom = Math.min(viewport.top + viewport.height, this.#dockTop);
|
|
711
|
+
return { ...viewport, height: Math.max(0, bottom - viewport.top) };
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Put a docked panel back where the user dragged it, held inside its room.
|
|
716
|
+
*
|
|
717
|
+
* The inline inset comes off first, which is both how the panel's resting
|
|
718
|
+
* box is measured and what leaves a phone full-bleed: the small-viewport
|
|
719
|
+
* block reads the host's inset before its own, so a dragged spot written
|
|
720
|
+
* there would hold the panel off the screen's edges. The spot itself stays,
|
|
721
|
+
* for the next time there is room for it.
|
|
722
|
+
*/
|
|
723
|
+
#placeDocked(): void {
|
|
724
|
+
if (!this.#docked() || !this.#panelMovable() || this.dragging()) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const at = this.#dockPos ?? this.#readDockPosition();
|
|
728
|
+
if (at === null) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
this.#dockPos = at;
|
|
732
|
+
this.#host.element.style.removeProperty("--ag-ui-inset");
|
|
733
|
+
const rest = this.#host.element.getBoundingClientRect();
|
|
734
|
+
if (coversViewport(rest, this.#viewport())) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
// Measured at the dock, so the inset below is written from the corner the
|
|
738
|
+
// dock holds still and a resize grows the panel away from it.
|
|
739
|
+
this.syncResizeAnchor();
|
|
740
|
+
this.#writeDockInset(
|
|
741
|
+
clampPanel(
|
|
742
|
+
{ left: at.left, top: at.top, right: at.left + rest.width, bottom: at.top + rest.height },
|
|
743
|
+
this.#dockRoom(),
|
|
744
|
+
SCREEN_EDGE_MARGIN,
|
|
745
|
+
),
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/** Write the inset that stands a docked panel at `box`. */
|
|
750
|
+
#writeDockInset(box: PanelRect): void {
|
|
751
|
+
this.#host.element.style.setProperty(
|
|
752
|
+
"--ag-ui-inset",
|
|
753
|
+
insetFrom(this.#anchor, box, this.#screen()),
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/** Remember a docked panel's dragged spot, or forget it for null. */
|
|
758
|
+
#storeDockPosition(at: { readonly left: number; readonly top: number } | null): void {
|
|
759
|
+
if (at === null) {
|
|
760
|
+
this.#host.clearPreference(DOCK_KEY);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
this.#host.writePreference(DOCK_KEY, JSON.stringify({ left: at.left, top: at.top }));
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/** The stored spot of a docked panel, or null for none or anything corrupt. */
|
|
767
|
+
#readDockPosition(): { readonly left: number; readonly top: number } | null {
|
|
768
|
+
const raw = this.#host.readPreference(DOCK_KEY);
|
|
769
|
+
if (raw === null) {
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
try {
|
|
773
|
+
return asPoint(JSON.parse(raw));
|
|
774
|
+
} catch {
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Whether the header drag and moveTo may move the open panel: the
|
|
781
|
+
* placement's answer, unless the host turned moving off.
|
|
782
|
+
*/
|
|
783
|
+
#panelMovable(): boolean {
|
|
784
|
+
return (
|
|
785
|
+
this.#host.element.getAttribute("data-launcher-drag") !== "false" &&
|
|
786
|
+
this.#surface().panelMovable
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Whether a header drag may start now. A docked panel on a phone is the
|
|
792
|
+
* screen, and a drag there would pull it off the edges it fills; a corner
|
|
793
|
+
* panel's header is left as it was.
|
|
794
|
+
*/
|
|
795
|
+
#canDragPanel(): boolean {
|
|
796
|
+
if (!this.#panelMovable()) {
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
return (
|
|
800
|
+
!this.#docked() ||
|
|
801
|
+
!coversViewport(this.#host.element.getBoundingClientRect(), this.#viewport())
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
|
|
623
805
|
/**
|
|
624
806
|
* Whether this placement lets the launcher be dragged, read per interaction
|
|
625
807
|
* because `placement` is a live attribute. `data-launcher-drag="false"` opts
|
|
@@ -863,6 +1045,13 @@ export class PanelPlacement {
|
|
|
863
1045
|
* other still names the old one would move the launcher for no reason.
|
|
864
1046
|
*/
|
|
865
1047
|
#movePanel(box: PanelRect, from: PanelRect): { held: PanelRect; launcher: LauncherBox | null } {
|
|
1048
|
+
if (this.#docked()) {
|
|
1049
|
+
// Inside the room above the dock, with nothing carried along.
|
|
1050
|
+
const held = clampPanel(box, this.#dockRoom(), SCREEN_EDGE_MARGIN);
|
|
1051
|
+
this.#dockPos = { left: held.left, top: held.top };
|
|
1052
|
+
this.#writeDockInset(held);
|
|
1053
|
+
return { held, launcher: null };
|
|
1054
|
+
}
|
|
866
1055
|
if (!this.#launcherDraggable()) {
|
|
867
1056
|
return { held: box, launcher: null };
|
|
868
1057
|
}
|
|
@@ -936,6 +1125,10 @@ export class PanelPlacement {
|
|
|
936
1125
|
// again. Recomputing it is how the two came apart: releasing the drag
|
|
937
1126
|
// moved the bubble by the panel's whole travel a second time.
|
|
938
1127
|
const { held, launcher } = this.#movePanel(box, from);
|
|
1128
|
+
if (this.#docked()) {
|
|
1129
|
+
this.#storeDockPosition({ left: held.left, top: held.top });
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
939
1132
|
if (launcher === null) {
|
|
940
1133
|
return;
|
|
941
1134
|
}
|
|
@@ -1026,6 +1219,13 @@ export class PanelPlacement {
|
|
|
1026
1219
|
* unreachable -- it is the only way back to a collapsed conversation.
|
|
1027
1220
|
*/
|
|
1028
1221
|
restoreLauncherPosition(): void {
|
|
1222
|
+
// Before the launcher's record is read at all: it belongs to the corner
|
|
1223
|
+
// surface, and seeding anything from it would put a spot dragged there on
|
|
1224
|
+
// a panel that should stand on its dock.
|
|
1225
|
+
if (this.#docked()) {
|
|
1226
|
+
this.#placeDocked();
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1029
1229
|
const stored = this.#readLauncherPosition();
|
|
1030
1230
|
const launcher = this.#launcherPos ?? stored;
|
|
1031
1231
|
if (launcher === null) {
|
|
@@ -1083,6 +1283,13 @@ export class PanelPlacement {
|
|
|
1083
1283
|
* the switch inline and pins a sidebar to wherever the floating launcher was.
|
|
1084
1284
|
*/
|
|
1085
1285
|
releaseLauncherPosition(): void {
|
|
1286
|
+
// A dragged dock spot belongs to a docked panel that moves. Any other
|
|
1287
|
+
// surface hands it back; a docked one keeps it, and syncDock holds it
|
|
1288
|
+
// against the new dock.
|
|
1289
|
+
if (this.#dockPos !== null && !(this.#docked() && this.#panelMovable())) {
|
|
1290
|
+
this.#dockPos = null;
|
|
1291
|
+
this.#host.element.style.removeProperty("--ag-ui-inset");
|
|
1292
|
+
}
|
|
1086
1293
|
if (this.#launcherDraggable()) {
|
|
1087
1294
|
return;
|
|
1088
1295
|
}
|
|
@@ -1110,11 +1317,12 @@ export class PanelPlacement {
|
|
|
1110
1317
|
*/
|
|
1111
1318
|
#applyResize(grip: ResizeGrip, box: PanelRect): PanelRect {
|
|
1112
1319
|
box = this.#withinViewport(box);
|
|
1113
|
-
if (this.#docked()) {
|
|
1114
|
-
// A
|
|
1320
|
+
if (this.#docked() && this.#dockPos === null) {
|
|
1321
|
+
// A panel standing on its dock keeps it through a resize: the edges it
|
|
1115
1322
|
// stands on are the bar's or the anchor's, and a grip on one of them
|
|
1116
1323
|
// would write the position below and take the panel off its dock. So
|
|
1117
1324
|
// the pinned edges stay where they are and only the free ones follow.
|
|
1325
|
+
// Once dragged off, it resizes like a corner panel, below.
|
|
1118
1326
|
const now = this.#host.element.getBoundingClientRect();
|
|
1119
1327
|
box = {
|
|
1120
1328
|
left: grip.x === this.#anchor.x ? now.left : box.left,
|
|
@@ -1126,6 +1334,11 @@ export class PanelPlacement {
|
|
|
1126
1334
|
return box;
|
|
1127
1335
|
}
|
|
1128
1336
|
this.#applySize({ width: box.right - box.left, height: box.bottom - box.top });
|
|
1337
|
+
if (this.#docked()) {
|
|
1338
|
+
// Any grip moves the top-left corner the spot is kept as, including one
|
|
1339
|
+
// on a free edge, which leaves the inset alone.
|
|
1340
|
+
this.#dockPos = { left: box.left, top: box.top };
|
|
1341
|
+
}
|
|
1129
1342
|
if (grip.x !== this.#anchor.x && grip.y !== this.#anchor.y) {
|
|
1130
1343
|
return box;
|
|
1131
1344
|
}
|
|
@@ -1161,6 +1374,9 @@ export class PanelPlacement {
|
|
|
1161
1374
|
const held = this.#applyResize(grip, box);
|
|
1162
1375
|
this.#persistSize({ width: held.right - held.left, height: held.bottom - held.top });
|
|
1163
1376
|
this.#storeLauncherPosition();
|
|
1377
|
+
if (this.#dockPos !== null) {
|
|
1378
|
+
this.#storeDockPosition(this.#dockPos);
|
|
1379
|
+
}
|
|
1164
1380
|
// Re-stamp after the drag: a host whose layout changed underneath us would
|
|
1165
1381
|
// otherwise keep the tab-reachable grip in the old corner, which reads as
|
|
1166
1382
|
// the control being in the wrong place even though the drag was right.
|