@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/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,17 +1093,15 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1120
1093
|
*/
|
|
1121
1094
|
_postAuth(iframeOrigin) {
|
|
1122
1095
|
if (!this._pendingAuth || !this._iframe?.contentWindow) return;
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
} : {
|
|
1096
|
+
if (!this._pendingAuth.publishableKey) {
|
|
1097
|
+
console.error("[Char] _postAuth called but _pendingAuth has no publishableKey");
|
|
1098
|
+
this._pendingAuth = null;
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
const message = {
|
|
1130
1102
|
type: "char-auth",
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
organizationId: this._pendingAuth.organizationId
|
|
1103
|
+
publishableKey: this._pendingAuth.publishableKey,
|
|
1104
|
+
idToken: this._pendingAuth.idToken
|
|
1134
1105
|
};
|
|
1135
1106
|
this._iframe.contentWindow.postMessage(message, iframeOrigin);
|
|
1136
1107
|
}
|
|
@@ -1277,36 +1248,6 @@ var CharAgentElement = class extends HTMLElement {
|
|
|
1277
1248
|
this._containerResizeObserver.observe(this);
|
|
1278
1249
|
}
|
|
1279
1250
|
/**
|
|
1280
|
-
* Resolve devMode from property (React 19) or attribute.
|
|
1281
|
-
*
|
|
1282
|
-
* @returns Parsed dev-mode configuration when valid.
|
|
1283
|
-
*/
|
|
1284
|
-
_resolveDevMode() {
|
|
1285
|
-
if (this.devMode && typeof this.devMode === "object") return this.devMode;
|
|
1286
|
-
const devModeAttr = this.getAttribute("dev-mode");
|
|
1287
|
-
if (!devModeAttr) return void 0;
|
|
1288
|
-
try {
|
|
1289
|
-
const parsed = JSON.parse(devModeAttr);
|
|
1290
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1291
|
-
const msg = `dev-mode attribute must be a JSON object, got: ${Array.isArray(parsed) ? "array" : typeof parsed}`;
|
|
1292
|
-
console.warn(`[Char] ${msg}`);
|
|
1293
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
|
-
const obj = parsed;
|
|
1297
|
-
return {
|
|
1298
|
-
anthropicApiKey: typeof obj.anthropicApiKey === "string" ? obj.anthropicApiKey : void 0,
|
|
1299
|
-
openaiApiKey: typeof obj.openaiApiKey === "string" ? obj.openaiApiKey : void 0,
|
|
1300
|
-
useLocalApi: typeof obj.useLocalApi === "boolean" ? obj.useLocalApi : void 0
|
|
1301
|
-
};
|
|
1302
|
-
} catch (e) {
|
|
1303
|
-
const msg = `Failed to parse dev-mode attribute as JSON: ${e instanceof Error ? e.message : String(e)}`;
|
|
1304
|
-
console.warn(`[Char] ${msg}`);
|
|
1305
|
-
this._emitCharError("INVALID_DEV_MODE", msg);
|
|
1306
|
-
return;
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
/**
|
|
1310
1251
|
* Resolve apiBase override from property (React 19) or attribute.
|
|
1311
1252
|
*
|
|
1312
1253
|
* @returns Sanitized API base URL without trailing slash.
|
|
@@ -1335,22 +1276,36 @@ registerChar();
|
|
|
1335
1276
|
//#region src/shell-component.ts
|
|
1336
1277
|
const SHELL_OPEN_CHANGE_EVENT = "char-shell-open-change";
|
|
1337
1278
|
const SHELL_Z_INDEX = 2147483e3;
|
|
1338
|
-
const
|
|
1339
|
-
const
|
|
1340
|
-
const
|
|
1341
|
-
const
|
|
1342
|
-
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";
|
|
1343
1284
|
const SHELL_HEALTH_TIMEOUT_MS = 1e4;
|
|
1344
|
-
const PANEL_BORDER = "1px solid rgba(15, 23, 42, 0.12)";
|
|
1345
1285
|
const SAFE_BOTTOM = "calc(16px + env(safe-area-inset-bottom, 0px))";
|
|
1346
|
-
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";
|
|
1347
1302
|
const SHELL_REVEAL_FADE_MS = 280;
|
|
1348
1303
|
const PIP_MIN_COLLAPSED_WIDTH = 70;
|
|
1349
1304
|
const PIP_MIN_COLLAPSED_HEIGHT = 30;
|
|
1350
1305
|
const PIP_MAX_MEASURED_HEIGHT = 320;
|
|
1351
1306
|
const PIP_COLLAPSE_DELAY_MS = 400;
|
|
1352
1307
|
const PIP_PILL_HEIGHT = 40;
|
|
1353
|
-
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";
|
|
1354
1309
|
const PIP_MORPH_DURATION_MS = 280;
|
|
1355
1310
|
const PIP_SCROLL_HIDE_OFFSET = 100;
|
|
1356
1311
|
const PIP_COLLAPSED_WIDTH_RATIO = .75;
|
|
@@ -1358,23 +1313,14 @@ const PIP_SWIPE_THRESHOLD = 50;
|
|
|
1358
1313
|
const PIP_SCROLL_DIRECTION_THRESHOLD = 40;
|
|
1359
1314
|
const WHEEL_DELTA_LINE_MULTIPLIER = 15;
|
|
1360
1315
|
const WHEEL_DELTA_PAGE_MULTIPLIER = 300;
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
if (value === null || value.trim() === "") return fallback;
|
|
1370
|
-
const parsed = Number.parseInt(value, 10);
|
|
1371
|
-
if (!Number.isFinite(parsed)) return fallback;
|
|
1372
|
-
return normalizePositiveInteger(parsed, fallback);
|
|
1373
|
-
}
|
|
1374
|
-
function normalizePipPosition(value) {
|
|
1375
|
-
if (value === "bottom-right" || value === "bottom-left") return value;
|
|
1376
|
-
return DEFAULT_PIP_POSITION;
|
|
1377
|
-
}
|
|
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>`;
|
|
1378
1324
|
function getPipPositionStyles(position) {
|
|
1379
1325
|
if (position === "bottom-right") return {
|
|
1380
1326
|
left: "auto",
|
|
@@ -1431,25 +1377,28 @@ function sanitizeShellHostContext(hostContext) {
|
|
|
1431
1377
|
var CharAgentShellElement = class extends HTMLElement {
|
|
1432
1378
|
static observedAttributes = [
|
|
1433
1379
|
"open",
|
|
1434
|
-
"fullscreen-breakpoint",
|
|
1435
|
-
"panel-width",
|
|
1436
|
-
"pip-position",
|
|
1437
|
-
"pip-width",
|
|
1438
|
-
"pip-height",
|
|
1439
|
-
"dev-mode",
|
|
1440
1380
|
"enable-debug-tools",
|
|
1441
1381
|
"api-base"
|
|
1442
1382
|
];
|
|
1443
1383
|
_agent = null;
|
|
1444
1384
|
_open = false;
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
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;
|
|
1450
1396
|
_isNarrowViewport = false;
|
|
1451
1397
|
_mediaQuery = null;
|
|
1452
1398
|
_mediaQueryHandler = null;
|
|
1399
|
+
_themeObserver = null;
|
|
1400
|
+
_themeMediaQuery = null;
|
|
1401
|
+
_themeMediaHandler = null;
|
|
1453
1402
|
_pendingConnectOptions = null;
|
|
1454
1403
|
_pendingHostContext = {};
|
|
1455
1404
|
_isApplyingOpenAttribute = false;
|
|
@@ -1467,6 +1416,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1467
1416
|
_pipAnimationTimeoutId = null;
|
|
1468
1417
|
_pendingPipMeasuredHeight = null;
|
|
1469
1418
|
_tapOutsideListener = null;
|
|
1419
|
+
_lastAppliedDisplayMode = null;
|
|
1470
1420
|
_pipHiddenByScroll = false;
|
|
1471
1421
|
_scrollListener = null;
|
|
1472
1422
|
_scrollRafId = null;
|
|
@@ -1486,75 +1436,36 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1486
1436
|
set open(value) {
|
|
1487
1437
|
this.setOpen(value);
|
|
1488
1438
|
}
|
|
1489
|
-
get fullscreenBreakpoint() {
|
|
1490
|
-
return this._fullscreenBreakpoint;
|
|
1491
|
-
}
|
|
1492
|
-
set fullscreenBreakpoint(value) {
|
|
1493
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1494
|
-
if (normalized === this._fullscreenBreakpoint) return;
|
|
1495
|
-
this._fullscreenBreakpoint = normalized;
|
|
1496
|
-
this.setAttribute("fullscreen-breakpoint", String(normalized));
|
|
1497
|
-
this._setupViewportObserver();
|
|
1498
|
-
this._applyDisplayModeAndLayout();
|
|
1499
|
-
}
|
|
1500
|
-
get panelWidth() {
|
|
1501
|
-
return this._panelWidth;
|
|
1502
|
-
}
|
|
1503
|
-
set panelWidth(value) {
|
|
1504
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PANEL_WIDTH);
|
|
1505
|
-
if (normalized === this._panelWidth) return;
|
|
1506
|
-
this._panelWidth = normalized;
|
|
1507
|
-
this.setAttribute("panel-width", String(normalized));
|
|
1508
|
-
this._applyDisplayModeAndLayout();
|
|
1509
|
-
}
|
|
1510
|
-
get pipWidth() {
|
|
1511
|
-
return this._pipWidth;
|
|
1512
|
-
}
|
|
1513
|
-
set pipWidth(value) {
|
|
1514
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PIP_WIDTH);
|
|
1515
|
-
if (normalized === this._pipWidth) return;
|
|
1516
|
-
this._pipWidth = normalized;
|
|
1517
|
-
this.setAttribute("pip-width", String(normalized));
|
|
1518
|
-
this._applyDisplayModeAndLayout();
|
|
1519
|
-
}
|
|
1520
|
-
get pipHeight() {
|
|
1521
|
-
return this._pipHeight;
|
|
1522
|
-
}
|
|
1523
|
-
set pipHeight(value) {
|
|
1524
|
-
const normalized = normalizePositiveInteger(value, DEFAULT_PIP_HEIGHT);
|
|
1525
|
-
if (normalized === this._pipHeight) return;
|
|
1526
|
-
this._pipHeight = normalized;
|
|
1527
|
-
this.setAttribute("pip-height", String(normalized));
|
|
1528
|
-
this._applyDisplayModeAndLayout();
|
|
1529
|
-
}
|
|
1530
|
-
get pipPosition() {
|
|
1531
|
-
return this._pipPosition;
|
|
1532
|
-
}
|
|
1533
|
-
set pipPosition(value) {
|
|
1534
|
-
const normalized = normalizePipPosition(value);
|
|
1535
|
-
if (normalized === this._pipPosition) return;
|
|
1536
|
-
this._pipPosition = normalized;
|
|
1537
|
-
this.setAttribute("pip-position", normalized);
|
|
1538
|
-
this._applyDisplayModeAndLayout();
|
|
1539
|
-
}
|
|
1540
1439
|
connectedCallback() {
|
|
1541
1440
|
this._upgradeProperty("open");
|
|
1542
|
-
this._upgradeProperty("fullscreenBreakpoint");
|
|
1543
|
-
this._upgradeProperty("panelWidth");
|
|
1544
|
-
this._upgradeProperty("pipPosition");
|
|
1545
|
-
this._upgradeProperty("pipWidth");
|
|
1546
|
-
this._upgradeProperty("pipHeight");
|
|
1547
|
-
this._upgradeProperty("devMode");
|
|
1548
1441
|
this._upgradeProperty("apiBase");
|
|
1549
1442
|
registerChar();
|
|
1550
1443
|
this._ensureAgent();
|
|
1551
1444
|
this._readConfigFromAttributes();
|
|
1552
1445
|
this._syncForwardedAttributes();
|
|
1553
1446
|
this._setupViewportObserver();
|
|
1447
|
+
this._setupThemeObserver();
|
|
1554
1448
|
this._applyDisplayModeAndLayout();
|
|
1555
1449
|
this._flushPendingConnectAndContext();
|
|
1556
1450
|
}
|
|
1557
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
|
+
}
|
|
1558
1469
|
this._stopHealthWait();
|
|
1559
1470
|
this._cancelRevealFade();
|
|
1560
1471
|
this._teardownViewportObserver();
|
|
@@ -1583,48 +1494,6 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1583
1494
|
});
|
|
1584
1495
|
return;
|
|
1585
1496
|
}
|
|
1586
|
-
case "fullscreen-breakpoint": {
|
|
1587
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1588
|
-
if (next !== this._fullscreenBreakpoint) {
|
|
1589
|
-
this._fullscreenBreakpoint = next;
|
|
1590
|
-
this._setupViewportObserver();
|
|
1591
|
-
this._applyDisplayModeAndLayout();
|
|
1592
|
-
}
|
|
1593
|
-
return;
|
|
1594
|
-
}
|
|
1595
|
-
case "panel-width": {
|
|
1596
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PANEL_WIDTH);
|
|
1597
|
-
if (next !== this._panelWidth) {
|
|
1598
|
-
this._panelWidth = next;
|
|
1599
|
-
this._applyDisplayModeAndLayout();
|
|
1600
|
-
}
|
|
1601
|
-
return;
|
|
1602
|
-
}
|
|
1603
|
-
case "pip-width": {
|
|
1604
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PIP_WIDTH);
|
|
1605
|
-
if (next !== this._pipWidth) {
|
|
1606
|
-
this._pipWidth = next;
|
|
1607
|
-
this._applyDisplayModeAndLayout();
|
|
1608
|
-
}
|
|
1609
|
-
return;
|
|
1610
|
-
}
|
|
1611
|
-
case "pip-height": {
|
|
1612
|
-
const next = parsePositiveIntegerAttribute(newValue, DEFAULT_PIP_HEIGHT);
|
|
1613
|
-
if (next !== this._pipHeight) {
|
|
1614
|
-
this._pipHeight = next;
|
|
1615
|
-
this._applyDisplayModeAndLayout();
|
|
1616
|
-
}
|
|
1617
|
-
return;
|
|
1618
|
-
}
|
|
1619
|
-
case "pip-position": {
|
|
1620
|
-
const next = normalizePipPosition(newValue);
|
|
1621
|
-
if (next !== this._pipPosition) {
|
|
1622
|
-
this._pipPosition = next;
|
|
1623
|
-
this._applyDisplayModeAndLayout();
|
|
1624
|
-
}
|
|
1625
|
-
return;
|
|
1626
|
-
}
|
|
1627
|
-
case "dev-mode":
|
|
1628
1497
|
case "enable-debug-tools":
|
|
1629
1498
|
case "api-base":
|
|
1630
1499
|
this._syncForwardedAttributes();
|
|
@@ -1634,7 +1503,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1634
1503
|
/**
|
|
1635
1504
|
* Connects shell authentication and starts availability health checks.
|
|
1636
1505
|
*
|
|
1637
|
-
* @param options - Authentication payload
|
|
1506
|
+
* @param options - Authentication payload (`publishableKey` + optional `idToken`).
|
|
1638
1507
|
* @returns `true` when connect is accepted by the inner element.
|
|
1639
1508
|
*/
|
|
1640
1509
|
connect(options) {
|
|
@@ -1742,15 +1611,10 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1742
1611
|
this._attachForwardedListeners();
|
|
1743
1612
|
}
|
|
1744
1613
|
_primeAgentBootConfig(agent) {
|
|
1745
|
-
for (const name of [
|
|
1746
|
-
"dev-mode",
|
|
1747
|
-
"enable-debug-tools",
|
|
1748
|
-
"api-base"
|
|
1749
|
-
]) {
|
|
1614
|
+
for (const name of ["enable-debug-tools", "api-base"]) {
|
|
1750
1615
|
const value = this.getAttribute(name);
|
|
1751
1616
|
if (value !== null) agent.setAttribute(name, value);
|
|
1752
1617
|
}
|
|
1753
|
-
agent.devMode = this.devMode && typeof this.devMode === "object" ? this.devMode : void 0;
|
|
1754
1618
|
agent.apiBase = typeof this.apiBase === "string" ? this.apiBase : void 0;
|
|
1755
1619
|
}
|
|
1756
1620
|
_attachForwardedListeners() {
|
|
@@ -1773,29 +1637,19 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1773
1637
|
}
|
|
1774
1638
|
_readConfigFromAttributes() {
|
|
1775
1639
|
this._open = parseBooleanAttribute(this.getAttribute("open"));
|
|
1776
|
-
this._fullscreenBreakpoint = parsePositiveIntegerAttribute(this.getAttribute("fullscreen-breakpoint"), DEFAULT_FULLSCREEN_BREAKPOINT);
|
|
1777
|
-
this._panelWidth = parsePositiveIntegerAttribute(this.getAttribute("panel-width"), DEFAULT_PANEL_WIDTH);
|
|
1778
|
-
this._pipWidth = parsePositiveIntegerAttribute(this.getAttribute("pip-width"), DEFAULT_PIP_WIDTH);
|
|
1779
|
-
this._pipHeight = parsePositiveIntegerAttribute(this.getAttribute("pip-height"), DEFAULT_PIP_HEIGHT);
|
|
1780
|
-
this._pipPosition = normalizePipPosition(this.getAttribute("pip-position"));
|
|
1781
1640
|
}
|
|
1782
1641
|
_syncForwardedAttributes() {
|
|
1783
1642
|
if (!this._agent) return;
|
|
1784
|
-
for (const name of [
|
|
1785
|
-
"dev-mode",
|
|
1786
|
-
"enable-debug-tools",
|
|
1787
|
-
"api-base"
|
|
1788
|
-
]) {
|
|
1643
|
+
for (const name of ["enable-debug-tools", "api-base"]) {
|
|
1789
1644
|
const value = this.getAttribute(name);
|
|
1790
1645
|
if (value === null) this._agent.removeAttribute(name);
|
|
1791
1646
|
else this._agent.setAttribute(name, value);
|
|
1792
1647
|
}
|
|
1793
|
-
this._agent.devMode = this.devMode && typeof this.devMode === "object" ? this.devMode : void 0;
|
|
1794
1648
|
this._agent.apiBase = typeof this.apiBase === "string" ? this.apiBase : void 0;
|
|
1795
1649
|
}
|
|
1796
1650
|
_setupViewportObserver() {
|
|
1797
1651
|
if (typeof window === "undefined") return;
|
|
1798
|
-
const query = `(max-width: ${
|
|
1652
|
+
const query = `(max-width: ${FULLSCREEN_BREAKPOINT - 1}px)`;
|
|
1799
1653
|
if (this._mediaQuery?.media === query) {
|
|
1800
1654
|
this._isNarrowViewport = this._mediaQuery.matches;
|
|
1801
1655
|
return;
|
|
@@ -1833,38 +1687,558 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1833
1687
|
if (displayMode !== "pip") {
|
|
1834
1688
|
if (this._pipMeasuredHeight !== null) this._pipMeasuredHeight = null;
|
|
1835
1689
|
}
|
|
1690
|
+
const prevMode = this._lastAppliedDisplayMode;
|
|
1691
|
+
this._lastAppliedDisplayMode = displayMode;
|
|
1836
1692
|
this._agent.setAttribute("display-mode", displayMode);
|
|
1837
1693
|
this._applyHostLayout(displayMode);
|
|
1838
|
-
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
|
+
}
|
|
1839
1759
|
}
|
|
1840
1760
|
_applyHostLayout(displayMode) {
|
|
1761
|
+
const useFlexLayout = this._isParentFlex();
|
|
1841
1762
|
if (displayMode === "inline" && !this._isNarrowViewport) {
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
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;
|
|
1850
2098
|
return;
|
|
1851
2099
|
}
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
setStyleValue(this, "
|
|
1857
|
-
setStyleValue(this, "
|
|
1858
|
-
|
|
1859
|
-
|
|
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();
|
|
1860
2234
|
}
|
|
1861
2235
|
_composePipTransform(opts) {
|
|
1862
2236
|
const parts = [];
|
|
1863
|
-
if (
|
|
2237
|
+
if (PIP_POSITION === "bottom-center") parts.push("translateX(-50%)");
|
|
1864
2238
|
if (opts.scrollHide) parts.push(`translateY(${PIP_SCROLL_HIDE_OFFSET}px)`);
|
|
1865
2239
|
return parts.length > 0 ? parts.join(" ") : "none";
|
|
1866
2240
|
}
|
|
1867
|
-
_applyAgentLayout(displayMode) {
|
|
2241
|
+
_applyAgentLayout(displayMode, prevMode) {
|
|
1868
2242
|
if (!this._agent) return;
|
|
1869
2243
|
if (displayMode === "inline" && !this._isNarrowViewport) {
|
|
1870
2244
|
this._detachPipInteractionListeners();
|
|
@@ -1872,20 +2246,32 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1872
2246
|
this._pipExpanded = false;
|
|
1873
2247
|
this._pipAnimating = false;
|
|
1874
2248
|
this._hidePipPill();
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
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
|
+
}
|
|
1880
2269
|
resetStyleValue(this._agent, "transform");
|
|
1881
2270
|
resetStyleValue(this._agent, "max-width");
|
|
1882
2271
|
resetStyleValue(this._agent, "max-height");
|
|
1883
|
-
setStyleValue(this._agent, "z-index", "auto");
|
|
1884
|
-
setStyleValue(this._agent, "width", "100%");
|
|
1885
|
-
setStyleValue(this._agent, "height", "100%");
|
|
1886
2272
|
setStyleValue(this._agent, "border-radius", "0");
|
|
1887
2273
|
setStyleValue(this._agent, "overflow", "hidden");
|
|
1888
|
-
|
|
2274
|
+
setStyleValue(this._agent, "transition", this._resizeDragging ? "none" : "width 220ms ease");
|
|
1889
2275
|
resetStyleValue(this._agent, "opacity");
|
|
1890
2276
|
resetStyleValue(this._agent, "pointer-events");
|
|
1891
2277
|
return;
|
|
@@ -1914,15 +2300,14 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1914
2300
|
resetStyleValue(this._agent, "pointer-events");
|
|
1915
2301
|
return;
|
|
1916
2302
|
}
|
|
1917
|
-
const pipPositionStyles = getPipPositionStyles(
|
|
1918
|
-
const expandedWidth = Math.max(PIP_MIN_COLLAPSED_WIDTH,
|
|
1919
|
-
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));
|
|
1920
2306
|
const targetWidth = this._pipExpanded ? expandedWidth : collapsedWidth;
|
|
1921
|
-
const collapsedHeight = Math.max(PIP_MIN_COLLAPSED_HEIGHT,
|
|
1922
|
-
const hasExplicitPipHeight = this.hasAttribute("pip-height");
|
|
2307
|
+
const collapsedHeight = Math.max(PIP_MIN_COLLAPSED_HEIGHT, PIP_HEIGHT);
|
|
1923
2308
|
const measuredHeightFloor = typeof this._pipMeasuredHeight === "number" ? this._pipMeasuredHeight : 0;
|
|
1924
2309
|
const expandedHeight = Math.max(collapsedHeight, measuredHeightFloor);
|
|
1925
|
-
const collapsedPipHeight =
|
|
2310
|
+
const collapsedPipHeight = PIP_PILL_HEIGHT;
|
|
1926
2311
|
const heightDelta = expandedHeight - collapsedPipHeight;
|
|
1927
2312
|
let targetHeight;
|
|
1928
2313
|
let targetBottom;
|
|
@@ -1933,13 +2318,14 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1933
2318
|
targetHeight = collapsedPipHeight;
|
|
1934
2319
|
targetBottom = heightDelta > 0 ? `calc(${heightDelta}px + 16px + env(safe-area-inset-bottom, 0px))` : SAFE_BOTTOM;
|
|
1935
2320
|
}
|
|
2321
|
+
const enteringFromPanel = prevMode != null && prevMode !== "pip" && this._healthState === "healthy";
|
|
1936
2322
|
const composedTransform = this._composePipTransform({ scrollHide: this._pipHiddenByScroll });
|
|
2323
|
+
if (enteringFromPanel) setStyleValue(this._agent, "transition", "none");
|
|
1937
2324
|
setStyleValue(this._agent, "position", "fixed");
|
|
1938
2325
|
resetStyleValue(this._agent, "top");
|
|
1939
2326
|
setStyleValue(this._agent, "right", pipPositionStyles.right);
|
|
1940
2327
|
setStyleValue(this._agent, "left", pipPositionStyles.left);
|
|
1941
2328
|
setStyleValue(this._agent, "bottom", targetBottom);
|
|
1942
|
-
setStyleValue(this._agent, "transform", composedTransform);
|
|
1943
2329
|
setStyleValue(this._agent, "z-index", String(SHELL_Z_INDEX));
|
|
1944
2330
|
setStyleValue(this._agent, "width", `min(${targetWidth}px, calc(100vw - 24px))`);
|
|
1945
2331
|
setStyleValue(this._agent, "height", `${targetHeight}px`);
|
|
@@ -1947,7 +2333,22 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1947
2333
|
setStyleValue(this._agent, "max-width", "100vw");
|
|
1948
2334
|
setStyleValue(this._agent, "border-radius", "16px");
|
|
1949
2335
|
setStyleValue(this._agent, "overflow", "hidden");
|
|
1950
|
-
|
|
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
|
+
}
|
|
1951
2352
|
if (this._healthState === "healthy") {
|
|
1952
2353
|
const pill = this._ensurePipPill();
|
|
1953
2354
|
if (!pill.parentNode) this.appendChild(pill);
|
|
@@ -1956,6 +2357,16 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1956
2357
|
this._attachScrollHideListener();
|
|
1957
2358
|
this._attachSwipeGestureListeners();
|
|
1958
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
|
+
}
|
|
1959
2370
|
this._applyPipVisualState();
|
|
1960
2371
|
}
|
|
1961
2372
|
}
|
|
@@ -1965,7 +2376,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
1965
2376
|
pill.setAttribute("role", "button");
|
|
1966
2377
|
pill.setAttribute("aria-label", "Open assistant");
|
|
1967
2378
|
pill.setAttribute("tabindex", "0");
|
|
1968
|
-
const pipPositionStyles = getPipPositionStyles(
|
|
2379
|
+
const pipPositionStyles = getPipPositionStyles(PIP_POSITION);
|
|
1969
2380
|
pill.style.cssText = [
|
|
1970
2381
|
"position:fixed",
|
|
1971
2382
|
`left:${pipPositionStyles.left}`,
|
|
@@ -2351,6 +2762,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2351
2762
|
this._pipExpanded = false;
|
|
2352
2763
|
this._pipAnimating = false;
|
|
2353
2764
|
this._hidePipPill();
|
|
2765
|
+
this._hideEdgeTab();
|
|
2354
2766
|
if (this._agent) this._agent.removeAttribute("display-mode");
|
|
2355
2767
|
this._resetHostLayoutStyles();
|
|
2356
2768
|
this._resetAgentLayoutStyles();
|
|
@@ -2396,7 +2808,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2396
2808
|
this._revealFrameId = null;
|
|
2397
2809
|
setStyleValue(this, "opacity", "1");
|
|
2398
2810
|
window.setTimeout(() => {
|
|
2399
|
-
|
|
2811
|
+
resetStyleValue(this, "will-change");
|
|
2400
2812
|
}, SHELL_REVEAL_FADE_MS);
|
|
2401
2813
|
});
|
|
2402
2814
|
}
|
|
@@ -2448,6 +2860,7 @@ var CharAgentShellElement = class extends HTMLElement {
|
|
|
2448
2860
|
return;
|
|
2449
2861
|
}
|
|
2450
2862
|
this._open = nextOpen;
|
|
2863
|
+
if (!nextOpen) this._currentPanelWidth = PANEL_WIDTH;
|
|
2451
2864
|
if (options.reflect) this._reflectOpenAttribute(nextOpen);
|
|
2452
2865
|
this._applyDisplayModeAndLayout();
|
|
2453
2866
|
if (options.emit) this.dispatchEvent(new CustomEvent(SHELL_OPEN_CHANGE_EVENT, {
|
|
@@ -2521,6 +2934,16 @@ function registerCharShell(tagName = "char-agent-shell") {
|
|
|
2521
2934
|
}
|
|
2522
2935
|
registerCharShell();
|
|
2523
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
|
+
|
|
2524
2947
|
//#endregion
|
|
2525
2948
|
export { DEFAULT_AVAILABLE_DISPLAY_MODES, isDisplayMode, isExpandedDisplayMode, registerChar, registerCharShell, resolvePolicyDisplayMode, resolveSupportedDisplayMode };
|
|
2526
2949
|
//# sourceMappingURL=index.js.map
|