livelocale 0.8.0 → 0.9.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.
- package/README.md +230 -5
- package/dist/client/index.cjs +178 -70
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +36 -22
- package/dist/client/index.d.ts +36 -22
- package/dist/client/index.js +177 -70
- package/dist/client/index.js.map +1 -1
- package/dist/codemod/index.cjs +3057 -152
- package/dist/codemod/index.cjs.map +1 -1
- package/dist/codemod/index.js +3058 -153
- package/dist/codemod/index.js.map +1 -1
- package/dist/react/index.cjs +185 -77
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +185 -77
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +108 -66
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +13 -3
- package/dist/server/index.d.ts +13 -3
- package/dist/server/index.js +107 -66
- package/dist/server/index.js.map +1 -1
- package/dist/studio/assets/index-Bvbtkry-.css +1 -0
- package/dist/studio/assets/index-DC_jRsox.js +13 -0
- package/dist/studio/assets/inter-latin-300-normal-BVlfKGgI.woff2 +0 -0
- package/dist/studio/assets/inter-latin-300-normal-i8F0SvXL.woff +0 -0
- package/dist/studio/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- package/dist/studio/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
- package/dist/studio/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
- package/dist/studio/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
- package/dist/studio/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
- package/dist/studio/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
- package/dist/studio/assets/inter-latin-700-normal-BLAVimhd.woff +0 -0
- package/dist/studio/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
- package/dist/studio/assets/outfit-latin-400-normal-BGsTXAXT.woff2 +0 -0
- package/dist/studio/assets/outfit-latin-400-normal-DMwTpYkH.woff +0 -0
- package/dist/studio/assets/outfit-latin-500-normal-ClnHRwRh.woff +0 -0
- package/dist/studio/assets/outfit-latin-500-normal-DKnIMDSk.woff2 +0 -0
- package/dist/studio/assets/outfit-latin-600-normal-B7SfZ07L.woff2 +0 -0
- package/dist/studio/assets/outfit-latin-600-normal-BEfTtDA7.woff +0 -0
- package/dist/studio/assets/outfit-latin-700-normal-Cu9v6i1X.woff2 +0 -0
- package/dist/studio/assets/outfit-latin-700-normal-D4itBLBr.woff +0 -0
- package/dist/studio/index.html +3 -6
- package/dist/vite/index.cjs +739 -77
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.d.cts +22 -2
- package/dist/vite/index.d.ts +22 -2
- package/dist/vite/index.js +738 -77
- package/dist/vite/index.js.map +1 -1
- package/dist/{wrap-CsOtHp8r.d.cts → wrap-2qVxLN9Q.d.cts} +10 -18
- package/dist/{wrap-CsOtHp8r.d.ts → wrap-2qVxLN9Q.d.ts} +10 -18
- package/package.json +4 -1
- package/dist/studio/assets/index-D7ifnqy5.js +0 -10
- package/dist/studio/assets/index-Zh0KtFUH.css +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/index.ts","../../src/client/watermark.ts","../../src/core/env.ts","../../src/client/dom-interceptor.ts","../../src/client/iframe-bridge.ts","../../src/client/wrap.ts"],"sourcesContent":["'use client';\n\nimport { DOMInterceptor } from './dom-interceptor.js';\nimport { IframeBridge } from './iframe-bridge.js';\nimport { decodeKey } from './watermark.js';\nimport { isLiveLocaleEnabled } from '../core/env.js';\n\nexport { DOMInterceptor } from './dom-interceptor.js';\nexport {\n IframeBridge,\n type TranslationPayload,\n type ClientMessageType,\n type StudioMessageType,\n} from './iframe-bridge.js';\nexport {\n encodeKey,\n decodeKey,\n stripWatermark,\n sanitizeZeroWidth,\n hasWatermark,\n unwrap,\n} from './watermark.js';\n\n// wrapTranslationEngine lives in wrap.ts (no 'use client') so it can also be\n// imported by Server Components via 'livelocale/server'.\nexport { wrapTranslationEngine, wrapNextIntl, type WrapOptions } from './wrap.js';\n\n// ─── Live preview support ───────────────────────────────────────────────────\n\n// Text nodes mutated by APPLY_PREVIEW, with their original value so the\n// preview can be reverted when the selection is cleared.\nconst previewNodes = new Map<Text, { key: string; original: string }>();\n\n/**\n * Applies a live preview value to every rendered element matching `key`\n * without touching the locale files. Only the visible text after the\n * watermark prefix is replaced, keeping the watermark (and thus the\n * data-i18n-key scanner) intact.\n */\nfunction applyPreview(key: string, value: string): void {\n const elements = document.querySelectorAll<HTMLElement>('[data-i18n-key]');\n elements.forEach((el) => {\n if (el.getAttribute('data-i18n-key') !== key) return;\n\n const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null);\n const node = walker.nextNode() as Text | null;\n if (!node) return;\n\n const raw = node.nodeValue ?? '';\n const decoded = decodeKey(raw);\n if (!decoded) return;\n\n // Preserve the watermark prefix, swap only the visible text\n const next = raw.slice(0, raw.length - decoded.cleanText.length) + value;\n // Skipping unchanged text avoids a mutation, so re-applying is idempotent.\n if (next === raw) return;\n if (!previewNodes.has(node)) {\n previewNodes.set(node, { key, original: raw });\n }\n node.nodeValue = next;\n });\n}\n\n/**\n * Makes a saved value the text that stays on the page. In development the\n * dev server re-renders from the edited file anyway; in preview mode the\n * deployment still serves the old text until it is rebuilt, so the Studio\n * re-sends saved values whenever the page reloads or changes.\n */\nfunction commitPreview(key: string, value: string): void {\n applyPreview(key, value);\n previewNodes.forEach((entry, node) => {\n if (entry.key === key) previewNodes.delete(node);\n });\n}\n\n/** Restores all previewed text nodes to their pre-preview values. */\nfunction restorePreviews(): void {\n previewNodes.forEach(({ original }, node) => {\n node.nodeValue = original;\n });\n previewNodes.clear();\n}\n\n// ─── Auto-initialization ────────────────────────────────────────────────────\n\n// Auto-initialize the interceptor if running in the browser, in development mode,\n// AND only when the page is embedded inside the studio iframe (not accessed directly).\nif (\n typeof window !== 'undefined' &&\n isLiveLocaleEnabled() &&\n window.parent !== window // <-- iframe guard: only activate inside the studio\n) {\n const urlParams = new URLSearchParams(window.location.search);\n const allowedOrigin = urlParams.get('livelocale-origin') || 'http://localhost:3010';\n\n const bridge = new IframeBridge(allowedOrigin);\n const interceptor = new DOMInterceptor(\n (payload) => {\n bridge.sendToParent('ELEMENT_SELECTED', payload);\n },\n (keys) => {\n bridge.sendToParent('VISIBLE_KEYS_CHANGED', keys);\n }\n );\n\n interceptor.init();\n\n // Send a READY message to the parent studio\n bridge.sendToParent('READY', { url: window.location.pathname + window.location.search });\n\n // Listen to studio commands\n bridge.listenToParent((type, payload) => {\n if (type === 'APPLY_PREVIEW') {\n if (payload && typeof payload.key === 'string' && typeof payload.value === 'string') {\n applyPreview(payload.key, payload.value);\n }\n } else if (type === 'COMMIT_PREVIEW') {\n if (payload && typeof payload.key === 'string' && typeof payload.value === 'string') {\n commitPreview(payload.key, payload.value);\n }\n } else if (type === 'CLEAR_SELECTION') {\n // Revert any live preview, then re-trigger/reset interceptor highlight states\n restorePreviews();\n interceptor.destroy();\n interceptor.init();\n }\n });\n\n // Attach reference to window for developer inspection/diagnostics\n (window as any).__LIVELOCALE_CLIENT__ = {\n bridge,\n interceptor,\n };\n}\n","/**\n * Zero-Width Watermarking Encoder/Decoder\n *\n * Maps translation keys into invisible Unicode zero-width characters and\n * prepends them as a binary watermark onto the translation string.\n *\n * Delimiters:\n * \\u200D – Zero-Width Joiner (ZWJ) → boundary start/end marker\n * \\u200B – Zero-Width Space (ZWS) → binary bit 0\n * \\u200C – Zero-Width Non-Joiner (ZWNJ) → binary bit 1\n *\n * The watermark is strictly prepended so it survives downstream truncation\n * (e.g., text.substring()) that removes only from the right.\n */\n\nconst DELIM = '\\u200D'; // ZWJ – boundary delimiter\nconst BIT_0 = '\\u200B'; // ZWS – represents bit 0\nconst BIT_1 = '\\u200C'; // ZWNJ – represents bit 1\n\nconst ZW_RE = /[\\u200B\\u200C\\u200D]/g;\n\n/** Encodes a translation key as an invisible watermark prefix on `text`. */\nexport function encodeKey(text: string, key: string): string {\n const bytes = new TextEncoder().encode(key);\n let bits = '';\n for (const byte of bytes) {\n bits += byte.toString(2).padStart(8, '0');\n }\n\n let watermark = DELIM;\n for (const bit of bits) {\n watermark += bit === '0' ? BIT_0 : BIT_1;\n }\n watermark += DELIM;\n\n return watermark + text;\n}\n\n/**\n * Decodes a watermark prefix from `text`.\n *\n * Returns `{ key, cleanText }` where `cleanText` is the original visible\n * text with the watermark stripped. If no valid watermark is found,\n * returns `null`.\n */\nexport function decodeKey(text: string): { key: string; cleanText: string } | null {\n if (!text.startsWith(DELIM)) return null;\n\n const end = text.indexOf(DELIM, 1);\n if (end === -1) return null;\n\n const bitsStr = text.slice(1, end);\n // Validate: every char must be a known ZW bit\n for (const ch of bitsStr) {\n if (ch !== BIT_0 && ch !== BIT_1) return null;\n }\n\n if (bitsStr.length !== 0 && bitsStr.length % 8 !== 0) return null;\n\n const bytes: number[] = [];\n for (let i = 0; i < bitsStr.length; i += 8) {\n let byte = 0;\n for (let j = 0; j < 8; j++) {\n byte = (byte << 1) | (bitsStr[i + j] === BIT_1 ? 1 : 0);\n }\n bytes.push(byte);\n }\n\n try {\n const key = new TextDecoder().decode(new Uint8Array(bytes));\n const cleanText = text.slice(end + 1);\n return { key, cleanText };\n } catch {\n return null;\n }\n}\n\n/**\n * Strips a valid watermark *prefix* from `text`, leaving any legitimate\n * zero-width characters inside the visible text untouched.\n *\n * ZWNJ (\\u200C) and ZWSP (\\u200B) are valid orthography in Persian, Urdu,\n * Thai, and other scripts — a blanket removal would silently corrupt real\n * translations. Only the decoded watermark prefix is removed.\n */\nexport function stripWatermark(text: string): string {\n const decoded = decodeKey(text);\n if (decoded) return decoded.cleanText;\n return text;\n}\n\n/**\n * Aggressively removes every zero-width character from `text`.\n *\n * Use only for clipboard/form sanitization where no visible text should ever\n * contain ZW characters. Do NOT use on translation content destined for\n * locale files — prefer `stripWatermark`.\n */\nexport function sanitizeZeroWidth(text: string): string {\n return text.replace(ZW_RE, '');\n}\n\n/**\n * Returns true if `text` begins with a valid watermark prefix.\n */\nexport function hasWatermark(text: string): boolean {\n return text.startsWith(DELIM);\n}\n\n/**\n * Deeply strips watermark prefixes from a value: strings are cleaned with\n * `stripWatermark`, arrays and plain objects are rebuilt with cleaned members,\n * everything else is returned as-is. The input is never mutated.\n *\n * Use it wherever a translated string must be compared, measured, serialized\n * or handed to code that is not display-only, e.g. `unwrap(t('yes')) === 'Yes'`.\n */\nexport function unwrap<T>(value: T): T {\n if (typeof value === 'string') return stripWatermark(value) as T;\n if (Array.isArray(value)) return value.map((item) => unwrap(item)) as T;\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n out[key] = unwrap((value as Record<string, unknown>)[key]);\n }\n return out as T;\n }\n return value;\n}\n","/**\n * Single source of truth for \"is LiveLocale active right now, and in which mode?\".\n *\n * Modes:\n * - `development`: NODE_ENV is `development` (the default, zero-config mode).\n * - `preview`: explicitly opted in with `LIVELOCALE=preview` (server) or\n * `NEXT_PUBLIC_LIVELOCALE=preview` (client bundles). Works in production builds so a\n * preview deployment can be edited through a hosted Studio. The mutation API then\n * requires a bearer token (see the router).\n * - `off`: everything else.\n *\n * Rules, in order:\n * 1. `LIVELOCALE=off|0|false` (or `NEXT_PUBLIC_LIVELOCALE=off`) always wins → off.\n * 2. `LIVELOCALE=preview` / `NEXT_PUBLIC_LIVELOCALE=preview` → preview.\n * 3. `NODE_ENV` must be `development`, otherwise off.\n * 4. `LIVELOCALE=force` skips rule 5.\n * 5. On the server, a known hosting platform (Vercel preview/production, Netlify,\n * Kubernetes, Render, Fly.io, Railway, Heroku) → off, so a mis-configured staging\n * deploy cannot leak watermarks. A one-time warning is logged.\n *\n * `process.env.NODE_ENV` and `process.env.NEXT_PUBLIC_LIVELOCALE` are referenced as\n * literal member expressions so Next.js and Vite can inline them in browser bundles.\n */\n\nexport type LiveLocaleMode = 'off' | 'development' | 'preview';\n\nconst OFF_VALUES = new Set(['off', '0', 'false']);\n\nlet warnedDeployed = false;\n\n/** Test hook: forget that the deployed-platform warning was already printed. */\nexport function resetEnvWarnings(): void {\n warnedDeployed = false;\n}\n\nfunction readNodeEnv(): string | undefined {\n try {\n return process.env.NODE_ENV;\n } catch {\n return undefined;\n }\n}\n\n/** Set by the Vite plugin (and usable by any host) to mirror the server flag into the page. */\nexport const BROWSER_FLAG_GLOBAL = '__LIVELOCALE_FLAG__';\n\nfunction readPublicFlag(): string | undefined {\n let value: string | undefined;\n try {\n value = process.env.NEXT_PUBLIC_LIVELOCALE;\n } catch {\n value = undefined;\n }\n if (value) return value;\n try {\n const g = globalThis as any;\n const fromGlobal = g.window?.[BROWSER_FLAG_GLOBAL] ?? g[BROWSER_FLAG_GLOBAL];\n return typeof fromGlobal === 'string' ? fromGlobal : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === 'undefined' || !process.env) return undefined;\n const value = process.env[name];\n return value === '' ? undefined : value;\n}\n\n/** Returns a human-readable platform name when running on a known hosting platform. */\nexport function detectDeployedPlatform(): string | null {\n if (typeof window !== 'undefined') return null; // browser: nothing to detect\n const vercelEnv = readEnv('VERCEL_ENV');\n if (vercelEnv === 'production' || vercelEnv === 'preview')\n return `Vercel (VERCEL_ENV=${vercelEnv})`;\n const netlifyContext = readEnv('CONTEXT');\n if (readEnv('NETLIFY') === 'true' && netlifyContext && netlifyContext !== 'dev') {\n return `Netlify (CONTEXT=${netlifyContext})`;\n }\n if (readEnv('KUBERNETES_SERVICE_HOST')) return 'Kubernetes';\n if (readEnv('RENDER') === 'true') return 'Render';\n if (readEnv('FLY_APP_NAME')) return 'Fly.io';\n if (readEnv('RAILWAY_ENVIRONMENT')) return 'Railway';\n if (readEnv('DYNO')) return 'Heroku';\n return null;\n}\n\nfunction evaluate(): { mode: LiveLocaleMode; reason: string | null } {\n const flag = (readEnv('LIVELOCALE') ?? '').toLowerCase();\n const publicFlag = (readPublicFlag() ?? '').toLowerCase();\n\n if (OFF_VALUES.has(flag)) return { mode: 'off', reason: 'LIVELOCALE kill switch is set' };\n if (OFF_VALUES.has(publicFlag)) {\n return { mode: 'off', reason: 'NEXT_PUBLIC_LIVELOCALE kill switch is set' };\n }\n\n if (flag === 'preview' || publicFlag === 'preview') return { mode: 'preview', reason: null };\n\n const nodeEnv = readNodeEnv();\n if (nodeEnv !== 'development') {\n return {\n mode: 'off',\n reason: `NODE_ENV is \"${nodeEnv ?? 'undefined'}\" (must be \"development\", or set LIVELOCALE=preview)`,\n };\n }\n\n if (flag === 'force') return { mode: 'development', reason: null };\n\n const platform = detectDeployedPlatform();\n if (platform) {\n if (!warnedDeployed) {\n warnedDeployed = true;\n console.warn(\n `[livelocale] Disabled: deployed environment detected (${platform}) while NODE_ENV=development. ` +\n `Set LIVELOCALE=force to override, LIVELOCALE=preview for a hosted preview, or LIVELOCALE=off to silence this warning.`\n );\n }\n return { mode: 'off', reason: `deployed environment detected (${platform})` };\n }\n\n return { mode: 'development', reason: null };\n}\n\n/** The active mode: `off`, `development`, or `preview`. */\nexport function getLiveLocaleMode(): LiveLocaleMode {\n return evaluate().mode;\n}\n\n/**\n * Returns `null` when LiveLocale should be active, otherwise a short reason\n * suitable for error messages and logs.\n */\nexport function getDisabledReason(): string | null {\n return evaluate().reason;\n}\n\n/** True when watermarking, the mutation API, and the client scanner should run. */\nexport function isLiveLocaleEnabled(): boolean {\n return evaluate().mode !== 'off';\n}\n","import { decodeKey, stripWatermark, sanitizeZeroWidth } from './watermark.js';\nimport { isLiveLocaleEnabled } from '../core/env.js';\n\n// Debounce helper: delays executing fn until after wait ms have elapsed\n// since the last call. Returns a cancel function.\nfunction debounce<T extends (...args: any[]) => void>(fn: T, wait: number): T & { cancel(): void } {\n let timer: ReturnType<typeof setTimeout> | null = null;\n const debounced = (...args: Parameters<T>) => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n fn(...args);\n }, wait);\n };\n debounced.cancel = () => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n };\n return debounced as T & { cancel(): void };\n}\n\nexport class DOMInterceptor {\n private onElementSelected: (payload: {\n key: string;\n fallbackValue: string;\n currentValue: string;\n }) => void;\n private onVisibleKeysChanged?: (keys: string[]) => void;\n\n private hoveredElement: HTMLElement | null = null;\n private observer: MutationObserver | null = null;\n private inputSanitizersAttached: boolean = false;\n\n // ─── Floating Overlay ────────────────────────────────────────────────────\n private overlay: HTMLDivElement | null = null;\n private overlayBadge: HTMLSpanElement | null = null;\n\n // ─── Alt key state ───────────────────────────────────────────────────────\n private isAltHeld: boolean = false;\n\n // Debounced key-broadcast to avoid flooding the parent on rapid DOM mutations\n private broadcastVisibleKeys: (() => void) & { cancel(): void };\n\n constructor(\n onElementSelected: (payload: {\n key: string;\n fallbackValue: string;\n currentValue: string;\n }) => void,\n onVisibleKeysChanged?: (keys: string[]) => void\n ) {\n this.onElementSelected = onElementSelected;\n this.onVisibleKeysChanged = onVisibleKeysChanged;\n\n this.broadcastVisibleKeys = debounce(() => {\n if (this.onVisibleKeysChanged) {\n this.onVisibleKeysChanged(this.getActiveKeys());\n }\n }, 200);\n }\n\n /**\n * Returns the unique set of translation keys currently visible in the DOM.\n */\n getActiveKeys(): string[] {\n const elements = document.querySelectorAll<HTMLElement>('[data-i18n-key]');\n const keys = new Set<string>();\n elements.forEach((el) => {\n const key = el.getAttribute('data-i18n-key');\n if (key) keys.add(key);\n });\n return Array.from(keys);\n }\n\n /**\n * Initializes the DOM event interceptors in development mode.\n * Also launches the watermark scanner and MutationObserver.\n */\n init(): void {\n // RULE CLT-004: INIT GUARD\n if (!isLiveLocaleEnabled()) {\n return;\n }\n\n if (typeof window === 'undefined') {\n return;\n }\n\n // Inject the floating highlighter overlay into the document body\n this.injectOverlay();\n\n // Attach listeners on window capture phase (RULE CLT-002)\n window.addEventListener('mouseover', this.handleMouseOver, true);\n window.addEventListener('mouseout', this.handleMouseOut, true);\n window.addEventListener('click', this.handleClick, true);\n window.addEventListener('copy', this.handleCopy, true);\n window.addEventListener('paste', this.handlePaste, true);\n window.addEventListener('scroll', this.handleScroll, true);\n window.addEventListener('resize', this.handleResize, false);\n window.addEventListener('keydown', this.handleKeyDown, true);\n window.addEventListener('keyup', this.handleKeyUp, true);\n\n // Phase 3: Scan existing DOM for watermarked text nodes\n this.scanSubtree(document.body);\n\n // Phase 3: Observe future DOM mutations for watermarked nodes\n this.attachMutationObserver();\n\n // Phase 3: Sanitize form inputs/submissions to strip ZW chars\n this.attachInputSanitizers();\n\n // Broadcast the initial set of visible keys to the studio sidebar\n this.broadcastVisibleKeys();\n }\n\n /**\n * Cleans up all attached event listeners, the MutationObserver,\n * the floating overlay, and the form-sanitization listeners.\n */\n destroy(): void {\n if (typeof window === 'undefined') {\n return;\n }\n window.removeEventListener('mouseover', this.handleMouseOver, true);\n window.removeEventListener('mouseout', this.handleMouseOut, true);\n window.removeEventListener('click', this.handleClick, true);\n window.removeEventListener('copy', this.handleCopy, true);\n window.removeEventListener('paste', this.handlePaste, true);\n window.removeEventListener('scroll', this.handleScroll, true);\n window.removeEventListener('resize', this.handleResize, false);\n window.removeEventListener('keydown', this.handleKeyDown, true);\n window.removeEventListener('keyup', this.handleKeyUp, true);\n window.removeEventListener('beforeinput', this.handleBeforeInput, true);\n\n // Detach form-submit sanitizer\n document.removeEventListener('submit', this.handleFormSubmit, true);\n\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n\n this.inputSanitizersAttached = false;\n\n // Remove floating overlay from DOM\n if (this.overlay && this.overlay.parentNode) {\n this.overlay.parentNode.removeChild(this.overlay);\n }\n this.overlay = null;\n this.overlayBadge = null;\n this.hoveredElement = null;\n\n // Cancel any pending debounced broadcasts\n this.broadcastVisibleKeys.cancel();\n }\n\n // ─── Floating Overlay ────────────────────────────────────────────────────\n\n /**\n * Creates and injects a single floating overlay div into the document body.\n * The overlay is positioned using fixed coordinates from getBoundingClientRect()\n * and uses pointer-events: none so it never blocks user interaction.\n */\n private injectOverlay(): void {\n if (this.overlay) return; // Guard against double injection\n\n const overlay = document.createElement('div');\n overlay.id = 'livelocale-highlighter-overlay';\n overlay.style.cssText = [\n 'position: fixed',\n 'top: 0',\n 'left: 0',\n 'width: 0',\n 'height: 0',\n 'pointer-events: none',\n 'z-index: 2147483647', // Maximum z-index\n 'box-sizing: border-box',\n 'opacity: 0',\n 'border-radius: 4px',\n 'transition: opacity 120ms ease, top 80ms ease, left 80ms ease, width 80ms ease, height 80ms ease, border-color 150ms ease',\n ].join('; ');\n\n const badge = document.createElement('span');\n badge.style.cssText = [\n 'position: absolute',\n 'top: -22px',\n 'left: 0',\n 'max-width: 280px',\n 'white-space: nowrap',\n 'overflow: hidden',\n 'text-overflow: ellipsis',\n 'background: rgba(15, 20, 40, 0.92)',\n 'border: 1px solid rgba(99, 102, 241, 0.5)',\n 'color: #a5b4fc',\n 'font-family: ui-monospace, SFMono-Regular, Menlo, monospace',\n 'font-size: 10px',\n 'font-weight: 600',\n 'line-height: 1',\n 'padding: 3px 7px',\n 'border-radius: 4px',\n 'letter-spacing: 0.02em',\n 'backdrop-filter: blur(4px)',\n 'pointer-events: none',\n ].join('; ');\n\n overlay.appendChild(badge);\n document.body.appendChild(overlay);\n\n this.overlay = overlay;\n this.overlayBadge = badge;\n }\n\n /**\n * Positions the floating overlay to surround the given element.\n * Uses getBoundingClientRect() for fixed-positioned geometry.\n */\n private positionOverlay(element: HTMLElement): void {\n if (!this.overlay) return;\n\n const rect = element.getBoundingClientRect();\n const computedStyle = window.getComputedStyle(element);\n const borderRadius = computedStyle.borderRadius || '4px';\n\n this.overlay.style.top = `${rect.top}px`;\n this.overlay.style.left = `${rect.left}px`;\n this.overlay.style.width = `${rect.width}px`;\n this.overlay.style.height = `${rect.height}px`;\n this.overlay.style.borderRadius = borderRadius;\n this.overlay.style.opacity = '1';\n\n this.updateOverlayStyle();\n }\n\n /**\n * Updates overlay border color and badge text based on current Alt key state.\n */\n private updateOverlayStyle(): void {\n if (!this.overlay || !this.overlayBadge) return;\n\n const key = this.hoveredElement?.getAttribute('data-i18n-key') || '';\n\n if (this.isAltHeld) {\n // Alt is held — bright solid border signals \"click to edit\"\n this.overlay.style.border = '2px solid #3b82f6';\n this.overlay.style.boxShadow =\n '0 0 0 1px rgba(59, 130, 246, 0.3), inset 0 0 0 1px rgba(59, 130, 246, 0.1)';\n this.overlayBadge.style.color = '#93c5fd';\n this.overlayBadge.style.borderColor = 'rgba(59, 130, 246, 0.6)';\n this.overlayBadge.textContent = `✎ Click to edit: ${key}`;\n } else {\n // Hover — subtle dashed border signals \"Alt + Click to edit\"\n this.overlay.style.border = '1.5px dashed rgba(99, 102, 241, 0.65)';\n this.overlay.style.boxShadow = '0 0 0 1px rgba(99, 102, 241, 0.15)';\n this.overlayBadge.style.color = '#a5b4fc';\n this.overlayBadge.style.borderColor = 'rgba(99, 102, 241, 0.5)';\n this.overlayBadge.textContent = `⌥ Alt+Click to edit: ${key}`;\n }\n }\n\n /**\n * Hides the floating overlay without removing it from the DOM.\n */\n private hideOverlay(): void {\n if (!this.overlay) return;\n this.overlay.style.opacity = '0';\n }\n\n // ─── Phase 3: Watermark DOM Scanner ──────────────────────────────────────\n\n /**\n * Walks all text nodes inside `root` and decodes any watermark prefix.\n * When found, attaches data-i18n-key / data-i18n-template to the\n * immediate parent HTMLElement without mutating the text node itself\n * (hydration safety: RULE CLT-007).\n */\n private scanSubtree(root: Node): void {\n if (!root) return;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);\n\n let node: Text | null;\n while ((node = walker.nextNode() as Text | null) !== null) {\n this.processTextNode(node);\n }\n }\n\n private processTextNode(node: Text): void {\n const raw = node.nodeValue ?? '';\n if (!raw.includes('\\u200D')) return; // fast bail-out\n\n const decoded = decodeKey(raw);\n if (!decoded) return;\n\n const parent = node.parentElement;\n if (!parent) return;\n\n // RULE CLT-007: Do NOT strip the ZW chars from the text node –\n // doing so would break React's reconciliation / hydration tree.\n // Only annotate the parent element.\n parent.setAttribute('data-i18n-key', decoded.key);\n\n // Use the clean (visible) text as the fallback template if not already set\n if (!parent.hasAttribute('data-i18n-template')) {\n parent.setAttribute('data-i18n-template', decoded.cleanText.trim());\n }\n }\n\n /**\n * Attaches a MutationObserver to catch newly rendered / streamed elements.\n */\n private attachMutationObserver(): void {\n if (this.observer) return;\n\n this.observer = new MutationObserver((mutations) => {\n let needsBroadcast = false;\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n for (const added of mutation.addedNodes) {\n if (added.nodeType === Node.ELEMENT_NODE) {\n this.scanSubtree(added);\n needsBroadcast = true;\n } else if (added.nodeType === Node.TEXT_NODE) {\n this.processTextNode(added as Text);\n needsBroadcast = true;\n }\n }\n } else if (mutation.type === 'characterData') {\n if (mutation.target.nodeType === Node.TEXT_NODE) {\n this.processTextNode(mutation.target as Text);\n needsBroadcast = true;\n }\n }\n }\n if (needsBroadcast) {\n this.broadcastVisibleKeys();\n }\n });\n\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n // ─── Phase 3: Input & Form Sanitization ──────────────────────────────────\n\n /**\n * Patches input value descriptors and attaches a form submit listener\n * to strip zero-width watermark characters before any data leaves the\n * browser (preventing backend schema validation rejections or storage\n * of invisible chars).\n */\n private attachInputSanitizers(): void {\n if (this.inputSanitizersAttached) return;\n this.inputSanitizersAttached = true;\n\n // Capture form submissions at the document level\n document.addEventListener('submit', this.handleFormSubmit, true);\n\n // Intercept `beforeinput` for direct keyboard input into inputs/textareas\n window.addEventListener('beforeinput', this.handleBeforeInput, true);\n\n // Patch HTMLInputElement.prototype.value getter/setter\n this.patchInputValueDescriptor(HTMLInputElement.prototype);\n this.patchInputValueDescriptor(HTMLTextAreaElement.prototype);\n }\n\n /**\n * Patches the `value` property descriptor of an input prototype so\n * that reads return sanitized strings even if the user somehow pastes\n * watermark characters.\n */\n private patchInputValueDescriptor(proto: HTMLInputElement | HTMLTextAreaElement): void {\n const original = Object.getOwnPropertyDescriptor(proto, 'value');\n if (!original || !original.get || !original.set) return;\n\n // Prevent double-patching\n if ((proto as any).__liveLocaleSanitized__) return;\n (proto as any).__liveLocaleSanitized__ = true;\n\n Object.defineProperty(proto, 'value', {\n get() {\n const raw: string = original.get!.call(this);\n return sanitizeZeroWidth(raw);\n },\n set(val: string) {\n const clean = typeof val === 'string' ? sanitizeZeroWidth(val) : val;\n original.set!.call(this, clean);\n },\n configurable: true,\n });\n }\n\n private handleFormSubmit = (event: Event): void => {\n const form = event.target as HTMLFormElement | null;\n if (!form) return;\n\n const inputs = form.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>('input, textarea');\n inputs.forEach((input) => {\n const cleaned = sanitizeZeroWidth(input.value);\n if (cleaned !== input.value) {\n // Use the original setter to set the cleaned value directly\n const proto =\n input instanceof HTMLTextAreaElement\n ? HTMLTextAreaElement.prototype\n : HTMLInputElement.prototype;\n const descriptor = Object.getOwnPropertyDescriptor(proto, 'value');\n if (descriptor?.set) {\n descriptor.set.call(input, cleaned);\n }\n }\n });\n };\n\n private handleBeforeInput = (_event: InputEvent): void => {\n // No-op: direct keyboard input doesn't contain ZW characters.\n // This listener is reserved for future clipboard paste interception.\n };\n\n // ─── Alt Key Tracking ─────────────────────────────────────────────────────\n\n private handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Alt') {\n this.isAltHeld = true;\n // Update the overlay style immediately if an element is hovered\n if (this.hoveredElement) {\n this.updateOverlayStyle();\n }\n }\n };\n\n private handleKeyUp = (event: KeyboardEvent): void => {\n if (event.key === 'Alt') {\n this.isAltHeld = false;\n // Revert to the \"hover hint\" style\n if (this.hoveredElement) {\n this.updateOverlayStyle();\n }\n }\n };\n\n // ─── Viewport change handlers (reposition overlay on scroll / resize) ─────\n\n private handleScroll = (): void => {\n if (this.hoveredElement) {\n this.positionOverlay(this.hoveredElement);\n }\n };\n\n private handleResize = (): void => {\n if (this.hoveredElement) {\n this.positionOverlay(this.hoveredElement);\n }\n };\n\n // ─── Mouse / Click Handlers ───────────────────────────────────────────────\n\n private handleMouseOver = (event: MouseEvent): void => {\n const target = event.target as HTMLElement | null;\n if (!target || typeof target.closest !== 'function') return;\n\n const keyedElement = target.closest('[data-i18n-key]') as HTMLElement | null;\n if (!keyedElement) {\n // Moved onto a non-keyed element — hide overlay\n if (this.hoveredElement) {\n this.hideOverlay();\n this.hoveredElement = null;\n }\n return;\n }\n\n // RULE CLT-003: ALWAYS CHECK data-i18n-key EXISTENCE\n const key = keyedElement.getAttribute('data-i18n-key');\n if (key === null) return;\n\n this.hoveredElement = keyedElement;\n this.positionOverlay(keyedElement);\n };\n\n private handleMouseOut = (event: MouseEvent): void => {\n const target = event.target as HTMLElement | null;\n if (!target || typeof target.closest !== 'function') return;\n\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n // If we're moving into a child of the same keyed element, don't hide\n if (relatedTarget && this.hoveredElement && this.hoveredElement.contains(relatedTarget)) {\n return;\n }\n\n const keyedElement = target.closest('[data-i18n-key]') as HTMLElement | null;\n if (!keyedElement || keyedElement !== this.hoveredElement) return;\n\n this.hideOverlay();\n this.hoveredElement = null;\n };\n\n private handleClick = (event: MouseEvent): void => {\n // Feature 1: Only intercept clicks when Alt key is held.\n // Without Alt, let the event propagate naturally so links and buttons work.\n if (!event.altKey) return;\n\n const target = event.target as HTMLElement | null;\n if (!target || typeof target.closest !== 'function') return;\n\n const keyedElement = target.closest('[data-i18n-key]') as HTMLElement | null;\n if (!keyedElement) return;\n\n // RULE CLT-003: ALWAYS CHECK data-i18n-key EXISTENCE\n const key = keyedElement.getAttribute('data-i18n-key');\n if (key === null) return;\n\n // RULE CLT-002: EVENT LISTENERS IN CAPTURE PHASE\n event.preventDefault();\n event.stopPropagation();\n\n // Use innerText, stripping any invisible ZW chars before sending to studio\n const rawText = keyedElement.innerText || '';\n const cleanText = stripWatermark(rawText);\n\n const fallbackValue = keyedElement.getAttribute('data-i18n-template') || cleanText;\n const currentValue = cleanText;\n\n this.onElementSelected({\n key,\n fallbackValue,\n currentValue,\n });\n };\n\n private handleCopy = (event: ClipboardEvent): void => {\n const selection = window.getSelection()?.toString() || '';\n if (\n selection.includes('\\u200D') ||\n selection.includes('\\u200B') ||\n selection.includes('\\u200C')\n ) {\n const clean = sanitizeZeroWidth(selection);\n event.clipboardData?.setData('text/plain', clean);\n event.preventDefault();\n }\n };\n\n private handlePaste = (event: ClipboardEvent): void => {\n const text = event.clipboardData?.getData('text/plain') || '';\n if (text.includes('\\u200D') || text.includes('\\u200B') || text.includes('\\u200C')) {\n event.preventDefault();\n const cleaned = sanitizeZeroWidth(text);\n const targetInput = event.target as HTMLInputElement | HTMLTextAreaElement | null;\n if (targetInput && 'value' in targetInput) {\n const proto =\n targetInput instanceof HTMLTextAreaElement\n ? HTMLTextAreaElement.prototype\n : HTMLInputElement.prototype;\n const nativeValueSetter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;\n\n if (nativeValueSetter) {\n const start = targetInput.selectionStart || 0;\n const end = targetInput.selectionEnd || 0;\n const val = targetInput.value;\n const cleanedValue = val.slice(0, start) + cleaned + val.slice(end);\n\n nativeValueSetter.call(targetInput, cleanedValue);\n targetInput.selectionStart = targetInput.selectionEnd = start + cleaned.length;\n\n // Dispatch bubbling input event so React virtual DOM state syncs\n targetInput.dispatchEvent(new Event('input', { bubbles: true }));\n }\n }\n }\n };\n}\n","export type ClientMessageType = 'ELEMENT_SELECTED' | 'READY' | 'ERROR' | 'VISIBLE_KEYS_CHANGED';\nexport type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION';\n\nexport interface TranslationPayload {\n key: string;\n fallbackValue: string;\n currentValue: string;\n}\n\nexport class IframeBridge {\n private allowedOrigin: string;\n\n constructor(allowedOrigin: string) {\n this.allowedOrigin = allowedOrigin;\n }\n\n /**\n * Sends a message from the iframe client to the parent Studio window.\n */\n sendToParent(type: ClientMessageType, payload?: any): void {\n if (typeof window === 'undefined' || !window.parent || window.parent === window) {\n // Not in an iframe context\n return;\n }\n\n // RULE CLT-006: PAYLOAD SANITIZATION\n if (type === 'ELEMENT_SELECTED') {\n const p = payload as TranslationPayload;\n if (\n !p ||\n typeof p.key !== 'string' ||\n p.key.trim() === '' ||\n !/^[a-zA-Z0-9._-]+$/.test(p.key) ||\n typeof p.currentValue !== 'string'\n ) {\n // Drop invalid payload silently\n return;\n }\n }\n\n if (type === 'VISIBLE_KEYS_CHANGED') {\n // Validate payload is an array of valid key strings\n if (!Array.isArray(payload)) return;\n const validKeys = (payload as unknown[]).filter(\n (k): k is string => typeof k === 'string' && /^[a-zA-Z0-9._-]+$/.test(k)\n );\n // Replace the payload with the sanitized array\n payload = validKeys;\n }\n\n const message = {\n source: 'livelocale-client',\n type,\n payload,\n };\n\n window.parent.postMessage(message, this.allowedOrigin);\n }\n\n /**\n * Sets up a listener for messages originating from the parent Studio dashboard.\n */\n listenToParent(callback: (type: StudioMessageType, payload: any) => void): () => void {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const listener = (event: MessageEvent) => {\n // RULE CLT-005: MESSAGE ORIGIN VALIDATION\n if (event.origin !== this.allowedOrigin) {\n return;\n }\n if (event.data?.source !== 'livelocale-studio') {\n return;\n }\n\n const { type, payload } = event.data;\n callback(type, payload);\n };\n\n window.addEventListener('message', listener);\n\n // Return a function to clean up the event listener\n return () => {\n window.removeEventListener('message', listener);\n };\n }\n}\n","import { encodeKey } from './watermark.js';\nimport { isLiveLocaleEnabled } from '../core/env.js';\n\nexport interface WrapOptions {\n /**\n * A dot-separated key prefix to prepend to all resolved key paths.\n *\n * Use this when wrapping a namespaced translation function (e.g.\n * react-i18next `useTranslation('home')` or next-intl `useTranslations('home')`)\n * so that the watermarked key matches the actual path in your locale JSON file.\n *\n * @example\n * const { t: rawT } = useTranslation('home');\n * const t = wrapTranslationEngine(rawT, { keyPrefix: 'home' });\n * // t('title') encodes as 'home.title' — matches the JSON structure\n */\n keyPrefix?: string;\n\n /**\n * Optional fallback translations to use when the target key is missing.\n */\n fallback?: Record<string, any>;\n}\n\n/**\n * Helper that extracts a dot-notated key string from an argument (string or object descriptor).\n */\nfunction extractKey(arg: unknown, keyPrefix: string): string {\n if (typeof arg === 'string') {\n return keyPrefix ? `${keyPrefix}.${arg}` : arg;\n }\n if (arg && typeof arg === 'object') {\n const obj = arg as Record<string, any>;\n if (typeof obj.id === 'string' && obj.id.trim() !== '') {\n return keyPrefix ? `${keyPrefix}.${obj.id}` : obj.id;\n }\n if (typeof obj.key === 'string' && obj.key.trim() !== '') {\n return keyPrefix ? `${keyPrefix}.${obj.key}` : obj.key;\n }\n }\n return keyPrefix;\n}\n\n/**\n * Wraps any translation function or record (e.g. from next-intl, react-i18next,\n * FormatJS, or a plain locale object loaded server-side) with a JavaScript Proxy\n * that automatically encodes translation keys as invisible zero-width watermark\n * prefixes in development. In production the proxy is transparent — the original\n * engine is returned as-is.\n */\nexport function wrapTranslationEngine<T extends object>(engine: T, options: WrapOptions = {}): T {\n if (!isLiveLocaleEnabled()) {\n return engine;\n }\n\n return buildProxy(engine, options.keyPrefix ?? '', options);\n}\n\n/**\n * Dedicated convenience wrapper for `next-intl` (App Router and Pages Router).\n * Automatically wraps `useTranslations(namespace)` or `getTranslations(namespace)`.\n *\n * @example\n * ```tsx\n * const t = wrapNextIntl(useTranslations('auth'), 'auth');\n * return <h1>{t('login.title')}</h1>;\n * ```\n */\nexport function wrapNextIntl<T extends object>(t: T, namespace?: string): T {\n return wrapTranslationEngine(t, { keyPrefix: namespace });\n}\n\nfunction getNestedProperty(obj: any, path: string): any {\n if (!obj || typeof obj !== 'object') return undefined;\n if (path in obj) {\n return obj[path];\n }\n const parts = path.split('.');\n let current = obj;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return undefined;\n }\n current = current[part];\n }\n return current;\n}\n\nfunction buildProxy<T extends object>(target: T, keyPrefix: string, options: WrapOptions): T {\n return new Proxy(target, {\n get(obj, prop: string | symbol) {\n if (typeof prop !== 'string') {\n const val = Reflect.get(obj, prop);\n return typeof val === 'object' && val !== null\n ? buildProxy(val as object, keyPrefix, options)\n : val;\n }\n\n let val = Reflect.get(obj, prop);\n const fullKey = keyPrefix ? `${keyPrefix}.${prop}` : prop;\n\n // If val is missing, try looking it up in fallback\n const fallbackObj =\n options.fallback ??\n (typeof window !== 'undefined' ? (window as any).__livelocale_fallback__ : undefined);\n if (val === undefined && fallbackObj) {\n const fbVal = getNestedProperty(fallbackObj, fullKey);\n if (fbVal !== undefined) {\n val = fbVal;\n }\n }\n\n // Leaf string value – encode it with its full key path\n if (typeof val === 'string') {\n return encodeKey(val, fullKey);\n }\n\n // Function (e.g. t() or t.raw() or t.rich() from next-intl) – wrap the call result\n if (typeof val === 'function') {\n return (...args: unknown[]) => {\n const result = (val as (...args: unknown[]) => unknown).apply(obj, args);\n const calledKey = extractKey(args[0], fullKey);\n\n let resolvedValue = result;\n const isMissing =\n resolvedValue === undefined ||\n resolvedValue === null ||\n resolvedValue === '' ||\n (typeof resolvedValue === 'string' &&\n (resolvedValue === args[0] || resolvedValue === calledKey));\n\n if (isMissing && fallbackObj && calledKey) {\n const fbVal = getNestedProperty(fallbackObj, calledKey);\n if (fbVal !== undefined) {\n resolvedValue = fbVal;\n }\n }\n\n if (typeof resolvedValue === 'string' && calledKey) {\n return encodeKey(resolvedValue, calledKey);\n }\n return resolvedValue;\n };\n }\n\n // Nested object – recurse with updated key path\n if (typeof val === 'object' && val !== null) {\n return buildProxy(val as object, fullKey, options);\n }\n\n return val;\n },\n\n // Allow the proxy to be called directly (e.g. t('key') or t('key', { name: 'value' }))\n apply(target: any, thisArg, args: unknown[]) {\n const result = Reflect.apply(target, thisArg, args);\n const calledKey = extractKey(args[0], keyPrefix);\n\n const fallbackObj =\n options.fallback ??\n (typeof window !== 'undefined' ? (window as any).__livelocale_fallback__ : undefined);\n let resolvedValue = result;\n const isMissing =\n resolvedValue === undefined ||\n resolvedValue === null ||\n resolvedValue === '' ||\n (typeof resolvedValue === 'string' &&\n (resolvedValue === args[0] || resolvedValue === calledKey));\n\n if (isMissing && fallbackObj && calledKey) {\n const fbVal = getNestedProperty(fallbackObj, calledKey);\n if (fbVal !== undefined) {\n resolvedValue = fbVal;\n }\n }\n\n if (typeof resolvedValue === 'string' && calledKey) {\n return encodeKey(resolvedValue, calledKey);\n }\n return resolvedValue;\n },\n });\n}\n"],"mappings":"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","names":["val","target"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts","../../src/client/watermark.ts","../../src/core/env.ts","../../src/client/iframe-bridge.ts","../../src/client/dom-interceptor.ts","../../src/client/wrap.ts"],"sourcesContent":["'use client';\n\nimport { DOMInterceptor } from './dom-interceptor.js';\nimport { IframeBridge, readSameOriginParent, resolveStudioOrigin } from './iframe-bridge.js';\nimport { decodeKey } from './watermark.js';\nimport { isLiveLocaleEnabled } from '../core/env.js';\n\nexport { DOMInterceptor } from './dom-interceptor.js';\nexport {\n IframeBridge,\n type TranslationPayload,\n type ClientMessageType,\n type StudioMessageType,\n type VisibleText,\n} from './iframe-bridge.js';\nexport {\n encodeKey,\n decodeKey,\n stripWatermark,\n sanitizeZeroWidth,\n removeWatermarks,\n hasWatermark,\n unwrap,\n} from './watermark.js';\n\n// wrapTranslationEngine lives in wrap.ts (no 'use client') so it can also be\n// imported by Server Components via 'livelocale/server'.\nexport { wrapTranslationEngine, wrapNextIntl, type WrapOptions } from './wrap.js';\n\n// ─── Live preview support ───────────────────────────────────────────────────\n\n// Text nodes mutated by APPLY_PREVIEW, with their original value so the\n// preview can be reverted when the selection is cleared.\nconst previewNodes = new Map<Text, { key: string; original: string }>();\n\n/**\n * Applies a live preview value to every rendered element matching `key`\n * without touching the locale files. Only the visible text after the\n * watermark prefix is replaced, keeping the watermark (and thus the\n * data-i18n-key scanner) intact.\n */\nfunction applyPreview(key: string, value: string): void {\n const elements = document.querySelectorAll<HTMLElement>('[data-i18n-key]');\n elements.forEach((el) => {\n if (el.getAttribute('data-i18n-key') !== key) return;\n\n const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null);\n const node = walker.nextNode() as Text | null;\n if (!node) return;\n\n const raw = node.nodeValue ?? '';\n const decoded = decodeKey(raw);\n if (!decoded) return;\n\n // Preserve the watermark prefix, swap only the visible text\n const next = raw.slice(0, raw.length - decoded.cleanText.length) + value;\n // Skipping unchanged text avoids a mutation, so re-applying is idempotent.\n if (next === raw) return;\n if (!previewNodes.has(node)) {\n previewNodes.set(node, { key, original: raw });\n }\n node.nodeValue = next;\n });\n}\n\n/**\n * Makes a saved value the text that stays on the page. In development the\n * dev server re-renders from the edited file anyway; in preview mode the\n * deployment still serves the old text until it is rebuilt, so the Studio\n * re-sends saved values whenever the page reloads or changes.\n */\nfunction commitPreview(key: string, value: string): void {\n applyPreview(key, value);\n previewNodes.forEach((entry, node) => {\n if (entry.key === key) previewNodes.delete(node);\n });\n}\n\n/** Restores all previewed text nodes to their pre-preview values. */\nfunction restorePreviews(): void {\n previewNodes.forEach(({ original }, node) => {\n node.nodeValue = original;\n });\n previewNodes.clear();\n}\n\n// ─── Auto-initialization ────────────────────────────────────────────────────\n\n// Auto-initialize the interceptor if running in the browser, in development mode,\n// AND only when the page is embedded inside the studio iframe (not accessed directly).\nif (\n typeof window !== 'undefined' &&\n isLiveLocaleEnabled() &&\n window.parent !== window // <-- iframe guard: only activate inside the studio\n) {\n let storage: Storage | null = null;\n try {\n storage = window.sessionStorage;\n } catch {}\n const allowedOrigin = resolveStudioOrigin(\n window.location.search,\n storage,\n readSameOriginParent(window)\n );\n\n const bridge = new IframeBridge(allowedOrigin);\n const interceptor = new DOMInterceptor(\n (payload) => {\n bridge.sendToParent('ELEMENT_SELECTED', payload);\n },\n (keys) => {\n bridge.sendToParent('VISIBLE_KEYS_CHANGED', keys);\n bridge.sendToParent('VISIBLE_TEXTS_CHANGED', interceptor.getVisibleTexts());\n }\n );\n\n interceptor.init();\n\n // Send a READY message to the parent studio\n bridge.sendToParent('READY', { url: window.location.pathname + window.location.search });\n\n // Listen to studio commands\n bridge.listenToParent((type, payload) => {\n if (type === 'APPLY_PREVIEW') {\n if (payload && typeof payload.key === 'string' && typeof payload.value === 'string') {\n applyPreview(payload.key, payload.value);\n }\n } else if (type === 'COMMIT_PREVIEW') {\n if (payload && typeof payload.key === 'string' && typeof payload.value === 'string') {\n commitPreview(payload.key, payload.value);\n }\n } else if (type === 'CLEAR_SELECTION') {\n // Revert any live preview, then re-trigger/reset interceptor highlight states\n restorePreviews();\n interceptor.destroy();\n interceptor.init();\n }\n });\n\n // Attach reference to window for developer inspection/diagnostics\n (window as any).__LIVELOCALE_CLIENT__ = {\n bridge,\n interceptor,\n };\n}\n","/**\n * Zero-Width Watermarking Encoder/Decoder\n *\n * Maps translation keys into invisible Unicode zero-width characters and\n * prepends them as a binary watermark onto the translation string.\n *\n * Delimiters:\n * \\u200C – Zero-Width Non-Joiner (ZWNJ) → boundary start/end marker\n * \\u200B – Zero-Width Space (ZWS) → binary bit 0\n * \\u200D – Zero-Width Joiner (ZWJ) → binary bit 1\n *\n * The delimiters touch the visible text, so they must be non-joining: a ZWJ\n * there is \"join-causing\" and makes the first Arabic letter take its connected\n * form, as if a hidden letter came before it. Bits never touch a letter.\n *\n * The watermark is strictly prepended so it survives downstream truncation\n * (e.g., text.substring()) that removes only from the right.\n */\n\nconst DELIM = '\\u200C'; // ZWNJ – boundary delimiter (non-joining)\nconst BIT_0 = '\\u200B'; // ZWS – represents bit 0\nconst BIT_1 = '\\u200D'; // ZWJ – represents bit 1\n\n/** First character of every watermark; a cheap pre-check before decoding. */\nexport const WATERMARK_DELIMITER = DELIM;\n\nconst ZW_RE = /[\\u200B\\u200C\\u200D]/g;\n\n/** A complete watermark anywhere in a string: delimiter, whole bytes of bits, delimiter. */\nconst WATERMARK_RE = /\\u200C(?:[\\u200B\\u200D]{8})+\\u200C/g;\n\n/** Encodes a translation key as an invisible watermark prefix on `text`. */\nexport function encodeKey(text: string, key: string): string {\n const bytes = new TextEncoder().encode(key);\n let bits = '';\n for (const byte of bytes) {\n bits += byte.toString(2).padStart(8, '0');\n }\n\n let watermark = DELIM;\n for (const bit of bits) {\n watermark += bit === '0' ? BIT_0 : BIT_1;\n }\n watermark += DELIM;\n\n return watermark + text;\n}\n\n/**\n * Decodes a watermark prefix from `text`.\n *\n * Returns `{ key, cleanText }` where `cleanText` is the original visible\n * text with the watermark stripped. If no valid watermark is found,\n * returns `null`.\n */\nexport function decodeKey(text: string): { key: string; cleanText: string } | null {\n if (!text.startsWith(DELIM)) return null;\n\n const end = text.indexOf(DELIM, 1);\n if (end === -1) return null;\n\n const bitsStr = text.slice(1, end);\n // Validate: every char must be a known ZW bit\n for (const ch of bitsStr) {\n if (ch !== BIT_0 && ch !== BIT_1) return null;\n }\n\n if (bitsStr.length !== 0 && bitsStr.length % 8 !== 0) return null;\n\n const bytes: number[] = [];\n for (let i = 0; i < bitsStr.length; i += 8) {\n let byte = 0;\n for (let j = 0; j < 8; j++) {\n byte = (byte << 1) | (bitsStr[i + j] === BIT_1 ? 1 : 0);\n }\n bytes.push(byte);\n }\n\n try {\n const key = new TextDecoder().decode(new Uint8Array(bytes));\n const cleanText = text.slice(end + 1);\n return { key, cleanText };\n } catch {\n return null;\n }\n}\n\n/**\n * Strips a valid watermark *prefix* from `text`, leaving any legitimate\n * zero-width characters inside the visible text untouched.\n *\n * ZWNJ (\\u200C) and ZWSP (\\u200B) are valid orthography in Persian, Urdu,\n * Thai, and other scripts — a blanket removal would silently corrupt real\n * translations. Only the decoded watermark prefix is removed.\n */\nexport function stripWatermark(text: string): string {\n const decoded = decodeKey(text);\n if (decoded) return decoded.cleanText;\n return text;\n}\n\n/**\n * Aggressively removes every zero-width character from `text`.\n *\n * This also removes ZWNJ and ZWSP that Persian, Urdu and Thai text needs, so\n * prefer `removeWatermarks` for user-visible text and `stripWatermark` for\n * translation content.\n */\nexport function sanitizeZeroWidth(text: string): string {\n return text.replace(ZW_RE, '');\n}\n\n/**\n * Removes every complete watermark from `text`, wherever it occurs, and keeps\n * all other zero-width characters. Persian and Urdu use ZWNJ inside words and\n * Thai uses ZWSP, so stripping every zero-width character would corrupt them.\n */\nexport function removeWatermarks(text: string): string {\n return text.replace(WATERMARK_RE, '');\n}\n\n/** True when `text` contains at least one complete watermark. */\nexport function containsWatermark(text: string): boolean {\n WATERMARK_RE.lastIndex = 0;\n const found = WATERMARK_RE.test(text);\n WATERMARK_RE.lastIndex = 0;\n return found;\n}\n\n/**\n * Returns true if `text` begins with a valid watermark prefix.\n */\nexport function hasWatermark(text: string): boolean {\n return text.startsWith(DELIM);\n}\n\n/**\n * Deeply strips watermark prefixes from a value: strings are cleaned with\n * `stripWatermark`, arrays and plain objects are rebuilt with cleaned members,\n * everything else is returned as-is. The input is never mutated.\n *\n * Use it wherever a translated string must be compared, measured, serialized\n * or handed to code that is not display-only, e.g. `unwrap(t('yes')) === 'Yes'`.\n */\nexport function unwrap<T>(value: T): T {\n if (typeof value === 'string') return stripWatermark(value) as T;\n if (Array.isArray(value)) return value.map((item) => unwrap(item)) as T;\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n out[key] = unwrap((value as Record<string, unknown>)[key]);\n }\n return out as T;\n }\n return value;\n}\n","/**\n * Single source of truth for \"is LiveLocale active right now, and in which mode?\".\n *\n * Modes:\n * - `development`: NODE_ENV is `development` (the default, zero-config mode).\n * - `preview`: explicitly opted in with `LIVELOCALE=preview` (server) or\n * `NEXT_PUBLIC_LIVELOCALE=preview` (client bundles). Works in production builds so a\n * preview deployment can be edited through a hosted Studio. The mutation API then\n * requires a bearer token (see the router).\n * - `off`: everything else.\n *\n * Rules, in order:\n * 1. `LIVELOCALE=off|0|false` (or `NEXT_PUBLIC_LIVELOCALE=off`) always wins → off.\n * 2. `LIVELOCALE=preview` / `NEXT_PUBLIC_LIVELOCALE=preview` → preview.\n * 3. `NODE_ENV` must be `development`, otherwise off.\n * 4. `LIVELOCALE=force` skips rule 5.\n * 5. On the server, a known hosting platform (Vercel preview/production, Netlify,\n * Kubernetes, Render, Fly.io, Railway, Heroku) → off, so a mis-configured staging\n * deploy cannot leak watermarks. A one-time warning is logged.\n *\n * `process.env.NODE_ENV` and `process.env.NEXT_PUBLIC_LIVELOCALE` are referenced as\n * literal member expressions so Next.js and Vite can inline them in browser bundles.\n */\n\nexport type LiveLocaleMode = 'off' | 'development' | 'preview';\n\nconst OFF_VALUES = new Set(['off', '0', 'false']);\n\nlet warnedDeployed = false;\n\n/** Test hook: forget that the deployed-platform warning was already printed. */\nexport function resetEnvWarnings(): void {\n warnedDeployed = false;\n}\n\nfunction readNodeEnv(): string | undefined {\n try {\n return process.env.NODE_ENV;\n } catch {\n return undefined;\n }\n}\n\n/** Set by the Vite plugin (and usable by any host) to mirror the server flag into the page. */\nexport const BROWSER_FLAG_GLOBAL = '__LIVELOCALE_FLAG__';\n\nfunction readPublicFlag(): string | undefined {\n let value: string | undefined;\n try {\n value = process.env.NEXT_PUBLIC_LIVELOCALE;\n } catch {\n value = undefined;\n }\n if (value) return value;\n try {\n const g = globalThis as any;\n const fromGlobal = g.window?.[BROWSER_FLAG_GLOBAL] ?? g[BROWSER_FLAG_GLOBAL];\n return typeof fromGlobal === 'string' ? fromGlobal : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === 'undefined' || !process.env) return undefined;\n const value = process.env[name];\n return value === '' ? undefined : value;\n}\n\n/** Returns a human-readable platform name when running on a known hosting platform. */\nexport function detectDeployedPlatform(): string | null {\n if (typeof window !== 'undefined') return null; // browser: nothing to detect\n const vercelEnv = readEnv('VERCEL_ENV');\n if (vercelEnv === 'production' || vercelEnv === 'preview')\n return `Vercel (VERCEL_ENV=${vercelEnv})`;\n const netlifyContext = readEnv('CONTEXT');\n if (readEnv('NETLIFY') === 'true' && netlifyContext && netlifyContext !== 'dev') {\n return `Netlify (CONTEXT=${netlifyContext})`;\n }\n if (readEnv('KUBERNETES_SERVICE_HOST')) return 'Kubernetes';\n if (readEnv('RENDER') === 'true') return 'Render';\n if (readEnv('FLY_APP_NAME')) return 'Fly.io';\n if (readEnv('RAILWAY_ENVIRONMENT')) return 'Railway';\n if (readEnv('DYNO')) return 'Heroku';\n return null;\n}\n\nfunction evaluate(): { mode: LiveLocaleMode; reason: string | null } {\n const flag = (readEnv('LIVELOCALE') ?? '').toLowerCase();\n const publicFlag = (readPublicFlag() ?? '').toLowerCase();\n\n if (OFF_VALUES.has(flag)) return { mode: 'off', reason: 'LIVELOCALE kill switch is set' };\n if (OFF_VALUES.has(publicFlag)) {\n return { mode: 'off', reason: 'NEXT_PUBLIC_LIVELOCALE kill switch is set' };\n }\n\n if (flag === 'preview' || publicFlag === 'preview') return { mode: 'preview', reason: null };\n\n const nodeEnv = readNodeEnv();\n if (nodeEnv !== 'development') {\n return {\n mode: 'off',\n reason: `NODE_ENV is \"${nodeEnv ?? 'undefined'}\" (must be \"development\", or set LIVELOCALE=preview)`,\n };\n }\n\n if (flag === 'force') return { mode: 'development', reason: null };\n\n const platform = detectDeployedPlatform();\n if (platform) {\n if (!warnedDeployed) {\n warnedDeployed = true;\n console.warn(\n `[livelocale] Disabled: deployed environment detected (${platform}) while NODE_ENV=development. ` +\n `Set LIVELOCALE=force to override, LIVELOCALE=preview for a hosted preview, or LIVELOCALE=off to silence this warning.`\n );\n }\n return { mode: 'off', reason: `deployed environment detected (${platform})` };\n }\n\n return { mode: 'development', reason: null };\n}\n\n/** The active mode: `off`, `development`, or `preview`. */\nexport function getLiveLocaleMode(): LiveLocaleMode {\n return evaluate().mode;\n}\n\n/**\n * Returns `null` when LiveLocale should be active, otherwise a short reason\n * suitable for error messages and logs.\n */\nexport function getDisabledReason(): string | null {\n return evaluate().reason;\n}\n\n/** True when watermarking, the mutation API, and the client scanner should run. */\nexport function isLiveLocaleEnabled(): boolean {\n return evaluate().mode !== 'off';\n}\n","export type ClientMessageType =\n | 'ELEMENT_SELECTED'\n | 'READY'\n | 'ERROR'\n | 'VISIBLE_KEYS_CHANGED'\n | 'VISIBLE_TEXTS_CHANGED';\nexport type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION';\n\n/** Where `livelocale studio` runs unless the Studio says otherwise. */\nexport const DEFAULT_STUDIO_ORIGIN = 'http://localhost:3010';\n\n/** Session key that keeps the Studio origin across in-app navigation within one tab. */\nexport const STUDIO_ORIGIN_STORAGE_KEY = 'livelocale-studio-origin';\n\nfunction toHttpOrigin(value: string | null | undefined): string | null {\n if (!value) return null;\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:' ? url.origin : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The origin of this page when its parent window is a different window with\n * the identical readable origin (a Studio served from the same host), else\n * `null`. Returns `toHttpOrigin(win.location.origin)` only when that is\n * non-null and equals `win.parent.location.origin`; a top-level page, a\n * missing parent, a cross-origin parent (whose `location` throws), a parent\n * on another origin and an opaque `'null'` origin all give `null`. Never throws.\n */\nexport function readSameOriginParent(win: Window): string | null {\n try {\n const parent = win.parent;\n if (!parent || parent === win) return null;\n const own = toHttpOrigin(win.location.origin);\n if (!own) return null;\n return parent.location.origin === own ? own : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The origin of the Studio that embeds this page, in this order:\n *\n * 1. `sameOriginParent`, when it is an http(s) origin. `sendToParent` always\n * posts to `window.parent`, so a readable same-origin parent is the only\n * origin that can receive the messages; it wins over a stale query value or\n * remembered origin. This branch neither reads nor writes storage.\n * 2. `?livelocale-origin=` on the iframe URL, which the Studio passes. The\n * app's own links drop that parameter, so it is remembered for the tab;\n * without it, messages after the first navigation would go to the default\n * origin and be discarded.\n * 3. The origin remembered for the tab.\n * 4. `DEFAULT_STUDIO_ORIGIN`.\n */\nexport function resolveStudioOrigin(\n search: string,\n storage: Storage | null,\n sameOriginParent?: string | null\n): string {\n const fromParent = toHttpOrigin(sameOriginParent);\n if (fromParent) return fromParent;\n const fromQuery = toHttpOrigin(new URLSearchParams(search).get('livelocale-origin'));\n if (fromQuery) {\n try {\n storage?.setItem(STUDIO_ORIGIN_STORAGE_KEY, fromQuery);\n } catch {}\n return fromQuery;\n }\n let remembered: string | null = null;\n try {\n remembered = toHttpOrigin(storage?.getItem(STUDIO_ORIGIN_STORAGE_KEY));\n } catch {}\n return remembered ?? DEFAULT_STUDIO_ORIGIN;\n}\n\n/** A key on the page with the text it shows now; the Studio infers the page locale from these. */\nexport interface VisibleText {\n key: string;\n text: string;\n}\n\n/** Most visible key/text pairs one VISIBLE_TEXTS_CHANGED message carries. */\nexport const MAX_VISIBLE_TEXTS = 100;\n\n/** Longest text, in UTF-16 code units, one visible key/text pair carries. */\nexport const MAX_VISIBLE_TEXT_LENGTH = 200;\n\nconst KEY_PATTERN = /^[a-zA-Z0-9._-]+$/;\n\nexport interface TranslationPayload {\n key: string;\n fallbackValue: string;\n currentValue: string;\n}\n\nexport class IframeBridge {\n private allowedOrigin: string;\n\n constructor(allowedOrigin: string) {\n this.allowedOrigin = allowedOrigin;\n }\n\n /**\n * Sends a message from the iframe client to the parent Studio window.\n */\n sendToParent(type: ClientMessageType, payload?: any): void {\n if (typeof window === 'undefined' || !window.parent || window.parent === window) {\n // Not in an iframe context\n return;\n }\n\n // RULE CLT-006: PAYLOAD SANITIZATION\n if (type === 'ELEMENT_SELECTED') {\n const p = payload as TranslationPayload;\n if (\n !p ||\n typeof p.key !== 'string' ||\n p.key.trim() === '' ||\n !/^[a-zA-Z0-9._-]+$/.test(p.key) ||\n typeof p.currentValue !== 'string'\n ) {\n // Drop invalid payload silently\n return;\n }\n }\n\n if (type === 'VISIBLE_KEYS_CHANGED') {\n // Validate payload is an array of valid key strings\n if (!Array.isArray(payload)) return;\n const validKeys = (payload as unknown[]).filter(\n (k): k is string => typeof k === 'string' && /^[a-zA-Z0-9._-]+$/.test(k)\n );\n // Replace the payload with the sanitized array\n payload = validKeys;\n }\n\n if (type === 'VISIBLE_TEXTS_CHANGED') {\n if (!Array.isArray(payload)) return;\n const texts: VisibleText[] = [];\n for (const entry of payload as unknown[]) {\n if (texts.length >= MAX_VISIBLE_TEXTS) break;\n if (!entry || typeof entry !== 'object') continue;\n const { key, text } = entry as { key?: unknown; text?: unknown };\n if (typeof key !== 'string' || !KEY_PATTERN.test(key) || typeof text !== 'string') {\n continue;\n }\n texts.push({ key, text: text.slice(0, MAX_VISIBLE_TEXT_LENGTH) });\n }\n payload = texts;\n }\n\n const message = {\n source: 'livelocale-client',\n type,\n payload,\n };\n\n window.parent.postMessage(message, this.allowedOrigin);\n }\n\n /**\n * Sets up a listener for messages originating from the parent Studio dashboard.\n */\n listenToParent(callback: (type: StudioMessageType, payload: any) => void): () => void {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const listener = (event: MessageEvent) => {\n // RULE CLT-005: MESSAGE ORIGIN VALIDATION\n if (event.origin !== this.allowedOrigin) {\n return;\n }\n if (event.data?.source !== 'livelocale-studio') {\n return;\n }\n\n const { type, payload } = event.data;\n callback(type, payload);\n };\n\n window.addEventListener('message', listener);\n\n // Return a function to clean up the event listener\n return () => {\n window.removeEventListener('message', listener);\n };\n }\n}\n","import {\n decodeKey,\n stripWatermark,\n removeWatermarks,\n containsWatermark,\n WATERMARK_DELIMITER,\n} from './watermark.js';\nimport { isLiveLocaleEnabled } from '../core/env.js';\nimport { MAX_VISIBLE_TEXTS, MAX_VISIBLE_TEXT_LENGTH, type VisibleText } from './iframe-bridge.js';\n\n// Debounce helper: delays executing fn until after wait ms have elapsed\n// since the last call. Returns a cancel function.\nfunction debounce<T extends (...args: any[]) => void>(fn: T, wait: number): T & { cancel(): void } {\n let timer: ReturnType<typeof setTimeout> | null = null;\n const debounced = (...args: Parameters<T>) => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n fn(...args);\n }, wait);\n };\n debounced.cancel = () => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n };\n return debounced as T & { cancel(): void };\n}\n\nexport class DOMInterceptor {\n private onElementSelected: (payload: {\n key: string;\n fallbackValue: string;\n currentValue: string;\n }) => void;\n private onVisibleKeysChanged?: (keys: string[]) => void;\n\n private hoveredElement: HTMLElement | null = null;\n private observer: MutationObserver | null = null;\n private inputSanitizersAttached: boolean = false;\n\n // ─── Floating Overlay ────────────────────────────────────────────────────\n private overlay: HTMLDivElement | null = null;\n private overlayBadge: HTMLSpanElement | null = null;\n\n // ─── Alt key state ───────────────────────────────────────────────────────\n private isAltHeld: boolean = false;\n\n // Debounced key-broadcast to avoid flooding the parent on rapid DOM mutations\n private broadcastVisibleKeys: (() => void) & { cancel(): void };\n\n constructor(\n onElementSelected: (payload: {\n key: string;\n fallbackValue: string;\n currentValue: string;\n }) => void,\n onVisibleKeysChanged?: (keys: string[]) => void\n ) {\n this.onElementSelected = onElementSelected;\n this.onVisibleKeysChanged = onVisibleKeysChanged;\n\n this.broadcastVisibleKeys = debounce(() => {\n if (this.onVisibleKeysChanged) {\n this.onVisibleKeysChanged(this.getActiveKeys());\n }\n }, 200);\n }\n\n /**\n * Returns the unique set of translation keys currently visible in the DOM.\n */\n getActiveKeys(): string[] {\n const elements = document.querySelectorAll<HTMLElement>('[data-i18n-key]');\n const keys = new Set<string>();\n elements.forEach((el) => {\n const key = el.getAttribute('data-i18n-key');\n if (key) keys.add(key);\n });\n return Array.from(keys);\n }\n\n /**\n * Returns one `{ key, text }` per translation key on the page, in document\n * order, with the text it shows now (watermark removed, trimmed, cut to\n * `MAX_VISIBLE_TEXT_LENGTH`). The first element carrying a key wins; its\n * text is the first descendant text node whose watermark names that key.\n * Elements without such a node, and blank texts, are skipped. At most\n * `limit` entries are returned.\n */\n getVisibleTexts(limit: number = MAX_VISIBLE_TEXTS): VisibleText[] {\n const texts: VisibleText[] = [];\n const seen = new Set<string>();\n const elements = document.querySelectorAll<HTMLElement>('[data-i18n-key]');\n for (const el of elements) {\n if (texts.length >= limit) break;\n const key = el.getAttribute('data-i18n-key');\n if (!key || seen.has(key)) continue;\n seen.add(key);\n const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null);\n let node: Text | null;\n while ((node = walker.nextNode() as Text | null) !== null) {\n const decoded = decodeKey(node.nodeValue ?? '');\n if (decoded?.key !== key) continue;\n const text = decoded.cleanText.trim().slice(0, MAX_VISIBLE_TEXT_LENGTH);\n if (text) texts.push({ key, text });\n break;\n }\n }\n return texts;\n }\n\n /**\n * Initializes the DOM event interceptors in development mode.\n * Also launches the watermark scanner and MutationObserver.\n */\n init(): void {\n // RULE CLT-004: INIT GUARD\n if (!isLiveLocaleEnabled()) {\n return;\n }\n\n if (typeof window === 'undefined') {\n return;\n }\n\n // Inject the floating highlighter overlay into the document body\n this.injectOverlay();\n\n // Attach listeners on window capture phase (RULE CLT-002)\n window.addEventListener('mouseover', this.handleMouseOver, true);\n window.addEventListener('mouseout', this.handleMouseOut, true);\n window.addEventListener('click', this.handleClick, true);\n window.addEventListener('copy', this.handleCopy, true);\n window.addEventListener('paste', this.handlePaste, true);\n window.addEventListener('scroll', this.handleScroll, true);\n window.addEventListener('resize', this.handleResize, false);\n window.addEventListener('keydown', this.handleKeyDown, true);\n window.addEventListener('keyup', this.handleKeyUp, true);\n\n // Phase 3: Scan existing DOM for watermarked text nodes\n this.scanSubtree(document.body);\n\n // Phase 3: Observe future DOM mutations for watermarked nodes\n this.attachMutationObserver();\n\n // Phase 3: Sanitize form inputs/submissions to strip ZW chars\n this.attachInputSanitizers();\n\n // Broadcast the initial set of visible keys to the studio sidebar\n this.broadcastVisibleKeys();\n }\n\n /**\n * Cleans up all attached event listeners, the MutationObserver,\n * the floating overlay, and the form-sanitization listeners.\n */\n destroy(): void {\n if (typeof window === 'undefined') {\n return;\n }\n window.removeEventListener('mouseover', this.handleMouseOver, true);\n window.removeEventListener('mouseout', this.handleMouseOut, true);\n window.removeEventListener('click', this.handleClick, true);\n window.removeEventListener('copy', this.handleCopy, true);\n window.removeEventListener('paste', this.handlePaste, true);\n window.removeEventListener('scroll', this.handleScroll, true);\n window.removeEventListener('resize', this.handleResize, false);\n window.removeEventListener('keydown', this.handleKeyDown, true);\n window.removeEventListener('keyup', this.handleKeyUp, true);\n window.removeEventListener('beforeinput', this.handleBeforeInput, true);\n\n // Detach form-submit sanitizer\n document.removeEventListener('submit', this.handleFormSubmit, true);\n\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n\n this.inputSanitizersAttached = false;\n\n // Remove floating overlay from DOM\n if (this.overlay && this.overlay.parentNode) {\n this.overlay.parentNode.removeChild(this.overlay);\n }\n this.overlay = null;\n this.overlayBadge = null;\n this.hoveredElement = null;\n\n // Cancel any pending debounced broadcasts\n this.broadcastVisibleKeys.cancel();\n }\n\n // ─── Floating Overlay ────────────────────────────────────────────────────\n\n /**\n * Creates and injects a single floating overlay div into the document body.\n * The overlay is positioned using fixed coordinates from getBoundingClientRect()\n * and uses pointer-events: none so it never blocks user interaction.\n */\n private injectOverlay(): void {\n if (this.overlay) return; // Guard against double injection\n\n const overlay = document.createElement('div');\n overlay.id = 'livelocale-highlighter-overlay';\n overlay.style.cssText = [\n 'position: fixed',\n 'top: 0',\n 'left: 0',\n 'width: 0',\n 'height: 0',\n 'pointer-events: none',\n 'z-index: 2147483647', // Maximum z-index\n 'box-sizing: border-box',\n 'opacity: 0',\n 'border-radius: 4px',\n 'transition: opacity 120ms ease, top 80ms ease, left 80ms ease, width 80ms ease, height 80ms ease, border-color 150ms ease',\n ].join('; ');\n\n const badge = document.createElement('span');\n badge.style.cssText = [\n 'position: absolute',\n 'top: -22px',\n 'left: 0',\n 'max-width: 280px',\n 'white-space: nowrap',\n 'overflow: hidden',\n 'text-overflow: ellipsis',\n 'background: rgba(15, 20, 40, 0.92)',\n 'border: 1px solid rgba(99, 102, 241, 0.5)',\n 'color: #a5b4fc',\n 'font-family: ui-monospace, SFMono-Regular, Menlo, monospace',\n 'font-size: 10px',\n 'font-weight: 600',\n 'line-height: 1',\n 'padding: 3px 7px',\n 'border-radius: 4px',\n 'letter-spacing: 0.02em',\n 'backdrop-filter: blur(4px)',\n 'pointer-events: none',\n ].join('; ');\n\n overlay.appendChild(badge);\n document.body.appendChild(overlay);\n\n this.overlay = overlay;\n this.overlayBadge = badge;\n }\n\n /**\n * Positions the floating overlay to surround the given element.\n * Uses getBoundingClientRect() for fixed-positioned geometry.\n */\n private positionOverlay(element: HTMLElement): void {\n if (!this.overlay) return;\n\n const rect = element.getBoundingClientRect();\n const computedStyle = window.getComputedStyle(element);\n const borderRadius = computedStyle.borderRadius || '4px';\n\n this.overlay.style.top = `${rect.top}px`;\n this.overlay.style.left = `${rect.left}px`;\n this.overlay.style.width = `${rect.width}px`;\n this.overlay.style.height = `${rect.height}px`;\n this.overlay.style.borderRadius = borderRadius;\n this.overlay.style.opacity = '1';\n\n this.updateOverlayStyle();\n }\n\n /**\n * Updates overlay border color and badge text based on current Alt key state.\n */\n private updateOverlayStyle(): void {\n if (!this.overlay || !this.overlayBadge) return;\n\n const key = this.hoveredElement?.getAttribute('data-i18n-key') || '';\n\n if (this.isAltHeld) {\n // Alt is held — bright solid border signals \"click to edit\"\n this.overlay.style.border = '2px solid #3b82f6';\n this.overlay.style.boxShadow =\n '0 0 0 1px rgba(59, 130, 246, 0.3), inset 0 0 0 1px rgba(59, 130, 246, 0.1)';\n this.overlayBadge.style.color = '#93c5fd';\n this.overlayBadge.style.borderColor = 'rgba(59, 130, 246, 0.6)';\n this.overlayBadge.textContent = `✎ Click to edit: ${key}`;\n } else {\n // Hover — subtle dashed border signals \"Alt + Click to edit\"\n this.overlay.style.border = '1.5px dashed rgba(99, 102, 241, 0.65)';\n this.overlay.style.boxShadow = '0 0 0 1px rgba(99, 102, 241, 0.15)';\n this.overlayBadge.style.color = '#a5b4fc';\n this.overlayBadge.style.borderColor = 'rgba(99, 102, 241, 0.5)';\n this.overlayBadge.textContent = `⌥ Alt+Click to edit: ${key}`;\n }\n }\n\n /**\n * Hides the floating overlay without removing it from the DOM.\n */\n private hideOverlay(): void {\n if (!this.overlay) return;\n this.overlay.style.opacity = '0';\n }\n\n // ─── Phase 3: Watermark DOM Scanner ──────────────────────────────────────\n\n /**\n * Walks all text nodes inside `root` and decodes any watermark prefix.\n * When found, attaches data-i18n-key / data-i18n-template to the\n * immediate parent HTMLElement without mutating the text node itself\n * (hydration safety: RULE CLT-007).\n */\n private scanSubtree(root: Node): void {\n if (!root) return;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);\n\n let node: Text | null;\n while ((node = walker.nextNode() as Text | null) !== null) {\n this.processTextNode(node);\n }\n }\n\n private processTextNode(node: Text): void {\n const raw = node.nodeValue ?? '';\n if (!raw.includes(WATERMARK_DELIMITER)) return; // fast bail-out\n\n const decoded = decodeKey(raw);\n if (!decoded) return;\n\n const parent = node.parentElement;\n if (!parent) return;\n\n // RULE CLT-007: Do NOT strip the ZW chars from the text node –\n // doing so would break React's reconciliation / hydration tree.\n // Only annotate the parent element.\n parent.setAttribute('data-i18n-key', decoded.key);\n\n // Use the clean (visible) text as the fallback template if not already set\n if (!parent.hasAttribute('data-i18n-template')) {\n parent.setAttribute('data-i18n-template', decoded.cleanText.trim());\n }\n }\n\n /**\n * Attaches a MutationObserver to catch newly rendered / streamed elements.\n */\n private attachMutationObserver(): void {\n if (this.observer) return;\n\n this.observer = new MutationObserver((mutations) => {\n let needsBroadcast = false;\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n for (const added of mutation.addedNodes) {\n if (added.nodeType === Node.ELEMENT_NODE) {\n this.scanSubtree(added);\n needsBroadcast = true;\n } else if (added.nodeType === Node.TEXT_NODE) {\n this.processTextNode(added as Text);\n needsBroadcast = true;\n }\n }\n } else if (mutation.type === 'characterData') {\n if (mutation.target.nodeType === Node.TEXT_NODE) {\n this.processTextNode(mutation.target as Text);\n needsBroadcast = true;\n }\n }\n }\n if (needsBroadcast) {\n this.broadcastVisibleKeys();\n }\n });\n\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n // ─── Phase 3: Input & Form Sanitization ──────────────────────────────────\n\n /**\n * Patches input value descriptors and attaches a form submit listener\n * to strip zero-width watermark characters before any data leaves the\n * browser (preventing backend schema validation rejections or storage\n * of invisible chars).\n */\n private attachInputSanitizers(): void {\n if (this.inputSanitizersAttached) return;\n this.inputSanitizersAttached = true;\n\n // Capture form submissions at the document level\n document.addEventListener('submit', this.handleFormSubmit, true);\n\n // Intercept `beforeinput` for direct keyboard input into inputs/textareas\n window.addEventListener('beforeinput', this.handleBeforeInput, true);\n\n // Patch HTMLInputElement.prototype.value getter/setter\n this.patchInputValueDescriptor(HTMLInputElement.prototype);\n this.patchInputValueDescriptor(HTMLTextAreaElement.prototype);\n }\n\n /**\n * Patches the `value` property descriptor of an input prototype so\n * that reads return sanitized strings even if the user somehow pastes\n * watermark characters.\n */\n private patchInputValueDescriptor(proto: HTMLInputElement | HTMLTextAreaElement): void {\n const original = Object.getOwnPropertyDescriptor(proto, 'value');\n if (!original || !original.get || !original.set) return;\n\n // Prevent double-patching\n if ((proto as any).__liveLocaleSanitized__) return;\n (proto as any).__liveLocaleSanitized__ = true;\n\n Object.defineProperty(proto, 'value', {\n get() {\n const raw: string = original.get!.call(this);\n return removeWatermarks(raw);\n },\n set(val: string) {\n const clean = typeof val === 'string' ? removeWatermarks(val) : val;\n original.set!.call(this, clean);\n },\n configurable: true,\n });\n }\n\n private handleFormSubmit = (event: Event): void => {\n const form = event.target as HTMLFormElement | null;\n if (!form) return;\n\n const inputs = form.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>('input, textarea');\n inputs.forEach((input) => {\n const cleaned = removeWatermarks(input.value);\n if (cleaned !== input.value) {\n // Use the original setter to set the cleaned value directly\n const proto =\n input instanceof HTMLTextAreaElement\n ? HTMLTextAreaElement.prototype\n : HTMLInputElement.prototype;\n const descriptor = Object.getOwnPropertyDescriptor(proto, 'value');\n if (descriptor?.set) {\n descriptor.set.call(input, cleaned);\n }\n }\n });\n };\n\n private handleBeforeInput = (_event: InputEvent): void => {\n // No-op: direct keyboard input doesn't contain ZW characters.\n // This listener is reserved for future clipboard paste interception.\n };\n\n // ─── Alt Key Tracking ─────────────────────────────────────────────────────\n\n private handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Alt') {\n this.isAltHeld = true;\n // Update the overlay style immediately if an element is hovered\n if (this.hoveredElement) {\n this.updateOverlayStyle();\n }\n }\n };\n\n private handleKeyUp = (event: KeyboardEvent): void => {\n if (event.key === 'Alt') {\n this.isAltHeld = false;\n // Revert to the \"hover hint\" style\n if (this.hoveredElement) {\n this.updateOverlayStyle();\n }\n }\n };\n\n // ─── Viewport change handlers (reposition overlay on scroll / resize) ─────\n\n private handleScroll = (): void => {\n if (this.hoveredElement) {\n this.positionOverlay(this.hoveredElement);\n }\n };\n\n private handleResize = (): void => {\n if (this.hoveredElement) {\n this.positionOverlay(this.hoveredElement);\n }\n };\n\n // ─── Mouse / Click Handlers ───────────────────────────────────────────────\n\n private handleMouseOver = (event: MouseEvent): void => {\n const target = event.target as HTMLElement | null;\n if (!target || typeof target.closest !== 'function') return;\n\n const keyedElement = target.closest('[data-i18n-key]') as HTMLElement | null;\n if (!keyedElement) {\n // Moved onto a non-keyed element — hide overlay\n if (this.hoveredElement) {\n this.hideOverlay();\n this.hoveredElement = null;\n }\n return;\n }\n\n // RULE CLT-003: ALWAYS CHECK data-i18n-key EXISTENCE\n const key = keyedElement.getAttribute('data-i18n-key');\n if (key === null) return;\n\n this.hoveredElement = keyedElement;\n this.positionOverlay(keyedElement);\n };\n\n private handleMouseOut = (event: MouseEvent): void => {\n const target = event.target as HTMLElement | null;\n if (!target || typeof target.closest !== 'function') return;\n\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n // If we're moving into a child of the same keyed element, don't hide\n if (relatedTarget && this.hoveredElement && this.hoveredElement.contains(relatedTarget)) {\n return;\n }\n\n const keyedElement = target.closest('[data-i18n-key]') as HTMLElement | null;\n if (!keyedElement || keyedElement !== this.hoveredElement) return;\n\n this.hideOverlay();\n this.hoveredElement = null;\n };\n\n private handleClick = (event: MouseEvent): void => {\n // Feature 1: Only intercept clicks when Alt key is held.\n // Without Alt, let the event propagate naturally so links and buttons work.\n if (!event.altKey) return;\n\n const target = event.target as HTMLElement | null;\n if (!target || typeof target.closest !== 'function') return;\n\n const keyedElement = target.closest('[data-i18n-key]') as HTMLElement | null;\n if (!keyedElement) return;\n\n // RULE CLT-003: ALWAYS CHECK data-i18n-key EXISTENCE\n const key = keyedElement.getAttribute('data-i18n-key');\n if (key === null) return;\n\n // RULE CLT-002: EVENT LISTENERS IN CAPTURE PHASE\n event.preventDefault();\n event.stopPropagation();\n\n // Use innerText, stripping any invisible ZW chars before sending to studio\n const rawText = keyedElement.innerText || '';\n const cleanText = stripWatermark(rawText);\n\n const fallbackValue = keyedElement.getAttribute('data-i18n-template') || cleanText;\n const currentValue = cleanText;\n\n this.onElementSelected({\n key,\n fallbackValue,\n currentValue,\n });\n };\n\n private handleCopy = (event: ClipboardEvent): void => {\n const selection = window.getSelection()?.toString() || '';\n if (containsWatermark(selection)) {\n const clean = removeWatermarks(selection);\n event.clipboardData?.setData('text/plain', clean);\n event.preventDefault();\n }\n };\n\n private handlePaste = (event: ClipboardEvent): void => {\n const text = event.clipboardData?.getData('text/plain') || '';\n if (containsWatermark(text)) {\n event.preventDefault();\n const cleaned = removeWatermarks(text);\n const targetInput = event.target as HTMLInputElement | HTMLTextAreaElement | null;\n if (targetInput && 'value' in targetInput) {\n const proto =\n targetInput instanceof HTMLTextAreaElement\n ? HTMLTextAreaElement.prototype\n : HTMLInputElement.prototype;\n const nativeValueSetter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;\n\n if (nativeValueSetter) {\n const start = targetInput.selectionStart || 0;\n const end = targetInput.selectionEnd || 0;\n const val = targetInput.value;\n const cleanedValue = val.slice(0, start) + cleaned + val.slice(end);\n\n nativeValueSetter.call(targetInput, cleanedValue);\n targetInput.selectionStart = targetInput.selectionEnd = start + cleaned.length;\n\n // Dispatch bubbling input event so React virtual DOM state syncs\n targetInput.dispatchEvent(new Event('input', { bubbles: true }));\n }\n }\n }\n };\n}\n","import { encodeKey } from './watermark.js';\nimport { isLiveLocaleEnabled } from '../core/env.js';\n\nexport interface WrapOptions {\n /**\n * A dot-separated key prefix to prepend to all resolved key paths.\n *\n * Use this when wrapping a namespaced translation function (e.g.\n * react-i18next `useTranslation('home')` or next-intl `useTranslations('home')`)\n * so that the watermarked key matches the actual path in your locale JSON file.\n *\n * @example\n * const { t: rawT } = useTranslation('home');\n * const t = wrapTranslationEngine(rawT, { keyPrefix: 'home' });\n * // t('title') encodes as 'home.title' — matches the JSON structure\n */\n keyPrefix?: string;\n\n /**\n * Optional fallback translations to use when the target key is missing.\n */\n fallback?: Record<string, any>;\n}\n\n/**\n * Helper that extracts a dot-notated key string from an argument (string or object descriptor).\n */\nfunction extractKey(arg: unknown, keyPrefix: string): string {\n if (typeof arg === 'string') {\n return keyPrefix ? `${keyPrefix}.${arg}` : arg;\n }\n if (arg && typeof arg === 'object') {\n const obj = arg as Record<string, any>;\n if (typeof obj.id === 'string' && obj.id.trim() !== '') {\n return keyPrefix ? `${keyPrefix}.${obj.id}` : obj.id;\n }\n if (typeof obj.key === 'string' && obj.key.trim() !== '') {\n return keyPrefix ? `${keyPrefix}.${obj.key}` : obj.key;\n }\n }\n return keyPrefix;\n}\n\n/**\n * Wraps any translation function or record (e.g. from next-intl, react-i18next,\n * FormatJS, or a plain locale object loaded server-side) with a JavaScript Proxy\n * that automatically encodes translation keys as invisible zero-width watermark\n * prefixes in development. In production the proxy is transparent — the original\n * engine is returned as-is.\n */\nexport function wrapTranslationEngine<T extends object>(engine: T, options: WrapOptions = {}): T {\n if (!isLiveLocaleEnabled()) {\n return engine;\n }\n\n return buildProxy(engine, options.keyPrefix ?? '', options);\n}\n\n/**\n * Dedicated convenience wrapper for `next-intl` (App Router and Pages Router).\n * Automatically wraps `useTranslations(namespace)` or `getTranslations(namespace)`.\n *\n * @example\n * ```tsx\n * const t = wrapNextIntl(useTranslations('auth'), 'auth');\n * return <h1>{t('login.title')}</h1>;\n * ```\n */\nexport function wrapNextIntl<T extends object>(t: T, namespace?: string): T {\n return wrapTranslationEngine(t, { keyPrefix: namespace });\n}\n\nfunction getNestedProperty(obj: any, path: string): any {\n if (!obj || typeof obj !== 'object') return undefined;\n if (path in obj) {\n return obj[path];\n }\n const parts = path.split('.');\n let current = obj;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return undefined;\n }\n current = current[part];\n }\n return current;\n}\n\nfunction buildProxy<T extends object>(target: T, keyPrefix: string, options: WrapOptions): T {\n return new Proxy(target, {\n get(obj, prop: string | symbol) {\n if (typeof prop !== 'string') {\n const val = Reflect.get(obj, prop);\n return typeof val === 'object' && val !== null\n ? buildProxy(val as object, keyPrefix, options)\n : val;\n }\n\n let val = Reflect.get(obj, prop);\n const fullKey = keyPrefix ? `${keyPrefix}.${prop}` : prop;\n\n // If val is missing, try looking it up in fallback\n const fallbackObj =\n options.fallback ??\n (typeof window !== 'undefined' ? (window as any).__livelocale_fallback__ : undefined);\n if (val === undefined && fallbackObj) {\n const fbVal = getNestedProperty(fallbackObj, fullKey);\n if (fbVal !== undefined) {\n val = fbVal;\n }\n }\n\n // Leaf string value – encode it with its full key path\n if (typeof val === 'string') {\n return encodeKey(val, fullKey);\n }\n\n // Function (e.g. t() or t.raw() or t.rich() from next-intl) – wrap the call result\n if (typeof val === 'function') {\n return (...args: unknown[]) => {\n const result = (val as (...args: unknown[]) => unknown).apply(obj, args);\n const calledKey = extractKey(args[0], fullKey);\n\n let resolvedValue = result;\n const isMissing =\n resolvedValue === undefined ||\n resolvedValue === null ||\n resolvedValue === '' ||\n (typeof resolvedValue === 'string' &&\n (resolvedValue === args[0] || resolvedValue === calledKey));\n\n if (isMissing && fallbackObj && calledKey) {\n const fbVal = getNestedProperty(fallbackObj, calledKey);\n if (fbVal !== undefined) {\n resolvedValue = fbVal;\n }\n }\n\n if (typeof resolvedValue === 'string' && calledKey) {\n return encodeKey(resolvedValue, calledKey);\n }\n return resolvedValue;\n };\n }\n\n // Nested object – recurse with updated key path\n if (typeof val === 'object' && val !== null) {\n return buildProxy(val as object, fullKey, options);\n }\n\n return val;\n },\n\n // Allow the proxy to be called directly (e.g. t('key') or t('key', { name: 'value' }))\n apply(target: any, thisArg, args: unknown[]) {\n const result = Reflect.apply(target, thisArg, args);\n const calledKey = extractKey(args[0], keyPrefix);\n\n const fallbackObj =\n options.fallback ??\n (typeof window !== 'undefined' ? (window as any).__livelocale_fallback__ : undefined);\n let resolvedValue = result;\n const isMissing =\n resolvedValue === undefined ||\n resolvedValue === null ||\n resolvedValue === '' ||\n (typeof resolvedValue === 'string' &&\n (resolvedValue === args[0] || resolvedValue === calledKey));\n\n if (isMissing && fallbackObj && calledKey) {\n const fbVal = getNestedProperty(fallbackObj, calledKey);\n if (fbVal !== undefined) {\n resolvedValue = fbVal;\n }\n }\n\n if (typeof resolvedValue === 'string' && calledKey) {\n return encodeKey(resolvedValue, calledKey);\n }\n return resolvedValue;\n },\n });\n}\n"],"mappings":"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","names":["val","target"]}
|
package/dist/client/index.d.cts
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
|
-
export { W as WrapOptions, d as decodeKey, e as encodeKey, h as hasWatermark, s as sanitizeZeroWidth, a as stripWatermark, u as unwrap, w as wrapNextIntl, b as wrapTranslationEngine } from '../wrap-
|
|
1
|
+
export { W as WrapOptions, d as decodeKey, e as encodeKey, h as hasWatermark, r as removeWatermarks, s as sanitizeZeroWidth, a as stripWatermark, u as unwrap, w as wrapNextIntl, b as wrapTranslationEngine } from '../wrap-2qVxLN9Q.cjs';
|
|
2
|
+
|
|
3
|
+
type ClientMessageType = 'ELEMENT_SELECTED' | 'READY' | 'ERROR' | 'VISIBLE_KEYS_CHANGED' | 'VISIBLE_TEXTS_CHANGED';
|
|
4
|
+
type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION';
|
|
5
|
+
/** A key on the page with the text it shows now; the Studio infers the page locale from these. */
|
|
6
|
+
interface VisibleText {
|
|
7
|
+
key: string;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
interface TranslationPayload {
|
|
11
|
+
key: string;
|
|
12
|
+
fallbackValue: string;
|
|
13
|
+
currentValue: string;
|
|
14
|
+
}
|
|
15
|
+
declare class IframeBridge {
|
|
16
|
+
private allowedOrigin;
|
|
17
|
+
constructor(allowedOrigin: string);
|
|
18
|
+
/**
|
|
19
|
+
* Sends a message from the iframe client to the parent Studio window.
|
|
20
|
+
*/
|
|
21
|
+
sendToParent(type: ClientMessageType, payload?: any): void;
|
|
22
|
+
/**
|
|
23
|
+
* Sets up a listener for messages originating from the parent Studio dashboard.
|
|
24
|
+
*/
|
|
25
|
+
listenToParent(callback: (type: StudioMessageType, payload: any) => void): () => void;
|
|
26
|
+
}
|
|
2
27
|
|
|
3
28
|
declare class DOMInterceptor {
|
|
4
29
|
private onElementSelected;
|
|
@@ -19,6 +44,15 @@ declare class DOMInterceptor {
|
|
|
19
44
|
* Returns the unique set of translation keys currently visible in the DOM.
|
|
20
45
|
*/
|
|
21
46
|
getActiveKeys(): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Returns one `{ key, text }` per translation key on the page, in document
|
|
49
|
+
* order, with the text it shows now (watermark removed, trimmed, cut to
|
|
50
|
+
* `MAX_VISIBLE_TEXT_LENGTH`). The first element carrying a key wins; its
|
|
51
|
+
* text is the first descendant text node whose watermark names that key.
|
|
52
|
+
* Elements without such a node, and blank texts, are skipped. At most
|
|
53
|
+
* `limit` entries are returned.
|
|
54
|
+
*/
|
|
55
|
+
getVisibleTexts(limit?: number): VisibleText[];
|
|
22
56
|
/**
|
|
23
57
|
* Initializes the DOM event interceptors in development mode.
|
|
24
58
|
* Also launches the watermark scanner and MutationObserver.
|
|
@@ -86,24 +120,4 @@ declare class DOMInterceptor {
|
|
|
86
120
|
private handlePaste;
|
|
87
121
|
}
|
|
88
122
|
|
|
89
|
-
type ClientMessageType
|
|
90
|
-
type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION';
|
|
91
|
-
interface TranslationPayload {
|
|
92
|
-
key: string;
|
|
93
|
-
fallbackValue: string;
|
|
94
|
-
currentValue: string;
|
|
95
|
-
}
|
|
96
|
-
declare class IframeBridge {
|
|
97
|
-
private allowedOrigin;
|
|
98
|
-
constructor(allowedOrigin: string);
|
|
99
|
-
/**
|
|
100
|
-
* Sends a message from the iframe client to the parent Studio window.
|
|
101
|
-
*/
|
|
102
|
-
sendToParent(type: ClientMessageType, payload?: any): void;
|
|
103
|
-
/**
|
|
104
|
-
* Sets up a listener for messages originating from the parent Studio dashboard.
|
|
105
|
-
*/
|
|
106
|
-
listenToParent(callback: (type: StudioMessageType, payload: any) => void): () => void;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export { type ClientMessageType, DOMInterceptor, IframeBridge, type StudioMessageType, type TranslationPayload };
|
|
123
|
+
export { type ClientMessageType, DOMInterceptor, IframeBridge, type StudioMessageType, type TranslationPayload, type VisibleText };
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
|
-
export { W as WrapOptions, d as decodeKey, e as encodeKey, h as hasWatermark, s as sanitizeZeroWidth, a as stripWatermark, u as unwrap, w as wrapNextIntl, b as wrapTranslationEngine } from '../wrap-
|
|
1
|
+
export { W as WrapOptions, d as decodeKey, e as encodeKey, h as hasWatermark, r as removeWatermarks, s as sanitizeZeroWidth, a as stripWatermark, u as unwrap, w as wrapNextIntl, b as wrapTranslationEngine } from '../wrap-2qVxLN9Q.js';
|
|
2
|
+
|
|
3
|
+
type ClientMessageType = 'ELEMENT_SELECTED' | 'READY' | 'ERROR' | 'VISIBLE_KEYS_CHANGED' | 'VISIBLE_TEXTS_CHANGED';
|
|
4
|
+
type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION';
|
|
5
|
+
/** A key on the page with the text it shows now; the Studio infers the page locale from these. */
|
|
6
|
+
interface VisibleText {
|
|
7
|
+
key: string;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
interface TranslationPayload {
|
|
11
|
+
key: string;
|
|
12
|
+
fallbackValue: string;
|
|
13
|
+
currentValue: string;
|
|
14
|
+
}
|
|
15
|
+
declare class IframeBridge {
|
|
16
|
+
private allowedOrigin;
|
|
17
|
+
constructor(allowedOrigin: string);
|
|
18
|
+
/**
|
|
19
|
+
* Sends a message from the iframe client to the parent Studio window.
|
|
20
|
+
*/
|
|
21
|
+
sendToParent(type: ClientMessageType, payload?: any): void;
|
|
22
|
+
/**
|
|
23
|
+
* Sets up a listener for messages originating from the parent Studio dashboard.
|
|
24
|
+
*/
|
|
25
|
+
listenToParent(callback: (type: StudioMessageType, payload: any) => void): () => void;
|
|
26
|
+
}
|
|
2
27
|
|
|
3
28
|
declare class DOMInterceptor {
|
|
4
29
|
private onElementSelected;
|
|
@@ -19,6 +44,15 @@ declare class DOMInterceptor {
|
|
|
19
44
|
* Returns the unique set of translation keys currently visible in the DOM.
|
|
20
45
|
*/
|
|
21
46
|
getActiveKeys(): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Returns one `{ key, text }` per translation key on the page, in document
|
|
49
|
+
* order, with the text it shows now (watermark removed, trimmed, cut to
|
|
50
|
+
* `MAX_VISIBLE_TEXT_LENGTH`). The first element carrying a key wins; its
|
|
51
|
+
* text is the first descendant text node whose watermark names that key.
|
|
52
|
+
* Elements without such a node, and blank texts, are skipped. At most
|
|
53
|
+
* `limit` entries are returned.
|
|
54
|
+
*/
|
|
55
|
+
getVisibleTexts(limit?: number): VisibleText[];
|
|
22
56
|
/**
|
|
23
57
|
* Initializes the DOM event interceptors in development mode.
|
|
24
58
|
* Also launches the watermark scanner and MutationObserver.
|
|
@@ -86,24 +120,4 @@ declare class DOMInterceptor {
|
|
|
86
120
|
private handlePaste;
|
|
87
121
|
}
|
|
88
122
|
|
|
89
|
-
type ClientMessageType
|
|
90
|
-
type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION';
|
|
91
|
-
interface TranslationPayload {
|
|
92
|
-
key: string;
|
|
93
|
-
fallbackValue: string;
|
|
94
|
-
currentValue: string;
|
|
95
|
-
}
|
|
96
|
-
declare class IframeBridge {
|
|
97
|
-
private allowedOrigin;
|
|
98
|
-
constructor(allowedOrigin: string);
|
|
99
|
-
/**
|
|
100
|
-
* Sends a message from the iframe client to the parent Studio window.
|
|
101
|
-
*/
|
|
102
|
-
sendToParent(type: ClientMessageType, payload?: any): void;
|
|
103
|
-
/**
|
|
104
|
-
* Sets up a listener for messages originating from the parent Studio dashboard.
|
|
105
|
-
*/
|
|
106
|
-
listenToParent(callback: (type: StudioMessageType, payload: any) => void): () => void;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export { type ClientMessageType, DOMInterceptor, IframeBridge, type StudioMessageType, type TranslationPayload };
|
|
123
|
+
export { type ClientMessageType, DOMInterceptor, IframeBridge, type StudioMessageType, type TranslationPayload, type VisibleText };
|