livelocale 0.11.0 → 0.12.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 +7 -0
- package/dist/client/index.cjs +125 -1
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +125 -1
- package/dist/client/index.js.map +1 -1
- package/dist/codemod/index.cjs +41 -7
- package/dist/codemod/index.cjs.map +1 -1
- package/dist/codemod/index.js +41 -7
- package/dist/codemod/index.js.map +1 -1
- package/dist/react/index.cjs +133 -2
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +133 -2
- package/dist/react/index.js.map +1 -1
- package/dist/studio/assets/index-C6OyWSoQ.js +14 -0
- package/dist/studio/assets/index-ClWjP6QY.css +1 -0
- package/dist/studio/index.html +2 -2
- package/dist/vite/index.cjs +41 -7
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +41 -7
- package/dist/vite/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/studio/assets/index-BNCZW1xW.css +0 -1
- package/dist/studio/assets/index-DriSmNkY.js +0 -14
package/README.md
CHANGED
|
@@ -171,6 +171,10 @@ Open `http://localhost:3010` to view the Studio, loaded with your app. `--app` d
|
|
|
171
171
|
`http://localhost:3000`; you can also switch apps without restarting by opening
|
|
172
172
|
`http://localhost:3010/?app=http://localhost:3001`.
|
|
173
173
|
|
|
174
|
+
The preview bar's address field shows the page you are on. Type a path and press Enter to open it
|
|
175
|
+
in the current language. Suggestions list your recent pages and, on a share link, every page of the
|
|
176
|
+
site. Reload reloads the page you are on.
|
|
177
|
+
|
|
174
178
|
The Studio has the new LiveLocale look, in light and dark. It speaks English and Arabic; the share
|
|
175
179
|
link follows the browser language, and the switch is in the header. Share links also follow your
|
|
176
180
|
system's light or dark setting; the dev Studio starts dark until you pick System, Light or Dark.
|
|
@@ -409,6 +413,9 @@ The share link is `<your site><base>livelocale/index.html`. Nothing is written o
|
|
|
409
413
|
`<outDir>/livelocale/` except that one script tag, and an existing `livelocale/` folder is replaced
|
|
410
414
|
only when pack wrote it.
|
|
411
415
|
|
|
416
|
+
`manifest.json` now lists the site's HTML pages (`pages`, at most 2,000) for the Studio's
|
|
417
|
+
suggestions.
|
|
418
|
+
|
|
412
419
|
### `build`
|
|
413
420
|
|
|
414
421
|
Runs your build command and packs its output in one step. The environment variables work the same
|
package/dist/client/index.cjs
CHANGED
|
@@ -237,6 +237,42 @@ function resolveStudioOrigin(search, storage, sameOriginParent) {
|
|
|
237
237
|
var MAX_VISIBLE_TEXTS = 100;
|
|
238
238
|
var MAX_VISIBLE_TEXT_LENGTH = 200;
|
|
239
239
|
var KEY_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
240
|
+
var MAX_LOCATION_LENGTH = 2048;
|
|
241
|
+
var LOCATION_CONTROL_RE = /[\u0000-\u001f\u007f-\u009f]/;
|
|
242
|
+
var LOCATION_PROBE_ORIGIN = "http://livelocale.invalid";
|
|
243
|
+
function toLocationUrl(payload) {
|
|
244
|
+
try {
|
|
245
|
+
if (!payload || typeof payload !== "object") return null;
|
|
246
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "url")) return null;
|
|
247
|
+
const url = payload.url;
|
|
248
|
+
if (typeof url !== "string" || url.length > MAX_LOCATION_LENGTH) return null;
|
|
249
|
+
if (!url.startsWith("/") || url.startsWith("//")) return null;
|
|
250
|
+
if (LOCATION_CONTROL_RE.test(url)) return null;
|
|
251
|
+
if (/^[^?#]*\\/.test(url)) return null;
|
|
252
|
+
if (new URL(url, LOCATION_PROBE_ORIGIN).origin !== LOCATION_PROBE_ORIGIN) return null;
|
|
253
|
+
return url;
|
|
254
|
+
} catch {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function navigationHref(payload, ownOrigin) {
|
|
259
|
+
try {
|
|
260
|
+
if (!payload || typeof payload !== "object") return null;
|
|
261
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "url")) return null;
|
|
262
|
+
const url = payload.url;
|
|
263
|
+
if (typeof url !== "string" || url.length > MAX_LOCATION_LENGTH) return null;
|
|
264
|
+
const parsed = new URL(url);
|
|
265
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
266
|
+
if (parsed.origin !== ownOrigin || parsed.username || parsed.password) return null;
|
|
267
|
+
return parsed.href;
|
|
268
|
+
} catch {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function followNavigation(loc, href) {
|
|
273
|
+
if (href === loc.href) loc.reload();
|
|
274
|
+
else loc.assign(href);
|
|
275
|
+
}
|
|
240
276
|
var IframeBridge = class {
|
|
241
277
|
allowedOrigin;
|
|
242
278
|
constructor(allowedOrigin) {
|
|
@@ -276,6 +312,10 @@ var IframeBridge = class {
|
|
|
276
312
|
}
|
|
277
313
|
payload = texts;
|
|
278
314
|
}
|
|
315
|
+
if (type === "READY" || type === "LOCATION_CHANGED") {
|
|
316
|
+
const url = toLocationUrl(payload);
|
|
317
|
+
payload = url === null ? {} : { url };
|
|
318
|
+
}
|
|
279
319
|
const message = {
|
|
280
320
|
source: "livelocale-client",
|
|
281
321
|
type,
|
|
@@ -768,6 +808,81 @@ var DOMInterceptor = class {
|
|
|
768
808
|
};
|
|
769
809
|
};
|
|
770
810
|
|
|
811
|
+
// src/client/location-watch.ts
|
|
812
|
+
var WATCH_STATE = /* @__PURE__ */ Symbol.for("livelocale.locationWatch");
|
|
813
|
+
function subscribe(history, method, notify) {
|
|
814
|
+
const current = history[method];
|
|
815
|
+
let state = current[WATCH_STATE];
|
|
816
|
+
if (!state) {
|
|
817
|
+
const original = current;
|
|
818
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
819
|
+
const wrapper = function(...args) {
|
|
820
|
+
const result = original.apply(this, args);
|
|
821
|
+
subscribers.forEach((cb) => cb());
|
|
822
|
+
return result;
|
|
823
|
+
};
|
|
824
|
+
state = { subscribers, original, wrapper };
|
|
825
|
+
wrapper[WATCH_STATE] = state;
|
|
826
|
+
history[method] = wrapper;
|
|
827
|
+
}
|
|
828
|
+
const own = state;
|
|
829
|
+
own.subscribers.add(notify);
|
|
830
|
+
return () => {
|
|
831
|
+
own.subscribers.delete(notify);
|
|
832
|
+
if (own.subscribers.size === 0 && history[method] === own.wrapper) {
|
|
833
|
+
history[method] = own.original;
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
function defaultSchedule(win) {
|
|
838
|
+
if (typeof win.requestAnimationFrame === "function") {
|
|
839
|
+
return (cb) => {
|
|
840
|
+
win.requestAnimationFrame(() => cb());
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
return (cb) => {
|
|
844
|
+
setTimeout(cb, 16);
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
function watchLocation(win, report, schedule = defaultSchedule(win)) {
|
|
848
|
+
const read = () => win.location.pathname + win.location.search + win.location.hash;
|
|
849
|
+
let last = read();
|
|
850
|
+
let pending = false;
|
|
851
|
+
let stopped = false;
|
|
852
|
+
const run = () => {
|
|
853
|
+
pending = false;
|
|
854
|
+
if (stopped) return;
|
|
855
|
+
const url = read();
|
|
856
|
+
if (url === last) return;
|
|
857
|
+
last = url;
|
|
858
|
+
try {
|
|
859
|
+
report(url);
|
|
860
|
+
} catch {
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
const notify = () => {
|
|
864
|
+
if (pending || stopped) return;
|
|
865
|
+
pending = true;
|
|
866
|
+
try {
|
|
867
|
+
schedule(run);
|
|
868
|
+
} catch {
|
|
869
|
+
pending = false;
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
const unsubscribePush = subscribe(win.history, "pushState", notify);
|
|
873
|
+
const unsubscribeReplace = subscribe(win.history, "replaceState", notify);
|
|
874
|
+
win.addEventListener("popstate", notify);
|
|
875
|
+
win.addEventListener("hashchange", notify);
|
|
876
|
+
return () => {
|
|
877
|
+
if (stopped) return;
|
|
878
|
+
stopped = true;
|
|
879
|
+
win.removeEventListener("popstate", notify);
|
|
880
|
+
win.removeEventListener("hashchange", notify);
|
|
881
|
+
unsubscribePush();
|
|
882
|
+
unsubscribeReplace();
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
|
|
771
886
|
// src/client/wrap.ts
|
|
772
887
|
function extractKey(arg, keyPrefix) {
|
|
773
888
|
if (typeof arg === "string") {
|
|
@@ -925,7 +1040,13 @@ if (typeof window !== "undefined" && isLiveLocaleEnabled() && window.parent !==
|
|
|
925
1040
|
}
|
|
926
1041
|
);
|
|
927
1042
|
interceptor.init();
|
|
928
|
-
|
|
1043
|
+
const locationUrl = () => window.location.pathname + window.location.search + window.location.hash;
|
|
1044
|
+
bridge.sendToParent("READY", { url: locationUrl() });
|
|
1045
|
+
watchLocation(window, (url) => bridge.sendToParent("LOCATION_CHANGED", { url }));
|
|
1046
|
+
window.addEventListener("pagehide", () => bridge.sendToParent("LEAVING"));
|
|
1047
|
+
window.addEventListener("pageshow", (event) => {
|
|
1048
|
+
if (event.persisted) bridge.sendToParent("READY", { url: locationUrl() });
|
|
1049
|
+
});
|
|
929
1050
|
bridge.listenToParent((type, payload) => {
|
|
930
1051
|
if (type === "APPLY_PREVIEW") {
|
|
931
1052
|
if (payload && typeof payload.key === "string" && typeof payload.value === "string") {
|
|
@@ -939,6 +1060,9 @@ if (typeof window !== "undefined" && isLiveLocaleEnabled() && window.parent !==
|
|
|
939
1060
|
restorePreviews();
|
|
940
1061
|
interceptor.destroy();
|
|
941
1062
|
interceptor.init();
|
|
1063
|
+
} else if (type === "NAVIGATE") {
|
|
1064
|
+
const href = navigationHref(payload, window.location.origin);
|
|
1065
|
+
if (href !== null) followNavigation(window.location, href);
|
|
942
1066
|
}
|
|
943
1067
|
});
|
|
944
1068
|
window.__LIVELOCALE_CLIENT__ = {
|
|
@@ -1 +1 @@
|
|
|
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: #0C0D0F', // Night\n 'border: 1px solid #4FC3DC', // Hunk\n 'color: #E8E4D9', // Paper\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 '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 — solid Hunk border signals \"click to edit\"\n this.overlay.style.border = '2px solid #4FC3DC';\n this.overlay.style.boxShadow = 'none';\n this.overlayBadge.style.color = '#E8E4D9';\n this.overlayBadge.style.borderColor = '#4FC3DC';\n this.overlayBadge.textContent = `✎ Click to edit: ${key}`;\n } else {\n // Hover — dashed Hunk border signals \"Alt + Click to edit\"\n this.overlay.style.border = '1.5px dashed #4FC3DC';\n this.overlay.style.boxShadow = 'none';\n this.overlayBadge.style.color = '#E8E4D9';\n this.overlayBadge.style.borderColor = '#4FC3DC';\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"]}
|
|
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/location-watch.ts","../../src/client/wrap.ts"],"sourcesContent":["'use client';\n\nimport { DOMInterceptor } from './dom-interceptor.js';\nimport {\n IframeBridge,\n followNavigation,\n navigationHref,\n readSameOriginParent,\n resolveStudioOrigin,\n} from './iframe-bridge.js';\nimport { decodeKey } from './watermark.js';\nimport { watchLocation } from './location-watch.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 const locationUrl = () =>\n window.location.pathname + window.location.search + window.location.hash;\n\n // Send a READY message to the parent studio\n bridge.sendToParent('READY', { url: locationUrl() });\n\n // Report client-side navigations, say when this document goes away (the\n // next one may have no client to answer), and greet again when the\n // back-forward cache restores it.\n watchLocation(window, (url) => bridge.sendToParent('LOCATION_CHANGED', { url }));\n window.addEventListener('pagehide', () => bridge.sendToParent('LEAVING'));\n window.addEventListener('pageshow', (event) => {\n if (event.persisted) bridge.sendToParent('READY', { url: locationUrl() });\n });\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 } else if (type === 'NAVIGATE') {\n const href = navigationHref(payload, window.location.origin);\n if (href !== null) followNavigation(window.location, href);\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'\n | 'LOCATION_CHANGED'\n | 'LEAVING';\nexport type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION' | 'NAVIGATE';\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\n/** Longest page location, in UTF-16 code units, READY and LOCATION_CHANGED carry. */\nexport const MAX_LOCATION_LENGTH = 2048;\n\n/** C0 and C1 control characters and DEL, which a URL parser strips or reads specially. */\nconst LOCATION_CONTROL_RE = /[\\u0000-\\u001f\\u007f-\\u009f]/;\n\n/** An origin a reported path is resolved against, to check that it stays on it. */\nconst LOCATION_PROBE_ORIGIN = 'http://livelocale.invalid';\n\n/**\n * The `url` own string of an object payload when it is a same-origin path:\n * starts with `/`, not with `//`; holds no C0/C1 control character or DEL\n * anywhere and no backslash before its query or hash (a URL parser drops tabs\n * and newlines and reads `\\` as `/`, so `/\\t/host` or `/\\host` would name a\n * host); still resolves on the origin it is read against; and is at most\n * MAX_LOCATION_LENGTH long. Else `null`. Never throws.\n */\nexport function toLocationUrl(payload: unknown): string | null {\n try {\n if (!payload || typeof payload !== 'object') return null;\n if (!Object.prototype.hasOwnProperty.call(payload, 'url')) return null;\n const url = (payload as { url?: unknown }).url;\n if (typeof url !== 'string' || url.length > MAX_LOCATION_LENGTH) return null;\n if (!url.startsWith('/') || url.startsWith('//')) return null;\n if (LOCATION_CONTROL_RE.test(url)) return null;\n if (/^[^?#]*\\\\/.test(url)) return null;\n if (new URL(url, LOCATION_PROBE_ORIGIN).origin !== LOCATION_PROBE_ORIGIN) return null;\n return url;\n } catch {\n return null;\n }\n}\n\n/**\n * The href a NAVIGATE payload asks for when it is safe to follow: the payload\n * is an object whose own `url` is a string of at most MAX_LOCATION_LENGTH that\n * parses on its own (relative and protocol-relative input is refused) as an\n * http(s) URL on `ownOrigin` without credentials; else `null`. Never throws.\n */\nexport function navigationHref(payload: unknown, ownOrigin: string): string | null {\n try {\n if (!payload || typeof payload !== 'object') return null;\n if (!Object.prototype.hasOwnProperty.call(payload, 'url')) return null;\n const url = (payload as { url?: unknown }).url;\n if (typeof url !== 'string' || url.length > MAX_LOCATION_LENGTH) return null;\n const parsed = new URL(url);\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return null;\n if (parsed.origin !== ownOrigin || parsed.username || parsed.password) return null;\n return parsed.href;\n } catch {\n return null;\n }\n}\n\n/**\n * Opens `href`, reloading when it is the current URL: assigning the current\n * URL with a hash is a fragment navigation that reloads nothing.\n */\nexport function followNavigation(\n loc: Pick<Location, 'href' | 'assign' | 'reload'>,\n href: string\n): void {\n if (href === loc.href) loc.reload();\n else loc.assign(href);\n}\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 if (type === 'READY' || type === 'LOCATION_CHANGED') {\n // Never dropped: the Studio must connect, and learn when the location is unknown.\n const url = toLocationUrl(payload);\n payload = url === null ? {} : { url };\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: #0C0D0F', // Night\n 'border: 1px solid #4FC3DC', // Hunk\n 'color: #E8E4D9', // Paper\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 '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 — solid Hunk border signals \"click to edit\"\n this.overlay.style.border = '2px solid #4FC3DC';\n this.overlay.style.boxShadow = 'none';\n this.overlayBadge.style.color = '#E8E4D9';\n this.overlayBadge.style.borderColor = '#4FC3DC';\n this.overlayBadge.textContent = `✎ Click to edit: ${key}`;\n } else {\n // Hover — dashed Hunk border signals \"Alt + Click to edit\"\n this.overlay.style.border = '1.5px dashed #4FC3DC';\n this.overlay.style.boxShadow = 'none';\n this.overlayBadge.style.color = '#E8E4D9';\n this.overlayBadge.style.borderColor = '#4FC3DC';\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","/**\n * Watches the page's address for client-side navigations (`history.pushState`,\n * `history.replaceState`, `popstate`, `hashchange`) and reports each new\n * `pathname + search + hash`, at most once per scheduled frame.\n */\n\n/** Where the wrapper keeps its subscribers, shared by every copy of this module. */\nconst WATCH_STATE = Symbol.for('livelocale.locationWatch');\n\ntype HistoryMethod = 'pushState' | 'replaceState';\ntype HistoryFn = (this: History, ...args: unknown[]) => unknown;\n\ninterface WrapperState {\n subscribers: Set<() => void>;\n original: HistoryFn;\n wrapper: HistoryFn;\n}\n\ntype Wrapped = HistoryFn & { [WATCH_STATE]?: WrapperState };\n\n/** The wrapper on `history[method]`, installing it when the current function is not one. */\nfunction subscribe(history: History, method: HistoryMethod, notify: () => void): () => void {\n const current = history[method] as unknown as Wrapped;\n let state = current[WATCH_STATE];\n if (!state) {\n const original = current;\n const subscribers = new Set<() => void>();\n const wrapper: Wrapped = function (this: History, ...args: unknown[]) {\n const result = original.apply(this, args);\n subscribers.forEach((cb) => cb());\n return result;\n };\n state = { subscribers, original, wrapper };\n wrapper[WATCH_STATE] = state;\n history[method] = wrapper as unknown as History[HistoryMethod];\n }\n const own = state;\n own.subscribers.add(notify);\n return () => {\n own.subscribers.delete(notify);\n // Put the original back only if nobody wrapped on top of ours since.\n if (own.subscribers.size === 0 && (history[method] as unknown) === own.wrapper) {\n history[method] = own.original as unknown as History[HistoryMethod];\n }\n };\n}\n\nfunction defaultSchedule(win: Window): (cb: () => void) => void {\n if (typeof win.requestAnimationFrame === 'function') {\n return (cb) => {\n win.requestAnimationFrame(() => cb());\n };\n }\n return (cb) => {\n setTimeout(cb, 16);\n };\n}\n\n/**\n * Calls `report` with the page's new `pathname + search + hash` after each\n * client-side navigation, coalescing a burst into one report per `schedule`\n * callback and skipping a URL equal to the last one reported (initially the\n * URL at start). `report` is never called synchronously and a throwing\n * `report` is swallowed, so the app's navigation never breaks. Returns the\n * cleanup.\n */\nexport function watchLocation(\n win: Window,\n report: (url: string) => void,\n schedule: (cb: () => void) => void = defaultSchedule(win)\n): () => void {\n const read = () => win.location.pathname + win.location.search + win.location.hash;\n let last = read();\n let pending = false;\n let stopped = false;\n\n const run = () => {\n pending = false;\n if (stopped) return;\n const url = read();\n if (url === last) return;\n last = url;\n try {\n report(url);\n } catch {}\n };\n\n const notify = () => {\n if (pending || stopped) return;\n pending = true;\n try {\n schedule(run);\n } catch {\n pending = false;\n }\n };\n\n const unsubscribePush = subscribe(win.history, 'pushState', notify);\n const unsubscribeReplace = subscribe(win.history, 'replaceState', notify);\n win.addEventListener('popstate', notify);\n win.addEventListener('hashchange', notify);\n\n return () => {\n if (stopped) return;\n stopped = true;\n win.removeEventListener('popstate', notify);\n win.removeEventListener('hashchange', notify);\n unsubscribePush();\n unsubscribeReplace();\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,7 +1,7 @@
|
|
|
1
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
2
|
|
|
3
|
-
type ClientMessageType = 'ELEMENT_SELECTED' | 'READY' | 'ERROR' | 'VISIBLE_KEYS_CHANGED' | 'VISIBLE_TEXTS_CHANGED';
|
|
4
|
-
type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION';
|
|
3
|
+
type ClientMessageType = 'ELEMENT_SELECTED' | 'READY' | 'ERROR' | 'VISIBLE_KEYS_CHANGED' | 'VISIBLE_TEXTS_CHANGED' | 'LOCATION_CHANGED' | 'LEAVING';
|
|
4
|
+
type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION' | 'NAVIGATE';
|
|
5
5
|
/** A key on the page with the text it shows now; the Studio infers the page locale from these. */
|
|
6
6
|
interface VisibleText {
|
|
7
7
|
key: string;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
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
2
|
|
|
3
|
-
type ClientMessageType = 'ELEMENT_SELECTED' | 'READY' | 'ERROR' | 'VISIBLE_KEYS_CHANGED' | 'VISIBLE_TEXTS_CHANGED';
|
|
4
|
-
type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION';
|
|
3
|
+
type ClientMessageType = 'ELEMENT_SELECTED' | 'READY' | 'ERROR' | 'VISIBLE_KEYS_CHANGED' | 'VISIBLE_TEXTS_CHANGED' | 'LOCATION_CHANGED' | 'LEAVING';
|
|
4
|
+
type StudioMessageType = 'APPLY_PREVIEW' | 'COMMIT_PREVIEW' | 'CLEAR_SELECTION' | 'NAVIGATE';
|
|
5
5
|
/** A key on the page with the text it shows now; the Studio infers the page locale from these. */
|
|
6
6
|
interface VisibleText {
|
|
7
7
|
key: string;
|