@mcp-b/char 0.1.2 → 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/LICENSE +23 -0
- 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 +702 -279
- 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 +692 -279
- package/dist/shell-component.js.map +1 -1
- package/dist/shell-standalone.iife.js +2928 -0
- package/dist/shell-standalone.iife.js.map +1 -0
- 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 +28 -87
- 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 +28 -87
- package/dist/web-component.js.map +1 -1
- package/package.json +13 -13
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,17 +1075,15 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1102
1075
|
*/
|
|
1103
1076
|
_postAuth(iframeOrigin) {
|
|
1104
1077
|
if (!this._pendingAuth || !this._iframe?.contentWindow) return;
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
} : {
|
|
1078
|
+
if (!this._pendingAuth.publishableKey) {
|
|
1079
|
+
console.error("[Char] _postAuth called but _pendingAuth has no publishableKey");
|
|
1080
|
+
this._pendingAuth = null;
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
const message = {
|
|
1112
1084
|
type: "char-auth",
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
organizationId: this._pendingAuth.organizationId
|
|
1085
|
+
publishableKey: this._pendingAuth.publishableKey,
|
|
1086
|
+
idToken: this._pendingAuth.idToken
|
|
1116
1087
|
};
|
|
1117
1088
|
this._iframe.contentWindow.postMessage(message, iframeOrigin);
|
|
1118
1089
|
}
|
|
@@ -1259,36 +1230,6 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1259
1230
|
this._containerResizeObserver.observe(this);
|
|
1260
1231
|
}
|
|
1261
1232
|
/**
|
|
1262
|
-
* Resolve devMode from property (React 19) or attribute.
|
|
1263
|
-
*
|
|
1264
|
-
* @returns Parsed dev-mode configuration when valid.
|
|
1265
|
-
*/
|
|
1266
|
-
_resolveDevMode() {
|
|
1267
|
-
if (this.devMode && typeof this.devMode === "object") return this.devMode;
|
|
1268
|
-
const devModeAttr = this.getAttribute("dev-mode");
|
|
1269
|
-
if (!devModeAttr) return void 0;
|
|
1270
|
-
try {
|
|
1271
|
-
const parsed = JSON.parse(devModeAttr);
|
|
1272
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1273
|
-
const msg = `dev-mode attribute must be a JSON object, got: ${Array.isArray(parsed) ? "array" : typeof parsed}`;
|
|
1274
|
-
console.warn(`[Char] ${msg}`);
|
|
1275
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1276
|
-
return;
|
|
1277
|
-
}
|
|
1278
|
-
const obj = parsed;
|
|
1279
|
-
return {
|
|
1280
|
-
anthropicApiKey: typeof obj.anthropicApiKey === "string" ? obj.anthropicApiKey : void 0,
|
|
1281
|
-
openaiApiKey: typeof obj.openaiApiKey === "string" ? obj.openaiApiKey : void 0,
|
|
1282
|
-
useLocalApi: typeof obj.useLocalApi === "boolean" ? obj.useLocalApi : void 0
|
|
1283
|
-
};
|
|
1284
|
-
} catch (e) {
|
|
1285
|
-
const msg = `Failed to parse dev-mode attribute as JSON: ${e instanceof Error ? e.message : String(e)}`;
|
|
1286
|
-
console.warn(`[Char] ${msg}`);
|
|
1287
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1288
|
-
return;
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
/**
|
|
1292
1233
|
* Resolve apiBase override from property (React 19) or attribute.
|
|
1293
1234
|
*
|
|
1294
1235
|
* @returns Sanitized API base URL without trailing slash.
|
|
@@ -1317,22 +1258,36 @@ registerChar();
|
|
|
1317
1258
|
//#region src/shell-component.ts
|
|
1318
1259
|
const SHELL_OPEN_CHANGE_EVENT = "char-shell-open-change";
|
|
1319
1260
|
const SHELL_Z_INDEX = 2147483e3;
|
|
1320
|
-
const
|
|
1321
|
-
const
|
|
1322
|
-
const
|
|
1323
|
-
const
|
|
1324
|
-
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";
|
|
1325
1266
|
const SHELL_HEALTH_TIMEOUT_MS = 1e4;
|
|
1326
|
-
const PANEL_BORDER = "1px solid rgba(15, 23, 42, 0.12)";
|
|
1327
1267
|
const SAFE_BOTTOM = "calc(16px + env(safe-area-inset-bottom, 0px))";
|
|
1328
|
-
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";
|
|
1329
1284
|
const SHELL_REVEAL_FADE_MS = 280;
|
|
1330
1285
|
const PIP_MIN_COLLAPSED_WIDTH = 70;
|
|
1331
1286
|
const PIP_MIN_COLLAPSED_HEIGHT = 30;
|
|
1332
1287
|
const PIP_MAX_MEASURED_HEIGHT = 320;
|
|
1333
1288
|
const PIP_COLLAPSE_DELAY_MS = 400;
|
|
1334
1289
|
const PIP_PILL_HEIGHT = 40;
|
|
1335
|
-
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";
|
|
1336
1291
|
const PIP_MORPH_DURATION_MS = 280;
|
|
1337
1292
|
const PIP_SCROLL_HIDE_OFFSET = 100;
|
|
1338
1293
|
const PIP_COLLAPSED_WIDTH_RATIO = .75;
|
|
@@ -1340,23 +1295,14 @@ const PIP_SWIPE_THRESHOLD = 50;
|
|
|
1340
1295
|
const PIP_SCROLL_DIRECTION_THRESHOLD = 40;
|
|
1341
1296
|
const WHEEL_DELTA_LINE_MULTIPLIER = 15;
|
|
1342
1297
|
const WHEEL_DELTA_PAGE_MULTIPLIER = 300;
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
if (value === null || value.trim() === "") return fallback;
|
|
1352
|
-
const parsed = Number.parseInt(value, 10);
|
|
1353
|
-
if (!Number.isFinite(parsed)) return fallback;
|
|
1354
|
-
return normalizePositiveInteger(parsed, fallback);
|
|
1355
|
-
}
|
|
1356
|
-
function normalizePipPosition(value) {
|
|
1357
|
-
if (value === "bottom-right" || value === "bottom-left") return value;
|
|
1358
|
-
return DEFAULT_PIP_POSITION;
|
|
1359
|
-
}
|
|
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>`;
|
|
1360
1306
|
function getPipPositionStyles(position) {
|
|
1361
1307
|
if (position === "bottom-right") return {
|
|
1362
1308
|
left: "auto",
|
|
@@ -1413,25 +1359,28 @@ function sanitizeShellHostContext(hostContext) {
|
|
|
1413
1359
|
var CharAgentShellElement = class extends HTMLElement {
|
|
1414
1360
|
static observedAttributes = [
|
|
1415
1361
|
"open",
|
|
1416
|
-
"fullscreen-breakpoint",
|
|
1417
|
-
"panel-width",
|
|
1418
|
-
"pip-position",
|
|
1419
|
-
"pip-width",
|
|
1420
|
-
"pip-height",
|
|
1421
|
-
"dev-mode",
|
|
1422
1362
|
"enable-debug-tools",
|
|
1423
1363
|
"api-base"
|
|
1424
1364
|
];
|
|
1425
1365
|
_agent = null;
|
|
1426
1366
|
_open = false;
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
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;
|
|
1432
1378
|
_isNarrowViewport = false;
|
|
1433
1379
|
_mediaQuery = null;
|
|
1434
1380
|
_mediaQueryHandler = null;
|
|
1381
|
+
_themeObserver = null;
|
|
1382
|
+
_themeMediaQuery = null;
|
|
1383
|
+
_themeMediaHandler = null;
|
|
1435
1384
|
_pendingConnectOptions = null;
|
|
1436
1385
|
_pendingHostContext = {};
|
|
1437
1386
|
_isApplyingOpenAttribute = false;
|
|
@@ -1449,6 +1398,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1449
1398
|
_pipAnimationTimeoutId = null;
|
|
1450
1399
|
_pendingPipMeasuredHeight = null;
|
|
1451
1400
|
_tapOutsideListener = null;
|
|
1401
|
+
_lastAppliedDisplayMode = null;
|
|
1452
1402
|
_pipHiddenByScroll = false;
|
|
1453
1403
|
_scrollListener = null;
|
|
1454
1404
|
_scrollRafId = null;
|
|
@@ -1468,75 +1418,36 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1468
1418
|
set open(value) {
|
|
1469
1419
|
this.setOpen(value);
|
|
1470
1420
|
}
|
|
1471
|
-
get fullscreenBreakpoint() {
|
|
1472
|
-
return this._fullscreenBreakpoint;
|
|
1473
|
-
}
|
|
1474
|
-
set fullscreenBreakpoint(value) {
|
|
1475
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1476
|
-
if (normalized === this._fullscreenBreakpoint) return;
|
|
1477
|
-
this._fullscreenBreakpoint = normalized;
|
|
1478
|
-
this.setAttribute("fullscreen-breakpoint", String(normalized));
|
|
1479
|
-
this._setupViewportObserver();
|
|
1480
|
-
this._applyDisplayModeAndLayout();
|
|
1481
|
-
}
|
|
1482
|
-
get panelWidth() {
|
|
1483
|
-
return this._panelWidth;
|
|
1484
|
-
}
|
|
1485
|
-
set panelWidth(value) {
|
|
1486
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PANEL_WIDTH);
|
|
1487
|
-
if (normalized === this._panelWidth) return;
|
|
1488
|
-
this._panelWidth = normalized;
|
|
1489
|
-
this.setAttribute("panel-width", String(normalized));
|
|
1490
|
-
this._applyDisplayModeAndLayout();
|
|
1491
|
-
}
|
|
1492
|
-
get pipWidth() {
|
|
1493
|
-
return this._pipWidth;
|
|
1494
|
-
}
|
|
1495
|
-
set pipWidth(value) {
|
|
1496
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PIP_WIDTH);
|
|
1497
|
-
if (normalized === this._pipWidth) return;
|
|
1498
|
-
this._pipWidth = normalized;
|
|
1499
|
-
this.setAttribute("pip-width", String(normalized));
|
|
1500
|
-
this._applyDisplayModeAndLayout();
|
|
1501
|
-
}
|
|
1502
|
-
get pipHeight() {
|
|
1503
|
-
return this._pipHeight;
|
|
1504
|
-
}
|
|
1505
|
-
set pipHeight(value) {
|
|
1506
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PIP_HEIGHT);
|
|
1507
|
-
if (normalized === this._pipHeight) return;
|
|
1508
|
-
this._pipHeight = normalized;
|
|
1509
|
-
this.setAttribute("pip-height", String(normalized));
|
|
1510
|
-
this._applyDisplayModeAndLayout();
|
|
1511
|
-
}
|
|
1512
|
-
get pipPosition() {
|
|
1513
|
-
return this._pipPosition;
|
|
1514
|
-
}
|
|
1515
|
-
set pipPosition(value) {
|
|
1516
|
-
const normalized = normalizePipPosition(value);
|
|
1517
|
-
if (normalized === this._pipPosition) return;
|
|
1518
|
-
this._pipPosition = normalized;
|
|
1519
|
-
this.setAttribute("pip-position", normalized);
|
|
1520
|
-
this._applyDisplayModeAndLayout();
|
|
1521
|
-
}
|
|
1522
1421
|
connectedCallback() {
|
|
1523
1422
|
this._upgradeProperty("open");
|
|
1524
|
-
this._upgradeProperty("fullscreenBreakpoint");
|
|
1525
|
-
this._upgradeProperty("panelWidth");
|
|
1526
|
-
this._upgradeProperty("pipPosition");
|
|
1527
|
-
this._upgradeProperty("pipWidth");
|
|
1528
|
-
this._upgradeProperty("pipHeight");
|
|
1529
|
-
this._upgradeProperty("devMode");
|
|
1530
1423
|
this._upgradeProperty("apiBase");
|
|
1531
1424
|
registerChar();
|
|
1532
1425
|
this._ensureAgent();
|
|
1533
1426
|
this._readConfigFromAttributes();
|
|
1534
1427
|
this._syncForwardedAttributes();
|
|
1535
1428
|
this._setupViewportObserver();
|
|
1429
|
+
this._setupThemeObserver();
|
|
1536
1430
|
this._applyDisplayModeAndLayout();
|
|
1537
1431
|
this._flushPendingConnectAndContext();
|
|
1538
1432
|
}
|
|
1539
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
|
+
}
|
|
1540
1451
|
this._stopHealthWait();
|
|
1541
1452
|
this._cancelRevealFade();
|
|
1542
1453
|
this._teardownViewportObserver();
|
|
@@ -1565,48 +1476,6 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1565
1476
|
});
|
|
1566
1477
|
return;
|
|
1567
1478
|
}
|
|
1568
|
-
case "fullscreen-breakpoint": {
|
|
1569
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1570
|
-
if (next !== this._fullscreenBreakpoint) {
|
|
1571
|
-
this._fullscreenBreakpoint = next;
|
|
1572
|
-
this._setupViewportObserver();
|
|
1573
|
-
this._applyDisplayModeAndLayout();
|
|
1574
|
-
}
|
|
1575
|
-
return;
|
|
1576
|
-
}
|
|
1577
|
-
case "panel-width": {
|
|
1578
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PANEL_WIDTH);
|
|
1579
|
-
if (next !== this._panelWidth) {
|
|
1580
|
-
this._panelWidth = next;
|
|
1581
|
-
this._applyDisplayModeAndLayout();
|
|
1582
|
-
}
|
|
1583
|
-
return;
|
|
1584
|
-
}
|
|
1585
|
-
case "pip-width": {
|
|
1586
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PIP_WIDTH);
|
|
1587
|
-
if (next !== this._pipWidth) {
|
|
1588
|
-
this._pipWidth = next;
|
|
1589
|
-
this._applyDisplayModeAndLayout();
|
|
1590
|
-
}
|
|
1591
|
-
return;
|
|
1592
|
-
}
|
|
1593
|
-
case "pip-height": {
|
|
1594
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PIP_HEIGHT);
|
|
1595
|
-
if (next !== this._pipHeight) {
|
|
1596
|
-
this._pipHeight = next;
|
|
1597
|
-
this._applyDisplayModeAndLayout();
|
|
1598
|
-
}
|
|
1599
|
-
return;
|
|
1600
|
-
}
|
|
1601
|
-
case "pip-position": {
|
|
1602
|
-
const next = normalizePipPosition(newValue);
|
|
1603
|
-
if (next !== this._pipPosition) {
|
|
1604
|
-
this._pipPosition = next;
|
|
1605
|
-
this._applyDisplayModeAndLayout();
|
|
1606
|
-
}
|
|
1607
|
-
return;
|
|
1608
|
-
}
|
|
1609
|
-
case "dev-mode":
|
|
1610
1479
|
case "enable-debug-tools":
|
|
1611
1480
|
case "api-base":
|
|
1612
1481
|
this._syncForwardedAttributes();
|
|
@@ -1616,7 +1485,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1616
1485
|
/**
|
|
1617
1486
|
* Connects shell authentication and starts availability health checks.
|
|
1618
1487
|
*
|
|
1619
|
-
* @param options - Authentication payload
|
|
1488
|
+
* @param options - Authentication payload (`publishableKey` + optional `idToken`).
|
|
1620
1489
|
* @returns `true` when connect is accepted by the inner element.
|
|
1621
1490
|
*/
|
|
1622
1491
|
connect(options) {
|
|
@@ -1724,15 +1593,10 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1724
1593
|
this._attachForwardedListeners();
|
|
1725
1594
|
}
|
|
1726
1595
|
_primeAgentBootConfig(agent) {
|
|
1727
|
-
for (const name of [
|
|
1728
|
-
"dev-mode",
|
|
1729
|
-
"enable-debug-tools",
|
|
1730
|
-
"api-base"
|
|
1731
|
-
]) {
|
|
1596
|
+
for (const name of ["enable-debug-tools", "api-base"]) {
|
|
1732
1597
|
const value = this.getAttribute(name);
|
|
1733
1598
|
if (value !== null) agent.setAttribute(name, value);
|
|
1734
1599
|
}
|
|
1735
|
-
agent.devMode = this.devMode && typeof this.devMode === "object" ? this.devMode : void 0;
|
|
1736
1600
|
agent.apiBase = typeof this.apiBase === "string" ? this.apiBase : void 0;
|
|
1737
1601
|
}
|
|
1738
1602
|
_attachForwardedListeners() {
|
|
@@ -1755,29 +1619,19 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1755
1619
|
}
|
|
1756
1620
|
_readConfigFromAttributes() {
|
|
1757
1621
|
this._open = parseBooleanAttribute(this.getAttribute("open"));
|
|
1758
|
-
this._fullscreenBreakpoint = parsePositiveIntegerAttribute(this.getAttribute("fullscreen-breakpoint"), DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1759
|
-
this._panelWidth = parsePositiveIntegerAttribute(this.getAttribute("panel-width"), DEFAULT_PANEL_WIDTH);
|
|
1760
|
-
this._pipWidth = parsePositiveIntegerAttribute(this.getAttribute("pip-width"), DEFAULT_PIP_WIDTH);
|
|
1761
|
-
this._pipHeight = parsePositiveIntegerAttribute(this.getAttribute("pip-height"), DEFAULT_PIP_HEIGHT);
|
|
1762
|
-
this._pipPosition = normalizePipPosition(this.getAttribute("pip-position"));
|
|
1763
1622
|
}
|
|
1764
1623
|
_syncForwardedAttributes() {
|
|
1765
1624
|
if (!this._agent) return;
|
|
1766
|
-
for (const name of [
|
|
1767
|
-
"dev-mode",
|
|
1768
|
-
"enable-debug-tools",
|
|
1769
|
-
"api-base"
|
|
1770
|
-
]) {
|
|
1625
|
+
for (const name of ["enable-debug-tools", "api-base"]) {
|
|
1771
1626
|
const value = this.getAttribute(name);
|
|
1772
1627
|
if (value === null) this._agent.removeAttribute(name);
|
|
1773
1628
|
else this._agent.setAttribute(name, value);
|
|
1774
1629
|
}
|
|
1775
|
-
this._agent.devMode = this.devMode && typeof this.devMode === "object" ? this.devMode : void 0;
|
|
1776
1630
|
this._agent.apiBase = typeof this.apiBase === "string" ? this.apiBase : void 0;
|
|
1777
1631
|
}
|
|
1778
1632
|
_setupViewportObserver() {
|
|
1779
1633
|
if (typeof window === "undefined") return;
|
|
1780
|
-
const query = `(max-width: ${
|
|
1634
|
+
const query = `(max-width: ${FULLSCREEN_BREAKPOINT - 1}px)`;
|
|
1781
1635
|
if (this._mediaQuery?.media === query) {
|
|
1782
1636
|
this._isNarrowViewport = this._mediaQuery.matches;
|
|
1783
1637
|
return;
|
|
@@ -1815,38 +1669,558 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1815
1669
|
if (displayMode !== "pip") {
|
|
1816
1670
|
if (this._pipMeasuredHeight !== null) this._pipMeasuredHeight = null;
|
|
1817
1671
|
}
|
|
1672
|
+
const prevMode = this._lastAppliedDisplayMode;
|
|
1673
|
+
this._lastAppliedDisplayMode = displayMode;
|
|
1818
1674
|
this._agent.setAttribute("display-mode", displayMode);
|
|
1819
1675
|
this._applyHostLayout(displayMode);
|
|
1820
|
-
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
|
+
}
|
|
1821
1741
|
}
|
|
1822
1742
|
_applyHostLayout(displayMode) {
|
|
1743
|
+
const useFlexLayout = this._isParentFlex();
|
|
1823
1744
|
if (displayMode === "inline" && !this._isNarrowViewport) {
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
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;
|
|
1832
2080
|
return;
|
|
1833
2081
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
setStyleValue(this, "
|
|
1839
|
-
setStyleValue(this, "
|
|
1840
|
-
|
|
1841
|
-
|
|
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();
|
|
1842
2216
|
}
|
|
1843
2217
|
_composePipTransform(opts) {
|
|
1844
2218
|
const parts = [];
|
|
1845
|
-
if (
|
|
2219
|
+
if (PIP_POSITION === "bottom-center") parts.push("translateX(-50%)");
|
|
1846
2220
|
if (opts.scrollHide) parts.push(`translateY(${PIP_SCROLL_HIDE_OFFSET}px)`);
|
|
1847
2221
|
return parts.length > 0 ? parts.join(" ") : "none";
|
|
1848
2222
|
}
|
|
1849
|
-
_applyAgentLayout(displayMode) {
|
|
2223
|
+
_applyAgentLayout(displayMode, prevMode) {
|
|
1850
2224
|
if (!this._agent) return;
|
|
1851
2225
|
if (displayMode === "inline" && !this._isNarrowViewport) {
|
|
1852
2226
|
this._detachPipInteractionListeners();
|
|
@@ -1854,20 +2228,32 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1854
2228
|
this._pipExpanded = false;
|
|
1855
2229
|
this._pipAnimating = false;
|
|
1856
2230
|
this._hidePipPill();
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
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
|
+
}
|
|
1862
2251
|
resetStyleValue(this._agent, "transform");
|
|
1863
2252
|
resetStyleValue(this._agent, "max-width");
|
|
1864
2253
|
resetStyleValue(this._agent, "max-height");
|
|
1865
|
-
setStyleValue(this._agent, "z-index", "auto");
|
|
1866
|
-
setStyleValue(this._agent, "width", "100%");
|
|
1867
|
-
setStyleValue(this._agent, "height", "100%");
|
|
1868
2254
|
setStyleValue(this._agent, "border-radius", "0");
|
|
1869
2255
|
setStyleValue(this._agent, "overflow", "hidden");
|
|
1870
|
-
|
|
2256
|
+
setStyleValue(this._agent, "transition", this._resizeDragging ? "none" : "width 220ms ease");
|
|
1871
2257
|
resetStyleValue(this._agent, "opacity");
|
|
1872
2258
|
resetStyleValue(this._agent, "pointer-events");
|
|
1873
2259
|
return;
|
|
@@ -1896,15 +2282,14 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1896
2282
|
resetStyleValue(this._agent, "pointer-events");
|
|
1897
2283
|
return;
|
|
1898
2284
|
}
|
|
1899
|
-
const pipPositionStyles = getPipPositionStyles(
|
|
1900
|
-
const expandedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH,
|
|
1901
|
-
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));
|
|
1902
2288
|
const targetWidth = this._pipExpanded ? expandedWidth : collapsedWidth;
|
|
1903
|
-
const collapsedHeight = Math.max(PIP_MIN_COLLAPSED_HEIGHT,
|
|
1904
|
-
const hasExplicitPipHeight = this.hasAttribute("pip-height");
|
|
2289
|
+
const collapsedHeight = Math.max(PIP_MIN_COLLAPSED_HEIGHT, PIP_HEIGHT);
|
|
1905
2290
|
const measuredHeightFloor = typeof this._pipMeasuredHeight === "number" ? this._pipMeasuredHeight : 0;
|
|
1906
2291
|
const expandedHeight = Math.max(collapsedHeight, measuredHeightFloor);
|
|
1907
|
-
const collapsedPipHeight =
|
|
2292
|
+
const collapsedPipHeight = PIP_PILL_HEIGHT;
|
|
1908
2293
|
const heightDelta = expandedHeight - collapsedPipHeight;
|
|
1909
2294
|
let targetHeight;
|
|
1910
2295
|
let targetBottom;
|
|
@@ -1915,13 +2300,14 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1915
2300
|
targetHeight = collapsedPipHeight;
|
|
1916
2301
|
targetBottom = heightDelta > 0 ? `calc(${heightDelta}px + 16px + env(safe-area-inset-bottom, 0px))` : SAFE_BOTTOM;
|
|
1917
2302
|
}
|
|
2303
|
+
const enteringFromPanel = prevMode != null && prevMode !== "pip" && this._healthState === "healthy";
|
|
1918
2304
|
const composedTransform = this._composePipTransform({ scrollHide: this._pipHiddenByScroll });
|
|
2305
|
+
if (enteringFromPanel) setStyleValue(this._agent, "transition", "none");
|
|
1919
2306
|
setStyleValue(this._agent, "position", "fixed");
|
|
1920
2307
|
resetStyleValue(this._agent, "top");
|
|
1921
2308
|
setStyleValue(this._agent, "right", pipPositionStyles.right);
|
|
1922
2309
|
setStyleValue(this._agent, "left", pipPositionStyles.left);
|
|
1923
2310
|
setStyleValue(this._agent, "bottom", targetBottom);
|
|
1924
|
-
setStyleValue(this._agent, "transform", composedTransform);
|
|
1925
2311
|
setStyleValue(this._agent, "z-index", String(SHELL_Z_INDEX));
|
|
1926
2312
|
setStyleValue(this._agent, "width", `min(${targetWidth}px, calc(100vw - 24px))`);
|
|
1927
2313
|
setStyleValue(this._agent, "height", `${targetHeight}px`);
|
|
@@ -1929,7 +2315,22 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1929
2315
|
setStyleValue(this._agent, "max-width", "100vw");
|
|
1930
2316
|
setStyleValue(this._agent, "border-radius", "16px");
|
|
1931
2317
|
setStyleValue(this._agent, "overflow", "hidden");
|
|
1932
|
-
|
|
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
|
+
}
|
|
1933
2334
|
if (this._healthState === "healthy") {
|
|
1934
2335
|
const pill = this._ensurePipPill();
|
|
1935
2336
|
if (!pill.parentNode) this.appendChild(pill);
|
|
@@ -1938,6 +2339,16 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1938
2339
|
this._attachScrollHideListener();
|
|
1939
2340
|
this._attachSwipeGestureListeners();
|
|
1940
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
|
+
}
|
|
1941
2352
|
this._applyPipVisualState();
|
|
1942
2353
|
}
|
|
1943
2354
|
}
|
|
@@ -1947,7 +2358,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1947
2358
|
pill.setAttribute("role", "button");
|
|
1948
2359
|
pill.setAttribute("aria-label", "Open assistant");
|
|
1949
2360
|
pill.setAttribute("tabindex", "0");
|
|
1950
|
-
const pipPositionStyles = getPipPositionStyles(
|
|
2361
|
+
const pipPositionStyles = getPipPositionStyles(PIP_POSITION);
|
|
1951
2362
|
pill.style.cssText = [
|
|
1952
2363
|
"position:fixed",
|
|
1953
2364
|
`left:${pipPositionStyles.left}`,
|
|
@@ -2333,6 +2744,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2333
2744
|
this._pipExpanded = false;
|
|
2334
2745
|
this._pipAnimating = false;
|
|
2335
2746
|
this._hidePipPill();
|
|
2747
|
+
this._hideEdgeTab();
|
|
2336
2748
|
if (this._agent) this._agent.removeAttribute("display-mode");
|
|
2337
2749
|
this._resetHostLayoutStyles();
|
|
2338
2750
|
this._resetAgentLayoutStyles();
|
|
@@ -2378,7 +2790,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2378
2790
|
this._revealFrameId = null;
|
|
2379
2791
|
setStyleValue(this, "opacity", "1");
|
|
2380
2792
|
window.setTimeout(() => {
|
|
2381
|
-
|
|
2793
|
+
resetStyleValue(this, "will-change");
|
|
2382
2794
|
}, SHELL_REVEAL_FADE_MS);
|
|
2383
2795
|
});
|
|
2384
2796
|
}
|
|
@@ -2430,6 +2842,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2430
2842
|
return;
|
|
2431
2843
|
}
|
|
2432
2844
|
this._open = nextOpen;
|
|
2845
|
+
if (!nextOpen) this._currentPanelWidth = PANEL_WIDTH;
|
|
2433
2846
|
if (options.reflect) this._reflectOpenAttribute(nextOpen);
|
|
2434
2847
|
this._applyDisplayModeAndLayout();
|
|
2435
2848
|
if (options.emit) this.dispatchEvent(new CustomEvent(SHELL_OPEN_CHANGE_EVENT, {
|