@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/index.js
CHANGED
|
@@ -485,8 +485,7 @@ function mergeHostContext(current, patch) {
|
|
|
485
485
|
styles: mergeStyles(previous.styles, patch.styles),
|
|
486
486
|
containerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),
|
|
487
487
|
deviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),
|
|
488
|
-
safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
|
|
489
|
-
hostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities)
|
|
488
|
+
safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
|
|
490
489
|
};
|
|
491
490
|
}
|
|
492
491
|
|
|
@@ -521,25 +520,6 @@ const DISPLAY_MODES = [
|
|
|
521
520
|
"pip"
|
|
522
521
|
];
|
|
523
522
|
/**
|
|
524
|
-
* Normalizes an API key by trimming whitespace and collapsing empty strings.
|
|
525
|
-
*/
|
|
526
|
-
function normalizeApiKey(value) {
|
|
527
|
-
const trimmed = value?.trim();
|
|
528
|
-
return trimmed ? trimmed : void 0;
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Produces a sanitized dev-mode configuration and drops empty payloads.
|
|
532
|
-
*/
|
|
533
|
-
function resolveDevMode(devMode) {
|
|
534
|
-
if (!devMode) return void 0;
|
|
535
|
-
const normalized = {
|
|
536
|
-
anthropicApiKey: normalizeApiKey(devMode.anthropicApiKey),
|
|
537
|
-
openaiApiKey: normalizeApiKey(devMode.openaiApiKey),
|
|
538
|
-
useLocalApi: devMode.useLocalApi === true ? true : void 0
|
|
539
|
-
};
|
|
540
|
-
return !!normalized.anthropicApiKey || !!normalized.openaiApiKey || !!normalized.useLocalApi ? normalized : void 0;
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
523
|
* Runtime guard for the message envelope emitted by the iframe.
|
|
544
524
|
*/
|
|
545
525
|
function isCharIframeMessageData(value) {
|
|
@@ -678,7 +658,7 @@ function getDeviceCapabilities() {
|
|
|
678
658
|
*/
|
|
679
659
|
var CharAgentElement = class extends HTMLElement {
|
|
680
660
|
static observedAttributes = [
|
|
681
|
-
"
|
|
661
|
+
"publishable-key",
|
|
682
662
|
"enable-debug-tools",
|
|
683
663
|
"display-mode",
|
|
684
664
|
"api-base"
|
|
@@ -728,12 +708,12 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
728
708
|
return;
|
|
729
709
|
}
|
|
730
710
|
if (!this.style.display) this.style.display = "block";
|
|
731
|
-
const
|
|
732
|
-
const apiBase = this._resolveApiBaseOverride() ?? (resolvedDevMode?.useLocalApi ? window.location.origin : WEBMCP_PRODUCTION_API_BASE);
|
|
711
|
+
const apiBase = this._resolveApiBaseOverride() ?? WEBMCP_PRODUCTION_API_BASE;
|
|
733
712
|
const iframeOrigin = this._resolveIframeOrigin(apiBase);
|
|
734
713
|
const iframe = this._createIframe(apiBase);
|
|
735
714
|
this._iframe = iframe;
|
|
736
|
-
|
|
715
|
+
const publishableKey = this.publishableKey ?? this.getAttribute("publishable-key") ?? void 0;
|
|
716
|
+
if (publishableKey) this._pendingAuth = { publishableKey };
|
|
737
717
|
this._proxy = new CharIframeProxy(iframe, iframeOrigin);
|
|
738
718
|
this._proxy.start();
|
|
739
719
|
this._messageListener = this._createMessageListener(iframe, iframeOrigin);
|
|
@@ -942,15 +922,21 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
942
922
|
/**
|
|
943
923
|
* Reacts to observed attribute updates after iframe readiness.
|
|
944
924
|
* Attributes that affect iframe boot configuration are intentionally ignored
|
|
945
|
-
* after mount (`
|
|
925
|
+
* after mount (`enable-debug-tools`, `api-base`).
|
|
946
926
|
*/
|
|
947
927
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
948
928
|
if (!this._iframe || !this._iframeReady) return;
|
|
949
929
|
switch (name) {
|
|
930
|
+
case "publishable-key":
|
|
931
|
+
if (newValue) {
|
|
932
|
+
this._pendingAuth = { publishableKey: newValue };
|
|
933
|
+
const iframeOrigin = this._getIframeOrigin();
|
|
934
|
+
if (iframeOrigin) this._postAuth(iframeOrigin);
|
|
935
|
+
}
|
|
936
|
+
break;
|
|
950
937
|
case "display-mode":
|
|
951
938
|
this.setHostContext({ displayMode: resolveDisplayModeFromAttribute(newValue) });
|
|
952
939
|
break;
|
|
953
|
-
case "dev-mode":
|
|
954
940
|
case "enable-debug-tools":
|
|
955
941
|
case "api-base": break;
|
|
956
942
|
}
|
|
@@ -961,23 +947,17 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
961
947
|
* The token is stored as a JavaScript property (not as a DOM attribute),
|
|
962
948
|
* preventing exposure to DOM inspection and session replay tools.
|
|
963
949
|
*
|
|
964
|
-
* @param options - Authentication payload
|
|
950
|
+
* @param options - Authentication payload.
|
|
965
951
|
* @returns `true` when the payload is accepted; `false` when validation fails.
|
|
966
952
|
*/
|
|
967
953
|
connect(options) {
|
|
968
|
-
if (!options?.
|
|
969
|
-
this._emitCharError("
|
|
970
|
-
return false;
|
|
971
|
-
}
|
|
972
|
-
if (options?.idToken && !options?.ticketAuth && !options?.clientId) {
|
|
973
|
-
this._emitCharError("MISSING_CLIENT_ID", "connect() requires clientId when using idToken authentication");
|
|
954
|
+
if (!options?.publishableKey) {
|
|
955
|
+
this._emitCharError("MISSING_PUBLISHABLE_KEY", "connect() requires publishableKey");
|
|
974
956
|
return false;
|
|
975
957
|
}
|
|
976
958
|
this._pendingAuth = {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
organizationId: options.ticketAuth ? void 0 : options.organizationId,
|
|
980
|
-
ticketAuth: options.ticketAuth
|
|
959
|
+
publishableKey: options.publishableKey,
|
|
960
|
+
idToken: options.idToken
|
|
981
961
|
};
|
|
982
962
|
const iframeOrigin = this._getIframeOrigin();
|
|
983
963
|
if (this._iframeReady && iframeOrigin) this._postAuth(iframeOrigin);
|
|
@@ -996,7 +976,7 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
996
976
|
}
|
|
997
977
|
/**
|
|
998
978
|
* Disconnect from the Char agent.
|
|
999
|
-
* Clears the
|
|
979
|
+
* Clears pending auth state and posts a disconnect message to the iframe.
|
|
1000
980
|
*
|
|
1001
981
|
* @returns `true` when the disconnect message was sent; `false` when the iframe was not ready.
|
|
1002
982
|
*/
|
|
@@ -1060,11 +1040,6 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1060
1040
|
};
|
|
1061
1041
|
const safeAreaInsets = getSafeAreaInsets();
|
|
1062
1042
|
const displayMode = resolveDisplayModeFromAttribute(displayModeAttr) ?? "inline";
|
|
1063
|
-
const hostCapabilities = {
|
|
1064
|
-
supportedDisplayModes: [...DISPLAY_MODES],
|
|
1065
|
-
supportsOpenLink: true,
|
|
1066
|
-
supportsTeardown: true
|
|
1067
|
-
};
|
|
1068
1043
|
const context = {
|
|
1069
1044
|
theme,
|
|
1070
1045
|
styles: { variables: vars },
|
|
@@ -1075,9 +1050,7 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1075
1050
|
timeZone,
|
|
1076
1051
|
platform: detectPlatform(),
|
|
1077
1052
|
deviceCapabilities: getDeviceCapabilities(),
|
|
1078
|
-
safeAreaInsets
|
|
1079
|
-
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
|
|
1080
|
-
hostCapabilities
|
|
1053
|
+
safeAreaInsets
|
|
1081
1054
|
};
|
|
1082
1055
|
this._hostContext = context;
|
|
1083
1056
|
if (!this._iframe?.contentWindow) {
|
|
@@ -1120,22 +1093,15 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1120
1093
|
*/
|
|
1121
1094
|
_postAuth(iframeOrigin) {
|
|
1122
1095
|
if (!this._pendingAuth || !this._iframe?.contentWindow) return;
|
|
1123
|
-
if (!this._pendingAuth.
|
|
1124
|
-
console.error("[Char] _postAuth called but _pendingAuth has no
|
|
1096
|
+
if (!this._pendingAuth.publishableKey) {
|
|
1097
|
+
console.error("[Char] _postAuth called but _pendingAuth has no publishableKey");
|
|
1125
1098
|
this._pendingAuth = null;
|
|
1126
1099
|
return;
|
|
1127
1100
|
}
|
|
1128
|
-
const message =
|
|
1129
|
-
type: "char-auth",
|
|
1130
|
-
ticketAuth: this._pendingAuth.ticketAuth
|
|
1131
|
-
} : this._pendingAuth.anthropicApiKey ? {
|
|
1101
|
+
const message = {
|
|
1132
1102
|
type: "char-auth",
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
type: "char-auth",
|
|
1136
|
-
idToken: this._pendingAuth.idToken,
|
|
1137
|
-
clientId: this._pendingAuth.clientId,
|
|
1138
|
-
organizationId: this._pendingAuth.organizationId
|
|
1103
|
+
publishableKey: this._pendingAuth.publishableKey,
|
|
1104
|
+
idToken: this._pendingAuth.idToken
|
|
1139
1105
|
};
|
|
1140
1106
|
this._iframe.contentWindow.postMessage(message, iframeOrigin);
|
|
1141
1107
|
}
|
|
@@ -1282,36 +1248,6 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1282
1248
|
this._containerResizeObserver.observe(this);
|
|
1283
1249
|
}
|
|
1284
1250
|
/**
|
|
1285
|
-
* Resolve devMode from property (React 19) or attribute.
|
|
1286
|
-
*
|
|
1287
|
-
* @returns Parsed dev-mode configuration when valid.
|
|
1288
|
-
*/
|
|
1289
|
-
_resolveDevMode() {
|
|
1290
|
-
if (this.devMode && typeof this.devMode === "object") return this.devMode;
|
|
1291
|
-
const devModeAttr = this.getAttribute("dev-mode");
|
|
1292
|
-
if (!devModeAttr) return void 0;
|
|
1293
|
-
try {
|
|
1294
|
-
const parsed = JSON.parse(devModeAttr);
|
|
1295
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1296
|
-
const msg = `dev-mode attribute must be a JSON object, got: ${Array.isArray(parsed) ? "array" : typeof parsed}`;
|
|
1297
|
-
console.warn(`[Char] ${msg}`);
|
|
1298
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1299
|
-
return;
|
|
1300
|
-
}
|
|
1301
|
-
const obj = parsed;
|
|
1302
|
-
return {
|
|
1303
|
-
anthropicApiKey: typeof obj.anthropicApiKey === "string" ? obj.anthropicApiKey : void 0,
|
|
1304
|
-
openaiApiKey: typeof obj.openaiApiKey === "string" ? obj.openaiApiKey : void 0,
|
|
1305
|
-
useLocalApi: typeof obj.useLocalApi === "boolean" ? obj.useLocalApi : void 0
|
|
1306
|
-
};
|
|
1307
|
-
} catch (e) {
|
|
1308
|
-
const msg = `Failed to parse dev-mode attribute as JSON: ${e instanceof Error ? e.message : String(e)}`;
|
|
1309
|
-
console.warn(`[Char] ${msg}`);
|
|
1310
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1311
|
-
return;
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
/**
|
|
1315
1251
|
* Resolve apiBase override from property (React 19) or attribute.
|
|
1316
1252
|
*
|
|
1317
1253
|
* @returns Sanitized API base URL without trailing slash.
|
|
@@ -1340,22 +1276,36 @@ registerChar();
|
|
|
1340
1276
|
//#region src/shell-component.ts
|
|
1341
1277
|
const SHELL_OPEN_CHANGE_EVENT = "char-shell-open-change";
|
|
1342
1278
|
const SHELL_Z_INDEX = 2147483e3;
|
|
1343
|
-
const
|
|
1344
|
-
const
|
|
1345
|
-
const
|
|
1346
|
-
const
|
|
1347
|
-
const
|
|
1279
|
+
const FULLSCREEN_BREAKPOINT = 1024;
|
|
1280
|
+
const PANEL_WIDTH = 420;
|
|
1281
|
+
const PIP_WIDTH = 400;
|
|
1282
|
+
const PIP_HEIGHT = 60;
|
|
1283
|
+
const PIP_POSITION = "bottom-center";
|
|
1348
1284
|
const SHELL_HEALTH_TIMEOUT_MS = 1e4;
|
|
1349
|
-
const PANEL_BORDER = "1px solid rgba(15, 23, 42, 0.12)";
|
|
1350
1285
|
const SAFE_BOTTOM = "calc(16px + env(safe-area-inset-bottom, 0px))";
|
|
1351
|
-
const
|
|
1286
|
+
const THEME_LIGHT = {
|
|
1287
|
+
background: "oklch(1 0 0)",
|
|
1288
|
+
foreground: "oklch(0.141 0.005 285.823)",
|
|
1289
|
+
border: "oklch(0.92 0.004 286.32)",
|
|
1290
|
+
mutedForeground: "oklch(0.552 0.016 285.938)",
|
|
1291
|
+
primary: "oklch(0.21 0.006 285.885)"
|
|
1292
|
+
};
|
|
1293
|
+
const THEME_DARK = {
|
|
1294
|
+
background: "oklch(0.141 0.005 285.823)",
|
|
1295
|
+
foreground: "oklch(0.985 0 0)",
|
|
1296
|
+
border: "oklch(1 0 0 / 10%)",
|
|
1297
|
+
mutedForeground: "oklch(0.705 0.015 286.067)",
|
|
1298
|
+
primary: "oklch(0.92 0.004 286.32)"
|
|
1299
|
+
};
|
|
1300
|
+
const BODY_MARGIN_TRANSITION = "margin-right 220ms ease";
|
|
1301
|
+
const SHELL_LAYOUT_TRANSITION = "opacity 220ms ease, width 220ms ease, min-width 220ms ease, flex-basis 220ms ease";
|
|
1352
1302
|
const SHELL_REVEAL_FADE_MS = 280;
|
|
1353
1303
|
const PIP_MIN_COLLAPSED_WIDTH = 70;
|
|
1354
1304
|
const PIP_MIN_COLLAPSED_HEIGHT = 30;
|
|
1355
1305
|
const PIP_MAX_MEASURED_HEIGHT = 320;
|
|
1356
1306
|
const PIP_COLLAPSE_DELAY_MS = 400;
|
|
1357
1307
|
const PIP_PILL_HEIGHT = 40;
|
|
1358
|
-
const PIP_MORPH_TRANSITION = "width 280ms ease, height 280ms ease, bottom 280ms ease, border-radius 280ms ease, opacity
|
|
1308
|
+
const PIP_MORPH_TRANSITION = "width 280ms ease, height 280ms ease, bottom 280ms ease, border-radius 280ms ease, opacity 280ms ease, transform 280ms ease";
|
|
1359
1309
|
const PIP_MORPH_DURATION_MS = 280;
|
|
1360
1310
|
const PIP_SCROLL_HIDE_OFFSET = 100;
|
|
1361
1311
|
const PIP_COLLAPSED_WIDTH_RATIO = .75;
|
|
@@ -1363,23 +1313,14 @@ const PIP_SWIPE_THRESHOLD = 50;
|
|
|
1363
1313
|
const PIP_SCROLL_DIRECTION_THRESHOLD = 40;
|
|
1364
1314
|
const WHEEL_DELTA_LINE_MULTIPLIER = 15;
|
|
1365
1315
|
const WHEEL_DELTA_PAGE_MULTIPLIER = 300;
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
if (value === null || value.trim() === "") return fallback;
|
|
1375
|
-
const parsed = Number.parseInt(value, 10);
|
|
1376
|
-
if (!Number.isFinite(parsed)) return fallback;
|
|
1377
|
-
return normalizePositiveInteger(parsed, fallback);
|
|
1378
|
-
}
|
|
1379
|
-
function normalizePipPosition(value) {
|
|
1380
|
-
if (value === "bottom-right" || value === "bottom-left") return value;
|
|
1381
|
-
return DEFAULT_PIP_POSITION;
|
|
1382
|
-
}
|
|
1316
|
+
const PANEL_MIN_WIDTH = 320;
|
|
1317
|
+
const PANEL_MAX_WIDTH = 700;
|
|
1318
|
+
const RESIZE_NUB_WIDTH = 6;
|
|
1319
|
+
const EDGE_TAB_WIDTH = 44;
|
|
1320
|
+
const EDGE_TAB_HEIGHT = 48;
|
|
1321
|
+
const EDGE_TAB_DRAG_THRESHOLD_PX = 4;
|
|
1322
|
+
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>`;
|
|
1323
|
+
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>`;
|
|
1383
1324
|
function getPipPositionStyles(position) {
|
|
1384
1325
|
if (position === "bottom-right") return {
|
|
1385
1326
|
left: "auto",
|
|
@@ -1436,25 +1377,28 @@ function sanitizeShellHostContext(hostContext) {
|
|
|
1436
1377
|
var CharAgentShellElement = class extends HTMLElement {
|
|
1437
1378
|
static observedAttributes = [
|
|
1438
1379
|
"open",
|
|
1439
|
-
"fullscreen-breakpoint",
|
|
1440
|
-
"panel-width",
|
|
1441
|
-
"pip-position",
|
|
1442
|
-
"pip-width",
|
|
1443
|
-
"pip-height",
|
|
1444
|
-
"dev-mode",
|
|
1445
1380
|
"enable-debug-tools",
|
|
1446
1381
|
"api-base"
|
|
1447
1382
|
];
|
|
1448
1383
|
_agent = null;
|
|
1449
1384
|
_open = false;
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1385
|
+
_savedBodyTransition = null;
|
|
1386
|
+
_bodyMarginTimeoutId = null;
|
|
1387
|
+
_currentPanelWidth = PANEL_WIDTH;
|
|
1388
|
+
_resizeNubElement = null;
|
|
1389
|
+
_edgeTabElement = null;
|
|
1390
|
+
_resizeDragging = false;
|
|
1391
|
+
_resizeDragOverlay = null;
|
|
1392
|
+
_resizeStartX = 0;
|
|
1393
|
+
_resizeStartWidth = 0;
|
|
1394
|
+
_edgeTabDragStartX = 0;
|
|
1395
|
+
_edgeTabDragging = false;
|
|
1455
1396
|
_isNarrowViewport = false;
|
|
1456
1397
|
_mediaQuery = null;
|
|
1457
1398
|
_mediaQueryHandler = null;
|
|
1399
|
+
_themeObserver = null;
|
|
1400
|
+
_themeMediaQuery = null;
|
|
1401
|
+
_themeMediaHandler = null;
|
|
1458
1402
|
_pendingConnectOptions = null;
|
|
1459
1403
|
_pendingHostContext = {};
|
|
1460
1404
|
_isApplyingOpenAttribute = false;
|
|
@@ -1472,6 +1416,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1472
1416
|
_pipAnimationTimeoutId = null;
|
|
1473
1417
|
_pendingPipMeasuredHeight = null;
|
|
1474
1418
|
_tapOutsideListener = null;
|
|
1419
|
+
_lastAppliedDisplayMode = null;
|
|
1475
1420
|
_pipHiddenByScroll = false;
|
|
1476
1421
|
_scrollListener = null;
|
|
1477
1422
|
_scrollRafId = null;
|
|
@@ -1491,75 +1436,36 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1491
1436
|
set open(value) {
|
|
1492
1437
|
this.setOpen(value);
|
|
1493
1438
|
}
|
|
1494
|
-
get fullscreenBreakpoint() {
|
|
1495
|
-
return this._fullscreenBreakpoint;
|
|
1496
|
-
}
|
|
1497
|
-
set fullscreenBreakpoint(value) {
|
|
1498
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1499
|
-
if (normalized === this._fullscreenBreakpoint) return;
|
|
1500
|
-
this._fullscreenBreakpoint = normalized;
|
|
1501
|
-
this.setAttribute("fullscreen-breakpoint", String(normalized));
|
|
1502
|
-
this._setupViewportObserver();
|
|
1503
|
-
this._applyDisplayModeAndLayout();
|
|
1504
|
-
}
|
|
1505
|
-
get panelWidth() {
|
|
1506
|
-
return this._panelWidth;
|
|
1507
|
-
}
|
|
1508
|
-
set panelWidth(value) {
|
|
1509
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PANEL_WIDTH);
|
|
1510
|
-
if (normalized === this._panelWidth) return;
|
|
1511
|
-
this._panelWidth = normalized;
|
|
1512
|
-
this.setAttribute("panel-width", String(normalized));
|
|
1513
|
-
this._applyDisplayModeAndLayout();
|
|
1514
|
-
}
|
|
1515
|
-
get pipWidth() {
|
|
1516
|
-
return this._pipWidth;
|
|
1517
|
-
}
|
|
1518
|
-
set pipWidth(value) {
|
|
1519
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PIP_WIDTH);
|
|
1520
|
-
if (normalized === this._pipWidth) return;
|
|
1521
|
-
this._pipWidth = normalized;
|
|
1522
|
-
this.setAttribute("pip-width", String(normalized));
|
|
1523
|
-
this._applyDisplayModeAndLayout();
|
|
1524
|
-
}
|
|
1525
|
-
get pipHeight() {
|
|
1526
|
-
return this._pipHeight;
|
|
1527
|
-
}
|
|
1528
|
-
set pipHeight(value) {
|
|
1529
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PIP_HEIGHT);
|
|
1530
|
-
if (normalized === this._pipHeight) return;
|
|
1531
|
-
this._pipHeight = normalized;
|
|
1532
|
-
this.setAttribute("pip-height", String(normalized));
|
|
1533
|
-
this._applyDisplayModeAndLayout();
|
|
1534
|
-
}
|
|
1535
|
-
get pipPosition() {
|
|
1536
|
-
return this._pipPosition;
|
|
1537
|
-
}
|
|
1538
|
-
set pipPosition(value) {
|
|
1539
|
-
const normalized = normalizePipPosition(value);
|
|
1540
|
-
if (normalized === this._pipPosition) return;
|
|
1541
|
-
this._pipPosition = normalized;
|
|
1542
|
-
this.setAttribute("pip-position", normalized);
|
|
1543
|
-
this._applyDisplayModeAndLayout();
|
|
1544
|
-
}
|
|
1545
1439
|
connectedCallback() {
|
|
1546
1440
|
this._upgradeProperty("open");
|
|
1547
|
-
this._upgradeProperty("fullscreenBreakpoint");
|
|
1548
|
-
this._upgradeProperty("panelWidth");
|
|
1549
|
-
this._upgradeProperty("pipPosition");
|
|
1550
|
-
this._upgradeProperty("pipWidth");
|
|
1551
|
-
this._upgradeProperty("pipHeight");
|
|
1552
|
-
this._upgradeProperty("devMode");
|
|
1553
1441
|
this._upgradeProperty("apiBase");
|
|
1554
1442
|
registerChar();
|
|
1555
1443
|
this._ensureAgent();
|
|
1556
1444
|
this._readConfigFromAttributes();
|
|
1557
1445
|
this._syncForwardedAttributes();
|
|
1558
1446
|
this._setupViewportObserver();
|
|
1447
|
+
this._setupThemeObserver();
|
|
1559
1448
|
this._applyDisplayModeAndLayout();
|
|
1560
1449
|
this._flushPendingConnectAndContext();
|
|
1561
1450
|
}
|
|
1562
1451
|
disconnectedCallback() {
|
|
1452
|
+
this._removeBodyMargin();
|
|
1453
|
+
if (this._bodyMarginTimeoutId !== null) {
|
|
1454
|
+
window.clearTimeout(this._bodyMarginTimeoutId);
|
|
1455
|
+
this._bodyMarginTimeoutId = null;
|
|
1456
|
+
}
|
|
1457
|
+
this._removeResizeDragOverlay();
|
|
1458
|
+
this._teardownThemeObserver();
|
|
1459
|
+
document.removeEventListener("mousemove", this._onEdgeTabMouseMove);
|
|
1460
|
+
document.removeEventListener("mouseup", this._onEdgeTabMouseUp);
|
|
1461
|
+
document.removeEventListener("touchmove", this._onEdgeTabTouchMove);
|
|
1462
|
+
document.removeEventListener("touchend", this._onEdgeTabTouchEnd);
|
|
1463
|
+
this._removeEdgeTab();
|
|
1464
|
+
this._hideResizeNub();
|
|
1465
|
+
if (this._resizeNubElement) {
|
|
1466
|
+
this._resizeNubElement.remove();
|
|
1467
|
+
this._resizeNubElement = null;
|
|
1468
|
+
}
|
|
1563
1469
|
this._stopHealthWait();
|
|
1564
1470
|
this._cancelRevealFade();
|
|
1565
1471
|
this._teardownViewportObserver();
|
|
@@ -1588,48 +1494,6 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1588
1494
|
});
|
|
1589
1495
|
return;
|
|
1590
1496
|
}
|
|
1591
|
-
case "fullscreen-breakpoint": {
|
|
1592
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1593
|
-
if (next !== this._fullscreenBreakpoint) {
|
|
1594
|
-
this._fullscreenBreakpoint = next;
|
|
1595
|
-
this._setupViewportObserver();
|
|
1596
|
-
this._applyDisplayModeAndLayout();
|
|
1597
|
-
}
|
|
1598
|
-
return;
|
|
1599
|
-
}
|
|
1600
|
-
case "panel-width": {
|
|
1601
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PANEL_WIDTH);
|
|
1602
|
-
if (next !== this._panelWidth) {
|
|
1603
|
-
this._panelWidth = next;
|
|
1604
|
-
this._applyDisplayModeAndLayout();
|
|
1605
|
-
}
|
|
1606
|
-
return;
|
|
1607
|
-
}
|
|
1608
|
-
case "pip-width": {
|
|
1609
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PIP_WIDTH);
|
|
1610
|
-
if (next !== this._pipWidth) {
|
|
1611
|
-
this._pipWidth = next;
|
|
1612
|
-
this._applyDisplayModeAndLayout();
|
|
1613
|
-
}
|
|
1614
|
-
return;
|
|
1615
|
-
}
|
|
1616
|
-
case "pip-height": {
|
|
1617
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PIP_HEIGHT);
|
|
1618
|
-
if (next !== this._pipHeight) {
|
|
1619
|
-
this._pipHeight = next;
|
|
1620
|
-
this._applyDisplayModeAndLayout();
|
|
1621
|
-
}
|
|
1622
|
-
return;
|
|
1623
|
-
}
|
|
1624
|
-
case "pip-position": {
|
|
1625
|
-
const next = normalizePipPosition(newValue);
|
|
1626
|
-
if (next !== this._pipPosition) {
|
|
1627
|
-
this._pipPosition = next;
|
|
1628
|
-
this._applyDisplayModeAndLayout();
|
|
1629
|
-
}
|
|
1630
|
-
return;
|
|
1631
|
-
}
|
|
1632
|
-
case "dev-mode":
|
|
1633
1497
|
case "enable-debug-tools":
|
|
1634
1498
|
case "api-base":
|
|
1635
1499
|
this._syncForwardedAttributes();
|
|
@@ -1639,7 +1503,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1639
1503
|
/**
|
|
1640
1504
|
* Connects shell authentication and starts availability health checks.
|
|
1641
1505
|
*
|
|
1642
|
-
* @param options - Authentication payload
|
|
1506
|
+
* @param options - Authentication payload (`publishableKey` + optional `idToken`).
|
|
1643
1507
|
* @returns `true` when connect is accepted by the inner element.
|
|
1644
1508
|
*/
|
|
1645
1509
|
connect(options) {
|
|
@@ -1747,15 +1611,10 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1747
1611
|
this._attachForwardedListeners();
|
|
1748
1612
|
}
|
|
1749
1613
|
_primeAgentBootConfig(agent) {
|
|
1750
|
-
for (const name of [
|
|
1751
|
-
"dev-mode",
|
|
1752
|
-
"enable-debug-tools",
|
|
1753
|
-
"api-base"
|
|
1754
|
-
]) {
|
|
1614
|
+
for (const name of ["enable-debug-tools", "api-base"]) {
|
|
1755
1615
|
const value = this.getAttribute(name);
|
|
1756
1616
|
if (value !== null) agent.setAttribute(name, value);
|
|
1757
1617
|
}
|
|
1758
|
-
agent.devMode = this.devMode && typeof this.devMode === "object" ? this.devMode : void 0;
|
|
1759
1618
|
agent.apiBase = typeof this.apiBase === "string" ? this.apiBase : void 0;
|
|
1760
1619
|
}
|
|
1761
1620
|
_attachForwardedListeners() {
|
|
@@ -1778,29 +1637,19 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1778
1637
|
}
|
|
1779
1638
|
_readConfigFromAttributes() {
|
|
1780
1639
|
this._open = parseBooleanAttribute(this.getAttribute("open"));
|
|
1781
|
-
this._fullscreenBreakpoint = parsePositiveIntegerAttribute(this.getAttribute("fullscreen-breakpoint"), DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1782
|
-
this._panelWidth = parsePositiveIntegerAttribute(this.getAttribute("panel-width"), DEFAULT_PANEL_WIDTH);
|
|
1783
|
-
this._pipWidth = parsePositiveIntegerAttribute(this.getAttribute("pip-width"), DEFAULT_PIP_WIDTH);
|
|
1784
|
-
this._pipHeight = parsePositiveIntegerAttribute(this.getAttribute("pip-height"), DEFAULT_PIP_HEIGHT);
|
|
1785
|
-
this._pipPosition = normalizePipPosition(this.getAttribute("pip-position"));
|
|
1786
1640
|
}
|
|
1787
1641
|
_syncForwardedAttributes() {
|
|
1788
1642
|
if (!this._agent) return;
|
|
1789
|
-
for (const name of [
|
|
1790
|
-
"dev-mode",
|
|
1791
|
-
"enable-debug-tools",
|
|
1792
|
-
"api-base"
|
|
1793
|
-
]) {
|
|
1643
|
+
for (const name of ["enable-debug-tools", "api-base"]) {
|
|
1794
1644
|
const value = this.getAttribute(name);
|
|
1795
1645
|
if (value === null) this._agent.removeAttribute(name);
|
|
1796
1646
|
else this._agent.setAttribute(name, value);
|
|
1797
1647
|
}
|
|
1798
|
-
this._agent.devMode = this.devMode && typeof this.devMode === "object" ? this.devMode : void 0;
|
|
1799
1648
|
this._agent.apiBase = typeof this.apiBase === "string" ? this.apiBase : void 0;
|
|
1800
1649
|
}
|
|
1801
1650
|
_setupViewportObserver() {
|
|
1802
1651
|
if (typeof window === "undefined") return;
|
|
1803
|
-
const query = `(max-width: ${
|
|
1652
|
+
const query = `(max-width: ${FULLSCREEN_BREAKPOINT - 1}px)`;
|
|
1804
1653
|
if (this._mediaQuery?.media === query) {
|
|
1805
1654
|
this._isNarrowViewport = this._mediaQuery.matches;
|
|
1806
1655
|
return;
|
|
@@ -1838,38 +1687,558 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1838
1687
|
if (displayMode !== "pip") {
|
|
1839
1688
|
if (this._pipMeasuredHeight !== null) this._pipMeasuredHeight = null;
|
|
1840
1689
|
}
|
|
1690
|
+
const prevMode = this._lastAppliedDisplayMode;
|
|
1691
|
+
this._lastAppliedDisplayMode = displayMode;
|
|
1841
1692
|
this._agent.setAttribute("display-mode", displayMode);
|
|
1842
1693
|
this._applyHostLayout(displayMode);
|
|
1843
|
-
this._applyAgentLayout(displayMode);
|
|
1694
|
+
this._applyAgentLayout(displayMode, prevMode);
|
|
1695
|
+
}
|
|
1696
|
+
_isParentFlex() {
|
|
1697
|
+
const parent = this.parentElement;
|
|
1698
|
+
if (!parent) return false;
|
|
1699
|
+
const display = getComputedStyle(parent).display;
|
|
1700
|
+
return display === "flex" || display === "inline-flex";
|
|
1701
|
+
}
|
|
1702
|
+
_isDarkMode() {
|
|
1703
|
+
const themeAttr = document.documentElement.getAttribute("data-theme");
|
|
1704
|
+
if (themeAttr === "dark") return true;
|
|
1705
|
+
if (themeAttr === "light") return false;
|
|
1706
|
+
return document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Read a `--char-color-*` variable from the shell element. If the host
|
|
1710
|
+
* page has set it (directly or via inheritance) use that value; otherwise
|
|
1711
|
+
* fall back to the char design-system default for the current light/dark mode.
|
|
1712
|
+
*/
|
|
1713
|
+
_resolveColor(token) {
|
|
1714
|
+
const host = getComputedStyle(this).getPropertyValue({
|
|
1715
|
+
background: "--char-color-background",
|
|
1716
|
+
foreground: "--char-color-foreground",
|
|
1717
|
+
border: "--char-color-border",
|
|
1718
|
+
mutedForeground: "--char-color-muted-foreground",
|
|
1719
|
+
primary: "--char-color-primary"
|
|
1720
|
+
}[token]).trim();
|
|
1721
|
+
if (host) return host;
|
|
1722
|
+
return (this._isDarkMode() ? THEME_DARK : THEME_LIGHT)[token];
|
|
1723
|
+
}
|
|
1724
|
+
_panelBorder() {
|
|
1725
|
+
return `1px solid ${this._resolveColor("border")}`;
|
|
1726
|
+
}
|
|
1727
|
+
_setupThemeObserver() {
|
|
1728
|
+
const onThemeChange = () => {
|
|
1729
|
+
this._applyEdgeTabVisual();
|
|
1730
|
+
this._refreshResizeNubColors();
|
|
1731
|
+
if (this._open && !this._isNarrowViewport) {
|
|
1732
|
+
if (this._isParentFlex()) setStyleValue(this, "border-left", this._panelBorder());
|
|
1733
|
+
else if (this._agent) setStyleValue(this._agent, "border-left", this._panelBorder());
|
|
1734
|
+
}
|
|
1735
|
+
};
|
|
1736
|
+
this._themeObserver = new MutationObserver(onThemeChange);
|
|
1737
|
+
this._themeObserver.observe(document.documentElement, {
|
|
1738
|
+
attributes: true,
|
|
1739
|
+
attributeFilter: [
|
|
1740
|
+
"class",
|
|
1741
|
+
"style",
|
|
1742
|
+
"data-theme"
|
|
1743
|
+
]
|
|
1744
|
+
});
|
|
1745
|
+
this._themeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1746
|
+
this._themeMediaHandler = onThemeChange;
|
|
1747
|
+
this._themeMediaQuery.addEventListener("change", this._themeMediaHandler);
|
|
1748
|
+
}
|
|
1749
|
+
_teardownThemeObserver() {
|
|
1750
|
+
if (this._themeObserver) {
|
|
1751
|
+
this._themeObserver.disconnect();
|
|
1752
|
+
this._themeObserver = null;
|
|
1753
|
+
}
|
|
1754
|
+
if (this._themeMediaQuery && this._themeMediaHandler) {
|
|
1755
|
+
this._themeMediaQuery.removeEventListener("change", this._themeMediaHandler);
|
|
1756
|
+
this._themeMediaQuery = null;
|
|
1757
|
+
this._themeMediaHandler = null;
|
|
1758
|
+
}
|
|
1844
1759
|
}
|
|
1845
1760
|
_applyHostLayout(displayMode) {
|
|
1761
|
+
const useFlexLayout = this._isParentFlex();
|
|
1846
1762
|
if (displayMode === "inline" && !this._isNarrowViewport) {
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1763
|
+
if (useFlexLayout) {
|
|
1764
|
+
setStyleValue(this, "position", "relative");
|
|
1765
|
+
setStyleValue(this, "flex", `0 0 ${this._currentPanelWidth}px`);
|
|
1766
|
+
setStyleValue(this, "width", `${this._currentPanelWidth}px`);
|
|
1767
|
+
setStyleValue(this, "min-width", `${this._currentPanelWidth}px`);
|
|
1768
|
+
setStyleValue(this, "height", "100%");
|
|
1769
|
+
setStyleValue(this, "z-index", String(SHELL_Z_INDEX));
|
|
1770
|
+
setStyleValue(this, "border-left", this._panelBorder());
|
|
1771
|
+
setStyleValue(this, "transition", this._resizeDragging ? "none" : SHELL_LAYOUT_TRANSITION);
|
|
1772
|
+
resetStyleValue(this, "overflow");
|
|
1773
|
+
resetStyleValue(this, "pointer-events");
|
|
1774
|
+
this._removeBodyMargin();
|
|
1775
|
+
} else {
|
|
1776
|
+
setStyleValue(this, "position", "fixed");
|
|
1777
|
+
setStyleValue(this, "width", "0px");
|
|
1778
|
+
setStyleValue(this, "height", "0px");
|
|
1779
|
+
setStyleValue(this, "z-index", String(SHELL_Z_INDEX));
|
|
1780
|
+
resetStyleValue(this, "flex");
|
|
1781
|
+
resetStyleValue(this, "min-width");
|
|
1782
|
+
resetStyleValue(this, "border-left");
|
|
1783
|
+
resetStyleValue(this, "overflow");
|
|
1784
|
+
resetStyleValue(this, "pointer-events");
|
|
1785
|
+
this._applyBodyMargin(this._currentPanelWidth);
|
|
1786
|
+
}
|
|
1787
|
+
this._showResizeNub();
|
|
1788
|
+
this._showEdgeTab();
|
|
1789
|
+
if (useFlexLayout) this._syncEdgeTabWithPanelTransition();
|
|
1790
|
+
} else if (displayMode === "fullscreen") {
|
|
1791
|
+
if (useFlexLayout) {
|
|
1792
|
+
setStyleValue(this, "position", "relative");
|
|
1793
|
+
setStyleValue(this, "flex", "0 0 0px");
|
|
1794
|
+
setStyleValue(this, "width", "0px");
|
|
1795
|
+
setStyleValue(this, "min-width", "0px");
|
|
1796
|
+
setStyleValue(this, "height", "0px");
|
|
1797
|
+
setStyleValue(this, "z-index", String(SHELL_Z_INDEX));
|
|
1798
|
+
resetStyleValue(this, "border-left");
|
|
1799
|
+
setStyleValue(this, "transition", SHELL_LAYOUT_TRANSITION);
|
|
1800
|
+
resetStyleValue(this, "overflow");
|
|
1801
|
+
resetStyleValue(this, "pointer-events");
|
|
1802
|
+
} else {
|
|
1803
|
+
setStyleValue(this, "position", "relative");
|
|
1804
|
+
resetStyleValue(this, "flex");
|
|
1805
|
+
resetStyleValue(this, "min-width");
|
|
1806
|
+
resetStyleValue(this, "border-left");
|
|
1807
|
+
resetStyleValue(this, "overflow");
|
|
1808
|
+
resetStyleValue(this, "pointer-events");
|
|
1809
|
+
}
|
|
1810
|
+
this._removeBodyMargin();
|
|
1811
|
+
this._hideResizeNub();
|
|
1812
|
+
this._hideEdgeTab();
|
|
1813
|
+
} else {
|
|
1814
|
+
if (useFlexLayout) {
|
|
1815
|
+
setStyleValue(this, "position", "relative");
|
|
1816
|
+
setStyleValue(this, "flex", "0 0 0px");
|
|
1817
|
+
setStyleValue(this, "width", "0px");
|
|
1818
|
+
setStyleValue(this, "min-width", "0px");
|
|
1819
|
+
setStyleValue(this, "height", "0px");
|
|
1820
|
+
setStyleValue(this, "z-index", String(SHELL_Z_INDEX));
|
|
1821
|
+
resetStyleValue(this, "border-left");
|
|
1822
|
+
setStyleValue(this, "transition", SHELL_LAYOUT_TRANSITION);
|
|
1823
|
+
resetStyleValue(this, "overflow");
|
|
1824
|
+
resetStyleValue(this, "pointer-events");
|
|
1825
|
+
} else {
|
|
1826
|
+
setStyleValue(this, "position", "relative");
|
|
1827
|
+
resetStyleValue(this, "flex");
|
|
1828
|
+
resetStyleValue(this, "min-width");
|
|
1829
|
+
resetStyleValue(this, "border-left");
|
|
1830
|
+
resetStyleValue(this, "overflow");
|
|
1831
|
+
resetStyleValue(this, "pointer-events");
|
|
1832
|
+
}
|
|
1833
|
+
this._removeBodyMargin();
|
|
1834
|
+
this._hideResizeNub();
|
|
1835
|
+
this._showEdgeTab();
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
_applyBodyMargin(width) {
|
|
1839
|
+
const body = document.body;
|
|
1840
|
+
if (this._savedBodyTransition === null) this._savedBodyTransition = body.style.transition || "";
|
|
1841
|
+
if (!this._resizeDragging) body.style.transition = BODY_MARGIN_TRANSITION;
|
|
1842
|
+
body.style.marginRight = `${width}px`;
|
|
1843
|
+
}
|
|
1844
|
+
_removeBodyMargin() {
|
|
1845
|
+
const body = document.body;
|
|
1846
|
+
body.style.marginRight = "";
|
|
1847
|
+
if (this._bodyMarginTimeoutId !== null) {
|
|
1848
|
+
window.clearTimeout(this._bodyMarginTimeoutId);
|
|
1849
|
+
this._bodyMarginTimeoutId = null;
|
|
1850
|
+
}
|
|
1851
|
+
if (this._savedBodyTransition !== null) {
|
|
1852
|
+
const saved = this._savedBodyTransition;
|
|
1853
|
+
this._bodyMarginTimeoutId = window.setTimeout(() => {
|
|
1854
|
+
this._bodyMarginTimeoutId = null;
|
|
1855
|
+
if (body.style.transition === BODY_MARGIN_TRANSITION) body.style.transition = saved || "";
|
|
1856
|
+
}, 220);
|
|
1857
|
+
this._savedBodyTransition = null;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
_ensureResizeNub() {
|
|
1861
|
+
if (this._resizeNubElement) return this._resizeNubElement;
|
|
1862
|
+
const nub = document.createElement("div");
|
|
1863
|
+
nub.setAttribute("role", "separator");
|
|
1864
|
+
nub.setAttribute("aria-orientation", "vertical");
|
|
1865
|
+
nub.setAttribute("aria-label", "Resize panel");
|
|
1866
|
+
nub.style.cssText = [
|
|
1867
|
+
"position:fixed",
|
|
1868
|
+
"top:0",
|
|
1869
|
+
`width:${RESIZE_NUB_WIDTH}px`,
|
|
1870
|
+
"height:100dvh",
|
|
1871
|
+
"cursor:col-resize",
|
|
1872
|
+
`z-index:${SHELL_Z_INDEX + 1}`,
|
|
1873
|
+
"background:transparent",
|
|
1874
|
+
"user-select:none",
|
|
1875
|
+
"-webkit-user-select:none",
|
|
1876
|
+
"touch-action:none"
|
|
1877
|
+
].join(";");
|
|
1878
|
+
const indicator = document.createElement("div");
|
|
1879
|
+
indicator.style.cssText = [
|
|
1880
|
+
"position:absolute",
|
|
1881
|
+
"top:0",
|
|
1882
|
+
"left:50%",
|
|
1883
|
+
"transform:translateX(-50%)",
|
|
1884
|
+
"width:2px",
|
|
1885
|
+
"height:100%",
|
|
1886
|
+
"border-radius:1px",
|
|
1887
|
+
"transition:background 150ms ease, width 150ms ease",
|
|
1888
|
+
"pointer-events:none"
|
|
1889
|
+
].join(";");
|
|
1890
|
+
nub.appendChild(indicator);
|
|
1891
|
+
this._applyResizeNubColors(indicator);
|
|
1892
|
+
nub.addEventListener("mouseenter", () => {
|
|
1893
|
+
const dark = this._isDarkMode();
|
|
1894
|
+
indicator.style.background = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.15)";
|
|
1895
|
+
indicator.style.width = "3px";
|
|
1896
|
+
});
|
|
1897
|
+
nub.addEventListener("mouseleave", () => {
|
|
1898
|
+
if (!this._resizeDragging) {
|
|
1899
|
+
this._applyResizeNubColors(indicator);
|
|
1900
|
+
indicator.style.width = "2px";
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1903
|
+
nub.addEventListener("mousedown", this._onResizeStart);
|
|
1904
|
+
nub.addEventListener("touchstart", this._onResizeTouchStart, { passive: false });
|
|
1905
|
+
this._resizeNubElement = nub;
|
|
1906
|
+
return nub;
|
|
1907
|
+
}
|
|
1908
|
+
_applyResizeNubColors(indicator) {
|
|
1909
|
+
const el = indicator ?? this._resizeNubElement?.querySelector("div");
|
|
1910
|
+
if (!el) return;
|
|
1911
|
+
const dark = this._isDarkMode();
|
|
1912
|
+
el.style.background = dark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.06)";
|
|
1913
|
+
}
|
|
1914
|
+
_refreshResizeNubColors() {
|
|
1915
|
+
this._applyResizeNubColors();
|
|
1916
|
+
}
|
|
1917
|
+
_showResizeNub() {
|
|
1918
|
+
const nub = this._ensureResizeNub();
|
|
1919
|
+
if (!nub.parentNode) document.body.appendChild(nub);
|
|
1920
|
+
setStyleValue(nub, "right", `${this._currentPanelWidth - Math.floor(RESIZE_NUB_WIDTH / 2)}px`);
|
|
1921
|
+
setStyleValue(nub, "display", "block");
|
|
1922
|
+
}
|
|
1923
|
+
_hideResizeNub() {
|
|
1924
|
+
if (this._resizeNubElement) setStyleValue(this._resizeNubElement, "display", "none");
|
|
1925
|
+
}
|
|
1926
|
+
_ensureEdgeTab() {
|
|
1927
|
+
if (this._edgeTabElement) return this._edgeTabElement;
|
|
1928
|
+
const tab = document.createElement("button");
|
|
1929
|
+
tab.setAttribute("type", "button");
|
|
1930
|
+
tab.setAttribute("aria-label", "Open assistant");
|
|
1931
|
+
tab.style.cssText = [
|
|
1932
|
+
"position:fixed",
|
|
1933
|
+
"top:50%",
|
|
1934
|
+
"right:0px",
|
|
1935
|
+
"transform:translateY(-50%)",
|
|
1936
|
+
`z-index:${SHELL_Z_INDEX + 2}`,
|
|
1937
|
+
`width:${EDGE_TAB_WIDTH}px`,
|
|
1938
|
+
`height:${EDGE_TAB_HEIGHT}px`,
|
|
1939
|
+
"display:flex",
|
|
1940
|
+
"align-items:center",
|
|
1941
|
+
"justify-content:center",
|
|
1942
|
+
"border-right:none",
|
|
1943
|
+
"border-radius:12px 0 0 12px",
|
|
1944
|
+
"padding:0",
|
|
1945
|
+
"outline:none",
|
|
1946
|
+
"transition:transform 200ms ease,background 150ms ease,color 150ms ease,box-shadow 200ms ease,border-color 150ms ease",
|
|
1947
|
+
"user-select:none",
|
|
1948
|
+
"-webkit-user-select:none"
|
|
1949
|
+
].join(";");
|
|
1950
|
+
const dot = document.createElement("span");
|
|
1951
|
+
dot.style.cssText = [
|
|
1952
|
+
"position:absolute",
|
|
1953
|
+
"top:8px",
|
|
1954
|
+
"right:10px",
|
|
1955
|
+
"width:7px",
|
|
1956
|
+
"height:7px",
|
|
1957
|
+
"border-radius:50%",
|
|
1958
|
+
"transition:opacity 200ms ease"
|
|
1959
|
+
].join(";");
|
|
1960
|
+
tab.appendChild(dot);
|
|
1961
|
+
const icon = document.createElement("span");
|
|
1962
|
+
icon.style.cssText = "display:flex;align-items:center;justify-content:center;pointer-events:none;";
|
|
1963
|
+
icon.innerHTML = ICON_MESSAGE;
|
|
1964
|
+
tab.appendChild(icon);
|
|
1965
|
+
tab.addEventListener("mouseenter", () => {
|
|
1966
|
+
const dark = this._isDarkMode();
|
|
1967
|
+
if (this._open && !this._isNarrowViewport) {
|
|
1968
|
+
tab.style.background = dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)";
|
|
1969
|
+
tab.style.boxShadow = "none";
|
|
1970
|
+
tab.style.color = this._resolveColor("foreground");
|
|
1971
|
+
tab.style.cursor = "col-resize";
|
|
1972
|
+
} else {
|
|
1973
|
+
tab.style.transform = "translateY(-50%) translateX(-3px)";
|
|
1974
|
+
tab.style.boxShadow = dark ? "-4px 0 16px rgba(0,0,0,0.3)" : "-4px 0 16px rgba(0,0,0,0.12)";
|
|
1975
|
+
tab.style.color = this._resolveColor("foreground");
|
|
1976
|
+
tab.style.cursor = "pointer";
|
|
1977
|
+
}
|
|
1978
|
+
});
|
|
1979
|
+
tab.addEventListener("mouseleave", () => {
|
|
1980
|
+
this._applyEdgeTabVisual();
|
|
1981
|
+
});
|
|
1982
|
+
tab.addEventListener("mousedown", this._onEdgeTabMouseDown);
|
|
1983
|
+
tab.addEventListener("touchstart", this._onEdgeTabTouchStart, { passive: false });
|
|
1984
|
+
this._edgeTabElement = tab;
|
|
1985
|
+
return tab;
|
|
1986
|
+
}
|
|
1987
|
+
/** Apply visual style (colors, shadow) based on open state — no position change. */
|
|
1988
|
+
_applyEdgeTabVisual() {
|
|
1989
|
+
if (!this._edgeTabElement) return;
|
|
1990
|
+
const tab = this._edgeTabElement;
|
|
1991
|
+
const icon = tab.querySelector("span:last-child");
|
|
1992
|
+
const dot = tab.querySelector("span:first-child");
|
|
1993
|
+
const dark = this._isDarkMode();
|
|
1994
|
+
const bg = this._resolveColor("background");
|
|
1995
|
+
const borderColor = this._resolveColor("border");
|
|
1996
|
+
const mutedFg = this._resolveColor("mutedForeground");
|
|
1997
|
+
const primary = this._resolveColor("primary");
|
|
1998
|
+
tab.style.border = `1px solid ${borderColor}`;
|
|
1999
|
+
tab.style.borderRight = "none";
|
|
2000
|
+
if (this._open && !this._isNarrowViewport) {
|
|
2001
|
+
tab.setAttribute("aria-label", "Close assistant");
|
|
2002
|
+
tab.setAttribute("aria-expanded", "true");
|
|
2003
|
+
if (icon) icon.innerHTML = ICON_CHEVRON_RIGHT;
|
|
2004
|
+
if (dot) dot.style.opacity = "0";
|
|
2005
|
+
tab.style.background = bg;
|
|
2006
|
+
tab.style.boxShadow = "none";
|
|
2007
|
+
tab.style.backdropFilter = "none";
|
|
2008
|
+
tab.style.setProperty("-webkit-backdrop-filter", "none");
|
|
2009
|
+
tab.style.color = mutedFg;
|
|
2010
|
+
tab.style.cursor = "col-resize";
|
|
2011
|
+
tab.style.transform = "translateY(-50%)";
|
|
2012
|
+
} else {
|
|
2013
|
+
tab.setAttribute("aria-label", "Open assistant");
|
|
2014
|
+
tab.setAttribute("aria-expanded", "false");
|
|
2015
|
+
if (icon) icon.innerHTML = ICON_MESSAGE;
|
|
2016
|
+
if (dot) {
|
|
2017
|
+
dot.style.opacity = "1";
|
|
2018
|
+
dot.style.background = primary;
|
|
2019
|
+
}
|
|
2020
|
+
tab.style.background = dark ? "color-mix(in oklch, " + bg + ", white 4%)" : "color-mix(in oklch, " + bg + ", transparent 5%)";
|
|
2021
|
+
tab.style.boxShadow = dark ? "-2px 0 12px rgba(0,0,0,0.25)" : "-2px 0 12px rgba(0,0,0,0.08)";
|
|
2022
|
+
tab.style.backdropFilter = "blur(8px)";
|
|
2023
|
+
tab.style.setProperty("-webkit-backdrop-filter", "blur(8px)");
|
|
2024
|
+
tab.style.color = mutedFg;
|
|
2025
|
+
tab.style.cursor = "pointer";
|
|
2026
|
+
tab.style.transform = "translateY(-50%)";
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
/** Set the tab's `right` position immediately (no transition). */
|
|
2030
|
+
_positionEdgeTab(rightPx) {
|
|
2031
|
+
if (this._edgeTabElement) this._edgeTabElement.style.right = `${rightPx}px`;
|
|
2032
|
+
}
|
|
2033
|
+
_showEdgeTab() {
|
|
2034
|
+
const tab = this._ensureEdgeTab();
|
|
2035
|
+
if (!tab.parentNode) document.body.appendChild(tab);
|
|
2036
|
+
setStyleValue(tab, "display", "flex");
|
|
2037
|
+
if (this._open && !this._isNarrowViewport) this._positionEdgeTab(this._currentPanelWidth - 1);
|
|
2038
|
+
else this._positionEdgeTab(0);
|
|
2039
|
+
this._applyEdgeTabVisual();
|
|
2040
|
+
}
|
|
2041
|
+
_hideEdgeTab() {
|
|
2042
|
+
if (this._edgeTabElement) setStyleValue(this._edgeTabElement, "display", "none");
|
|
2043
|
+
}
|
|
2044
|
+
_removeEdgeTab() {
|
|
2045
|
+
if (this._edgeTabElement) {
|
|
2046
|
+
this._edgeTabElement.remove();
|
|
2047
|
+
this._edgeTabElement = null;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
_onEdgeTabMouseDown = (e) => {
|
|
2051
|
+
e.preventDefault();
|
|
2052
|
+
this._edgeTabDragStartX = e.clientX;
|
|
2053
|
+
this._edgeTabDragging = false;
|
|
2054
|
+
document.addEventListener("mousemove", this._onEdgeTabMouseMove);
|
|
2055
|
+
document.addEventListener("mouseup", this._onEdgeTabMouseUp);
|
|
2056
|
+
};
|
|
2057
|
+
_onEdgeTabTouchStart = (e) => {
|
|
2058
|
+
if (e.touches.length !== 1) return;
|
|
2059
|
+
e.preventDefault();
|
|
2060
|
+
this._edgeTabDragStartX = e.touches[0].clientX;
|
|
2061
|
+
this._edgeTabDragging = false;
|
|
2062
|
+
document.addEventListener("touchmove", this._onEdgeTabTouchMove, { passive: false });
|
|
2063
|
+
document.addEventListener("touchend", this._onEdgeTabTouchEnd);
|
|
2064
|
+
};
|
|
2065
|
+
_onEdgeTabMouseMove = (e) => {
|
|
2066
|
+
const dx = Math.abs(e.clientX - this._edgeTabDragStartX);
|
|
2067
|
+
if (!this._edgeTabDragging && dx >= EDGE_TAB_DRAG_THRESHOLD_PX) {
|
|
2068
|
+
this._edgeTabDragging = true;
|
|
2069
|
+
this._promoteEdgeTabDragToResize(this._edgeTabDragStartX);
|
|
2070
|
+
}
|
|
2071
|
+
};
|
|
2072
|
+
_onEdgeTabTouchMove = (e) => {
|
|
2073
|
+
if (e.touches.length !== 1) return;
|
|
2074
|
+
e.preventDefault();
|
|
2075
|
+
const dx = Math.abs(e.touches[0].clientX - this._edgeTabDragStartX);
|
|
2076
|
+
if (!this._edgeTabDragging && dx >= EDGE_TAB_DRAG_THRESHOLD_PX) {
|
|
2077
|
+
this._edgeTabDragging = true;
|
|
2078
|
+
this._promoteEdgeTabDragToResize(this._edgeTabDragStartX);
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
_onEdgeTabMouseUp = (_e) => {
|
|
2082
|
+
document.removeEventListener("mousemove", this._onEdgeTabMouseMove);
|
|
2083
|
+
document.removeEventListener("mouseup", this._onEdgeTabMouseUp);
|
|
2084
|
+
if (!this._edgeTabDragging) this.toggleOpen();
|
|
2085
|
+
};
|
|
2086
|
+
_onEdgeTabTouchEnd = (_e) => {
|
|
2087
|
+
document.removeEventListener("touchmove", this._onEdgeTabTouchMove);
|
|
2088
|
+
document.removeEventListener("touchend", this._onEdgeTabTouchEnd);
|
|
2089
|
+
if (!this._edgeTabDragging) this.toggleOpen();
|
|
2090
|
+
};
|
|
2091
|
+
/**
|
|
2092
|
+
* Once the edge tab drag crosses the threshold, hand off to the existing
|
|
2093
|
+
* resize machinery so the panel tracks the pointer smoothly.
|
|
2094
|
+
*/
|
|
2095
|
+
_promoteEdgeTabDragToResize(startX) {
|
|
2096
|
+
if (!this._open || this._isNarrowViewport) {
|
|
2097
|
+
this._edgeTabDragging = false;
|
|
1855
2098
|
return;
|
|
1856
2099
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
setStyleValue(this, "
|
|
1862
|
-
setStyleValue(this, "
|
|
1863
|
-
|
|
1864
|
-
|
|
2100
|
+
this._resizeDragging = true;
|
|
2101
|
+
this._resizeStartX = startX;
|
|
2102
|
+
this._resizeStartWidth = this._currentPanelWidth;
|
|
2103
|
+
this._showResizeDragOverlay();
|
|
2104
|
+
if (this._agent) setStyleValue(this._agent, "transition", "none");
|
|
2105
|
+
setStyleValue(this, "transition", "none");
|
|
2106
|
+
document.body.style.transition = "none";
|
|
2107
|
+
document.body.style.cursor = "col-resize";
|
|
2108
|
+
document.body.style.userSelect = "none";
|
|
2109
|
+
document.addEventListener("mousemove", this._onResizeMove);
|
|
2110
|
+
document.addEventListener("mouseup", this._onResizeEnd);
|
|
2111
|
+
document.addEventListener("touchmove", this._onResizeTouchMove, { passive: false });
|
|
2112
|
+
document.addEventListener("touchend", this._onResizeTouchEnd);
|
|
2113
|
+
}
|
|
2114
|
+
/**
|
|
2115
|
+
* Run a rAF loop that reads the shell element's actual rendered width
|
|
2116
|
+
* and pins the edge tab to it. Used during flex layout transitions so
|
|
2117
|
+
* the tab tracks the animated flex-basis exactly, frame by frame.
|
|
2118
|
+
*/
|
|
2119
|
+
_syncEdgeTabWithPanelTransition() {
|
|
2120
|
+
if (!this._edgeTabElement) return;
|
|
2121
|
+
const start = performance.now();
|
|
2122
|
+
const maxDuration = 300;
|
|
2123
|
+
const tick = () => {
|
|
2124
|
+
if (!this._edgeTabElement || !this._open) return;
|
|
2125
|
+
if (this._isParentFlex()) {
|
|
2126
|
+
const width = this.getBoundingClientRect().width;
|
|
2127
|
+
this._positionEdgeTab(width - 1);
|
|
2128
|
+
}
|
|
2129
|
+
if (performance.now() - start < maxDuration) requestAnimationFrame(tick);
|
|
2130
|
+
};
|
|
2131
|
+
requestAnimationFrame(tick);
|
|
2132
|
+
}
|
|
2133
|
+
_showResizeDragOverlay() {
|
|
2134
|
+
if (this._resizeDragOverlay) return;
|
|
2135
|
+
const overlay = document.createElement("div");
|
|
2136
|
+
overlay.style.cssText = [
|
|
2137
|
+
"position:fixed",
|
|
2138
|
+
"top:0",
|
|
2139
|
+
"left:0",
|
|
2140
|
+
"width:100vw",
|
|
2141
|
+
"height:100vh",
|
|
2142
|
+
"cursor:col-resize",
|
|
2143
|
+
`z-index:${SHELL_Z_INDEX + 1}`,
|
|
2144
|
+
"background:transparent",
|
|
2145
|
+
"user-select:none",
|
|
2146
|
+
"-webkit-user-select:none"
|
|
2147
|
+
].join(";");
|
|
2148
|
+
document.body.appendChild(overlay);
|
|
2149
|
+
this._resizeDragOverlay = overlay;
|
|
2150
|
+
}
|
|
2151
|
+
_removeResizeDragOverlay() {
|
|
2152
|
+
if (this._resizeDragOverlay) {
|
|
2153
|
+
this._resizeDragOverlay.remove();
|
|
2154
|
+
this._resizeDragOverlay = null;
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
_onResizeStart = (e) => {
|
|
2158
|
+
e.preventDefault();
|
|
2159
|
+
this._resizeDragging = true;
|
|
2160
|
+
this._resizeStartX = e.clientX;
|
|
2161
|
+
this._resizeStartWidth = this._currentPanelWidth;
|
|
2162
|
+
this._showResizeDragOverlay();
|
|
2163
|
+
if (this._agent) setStyleValue(this._agent, "transition", "none");
|
|
2164
|
+
setStyleValue(this, "transition", "none");
|
|
2165
|
+
document.body.style.transition = "none";
|
|
2166
|
+
document.body.style.cursor = "col-resize";
|
|
2167
|
+
document.body.style.userSelect = "none";
|
|
2168
|
+
document.addEventListener("mousemove", this._onResizeMove);
|
|
2169
|
+
document.addEventListener("mouseup", this._onResizeEnd);
|
|
2170
|
+
};
|
|
2171
|
+
_onResizeTouchStart = (e) => {
|
|
2172
|
+
if (e.touches.length !== 1) return;
|
|
2173
|
+
e.preventDefault();
|
|
2174
|
+
const touch = e.touches[0];
|
|
2175
|
+
this._resizeDragging = true;
|
|
2176
|
+
this._resizeStartX = touch.clientX;
|
|
2177
|
+
this._resizeStartWidth = this._currentPanelWidth;
|
|
2178
|
+
this._showResizeDragOverlay();
|
|
2179
|
+
if (this._agent) setStyleValue(this._agent, "transition", "none");
|
|
2180
|
+
setStyleValue(this, "transition", "none");
|
|
2181
|
+
document.body.style.transition = "none";
|
|
2182
|
+
document.addEventListener("touchmove", this._onResizeTouchMove, { passive: false });
|
|
2183
|
+
document.addEventListener("touchend", this._onResizeTouchEnd);
|
|
2184
|
+
};
|
|
2185
|
+
_onResizeMove = (e) => {
|
|
2186
|
+
if (!this._resizeDragging) return;
|
|
2187
|
+
const delta = this._resizeStartX - e.clientX;
|
|
2188
|
+
this._applyResizeWidth(this._resizeStartWidth + delta);
|
|
2189
|
+
};
|
|
2190
|
+
_onResizeTouchMove = (e) => {
|
|
2191
|
+
if (!this._resizeDragging || e.touches.length !== 1) return;
|
|
2192
|
+
e.preventDefault();
|
|
2193
|
+
const delta = this._resizeStartX - e.touches[0].clientX;
|
|
2194
|
+
this._applyResizeWidth(this._resizeStartWidth + delta);
|
|
2195
|
+
};
|
|
2196
|
+
_applyResizeWidth(rawWidth) {
|
|
2197
|
+
const width = Math.max(PANEL_MIN_WIDTH, Math.min(rawWidth, PANEL_MAX_WIDTH));
|
|
2198
|
+
this._currentPanelWidth = width;
|
|
2199
|
+
if (this._isParentFlex()) {
|
|
2200
|
+
setStyleValue(this, "flex", `0 0 ${width}px`);
|
|
2201
|
+
setStyleValue(this, "width", `${width}px`);
|
|
2202
|
+
setStyleValue(this, "min-width", `${width}px`);
|
|
2203
|
+
} else {
|
|
2204
|
+
if (this._agent) setStyleValue(this._agent, "width", `${width}px`);
|
|
2205
|
+
document.body.style.marginRight = `${width}px`;
|
|
2206
|
+
}
|
|
2207
|
+
if (this._resizeNubElement) {
|
|
2208
|
+
const rightPos = width - Math.floor(RESIZE_NUB_WIDTH / 2);
|
|
2209
|
+
setStyleValue(this._resizeNubElement, "right", `${rightPos}px`);
|
|
2210
|
+
}
|
|
2211
|
+
if (this._edgeTabElement) this._edgeTabElement.style.right = `${width - 1}px`;
|
|
2212
|
+
}
|
|
2213
|
+
_onResizeEnd = (e) => {
|
|
2214
|
+
this._finishResize(e.clientX);
|
|
2215
|
+
document.removeEventListener("mousemove", this._onResizeMove);
|
|
2216
|
+
document.removeEventListener("mouseup", this._onResizeEnd);
|
|
2217
|
+
};
|
|
2218
|
+
_onResizeTouchEnd = (e) => {
|
|
2219
|
+
const x = e.changedTouches[0]?.clientX ?? this._resizeStartX;
|
|
2220
|
+
this._finishResize(x);
|
|
2221
|
+
document.removeEventListener("touchmove", this._onResizeTouchMove);
|
|
2222
|
+
document.removeEventListener("touchend", this._onResizeTouchEnd);
|
|
2223
|
+
};
|
|
2224
|
+
_finishResize(endX) {
|
|
2225
|
+
this._resizeDragging = false;
|
|
2226
|
+
this._removeResizeDragOverlay();
|
|
2227
|
+
document.body.style.cursor = "";
|
|
2228
|
+
document.body.style.userSelect = "";
|
|
2229
|
+
document.body.style.transition = "";
|
|
2230
|
+
const delta = this._resizeStartX - endX;
|
|
2231
|
+
const finalWidth = this._resizeStartWidth + delta;
|
|
2232
|
+
this._currentPanelWidth = Math.max(PANEL_MIN_WIDTH, Math.min(finalWidth, PANEL_MAX_WIDTH));
|
|
2233
|
+
this._applyDisplayModeAndLayout();
|
|
1865
2234
|
}
|
|
1866
2235
|
_composePipTransform(opts) {
|
|
1867
2236
|
const parts = [];
|
|
1868
|
-
if (
|
|
2237
|
+
if (PIP_POSITION === "bottom-center") parts.push("translateX(-50%)");
|
|
1869
2238
|
if (opts.scrollHide) parts.push(`translateY(${PIP_SCROLL_HIDE_OFFSET}px)`);
|
|
1870
2239
|
return parts.length > 0 ? parts.join(" ") : "none";
|
|
1871
2240
|
}
|
|
1872
|
-
_applyAgentLayout(displayMode) {
|
|
2241
|
+
_applyAgentLayout(displayMode, prevMode) {
|
|
1873
2242
|
if (!this._agent) return;
|
|
1874
2243
|
if (displayMode === "inline" && !this._isNarrowViewport) {
|
|
1875
2244
|
this._detachPipInteractionListeners();
|
|
@@ -1877,20 +2246,32 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1877
2246
|
this._pipExpanded = false;
|
|
1878
2247
|
this._pipAnimating = false;
|
|
1879
2248
|
this._hidePipPill();
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
2249
|
+
if (this._isParentFlex()) {
|
|
2250
|
+
setStyleValue(this._agent, "position", "relative");
|
|
2251
|
+
resetStyleValue(this._agent, "top");
|
|
2252
|
+
resetStyleValue(this._agent, "right");
|
|
2253
|
+
resetStyleValue(this._agent, "bottom");
|
|
2254
|
+
resetStyleValue(this._agent, "left");
|
|
2255
|
+
setStyleValue(this._agent, "z-index", "auto");
|
|
2256
|
+
setStyleValue(this._agent, "width", "100%");
|
|
2257
|
+
setStyleValue(this._agent, "height", "100%");
|
|
2258
|
+
} else {
|
|
2259
|
+
setStyleValue(this._agent, "position", "fixed");
|
|
2260
|
+
setStyleValue(this._agent, "top", "0");
|
|
2261
|
+
setStyleValue(this._agent, "right", "0");
|
|
2262
|
+
resetStyleValue(this._agent, "bottom");
|
|
2263
|
+
resetStyleValue(this._agent, "left");
|
|
2264
|
+
setStyleValue(this._agent, "z-index", String(SHELL_Z_INDEX));
|
|
2265
|
+
setStyleValue(this._agent, "width", `${this._currentPanelWidth}px`);
|
|
2266
|
+
setStyleValue(this._agent, "height", "100dvh");
|
|
2267
|
+
setStyleValue(this._agent, "border-left", this._panelBorder());
|
|
2268
|
+
}
|
|
1885
2269
|
resetStyleValue(this._agent, "transform");
|
|
1886
2270
|
resetStyleValue(this._agent, "max-width");
|
|
1887
2271
|
resetStyleValue(this._agent, "max-height");
|
|
1888
|
-
setStyleValue(this._agent, "z-index", "auto");
|
|
1889
|
-
setStyleValue(this._agent, "width", "100%");
|
|
1890
|
-
setStyleValue(this._agent, "height", "100%");
|
|
1891
2272
|
setStyleValue(this._agent, "border-radius", "0");
|
|
1892
2273
|
setStyleValue(this._agent, "overflow", "hidden");
|
|
1893
|
-
|
|
2274
|
+
setStyleValue(this._agent, "transition", this._resizeDragging ? "none" : "width 220ms ease");
|
|
1894
2275
|
resetStyleValue(this._agent, "opacity");
|
|
1895
2276
|
resetStyleValue(this._agent, "pointer-events");
|
|
1896
2277
|
return;
|
|
@@ -1919,15 +2300,14 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1919
2300
|
resetStyleValue(this._agent, "pointer-events");
|
|
1920
2301
|
return;
|
|
1921
2302
|
}
|
|
1922
|
-
const pipPositionStyles = getPipPositionStyles(
|
|
1923
|
-
const expandedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH,
|
|
1924
|
-
const collapsedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH, Math.round(
|
|
2303
|
+
const pipPositionStyles = getPipPositionStyles(PIP_POSITION);
|
|
2304
|
+
const expandedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH, PIP_WIDTH);
|
|
2305
|
+
const collapsedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH, Math.round(PIP_WIDTH * PIP_COLLAPSED_WIDTH_RATIO));
|
|
1925
2306
|
const targetWidth = this._pipExpanded ? expandedWidth : collapsedWidth;
|
|
1926
|
-
const collapsedHeight = Math.max(PIP_MIN_COLLAPSED_HEIGHT,
|
|
1927
|
-
const hasExplicitPipHeight = this.hasAttribute("pip-height");
|
|
2307
|
+
const collapsedHeight = Math.max(PIP_MIN_COLLAPSED_HEIGHT, PIP_HEIGHT);
|
|
1928
2308
|
const measuredHeightFloor = typeof this._pipMeasuredHeight === "number" ? this._pipMeasuredHeight : 0;
|
|
1929
2309
|
const expandedHeight = Math.max(collapsedHeight, measuredHeightFloor);
|
|
1930
|
-
const collapsedPipHeight =
|
|
2310
|
+
const collapsedPipHeight = PIP_PILL_HEIGHT;
|
|
1931
2311
|
const heightDelta = expandedHeight - collapsedPipHeight;
|
|
1932
2312
|
let targetHeight;
|
|
1933
2313
|
let targetBottom;
|
|
@@ -1938,13 +2318,14 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1938
2318
|
targetHeight = collapsedPipHeight;
|
|
1939
2319
|
targetBottom = heightDelta > 0 ? `calc(${heightDelta}px + 16px + env(safe-area-inset-bottom, 0px))` : SAFE_BOTTOM;
|
|
1940
2320
|
}
|
|
2321
|
+
const enteringFromPanel = prevMode != null && prevMode !== "pip" && this._healthState === "healthy";
|
|
1941
2322
|
const composedTransform = this._composePipTransform({ scrollHide: this._pipHiddenByScroll });
|
|
2323
|
+
if (enteringFromPanel) setStyleValue(this._agent, "transition", "none");
|
|
1942
2324
|
setStyleValue(this._agent, "position", "fixed");
|
|
1943
2325
|
resetStyleValue(this._agent, "top");
|
|
1944
2326
|
setStyleValue(this._agent, "right", pipPositionStyles.right);
|
|
1945
2327
|
setStyleValue(this._agent, "left", pipPositionStyles.left);
|
|
1946
2328
|
setStyleValue(this._agent, "bottom", targetBottom);
|
|
1947
|
-
setStyleValue(this._agent, "transform", composedTransform);
|
|
1948
2329
|
setStyleValue(this._agent, "z-index", String(SHELL_Z_INDEX));
|
|
1949
2330
|
setStyleValue(this._agent, "width", `min(${targetWidth}px, calc(100vw - 24px))`);
|
|
1950
2331
|
setStyleValue(this._agent, "height", `${targetHeight}px`);
|
|
@@ -1952,7 +2333,22 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1952
2333
|
setStyleValue(this._agent, "max-width", "100vw");
|
|
1953
2334
|
setStyleValue(this._agent, "border-radius", "16px");
|
|
1954
2335
|
setStyleValue(this._agent, "overflow", "hidden");
|
|
1955
|
-
|
|
2336
|
+
resetStyleValue(this._agent, "border-left");
|
|
2337
|
+
if (enteringFromPanel) {
|
|
2338
|
+
setStyleValue(this._agent, "transform", this._composePipTransform({ scrollHide: true }));
|
|
2339
|
+
setStyleValue(this._agent, "opacity", "0");
|
|
2340
|
+
this._agent.offsetHeight;
|
|
2341
|
+
requestAnimationFrame(() => {
|
|
2342
|
+
if (!this._agent) return;
|
|
2343
|
+
setStyleValue(this._agent, "transition", PIP_MORPH_TRANSITION);
|
|
2344
|
+
setStyleValue(this._agent, "transform", composedTransform);
|
|
2345
|
+
setStyleValue(this._agent, "opacity", "1");
|
|
2346
|
+
});
|
|
2347
|
+
} else {
|
|
2348
|
+
setStyleValue(this._agent, "transform", composedTransform);
|
|
2349
|
+
setStyleValue(this._agent, "transition", this._healthState === "healthy" ? PIP_MORPH_TRANSITION : "none");
|
|
2350
|
+
resetStyleValue(this._agent, "opacity");
|
|
2351
|
+
}
|
|
1956
2352
|
if (this._healthState === "healthy") {
|
|
1957
2353
|
const pill = this._ensurePipPill();
|
|
1958
2354
|
if (!pill.parentNode) this.appendChild(pill);
|
|
@@ -1961,6 +2357,16 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1961
2357
|
this._attachScrollHideListener();
|
|
1962
2358
|
this._attachSwipeGestureListeners();
|
|
1963
2359
|
this._attachWheelFallbackListener();
|
|
2360
|
+
if (enteringFromPanel) {
|
|
2361
|
+
pill.style.opacity = "0";
|
|
2362
|
+
pill.style.pointerEvents = "none";
|
|
2363
|
+
pill.style.transition = "none";
|
|
2364
|
+
requestAnimationFrame(() => {
|
|
2365
|
+
pill.style.transition = PIP_MORPH_TRANSITION;
|
|
2366
|
+
pill.style.opacity = "1";
|
|
2367
|
+
pill.style.pointerEvents = "";
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
1964
2370
|
this._applyPipVisualState();
|
|
1965
2371
|
}
|
|
1966
2372
|
}
|
|
@@ -1970,7 +2376,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1970
2376
|
pill.setAttribute("role", "button");
|
|
1971
2377
|
pill.setAttribute("aria-label", "Open assistant");
|
|
1972
2378
|
pill.setAttribute("tabindex", "0");
|
|
1973
|
-
const pipPositionStyles = getPipPositionStyles(
|
|
2379
|
+
const pipPositionStyles = getPipPositionStyles(PIP_POSITION);
|
|
1974
2380
|
pill.style.cssText = [
|
|
1975
2381
|
"position:fixed",
|
|
1976
2382
|
`left:${pipPositionStyles.left}`,
|
|
@@ -2356,6 +2762,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2356
2762
|
this._pipExpanded = false;
|
|
2357
2763
|
this._pipAnimating = false;
|
|
2358
2764
|
this._hidePipPill();
|
|
2765
|
+
this._hideEdgeTab();
|
|
2359
2766
|
if (this._agent) this._agent.removeAttribute("display-mode");
|
|
2360
2767
|
this._resetHostLayoutStyles();
|
|
2361
2768
|
this._resetAgentLayoutStyles();
|
|
@@ -2401,7 +2808,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2401
2808
|
this._revealFrameId = null;
|
|
2402
2809
|
setStyleValue(this, "opacity", "1");
|
|
2403
2810
|
window.setTimeout(() => {
|
|
2404
|
-
|
|
2811
|
+
resetStyleValue(this, "will-change");
|
|
2405
2812
|
}, SHELL_REVEAL_FADE_MS);
|
|
2406
2813
|
});
|
|
2407
2814
|
}
|
|
@@ -2453,6 +2860,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2453
2860
|
return;
|
|
2454
2861
|
}
|
|
2455
2862
|
this._open = nextOpen;
|
|
2863
|
+
if (!nextOpen) this._currentPanelWidth = PANEL_WIDTH;
|
|
2456
2864
|
if (options.reflect) this._reflectOpenAttribute(nextOpen);
|
|
2457
2865
|
this._applyDisplayModeAndLayout();
|
|
2458
2866
|
if (options.emit) this.dispatchEvent(new CustomEvent(SHELL_OPEN_CHANGE_EVENT, {
|
|
@@ -2526,6 +2934,16 @@ function registerCharShell(tagName = "char-agent-shell") {
|
|
|
2526
2934
|
}
|
|
2527
2935
|
registerCharShell();
|
|
2528
2936
|
|
|
2937
|
+
//#endregion
|
|
2938
|
+
//#region src/index.ts
|
|
2939
|
+
/**
|
|
2940
|
+
* @packageDocumentation
|
|
2941
|
+
* @mcp-b/char
|
|
2942
|
+
*
|
|
2943
|
+
* Host entry point (auto-registers <char-agent> and <char-agent-shell>).
|
|
2944
|
+
* This package root is intentionally host-only.
|
|
2945
|
+
*/
|
|
2946
|
+
|
|
2529
2947
|
//#endregion
|
|
2530
2948
|
export { DEFAULT_AVAILABLE_DISPLAY_MODES, isDisplayMode, isExpandedDisplayMode, registerChar, registerCharShell, resolvePolicyDisplayMode, resolveSupportedDisplayMode };
|
|
2531
2949
|
//# sourceMappingURL=index.js.map
|