@stringpush/sdk 0.2.0 → 0.3.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/translations/translations/packages/sdk/dist/overlay-WO46AIRO.umd.cjs","../src/overlay/constants.ts","../src/overlay/overlay-api.ts","../src/overlay/locale-display.ts","../src/overlay/edit-panel.ts","../src/overlay/edit-panel-remote.ts","../src/overlay/edit-panel-copy.ts","../src/overlay/edit-panel-styles.ts","../src/overlay/highlights.ts","../src/overlay/realtime-sync.ts","../src/overlay/realtime-reconnect.ts","../src/overlay/realtime-client.ts","../src/overlay/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/translations/translations/packages/sdk/dist/overlay-WO46AIRO.umd.cjs","sourcesContent":[null,"/**\n * DOM and bundle-split markers for the lazy-loaded overlay chunk.\n *\n * Intent: stable ids/markers isolate overlay CSS/DOM and keep the main SDK chunk small.\n */\nexport const OVERLAY_ROOT_ID = \"translation-overlay-root\";\nexport const EDIT_PANEL_HOST_ID = \"translation-edit-panel-host\";\nexport const EDIT_PANEL_BACKDROP_ID = \"stringpush-edit-panel-backdrop\";\n/** Marker string used in bundle-split tests — must not appear in the main SDK chunk. */\nexport const OVERLAY_CHUNK_MARKER = \"translation-overlay-chunk-v1\";\n","/**\n * Overlay HTTP client (edit JWT) for locales, key values, and saves (M2-SDK-03).\n *\n * Intent: thin fetch wrapper shared by edit panel; maps 409 VERSION_CONFLICT for conflict UI.\n */\nimport { resolveRequestOrigin } from \"@stringpush/runtime-core\";\n\nexport type OverlayLocale = {\n id: string;\n code: string;\n};\n\nexport type OverlayKeyValueEntry = {\n localeId: string;\n localeCode: string;\n value: string;\n version: number | null;\n status: \"draft\" | \"reviewed\" | \"published\";\n};\n\nexport type OverlayKeyValuesResponse = {\n keyId: string;\n keyPath: string;\n defaultMessage: string | null;\n approvalEnabled: boolean;\n entries: OverlayKeyValueEntry[];\n};\n\nexport type OverlayTranslationValue = {\n id: string;\n translationKeyId: string;\n localeId: string;\n environmentId: string;\n value: string;\n status: string;\n version: number;\n};\n\nexport type OverlayApiConfig = {\n apiBaseUrl: string;\n editToken: string;\n origin?: string;\n fetch?: typeof fetch;\n};\n\nexport class OverlayApiError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly body: unknown,\n ) {\n super(message);\n this.name = \"OverlayApiError\";\n }\n\n get isVersionConflict(): boolean {\n return (\n this.status === 409 &&\n typeof this.body === \"object\" &&\n this.body !== null &&\n \"error\" in this.body &&\n (this.body as { error?: { code?: string } }).error?.code === \"VERSION_CONFLICT\"\n );\n }\n\n get isKeyNotFound(): boolean {\n return (\n this.status === 404 &&\n typeof this.body === \"object\" &&\n this.body !== null &&\n \"error\" in this.body &&\n (this.body as { error?: { code?: string } }).error?.code === \"NOT_FOUND\"\n );\n }\n}\n\nfunction overlayHeaders(config: OverlayApiConfig): HeadersInit {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.editToken}`,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n };\n const origin = resolveRequestOrigin(config.origin);\n if (origin) {\n headers.Origin = origin;\n }\n return headers;\n}\n\nasync function overlayFetch(\n config: OverlayApiConfig,\n path: string,\n init: RequestInit = {},\n): Promise<Response> {\n const fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);\n const url = `${config.apiBaseUrl.replace(/\\/$/, \"\")}${path}`;\n return fetchFn(url, {\n ...init,\n headers: {\n ...overlayHeaders(config),\n ...(init.headers as Record<string, string> | undefined),\n },\n });\n}\n\nasync function parseJson<T>(response: Response): Promise<T> {\n return (await response.json()) as T;\n}\n\nexport async function fetchOverlayLocales(config: OverlayApiConfig): Promise<OverlayLocale[]> {\n const response = await overlayFetch(config, \"/v1/overlay/locales\");\n const body = await parseJson<{ data: Array<{ id: string; code: string }> } | { error: unknown }>(\n response,\n );\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to load locales (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return (body as { data: Array<{ id: string; code: string }> }).data.map((row) => ({\n id: row.id,\n code: row.code,\n }));\n}\n\nexport async function fetchOverlayKeyValues(\n config: OverlayApiConfig,\n keyPath: string,\n): Promise<OverlayKeyValuesResponse> {\n const params = new URLSearchParams({ key: keyPath });\n const response = await overlayFetch(config, `/v1/overlay/keys/values?${params}`);\n const body = await parseJson<OverlayKeyValuesResponse | { error: unknown }>(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to load key values (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return body as OverlayKeyValuesResponse;\n}\n\nexport async function createOverlayKey(\n config: OverlayApiConfig,\n input: { keyPath: string; defaultMessage?: string | null },\n): Promise<{ id: string; keyPath: string }> {\n const response = await overlayFetch(config, \"/v1/overlay/keys\", {\n method: \"POST\",\n body: JSON.stringify({\n keyPath: input.keyPath,\n defaultMessage: input.defaultMessage ?? null,\n }),\n });\n\n const body = await parseJson<\n | { id: string; key: string; namespace: string }\n | { error: unknown }\n >(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to create translation key (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n const record = body as { id: string; key: string; namespace: string };\n const keyPath = record.namespace ? `${record.namespace}.${record.key}` : record.key;\n return { id: record.id, keyPath };\n}\n\nexport async function patchOverlayValue(\n config: OverlayApiConfig,\n input: { keyId: string; localeId: string; value: string; version: number | null },\n): Promise<OverlayTranslationValue> {\n const headers: Record<string, string> = {};\n if (input.version !== null) {\n headers[\"If-Match\"] = String(input.version);\n }\n\n const response = await overlayFetch(config, \"/v1/overlay/values\", {\n method: \"PATCH\",\n headers,\n body: JSON.stringify({\n keyId: input.keyId,\n localeId: input.localeId,\n value: input.value,\n }),\n });\n\n const body = await parseJson<OverlayTranslationValue | { error: unknown }>(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to save translation (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return body as OverlayTranslationValue;\n}\n","const LOCALE_FLAGS: Record<string, string> = {\n en: \"🇺🇸\",\n fr: \"🇫🇷\",\n de: \"🇩🇪\",\n \"de-DE\": \"🇩🇪\",\n es: \"🇪🇸\",\n};\n\nconst LOCALE_NAMES: Record<string, string> = {\n en: \"English\",\n fr: \"French\",\n de: \"German\",\n \"de-DE\": \"German\",\n es: \"Spanish\",\n};\n\nexport function overlayLocaleFlag(code: string): string {\n return LOCALE_FLAGS[code] ?? \"🌐\";\n}\n\nexport function overlayLocaleLabel(code: string): string {\n const name = LOCALE_NAMES[code] ?? code.toUpperCase();\n return `${name} (${code})`;\n}\n","/**\n * Side panel UI to edit all locales for one translation key (M2-SDK-03, M11 UI-14).\n *\n * Intent: opened from overlay click; loads via edit JWT; saves with If-Match; Figma 10:2 layout and copy.\n */\nimport {\n createOverlayKey,\n fetchOverlayKeyValues,\n fetchOverlayLocales,\n OverlayApiError,\n patchOverlayValue,\n type OverlayApiConfig,\n type OverlayKeyValueEntry,\n} from \"./overlay-api.js\";\nimport { overlayLocaleFlag, overlayLocaleLabel } from \"./locale-display.js\";\nimport { recordTranslationVersion, shouldApplyTranslationUpdate } from \"@stringpush/runtime-core\";\nimport {\n EDIT_PANEL_BACKDROP_ID,\n EDIT_PANEL_HOST_ID,\n} from \"./constants.js\";\nimport {\n registerEditPanelRemoteHandler,\n type RemoteTranslationUpdate,\n} from \"./edit-panel-remote.js\";\nimport {\n buildAdminKeyUrl,\n EDIT_PANEL_CONFLICT_BODY,\n EDIT_PANEL_CONFLICT_TITLE,\n EDIT_PANEL_FOOTNOTE_STAGING,\n EDIT_PANEL_RELOAD_LABEL,\n formatEnvironmentBadgeLabel,\n formatSaveToEnvironmentLabel,\n} from \"./edit-panel-copy.js\";\nimport { EDIT_PANEL_BACKDROP_STYLES, EDIT_PANEL_STYLES } from \"./edit-panel-styles.js\";\n\nexport type EditPanelContext = OverlayApiConfig & {\n projectId: string;\n environmentName: string;\n approvalEnabled: boolean;\n adminWebOrigin?: string;\n activeLocaleCode: string;\n suggestedDefaultMessage?: string;\n onSaved: (keyPath: string, localeCode: string, value: string, version: number) => void;\n};\n\nexport type EditPanelHandle = {\n destroy: () => void;\n};\n\ntype LocaleRowState = OverlayKeyValueEntry & {\n draftValue: string;\n};\n\nexport function openEditPanel(keyPath: string, context: EditPanelContext): EditPanelHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n closeEditPanel();\n\n const backdrop = document.createElement(\"div\");\n backdrop.id = EDIT_PANEL_BACKDROP_ID;\n backdrop.setAttribute(\"aria-hidden\", \"true\");\n backdrop.style.cssText = EDIT_PANEL_BACKDROP_STYLES;\n\n const host = document.createElement(\"div\");\n host.id = EDIT_PANEL_HOST_ID;\n const shadow = host.attachShadow({ mode: \"open\" });\n\n const style = document.createElement(\"style\");\n style.textContent = EDIT_PANEL_STYLES;\n\n const panel = document.createElement(\"div\");\n panel.className = \"panel\";\n panel.setAttribute(\"role\", \"dialog\");\n panel.setAttribute(\"aria-label\", \"Edit translation\");\n\n const header = document.createElement(\"header\");\n header.className = \"header\";\n\n const headerTop = document.createElement(\"div\");\n headerTop.className = \"header-top\";\n const brand = document.createElement(\"div\");\n brand.className = \"brand\";\n const brandIcon = document.createElement(\"span\");\n brandIcon.className = \"brand-icon\";\n brandIcon.setAttribute(\"aria-hidden\", \"true\");\n brand.append(brandIcon, document.createTextNode(\"StringPush\"));\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"close-btn\";\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n closeBtn.textContent = \"×\";\n headerTop.append(brand, closeBtn);\n\n const title = document.createElement(\"h2\");\n title.className = \"title\";\n title.textContent = \"Edit translation\";\n\n const metaRow = document.createElement(\"div\");\n metaRow.className = \"meta-row\";\n const keyChip = document.createElement(\"span\");\n keyChip.className = \"key-chip\";\n keyChip.textContent = keyPath;\n const envBadge = document.createElement(\"span\");\n envBadge.className =\n context.environmentName === \"production\"\n ? \"env-badge env-badge--production\"\n : \"env-badge env-badge--staging\";\n const envDot = document.createElement(\"span\");\n envDot.className = \"env-dot\";\n envDot.setAttribute(\"aria-hidden\", \"true\");\n envBadge.append(\n envDot,\n document.createTextNode(formatEnvironmentBadgeLabel(context.environmentName)),\n );\n metaRow.append(keyChip, envBadge);\n\n header.append(headerTop, title, metaRow);\n\n const bodyEl = document.createElement(\"div\");\n bodyEl.className = \"body\";\n\n const status = document.createElement(\"div\");\n status.className = \"status\";\n status.textContent = \"Loading…\";\n bodyEl.append(status);\n\n const banner = document.createElement(\"div\");\n banner.className = \"banner\";\n banner.hidden = true;\n\n const footer = document.createElement(\"footer\");\n footer.className = \"footer\";\n\n const footerActions = document.createElement(\"div\");\n footerActions.className = \"footer-actions\";\n\n const discardBtn = document.createElement(\"button\");\n discardBtn.type = \"button\";\n discardBtn.className = \"discard\";\n discardBtn.textContent = \"Discard\";\n\n const saveBtn = document.createElement(\"button\");\n saveBtn.type = \"button\";\n saveBtn.className = \"primary\";\n saveBtn.disabled = true;\n const saveLabel = document.createElement(\"span\");\n saveLabel.className = \"primary-label\";\n const saveSub = document.createElement(\"span\");\n saveSub.className = \"primary-sub\";\n saveBtn.append(saveLabel, saveSub);\n updateSaveButtonLabels(context, saveLabel, saveSub);\n\n footerActions.append(discardBtn, saveBtn);\n\n const footerMeta = document.createElement(\"div\");\n footerMeta.className = \"footer-meta\";\n\n const adminLink = document.createElement(\"a\");\n adminLink.className = \"admin-link\";\n adminLink.target = \"_blank\";\n adminLink.rel = \"noopener noreferrer\";\n adminLink.textContent = \"Open in admin ↗\";\n\n const footerNote = document.createElement(\"span\");\n footerNote.className = \"footer-note\";\n footerNote.textContent =\n context.environmentName === \"production\"\n ? \"Changes apply to production immediately.\"\n : EDIT_PANEL_FOOTNOTE_STAGING;\n\n footerMeta.append(adminLink, footerNote);\n footer.append(footerActions, footerMeta);\n\n panel.append(header, bodyEl, footer);\n shadow.append(style, panel);\n document.body.append(backdrop, host);\n\n let destroyed = false;\n let rows: LocaleRowState[] = [];\n let keyId = \"\";\n\n const destroy = () => {\n if (destroyed) {\n return;\n }\n destroyed = true;\n registerEditPanelRemoteHandler(null);\n backdrop.remove();\n host.remove();\n };\n\n function hasUnsavedChanges(): boolean {\n return rows.some((row) => row.draftValue !== row.value);\n }\n\n function updateSaveDisabled(): void {\n saveBtn.disabled = !hasUnsavedChanges() || destroyed;\n }\n\n function applyRemoteUpdate(update: RemoteTranslationUpdate): boolean {\n if (!shouldApplyTranslationUpdate(update.keyPath, update.localeCode, update.version)) {\n return false;\n }\n\n const row = rows.find((entry) => entry.localeCode === update.localeCode);\n if (!row) {\n return false;\n }\n\n if ((row.version ?? 0) > update.version) {\n return false;\n }\n\n if (row.draftValue !== row.value) {\n showConflict();\n return true;\n }\n\n row.value = update.value;\n row.draftValue = update.value;\n row.version = update.version;\n recordTranslationVersion(update.keyPath, update.localeCode, update.version);\n renderRows();\n return true;\n }\n\n closeBtn.addEventListener(\"click\", destroy);\n discardBtn.addEventListener(\"click\", destroy);\n\n function renderRows(): void {\n bodyEl.replaceChildren();\n if (!banner.hidden) {\n bodyEl.append(banner);\n }\n\n for (const row of rows) {\n const isDirty = row.draftValue !== row.value;\n const wrap = document.createElement(\"div\");\n wrap.className = \"locale-row\";\n\n const localeHeader = document.createElement(\"div\");\n localeHeader.className = \"locale-header\";\n\n const label = document.createElement(\"span\");\n label.className = \"locale-label\";\n const flag = document.createElement(\"span\");\n flag.className = \"locale-flag\";\n flag.textContent = overlayLocaleFlag(row.localeCode);\n flag.setAttribute(\"aria-hidden\", \"true\");\n label.append(flag, document.createTextNode(overlayLocaleLabel(row.localeCode)));\n\n const badges = document.createElement(\"span\");\n badges.className = \"locale-badges\";\n if (row.localeCode === context.activeLocaleCode) {\n const defaultBadge = document.createElement(\"span\");\n defaultBadge.className = \"badge badge-default\";\n defaultBadge.textContent = \"Default locale\";\n badges.append(defaultBadge);\n }\n\n const unsaved = document.createElement(\"span\");\n unsaved.className = \"unsaved\";\n unsaved.hidden = !isDirty;\n const dot = document.createElement(\"span\");\n dot.className = \"unsaved-dot\";\n dot.setAttribute(\"aria-hidden\", \"true\");\n unsaved.append(dot, document.createTextNode(\"Unsaved changes\"));\n badges.append(unsaved);\n\n let draftBadge: HTMLSpanElement | null = null;\n if (row.status === \"draft\" && (isDirty || row.version !== null)) {\n draftBadge = document.createElement(\"span\");\n draftBadge.className = \"badge badge-draft\";\n draftBadge.textContent = \"Draft\";\n badges.append(draftBadge);\n } else if (row.status === \"reviewed\") {\n const reviewedBadge = document.createElement(\"span\");\n reviewedBadge.className = \"badge badge-reviewed\";\n reviewedBadge.textContent = \"Reviewed\";\n badges.append(reviewedBadge);\n } else if (row.status === \"published\") {\n const publishedBadge = document.createElement(\"span\");\n publishedBadge.className = \"badge badge-published\";\n publishedBadge.textContent = \"Published\";\n badges.append(publishedBadge);\n }\n\n localeHeader.append(label, badges);\n\n const textarea = document.createElement(\"textarea\");\n textarea.id = `locale-${row.localeId}`;\n textarea.setAttribute(\"aria-label\", `${row.localeCode} translation`);\n textarea.value = row.draftValue;\n if (isDirty) {\n textarea.classList.add(\"is-dirty\");\n }\n textarea.addEventListener(\"input\", () => {\n row.draftValue = textarea.value;\n const dirty = row.draftValue !== row.value;\n textarea.classList.toggle(\"is-dirty\", dirty);\n unsaved.hidden = !dirty;\n if (draftBadge) {\n draftBadge.hidden = !dirty && row.status !== \"draft\";\n }\n updateSaveDisabled();\n });\n\n wrap.append(localeHeader, textarea);\n bodyEl.append(wrap);\n }\n\n updateSaveDisabled();\n updateAdminLink();\n }\n\n function updateAdminLink(): void {\n if (context.adminWebOrigin && keyId) {\n adminLink.href = buildAdminKeyUrl(context.adminWebOrigin, context.projectId, keyId);\n adminLink.hidden = false;\n } else {\n adminLink.hidden = true;\n }\n }\n\n async function load(): Promise<void> {\n try {\n const [locales, keyValues] = await Promise.all([\n fetchOverlayLocales(context),\n fetchOverlayKeyValues(context, keyPath),\n ]);\n\n if (destroyed) {\n return;\n }\n\n keyId = keyValues.keyId;\n context.approvalEnabled = keyValues.approvalEnabled;\n updateSaveButtonLabels(context, saveLabel, saveSub);\n\n const localeCodes = new Set(locales.map((l) => l.code));\n rows = keyValues.entries\n .filter((entry) => localeCodes.has(entry.localeCode))\n .map((entry) => ({\n ...entry,\n draftValue: entry.value,\n }));\n\n for (const row of rows) {\n if (row.version !== null) {\n recordTranslationVersion(keyPath, row.localeCode, row.version);\n }\n }\n\n banner.hidden = true;\n footer.hidden = false;\n renderRows();\n } catch (error) {\n if (error instanceof OverlayApiError && error.isKeyNotFound) {\n showCreateKeyPrompt();\n return;\n }\n status.textContent =\n error instanceof Error ? error.message : \"Failed to load translations\";\n }\n }\n\n function showCreateKeyPrompt(): void {\n footer.hidden = true;\n bodyEl.replaceChildren();\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"create-panel\";\n\n const intro = document.createElement(\"p\");\n intro.textContent =\n \"This key is not in your project yet. Create it to start editing translations for all locales.\";\n\n const label = document.createElement(\"label\");\n label.textContent = \"Default message (optional)\";\n label.htmlFor = \"create-default-message\";\n\n const input = document.createElement(\"input\");\n input.id = \"create-default-message\";\n input.type = \"text\";\n input.placeholder = \"Text shown when no translation is saved\";\n if (context.suggestedDefaultMessage) {\n input.value = context.suggestedDefaultMessage;\n }\n\n const errorEl = document.createElement(\"p\");\n errorEl.className = \"error\";\n errorEl.hidden = true;\n\n const createBtn = document.createElement(\"button\");\n createBtn.type = \"button\";\n createBtn.className = \"create-btn\";\n createBtn.textContent = \"Create key\";\n\n createBtn.addEventListener(\"click\", () => {\n void (async () => {\n createBtn.disabled = true;\n errorEl.hidden = true;\n\n try {\n await createOverlayKey(context, {\n keyPath,\n defaultMessage: input.value.trim() || null,\n });\n if (destroyed) {\n return;\n }\n status.textContent = \"Loading…\";\n bodyEl.replaceChildren(status);\n footer.hidden = false;\n saveBtn.disabled = true;\n await load();\n } catch (createError) {\n createBtn.disabled = false;\n errorEl.hidden = false;\n errorEl.textContent =\n createError instanceof Error ? createError.message : \"Failed to create key\";\n }\n })();\n });\n\n wrap.append(intro, label, input, errorEl, createBtn);\n bodyEl.append(wrap);\n }\n\n function showConflict(): void {\n banner.hidden = false;\n banner.replaceChildren();\n\n const row = document.createElement(\"div\");\n row.className = \"banner-row\";\n\n const icon = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n icon.setAttribute(\"class\", \"banner-icon\");\n icon.setAttribute(\"viewBox\", \"0 0 20 20\");\n icon.setAttribute(\"fill\", \"currentColor\");\n icon.innerHTML =\n '<path fill-rule=\"evenodd\" d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\" clip-rule=\"evenodd\"/>';\n\n const copy = document.createElement(\"div\");\n const titleEl = document.createElement(\"p\");\n titleEl.className = \"banner-title\";\n titleEl.textContent = EDIT_PANEL_CONFLICT_TITLE;\n const body = document.createElement(\"p\");\n body.className = \"banner-body\";\n body.textContent = EDIT_PANEL_CONFLICT_BODY;\n copy.append(titleEl, body);\n\n row.append(icon, copy);\n banner.append(row);\n\n const reloadBtn = document.createElement(\"button\");\n reloadBtn.type = \"button\";\n reloadBtn.className = \"banner-reload\";\n reloadBtn.textContent = EDIT_PANEL_RELOAD_LABEL;\n reloadBtn.addEventListener(\"click\", () => {\n status.textContent = \"Loading…\";\n bodyEl.replaceChildren(status);\n saveBtn.disabled = true;\n void load();\n });\n banner.append(reloadBtn);\n\n if (!bodyEl.contains(banner)) {\n renderRows();\n }\n }\n\n saveBtn.addEventListener(\"click\", () => {\n void (async () => {\n saveBtn.disabled = true;\n let hadConflict = false;\n\n for (const row of rows) {\n if (row.draftValue === row.value) {\n continue;\n }\n\n try {\n const saved = await patchOverlayValue(context, {\n keyId,\n localeId: row.localeId,\n value: row.draftValue,\n version: row.version,\n });\n row.value = saved.value;\n row.version = saved.version;\n row.draftValue = saved.value;\n row.status = saved.status as LocaleRowState[\"status\"];\n context.onSaved(keyPath, row.localeCode, saved.value, saved.version);\n } catch (error) {\n if (error instanceof OverlayApiError && error.isVersionConflict) {\n hadConflict = true;\n showConflict();\n break;\n }\n showConflict();\n hadConflict = true;\n break;\n }\n }\n\n if (!hadConflict) {\n destroy();\n } else {\n updateSaveDisabled();\n }\n })();\n });\n\n registerEditPanelRemoteHandler({ keyPath, applyRemoteUpdate });\n\n void load();\n\n return { destroy };\n}\n\nfunction updateSaveButtonLabels(\n context: EditPanelContext,\n saveLabel: HTMLElement,\n saveSub: HTMLElement,\n): void {\n if (context.approvalEnabled) {\n saveLabel.textContent = \"Save draft\";\n saveSub.textContent = \"Needs review before publish\";\n return;\n }\n saveLabel.textContent = formatSaveToEnvironmentLabel(context.environmentName);\n saveSub.textContent = \"Updates site immediately\";\n}\n\nexport function closeEditPanel(): void {\n registerEditPanelRemoteHandler(null);\n document.getElementById(EDIT_PANEL_BACKDROP_ID)?.remove();\n document.getElementById(EDIT_PANEL_HOST_ID)?.remove();\n}\n","/**\n * Routes realtime `translation.updated` events to the open edit panel (M2-SDK-04).\n *\n * Intent: single active handler per key so concurrent editors see live value/version without full reload.\n */\nexport type RemoteTranslationUpdate = {\n keyPath: string;\n localeCode: string;\n value: string;\n version: number;\n};\n\nexport type EditPanelRemoteHandler = {\n keyPath: string;\n applyRemoteUpdate: (update: RemoteTranslationUpdate) => boolean;\n};\n\nlet activeHandler: EditPanelRemoteHandler | null = null;\n\nexport function registerEditPanelRemoteHandler(handler: EditPanelRemoteHandler | null): void {\n activeHandler = handler;\n}\n\nexport function notifyEditPanelRemoteUpdate(update: RemoteTranslationUpdate): boolean {\n if (!activeHandler || activeHandler.keyPath !== update.keyPath) {\n return false;\n }\n return activeHandler.applyRemoteUpdate(update);\n}\n","/**\n * User-facing strings for overlay edit panel (M11 UI-14–UI-16).\n *\n * Intent: environment-aware labels and collaboration copy aligned with Figma 10:2.\n */\n\nexport const EDIT_PANEL_CONFLICT_TITLE = \"Conflict detected\";\nexport const EDIT_PANEL_CONFLICT_BODY =\n \"Another editor just saved changes to this key.\";\nexport const EDIT_PANEL_RELOAD_LABEL = \"Reload latest values\";\nexport const EDIT_PANEL_FOOTNOTE_STAGING =\n \"Production requires separate promote step.\";\n\nexport function formatEnvironmentBadgeLabel(environmentName: string): string {\n const label = capitalizeEnvironment(environmentName);\n return `${label} Environment`;\n}\n\nexport function formatSaveToEnvironmentLabel(environmentName: string): string {\n return `Save to ${capitalizeEnvironment(environmentName)}`;\n}\n\nexport function buildAdminKeyUrl(\n adminWebOrigin: string,\n projectId: string,\n keyId: string,\n): string {\n const origin = adminWebOrigin.replace(/\\/$/, \"\");\n const params = new URLSearchParams({\n projectId,\n keyId,\n });\n return `${origin}/keys?${params.toString()}`;\n}\n\nfunction capitalizeEnvironment(environmentName: string): string {\n if (!environmentName) {\n return \"Environment\";\n }\n return environmentName.charAt(0).toUpperCase() + environmentName.slice(1);\n}\n","/**\n * Shadow DOM styles for overlay edit panel (M11 UI-14).\n *\n * Intent: Figma 10:2 visual spec; kept separate from DOM logic in edit-panel.ts.\n */\nexport const EDIT_PANEL_STYLES = `\n:host {\n all: initial;\n font-family: Inter, system-ui, -apple-system, Segoe UI, sans-serif;\n --sp-bg: #ffffff;\n --sp-text: #0f172b;\n --sp-muted: #62748e;\n --sp-border: #e2e8f0;\n --sp-accent: #4f39f6;\n --sp-accent-hover: #4338ca;\n --sp-accent-subtle: #c6d2ff;\n --sp-surface-muted: #f8fafc;\n --sp-warning-bg: #fffbeb;\n --sp-warning-border: #fee685;\n --sp-warning-title: #7b3306;\n --sp-warning-body: #bb4d00;\n --sp-warning-btn-border: #ffd230;\n --sp-badge-draft-bg: #fef3c6;\n --sp-badge-draft-text: #973c00;\n --sp-badge-reviewed-bg: #eff6ff;\n --sp-badge-reviewed-text: #1447e6;\n --sp-badge-reviewed-border: #bedbff;\n --sp-badge-published-bg: #ecfdf5;\n --sp-badge-published-text: #007a55;\n --sp-badge-published-border: #a4f4cf;\n --sp-badge-default-bg: #f1f5f9;\n --sp-badge-default-text: #45556c;\n --sp-env-staging-bg: #ecfdf5;\n --sp-env-staging-border: #a4f4cf;\n --sp-env-staging-text: #007a55;\n --sp-env-production-bg: #fff7ed;\n --sp-env-production-border: #fdba74;\n --sp-env-production-text: #c2410c;\n --sp-unsaved: #e17100;\n}\n.panel {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n max-width: 100vw;\n height: 100vh;\n background: var(--sp-bg);\n color: var(--sp-text);\n box-shadow: -25px 0 25px rgba(0, 0, 0, 0.25);\n border-left: 1px solid var(--sp-border);\n display: flex;\n flex-direction: column;\n z-index: 2147483647;\n}\n.header {\n background: rgba(248, 250, 252, 0.5);\n border-bottom: 1px solid var(--sp-border);\n padding: 20px 24px 16px;\n}\n.header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 12px;\n}\n.brand {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--sp-muted);\n}\n.brand-icon {\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--sp-accent);\n opacity: 0.85;\n}\n.title {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n line-height: 1.25;\n letter-spacing: -0.02em;\n}\n.meta-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n}\n.key-chip {\n font: 11px/1.4 ui-monospace, Menlo, monospace;\n color: #45556c;\n background: #fff;\n border: 1px solid var(--sp-border);\n border-radius: 4px;\n padding: 4px 8px;\n}\n.env-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 9px;\n border-radius: 4px;\n font-size: 10px;\n font-weight: 500;\n letter-spacing: 0.01em;\n border: 1px solid transparent;\n}\n.env-badge--staging {\n background: var(--sp-env-staging-bg);\n border-color: var(--sp-env-staging-border);\n color: var(--sp-env-staging-text);\n}\n.env-badge--production {\n background: var(--sp-env-production-bg);\n border-color: var(--sp-env-production-border);\n color: var(--sp-env-production-text);\n}\n.env-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentColor;\n}\n.close-btn {\n border: none;\n background: transparent;\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n color: var(--sp-muted);\n padding: 6px;\n border-radius: 8px;\n}\n.close-btn:focus-visible {\n outline: 2px solid var(--sp-accent);\n outline-offset: 2px;\n}\n.body {\n flex: 1;\n overflow: auto;\n padding: 24px;\n}\n.banner {\n margin-bottom: 32px;\n padding: 17px;\n border-radius: 10px;\n border: 1px solid var(--sp-warning-border);\n background: var(--sp-warning-bg);\n}\n.banner-row {\n display: flex;\n gap: 12px;\n align-items: flex-start;\n}\n.banner-icon {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n margin-top: 2px;\n color: var(--sp-warning-body);\n}\n.banner-title {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: var(--sp-warning-title);\n}\n.banner-body {\n margin: 4px 0 0;\n font-size: 14px;\n line-height: 1.45;\n color: var(--sp-warning-body);\n}\n.banner-reload {\n margin-top: 12px;\n border: 1px solid var(--sp-warning-btn-border);\n background: rgba(255, 251, 235, 0.5);\n border-radius: 8px;\n padding: 6px 13px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n color: var(--sp-badge-draft-text);\n}\n.banner-reload:focus-visible {\n outline: 2px solid var(--sp-warning-body);\n outline-offset: 2px;\n}\n.locale-row + .locale-row {\n margin-top: 24px;\n}\n.locale-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 12px;\n}\n.locale-label {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 600;\n color: var(--sp-text);\n}\n.locale-flag {\n font-size: 15px;\n line-height: 1;\n}\n.locale-badges {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.unsaved {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 10px;\n font-weight: 500;\n color: var(--sp-unsaved);\n}\n.unsaved-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #fe9a00;\n}\n.badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 10px;\n font-weight: 500;\n letter-spacing: 0.01em;\n border: 1px solid transparent;\n}\n.badge-default {\n background: var(--sp-badge-default-bg);\n border-color: var(--sp-border);\n color: var(--sp-badge-default-text);\n}\n.badge-draft {\n background: var(--sp-badge-draft-bg);\n border-color: var(--sp-warning-border);\n color: var(--sp-badge-draft-text);\n}\n.badge-reviewed {\n background: var(--sp-badge-reviewed-bg);\n border-color: var(--sp-badge-reviewed-border);\n color: var(--sp-badge-reviewed-text);\n}\n.badge-published {\n background: var(--sp-badge-published-bg);\n border-color: var(--sp-badge-published-border);\n color: var(--sp-badge-published-text);\n}\n.locale-row textarea {\n width: 100%;\n min-height: 60px;\n box-sizing: border-box;\n border: 1px solid #cad5e2;\n border-radius: 8px;\n padding: 10px 12px;\n font: 14px/1.45 inherit;\n resize: vertical;\n color: var(--sp-text);\n background: var(--sp-surface-muted);\n}\n.locale-row textarea.is-dirty {\n background: #fff;\n border-color: #a3b3ff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);\n}\n.locale-row textarea:focus-visible {\n outline: 2px solid var(--sp-accent);\n outline-offset: 1px;\n border-color: var(--sp-accent);\n}\n.footer {\n border-top: 1px solid var(--sp-border);\n background: var(--sp-surface-muted);\n padding: 16px 24px;\n}\n.footer-actions {\n display: flex;\n gap: 12px;\n}\n.footer-actions button {\n border-radius: 8px;\n cursor: pointer;\n font-weight: 500;\n}\n.footer .discard {\n flex: 1;\n min-height: 40px;\n border: 1px solid rgba(0, 0, 0, 0.1);\n background: #fff;\n color: var(--sp-text);\n font-size: 16px;\n}\n.footer .primary {\n flex: 1;\n min-height: 53px;\n border: none;\n background: var(--sp-accent);\n color: #fff;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 8px 16px;\n}\n.footer .primary:hover:not(:disabled) {\n background: var(--sp-accent-hover);\n}\n.footer .primary:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n.primary-label {\n font-size: 14px;\n font-weight: 600;\n line-height: 1.25;\n}\n.primary-sub {\n margin-top: 2px;\n font-size: 10px;\n font-weight: 400;\n color: var(--sp-accent-subtle);\n line-height: 1.2;\n}\n.footer-meta {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-top: 16px;\n padding-bottom: 4px;\n}\n.admin-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n font-weight: 500;\n color: var(--sp-accent);\n text-decoration: none;\n}\n.admin-link:focus-visible {\n outline: 2px solid var(--sp-accent);\n outline-offset: 2px;\n}\n.footer-note {\n font-size: 10px;\n color: var(--sp-muted);\n text-align: right;\n line-height: 1.35;\n}\n.status {\n padding: 24px 16px;\n color: var(--sp-muted);\n font-size: 14px;\n}\n.create-panel {\n padding: 8px 0;\n}\n.create-panel p {\n margin: 0 0 12px;\n font-size: 14px;\n color: #334155;\n line-height: 1.5;\n}\n.create-panel label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: #334155;\n margin-bottom: 6px;\n}\n.create-panel input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n padding: 8px 10px;\n font: 14px/1.4 inherit;\n margin-bottom: 12px;\n}\n.create-panel .create-btn {\n border: none;\n border-radius: 8px;\n padding: 9px 14px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n background: var(--sp-accent);\n color: #fff;\n}\n.create-panel .error {\n margin: 0 0 12px;\n color: #b91c1c;\n font-size: 13px;\n}\n`;\n\nexport const EDIT_PANEL_BACKDROP_STYLES = `\n position: fixed;\n inset: 0;\n background: rgba(15, 23, 43, 0.2);\n z-index: 2147483646;\n pointer-events: none;\n`;\n","/**\n * Hover/focus highlights for translatable DOM nodes in edit mode (M2-SDK-02).\n *\n * Intent: pointer-events stay on the host page — only outlines and a read-only key tooltip are added.\n */\nimport { EDIT_PANEL_HOST_ID, OVERLAY_ROOT_ID } from \"./constants.js\";\n\nconst HIGHLIGHT_CLASS = \"translation-sdk-target-highlight\";\nconst STYLES_ID = \"translation-sdk-highlight-styles\";\nconst TOOLTIP_ID = \"translation-sdk-key-tooltip\";\n\nconst HIGHLIGHT_CSS = `\n.${HIGHLIGHT_CLASS} {\n outline: 2px solid #2563eb !important;\n outline-offset: 2px !important;\n box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.28) !important;\n}\n.${HIGHLIGHT_CLASS}:focus-visible {\n outline: 3px solid #1d4ed8 !important;\n outline-offset: 2px !important;\n}\n#${TOOLTIP_ID} {\n position: fixed;\n z-index: 2147483645;\n max-width: min(360px, 90vw);\n padding: 4px 8px;\n border-radius: 6px;\n font: 12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #f8fafc;\n background: #0f172a;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.28);\n pointer-events: none;\n display: none;\n}\n#${TOOLTIP_ID}[data-visible=\"true\"] {\n display: block;\n}\n`;\n\nexport type HighlightControllerOptions = {\n resolveKey: (element: Element) => string | null;\n onActivateKey?: (keyPath: string, element: Element) => void;\n};\n\nexport type HighlightController = {\n destroy: () => void;\n};\n\nexport function createHighlightController(\n options: HighlightControllerOptions,\n): HighlightController {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n const abort = new AbortController();\n const { signal } = abort;\n\n let highlighted: Element | null = null;\n\n const styleEl = document.createElement(\"style\");\n styleEl.id = STYLES_ID;\n styleEl.textContent = HIGHLIGHT_CSS;\n document.head.append(styleEl);\n\n const tip = document.createElement(\"div\");\n tip.id = TOOLTIP_ID;\n document.body.append(tip);\n\n function isInsideOverlay(node: Node | null): boolean {\n if (!node || !(node instanceof Element)) {\n return false;\n }\n return Boolean(\n node.closest(`#${OVERLAY_ROOT_ID}, #${EDIT_PANEL_HOST_ID}`),\n );\n }\n\n function clearHighlight(): void {\n if (highlighted) {\n highlighted.classList.remove(HIGHLIGHT_CLASS);\n highlighted = null;\n }\n tip.dataset.visible = \"false\";\n tip.textContent = \"\";\n }\n\n function positionTooltip(key: string, target: Element, clientX: number): void {\n tip.textContent = key;\n tip.dataset.visible = \"true\";\n\n const rect = target.getBoundingClientRect();\n const top = Math.min(rect.bottom + 8, window.innerHeight - 32);\n const left = Math.min(Math.max(clientX, 8), window.innerWidth - 200);\n tip.style.top = `${top}px`;\n tip.style.left = `${left}px`;\n }\n\n function applyHighlight(target: Element, key: string, clientX: number): void {\n if (highlighted !== target) {\n clearHighlight();\n highlighted = target;\n target.classList.add(HIGHLIGHT_CLASS);\n }\n positionTooltip(key, target, clientX);\n }\n\n function inspectTarget(target: Element | null, clientX: number, _clientY: number): void {\n if (!target || isInsideOverlay(target)) {\n clearHighlight();\n return;\n }\n\n const key = options.resolveKey(target);\n if (!key) {\n clearHighlight();\n return;\n }\n\n applyHighlight(target, key, clientX);\n }\n\n document.addEventListener(\n \"pointermove\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n inspectTarget(target, event.clientX, event.clientY);\n },\n { signal },\n );\n\n document.addEventListener(\n \"focusin\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n if (!target) {\n return;\n }\n const rect = target.getBoundingClientRect();\n inspectTarget(target, rect.left + rect.width / 2, rect.top);\n },\n { capture: true, signal },\n );\n\n document.addEventListener(\n \"click\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n if (!target || isInsideOverlay(target)) {\n return;\n }\n const key = options.resolveKey(target);\n if (!key) {\n return;\n }\n options.onActivateKey?.(key, target);\n },\n { capture: true, signal },\n );\n\n return {\n destroy: () => {\n abort.abort();\n clearHighlight();\n styleEl.remove();\n tip.remove();\n },\n };\n}\n","/**\n * Applies realtime gateway events to the in-memory SDK catalog (M2-SDK-04).\n */\nimport { patchCatalogEntry, reloadIfBundlePublishedNewer } from \"@stringpush/runtime-core\";\nimport type { RealtimeEvent } from \"@stringpush/realtime-protocol\";\nimport { parseRealtimeEvent } from \"@stringpush/realtime-protocol\";\nimport { notifyEditPanelRemoteUpdate } from \"./edit-panel-remote.js\";\n\nexport type RealtimeSyncContext = {\n projectId: string;\n applicationId: string;\n environmentId: string;\n notifyTranslationsUpdated: () => void;\n};\n\nexport function handleRealtimeWireMessage(raw: string, context: RealtimeSyncContext): void {\n let event: RealtimeEvent;\n try {\n event = parseRealtimeEvent(raw);\n } catch {\n return;\n }\n\n void applyRealtimeEvent(event, context).catch(() => {\n // Intent: network errors on bundle reload must not surface as unhandled rejections.\n });\n}\n\nasync function applyRealtimeEvent(\n event: RealtimeEvent,\n context: RealtimeSyncContext,\n): Promise<void> {\n if (event.type === \"translation.updated\") {\n const { payload } = event;\n if (\n payload.projectId !== context.projectId ||\n payload.environmentId !== context.environmentId\n ) {\n return;\n }\n\n notifyEditPanelRemoteUpdate({\n keyPath: payload.keyPath,\n localeCode: payload.localeCode,\n value: payload.value,\n version: payload.version,\n });\n\n if (\n patchCatalogEntry(\n payload.keyPath,\n payload.localeCode,\n payload.value,\n payload.version,\n )\n ) {\n context.notifyTranslationsUpdated();\n }\n return;\n }\n\n if (event.type === \"bundle.published\") {\n const { payload } = event;\n if (\n payload.projectId !== context.projectId ||\n payload.applicationId !== context.applicationId ||\n payload.environmentId !== context.environmentId\n ) {\n return;\n }\n\n const reloaded = await reloadIfBundlePublishedNewer(payload.releases);\n if (reloaded) {\n context.notifyTranslationsUpdated();\n }\n }\n}\n","/**\n * Exponential backoff for overlay WebSocket reconnect (M2-SDK-04).\n *\n * Intent: cap delay growth so transient disconnects recover without hammering the gateway.\n */\nexport const REALTIME_RECONNECT_INITIAL_MS = 1_000;\nexport const REALTIME_RECONNECT_MAX_MS = 30_000;\n\nexport function computeRealtimeReconnectDelayMs(\n attempt: number,\n initialMs = REALTIME_RECONNECT_INITIAL_MS,\n maxMs = REALTIME_RECONNECT_MAX_MS,\n): number {\n if (attempt <= 0) {\n return initialMs;\n }\n return Math.min(initialMs * 2 ** (attempt - 1), maxMs);\n}\n","/**\n * WebSocket client for the realtime gateway (M2-SDK-01, M2-SDK-04).\n *\n * Intent: lives only in the lazy overlay chunk — torn down by `disableEditMode()`; reconnects with backoff.\n */\nimport { handleRealtimeWireMessage, type RealtimeSyncContext } from \"./realtime-sync.js\";\nimport { computeRealtimeReconnectDelayMs } from \"./realtime-reconnect.js\";\n\nexport type RealtimeClientOptions = {\n realtimeUrl: string;\n editToken: string;\n projectId: string;\n sync: RealtimeSyncContext;\n};\n\nexport type RealtimeClient = {\n close: () => void;\n};\n\nexport function connectRealtime(options: RealtimeClientOptions): RealtimeClient | null {\n if (typeof globalThis === \"undefined\" || !(\"WebSocket\" in globalThis)) {\n return null;\n }\n\n const WebSocketCtor = (globalThis as typeof globalThis & { WebSocket: typeof WebSocket })\n .WebSocket;\n\n let intentionalClose = false;\n let reconnectAttempt = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let socket: WebSocket | null = null;\n\n const clearReconnectTimer = () => {\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n };\n\n const sendAuth = (ws: WebSocket) => {\n ws.send(\n JSON.stringify({\n type: \"auth\",\n token: options.editToken,\n projectId: options.projectId,\n }),\n );\n };\n\n const attachSocket = (ws: WebSocket) => {\n ws.addEventListener(\"open\", () => {\n reconnectAttempt = 0;\n sendAuth(ws);\n });\n\n ws.addEventListener(\"message\", (event) => {\n const raw = typeof event.data === \"string\" ? event.data : null;\n if (!raw) {\n return;\n }\n handleRealtimeWireMessage(raw, options.sync);\n });\n\n ws.addEventListener(\"close\", () => {\n if (socket === ws) {\n socket = null;\n }\n if (!intentionalClose) {\n scheduleReconnect();\n }\n });\n };\n\n const scheduleReconnect = () => {\n if (intentionalClose) {\n return;\n }\n clearReconnectTimer();\n reconnectAttempt += 1;\n const delayMs = computeRealtimeReconnectDelayMs(reconnectAttempt);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n openSocket();\n }, delayMs);\n };\n\n const openSocket = () => {\n if (intentionalClose) {\n return;\n }\n const ws = new WebSocketCtor(options.realtimeUrl);\n socket = ws;\n attachSocket(ws);\n };\n\n openSocket();\n\n return {\n close: () => {\n intentionalClose = true;\n clearReconnectTimer();\n if (\n socket &&\n (socket.readyState === WebSocketCtor.OPEN || socket.readyState === WebSocketCtor.CONNECTING)\n ) {\n socket.close();\n }\n socket = null;\n },\n };\n}\n","/**\n * Lazy-loaded overlay UI chunk (M2-SDK-01+).\n *\n * Intent: not imported by the default SDK entry — loaded via dynamic `import()` when edit mode starts.\n */\nimport { OVERLAY_CHUNK_MARKER, OVERLAY_ROOT_ID } from \"./constants.js\";\nimport { closeEditPanel, openEditPanel } from \"./edit-panel.js\";\nimport { createHighlightController } from \"./highlights.js\";\nimport { connectRealtime } from \"./realtime-client.js\";\n\nexport { OVERLAY_CHUNK_MARKER, OVERLAY_ROOT_ID } from \"./constants.js\";\n\nexport type OverlayMountContext = {\n projectId: string;\n applicationId: string;\n environmentId: string;\n environmentName: string;\n approvalEnabled?: boolean;\n adminWebOrigin?: string;\n realtimeUrl: string;\n editToken: string;\n apiBaseUrl: string;\n origin?: string;\n activeLocaleCode: string;\n resolveKey: (element: Element) => string | null;\n onCatalogPatched: (\n keyPath: string,\n localeCode: string,\n value: string,\n version: number,\n ) => void;\n onTranslationsUpdated: () => void;\n};\n\nexport type OverlayHandle = {\n destroy: () => void;\n};\n\n/**\n * Mounts overlay shell (shadow DOM), hover highlights, edit panel, and realtime WebSocket.\n */\nexport function mountOverlay(context: OverlayMountContext): OverlayHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n document.getElementById(OVERLAY_ROOT_ID)?.remove();\n closeEditPanel();\n\n const mountHost = document.createElement(\"div\");\n mountHost.id = OVERLAY_ROOT_ID;\n mountHost.setAttribute(\"data-translation-overlay\", \"true\");\n\n const shadow = mountHost.attachShadow({ mode: \"open\" });\n const label = document.createElement(\"div\");\n label.setAttribute(\"data-overlay-marker\", OVERLAY_CHUNK_MARKER);\n label.textContent = \"Translation edit mode — click text to edit\";\n label.style.cssText = [\n \"position:fixed\",\n \"bottom:16px\",\n \"right:16px\",\n \"z-index:2147483646\",\n \"padding:8px 12px\",\n \"border-radius:8px\",\n \"font:600 13px/1.4 system-ui,sans-serif\",\n \"color:#fff\",\n \"background:#1e3a5f\",\n \"box-shadow:0 4px 12px rgba(0,0,0,.25)\",\n \"pointer-events:none\",\n ].join(\";\");\n\n shadow.append(label);\n document.body.append(mountHost);\n\n let panelHandle: { destroy: () => void } | null = null;\n\n const highlights = createHighlightController({\n resolveKey: context.resolveKey,\n onActivateKey: (keyPath, element) => {\n panelHandle?.destroy();\n const suggestedDefaultMessage = element.textContent?.trim() || undefined;\n panelHandle = openEditPanel(keyPath, {\n apiBaseUrl: context.apiBaseUrl,\n editToken: context.editToken,\n origin: context.origin,\n projectId: context.projectId,\n environmentName: context.environmentName,\n approvalEnabled: context.approvalEnabled ?? false,\n adminWebOrigin: context.adminWebOrigin,\n activeLocaleCode: context.activeLocaleCode,\n suggestedDefaultMessage,\n onSaved: context.onCatalogPatched,\n });\n },\n });\n\n const realtime = connectRealtime({\n realtimeUrl: context.realtimeUrl,\n editToken: context.editToken,\n projectId: context.projectId,\n sync: {\n projectId: context.projectId,\n applicationId: context.applicationId,\n environmentId: context.environmentId,\n notifyTranslationsUpdated: context.onTranslationsUpdated,\n },\n });\n\n return {\n destroy: () => {\n panelHandle?.destroy();\n highlights.destroy();\n realtime?.close();\n mountHost.remove();\n },\n };\n}\n"]}