@mcp-b/char 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -13
- package/dist/custom-elements.json +491 -141
- package/dist/display-mode-policy.d.ts.map +1 -1
- package/dist/index.d.ts +89 -114
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +699 -281
- package/dist/index.js.map +1 -1
- package/dist/shell-component.d.ts +79 -86
- package/dist/shell-component.d.ts.map +1 -1
- package/dist/shell-component.js +689 -281
- package/dist/shell-component.js.map +1 -1
- package/dist/shell-standalone.iife.js +689 -281
- package/dist/shell-standalone.iife.js.map +1 -1
- package/dist/utils.d.ts +0 -13
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -2
- package/dist/utils.js.map +1 -1
- package/dist/web-component-standalone.iife.js +25 -89
- package/dist/web-component-standalone.iife.js.map +1 -1
- package/dist/web-component.d.ts +20 -83
- package/dist/web-component.d.ts.map +1 -1
- package/dist/web-component.js +25 -89
- package/dist/web-component.js.map +1 -1
- package/package.json +3 -3
package/dist/shell-component.js
CHANGED
|
@@ -90,8 +90,7 @@ function mergeHostContext(current, patch) {
|
|
|
90
90
|
styles: mergeStyles(previous.styles, patch.styles),
|
|
91
91
|
containerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),
|
|
92
92
|
deviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),
|
|
93
|
-
safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
|
|
94
|
-
hostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities)
|
|
93
|
+
safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
|
|
95
94
|
};
|
|
96
95
|
}
|
|
97
96
|
|
|
@@ -503,25 +502,6 @@ const DISPLAY_MODES = [
|
|
|
503
502
|
"pip"
|
|
504
503
|
];
|
|
505
504
|
/**
|
|
506
|
-
* Normalizes an API key by trimming whitespace and collapsing empty strings.
|
|
507
|
-
*/
|
|
508
|
-
function normalizeApiKey(value) {
|
|
509
|
-
const trimmed = value?.trim();
|
|
510
|
-
return trimmed ? trimmed : void 0;
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* Produces a sanitized dev-mode configuration and drops empty payloads.
|
|
514
|
-
*/
|
|
515
|
-
function resolveDevMode(devMode) {
|
|
516
|
-
if (!devMode) return void 0;
|
|
517
|
-
const normalized = {
|
|
518
|
-
anthropicApiKey: normalizeApiKey(devMode.anthropicApiKey),
|
|
519
|
-
openaiApiKey: normalizeApiKey(devMode.openaiApiKey),
|
|
520
|
-
useLocalApi: devMode.useLocalApi === true ? true : void 0
|
|
521
|
-
};
|
|
522
|
-
return !!normalized.anthropicApiKey || !!normalized.openaiApiKey || !!normalized.useLocalApi ? normalized : void 0;
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
505
|
* Runtime guard for the message envelope emitted by the iframe.
|
|
526
506
|
*/
|
|
527
507
|
function isCharIframeMessageData(value) {
|
|
@@ -660,7 +640,7 @@ function getDeviceCapabilities() {
|
|
|
660
640
|
*/
|
|
661
641
|
var CharAgentElement = class extends HTMLElement {
|
|
662
642
|
static observedAttributes = [
|
|
663
|
-
"
|
|
643
|
+
"publishable-key",
|
|
664
644
|
"enable-debug-tools",
|
|
665
645
|
"display-mode",
|
|
666
646
|
"api-base"
|
|
@@ -710,12 +690,12 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
710
690
|
return;
|
|
711
691
|
}
|
|
712
692
|
if (!this.style.display) this.style.display = "block";
|
|
713
|
-
const
|
|
714
|
-
const apiBase = this._resolveApiBaseOverride() ?? (resolvedDevMode?.useLocalApi ? window.location.origin : WEBMCP_PRODUCTION_API_BASE);
|
|
693
|
+
const apiBase = this._resolveApiBaseOverride() ?? WEBMCP_PRODUCTION_API_BASE;
|
|
715
694
|
const iframeOrigin = this._resolveIframeOrigin(apiBase);
|
|
716
695
|
const iframe = this._createIframe(apiBase);
|
|
717
696
|
this._iframe = iframe;
|
|
718
|
-
|
|
697
|
+
const publishableKey = this.publishableKey ?? this.getAttribute("publishable-key") ?? void 0;
|
|
698
|
+
if (publishableKey) this._pendingAuth = { publishableKey };
|
|
719
699
|
this._proxy = new CharIframeProxy(iframe, iframeOrigin);
|
|
720
700
|
this._proxy.start();
|
|
721
701
|
this._messageListener = this._createMessageListener(iframe, iframeOrigin);
|
|
@@ -924,15 +904,21 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
924
904
|
/**
|
|
925
905
|
* Reacts to observed attribute updates after iframe readiness.
|
|
926
906
|
* Attributes that affect iframe boot configuration are intentionally ignored
|
|
927
|
-
* after mount (`
|
|
907
|
+
* after mount (`enable-debug-tools`, `api-base`).
|
|
928
908
|
*/
|
|
929
909
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
930
910
|
if (!this._iframe || !this._iframeReady) return;
|
|
931
911
|
switch (name) {
|
|
912
|
+
case "publishable-key":
|
|
913
|
+
if (newValue) {
|
|
914
|
+
this._pendingAuth = { publishableKey: newValue };
|
|
915
|
+
const iframeOrigin = this._getIframeOrigin();
|
|
916
|
+
if (iframeOrigin) this._postAuth(iframeOrigin);
|
|
917
|
+
}
|
|
918
|
+
break;
|
|
932
919
|
case "display-mode":
|
|
933
920
|
this.setHostContext({ displayMode: resolveDisplayModeFromAttribute(newValue) });
|
|
934
921
|
break;
|
|
935
|
-
case "dev-mode":
|
|
936
922
|
case "enable-debug-tools":
|
|
937
923
|
case "api-base": break;
|
|
938
924
|
}
|
|
@@ -943,23 +929,17 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
943
929
|
* The token is stored as a JavaScript property (not as a DOM attribute),
|
|
944
930
|
* preventing exposure to DOM inspection and session replay tools.
|
|
945
931
|
*
|
|
946
|
-
* @param options - Authentication payload
|
|
932
|
+
* @param options - Authentication payload.
|
|
947
933
|
* @returns `true` when the payload is accepted; `false` when validation fails.
|
|
948
934
|
*/
|
|
949
935
|
connect(options) {
|
|
950
|
-
if (!options?.
|
|
951
|
-
this._emitCharError("
|
|
952
|
-
return false;
|
|
953
|
-
}
|
|
954
|
-
if (options?.idToken && !options?.ticketAuth && !options?.clientId) {
|
|
955
|
-
this._emitCharError("MISSING_CLIENT_ID", "connect() requires clientId when using idToken authentication");
|
|
936
|
+
if (!options?.publishableKey) {
|
|
937
|
+
this._emitCharError("MISSING_PUBLISHABLE_KEY", "connect() requires publishableKey");
|
|
956
938
|
return false;
|
|
957
939
|
}
|
|
958
940
|
this._pendingAuth = {
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
organizationId: options.ticketAuth ? void 0 : options.organizationId,
|
|
962
|
-
ticketAuth: options.ticketAuth
|
|
941
|
+
publishableKey: options.publishableKey,
|
|
942
|
+
idToken: options.idToken
|
|
963
943
|
};
|
|
964
944
|
const iframeOrigin = this._getIframeOrigin();
|
|
965
945
|
if (this._iframeReady && iframeOrigin) this._postAuth(iframeOrigin);
|
|
@@ -978,7 +958,7 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
978
958
|
}
|
|
979
959
|
/**
|
|
980
960
|
* Disconnect from the Char agent.
|
|
981
|
-
* Clears the
|
|
961
|
+
* Clears pending auth state and posts a disconnect message to the iframe.
|
|
982
962
|
*
|
|
983
963
|
* @returns `true` when the disconnect message was sent; `false` when the iframe was not ready.
|
|
984
964
|
*/
|
|
@@ -1042,11 +1022,6 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1042
1022
|
};
|
|
1043
1023
|
const safeAreaInsets = getSafeAreaInsets();
|
|
1044
1024
|
const displayMode = resolveDisplayModeFromAttribute(displayModeAttr) ?? "inline";
|
|
1045
|
-
const hostCapabilities = {
|
|
1046
|
-
supportedDisplayModes: [...DISPLAY_MODES],
|
|
1047
|
-
supportsOpenLink: true,
|
|
1048
|
-
supportsTeardown: true
|
|
1049
|
-
};
|
|
1050
1025
|
const context = {
|
|
1051
1026
|
theme,
|
|
1052
1027
|
styles: { variables: vars },
|
|
@@ -1057,9 +1032,7 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1057
1032
|
timeZone,
|
|
1058
1033
|
platform: detectPlatform(),
|
|
1059
1034
|
deviceCapabilities: getDeviceCapabilities(),
|
|
1060
|
-
safeAreaInsets
|
|
1061
|
-
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
|
|
1062
|
-
hostCapabilities
|
|
1035
|
+
safeAreaInsets
|
|
1063
1036
|
};
|
|
1064
1037
|
this._hostContext = context;
|
|
1065
1038
|
if (!this._iframe?.contentWindow) {
|
|
@@ -1102,22 +1075,15 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1102
1075
|
*/
|
|
1103
1076
|
_postAuth(iframeOrigin) {
|
|
1104
1077
|
if (!this._pendingAuth || !this._iframe?.contentWindow) return;
|
|
1105
|
-
if (!this._pendingAuth.
|
|
1106
|
-
console.error("[Char] _postAuth called but _pendingAuth has no
|
|
1078
|
+
if (!this._pendingAuth.publishableKey) {
|
|
1079
|
+
console.error("[Char] _postAuth called but _pendingAuth has no publishableKey");
|
|
1107
1080
|
this._pendingAuth = null;
|
|
1108
1081
|
return;
|
|
1109
1082
|
}
|
|
1110
|
-
const message =
|
|
1111
|
-
type: "char-auth",
|
|
1112
|
-
ticketAuth: this._pendingAuth.ticketAuth
|
|
1113
|
-
} : this._pendingAuth.anthropicApiKey ? {
|
|
1083
|
+
const message = {
|
|
1114
1084
|
type: "char-auth",
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
type: "char-auth",
|
|
1118
|
-
idToken: this._pendingAuth.idToken,
|
|
1119
|
-
clientId: this._pendingAuth.clientId,
|
|
1120
|
-
organizationId: this._pendingAuth.organizationId
|
|
1085
|
+
publishableKey: this._pendingAuth.publishableKey,
|
|
1086
|
+
idToken: this._pendingAuth.idToken
|
|
1121
1087
|
};
|
|
1122
1088
|
this._iframe.contentWindow.postMessage(message, iframeOrigin);
|
|
1123
1089
|
}
|
|
@@ -1264,36 +1230,6 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1264
1230
|
this._containerResizeObserver.observe(this);
|
|
1265
1231
|
}
|
|
1266
1232
|
/**
|
|
1267
|
-
* Resolve devMode from property (React 19) or attribute.
|
|
1268
|
-
*
|
|
1269
|
-
* @returns Parsed dev-mode configuration when valid.
|
|
1270
|
-
*/
|
|
1271
|
-
_resolveDevMode() {
|
|
1272
|
-
if (this.devMode && typeof this.devMode === "object") return this.devMode;
|
|
1273
|
-
const devModeAttr = this.getAttribute("dev-mode");
|
|
1274
|
-
if (!devModeAttr) return void 0;
|
|
1275
|
-
try {
|
|
1276
|
-
const parsed = JSON.parse(devModeAttr);
|
|
1277
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1278
|
-
const msg = `dev-mode attribute must be a JSON object, got: ${Array.isArray(parsed) ? "array" : typeof parsed}`;
|
|
1279
|
-
console.warn(`[Char] ${msg}`);
|
|
1280
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1281
|
-
return;
|
|
1282
|
-
}
|
|
1283
|
-
const obj = parsed;
|
|
1284
|
-
return {
|
|
1285
|
-
anthropicApiKey: typeof obj.anthropicApiKey === "string" ? obj.anthropicApiKey : void 0,
|
|
1286
|
-
openaiApiKey: typeof obj.openaiApiKey === "string" ? obj.openaiApiKey : void 0,
|
|
1287
|
-
useLocalApi: typeof obj.useLocalApi === "boolean" ? obj.useLocalApi : void 0
|
|
1288
|
-
};
|
|
1289
|
-
} catch (e) {
|
|
1290
|
-
const msg = `Failed to parse dev-mode attribute as JSON: ${e instanceof Error ? e.message : String(e)}`;
|
|
1291
|
-
console.warn(`[Char] ${msg}`);
|
|
1292
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
/**
|
|
1297
1233
|
* Resolve apiBase override from property (React 19) or attribute.
|
|
1298
1234
|
*
|
|
1299
1235
|
* @returns Sanitized API base URL without trailing slash.
|
|
@@ -1322,22 +1258,36 @@ registerChar();
|
|
|
1322
1258
|
//#region src/shell-component.ts
|
|
1323
1259
|
const SHELL_OPEN_CHANGE_EVENT = "char-shell-open-change";
|
|
1324
1260
|
const SHELL_Z_INDEX = 2147483e3;
|
|
1325
|
-
const
|
|
1326
|
-
const
|
|
1327
|
-
const
|
|
1328
|
-
const
|
|
1329
|
-
const
|
|
1261
|
+
const FULLSCREEN_BREAKPOINT = 1024;
|
|
1262
|
+
const PANEL_WIDTH = 420;
|
|
1263
|
+
const PIP_WIDTH = 400;
|
|
1264
|
+
const PIP_HEIGHT = 60;
|
|
1265
|
+
const PIP_POSITION = "bottom-center";
|
|
1330
1266
|
const SHELL_HEALTH_TIMEOUT_MS = 1e4;
|
|
1331
|
-
const PANEL_BORDER = "1px solid rgba(15, 23, 42, 0.12)";
|
|
1332
1267
|
const SAFE_BOTTOM = "calc(16px + env(safe-area-inset-bottom, 0px))";
|
|
1333
|
-
const
|
|
1268
|
+
const THEME_LIGHT = {
|
|
1269
|
+
background: "oklch(1 0 0)",
|
|
1270
|
+
foreground: "oklch(0.141 0.005 285.823)",
|
|
1271
|
+
border: "oklch(0.92 0.004 286.32)",
|
|
1272
|
+
mutedForeground: "oklch(0.552 0.016 285.938)",
|
|
1273
|
+
primary: "oklch(0.21 0.006 285.885)"
|
|
1274
|
+
};
|
|
1275
|
+
const THEME_DARK = {
|
|
1276
|
+
background: "oklch(0.141 0.005 285.823)",
|
|
1277
|
+
foreground: "oklch(0.985 0 0)",
|
|
1278
|
+
border: "oklch(1 0 0 / 10%)",
|
|
1279
|
+
mutedForeground: "oklch(0.705 0.015 286.067)",
|
|
1280
|
+
primary: "oklch(0.92 0.004 286.32)"
|
|
1281
|
+
};
|
|
1282
|
+
const BODY_MARGIN_TRANSITION = "margin-right 220ms ease";
|
|
1283
|
+
const SHELL_LAYOUT_TRANSITION = "opacity 220ms ease, width 220ms ease, min-width 220ms ease, flex-basis 220ms ease";
|
|
1334
1284
|
const SHELL_REVEAL_FADE_MS = 280;
|
|
1335
1285
|
const PIP_MIN_COLLAPSED_WIDTH = 70;
|
|
1336
1286
|
const PIP_MIN_COLLAPSED_HEIGHT = 30;
|
|
1337
1287
|
const PIP_MAX_MEASURED_HEIGHT = 320;
|
|
1338
1288
|
const PIP_COLLAPSE_DELAY_MS = 400;
|
|
1339
1289
|
const PIP_PILL_HEIGHT = 40;
|
|
1340
|
-
const PIP_MORPH_TRANSITION = "width 280ms ease, height 280ms ease, bottom 280ms ease, border-radius 280ms ease, opacity
|
|
1290
|
+
const PIP_MORPH_TRANSITION = "width 280ms ease, height 280ms ease, bottom 280ms ease, border-radius 280ms ease, opacity 280ms ease, transform 280ms ease";
|
|
1341
1291
|
const PIP_MORPH_DURATION_MS = 280;
|
|
1342
1292
|
const PIP_SCROLL_HIDE_OFFSET = 100;
|
|
1343
1293
|
const PIP_COLLAPSED_WIDTH_RATIO = .75;
|
|
@@ -1345,23 +1295,14 @@ const PIP_SWIPE_THRESHOLD = 50;
|
|
|
1345
1295
|
const PIP_SCROLL_DIRECTION_THRESHOLD = 40;
|
|
1346
1296
|
const WHEEL_DELTA_LINE_MULTIPLIER = 15;
|
|
1347
1297
|
const WHEEL_DELTA_PAGE_MULTIPLIER = 300;
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
if (value === null || value.trim() === "") return fallback;
|
|
1357
|
-
const parsed = Number.parseInt(value, 10);
|
|
1358
|
-
if (!Number.isFinite(parsed)) return fallback;
|
|
1359
|
-
return normalizePositiveInteger(parsed, fallback);
|
|
1360
|
-
}
|
|
1361
|
-
function normalizePipPosition(value) {
|
|
1362
|
-
if (value === "bottom-right" || value === "bottom-left") return value;
|
|
1363
|
-
return DEFAULT_PIP_POSITION;
|
|
1364
|
-
}
|
|
1298
|
+
const PANEL_MIN_WIDTH = 320;
|
|
1299
|
+
const PANEL_MAX_WIDTH = 700;
|
|
1300
|
+
const RESIZE_NUB_WIDTH = 6;
|
|
1301
|
+
const EDGE_TAB_WIDTH = 44;
|
|
1302
|
+
const EDGE_TAB_HEIGHT = 48;
|
|
1303
|
+
const EDGE_TAB_DRAG_THRESHOLD_PX = 4;
|
|
1304
|
+
const ICON_MESSAGE = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/></svg>`;
|
|
1305
|
+
const ICON_CHEVRON_RIGHT = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>`;
|
|
1365
1306
|
function getPipPositionStyles(position) {
|
|
1366
1307
|
if (position === "bottom-right") return {
|
|
1367
1308
|
left: "auto",
|
|
@@ -1418,25 +1359,28 @@ function sanitizeShellHostContext(hostContext) {
|
|
|
1418
1359
|
var CharAgentShellElement = class extends HTMLElement {
|
|
1419
1360
|
static observedAttributes = [
|
|
1420
1361
|
"open",
|
|
1421
|
-
"fullscreen-breakpoint",
|
|
1422
|
-
"panel-width",
|
|
1423
|
-
"pip-position",
|
|
1424
|
-
"pip-width",
|
|
1425
|
-
"pip-height",
|
|
1426
|
-
"dev-mode",
|
|
1427
1362
|
"enable-debug-tools",
|
|
1428
1363
|
"api-base"
|
|
1429
1364
|
];
|
|
1430
1365
|
_agent = null;
|
|
1431
1366
|
_open = false;
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1367
|
+
_savedBodyTransition = null;
|
|
1368
|
+
_bodyMarginTimeoutId = null;
|
|
1369
|
+
_currentPanelWidth = PANEL_WIDTH;
|
|
1370
|
+
_resizeNubElement = null;
|
|
1371
|
+
_edgeTabElement = null;
|
|
1372
|
+
_resizeDragging = false;
|
|
1373
|
+
_resizeDragOverlay = null;
|
|
1374
|
+
_resizeStartX = 0;
|
|
1375
|
+
_resizeStartWidth = 0;
|
|
1376
|
+
_edgeTabDragStartX = 0;
|
|
1377
|
+
_edgeTabDragging = false;
|
|
1437
1378
|
_isNarrowViewport = false;
|
|
1438
1379
|
_mediaQuery = null;
|
|
1439
1380
|
_mediaQueryHandler = null;
|
|
1381
|
+
_themeObserver = null;
|
|
1382
|
+
_themeMediaQuery = null;
|
|
1383
|
+
_themeMediaHandler = null;
|
|
1440
1384
|
_pendingConnectOptions = null;
|
|
1441
1385
|
_pendingHostContext = {};
|
|
1442
1386
|
_isApplyingOpenAttribute = false;
|
|
@@ -1454,6 +1398,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1454
1398
|
_pipAnimationTimeoutId = null;
|
|
1455
1399
|
_pendingPipMeasuredHeight = null;
|
|
1456
1400
|
_tapOutsideListener = null;
|
|
1401
|
+
_lastAppliedDisplayMode = null;
|
|
1457
1402
|
_pipHiddenByScroll = false;
|
|
1458
1403
|
_scrollListener = null;
|
|
1459
1404
|
_scrollRafId = null;
|
|
@@ -1473,75 +1418,36 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1473
1418
|
set open(value) {
|
|
1474
1419
|
this.setOpen(value);
|
|
1475
1420
|
}
|
|
1476
|
-
get fullscreenBreakpoint() {
|
|
1477
|
-
return this._fullscreenBreakpoint;
|
|
1478
|
-
}
|
|
1479
|
-
set fullscreenBreakpoint(value) {
|
|
1480
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1481
|
-
if (normalized === this._fullscreenBreakpoint) return;
|
|
1482
|
-
this._fullscreenBreakpoint = normalized;
|
|
1483
|
-
this.setAttribute("fullscreen-breakpoint", String(normalized));
|
|
1484
|
-
this._setupViewportObserver();
|
|
1485
|
-
this._applyDisplayModeAndLayout();
|
|
1486
|
-
}
|
|
1487
|
-
get panelWidth() {
|
|
1488
|
-
return this._panelWidth;
|
|
1489
|
-
}
|
|
1490
|
-
set panelWidth(value) {
|
|
1491
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PANEL_WIDTH);
|
|
1492
|
-
if (normalized === this._panelWidth) return;
|
|
1493
|
-
this._panelWidth = normalized;
|
|
1494
|
-
this.setAttribute("panel-width", String(normalized));
|
|
1495
|
-
this._applyDisplayModeAndLayout();
|
|
1496
|
-
}
|
|
1497
|
-
get pipWidth() {
|
|
1498
|
-
return this._pipWidth;
|
|
1499
|
-
}
|
|
1500
|
-
set pipWidth(value) {
|
|
1501
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PIP_WIDTH);
|
|
1502
|
-
if (normalized === this._pipWidth) return;
|
|
1503
|
-
this._pipWidth = normalized;
|
|
1504
|
-
this.setAttribute("pip-width", String(normalized));
|
|
1505
|
-
this._applyDisplayModeAndLayout();
|
|
1506
|
-
}
|
|
1507
|
-
get pipHeight() {
|
|
1508
|
-
return this._pipHeight;
|
|
1509
|
-
}
|
|
1510
|
-
set pipHeight(value) {
|
|
1511
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PIP_HEIGHT);
|
|
1512
|
-
if (normalized === this._pipHeight) return;
|
|
1513
|
-
this._pipHeight = normalized;
|
|
1514
|
-
this.setAttribute("pip-height", String(normalized));
|
|
1515
|
-
this._applyDisplayModeAndLayout();
|
|
1516
|
-
}
|
|
1517
|
-
get pipPosition() {
|
|
1518
|
-
return this._pipPosition;
|
|
1519
|
-
}
|
|
1520
|
-
set pipPosition(value) {
|
|
1521
|
-
const normalized = normalizePipPosition(value);
|
|
1522
|
-
if (normalized === this._pipPosition) return;
|
|
1523
|
-
this._pipPosition = normalized;
|
|
1524
|
-
this.setAttribute("pip-position", normalized);
|
|
1525
|
-
this._applyDisplayModeAndLayout();
|
|
1526
|
-
}
|
|
1527
1421
|
connectedCallback() {
|
|
1528
1422
|
this._upgradeProperty("open");
|
|
1529
|
-
this._upgradeProperty("fullscreenBreakpoint");
|
|
1530
|
-
this._upgradeProperty("panelWidth");
|
|
1531
|
-
this._upgradeProperty("pipPosition");
|
|
1532
|
-
this._upgradeProperty("pipWidth");
|
|
1533
|
-
this._upgradeProperty("pipHeight");
|
|
1534
|
-
this._upgradeProperty("devMode");
|
|
1535
1423
|
this._upgradeProperty("apiBase");
|
|
1536
1424
|
registerChar();
|
|
1537
1425
|
this._ensureAgent();
|
|
1538
1426
|
this._readConfigFromAttributes();
|
|
1539
1427
|
this._syncForwardedAttributes();
|
|
1540
1428
|
this._setupViewportObserver();
|
|
1429
|
+
this._setupThemeObserver();
|
|
1541
1430
|
this._applyDisplayModeAndLayout();
|
|
1542
1431
|
this._flushPendingConnectAndContext();
|
|
1543
1432
|
}
|
|
1544
1433
|
disconnectedCallback() {
|
|
1434
|
+
this._removeBodyMargin();
|
|
1435
|
+
if (this._bodyMarginTimeoutId !== null) {
|
|
1436
|
+
window.clearTimeout(this._bodyMarginTimeoutId);
|
|
1437
|
+
this._bodyMarginTimeoutId = null;
|
|
1438
|
+
}
|
|
1439
|
+
this._removeResizeDragOverlay();
|
|
1440
|
+
this._teardownThemeObserver();
|
|
1441
|
+
document.removeEventListener("mousemove", this._onEdgeTabMouseMove);
|
|
1442
|
+
document.removeEventListener("mouseup", this._onEdgeTabMouseUp);
|
|
1443
|
+
document.removeEventListener("touchmove", this._onEdgeTabTouchMove);
|
|
1444
|
+
document.removeEventListener("touchend", this._onEdgeTabTouchEnd);
|
|
1445
|
+
this._removeEdgeTab();
|
|
1446
|
+
this._hideResizeNub();
|
|
1447
|
+
if (this._resizeNubElement) {
|
|
1448
|
+
this._resizeNubElement.remove();
|
|
1449
|
+
this._resizeNubElement = null;
|
|
1450
|
+
}
|
|
1545
1451
|
this._stopHealthWait();
|
|
1546
1452
|
this._cancelRevealFade();
|
|
1547
1453
|
this._teardownViewportObserver();
|
|
@@ -1570,48 +1476,6 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1570
1476
|
});
|
|
1571
1477
|
return;
|
|
1572
1478
|
}
|
|
1573
|
-
case "fullscreen-breakpoint": {
|
|
1574
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1575
|
-
if (next !== this._fullscreenBreakpoint) {
|
|
1576
|
-
this._fullscreenBreakpoint = next;
|
|
1577
|
-
this._setupViewportObserver();
|
|
1578
|
-
this._applyDisplayModeAndLayout();
|
|
1579
|
-
}
|
|
1580
|
-
return;
|
|
1581
|
-
}
|
|
1582
|
-
case "panel-width": {
|
|
1583
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PANEL_WIDTH);
|
|
1584
|
-
if (next !== this._panelWidth) {
|
|
1585
|
-
this._panelWidth = next;
|
|
1586
|
-
this._applyDisplayModeAndLayout();
|
|
1587
|
-
}
|
|
1588
|
-
return;
|
|
1589
|
-
}
|
|
1590
|
-
case "pip-width": {
|
|
1591
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PIP_WIDTH);
|
|
1592
|
-
if (next !== this._pipWidth) {
|
|
1593
|
-
this._pipWidth = next;
|
|
1594
|
-
this._applyDisplayModeAndLayout();
|
|
1595
|
-
}
|
|
1596
|
-
return;
|
|
1597
|
-
}
|
|
1598
|
-
case "pip-height": {
|
|
1599
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PIP_HEIGHT);
|
|
1600
|
-
if (next !== this._pipHeight) {
|
|
1601
|
-
this._pipHeight = next;
|
|
1602
|
-
this._applyDisplayModeAndLayout();
|
|
1603
|
-
}
|
|
1604
|
-
return;
|
|
1605
|
-
}
|
|
1606
|
-
case "pip-position": {
|
|
1607
|
-
const next = normalizePipPosition(newValue);
|
|
1608
|
-
if (next !== this._pipPosition) {
|
|
1609
|
-
this._pipPosition = next;
|
|
1610
|
-
this._applyDisplayModeAndLayout();
|
|
1611
|
-
}
|
|
1612
|
-
return;
|
|
1613
|
-
}
|
|
1614
|
-
case "dev-mode":
|
|
1615
1479
|
case "enable-debug-tools":
|
|
1616
1480
|
case "api-base":
|
|
1617
1481
|
this._syncForwardedAttributes();
|
|
@@ -1621,7 +1485,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1621
1485
|
/**
|
|
1622
1486
|
* Connects shell authentication and starts availability health checks.
|
|
1623
1487
|
*
|
|
1624
|
-
* @param options - Authentication payload
|
|
1488
|
+
* @param options - Authentication payload (`publishableKey` + optional `idToken`).
|
|
1625
1489
|
* @returns `true` when connect is accepted by the inner element.
|
|
1626
1490
|
*/
|
|
1627
1491
|
connect(options) {
|
|
@@ -1729,15 +1593,10 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1729
1593
|
this._attachForwardedListeners();
|
|
1730
1594
|
}
|
|
1731
1595
|
_primeAgentBootConfig(agent) {
|
|
1732
|
-
for (const name of [
|
|
1733
|
-
"dev-mode",
|
|
1734
|
-
"enable-debug-tools",
|
|
1735
|
-
"api-base"
|
|
1736
|
-
]) {
|
|
1596
|
+
for (const name of ["enable-debug-tools", "api-base"]) {
|
|
1737
1597
|
const value = this.getAttribute(name);
|
|
1738
1598
|
if (value !== null) agent.setAttribute(name, value);
|
|
1739
1599
|
}
|
|
1740
|
-
agent.devMode = this.devMode && typeof this.devMode === "object" ? this.devMode : void 0;
|
|
1741
1600
|
agent.apiBase = typeof this.apiBase === "string" ? this.apiBase : void 0;
|
|
1742
1601
|
}
|
|
1743
1602
|
_attachForwardedListeners() {
|
|
@@ -1760,29 +1619,19 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1760
1619
|
}
|
|
1761
1620
|
_readConfigFromAttributes() {
|
|
1762
1621
|
this._open = parseBooleanAttribute(this.getAttribute("open"));
|
|
1763
|
-
this._fullscreenBreakpoint = parsePositiveIntegerAttribute(this.getAttribute("fullscreen-breakpoint"), DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1764
|
-
this._panelWidth = parsePositiveIntegerAttribute(this.getAttribute("panel-width"), DEFAULT_PANEL_WIDTH);
|
|
1765
|
-
this._pipWidth = parsePositiveIntegerAttribute(this.getAttribute("pip-width"), DEFAULT_PIP_WIDTH);
|
|
1766
|
-
this._pipHeight = parsePositiveIntegerAttribute(this.getAttribute("pip-height"), DEFAULT_PIP_HEIGHT);
|
|
1767
|
-
this._pipPosition = normalizePipPosition(this.getAttribute("pip-position"));
|
|
1768
1622
|
}
|
|
1769
1623
|
_syncForwardedAttributes() {
|
|
1770
1624
|
if (!this._agent) return;
|
|
1771
|
-
for (const name of [
|
|
1772
|
-
"dev-mode",
|
|
1773
|
-
"enable-debug-tools",
|
|
1774
|
-
"api-base"
|
|
1775
|
-
]) {
|
|
1625
|
+
for (const name of ["enable-debug-tools", "api-base"]) {
|
|
1776
1626
|
const value = this.getAttribute(name);
|
|
1777
1627
|
if (value === null) this._agent.removeAttribute(name);
|
|
1778
1628
|
else this._agent.setAttribute(name, value);
|
|
1779
1629
|
}
|
|
1780
|
-
this._agent.devMode = this.devMode && typeof this.devMode === "object" ? this.devMode : void 0;
|
|
1781
1630
|
this._agent.apiBase = typeof this.apiBase === "string" ? this.apiBase : void 0;
|
|
1782
1631
|
}
|
|
1783
1632
|
_setupViewportObserver() {
|
|
1784
1633
|
if (typeof window === "undefined") return;
|
|
1785
|
-
const query = `(max-width: ${
|
|
1634
|
+
const query = `(max-width: ${FULLSCREEN_BREAKPOINT - 1}px)`;
|
|
1786
1635
|
if (this._mediaQuery?.media === query) {
|
|
1787
1636
|
this._isNarrowViewport = this._mediaQuery.matches;
|
|
1788
1637
|
return;
|
|
@@ -1820,38 +1669,558 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1820
1669
|
if (displayMode !== "pip") {
|
|
1821
1670
|
if (this._pipMeasuredHeight !== null) this._pipMeasuredHeight = null;
|
|
1822
1671
|
}
|
|
1672
|
+
const prevMode = this._lastAppliedDisplayMode;
|
|
1673
|
+
this._lastAppliedDisplayMode = displayMode;
|
|
1823
1674
|
this._agent.setAttribute("display-mode", displayMode);
|
|
1824
1675
|
this._applyHostLayout(displayMode);
|
|
1825
|
-
this._applyAgentLayout(displayMode);
|
|
1676
|
+
this._applyAgentLayout(displayMode, prevMode);
|
|
1677
|
+
}
|
|
1678
|
+
_isParentFlex() {
|
|
1679
|
+
const parent = this.parentElement;
|
|
1680
|
+
if (!parent) return false;
|
|
1681
|
+
const display = getComputedStyle(parent).display;
|
|
1682
|
+
return display === "flex" || display === "inline-flex";
|
|
1683
|
+
}
|
|
1684
|
+
_isDarkMode() {
|
|
1685
|
+
const themeAttr = document.documentElement.getAttribute("data-theme");
|
|
1686
|
+
if (themeAttr === "dark") return true;
|
|
1687
|
+
if (themeAttr === "light") return false;
|
|
1688
|
+
return document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Read a `--char-color-*` variable from the shell element. If the host
|
|
1692
|
+
* page has set it (directly or via inheritance) use that value; otherwise
|
|
1693
|
+
* fall back to the char design-system default for the current light/dark mode.
|
|
1694
|
+
*/
|
|
1695
|
+
_resolveColor(token) {
|
|
1696
|
+
const host = getComputedStyle(this).getPropertyValue({
|
|
1697
|
+
background: "--char-color-background",
|
|
1698
|
+
foreground: "--char-color-foreground",
|
|
1699
|
+
border: "--char-color-border",
|
|
1700
|
+
mutedForeground: "--char-color-muted-foreground",
|
|
1701
|
+
primary: "--char-color-primary"
|
|
1702
|
+
}[token]).trim();
|
|
1703
|
+
if (host) return host;
|
|
1704
|
+
return (this._isDarkMode() ? THEME_DARK : THEME_LIGHT)[token];
|
|
1705
|
+
}
|
|
1706
|
+
_panelBorder() {
|
|
1707
|
+
return `1px solid ${this._resolveColor("border")}`;
|
|
1708
|
+
}
|
|
1709
|
+
_setupThemeObserver() {
|
|
1710
|
+
const onThemeChange = () => {
|
|
1711
|
+
this._applyEdgeTabVisual();
|
|
1712
|
+
this._refreshResizeNubColors();
|
|
1713
|
+
if (this._open && !this._isNarrowViewport) {
|
|
1714
|
+
if (this._isParentFlex()) setStyleValue(this, "border-left", this._panelBorder());
|
|
1715
|
+
else if (this._agent) setStyleValue(this._agent, "border-left", this._panelBorder());
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
this._themeObserver = new MutationObserver(onThemeChange);
|
|
1719
|
+
this._themeObserver.observe(document.documentElement, {
|
|
1720
|
+
attributes: true,
|
|
1721
|
+
attributeFilter: [
|
|
1722
|
+
"class",
|
|
1723
|
+
"style",
|
|
1724
|
+
"data-theme"
|
|
1725
|
+
]
|
|
1726
|
+
});
|
|
1727
|
+
this._themeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1728
|
+
this._themeMediaHandler = onThemeChange;
|
|
1729
|
+
this._themeMediaQuery.addEventListener("change", this._themeMediaHandler);
|
|
1730
|
+
}
|
|
1731
|
+
_teardownThemeObserver() {
|
|
1732
|
+
if (this._themeObserver) {
|
|
1733
|
+
this._themeObserver.disconnect();
|
|
1734
|
+
this._themeObserver = null;
|
|
1735
|
+
}
|
|
1736
|
+
if (this._themeMediaQuery && this._themeMediaHandler) {
|
|
1737
|
+
this._themeMediaQuery.removeEventListener("change", this._themeMediaHandler);
|
|
1738
|
+
this._themeMediaQuery = null;
|
|
1739
|
+
this._themeMediaHandler = null;
|
|
1740
|
+
}
|
|
1826
1741
|
}
|
|
1827
1742
|
_applyHostLayout(displayMode) {
|
|
1743
|
+
const useFlexLayout = this._isParentFlex();
|
|
1828
1744
|
if (displayMode === "inline" && !this._isNarrowViewport) {
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1745
|
+
if (useFlexLayout) {
|
|
1746
|
+
setStyleValue(this, "position", "relative");
|
|
1747
|
+
setStyleValue(this, "flex", `0 0 ${this._currentPanelWidth}px`);
|
|
1748
|
+
setStyleValue(this, "width", `${this._currentPanelWidth}px`);
|
|
1749
|
+
setStyleValue(this, "min-width", `${this._currentPanelWidth}px`);
|
|
1750
|
+
setStyleValue(this, "height", "100%");
|
|
1751
|
+
setStyleValue(this, "z-index", String(SHELL_Z_INDEX));
|
|
1752
|
+
setStyleValue(this, "border-left", this._panelBorder());
|
|
1753
|
+
setStyleValue(this, "transition", this._resizeDragging ? "none" : SHELL_LAYOUT_TRANSITION);
|
|
1754
|
+
resetStyleValue(this, "overflow");
|
|
1755
|
+
resetStyleValue(this, "pointer-events");
|
|
1756
|
+
this._removeBodyMargin();
|
|
1757
|
+
} else {
|
|
1758
|
+
setStyleValue(this, "position", "fixed");
|
|
1759
|
+
setStyleValue(this, "width", "0px");
|
|
1760
|
+
setStyleValue(this, "height", "0px");
|
|
1761
|
+
setStyleValue(this, "z-index", String(SHELL_Z_INDEX));
|
|
1762
|
+
resetStyleValue(this, "flex");
|
|
1763
|
+
resetStyleValue(this, "min-width");
|
|
1764
|
+
resetStyleValue(this, "border-left");
|
|
1765
|
+
resetStyleValue(this, "overflow");
|
|
1766
|
+
resetStyleValue(this, "pointer-events");
|
|
1767
|
+
this._applyBodyMargin(this._currentPanelWidth);
|
|
1768
|
+
}
|
|
1769
|
+
this._showResizeNub();
|
|
1770
|
+
this._showEdgeTab();
|
|
1771
|
+
if (useFlexLayout) this._syncEdgeTabWithPanelTransition();
|
|
1772
|
+
} else if (displayMode === "fullscreen") {
|
|
1773
|
+
if (useFlexLayout) {
|
|
1774
|
+
setStyleValue(this, "position", "relative");
|
|
1775
|
+
setStyleValue(this, "flex", "0 0 0px");
|
|
1776
|
+
setStyleValue(this, "width", "0px");
|
|
1777
|
+
setStyleValue(this, "min-width", "0px");
|
|
1778
|
+
setStyleValue(this, "height", "0px");
|
|
1779
|
+
setStyleValue(this, "z-index", String(SHELL_Z_INDEX));
|
|
1780
|
+
resetStyleValue(this, "border-left");
|
|
1781
|
+
setStyleValue(this, "transition", SHELL_LAYOUT_TRANSITION);
|
|
1782
|
+
resetStyleValue(this, "overflow");
|
|
1783
|
+
resetStyleValue(this, "pointer-events");
|
|
1784
|
+
} else {
|
|
1785
|
+
setStyleValue(this, "position", "relative");
|
|
1786
|
+
resetStyleValue(this, "flex");
|
|
1787
|
+
resetStyleValue(this, "min-width");
|
|
1788
|
+
resetStyleValue(this, "border-left");
|
|
1789
|
+
resetStyleValue(this, "overflow");
|
|
1790
|
+
resetStyleValue(this, "pointer-events");
|
|
1791
|
+
}
|
|
1792
|
+
this._removeBodyMargin();
|
|
1793
|
+
this._hideResizeNub();
|
|
1794
|
+
this._hideEdgeTab();
|
|
1795
|
+
} else {
|
|
1796
|
+
if (useFlexLayout) {
|
|
1797
|
+
setStyleValue(this, "position", "relative");
|
|
1798
|
+
setStyleValue(this, "flex", "0 0 0px");
|
|
1799
|
+
setStyleValue(this, "width", "0px");
|
|
1800
|
+
setStyleValue(this, "min-width", "0px");
|
|
1801
|
+
setStyleValue(this, "height", "0px");
|
|
1802
|
+
setStyleValue(this, "z-index", String(SHELL_Z_INDEX));
|
|
1803
|
+
resetStyleValue(this, "border-left");
|
|
1804
|
+
setStyleValue(this, "transition", SHELL_LAYOUT_TRANSITION);
|
|
1805
|
+
resetStyleValue(this, "overflow");
|
|
1806
|
+
resetStyleValue(this, "pointer-events");
|
|
1807
|
+
} else {
|
|
1808
|
+
setStyleValue(this, "position", "relative");
|
|
1809
|
+
resetStyleValue(this, "flex");
|
|
1810
|
+
resetStyleValue(this, "min-width");
|
|
1811
|
+
resetStyleValue(this, "border-left");
|
|
1812
|
+
resetStyleValue(this, "overflow");
|
|
1813
|
+
resetStyleValue(this, "pointer-events");
|
|
1814
|
+
}
|
|
1815
|
+
this._removeBodyMargin();
|
|
1816
|
+
this._hideResizeNub();
|
|
1817
|
+
this._showEdgeTab();
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
_applyBodyMargin(width) {
|
|
1821
|
+
const body = document.body;
|
|
1822
|
+
if (this._savedBodyTransition === null) this._savedBodyTransition = body.style.transition || "";
|
|
1823
|
+
if (!this._resizeDragging) body.style.transition = BODY_MARGIN_TRANSITION;
|
|
1824
|
+
body.style.marginRight = `${width}px`;
|
|
1825
|
+
}
|
|
1826
|
+
_removeBodyMargin() {
|
|
1827
|
+
const body = document.body;
|
|
1828
|
+
body.style.marginRight = "";
|
|
1829
|
+
if (this._bodyMarginTimeoutId !== null) {
|
|
1830
|
+
window.clearTimeout(this._bodyMarginTimeoutId);
|
|
1831
|
+
this._bodyMarginTimeoutId = null;
|
|
1832
|
+
}
|
|
1833
|
+
if (this._savedBodyTransition !== null) {
|
|
1834
|
+
const saved = this._savedBodyTransition;
|
|
1835
|
+
this._bodyMarginTimeoutId = window.setTimeout(() => {
|
|
1836
|
+
this._bodyMarginTimeoutId = null;
|
|
1837
|
+
if (body.style.transition === BODY_MARGIN_TRANSITION) body.style.transition = saved || "";
|
|
1838
|
+
}, 220);
|
|
1839
|
+
this._savedBodyTransition = null;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
_ensureResizeNub() {
|
|
1843
|
+
if (this._resizeNubElement) return this._resizeNubElement;
|
|
1844
|
+
const nub = document.createElement("div");
|
|
1845
|
+
nub.setAttribute("role", "separator");
|
|
1846
|
+
nub.setAttribute("aria-orientation", "vertical");
|
|
1847
|
+
nub.setAttribute("aria-label", "Resize panel");
|
|
1848
|
+
nub.style.cssText = [
|
|
1849
|
+
"position:fixed",
|
|
1850
|
+
"top:0",
|
|
1851
|
+
`width:${RESIZE_NUB_WIDTH}px`,
|
|
1852
|
+
"height:100dvh",
|
|
1853
|
+
"cursor:col-resize",
|
|
1854
|
+
`z-index:${SHELL_Z_INDEX + 1}`,
|
|
1855
|
+
"background:transparent",
|
|
1856
|
+
"user-select:none",
|
|
1857
|
+
"-webkit-user-select:none",
|
|
1858
|
+
"touch-action:none"
|
|
1859
|
+
].join(";");
|
|
1860
|
+
const indicator = document.createElement("div");
|
|
1861
|
+
indicator.style.cssText = [
|
|
1862
|
+
"position:absolute",
|
|
1863
|
+
"top:0",
|
|
1864
|
+
"left:50%",
|
|
1865
|
+
"transform:translateX(-50%)",
|
|
1866
|
+
"width:2px",
|
|
1867
|
+
"height:100%",
|
|
1868
|
+
"border-radius:1px",
|
|
1869
|
+
"transition:background 150ms ease, width 150ms ease",
|
|
1870
|
+
"pointer-events:none"
|
|
1871
|
+
].join(";");
|
|
1872
|
+
nub.appendChild(indicator);
|
|
1873
|
+
this._applyResizeNubColors(indicator);
|
|
1874
|
+
nub.addEventListener("mouseenter", () => {
|
|
1875
|
+
const dark = this._isDarkMode();
|
|
1876
|
+
indicator.style.background = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.15)";
|
|
1877
|
+
indicator.style.width = "3px";
|
|
1878
|
+
});
|
|
1879
|
+
nub.addEventListener("mouseleave", () => {
|
|
1880
|
+
if (!this._resizeDragging) {
|
|
1881
|
+
this._applyResizeNubColors(indicator);
|
|
1882
|
+
indicator.style.width = "2px";
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
nub.addEventListener("mousedown", this._onResizeStart);
|
|
1886
|
+
nub.addEventListener("touchstart", this._onResizeTouchStart, { passive: false });
|
|
1887
|
+
this._resizeNubElement = nub;
|
|
1888
|
+
return nub;
|
|
1889
|
+
}
|
|
1890
|
+
_applyResizeNubColors(indicator) {
|
|
1891
|
+
const el = indicator ?? this._resizeNubElement?.querySelector("div");
|
|
1892
|
+
if (!el) return;
|
|
1893
|
+
const dark = this._isDarkMode();
|
|
1894
|
+
el.style.background = dark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.06)";
|
|
1895
|
+
}
|
|
1896
|
+
_refreshResizeNubColors() {
|
|
1897
|
+
this._applyResizeNubColors();
|
|
1898
|
+
}
|
|
1899
|
+
_showResizeNub() {
|
|
1900
|
+
const nub = this._ensureResizeNub();
|
|
1901
|
+
if (!nub.parentNode) document.body.appendChild(nub);
|
|
1902
|
+
setStyleValue(nub, "right", `${this._currentPanelWidth - Math.floor(RESIZE_NUB_WIDTH / 2)}px`);
|
|
1903
|
+
setStyleValue(nub, "display", "block");
|
|
1904
|
+
}
|
|
1905
|
+
_hideResizeNub() {
|
|
1906
|
+
if (this._resizeNubElement) setStyleValue(this._resizeNubElement, "display", "none");
|
|
1907
|
+
}
|
|
1908
|
+
_ensureEdgeTab() {
|
|
1909
|
+
if (this._edgeTabElement) return this._edgeTabElement;
|
|
1910
|
+
const tab = document.createElement("button");
|
|
1911
|
+
tab.setAttribute("type", "button");
|
|
1912
|
+
tab.setAttribute("aria-label", "Open assistant");
|
|
1913
|
+
tab.style.cssText = [
|
|
1914
|
+
"position:fixed",
|
|
1915
|
+
"top:50%",
|
|
1916
|
+
"right:0px",
|
|
1917
|
+
"transform:translateY(-50%)",
|
|
1918
|
+
`z-index:${SHELL_Z_INDEX + 2}`,
|
|
1919
|
+
`width:${EDGE_TAB_WIDTH}px`,
|
|
1920
|
+
`height:${EDGE_TAB_HEIGHT}px`,
|
|
1921
|
+
"display:flex",
|
|
1922
|
+
"align-items:center",
|
|
1923
|
+
"justify-content:center",
|
|
1924
|
+
"border-right:none",
|
|
1925
|
+
"border-radius:12px 0 0 12px",
|
|
1926
|
+
"padding:0",
|
|
1927
|
+
"outline:none",
|
|
1928
|
+
"transition:transform 200ms ease,background 150ms ease,color 150ms ease,box-shadow 200ms ease,border-color 150ms ease",
|
|
1929
|
+
"user-select:none",
|
|
1930
|
+
"-webkit-user-select:none"
|
|
1931
|
+
].join(";");
|
|
1932
|
+
const dot = document.createElement("span");
|
|
1933
|
+
dot.style.cssText = [
|
|
1934
|
+
"position:absolute",
|
|
1935
|
+
"top:8px",
|
|
1936
|
+
"right:10px",
|
|
1937
|
+
"width:7px",
|
|
1938
|
+
"height:7px",
|
|
1939
|
+
"border-radius:50%",
|
|
1940
|
+
"transition:opacity 200ms ease"
|
|
1941
|
+
].join(";");
|
|
1942
|
+
tab.appendChild(dot);
|
|
1943
|
+
const icon = document.createElement("span");
|
|
1944
|
+
icon.style.cssText = "display:flex;align-items:center;justify-content:center;pointer-events:none;";
|
|
1945
|
+
icon.innerHTML = ICON_MESSAGE;
|
|
1946
|
+
tab.appendChild(icon);
|
|
1947
|
+
tab.addEventListener("mouseenter", () => {
|
|
1948
|
+
const dark = this._isDarkMode();
|
|
1949
|
+
if (this._open && !this._isNarrowViewport) {
|
|
1950
|
+
tab.style.background = dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)";
|
|
1951
|
+
tab.style.boxShadow = "none";
|
|
1952
|
+
tab.style.color = this._resolveColor("foreground");
|
|
1953
|
+
tab.style.cursor = "col-resize";
|
|
1954
|
+
} else {
|
|
1955
|
+
tab.style.transform = "translateY(-50%) translateX(-3px)";
|
|
1956
|
+
tab.style.boxShadow = dark ? "-4px 0 16px rgba(0,0,0,0.3)" : "-4px 0 16px rgba(0,0,0,0.12)";
|
|
1957
|
+
tab.style.color = this._resolveColor("foreground");
|
|
1958
|
+
tab.style.cursor = "pointer";
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
tab.addEventListener("mouseleave", () => {
|
|
1962
|
+
this._applyEdgeTabVisual();
|
|
1963
|
+
});
|
|
1964
|
+
tab.addEventListener("mousedown", this._onEdgeTabMouseDown);
|
|
1965
|
+
tab.addEventListener("touchstart", this._onEdgeTabTouchStart, { passive: false });
|
|
1966
|
+
this._edgeTabElement = tab;
|
|
1967
|
+
return tab;
|
|
1968
|
+
}
|
|
1969
|
+
/** Apply visual style (colors, shadow) based on open state — no position change. */
|
|
1970
|
+
_applyEdgeTabVisual() {
|
|
1971
|
+
if (!this._edgeTabElement) return;
|
|
1972
|
+
const tab = this._edgeTabElement;
|
|
1973
|
+
const icon = tab.querySelector("span:last-child");
|
|
1974
|
+
const dot = tab.querySelector("span:first-child");
|
|
1975
|
+
const dark = this._isDarkMode();
|
|
1976
|
+
const bg = this._resolveColor("background");
|
|
1977
|
+
const borderColor = this._resolveColor("border");
|
|
1978
|
+
const mutedFg = this._resolveColor("mutedForeground");
|
|
1979
|
+
const primary = this._resolveColor("primary");
|
|
1980
|
+
tab.style.border = `1px solid ${borderColor}`;
|
|
1981
|
+
tab.style.borderRight = "none";
|
|
1982
|
+
if (this._open && !this._isNarrowViewport) {
|
|
1983
|
+
tab.setAttribute("aria-label", "Close assistant");
|
|
1984
|
+
tab.setAttribute("aria-expanded", "true");
|
|
1985
|
+
if (icon) icon.innerHTML = ICON_CHEVRON_RIGHT;
|
|
1986
|
+
if (dot) dot.style.opacity = "0";
|
|
1987
|
+
tab.style.background = bg;
|
|
1988
|
+
tab.style.boxShadow = "none";
|
|
1989
|
+
tab.style.backdropFilter = "none";
|
|
1990
|
+
tab.style.setProperty("-webkit-backdrop-filter", "none");
|
|
1991
|
+
tab.style.color = mutedFg;
|
|
1992
|
+
tab.style.cursor = "col-resize";
|
|
1993
|
+
tab.style.transform = "translateY(-50%)";
|
|
1994
|
+
} else {
|
|
1995
|
+
tab.setAttribute("aria-label", "Open assistant");
|
|
1996
|
+
tab.setAttribute("aria-expanded", "false");
|
|
1997
|
+
if (icon) icon.innerHTML = ICON_MESSAGE;
|
|
1998
|
+
if (dot) {
|
|
1999
|
+
dot.style.opacity = "1";
|
|
2000
|
+
dot.style.background = primary;
|
|
2001
|
+
}
|
|
2002
|
+
tab.style.background = dark ? "color-mix(in oklch, " + bg + ", white 4%)" : "color-mix(in oklch, " + bg + ", transparent 5%)";
|
|
2003
|
+
tab.style.boxShadow = dark ? "-2px 0 12px rgba(0,0,0,0.25)" : "-2px 0 12px rgba(0,0,0,0.08)";
|
|
2004
|
+
tab.style.backdropFilter = "blur(8px)";
|
|
2005
|
+
tab.style.setProperty("-webkit-backdrop-filter", "blur(8px)");
|
|
2006
|
+
tab.style.color = mutedFg;
|
|
2007
|
+
tab.style.cursor = "pointer";
|
|
2008
|
+
tab.style.transform = "translateY(-50%)";
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
/** Set the tab's `right` position immediately (no transition). */
|
|
2012
|
+
_positionEdgeTab(rightPx) {
|
|
2013
|
+
if (this._edgeTabElement) this._edgeTabElement.style.right = `${rightPx}px`;
|
|
2014
|
+
}
|
|
2015
|
+
_showEdgeTab() {
|
|
2016
|
+
const tab = this._ensureEdgeTab();
|
|
2017
|
+
if (!tab.parentNode) document.body.appendChild(tab);
|
|
2018
|
+
setStyleValue(tab, "display", "flex");
|
|
2019
|
+
if (this._open && !this._isNarrowViewport) this._positionEdgeTab(this._currentPanelWidth - 1);
|
|
2020
|
+
else this._positionEdgeTab(0);
|
|
2021
|
+
this._applyEdgeTabVisual();
|
|
2022
|
+
}
|
|
2023
|
+
_hideEdgeTab() {
|
|
2024
|
+
if (this._edgeTabElement) setStyleValue(this._edgeTabElement, "display", "none");
|
|
2025
|
+
}
|
|
2026
|
+
_removeEdgeTab() {
|
|
2027
|
+
if (this._edgeTabElement) {
|
|
2028
|
+
this._edgeTabElement.remove();
|
|
2029
|
+
this._edgeTabElement = null;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
_onEdgeTabMouseDown = (e) => {
|
|
2033
|
+
e.preventDefault();
|
|
2034
|
+
this._edgeTabDragStartX = e.clientX;
|
|
2035
|
+
this._edgeTabDragging = false;
|
|
2036
|
+
document.addEventListener("mousemove", this._onEdgeTabMouseMove);
|
|
2037
|
+
document.addEventListener("mouseup", this._onEdgeTabMouseUp);
|
|
2038
|
+
};
|
|
2039
|
+
_onEdgeTabTouchStart = (e) => {
|
|
2040
|
+
if (e.touches.length !== 1) return;
|
|
2041
|
+
e.preventDefault();
|
|
2042
|
+
this._edgeTabDragStartX = e.touches[0].clientX;
|
|
2043
|
+
this._edgeTabDragging = false;
|
|
2044
|
+
document.addEventListener("touchmove", this._onEdgeTabTouchMove, { passive: false });
|
|
2045
|
+
document.addEventListener("touchend", this._onEdgeTabTouchEnd);
|
|
2046
|
+
};
|
|
2047
|
+
_onEdgeTabMouseMove = (e) => {
|
|
2048
|
+
const dx = Math.abs(e.clientX - this._edgeTabDragStartX);
|
|
2049
|
+
if (!this._edgeTabDragging && dx >= EDGE_TAB_DRAG_THRESHOLD_PX) {
|
|
2050
|
+
this._edgeTabDragging = true;
|
|
2051
|
+
this._promoteEdgeTabDragToResize(this._edgeTabDragStartX);
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
_onEdgeTabTouchMove = (e) => {
|
|
2055
|
+
if (e.touches.length !== 1) return;
|
|
2056
|
+
e.preventDefault();
|
|
2057
|
+
const dx = Math.abs(e.touches[0].clientX - this._edgeTabDragStartX);
|
|
2058
|
+
if (!this._edgeTabDragging && dx >= EDGE_TAB_DRAG_THRESHOLD_PX) {
|
|
2059
|
+
this._edgeTabDragging = true;
|
|
2060
|
+
this._promoteEdgeTabDragToResize(this._edgeTabDragStartX);
|
|
2061
|
+
}
|
|
2062
|
+
};
|
|
2063
|
+
_onEdgeTabMouseUp = (_e) => {
|
|
2064
|
+
document.removeEventListener("mousemove", this._onEdgeTabMouseMove);
|
|
2065
|
+
document.removeEventListener("mouseup", this._onEdgeTabMouseUp);
|
|
2066
|
+
if (!this._edgeTabDragging) this.toggleOpen();
|
|
2067
|
+
};
|
|
2068
|
+
_onEdgeTabTouchEnd = (_e) => {
|
|
2069
|
+
document.removeEventListener("touchmove", this._onEdgeTabTouchMove);
|
|
2070
|
+
document.removeEventListener("touchend", this._onEdgeTabTouchEnd);
|
|
2071
|
+
if (!this._edgeTabDragging) this.toggleOpen();
|
|
2072
|
+
};
|
|
2073
|
+
/**
|
|
2074
|
+
* Once the edge tab drag crosses the threshold, hand off to the existing
|
|
2075
|
+
* resize machinery so the panel tracks the pointer smoothly.
|
|
2076
|
+
*/
|
|
2077
|
+
_promoteEdgeTabDragToResize(startX) {
|
|
2078
|
+
if (!this._open || this._isNarrowViewport) {
|
|
2079
|
+
this._edgeTabDragging = false;
|
|
1837
2080
|
return;
|
|
1838
2081
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
setStyleValue(this, "
|
|
1844
|
-
setStyleValue(this, "
|
|
1845
|
-
|
|
1846
|
-
|
|
2082
|
+
this._resizeDragging = true;
|
|
2083
|
+
this._resizeStartX = startX;
|
|
2084
|
+
this._resizeStartWidth = this._currentPanelWidth;
|
|
2085
|
+
this._showResizeDragOverlay();
|
|
2086
|
+
if (this._agent) setStyleValue(this._agent, "transition", "none");
|
|
2087
|
+
setStyleValue(this, "transition", "none");
|
|
2088
|
+
document.body.style.transition = "none";
|
|
2089
|
+
document.body.style.cursor = "col-resize";
|
|
2090
|
+
document.body.style.userSelect = "none";
|
|
2091
|
+
document.addEventListener("mousemove", this._onResizeMove);
|
|
2092
|
+
document.addEventListener("mouseup", this._onResizeEnd);
|
|
2093
|
+
document.addEventListener("touchmove", this._onResizeTouchMove, { passive: false });
|
|
2094
|
+
document.addEventListener("touchend", this._onResizeTouchEnd);
|
|
2095
|
+
}
|
|
2096
|
+
/**
|
|
2097
|
+
* Run a rAF loop that reads the shell element's actual rendered width
|
|
2098
|
+
* and pins the edge tab to it. Used during flex layout transitions so
|
|
2099
|
+
* the tab tracks the animated flex-basis exactly, frame by frame.
|
|
2100
|
+
*/
|
|
2101
|
+
_syncEdgeTabWithPanelTransition() {
|
|
2102
|
+
if (!this._edgeTabElement) return;
|
|
2103
|
+
const start = performance.now();
|
|
2104
|
+
const maxDuration = 300;
|
|
2105
|
+
const tick = () => {
|
|
2106
|
+
if (!this._edgeTabElement || !this._open) return;
|
|
2107
|
+
if (this._isParentFlex()) {
|
|
2108
|
+
const width = this.getBoundingClientRect().width;
|
|
2109
|
+
this._positionEdgeTab(width - 1);
|
|
2110
|
+
}
|
|
2111
|
+
if (performance.now() - start < maxDuration) requestAnimationFrame(tick);
|
|
2112
|
+
};
|
|
2113
|
+
requestAnimationFrame(tick);
|
|
2114
|
+
}
|
|
2115
|
+
_showResizeDragOverlay() {
|
|
2116
|
+
if (this._resizeDragOverlay) return;
|
|
2117
|
+
const overlay = document.createElement("div");
|
|
2118
|
+
overlay.style.cssText = [
|
|
2119
|
+
"position:fixed",
|
|
2120
|
+
"top:0",
|
|
2121
|
+
"left:0",
|
|
2122
|
+
"width:100vw",
|
|
2123
|
+
"height:100vh",
|
|
2124
|
+
"cursor:col-resize",
|
|
2125
|
+
`z-index:${SHELL_Z_INDEX + 1}`,
|
|
2126
|
+
"background:transparent",
|
|
2127
|
+
"user-select:none",
|
|
2128
|
+
"-webkit-user-select:none"
|
|
2129
|
+
].join(";");
|
|
2130
|
+
document.body.appendChild(overlay);
|
|
2131
|
+
this._resizeDragOverlay = overlay;
|
|
2132
|
+
}
|
|
2133
|
+
_removeResizeDragOverlay() {
|
|
2134
|
+
if (this._resizeDragOverlay) {
|
|
2135
|
+
this._resizeDragOverlay.remove();
|
|
2136
|
+
this._resizeDragOverlay = null;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
_onResizeStart = (e) => {
|
|
2140
|
+
e.preventDefault();
|
|
2141
|
+
this._resizeDragging = true;
|
|
2142
|
+
this._resizeStartX = e.clientX;
|
|
2143
|
+
this._resizeStartWidth = this._currentPanelWidth;
|
|
2144
|
+
this._showResizeDragOverlay();
|
|
2145
|
+
if (this._agent) setStyleValue(this._agent, "transition", "none");
|
|
2146
|
+
setStyleValue(this, "transition", "none");
|
|
2147
|
+
document.body.style.transition = "none";
|
|
2148
|
+
document.body.style.cursor = "col-resize";
|
|
2149
|
+
document.body.style.userSelect = "none";
|
|
2150
|
+
document.addEventListener("mousemove", this._onResizeMove);
|
|
2151
|
+
document.addEventListener("mouseup", this._onResizeEnd);
|
|
2152
|
+
};
|
|
2153
|
+
_onResizeTouchStart = (e) => {
|
|
2154
|
+
if (e.touches.length !== 1) return;
|
|
2155
|
+
e.preventDefault();
|
|
2156
|
+
const touch = e.touches[0];
|
|
2157
|
+
this._resizeDragging = true;
|
|
2158
|
+
this._resizeStartX = touch.clientX;
|
|
2159
|
+
this._resizeStartWidth = this._currentPanelWidth;
|
|
2160
|
+
this._showResizeDragOverlay();
|
|
2161
|
+
if (this._agent) setStyleValue(this._agent, "transition", "none");
|
|
2162
|
+
setStyleValue(this, "transition", "none");
|
|
2163
|
+
document.body.style.transition = "none";
|
|
2164
|
+
document.addEventListener("touchmove", this._onResizeTouchMove, { passive: false });
|
|
2165
|
+
document.addEventListener("touchend", this._onResizeTouchEnd);
|
|
2166
|
+
};
|
|
2167
|
+
_onResizeMove = (e) => {
|
|
2168
|
+
if (!this._resizeDragging) return;
|
|
2169
|
+
const delta = this._resizeStartX - e.clientX;
|
|
2170
|
+
this._applyResizeWidth(this._resizeStartWidth + delta);
|
|
2171
|
+
};
|
|
2172
|
+
_onResizeTouchMove = (e) => {
|
|
2173
|
+
if (!this._resizeDragging || e.touches.length !== 1) return;
|
|
2174
|
+
e.preventDefault();
|
|
2175
|
+
const delta = this._resizeStartX - e.touches[0].clientX;
|
|
2176
|
+
this._applyResizeWidth(this._resizeStartWidth + delta);
|
|
2177
|
+
};
|
|
2178
|
+
_applyResizeWidth(rawWidth) {
|
|
2179
|
+
const width = Math.max(PANEL_MIN_WIDTH, Math.min(rawWidth, PANEL_MAX_WIDTH));
|
|
2180
|
+
this._currentPanelWidth = width;
|
|
2181
|
+
if (this._isParentFlex()) {
|
|
2182
|
+
setStyleValue(this, "flex", `0 0 ${width}px`);
|
|
2183
|
+
setStyleValue(this, "width", `${width}px`);
|
|
2184
|
+
setStyleValue(this, "min-width", `${width}px`);
|
|
2185
|
+
} else {
|
|
2186
|
+
if (this._agent) setStyleValue(this._agent, "width", `${width}px`);
|
|
2187
|
+
document.body.style.marginRight = `${width}px`;
|
|
2188
|
+
}
|
|
2189
|
+
if (this._resizeNubElement) {
|
|
2190
|
+
const rightPos = width - Math.floor(RESIZE_NUB_WIDTH / 2);
|
|
2191
|
+
setStyleValue(this._resizeNubElement, "right", `${rightPos}px`);
|
|
2192
|
+
}
|
|
2193
|
+
if (this._edgeTabElement) this._edgeTabElement.style.right = `${width - 1}px`;
|
|
2194
|
+
}
|
|
2195
|
+
_onResizeEnd = (e) => {
|
|
2196
|
+
this._finishResize(e.clientX);
|
|
2197
|
+
document.removeEventListener("mousemove", this._onResizeMove);
|
|
2198
|
+
document.removeEventListener("mouseup", this._onResizeEnd);
|
|
2199
|
+
};
|
|
2200
|
+
_onResizeTouchEnd = (e) => {
|
|
2201
|
+
const x = e.changedTouches[0]?.clientX ?? this._resizeStartX;
|
|
2202
|
+
this._finishResize(x);
|
|
2203
|
+
document.removeEventListener("touchmove", this._onResizeTouchMove);
|
|
2204
|
+
document.removeEventListener("touchend", this._onResizeTouchEnd);
|
|
2205
|
+
};
|
|
2206
|
+
_finishResize(endX) {
|
|
2207
|
+
this._resizeDragging = false;
|
|
2208
|
+
this._removeResizeDragOverlay();
|
|
2209
|
+
document.body.style.cursor = "";
|
|
2210
|
+
document.body.style.userSelect = "";
|
|
2211
|
+
document.body.style.transition = "";
|
|
2212
|
+
const delta = this._resizeStartX - endX;
|
|
2213
|
+
const finalWidth = this._resizeStartWidth + delta;
|
|
2214
|
+
this._currentPanelWidth = Math.max(PANEL_MIN_WIDTH, Math.min(finalWidth, PANEL_MAX_WIDTH));
|
|
2215
|
+
this._applyDisplayModeAndLayout();
|
|
1847
2216
|
}
|
|
1848
2217
|
_composePipTransform(opts) {
|
|
1849
2218
|
const parts = [];
|
|
1850
|
-
if (
|
|
2219
|
+
if (PIP_POSITION === "bottom-center") parts.push("translateX(-50%)");
|
|
1851
2220
|
if (opts.scrollHide) parts.push(`translateY(${PIP_SCROLL_HIDE_OFFSET}px)`);
|
|
1852
2221
|
return parts.length > 0 ? parts.join(" ") : "none";
|
|
1853
2222
|
}
|
|
1854
|
-
_applyAgentLayout(displayMode) {
|
|
2223
|
+
_applyAgentLayout(displayMode, prevMode) {
|
|
1855
2224
|
if (!this._agent) return;
|
|
1856
2225
|
if (displayMode === "inline" && !this._isNarrowViewport) {
|
|
1857
2226
|
this._detachPipInteractionListeners();
|
|
@@ -1859,20 +2228,32 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1859
2228
|
this._pipExpanded = false;
|
|
1860
2229
|
this._pipAnimating = false;
|
|
1861
2230
|
this._hidePipPill();
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
2231
|
+
if (this._isParentFlex()) {
|
|
2232
|
+
setStyleValue(this._agent, "position", "relative");
|
|
2233
|
+
resetStyleValue(this._agent, "top");
|
|
2234
|
+
resetStyleValue(this._agent, "right");
|
|
2235
|
+
resetStyleValue(this._agent, "bottom");
|
|
2236
|
+
resetStyleValue(this._agent, "left");
|
|
2237
|
+
setStyleValue(this._agent, "z-index", "auto");
|
|
2238
|
+
setStyleValue(this._agent, "width", "100%");
|
|
2239
|
+
setStyleValue(this._agent, "height", "100%");
|
|
2240
|
+
} else {
|
|
2241
|
+
setStyleValue(this._agent, "position", "fixed");
|
|
2242
|
+
setStyleValue(this._agent, "top", "0");
|
|
2243
|
+
setStyleValue(this._agent, "right", "0");
|
|
2244
|
+
resetStyleValue(this._agent, "bottom");
|
|
2245
|
+
resetStyleValue(this._agent, "left");
|
|
2246
|
+
setStyleValue(this._agent, "z-index", String(SHELL_Z_INDEX));
|
|
2247
|
+
setStyleValue(this._agent, "width", `${this._currentPanelWidth}px`);
|
|
2248
|
+
setStyleValue(this._agent, "height", "100dvh");
|
|
2249
|
+
setStyleValue(this._agent, "border-left", this._panelBorder());
|
|
2250
|
+
}
|
|
1867
2251
|
resetStyleValue(this._agent, "transform");
|
|
1868
2252
|
resetStyleValue(this._agent, "max-width");
|
|
1869
2253
|
resetStyleValue(this._agent, "max-height");
|
|
1870
|
-
setStyleValue(this._agent, "z-index", "auto");
|
|
1871
|
-
setStyleValue(this._agent, "width", "100%");
|
|
1872
|
-
setStyleValue(this._agent, "height", "100%");
|
|
1873
2254
|
setStyleValue(this._agent, "border-radius", "0");
|
|
1874
2255
|
setStyleValue(this._agent, "overflow", "hidden");
|
|
1875
|
-
|
|
2256
|
+
setStyleValue(this._agent, "transition", this._resizeDragging ? "none" : "width 220ms ease");
|
|
1876
2257
|
resetStyleValue(this._agent, "opacity");
|
|
1877
2258
|
resetStyleValue(this._agent, "pointer-events");
|
|
1878
2259
|
return;
|
|
@@ -1901,15 +2282,14 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1901
2282
|
resetStyleValue(this._agent, "pointer-events");
|
|
1902
2283
|
return;
|
|
1903
2284
|
}
|
|
1904
|
-
const pipPositionStyles = getPipPositionStyles(
|
|
1905
|
-
const expandedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH,
|
|
1906
|
-
const collapsedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH, Math.round(
|
|
2285
|
+
const pipPositionStyles = getPipPositionStyles(PIP_POSITION);
|
|
2286
|
+
const expandedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH, PIP_WIDTH);
|
|
2287
|
+
const collapsedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH, Math.round(PIP_WIDTH * PIP_COLLAPSED_WIDTH_RATIO));
|
|
1907
2288
|
const targetWidth = this._pipExpanded ? expandedWidth : collapsedWidth;
|
|
1908
|
-
const collapsedHeight = Math.max(PIP_MIN_COLLAPSED_HEIGHT,
|
|
1909
|
-
const hasExplicitPipHeight = this.hasAttribute("pip-height");
|
|
2289
|
+
const collapsedHeight = Math.max(PIP_MIN_COLLAPSED_HEIGHT, PIP_HEIGHT);
|
|
1910
2290
|
const measuredHeightFloor = typeof this._pipMeasuredHeight === "number" ? this._pipMeasuredHeight : 0;
|
|
1911
2291
|
const expandedHeight = Math.max(collapsedHeight, measuredHeightFloor);
|
|
1912
|
-
const collapsedPipHeight =
|
|
2292
|
+
const collapsedPipHeight = PIP_PILL_HEIGHT;
|
|
1913
2293
|
const heightDelta = expandedHeight - collapsedPipHeight;
|
|
1914
2294
|
let targetHeight;
|
|
1915
2295
|
let targetBottom;
|
|
@@ -1920,13 +2300,14 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1920
2300
|
targetHeight = collapsedPipHeight;
|
|
1921
2301
|
targetBottom = heightDelta > 0 ? `calc(${heightDelta}px + 16px + env(safe-area-inset-bottom, 0px))` : SAFE_BOTTOM;
|
|
1922
2302
|
}
|
|
2303
|
+
const enteringFromPanel = prevMode != null && prevMode !== "pip" && this._healthState === "healthy";
|
|
1923
2304
|
const composedTransform = this._composePipTransform({ scrollHide: this._pipHiddenByScroll });
|
|
2305
|
+
if (enteringFromPanel) setStyleValue(this._agent, "transition", "none");
|
|
1924
2306
|
setStyleValue(this._agent, "position", "fixed");
|
|
1925
2307
|
resetStyleValue(this._agent, "top");
|
|
1926
2308
|
setStyleValue(this._agent, "right", pipPositionStyles.right);
|
|
1927
2309
|
setStyleValue(this._agent, "left", pipPositionStyles.left);
|
|
1928
2310
|
setStyleValue(this._agent, "bottom", targetBottom);
|
|
1929
|
-
setStyleValue(this._agent, "transform", composedTransform);
|
|
1930
2311
|
setStyleValue(this._agent, "z-index", String(SHELL_Z_INDEX));
|
|
1931
2312
|
setStyleValue(this._agent, "width", `min(${targetWidth}px, calc(100vw - 24px))`);
|
|
1932
2313
|
setStyleValue(this._agent, "height", `${targetHeight}px`);
|
|
@@ -1934,7 +2315,22 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1934
2315
|
setStyleValue(this._agent, "max-width", "100vw");
|
|
1935
2316
|
setStyleValue(this._agent, "border-radius", "16px");
|
|
1936
2317
|
setStyleValue(this._agent, "overflow", "hidden");
|
|
1937
|
-
|
|
2318
|
+
resetStyleValue(this._agent, "border-left");
|
|
2319
|
+
if (enteringFromPanel) {
|
|
2320
|
+
setStyleValue(this._agent, "transform", this._composePipTransform({ scrollHide: true }));
|
|
2321
|
+
setStyleValue(this._agent, "opacity", "0");
|
|
2322
|
+
this._agent.offsetHeight;
|
|
2323
|
+
requestAnimationFrame(() => {
|
|
2324
|
+
if (!this._agent) return;
|
|
2325
|
+
setStyleValue(this._agent, "transition", PIP_MORPH_TRANSITION);
|
|
2326
|
+
setStyleValue(this._agent, "transform", composedTransform);
|
|
2327
|
+
setStyleValue(this._agent, "opacity", "1");
|
|
2328
|
+
});
|
|
2329
|
+
} else {
|
|
2330
|
+
setStyleValue(this._agent, "transform", composedTransform);
|
|
2331
|
+
setStyleValue(this._agent, "transition", this._healthState === "healthy" ? PIP_MORPH_TRANSITION : "none");
|
|
2332
|
+
resetStyleValue(this._agent, "opacity");
|
|
2333
|
+
}
|
|
1938
2334
|
if (this._healthState === "healthy") {
|
|
1939
2335
|
const pill = this._ensurePipPill();
|
|
1940
2336
|
if (!pill.parentNode) this.appendChild(pill);
|
|
@@ -1943,6 +2339,16 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1943
2339
|
this._attachScrollHideListener();
|
|
1944
2340
|
this._attachSwipeGestureListeners();
|
|
1945
2341
|
this._attachWheelFallbackListener();
|
|
2342
|
+
if (enteringFromPanel) {
|
|
2343
|
+
pill.style.opacity = "0";
|
|
2344
|
+
pill.style.pointerEvents = "none";
|
|
2345
|
+
pill.style.transition = "none";
|
|
2346
|
+
requestAnimationFrame(() => {
|
|
2347
|
+
pill.style.transition = PIP_MORPH_TRANSITION;
|
|
2348
|
+
pill.style.opacity = "1";
|
|
2349
|
+
pill.style.pointerEvents = "";
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
1946
2352
|
this._applyPipVisualState();
|
|
1947
2353
|
}
|
|
1948
2354
|
}
|
|
@@ -1952,7 +2358,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1952
2358
|
pill.setAttribute("role", "button");
|
|
1953
2359
|
pill.setAttribute("aria-label", "Open assistant");
|
|
1954
2360
|
pill.setAttribute("tabindex", "0");
|
|
1955
|
-
const pipPositionStyles = getPipPositionStyles(
|
|
2361
|
+
const pipPositionStyles = getPipPositionStyles(PIP_POSITION);
|
|
1956
2362
|
pill.style.cssText = [
|
|
1957
2363
|
"position:fixed",
|
|
1958
2364
|
`left:${pipPositionStyles.left}`,
|
|
@@ -2338,6 +2744,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2338
2744
|
this._pipExpanded = false;
|
|
2339
2745
|
this._pipAnimating = false;
|
|
2340
2746
|
this._hidePipPill();
|
|
2747
|
+
this._hideEdgeTab();
|
|
2341
2748
|
if (this._agent) this._agent.removeAttribute("display-mode");
|
|
2342
2749
|
this._resetHostLayoutStyles();
|
|
2343
2750
|
this._resetAgentLayoutStyles();
|
|
@@ -2383,7 +2790,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2383
2790
|
this._revealFrameId = null;
|
|
2384
2791
|
setStyleValue(this, "opacity", "1");
|
|
2385
2792
|
window.setTimeout(() => {
|
|
2386
|
-
|
|
2793
|
+
resetStyleValue(this, "will-change");
|
|
2387
2794
|
}, SHELL_REVEAL_FADE_MS);
|
|
2388
2795
|
});
|
|
2389
2796
|
}
|
|
@@ -2435,6 +2842,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2435
2842
|
return;
|
|
2436
2843
|
}
|
|
2437
2844
|
this._open = nextOpen;
|
|
2845
|
+
if (!nextOpen) this._currentPanelWidth = PANEL_WIDTH;
|
|
2438
2846
|
if (options.reflect) this._reflectOpenAttribute(nextOpen);
|
|
2439
2847
|
this._applyDisplayModeAndLayout();
|
|
2440
2848
|
if (options.emit) this.dispatchEvent(new CustomEvent(SHELL_OPEN_CHANGE_EVENT, {
|