@translation-cms/sync 1.2.28 → 1.2.29

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.
Files changed (197) hide show
  1. package/dist/api.d.ts +105 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +208 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/bin.d.ts +25 -0
  6. package/dist/bin.d.ts.map +1 -0
  7. package/dist/bin.js +208 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/commands/init.d.ts +10 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +107 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/pull.d.ts +10 -0
  14. package/dist/commands/pull.d.ts.map +1 -0
  15. package/dist/commands/pull.js +35 -0
  16. package/dist/commands/pull.js.map +1 -0
  17. package/dist/commands/status.d.ts +15 -0
  18. package/dist/commands/status.d.ts.map +1 -0
  19. package/dist/commands/status.js +62 -0
  20. package/dist/commands/status.js.map +1 -0
  21. package/dist/commands/sync.d.ts +24 -0
  22. package/dist/commands/sync.d.ts.map +1 -0
  23. package/dist/commands/sync.js +131 -0
  24. package/dist/commands/sync.js.map +1 -0
  25. package/dist/commands/watch.d.ts +18 -0
  26. package/dist/commands/watch.d.ts.map +1 -0
  27. package/dist/commands/watch.js +71 -0
  28. package/dist/commands/watch.js.map +1 -0
  29. package/dist/config/config-internals/args.d.ts +11 -0
  30. package/dist/config/config-internals/args.d.ts.map +1 -0
  31. package/dist/config/config-internals/args.js +22 -0
  32. package/dist/config/config-internals/args.js.map +1 -0
  33. package/dist/config/config-internals/env.d.ts +10 -0
  34. package/dist/config/config-internals/env.d.ts.map +1 -0
  35. package/dist/config/config-internals/env.js +35 -0
  36. package/dist/config/config-internals/env.js.map +1 -0
  37. package/dist/config/config-internals/file.d.ts +11 -0
  38. package/dist/config/config-internals/file.d.ts.map +1 -0
  39. package/dist/config/config-internals/file.js +28 -0
  40. package/dist/config/config-internals/file.js.map +1 -0
  41. package/dist/config/config-internals/resolve.d.ts +21 -0
  42. package/dist/config/config-internals/resolve.d.ts.map +1 -0
  43. package/dist/config/config-internals/resolve.js +73 -0
  44. package/dist/config/config-internals/resolve.js.map +1 -0
  45. package/dist/config/config-internals/root.d.ts +9 -0
  46. package/dist/config/config-internals/root.d.ts.map +1 -0
  47. package/dist/config/config-internals/root.js +22 -0
  48. package/dist/config/config-internals/root.js.map +1 -0
  49. package/dist/config/config-internals/types.d.ts +91 -0
  50. package/dist/config/config-internals/types.d.ts.map +1 -0
  51. package/dist/config/config-internals/types.js +9 -0
  52. package/dist/config/config-internals/types.js.map +1 -0
  53. package/dist/config/resolve-config.d.ts +11 -0
  54. package/dist/config/resolve-config.d.ts.map +1 -0
  55. package/dist/config/resolve-config.js +10 -0
  56. package/dist/config/resolve-config.js.map +1 -0
  57. package/dist/core/api-internals/helpers.d.ts +9 -0
  58. package/dist/core/api-internals/helpers.d.ts.map +1 -0
  59. package/dist/core/api-internals/helpers.js +14 -0
  60. package/dist/core/api-internals/helpers.js.map +1 -0
  61. package/dist/core/api-internals/pull.d.ts +19 -0
  62. package/dist/core/api-internals/pull.d.ts.map +1 -0
  63. package/dist/core/api-internals/pull.js +269 -0
  64. package/dist/core/api-internals/pull.js.map +1 -0
  65. package/dist/core/api-internals/route-config.d.ts +13 -0
  66. package/dist/core/api-internals/route-config.d.ts.map +1 -0
  67. package/dist/core/api-internals/route-config.js +34 -0
  68. package/dist/core/api-internals/route-config.js.map +1 -0
  69. package/dist/core/api-internals/sync.d.ts +19 -0
  70. package/dist/core/api-internals/sync.d.ts.map +1 -0
  71. package/dist/core/api-internals/sync.js +139 -0
  72. package/dist/core/api-internals/sync.js.map +1 -0
  73. package/dist/core/api-internals/types.d.ts +33 -0
  74. package/dist/core/api-internals/types.d.ts.map +1 -0
  75. package/dist/core/api-internals/types.js +5 -0
  76. package/dist/core/api-internals/types.js.map +1 -0
  77. package/dist/core/api.d.ts +11 -0
  78. package/dist/core/api.d.ts.map +1 -0
  79. package/dist/core/api.js +11 -0
  80. package/dist/core/api.js.map +1 -0
  81. package/dist/core/cache-internals/format.d.ts +20 -0
  82. package/dist/core/cache-internals/format.d.ts.map +1 -0
  83. package/dist/core/cache-internals/format.js +33 -0
  84. package/dist/core/cache-internals/format.js.map +1 -0
  85. package/dist/core/cache-internals/params.d.ts +33 -0
  86. package/dist/core/cache-internals/params.d.ts.map +1 -0
  87. package/dist/core/cache-internals/params.js +155 -0
  88. package/dist/core/cache-internals/params.js.map +1 -0
  89. package/dist/core/cache-internals/pull.d.ts +17 -0
  90. package/dist/core/cache-internals/pull.d.ts.map +1 -0
  91. package/dist/core/cache-internals/pull.js +34 -0
  92. package/dist/core/cache-internals/pull.js.map +1 -0
  93. package/dist/core/cache-internals/sync.d.ts +29 -0
  94. package/dist/core/cache-internals/sync.d.ts.map +1 -0
  95. package/dist/core/cache-internals/sync.js +104 -0
  96. package/dist/core/cache-internals/sync.js.map +1 -0
  97. package/dist/core/cache-internals/types.d.ts +48 -0
  98. package/dist/core/cache-internals/types.d.ts.map +1 -0
  99. package/dist/core/cache-internals/types.js +2 -0
  100. package/dist/core/cache-internals/types.js.map +1 -0
  101. package/dist/core/cache.d.ts +13 -0
  102. package/dist/core/cache.d.ts.map +1 -0
  103. package/dist/core/cache.js +33 -0
  104. package/dist/core/cache.js.map +1 -0
  105. package/dist/core/scanner-internals/ast.d.ts +40 -0
  106. package/dist/core/scanner-internals/ast.d.ts.map +1 -0
  107. package/dist/core/scanner-internals/ast.js +100 -0
  108. package/dist/core/scanner-internals/ast.js.map +1 -0
  109. package/dist/core/scanner-internals/file-walker.d.ts +10 -0
  110. package/dist/core/scanner-internals/file-walker.d.ts.map +1 -0
  111. package/dist/core/scanner-internals/file-walker.js +23 -0
  112. package/dist/core/scanner-internals/file-walker.js.map +1 -0
  113. package/dist/core/scanner-internals/import-resolver.d.ts +21 -0
  114. package/dist/core/scanner-internals/import-resolver.d.ts.map +1 -0
  115. package/dist/core/scanner-internals/import-resolver.js +119 -0
  116. package/dist/core/scanner-internals/import-resolver.js.map +1 -0
  117. package/dist/core/scanner-internals/key-extractor.d.ts +20 -0
  118. package/dist/core/scanner-internals/key-extractor.d.ts.map +1 -0
  119. package/dist/core/scanner-internals/key-extractor.js +274 -0
  120. package/dist/core/scanner-internals/key-extractor.js.map +1 -0
  121. package/dist/core/scanner-internals/nav-config-scanner.d.ts +30 -0
  122. package/dist/core/scanner-internals/nav-config-scanner.d.ts.map +1 -0
  123. package/dist/core/scanner-internals/nav-config-scanner.js +170 -0
  124. package/dist/core/scanner-internals/nav-config-scanner.js.map +1 -0
  125. package/dist/core/scanner-internals/route-detector.d.ts +27 -0
  126. package/dist/core/scanner-internals/route-detector.d.ts.map +1 -0
  127. package/dist/core/scanner-internals/route-detector.js +78 -0
  128. package/dist/core/scanner-internals/route-detector.js.map +1 -0
  129. package/dist/core/scanner-internals/types.d.ts +60 -0
  130. package/dist/core/scanner-internals/types.d.ts.map +1 -0
  131. package/dist/core/scanner-internals/types.js +29 -0
  132. package/dist/core/scanner-internals/types.js.map +1 -0
  133. package/dist/core/scanner.d.ts +21 -0
  134. package/dist/core/scanner.d.ts.map +1 -0
  135. package/dist/core/scanner.js +162 -0
  136. package/dist/core/scanner.js.map +1 -0
  137. package/dist/index.d.ts +3 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +3 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/next.d.ts +33 -0
  142. package/dist/next.d.ts.map +1 -0
  143. package/dist/next.js +110 -0
  144. package/dist/next.js.map +1 -0
  145. package/dist/preview/index.d.ts +39 -0
  146. package/dist/preview/index.d.ts.map +1 -0
  147. package/dist/preview/index.js +123 -0
  148. package/dist/preview/index.js.map +1 -0
  149. package/dist/preview/internals/highlight.d.ts +31 -0
  150. package/dist/preview/internals/highlight.d.ts.map +1 -0
  151. package/dist/preview/internals/highlight.js +184 -0
  152. package/dist/preview/internals/highlight.js.map +1 -0
  153. package/dist/preview/internals/interactions.d.ts +15 -0
  154. package/dist/preview/internals/interactions.d.ts.map +1 -0
  155. package/dist/preview/internals/interactions.js +38 -0
  156. package/dist/preview/internals/interactions.js.map +1 -0
  157. package/dist/preview/internals/interactive.d.ts +14 -0
  158. package/dist/preview/internals/interactive.d.ts.map +1 -0
  159. package/dist/preview/internals/interactive.js +122 -0
  160. package/dist/preview/internals/interactive.js.map +1 -0
  161. package/dist/preview/internals/locales.d.ts +9 -0
  162. package/dist/preview/internals/locales.d.ts.map +1 -0
  163. package/dist/preview/internals/locales.js +24 -0
  164. package/dist/preview/internals/locales.js.map +1 -0
  165. package/dist/preview/internals/state.d.ts +37 -0
  166. package/dist/preview/internals/state.d.ts.map +1 -0
  167. package/dist/preview/internals/state.js +74 -0
  168. package/dist/preview/internals/state.js.map +1 -0
  169. package/dist/preview/internals/styles.d.ts +8 -0
  170. package/dist/preview/internals/styles.d.ts.map +1 -0
  171. package/dist/preview/internals/styles.js +28 -0
  172. package/dist/preview/internals/styles.js.map +1 -0
  173. package/dist/preview/internals/types.d.ts +74 -0
  174. package/dist/preview/internals/types.d.ts.map +1 -0
  175. package/dist/preview/internals/types.js +5 -0
  176. package/dist/preview/internals/types.js.map +1 -0
  177. package/dist/preview/internals/ui-indicators.d.ts +15 -0
  178. package/dist/preview/internals/ui-indicators.d.ts.map +1 -0
  179. package/dist/preview/internals/ui-indicators.js +92 -0
  180. package/dist/preview/internals/ui-indicators.js.map +1 -0
  181. package/dist/scaffold/index.d.ts +3 -0
  182. package/dist/scaffold/index.d.ts.map +1 -0
  183. package/dist/scaffold/index.js +3 -0
  184. package/dist/scaffold/index.js.map +1 -0
  185. package/dist/scaffold/intenrals/scaffold.d.ts +24 -0
  186. package/dist/scaffold/intenrals/scaffold.d.ts.map +1 -0
  187. package/dist/scaffold/intenrals/scaffold.js +53 -0
  188. package/dist/scaffold/intenrals/scaffold.js.map +1 -0
  189. package/dist/scaffold/intenrals/templates.d.ts +10 -0
  190. package/dist/scaffold/intenrals/templates.d.ts.map +1 -0
  191. package/dist/scaffold/intenrals/templates.js +344 -0
  192. package/dist/scaffold/intenrals/templates.js.map +1 -0
  193. package/dist/scaffold/intenrals/types.d.ts +14 -0
  194. package/dist/scaffold/intenrals/types.d.ts.map +1 -0
  195. package/dist/scaffold/intenrals/types.js +5 -0
  196. package/dist/scaffold/intenrals/types.js.map +1 -0
  197. package/package.json +3 -3
package/dist/next.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Next.js Plugin for automatic translation sync
3
+ *
4
+ * Usage in next.config.ts:
5
+ * import withTranslationsCMS from '@translation-cms/sync/next';
6
+ *
7
+ * export default withTranslationsCMS(nextConfig, {
8
+ * pullOnBuild: true, // default
9
+ * pullOnDev: true, // default
10
+ * });
11
+ */
12
+ import path from 'path';
13
+ import { runPull } from './commands/pull.js';
14
+ import { resolveConfig, findProjectRoot, loadConfigFile, } from './config/resolve-config.js';
15
+ import { getLastPulledTime } from './core/cache.js';
16
+ import { pullTranslations } from './core/api.js';
17
+ /**
18
+ * Higher-order function that wraps your Next.js config with automatic translation syncing.
19
+ *
20
+ * @param userConfig - Your existing next.config.ts configuration
21
+ * @param pluginOptions - Plugin-specific options
22
+ * @returns Enhanced Next.js configuration with sync hooks
23
+ */
24
+ export function withTranslationsCMS(userConfig, pluginOptions = {}) {
25
+ const { pullOnBuild = true, pullOnDev = true, watchInterval = 10_000, } = pluginOptions;
26
+ let initialized = false;
27
+ // Initialize and run pull if needed
28
+ const initTranslations = async () => {
29
+ if (initialized)
30
+ return;
31
+ initialized = true;
32
+ try {
33
+ const root = findProjectRoot();
34
+ const fileConfig = loadConfigFile(root);
35
+ const config = resolveConfig(root, fileConfig);
36
+ // Only pull if we have valid configuration
37
+ if (config.cmsUrl && config.projectId && config.apiKey) {
38
+ const isDev = process.env.NODE_ENV === 'development';
39
+ const shouldPull = (isDev && pullOnDev) || (!isDev && pullOnBuild);
40
+ if (shouldPull) {
41
+ await runPull(root, config, fileConfig, []);
42
+ }
43
+ // In dev mode, start a polling loop to auto-pull when the CMS publishes
44
+ if (isDev && watchInterval > 0) {
45
+ const outputDir = fileConfig.outputDir
46
+ ? path.resolve(root, fileConfig.outputDir)
47
+ : path.join(root, 'public', 'locales');
48
+ startWatcher(config, outputDir, watchInterval);
49
+ }
50
+ }
51
+ }
52
+ catch (error) {
53
+ // Log error but don't fail the build
54
+ // eslint-disable-next-line no-console
55
+ console.warn('Translation sync failed (continuing with build):', error instanceof Error ? error.message : String(error));
56
+ }
57
+ };
58
+ return {
59
+ ...userConfig,
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ webpack: (config, context) => {
62
+ // Run sync on webpack initialization (first build/dev startup)
63
+ if (context.isServer && !initialized) {
64
+ // eslint-disable-next-line no-console
65
+ initTranslations().catch(console.error);
66
+ }
67
+ // Call any existing webpack config if provided
68
+ if (typeof userConfig?.webpack === 'function') {
69
+ return userConfig.webpack(config, context);
70
+ }
71
+ return config;
72
+ },
73
+ };
74
+ }
75
+ /**
76
+ * Poll the CMS status endpoint every `intervalMs` ms.
77
+ * When a publish newer than the last local pull is detected, re-pulls translations.
78
+ * Errors are silently swallowed so a temporarily unreachable CMS never crashes dev.
79
+ */
80
+ function startWatcher(config, outputDir, intervalMs) {
81
+ const statusUrl = `${config.cmsUrl.replace(/\/$/, '')}/api/sync/${config.projectId}/status`;
82
+ const poll = async () => {
83
+ try {
84
+ const res = await fetch(statusUrl, {
85
+ headers: { 'x-api-key': config.apiKey },
86
+ signal: AbortSignal.timeout(5000),
87
+ });
88
+ if (!res.ok)
89
+ return;
90
+ const { lastPublishedAt } = (await res.json());
91
+ if (!lastPublishedAt)
92
+ return;
93
+ const lastPulled = getLastPulledTime(outputDir);
94
+ const publishedMs = new Date(lastPublishedAt).getTime();
95
+ if (lastPulled === null || publishedMs > lastPulled) {
96
+ // eslint-disable-next-line no-console
97
+ console.log('[CMS] New translations detected — pulling...');
98
+ await pullTranslations(config, { outputDir, force: true });
99
+ }
100
+ }
101
+ catch {
102
+ // Silently ignore — CMS may be unreachable or dev server starting up
103
+ }
104
+ };
105
+ setInterval(poll, intervalMs);
106
+ // eslint-disable-next-line no-console
107
+ console.log(`[CMS] Watching for translation updates every ${intervalMs / 1000}s`);
108
+ }
109
+ export default withTranslationsCMS;
110
+ //# sourceMappingURL=next.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.js","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Translation CMS — Preview Listener
3
+ *
4
+ * Add this to your client app to enable live in-context preview of translations
5
+ * from the CMS. No per-component setup required.
6
+ *
7
+ * Usage in Next.js root layout:
8
+ * ```tsx
9
+ * 'use client';
10
+ * import { useEffect } from 'react';
11
+ * import { initPreviewListener, cleanupPreviewListener } from '@translation-cms/sync/preview';
12
+ *
13
+ * export function CMSPreview() {
14
+ * useEffect(() => {
15
+ * initPreviewListener({ onLocaleSwitch: (locale) => i18n.changeLanguage(locale) });
16
+ * return () => cleanupPreviewListener();
17
+ * }, []);
18
+ * return null;
19
+ * }
20
+ * ```
21
+ *
22
+ * Precise highlighting (recommended):
23
+ * Add `data-cms-key="namespace:key"` to elements that render a translation.
24
+ * The listener will target those elements directly, bypassing text-search heuristics.
25
+ * ```tsx
26
+ * <h1 data-cms-key="blog:post.title">{t('blog:post.title')}</h1>
27
+ * ```
28
+ */
29
+ export type { PreviewListenerOptions } from './internals/types.js';
30
+ export { updateStateDisplay } from './internals/ui-indicators.js';
31
+ import type { PreviewListenerOptions } from './internals/types.js';
32
+ /**
33
+ * Initialize the preview listener.
34
+ * Safe to call multiple times — only initialises once.
35
+ */
36
+ export declare function initPreviewListener(options?: PreviewListenerOptions): void;
37
+ /** Remove the preview listener and clear all highlights. */
38
+ export declare function cleanupPreviewListener(): void;
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/preview/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAuBlE,OAAO,KAAK,EAIR,sBAAsB,EACzB,MAAM,sBAAsB,CAAC;AAM9B;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAoC1E;AAED,4DAA4D;AAC5D,wBAAgB,sBAAsB,IAAI,IAAI,CAS7C"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Translation CMS — Preview Listener
3
+ *
4
+ * Add this to your client app to enable live in-context preview of translations
5
+ * from the CMS. No per-component setup required.
6
+ *
7
+ * Usage in Next.js root layout:
8
+ * ```tsx
9
+ * 'use client';
10
+ * import { useEffect } from 'react';
11
+ * import { initPreviewListener, cleanupPreviewListener } from '@translation-cms/sync/preview';
12
+ *
13
+ * export function CMSPreview() {
14
+ * useEffect(() => {
15
+ * initPreviewListener({ onLocaleSwitch: (locale) => i18n.changeLanguage(locale) });
16
+ * return () => cleanupPreviewListener();
17
+ * }, []);
18
+ * return null;
19
+ * }
20
+ * ```
21
+ *
22
+ * Precise highlighting (recommended):
23
+ * Add `data-cms-key="namespace:key"` to elements that render a translation.
24
+ * The listener will target those elements directly, bypassing text-search heuristics.
25
+ * ```tsx
26
+ * <h1 data-cms-key="blog:post.title">{t('blog:post.title')}</h1>
27
+ * ```
28
+ */
29
+ export { updateStateDisplay } from './internals/ui-indicators.js';
30
+ // Internal imports
31
+ import * as state from './internals/state.js';
32
+ import { highlightTranslationKey, clearHighlights, updateLiveText, } from './internals/highlight.js';
33
+ import { handleLocaleSwitch } from './internals/locales.js';
34
+ import { injectInteractionBlocker, removeInteractionBlocker, } from './internals/interactions.js';
35
+ import { injectStyles } from './internals/styles.js';
36
+ import { initInteractiveMode, cleanupInteractiveMode, } from './internals/interactive.js';
37
+ import { injectUIIndicators, removeUIIndicators, } from './internals/ui-indicators.js';
38
+ // ---------------------------------------------------------------------------
39
+ // Public functions
40
+ // ---------------------------------------------------------------------------
41
+ /**
42
+ * Initialize the preview listener.
43
+ * Safe to call multiple times — only initialises once.
44
+ */
45
+ export function initPreviewListener(options) {
46
+ if (state.isInitialized)
47
+ return;
48
+ const isDev = typeof window !== 'undefined' &&
49
+ (window.location.hostname === 'localhost' ||
50
+ window.location.hostname === '127.0.0.1' ||
51
+ window.location.search.includes('preview=true'));
52
+ if (!isDev) {
53
+ console.log('[CMS Preview] Not in development mode, skipping...');
54
+ return;
55
+ }
56
+ state.initializeState(options ?? {});
57
+ state.setIsInitialized(true);
58
+ window.addEventListener('message', handleMessage);
59
+ injectStyles();
60
+ // Only inject the interaction blocker when NOT in interactive mode
61
+ // and when running inside an iframe (i.e. the CMS preview context)
62
+ if (window.top !== window && !options?.interactiveMode) {
63
+ injectInteractionBlocker();
64
+ }
65
+ // Initialize interactive mode if enabled
66
+ if (options?.interactiveMode) {
67
+ initInteractiveMode();
68
+ injectUIIndicators();
69
+ }
70
+ console.log(`%c[CMS Preview] Ready ${options?.interactiveMode ? '(interactive mode)' : ''}`, 'color: #10b981; font-weight: bold;');
71
+ }
72
+ /** Remove the preview listener and clear all highlights. */
73
+ export function cleanupPreviewListener() {
74
+ if (!state.isInitialized)
75
+ return;
76
+ window.removeEventListener('message', handleMessage);
77
+ removeInteractionBlocker();
78
+ cleanupInteractiveMode();
79
+ removeUIIndicators();
80
+ clearHighlights();
81
+ state.resetState();
82
+ console.log('[CMS Preview] Listener cleaned up');
83
+ }
84
+ // ---------------------------------------------------------------------------
85
+ // Message handler
86
+ // ---------------------------------------------------------------------------
87
+ function handleMessage(event) {
88
+ const { type } = (event.data ?? {});
89
+ if (type === 'CMS_HIGHLIGHT_KEY') {
90
+ const { key, value, locale } = event.data;
91
+ console.log(`[CMS Preview] Highlighting: ${key} (${locale}) = "${value}"`);
92
+ // Only switch locale when it actually changed. Switching unconditionally
93
+ // triggers a re-render in the consuming app, which replaces DOM nodes
94
+ // and immediately removes the highlight class we just applied.
95
+ if (locale && locale !== state.activeLocale) {
96
+ state.setActiveLocale(locale);
97
+ handleLocaleSwitch(locale);
98
+ }
99
+ state.setCurrentLiveValue(null);
100
+ state.setPendingHighlight({ key, value });
101
+ highlightTranslationKey(value, key);
102
+ // If no elements were found (e.g. dialog still animating open), schedule
103
+ // a backup retry after the animation should have settled.
104
+ if (state.highlightedElements.length === 0) {
105
+ setTimeout(() => {
106
+ if (state.pendingHighlight?.key === key) {
107
+ console.log(`[CMS Preview] Retrying highlight for: ${key}`);
108
+ highlightTranslationKey(value, key);
109
+ }
110
+ }, 600);
111
+ }
112
+ }
113
+ else if (type === 'CMS_LIVE_UPDATE') {
114
+ const { oldValue, newValue } = event.data;
115
+ updateLiveText(oldValue, newValue);
116
+ }
117
+ else if (type === 'CMS_SWITCH_LOCALE') {
118
+ const { locale } = event.data;
119
+ state.setActiveLocale(locale);
120
+ handleLocaleSwitch(locale);
121
+ }
122
+ }
123
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/preview/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Translation highlighting logic.
3
+ * Handles searching for and highlighting elements that display translations.
4
+ */
5
+ /**
6
+ * Find and highlight all DOM elements that display the given translation.
7
+ *
8
+ * Search order (first strategy that yields a match wins):
9
+ * 0. `data-cms-key` attribute — precise, skips all heuristics
10
+ * 1. Exact text node match
11
+ * 2. Element content match — handles text split across siblings
12
+ * 3. Normalized text node — strips `{{interpolation}}` placeholders
13
+ * 4. Normalized element match
14
+ * 5. Key fallback — used when `value` is empty (i18next renders the key)
15
+ */
16
+ export declare function highlightTranslationKey(value: string, key: string): void;
17
+ /**
18
+ * Remove the highlight class from all currently highlighted elements and
19
+ * reset the associated state (element list + search value).
20
+ */
21
+ export declare function clearHighlights(): void;
22
+ /**
23
+ * Patch the live text of highlighted elements as the user types a new
24
+ * translation value in the CMS panel.
25
+ *
26
+ * Searches each highlighted element's text nodes for the old value (or the
27
+ * last live value if the user is already mid-edit) and replaces it in place —
28
+ * no DOM re-render needed.
29
+ */
30
+ export declare function updateLiveText(oldValue: string, newValue: string): void;
31
+ //# sourceMappingURL=highlight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../../../src/preview/internals/highlight.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+EH;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CA0ExE;AAoBD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAKtC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAwBvE"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Translation highlighting logic.
3
+ * Handles searching for and highlighting elements that display translations.
4
+ */
5
+ import * as state from './state.js';
6
+ /**
7
+ * Strip interpolation placeholders (`{{var}}` and `{var}`) and collapse
8
+ * whitespace so that translated strings with variables can still be matched
9
+ * against the rendered text in the DOM.
10
+ */
11
+ function normalizeForSearch(value) {
12
+ return value
13
+ .replace(/\{\{[^}]+\}\}/g, '')
14
+ .replace(/\{[^}]+\}/g, '')
15
+ .replace(/\s+/g, ' ')
16
+ .trim();
17
+ }
18
+ /**
19
+ * Strategy 0 (precise): find elements with `data-cms-key="<key>"` attribute.
20
+ * When present this is always preferred over text-content heuristics.
21
+ */
22
+ function findByDataAttribute(key) {
23
+ return Array.from(document.querySelectorAll(`[data-cms-key="${CSS.escape(key)}"]`));
24
+ }
25
+ /**
26
+ * Strategy 1: walk all text nodes in the document body and return the parent
27
+ * element of each node whose text includes `searchValue`.
28
+ * Excludes elements that are already highlighted to avoid double-wrapping.
29
+ */
30
+ function findByTextNodes(searchValue) {
31
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
32
+ const results = [];
33
+ let node;
34
+ while ((node = walker.nextNode())) {
35
+ if (!node.textContent?.includes(searchValue))
36
+ continue;
37
+ const el = node.parentElement;
38
+ if (el && !el.classList.contains('cms-preview-highlight'))
39
+ results.push(el);
40
+ }
41
+ return results;
42
+ }
43
+ /**
44
+ * Strategy 2: find the most specific (shortest textContent) element in the
45
+ * document whose combined `textContent` includes `searchValue`.
46
+ *
47
+ * This works when the translated string is split across sibling text nodes,
48
+ * which the text-node walker cannot detect.
49
+ */
50
+ function findByElementContent(searchValue) {
51
+ const all = Array.from(document.body.querySelectorAll('*'));
52
+ let best = null;
53
+ let bestLen = Infinity;
54
+ for (const el of all) {
55
+ if (!el.textContent?.includes(searchValue))
56
+ continue;
57
+ if (el.classList.contains('cms-preview-highlight'))
58
+ continue;
59
+ const len = el.textContent.length;
60
+ if (len < bestLen) {
61
+ bestLen = len;
62
+ best = el;
63
+ }
64
+ }
65
+ return best ? [best] : [];
66
+ }
67
+ /**
68
+ * Find and highlight all DOM elements that display the given translation.
69
+ *
70
+ * Search order (first strategy that yields a match wins):
71
+ * 0. `data-cms-key` attribute — precise, skips all heuristics
72
+ * 1. Exact text node match
73
+ * 2. Element content match — handles text split across siblings
74
+ * 3. Normalized text node — strips `{{interpolation}}` placeholders
75
+ * 4. Normalized element match
76
+ * 5. Key fallback — used when `value` is empty (i18next renders the key)
77
+ */
78
+ export function highlightTranslationKey(value, key) {
79
+ clearHighlights();
80
+ // --- Strategy 0: data-cms-key attribute (precise) -----------------------
81
+ const byAttr = findByDataAttribute(key);
82
+ if (byAttr.length > 0) {
83
+ applyHighlights(byAttr, 'data-cms-key attribute');
84
+ return;
85
+ }
86
+ // --- Heuristic fallback strategies --------------------------------------
87
+ const normalized = value ? normalizeForSearch(value) : '';
88
+ const hasInterpolation = value ? normalized !== value : false;
89
+ const candidates = [];
90
+ if (value && value.trim() !== '') {
91
+ // Primary: match on the raw translation value
92
+ candidates.push({ label: 'exact text node', search: value, fn: findByTextNodes }, {
93
+ label: 'element content',
94
+ search: value,
95
+ fn: findByElementContent,
96
+ });
97
+ // Secondary: also try the normalized form when the value contains interpolations
98
+ if (hasInterpolation && normalized) {
99
+ candidates.push({
100
+ label: 'normalized text node',
101
+ search: normalized,
102
+ fn: findByTextNodes,
103
+ }, {
104
+ label: 'normalized element',
105
+ search: normalized,
106
+ fn: findByElementContent,
107
+ });
108
+ }
109
+ }
110
+ else {
111
+ // Value is empty — i18next falls back to rendering the key itself as text
112
+ const keyWithoutNs = key.includes(':')
113
+ ? key.split(':').slice(1).join(':')
114
+ : key;
115
+ candidates.push({ label: 'key (full)', search: key, fn: findByTextNodes }, { label: 'key (no ns)', search: keyWithoutNs, fn: findByTextNodes }, { label: 'key element', search: key, fn: findByElementContent }, {
116
+ label: 'key element (no ns)',
117
+ search: keyWithoutNs,
118
+ fn: findByElementContent,
119
+ });
120
+ }
121
+ for (const { label, search, fn } of candidates) {
122
+ const found = fn(search);
123
+ if (found.length === 0)
124
+ continue;
125
+ state.setCurrentSearchValue(search);
126
+ applyHighlights(found, label);
127
+ return;
128
+ }
129
+ console.warn(`[CMS Preview] No elements found for key: "${key}" value: "${value}"`);
130
+ }
131
+ /**
132
+ * Add the highlight CSS class to each element and scroll the first one into
133
+ * view. Registers elements with the state module so `clearHighlights` can
134
+ * remove them later.
135
+ */
136
+ function applyHighlights(elements, matchLabel) {
137
+ console.log(`[CMS Preview] Highlighting ${elements.length} element(s) via ${matchLabel}`);
138
+ for (let i = 0; i < elements.length; i++) {
139
+ const el = elements[i];
140
+ if (!el)
141
+ continue;
142
+ el.classList.add('cms-preview-highlight');
143
+ state.addHighlightedElement(el);
144
+ if (i === 0)
145
+ el.scrollIntoView({ behavior: 'smooth', block: 'center' });
146
+ }
147
+ }
148
+ /**
149
+ * Remove the highlight class from all currently highlighted elements and
150
+ * reset the associated state (element list + search value).
151
+ */
152
+ export function clearHighlights() {
153
+ for (const el of state.highlightedElements)
154
+ el.classList.remove('cms-preview-highlight');
155
+ state.setHighlightedElements([]);
156
+ state.setCurrentSearchValue(null);
157
+ }
158
+ /**
159
+ * Patch the live text of highlighted elements as the user types a new
160
+ * translation value in the CMS panel.
161
+ *
162
+ * Searches each highlighted element's text nodes for the old value (or the
163
+ * last live value if the user is already mid-edit) and replaces it in place —
164
+ * no DOM re-render needed.
165
+ */
166
+ export function updateLiveText(oldValue, newValue) {
167
+ if (state.highlightedElements.length === 0)
168
+ return;
169
+ // Prefer the last live value so repeated keystrokes replace the previous
170
+ // intermediate value rather than the original translation
171
+ const searchFor = state.currentLiveValue ??
172
+ (oldValue || state.currentSearchValue) ??
173
+ oldValue;
174
+ for (const el of state.highlightedElements) {
175
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null);
176
+ let node;
177
+ while ((node = walker.nextNode())) {
178
+ if (node.nodeValue?.includes(searchFor))
179
+ node.nodeValue = node.nodeValue.replace(searchFor, newValue);
180
+ }
181
+ }
182
+ state.setCurrentLiveValue(newValue);
183
+ }
184
+ //# sourceMappingURL=highlight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlight.js","sourceRoot":"","sources":["../../../src/preview/internals/highlight.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,KAAa;IACrC,OAAO,KAAK;SACP,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;SAC7B,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,GAAW;IACpC,OAAO,KAAK,CAAC,IAAI,CACb,QAAQ,CAAC,gBAAgB,CACrB,kBAAkB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACxC,CACJ,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,WAAmB;IACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CACpC,QAAQ,CAAC,IAAI,EACb,UAAU,CAAC,SAAS,EACpB,IAAI,CACP,CAAC;IACF,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,IAAI,IAAiB,CAAC;IAEtB,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC;YAAE,SAAS;QACvD,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;QAC9B,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YACrD,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,oBAAoB,CAAC,WAAmB;IAC7C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAc,GAAG,CAAC,CAAC,CAAC;IACzE,IAAI,IAAI,GAAuB,IAAI,CAAC;IACpC,IAAI,OAAO,GAAG,QAAQ,CAAC;IAEvB,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC;YAAE,SAAS;QACrD,IAAI,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAAE,SAAS;QAE7D,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC;QAClC,IAAI,GAAG,GAAG,OAAO,EAAE,CAAC;YAChB,OAAO,GAAG,GAAG,CAAC;YACd,IAAI,GAAG,EAAE,CAAC;QACd,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,GAAW;IAC9D,eAAe,EAAE,CAAC;IAElB,2EAA2E;IAC3E,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,eAAe,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;QAClD,OAAO;IACX,CAAC;IAED,2EAA2E;IAC3E,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,MAAM,gBAAgB,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAO9D,MAAM,UAAU,GAAgB,EAAE,CAAC;IAEnC,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC/B,8CAA8C;QAC9C,UAAU,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,eAAe,EAAE,EAChE;YACI,KAAK,EAAE,iBAAiB;YACxB,MAAM,EAAE,KAAK;YACb,EAAE,EAAE,oBAAoB;SAC3B,CACJ,CAAC;QACF,iFAAiF;QACjF,IAAI,gBAAgB,IAAI,UAAU,EAAE,CAAC;YACjC,UAAU,CAAC,IAAI,CACX;gBACI,KAAK,EAAE,sBAAsB;gBAC7B,MAAM,EAAE,UAAU;gBAClB,EAAE,EAAE,eAAe;aACtB,EACD;gBACI,KAAK,EAAE,oBAAoB;gBAC3B,MAAM,EAAE,UAAU;gBAClB,EAAE,EAAE,oBAAoB;aAC3B,CACJ,CAAC;QACN,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,0EAA0E;QAC1E,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;YAClC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YACnC,CAAC,CAAC,GAAG,CAAC;QACV,UAAU,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,eAAe,EAAE,EACzD,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,eAAe,EAAE,EACnE,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAC/D;YACI,KAAK,EAAE,qBAAqB;YAC5B,MAAM,EAAE,YAAY;YACpB,EAAE,EAAE,oBAAoB;SAC3B,CACJ,CAAC;IACN,CAAC;IAED,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,UAAU,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,KAAK,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACpC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC9B,OAAO;IACX,CAAC;IAED,OAAO,CAAC,IAAI,CACR,6CAA6C,GAAG,aAAa,KAAK,GAAG,CACxE,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,QAAuB,EAAE,UAAkB;IAChE,OAAO,CAAC,GAAG,CACP,8BAA8B,QAAQ,CAAC,MAAM,mBAAmB,UAAU,EAAE,CAC/E,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACvB,IAAI,CAAC,EAAE;YAAE,SAAS;QAClB,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QAC1C,KAAK,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC;YAAE,EAAE,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5E,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC3B,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,mBAAmB;QACtC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;IACjD,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;IACjC,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,QAAgB;IAC7D,IAAI,KAAK,CAAC,mBAAmB,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEnD,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,SAAS,GACX,KAAK,CAAC,gBAAgB;QACtB,CAAC,QAAQ,IAAI,KAAK,CAAC,kBAAkB,CAAC;QACtC,QAAQ,CAAC;IAEb,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,mBAAmB,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CACpC,EAAE,EACF,UAAU,CAAC,SAAS,EACpB,IAAI,CACP,CAAC;QACF,IAAI,IAAiB,CAAC;QACtB,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC;gBACnC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACrE,CAAC;IACL,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Interaction blocking for preview mode.
3
+ * Prevents users from clicking links or submitting forms while previewing.
4
+ */
5
+ /**
6
+ * Inject a transparent full-page overlay that absorbs pointer events,
7
+ * and register capture-phase listeners for belt-and-suspenders blocking
8
+ * (covers keyboard-activated links/buttons the overlay doesn't intercept).
9
+ */
10
+ export declare function injectInteractionBlocker(): void;
11
+ /**
12
+ * Remove interaction blocker and event listeners.
13
+ */
14
+ export declare function removeInteractionBlocker(): void;
15
+ //# sourceMappingURL=interactions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions.d.ts","sourceRoot":"","sources":["../../../src/preview/internals/interactions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAU/C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAK/C"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Interaction blocking for preview mode.
3
+ * Prevents users from clicking links or submitting forms while previewing.
4
+ */
5
+ import * as state from './state.js';
6
+ /**
7
+ * Capture-phase handler that swallows all clicks and form submits.
8
+ * Prevents links from navigating and buttons from firing actions.
9
+ */
10
+ function blockInteraction(e) {
11
+ e.preventDefault();
12
+ e.stopPropagation();
13
+ }
14
+ /**
15
+ * Inject a transparent full-page overlay that absorbs pointer events,
16
+ * and register capture-phase listeners for belt-and-suspenders blocking
17
+ * (covers keyboard-activated links/buttons the overlay doesn't intercept).
18
+ */
19
+ export function injectInteractionBlocker() {
20
+ const overlay = document.createElement('div');
21
+ overlay.id = 'cms-preview-blocker';
22
+ overlay.style.cssText =
23
+ 'position:fixed;inset:0;z-index:9997;pointer-events:all;cursor:default;';
24
+ document.body.appendChild(overlay);
25
+ state.setInteractionBlockerOverlay(overlay);
26
+ document.addEventListener('click', blockInteraction, true);
27
+ document.addEventListener('submit', blockInteraction, true);
28
+ }
29
+ /**
30
+ * Remove interaction blocker and event listeners.
31
+ */
32
+ export function removeInteractionBlocker() {
33
+ document.removeEventListener('click', blockInteraction, true);
34
+ document.removeEventListener('submit', blockInteraction, true);
35
+ state.interactionBlockerOverlay?.remove();
36
+ state.setInteractionBlockerOverlay(null);
37
+ }
38
+ //# sourceMappingURL=interactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions.js","sourceRoot":"","sources":["../../../src/preview/internals/interactions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC;;;GAGG;AACH,SAAS,gBAAgB,CAAC,CAAQ;IAC9B,CAAC,CAAC,cAAc,EAAE,CAAC;IACnB,CAAC,CAAC,eAAe,EAAE,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,CAAC,EAAE,GAAG,qBAAqB,CAAC;IACnC,OAAO,CAAC,KAAK,CAAC,OAAO;QACjB,wEAAwE,CAAC;IAC7E,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,CAAC,4BAA4B,CAAC,OAAO,CAAC,CAAC;IAE5C,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;IAC3D,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB;IACpC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;IAC9D,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;IAC/D,KAAK,CAAC,yBAAyB,EAAE,MAAM,EAAE,CAAC;IAC1C,KAAK,CAAC,4BAA4B,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Interactive preview mode: allow clicks, form interactions, navigation.
3
+ * Editors can explore the app while seeing translations highlighted.
4
+ */
5
+ /**
6
+ * Initialize interactive mode:
7
+ * - Allow form interactions (typing, selecting)
8
+ * - Intercept navigation to report back to CMS
9
+ * - Capture/restore app state when navigating
10
+ */
11
+ export declare function initInteractiveMode(): void;
12
+ /** Clean up interactive mode event listeners. */
13
+ export declare function cleanupInteractiveMode(): void;
14
+ //# sourceMappingURL=interactive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../../src/preview/internals/interactive.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAc1C;AAED,iDAAiD;AACjD,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C"}