@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/dist/utils.d.ts CHANGED
@@ -121,17 +121,6 @@ interface CharSafeAreaInsets {
121
121
  bottom?: number;
122
122
  left?: number;
123
123
  }
124
- /**
125
- * Host shell capability flags advertised to the iframe runtime.
126
- *
127
- * @public
128
- */
129
- interface CharHostCapabilities {
130
- supportedDisplayModes?: CharDisplayMode[];
131
- supportsOpenLink?: boolean;
132
- supportsTeardown?: boolean;
133
- maxContainerWidth?: number;
134
- }
135
124
  /**
136
125
  * Host context payload sent from the embedding page to the iframe runtime.
137
126
  *
@@ -148,8 +137,6 @@ interface CharHostContext {
148
137
  platform?: CharPlatform;
149
138
  deviceCapabilities?: CharDeviceCapabilities;
150
139
  safeAreaInsets?: CharSafeAreaInsets;
151
- userAgent?: string;
152
- hostCapabilities?: CharHostCapabilities;
153
140
  }
154
141
  //#endregion
155
142
  //#region src/utils/host-context-merge.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","names":[],"sources":["../src/utils/css-resolver.ts","../src/utils/css-sanitizer.ts","../src/utils/css-variables.ts","../src/types.ts","../src/utils/host-context-merge.ts"],"mappings":";;;AAUA;;;;;AAEA;;;cAFa,yBAAA;AAAA,iBAEG,iBAAA,CAAkB,KAAA,UAAe,cAAA;AAAA,iBAWjC,kBAAA,CAAmB,KAAA;AAAA,iBAWnB,eAAA,CACf,QAAA,sBACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;AAAA,iBAMe,kBAAA,CACf,YAAA,UACA,QAAA,sBACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;AAAA,iBAuBe,eAAA,CACf,KAAA,UACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;;;;AAlED;;;;;AAEA;;;;;AAWA;;;cCTa,6BAAA;AAAA,cACA,wBAAA;ADmBb;;;;;;;AAAA,iBCegB,wBAAA,CAAyB,KAAA,UAAe,YAAA;;;;;iBAuBxC,mBAAA,CAAoB,OAAA;;;;AD9DpC;;;;;AAEA;;;;;cE6Ma,uBAAA;;;;AD3Mb;;;;KEmDY,SAAA;AFlDZ;;;;;AAAA,KEyDY,eAAA;;;;;AFAZ;KEOY,YAAA;;;;;;UAOK,cAAA;EAChB,SAAA,GAAY,MAAA;EACZ,KAAA;AAAA;;;;;;UAQgB,uBAAA;EAChB,KAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;AAAA;;;;;AArBD;UA6BiB,sBAAA;EAChB,KAAA;EACA,KAAA;EACA,WAAA;AAAA;;;;;;UAQgB,kBAAA;EAChB,GAAA;EACA,KAAA;EACA,MAAA;EACA,IAAA;AAAA;;;;;;UAQgB,oBAAA;EAChB,qBAAA,GAAwB,eAAA;EACxB,gBAAA;EACA,gBAAA;EACA,iBAAA;AAAA;;;;;;UAQgB,eAAA;EAChB,KAAA,GAAQ,SAAA;EACR,MAAA,GAAS,cAAA;EACT,WAAA,GAAc,eAAA;EACd,qBAAA,GAAwB,eAAA;EACxB,mBAAA,GAAsB,uBAAA;EACtB,MAAA;EACA,QAAA;EACA,QAAA,GAAW,YAAA;EACX,kBAAA,GAAqB,sBAAA;EACrB,cAAA,GAAiB,kBAAA;EACjB,SAAA;EACA,gBAAA,GAAmB,oBAAA;AAAA;;;iBCjJJ,WAAA,CACf,OAAA,EAAS,eAAA,wBACT,KAAA,EAAO,eAAA,yBACL,eAAA;AAAA,iBAWa,YAAA,kBAAA,CACf,OAAA,EAAS,CAAA,cACT,KAAA,EAAO,CAAA,eACL,CAAA;AAAA,iBAKa,gBAAA,CACf,OAAA,EAAS,eAAA,SACT,KAAA,EAAO,eAAA,GACL,eAAA"}
1
+ {"version":3,"file":"utils.d.ts","names":[],"sources":["../src/utils/css-resolver.ts","../src/utils/css-sanitizer.ts","../src/utils/css-variables.ts","../src/types.ts","../src/utils/host-context-merge.ts"],"mappings":";;;AAUA;;;;;AAEA;;;cAFa,yBAAA;AAAA,iBAEG,iBAAA,CAAkB,KAAA,UAAe,cAAA;AAAA,iBAWjC,kBAAA,CAAmB,KAAA;AAAA,iBAWnB,eAAA,CACf,QAAA,sBACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;AAAA,iBAMe,kBAAA,CACf,YAAA,UACA,QAAA,sBACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;AAAA,iBAuBe,eAAA,CACf,KAAA,UACA,QAAA,EAAU,mBAAA,EACV,IAAA,EAAM,GAAA,UACN,KAAA;;;;AAlED;;;;;AAEA;;;;;AAWA;;;cCTa,6BAAA;AAAA,cACA,wBAAA;ADmBb;;;;;;;AAAA,iBCegB,wBAAA,CAAyB,KAAA,UAAe,YAAA;;;;;iBAuBxC,mBAAA,CAAoB,OAAA;;;;AD9DpC;;;;;AAEA;;;;;cE6Ma,uBAAA;;;;AFvLb;;;;KGbY,SAAA;;;;;;KAOA,eAAA;;AHgBZ;;;;KGTY,YAAA;;;;;;UAOK,cAAA;EAChB,SAAA,GAAY,MAAA;EACZ,KAAA;AAAA;;;;;;UAQgB,uBAAA;EAChB,KAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;AAAA;;;;AF1CD;;UEkDiB,sBAAA;EAChB,KAAA;EACA,KAAA;EACA,WAAA;AAAA;;;;AFlBD;;UE0BiB,kBAAA;EAChB,GAAA;EACA,KAAA;EACA,MAAA;EACA,IAAA;AAAA;;;;;;UAQgB,eAAA;EAChB,KAAA,GAAQ,SAAA;EACR,MAAA,GAAS,cAAA;EACT,WAAA,GAAc,eAAA;EACd,qBAAA,GAAwB,eAAA;EACxB,mBAAA,GAAsB,uBAAA;EACtB,MAAA;EACA,QAAA;EACA,QAAA,GAAW,YAAA;EACX,kBAAA,GAAqB,sBAAA;EACrB,cAAA,GAAiB,kBAAA;AAAA;;;iBCvFF,WAAA,CACf,OAAA,EAAS,eAAA,wBACT,KAAA,EAAO,eAAA,yBACL,eAAA;AAAA,iBAUa,YAAA,kBAAA,CACf,OAAA,EAAS,CAAA,cACT,KAAA,EAAO,CAAA,eACL,CAAA;AAAA,iBAKa,gBAAA,CACf,OAAA,EAAS,eAAA,SACT,KAAA,EAAO,eAAA,GACL,eAAA"}
package/dist/utils.js CHANGED
@@ -383,8 +383,7 @@ function mergeHostContext(current, patch) {
383
383
  styles: mergeStyles(previous.styles, patch.styles),
384
384
  containerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),
385
385
  deviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),
386
- safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets),
387
- hostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities)
386
+ safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
388
387
  };
389
388
  }
390
389
 
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../src/utils/css-resolver.ts","../src/utils/css-sanitizer.ts","../src/utils/css-variables.ts","../src/utils/host-context-merge.ts"],"sourcesContent":["/**\n * CSS var() Resolver\n *\n * Recursively resolves CSS custom property references (`var(--x, fallback)`)\n * to concrete values using getComputedStyle(). Necessary because `var()`\n * references are not valid across iframe boundaries.\n *\n * Handles nested var() references, fallbacks, and cycle detection.\n */\n\nexport const MAX_CSS_VAR_RESOLVE_DEPTH = 12\n\nexport function findMatchingParen(value: string, openParenIndex: number): number {\n\tlet depth = 1\n\tfor (let i = openParenIndex + 1; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth -= 1\n\t\tif (depth === 0) return i\n\t}\n\treturn -1\n}\n\nexport function splitTopLevelComma(value: string): [string, string | undefined] {\n\tlet depth = 0\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth = Math.max(0, depth - 1)\n\t\telse if (char === ',' && depth === 0) return [value.slice(0, i), value.slice(i + 1)]\n\t}\n\treturn [value, undefined]\n}\n\nexport function resolveFallback(\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (!fallback) return undefined\n\treturn resolveCssValue(fallback, computed, seen, depth + 1) || undefined\n}\n\nexport function resolveCssVariable(\n\tvariableName: string,\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\tconsole.warn(`[Char] CSS variable resolution depth exceeded for \"${variableName}\". Possible circular reference.`)\n\t\treturn fallback?.trim()\n\t}\n\n\tif (!variableName.startsWith('--') || seen.has(variableName)) {\n\t\treturn resolveFallback(fallback, computed, seen, depth)\n\t}\n\n\tconst nextSeen = new Set(seen)\n\tnextSeen.add(variableName)\n\n\tconst rawValue = computed.getPropertyValue(variableName).trim()\n\tif (rawValue) {\n\t\tconst resolvedRaw = resolveCssValue(rawValue, computed, nextSeen, depth + 1)\n\t\tif (resolvedRaw) return resolvedRaw\n\t}\n\n\treturn resolveFallback(fallback, computed, seen, depth)\n}\n\nexport function resolveCssValue(\n\tvalue: string,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth = 0,\n): string {\n\tconst trimmed = value.trim()\n\tif (!trimmed || !trimmed.includes('var(') || depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\treturn trimmed\n\t}\n\n\tlet cursor = 0\n\tlet resolved = ''\n\tlet replacedAny = false\n\n\twhile (cursor < trimmed.length) {\n\t\tconst varStart = trimmed.indexOf('var(', cursor)\n\t\tif (varStart === -1) {\n\t\t\tresolved += trimmed.slice(cursor)\n\t\t\tbreak\n\t\t}\n\n\t\tresolved += trimmed.slice(cursor, varStart)\n\n\t\tconst openParenIndex = varStart + 3\n\t\tconst closeParenIndex = findMatchingParen(trimmed, openParenIndex)\n\t\tif (closeParenIndex === -1) {\n\t\t\tresolved += trimmed.slice(varStart)\n\t\t\tbreak\n\t\t}\n\n\t\tconst rawArgs = trimmed.slice(openParenIndex + 1, closeParenIndex)\n\t\tconst [rawVariableName, rawFallback] = splitTopLevelComma(rawArgs)\n\t\tconst variableName = rawVariableName.trim()\n\t\tconst fallback = rawFallback?.trim()\n\n\t\tconst replacement = variableName\n\t\t\t? resolveCssVariable(variableName, fallback, computed, seen, depth + 1)\n\t\t\t: undefined\n\n\t\tif (replacement !== undefined) {\n\t\t\tresolved += replacement\n\t\t\treplacedAny = true\n\t\t} else {\n\t\t\tresolved += trimmed.slice(varStart, closeParenIndex + 1)\n\t\t}\n\n\t\tcursor = closeParenIndex + 1\n\t}\n\n\tconst normalized = resolved.trim()\n\tif (!replacedAny || !normalized.includes('var(')) {\n\t\treturn normalized\n\t}\n\n\treturn resolveCssValue(normalized, computed, seen, depth + 1)\n}\n","/**\n * Maximum allowed length for CSS custom property values transported to iframe.\n *\n * NOTE: This value and the blocked-token policy intentionally mirror\n * `packages/shared-types/src/schemas/embed-bridge.ts` so validation happens\n * both before transport (host) and at contract parse-time (iframe).\n *\n * CONTROL_CHAR_PATTERN here is intentionally more permissive than\n * embed-bridge.ts: it allows tabs/newlines/CR/FF because\n * normalizeCssFormattingWhitespace converts them to spaces before the\n * control-char check. The iframe-side schema (embed-bridge.ts) uses\n * the stricter pattern as defense-in-depth for values that bypass\n * host-side sanitization.\n */\nexport const MAX_CSS_VARIABLE_VALUE_LENGTH = 1024\nexport const MAX_HOST_FONT_CSS_LENGTH = 16_384\n\nconst CONTROL_CHAR_PATTERN = /[\\u0000-\\u0008\\u000b\\u000e-\\u001f\\u007f]/u\nconst FONT_RULES_PATTERN = /^(?:\\s*@font-face\\s*\\{[^{}]*\\}\\s*)+$/iu\nconst CSS_COMMENT_PATTERN = /\\/\\*[\\s\\S]*?\\*\\//g\nconst CSS_FORMATTING_WHITESPACE_PATTERN = /[\\t\\n\\r\\f]+/gu\n\n/**\n * Normalizes CSS values for dangerous-token scanning.\n * Lowercases and removes CSS comments/whitespace/control chars to catch obfuscation.\n */\nfunction normalizeForSecurityScan(value: string): string {\n\tconst withoutComments = value.replace(CSS_COMMENT_PATTERN, '')\n\treturn withoutComments.toLowerCase().replace(/[\\s\\u0000-\\u001f\\u007f]+/gu, '')\n}\n\n/**\n * Collapses CSS formatting whitespace (tabs, newlines, carriage returns, form feeds)\n * into single spaces. Applied to the output value before trimming, so multiline\n * host-provided values (e.g., font stacks) become single-line without losing\n * meaningful token boundaries. This also ensures split-token obfuscation\n * (e.g., \"java\\nscript:\") is collapsed before the security scan.\n */\nfunction normalizeCssFormattingWhitespace(value: string): string {\n\treturn value.replace(CSS_FORMATTING_WHITESPACE_PATTERN, ' ')\n}\n\n/**\n * Sanitizes host-provided CSS variable values before iframe transport.\n * Returns `undefined` for unsafe or invalid values.\n *\n * @param value - The raw CSS property value.\n * @param variableName - Optional variable name for diagnostic logging.\n */\nexport function sanitizeCssVariableValue(value: string, variableName?: string): string | undefined {\n\tconst trimmed = normalizeCssFormattingWhitespace(value).trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_CSS_VARIABLE_VALUE_LENGTH) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: value exceeds max length (${trimmed.length} > ${MAX_CSS_VARIABLE_VALUE_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains control characters`)\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedCssVariableToken(normalized)\n\tif (blockedToken) return warnAndReject(variableName, blockedToken)\n\n\treturn trimmed\n}\n\n/**\n * Sanitizes host-provided font CSS before iframe injection.\n * Only `@font-face` rules are allowed.\n */\nexport function sanitizeHostFontCss(fontCss: string): string | undefined {\n\tconst trimmed = fontCss.trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_HOST_FONT_CSS_LENGTH) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: value exceeds max length (${trimmed.length} > ${MAX_HOST_FONT_CSS_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: contains control characters')\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedFontToken(normalized)\n\tif (blockedToken) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: contains blocked token \"${blockedToken}\"`)\n\t\treturn undefined\n\t}\n\n\tconst withoutComments = trimmed.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').trim()\n\tif (!FONT_RULES_PATTERN.test(withoutComments)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: only @font-face rules are allowed')\n\t\treturn undefined\n\t}\n\n\treturn trimmed\n}\n\nfunction getBlockedCssVariableToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('@import')) return '@import'\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\tif (normalizedValue.includes('url(')) return 'url('\n\treturn undefined\n}\n\nfunction getBlockedFontToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\treturn undefined\n}\n\nfunction warnAndReject(variableName: string | undefined, blockedToken: string): undefined {\n\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains blocked token \"${blockedToken}\"`)\n\treturn undefined\n}\n","/**\n * Public CSS variable names for the Char widget.\n *\n * These are the external API — host pages set these to customise the widget.\n * We maintain static lists because `getComputedStyle()` cannot enumerate\n * custom properties.\n *\n * Source of truth:\n * - `src/styles/globals.css` (`--char-*`)\n * - WebMCP ext-apps UI spec (`--color-*`, `--font-*`, etc.)\n */\n\nconst CHAR_PUBLIC_VARIABLE_NAMES = [\n\t// Colors - Core\n\t'--char-color-background',\n\t'--char-color-foreground',\n\t'--char-color-card',\n\t'--char-color-card-foreground',\n\t'--char-color-popover',\n\t'--char-color-popover-foreground',\n\t'--char-color-primary',\n\t'--char-color-primary-foreground',\n\t'--char-color-secondary',\n\t'--char-color-secondary-foreground',\n\t'--char-color-muted',\n\t'--char-color-muted-foreground',\n\t'--char-color-accent',\n\t'--char-color-accent-foreground',\n\t'--char-color-destructive',\n\t'--char-color-destructive-foreground',\n\t'--char-color-border',\n\n\t// Colors - Semantic\n\t'--char-color-success',\n\t'--char-color-warning',\n\t'--char-color-error',\n\n\t// Colors - Derived\n\t'--char-color-input',\n\t'--char-color-ring',\n\n\t// Message Bubbles\n\t'--char-user-bubble-bg',\n\t'--char-user-bubble-text',\n\t'--char-assistant-bubble-bg',\n\t'--char-assistant-bubble-text',\n\n\t// Composer\n\t'--char-composer-bg',\n\t'--char-composer-border',\n\t'--char-composer-text',\n\t'--char-composer-placeholder',\n\t'--char-composer-button-bg',\n\t'--char-composer-button-text',\n\n\t// Tool Cards\n\t'--char-tool-bg',\n\t'--char-tool-border',\n\t'--char-tool-text',\n\t'--char-tool-header-bg',\n\t'--char-tool-approve-bg',\n\t'--char-tool-approve-text',\n\t'--char-tool-deny-bg',\n\t'--char-tool-deny-text',\n\n\t// Code Blocks\n\t'--char-code-bg',\n\t'--char-code-text',\n\t'--char-code-header-bg',\n\n\t// Sizing\n\t'--char-radius',\n\t'--char-radius-sm',\n\t'--char-radius-md',\n\t'--char-radius-lg',\n\t'--char-radius-xl',\n\t'--char-radius-2xl',\n\t'--char-radius-3xl',\n\t'--char-radius-full',\n\t'--char-spacing-unit',\n\n\t// Typography\n\t'--char-font-sans',\n\t'--char-font-mono',\n\t'--char-font-size-xs',\n\t'--char-font-size-sm',\n\t'--char-font-size-base',\n\t'--char-font-size-lg',\n\n\t// Motion\n\t'--char-duration-fast',\n\t'--char-duration-normal',\n\t'--char-duration-slow',\n\t'--char-easing-default',\n\t'--char-easing-spring',\n\n\t// Z-index\n\t'--char-z-base',\n\t'--char-z-content',\n\t'--char-z-overlay',\n\t'--char-z-max',\n\n\t// Shadows\n\t'--char-shadow-sm',\n\t'--char-shadow-md',\n\t'--char-shadow-lg',\n\n\t// Blur\n\t'--char-blur-sm',\n\t'--char-blur-md',\n\t'--char-blur-lg',\n] as const\n\nconst MCP_UI_STYLE_VARIABLE_NAMES = [\n\t// Background colors\n\t'--color-background-primary',\n\t'--color-background-secondary',\n\t'--color-background-tertiary',\n\t'--color-background-inverse',\n\t'--color-background-ghost',\n\t'--color-background-info',\n\t'--color-background-danger',\n\t'--color-background-success',\n\t'--color-background-warning',\n\t'--color-background-disabled',\n\n\t// Text colors\n\t'--color-text-primary',\n\t'--color-text-secondary',\n\t'--color-text-tertiary',\n\t'--color-text-inverse',\n\t'--color-text-ghost',\n\t'--color-text-info',\n\t'--color-text-danger',\n\t'--color-text-success',\n\t'--color-text-warning',\n\t'--color-text-disabled',\n\n\t// Border colors\n\t'--color-border-primary',\n\t'--color-border-secondary',\n\t'--color-border-tertiary',\n\t'--color-border-inverse',\n\t'--color-border-ghost',\n\t'--color-border-info',\n\t'--color-border-danger',\n\t'--color-border-success',\n\t'--color-border-warning',\n\t'--color-border-disabled',\n\n\t// Ring colors\n\t'--color-ring-primary',\n\t'--color-ring-secondary',\n\t'--color-ring-inverse',\n\t'--color-ring-info',\n\t'--color-ring-danger',\n\t'--color-ring-success',\n\t'--color-ring-warning',\n\n\t// Typography - Family\n\t'--font-sans',\n\t'--font-mono',\n\n\t// Typography - Weight\n\t'--font-weight-normal',\n\t'--font-weight-medium',\n\t'--font-weight-semibold',\n\t'--font-weight-bold',\n\n\t// Typography - Text Size\n\t'--font-text-xs-size',\n\t'--font-text-sm-size',\n\t'--font-text-md-size',\n\t'--font-text-lg-size',\n\n\t// Typography - Heading Size\n\t'--font-heading-xs-size',\n\t'--font-heading-sm-size',\n\t'--font-heading-md-size',\n\t'--font-heading-lg-size',\n\t'--font-heading-xl-size',\n\t'--font-heading-2xl-size',\n\t'--font-heading-3xl-size',\n\n\t// Typography - Text Line Height\n\t'--font-text-xs-line-height',\n\t'--font-text-sm-line-height',\n\t'--font-text-md-line-height',\n\t'--font-text-lg-line-height',\n\n\t// Typography - Heading Line Height\n\t'--font-heading-xs-line-height',\n\t'--font-heading-sm-line-height',\n\t'--font-heading-md-line-height',\n\t'--font-heading-lg-line-height',\n\t'--font-heading-xl-line-height',\n\t'--font-heading-2xl-line-height',\n\t'--font-heading-3xl-line-height',\n\n\t// Border radius\n\t'--border-radius-xs',\n\t'--border-radius-sm',\n\t'--border-radius-md',\n\t'--border-radius-lg',\n\t'--border-radius-xl',\n\t'--border-radius-full',\n\n\t// Border width\n\t'--border-width-regular',\n\n\t// Shadows\n\t'--shadow-hairline',\n\t'--shadow-sm',\n\t'--shadow-md',\n\t'--shadow-lg',\n] as const\n\nexport const CHAR_CSS_VARIABLE_NAMES = [\n\t...CHAR_PUBLIC_VARIABLE_NAMES,\n\t...MCP_UI_STYLE_VARIABLE_NAMES,\n] as const\n","/**\n * Host Context Merge Utilities\n *\n * Canonical merge logic for CharHostContext deltas. Used by both the host-side\n * web component and the iframe-side useHostContext hook to ensure identical\n * merge behavior on both sides of the postMessage bridge.\n */\n\nimport type { CharHostContext } from '../types'\n\nexport function mergeStyles(\n\tcurrent: CharHostContext['styles'] | undefined,\n\tpatch: CharHostContext['styles'] | undefined,\n): CharHostContext['styles'] | undefined {\n\tif (!patch) return current\n\treturn {\n\t\t...current,\n\t\t...patch,\n\t\t// variables are sent as a snapshot so removals propagate\n\t\tvariables: patch.variables ?? current?.variables,\n\t\tfonts: patch.fonts ?? current?.fonts,\n\t}\n}\n\nexport function shallowMerge<T extends object>(\n\tcurrent: T | undefined,\n\tpatch: T | undefined,\n): T | undefined {\n\tif (!patch) return current\n\treturn { ...current, ...patch }\n}\n\nexport function mergeHostContext(\n\tcurrent: CharHostContext | null,\n\tpatch: CharHostContext,\n): CharHostContext {\n\tconst previous = current ?? {}\n\treturn {\n\t\t...previous,\n\t\t...patch,\n\t\tstyles: mergeStyles(previous.styles, patch.styles),\n\t\tcontainerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),\n\t\tdeviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),\n\t\tsafeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets),\n\t\thostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities),\n\t}\n}\n"],"mappings":";;;;;;;;;;;AAUA,MAAa,4BAA4B;AAEzC,SAAgB,kBAAkB,OAAe,gBAAgC;CAChF,IAAI,QAAQ;AACZ,MAAK,IAAI,IAAI,iBAAiB,GAAG,IAAI,MAAM,QAAQ,KAAK;EACvD,MAAM,OAAO,MAAM;AACnB,MAAI,SAAS,IAAK,UAAS;WAClB,SAAS,IAAK,UAAS;AAChC,MAAI,UAAU,EAAG,QAAO;;AAEzB,QAAO;;AAGR,SAAgB,mBAAmB,OAA6C;CAC/E,IAAI,QAAQ;AACZ,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACtC,MAAM,OAAO,MAAM;AACnB,MAAI,SAAS,IAAK,UAAS;WAClB,SAAS,IAAK,SAAQ,KAAK,IAAI,GAAG,QAAQ,EAAE;WAC5C,SAAS,OAAO,UAAU,EAAG,QAAO,CAAC,MAAM,MAAM,GAAG,EAAE,EAAE,MAAM,MAAM,IAAI,EAAE,CAAC;;AAErF,QAAO,CAAC,OAAO,OAAU;;AAG1B,SAAgB,gBACf,UACA,UACA,MACA,OACqB;AACrB,KAAI,CAAC,SAAU,QAAO;AACtB,QAAO,gBAAgB,UAAU,UAAU,MAAM,QAAQ,EAAE,IAAI;;AAGhE,SAAgB,mBACf,cACA,UACA,UACA,MACA,OACqB;AACrB,KAAI,QAAQ,2BAA2B;AACtC,UAAQ,KAAK,sDAAsD,aAAa,iCAAiC;AACjH,SAAO,UAAU,MAAM;;AAGxB,KAAI,CAAC,aAAa,WAAW,KAAK,IAAI,KAAK,IAAI,aAAa,CAC3D,QAAO,gBAAgB,UAAU,UAAU,MAAM,MAAM;CAGxD,MAAM,WAAW,IAAI,IAAI,KAAK;AAC9B,UAAS,IAAI,aAAa;CAE1B,MAAM,WAAW,SAAS,iBAAiB,aAAa,CAAC,MAAM;AAC/D,KAAI,UAAU;EACb,MAAM,cAAc,gBAAgB,UAAU,UAAU,UAAU,QAAQ,EAAE;AAC5E,MAAI,YAAa,QAAO;;AAGzB,QAAO,gBAAgB,UAAU,UAAU,MAAM,MAAM;;AAGxD,SAAgB,gBACf,OACA,UACA,MACA,QAAQ,GACC;CACT,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,CAAC,WAAW,CAAC,QAAQ,SAAS,OAAO,IAAI,QAAQ,0BACpD,QAAO;CAGR,IAAI,SAAS;CACb,IAAI,WAAW;CACf,IAAI,cAAc;AAElB,QAAO,SAAS,QAAQ,QAAQ;EAC/B,MAAM,WAAW,QAAQ,QAAQ,QAAQ,OAAO;AAChD,MAAI,aAAa,IAAI;AACpB,eAAY,QAAQ,MAAM,OAAO;AACjC;;AAGD,cAAY,QAAQ,MAAM,QAAQ,SAAS;EAE3C,MAAM,iBAAiB,WAAW;EAClC,MAAM,kBAAkB,kBAAkB,SAAS,eAAe;AAClE,MAAI,oBAAoB,IAAI;AAC3B,eAAY,QAAQ,MAAM,SAAS;AACnC;;EAID,MAAM,CAAC,iBAAiB,eAAe,mBADvB,QAAQ,MAAM,iBAAiB,GAAG,gBAAgB,CACA;EAClE,MAAM,eAAe,gBAAgB,MAAM;EAC3C,MAAM,WAAW,aAAa,MAAM;EAEpC,MAAM,cAAc,eACjB,mBAAmB,cAAc,UAAU,UAAU,MAAM,QAAQ,EAAE,GACrE;AAEH,MAAI,gBAAgB,QAAW;AAC9B,eAAY;AACZ,iBAAc;QAEd,aAAY,QAAQ,MAAM,UAAU,kBAAkB,EAAE;AAGzD,WAAS,kBAAkB;;CAG5B,MAAM,aAAa,SAAS,MAAM;AAClC,KAAI,CAAC,eAAe,CAAC,WAAW,SAAS,OAAO,CAC/C,QAAO;AAGR,QAAO,gBAAgB,YAAY,UAAU,MAAM,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;ACjH9D,MAAa,gCAAgC;AAC7C,MAAa,2BAA2B;AAExC,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,oCAAoC;;;;;AAM1C,SAAS,yBAAyB,OAAuB;AAExD,QADwB,MAAM,QAAQ,qBAAqB,GAAG,CACvC,aAAa,CAAC,QAAQ,8BAA8B,GAAG;;;;;;;;;AAU/E,SAAS,iCAAiC,OAAuB;AAChE,QAAO,MAAM,QAAQ,mCAAmC,IAAI;;;;;;;;;AAU7D,SAAgB,yBAAyB,OAAe,cAA2C;CAClG,MAAM,UAAU,iCAAiC,MAAM,CAAC,MAAM;AAC9D,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI,QAAQ,SAAS,+BAA+B;AACnD,UAAQ,KAAK,uBAAuB,gBAAgB,YAAY,uCAAuC,QAAQ,OAAO,KAAK,8BAA8B,GAAG;AAC5J;;AAED,KAAI,qBAAqB,KAAK,QAAQ,EAAE;AACvC,UAAQ,KAAK,uBAAuB,gBAAgB,YAAY,wCAAwC;AACxG;;CAID,MAAM,eAAe,2BADF,yBAAyB,QAAQ,CACO;AAC3D,KAAI,aAAc,QAAO,cAAc,cAAc,aAAa;AAElE,QAAO;;;;;;AAOR,SAAgB,oBAAoB,SAAqC;CACxE,MAAM,UAAU,QAAQ,MAAM;AAC9B,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI,QAAQ,SAAS,0BAA0B;AAC9C,UAAQ,KAAK,4DAA4D,QAAQ,OAAO,KAAK,yBAAyB,GAAG;AACzH;;AAED,KAAI,qBAAqB,KAAK,QAAQ,EAAE;AACvC,UAAQ,KAAK,6DAA6D;AAC1E;;CAID,MAAM,eAAe,oBADF,yBAAyB,QAAQ,CACA;AACpD,KAAI,cAAc;AACjB,UAAQ,KAAK,0DAA0D,aAAa,GAAG;AACvF;;CAGD,MAAM,kBAAkB,QAAQ,QAAQ,qBAAqB,GAAG,CAAC,MAAM;AACvE,KAAI,CAAC,mBAAmB,KAAK,gBAAgB,EAAE;AAC9C,UAAQ,KAAK,mEAAmE;AAChF;;AAGD,QAAO;;AAGR,SAAS,2BAA2B,iBAA6C;AAChF,KAAI,gBAAgB,SAAS,cAAc,CAAE,QAAO;AACpD,KAAI,gBAAgB,SAAS,YAAY,CAAE,QAAO;AAClD,KAAI,gBAAgB,SAAS,cAAc,CAAE,QAAO;AACpD,KAAI,gBAAgB,SAAS,UAAU,CAAE,QAAO;AAChD,KAAI,gBAAgB,SAAS,eAAe,CAAE,QAAO;AACrD,KAAI,gBAAgB,SAAS,OAAO,CAAE,QAAO;;AAI9C,SAAS,oBAAoB,iBAA6C;AACzE,KAAI,gBAAgB,SAAS,cAAc,CAAE,QAAO;AACpD,KAAI,gBAAgB,SAAS,YAAY,CAAE,QAAO;AAClD,KAAI,gBAAgB,SAAS,cAAc,CAAE,QAAO;AACpD,KAAI,gBAAgB,SAAS,eAAe,CAAE,QAAO;;AAItD,SAAS,cAAc,cAAkC,cAAiC;AACzF,SAAQ,KAAK,uBAAuB,gBAAgB,YAAY,qCAAqC,aAAa,GAAG;;;;;;;;;;;;;;;;AC3GtH,MAAM,6BAA6B;CAElC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;AAED,MAAM,8BAA8B;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CAGA;CACA;CACA;CACA;CACA;AAED,MAAa,0BAA0B,CACtC,GAAG,4BACH,GAAG,4BACH;;;;AClND,SAAgB,YACf,SACA,OACwC;AACxC,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EACN,GAAG;EACH,GAAG;EAEH,WAAW,MAAM,aAAa,SAAS;EACvC,OAAO,MAAM,SAAS,SAAS;EAC/B;;AAGF,SAAgB,aACf,SACA,OACgB;AAChB,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EAAE,GAAG;EAAS,GAAG;EAAO;;AAGhC,SAAgB,iBACf,SACA,OACkB;CAClB,MAAM,WAAW,WAAW,EAAE;AAC9B,QAAO;EACN,GAAG;EACH,GAAG;EACH,QAAQ,YAAY,SAAS,QAAQ,MAAM,OAAO;EAClD,qBAAqB,aAAa,SAAS,qBAAqB,MAAM,oBAAoB;EAC1F,oBAAoB,aAAa,SAAS,oBAAoB,MAAM,mBAAmB;EACvF,gBAAgB,aAAa,SAAS,gBAAgB,MAAM,eAAe;EAC3E,kBAAkB,aAAa,SAAS,kBAAkB,MAAM,iBAAiB;EACjF"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../src/utils/css-resolver.ts","../src/utils/css-sanitizer.ts","../src/utils/css-variables.ts","../src/utils/host-context-merge.ts"],"sourcesContent":["/**\n * CSS var() Resolver\n *\n * Recursively resolves CSS custom property references (`var(--x, fallback)`)\n * to concrete values using getComputedStyle(). Necessary because `var()`\n * references are not valid across iframe boundaries.\n *\n * Handles nested var() references, fallbacks, and cycle detection.\n */\n\nexport const MAX_CSS_VAR_RESOLVE_DEPTH = 12\n\nexport function findMatchingParen(value: string, openParenIndex: number): number {\n\tlet depth = 1\n\tfor (let i = openParenIndex + 1; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth -= 1\n\t\tif (depth === 0) return i\n\t}\n\treturn -1\n}\n\nexport function splitTopLevelComma(value: string): [string, string | undefined] {\n\tlet depth = 0\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth = Math.max(0, depth - 1)\n\t\telse if (char === ',' && depth === 0) return [value.slice(0, i), value.slice(i + 1)]\n\t}\n\treturn [value, undefined]\n}\n\nexport function resolveFallback(\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (!fallback) return undefined\n\treturn resolveCssValue(fallback, computed, seen, depth + 1) || undefined\n}\n\nexport function resolveCssVariable(\n\tvariableName: string,\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\tconsole.warn(`[Char] CSS variable resolution depth exceeded for \"${variableName}\". Possible circular reference.`)\n\t\treturn fallback?.trim()\n\t}\n\n\tif (!variableName.startsWith('--') || seen.has(variableName)) {\n\t\treturn resolveFallback(fallback, computed, seen, depth)\n\t}\n\n\tconst nextSeen = new Set(seen)\n\tnextSeen.add(variableName)\n\n\tconst rawValue = computed.getPropertyValue(variableName).trim()\n\tif (rawValue) {\n\t\tconst resolvedRaw = resolveCssValue(rawValue, computed, nextSeen, depth + 1)\n\t\tif (resolvedRaw) return resolvedRaw\n\t}\n\n\treturn resolveFallback(fallback, computed, seen, depth)\n}\n\nexport function resolveCssValue(\n\tvalue: string,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth = 0,\n): string {\n\tconst trimmed = value.trim()\n\tif (!trimmed || !trimmed.includes('var(') || depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\treturn trimmed\n\t}\n\n\tlet cursor = 0\n\tlet resolved = ''\n\tlet replacedAny = false\n\n\twhile (cursor < trimmed.length) {\n\t\tconst varStart = trimmed.indexOf('var(', cursor)\n\t\tif (varStart === -1) {\n\t\t\tresolved += trimmed.slice(cursor)\n\t\t\tbreak\n\t\t}\n\n\t\tresolved += trimmed.slice(cursor, varStart)\n\n\t\tconst openParenIndex = varStart + 3\n\t\tconst closeParenIndex = findMatchingParen(trimmed, openParenIndex)\n\t\tif (closeParenIndex === -1) {\n\t\t\tresolved += trimmed.slice(varStart)\n\t\t\tbreak\n\t\t}\n\n\t\tconst rawArgs = trimmed.slice(openParenIndex + 1, closeParenIndex)\n\t\tconst [rawVariableName, rawFallback] = splitTopLevelComma(rawArgs)\n\t\tconst variableName = rawVariableName.trim()\n\t\tconst fallback = rawFallback?.trim()\n\n\t\tconst replacement = variableName\n\t\t\t? resolveCssVariable(variableName, fallback, computed, seen, depth + 1)\n\t\t\t: undefined\n\n\t\tif (replacement !== undefined) {\n\t\t\tresolved += replacement\n\t\t\treplacedAny = true\n\t\t} else {\n\t\t\tresolved += trimmed.slice(varStart, closeParenIndex + 1)\n\t\t}\n\n\t\tcursor = closeParenIndex + 1\n\t}\n\n\tconst normalized = resolved.trim()\n\tif (!replacedAny || !normalized.includes('var(')) {\n\t\treturn normalized\n\t}\n\n\treturn resolveCssValue(normalized, computed, seen, depth + 1)\n}\n","/**\n * Maximum allowed length for CSS custom property values transported to iframe.\n *\n * NOTE: This value and the blocked-token policy intentionally mirror\n * `packages/shared-types/src/schemas/embed-bridge.ts` so validation happens\n * both before transport (host) and at contract parse-time (iframe).\n *\n * CONTROL_CHAR_PATTERN here is intentionally more permissive than\n * embed-bridge.ts: it allows tabs/newlines/CR/FF because\n * normalizeCssFormattingWhitespace converts them to spaces before the\n * control-char check. The iframe-side schema (embed-bridge.ts) uses\n * the stricter pattern as defense-in-depth for values that bypass\n * host-side sanitization.\n */\nexport const MAX_CSS_VARIABLE_VALUE_LENGTH = 1024\nexport const MAX_HOST_FONT_CSS_LENGTH = 16_384\n\nconst CONTROL_CHAR_PATTERN = /[\\u0000-\\u0008\\u000b\\u000e-\\u001f\\u007f]/u\nconst FONT_RULES_PATTERN = /^(?:\\s*@font-face\\s*\\{[^{}]*\\}\\s*)+$/iu\nconst CSS_COMMENT_PATTERN = /\\/\\*[\\s\\S]*?\\*\\//g\nconst CSS_FORMATTING_WHITESPACE_PATTERN = /[\\t\\n\\r\\f]+/gu\n\n/**\n * Normalizes CSS values for dangerous-token scanning.\n * Lowercases and removes CSS comments/whitespace/control chars to catch obfuscation.\n */\nfunction normalizeForSecurityScan(value: string): string {\n\tconst withoutComments = value.replace(CSS_COMMENT_PATTERN, '')\n\treturn withoutComments.toLowerCase().replace(/[\\s\\u0000-\\u001f\\u007f]+/gu, '')\n}\n\n/**\n * Collapses CSS formatting whitespace (tabs, newlines, carriage returns, form feeds)\n * into single spaces. Applied to the output value before trimming, so multiline\n * host-provided values (e.g., font stacks) become single-line without losing\n * meaningful token boundaries. This also ensures split-token obfuscation\n * (e.g., \"java\\nscript:\") is collapsed before the security scan.\n */\nfunction normalizeCssFormattingWhitespace(value: string): string {\n\treturn value.replace(CSS_FORMATTING_WHITESPACE_PATTERN, ' ')\n}\n\n/**\n * Sanitizes host-provided CSS variable values before iframe transport.\n * Returns `undefined` for unsafe or invalid values.\n *\n * @param value - The raw CSS property value.\n * @param variableName - Optional variable name for diagnostic logging.\n */\nexport function sanitizeCssVariableValue(value: string, variableName?: string): string | undefined {\n\tconst trimmed = normalizeCssFormattingWhitespace(value).trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_CSS_VARIABLE_VALUE_LENGTH) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: value exceeds max length (${trimmed.length} > ${MAX_CSS_VARIABLE_VALUE_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains control characters`)\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedCssVariableToken(normalized)\n\tif (blockedToken) return warnAndReject(variableName, blockedToken)\n\n\treturn trimmed\n}\n\n/**\n * Sanitizes host-provided font CSS before iframe injection.\n * Only `@font-face` rules are allowed.\n */\nexport function sanitizeHostFontCss(fontCss: string): string | undefined {\n\tconst trimmed = fontCss.trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_HOST_FONT_CSS_LENGTH) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: value exceeds max length (${trimmed.length} > ${MAX_HOST_FONT_CSS_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: contains control characters')\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedFontToken(normalized)\n\tif (blockedToken) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: contains blocked token \"${blockedToken}\"`)\n\t\treturn undefined\n\t}\n\n\tconst withoutComments = trimmed.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').trim()\n\tif (!FONT_RULES_PATTERN.test(withoutComments)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: only @font-face rules are allowed')\n\t\treturn undefined\n\t}\n\n\treturn trimmed\n}\n\nfunction getBlockedCssVariableToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('@import')) return '@import'\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\tif (normalizedValue.includes('url(')) return 'url('\n\treturn undefined\n}\n\nfunction getBlockedFontToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\treturn undefined\n}\n\nfunction warnAndReject(variableName: string | undefined, blockedToken: string): undefined {\n\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains blocked token \"${blockedToken}\"`)\n\treturn undefined\n}\n","/**\n * Public CSS variable names for the Char widget.\n *\n * These are the external API — host pages set these to customise the widget.\n * We maintain static lists because `getComputedStyle()` cannot enumerate\n * custom properties.\n *\n * Source of truth:\n * - `src/styles/globals.css` (`--char-*`)\n * - WebMCP ext-apps UI spec (`--color-*`, `--font-*`, etc.)\n */\n\nconst CHAR_PUBLIC_VARIABLE_NAMES = [\n\t// Colors - Core\n\t'--char-color-background',\n\t'--char-color-foreground',\n\t'--char-color-card',\n\t'--char-color-card-foreground',\n\t'--char-color-popover',\n\t'--char-color-popover-foreground',\n\t'--char-color-primary',\n\t'--char-color-primary-foreground',\n\t'--char-color-secondary',\n\t'--char-color-secondary-foreground',\n\t'--char-color-muted',\n\t'--char-color-muted-foreground',\n\t'--char-color-accent',\n\t'--char-color-accent-foreground',\n\t'--char-color-destructive',\n\t'--char-color-destructive-foreground',\n\t'--char-color-border',\n\n\t// Colors - Semantic\n\t'--char-color-success',\n\t'--char-color-warning',\n\t'--char-color-error',\n\n\t// Colors - Derived\n\t'--char-color-input',\n\t'--char-color-ring',\n\n\t// Message Bubbles\n\t'--char-user-bubble-bg',\n\t'--char-user-bubble-text',\n\t'--char-assistant-bubble-bg',\n\t'--char-assistant-bubble-text',\n\n\t// Composer\n\t'--char-composer-bg',\n\t'--char-composer-border',\n\t'--char-composer-text',\n\t'--char-composer-placeholder',\n\t'--char-composer-button-bg',\n\t'--char-composer-button-text',\n\n\t// Tool Cards\n\t'--char-tool-bg',\n\t'--char-tool-border',\n\t'--char-tool-text',\n\t'--char-tool-header-bg',\n\t'--char-tool-approve-bg',\n\t'--char-tool-approve-text',\n\t'--char-tool-deny-bg',\n\t'--char-tool-deny-text',\n\n\t// Code Blocks\n\t'--char-code-bg',\n\t'--char-code-text',\n\t'--char-code-header-bg',\n\n\t// Sizing\n\t'--char-radius',\n\t'--char-radius-sm',\n\t'--char-radius-md',\n\t'--char-radius-lg',\n\t'--char-radius-xl',\n\t'--char-radius-2xl',\n\t'--char-radius-3xl',\n\t'--char-radius-full',\n\t'--char-spacing-unit',\n\n\t// Typography\n\t'--char-font-sans',\n\t'--char-font-mono',\n\t'--char-font-size-xs',\n\t'--char-font-size-sm',\n\t'--char-font-size-base',\n\t'--char-font-size-lg',\n\n\t// Motion\n\t'--char-duration-fast',\n\t'--char-duration-normal',\n\t'--char-duration-slow',\n\t'--char-easing-default',\n\t'--char-easing-spring',\n\n\t// Z-index\n\t'--char-z-base',\n\t'--char-z-content',\n\t'--char-z-overlay',\n\t'--char-z-max',\n\n\t// Shadows\n\t'--char-shadow-sm',\n\t'--char-shadow-md',\n\t'--char-shadow-lg',\n\n\t// Blur\n\t'--char-blur-sm',\n\t'--char-blur-md',\n\t'--char-blur-lg',\n] as const\n\nconst MCP_UI_STYLE_VARIABLE_NAMES = [\n\t// Background colors\n\t'--color-background-primary',\n\t'--color-background-secondary',\n\t'--color-background-tertiary',\n\t'--color-background-inverse',\n\t'--color-background-ghost',\n\t'--color-background-info',\n\t'--color-background-danger',\n\t'--color-background-success',\n\t'--color-background-warning',\n\t'--color-background-disabled',\n\n\t// Text colors\n\t'--color-text-primary',\n\t'--color-text-secondary',\n\t'--color-text-tertiary',\n\t'--color-text-inverse',\n\t'--color-text-ghost',\n\t'--color-text-info',\n\t'--color-text-danger',\n\t'--color-text-success',\n\t'--color-text-warning',\n\t'--color-text-disabled',\n\n\t// Border colors\n\t'--color-border-primary',\n\t'--color-border-secondary',\n\t'--color-border-tertiary',\n\t'--color-border-inverse',\n\t'--color-border-ghost',\n\t'--color-border-info',\n\t'--color-border-danger',\n\t'--color-border-success',\n\t'--color-border-warning',\n\t'--color-border-disabled',\n\n\t// Ring colors\n\t'--color-ring-primary',\n\t'--color-ring-secondary',\n\t'--color-ring-inverse',\n\t'--color-ring-info',\n\t'--color-ring-danger',\n\t'--color-ring-success',\n\t'--color-ring-warning',\n\n\t// Typography - Family\n\t'--font-sans',\n\t'--font-mono',\n\n\t// Typography - Weight\n\t'--font-weight-normal',\n\t'--font-weight-medium',\n\t'--font-weight-semibold',\n\t'--font-weight-bold',\n\n\t// Typography - Text Size\n\t'--font-text-xs-size',\n\t'--font-text-sm-size',\n\t'--font-text-md-size',\n\t'--font-text-lg-size',\n\n\t// Typography - Heading Size\n\t'--font-heading-xs-size',\n\t'--font-heading-sm-size',\n\t'--font-heading-md-size',\n\t'--font-heading-lg-size',\n\t'--font-heading-xl-size',\n\t'--font-heading-2xl-size',\n\t'--font-heading-3xl-size',\n\n\t// Typography - Text Line Height\n\t'--font-text-xs-line-height',\n\t'--font-text-sm-line-height',\n\t'--font-text-md-line-height',\n\t'--font-text-lg-line-height',\n\n\t// Typography - Heading Line Height\n\t'--font-heading-xs-line-height',\n\t'--font-heading-sm-line-height',\n\t'--font-heading-md-line-height',\n\t'--font-heading-lg-line-height',\n\t'--font-heading-xl-line-height',\n\t'--font-heading-2xl-line-height',\n\t'--font-heading-3xl-line-height',\n\n\t// Border radius\n\t'--border-radius-xs',\n\t'--border-radius-sm',\n\t'--border-radius-md',\n\t'--border-radius-lg',\n\t'--border-radius-xl',\n\t'--border-radius-full',\n\n\t// Border width\n\t'--border-width-regular',\n\n\t// Shadows\n\t'--shadow-hairline',\n\t'--shadow-sm',\n\t'--shadow-md',\n\t'--shadow-lg',\n] as const\n\nexport const CHAR_CSS_VARIABLE_NAMES = [\n\t...CHAR_PUBLIC_VARIABLE_NAMES,\n\t...MCP_UI_STYLE_VARIABLE_NAMES,\n] as const\n","/**\n * Host Context Merge Utilities\n *\n * Canonical merge logic for CharHostContext deltas. Used by both the host-side\n * web component and the iframe-side useHostContext hook to ensure identical\n * merge behavior on both sides of the postMessage bridge.\n */\n\nimport type { CharHostContext } from '../types'\n\nexport function mergeStyles(\n\tcurrent: CharHostContext['styles'] | undefined,\n\tpatch: CharHostContext['styles'] | undefined,\n): CharHostContext['styles'] | undefined {\n\tif (!patch) return current\n\treturn {\n\t\t...current,\n\t\t...patch,\n\t\tvariables: patch.variables ?? current?.variables,\n\t\tfonts: patch.fonts ?? current?.fonts,\n\t}\n}\n\nexport function shallowMerge<T extends object>(\n\tcurrent: T | undefined,\n\tpatch: T | undefined,\n): T | undefined {\n\tif (!patch) return current\n\treturn { ...current, ...patch }\n}\n\nexport function mergeHostContext(\n\tcurrent: CharHostContext | null,\n\tpatch: CharHostContext,\n): CharHostContext {\n\tconst previous = current ?? {}\n\treturn {\n\t\t...previous,\n\t\t...patch,\n\t\tstyles: mergeStyles(previous.styles, patch.styles),\n\t\tcontainerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),\n\t\tdeviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),\n\t\tsafeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets),\n\t}\n}\n"],"mappings":"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A;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;AAED,MAAM,8BAA8B;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CAGA;CACA;CACA;CACA;CACA;AAED,MAAa,0BAA0B,CACtC,GAAG,4BACH,GAAG,4BACH;;;;AClND,SAAgB,YACf,SACA,OACwC;AACxC,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EACN,GAAG;EACH,GAAG;EACH,WAAW,MAAM,aAAa,SAAS;EACvC,OAAO,MAAM,SAAS,SAAS;EAC/B;;AAGF,SAAgB,aACf,SACA,OACgB;AAChB,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EAAE,GAAG;EAAS,GAAG;EAAO;;AAGhC,SAAgB,iBACf,SACA,OACkB;CAClB,MAAM,WAAW,WAAW,EAAE;AAC9B,QAAO;EACN,GAAG;EACH,GAAG;EACH,QAAQ,YAAY,SAAS,QAAQ,MAAM,OAAO;EAClD,qBAAqB,aAAa,SAAS,qBAAqB,MAAM,oBAAoB;EAC1F,oBAAoB,aAAa,SAAS,oBAAoB,MAAM,mBAAmB;EACvF,gBAAgB,aAAa,SAAS,gBAAgB,MAAM,eAAe;EAC3E"}
@@ -419,8 +419,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
419
419
  styles: mergeStyles(previous.styles, patch.styles),
420
420
  containerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),
421
421
  deviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),
422
- safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets),
423
- hostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities)
422
+ safeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets)
424
423
  };
425
424
  }
426
425
 
@@ -455,25 +454,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
455
454
  "pip"
456
455
  ];
457
456
  /**
458
- * Normalizes an API key by trimming whitespace and collapsing empty strings.
459
- */
460
- function normalizeApiKey(value) {
461
- const trimmed = value?.trim();
462
- return trimmed ? trimmed : void 0;
463
- }
464
- /**
465
- * Produces a sanitized dev-mode configuration and drops empty payloads.
466
- */
467
- function resolveDevMode(devMode) {
468
- if (!devMode) return void 0;
469
- const normalized = {
470
- anthropicApiKey: normalizeApiKey(devMode.anthropicApiKey),
471
- openaiApiKey: normalizeApiKey(devMode.openaiApiKey),
472
- useLocalApi: devMode.useLocalApi === true ? true : void 0
473
- };
474
- return !!normalized.anthropicApiKey || !!normalized.openaiApiKey || !!normalized.useLocalApi ? normalized : void 0;
475
- }
476
- /**
477
457
  * Runtime guard for the message envelope emitted by the iframe.
478
458
  */
479
459
  function isCharIframeMessageData(value) {
@@ -612,7 +592,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
612
592
  */
613
593
  var CharAgentElement = class extends HTMLElement {
614
594
  static observedAttributes = [
615
- "dev-mode",
595
+ "publishable-key",
616
596
  "enable-debug-tools",
617
597
  "display-mode",
618
598
  "api-base"
@@ -662,12 +642,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
662
642
  return;
663
643
  }
664
644
  if (!this.style.display) this.style.display = "block";
665
- const resolvedDevMode = resolveDevMode(this._resolveDevMode());
666
- const apiBase = this._resolveApiBaseOverride() ?? (resolvedDevMode?.useLocalApi ? window.location.origin : WEBMCP_PRODUCTION_API_BASE);
645
+ const apiBase = this._resolveApiBaseOverride() ?? WEBMCP_PRODUCTION_API_BASE;
667
646
  const iframeOrigin = this._resolveIframeOrigin(apiBase);
668
647
  const iframe = this._createIframe(apiBase);
669
648
  this._iframe = iframe;
670
- if (resolvedDevMode?.anthropicApiKey) this._pendingAuth = { anthropicApiKey: resolvedDevMode.anthropicApiKey };
649
+ const publishableKey = this.publishableKey ?? this.getAttribute("publishable-key") ?? void 0;
650
+ if (publishableKey) this._pendingAuth = { publishableKey };
671
651
  this._proxy = new CharIframeProxy(iframe, iframeOrigin);
672
652
  this._proxy.start();
673
653
  this._messageListener = this._createMessageListener(iframe, iframeOrigin);
@@ -876,15 +856,21 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
876
856
  /**
877
857
  * Reacts to observed attribute updates after iframe readiness.
878
858
  * Attributes that affect iframe boot configuration are intentionally ignored
879
- * after mount (`dev-mode`, `enable-debug-tools`, `api-base`).
859
+ * after mount (`enable-debug-tools`, `api-base`).
880
860
  */
881
861
  attributeChangedCallback(name, _oldValue, newValue) {
882
862
  if (!this._iframe || !this._iframeReady) return;
883
863
  switch (name) {
864
+ case "publishable-key":
865
+ if (newValue) {
866
+ this._pendingAuth = { publishableKey: newValue };
867
+ const iframeOrigin = this._getIframeOrigin();
868
+ if (iframeOrigin) this._postAuth(iframeOrigin);
869
+ }
870
+ break;
884
871
  case "display-mode":
885
872
  this.setHostContext({ displayMode: resolveDisplayModeFromAttribute(newValue) });
886
873
  break;
887
- case "dev-mode":
888
874
  case "enable-debug-tools":
889
875
  case "api-base": break;
890
876
  }
@@ -895,23 +881,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
895
881
  * The token is stored as a JavaScript property (not as a DOM attribute),
896
882
  * preventing exposure to DOM inspection and session replay tools.
897
883
  *
898
- * @param options - Authentication payload for either ID token or ticket auth.
884
+ * @param options - Authentication payload.
899
885
  * @returns `true` when the payload is accepted; `false` when validation fails.
900
886
  */
901
887
  connect(options) {
902
- if (!options?.idToken && !options?.ticketAuth) {
903
- this._emitCharError("MISSING_TOKEN", "connect() requires either idToken or ticketAuth parameter");
904
- return false;
905
- }
906
- if (options?.idToken && !options?.ticketAuth && !options?.clientId) {
907
- this._emitCharError("MISSING_CLIENT_ID", "connect() requires clientId when using idToken authentication");
888
+ if (!options?.publishableKey) {
889
+ this._emitCharError("MISSING_PUBLISHABLE_KEY", "connect() requires publishableKey");
908
890
  return false;
909
891
  }
910
892
  this._pendingAuth = {
911
- idToken: options.ticketAuth ? void 0 : options.idToken,
912
- clientId: options.ticketAuth ? void 0 : options.clientId,
913
- organizationId: options.ticketAuth ? void 0 : options.organizationId,
914
- ticketAuth: options.ticketAuth
893
+ publishableKey: options.publishableKey,
894
+ idToken: options.idToken
915
895
  };
916
896
  const iframeOrigin = this._getIframeOrigin();
917
897
  if (this._iframeReady && iframeOrigin) this._postAuth(iframeOrigin);
@@ -930,7 +910,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
930
910
  }
931
911
  /**
932
912
  * Disconnect from the Char agent.
933
- * Clears the authentication token.
913
+ * Clears pending auth state and posts a disconnect message to the iframe.
934
914
  *
935
915
  * @returns `true` when the disconnect message was sent; `false` when the iframe was not ready.
936
916
  */
@@ -994,11 +974,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
994
974
  };
995
975
  const safeAreaInsets = getSafeAreaInsets();
996
976
  const displayMode = resolveDisplayModeFromAttribute(displayModeAttr) ?? "inline";
997
- const hostCapabilities = {
998
- supportedDisplayModes: [...DISPLAY_MODES],
999
- supportsOpenLink: true,
1000
- supportsTeardown: true
1001
- };
1002
977
  const context = {
1003
978
  theme,
1004
979
  styles: { variables: vars },
@@ -1009,9 +984,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1009
984
  timeZone,
1010
985
  platform: detectPlatform(),
1011
986
  deviceCapabilities: getDeviceCapabilities(),
1012
- safeAreaInsets,
1013
- userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
1014
- hostCapabilities
987
+ safeAreaInsets
1015
988
  };
1016
989
  this._hostContext = context;
1017
990
  if (!this._iframe?.contentWindow) {
@@ -1054,22 +1027,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1054
1027
  */
1055
1028
  _postAuth(iframeOrigin) {
1056
1029
  if (!this._pendingAuth || !this._iframe?.contentWindow) return;
1057
- if (!this._pendingAuth.ticketAuth && !this._pendingAuth.anthropicApiKey && !this._pendingAuth.idToken) {
1058
- console.error("[Char] _postAuth called but _pendingAuth has no auth credentials");
1030
+ if (!this._pendingAuth.publishableKey) {
1031
+ console.error("[Char] _postAuth called but _pendingAuth has no publishableKey");
1059
1032
  this._pendingAuth = null;
1060
1033
  return;
1061
1034
  }
1062
- const message = this._pendingAuth.ticketAuth ? {
1035
+ const message = {
1063
1036
  type: "char-auth",
1064
- ticketAuth: this._pendingAuth.ticketAuth
1065
- } : this._pendingAuth.anthropicApiKey ? {
1066
- type: "char-auth",
1067
- anthropicApiKey: this._pendingAuth.anthropicApiKey
1068
- } : {
1069
- type: "char-auth",
1070
- idToken: this._pendingAuth.idToken,
1071
- clientId: this._pendingAuth.clientId,
1072
- organizationId: this._pendingAuth.organizationId
1037
+ publishableKey: this._pendingAuth.publishableKey,
1038
+ idToken: this._pendingAuth.idToken
1073
1039
  };
1074
1040
  this._iframe.contentWindow.postMessage(message, iframeOrigin);
1075
1041
  }
@@ -1216,36 +1182,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1216
1182
  this._containerResizeObserver.observe(this);
1217
1183
  }
1218
1184
  /**
1219
- * Resolve devMode from property (React 19) or attribute.
1220
- *
1221
- * @returns Parsed dev-mode configuration when valid.
1222
- */
1223
- _resolveDevMode() {
1224
- if (this.devMode && typeof this.devMode === "object") return this.devMode;
1225
- const devModeAttr = this.getAttribute("dev-mode");
1226
- if (!devModeAttr) return void 0;
1227
- try {
1228
- const parsed = JSON.parse(devModeAttr);
1229
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1230
- const msg = `dev-mode attribute must be a JSON object, got: ${Array.isArray(parsed) ? "array" : typeof parsed}`;
1231
- console.warn(`[Char] ${msg}`);
1232
- this._emitCharError("INVALID_DEV_MODE", msg);
1233
- return;
1234
- }
1235
- const obj = parsed;
1236
- return {
1237
- anthropicApiKey: typeof obj.anthropicApiKey === "string" ? obj.anthropicApiKey : void 0,
1238
- openaiApiKey: typeof obj.openaiApiKey === "string" ? obj.openaiApiKey : void 0,
1239
- useLocalApi: typeof obj.useLocalApi === "boolean" ? obj.useLocalApi : void 0
1240
- };
1241
- } catch (e) {
1242
- const msg = `Failed to parse dev-mode attribute as JSON: ${e instanceof Error ? e.message : String(e)}`;
1243
- console.warn(`[Char] ${msg}`);
1244
- this._emitCharError("INVALID_DEV_MODE", msg);
1245
- return;
1246
- }
1247
- }
1248
- /**
1249
1185
  * Resolve apiBase override from property (React 19) or attribute.
1250
1186
  *
1251
1187
  * @returns Sanitized API base URL without trailing slash.