@intlayer/editor 8.7.3 → 8.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/cjs/_virtual/{_@oxc-project_runtime@0.124.0 → _@oxc-project_runtime@0.126.0}/helpers/decorate.cjs +1 -1
  2. package/dist/cjs/components/ContentSelector.cjs +1 -1
  3. package/dist/cjs/components/ContentSelectorWrapper.cjs +3 -3
  4. package/dist/cjs/components/ContentSelectorWrapper.cjs.map +1 -1
  5. package/dist/cjs/components/EditedContent.cjs +1 -1
  6. package/dist/cjs/components/IntlayerEditor.cjs +1 -1
  7. package/dist/cjs/core/EditorStateManager.cjs +18 -18
  8. package/dist/cjs/core/EditorStateManager.cjs.map +1 -1
  9. package/dist/cjs/core/IframeClickInterceptor.cjs +2 -2
  10. package/dist/cjs/core/IframeClickInterceptor.cjs.map +1 -1
  11. package/dist/cjs/core/UrlStateManager.cjs +1 -1
  12. package/dist/cjs/core/UrlStateManager.cjs.map +1 -1
  13. package/dist/esm/_virtual/{_@oxc-project_runtime@0.124.0 → _@oxc-project_runtime@0.126.0}/helpers/decorate.mjs +1 -1
  14. package/dist/esm/components/ContentSelector.mjs +1 -1
  15. package/dist/esm/components/ContentSelectorWrapper.mjs +3 -3
  16. package/dist/esm/components/ContentSelectorWrapper.mjs.map +1 -1
  17. package/dist/esm/components/EditedContent.mjs +1 -1
  18. package/dist/esm/components/IntlayerEditor.mjs +1 -1
  19. package/dist/esm/core/EditorStateManager.mjs +18 -18
  20. package/dist/esm/core/EditorStateManager.mjs.map +1 -1
  21. package/dist/esm/core/IframeClickInterceptor.mjs +2 -2
  22. package/dist/esm/core/IframeClickInterceptor.mjs.map +1 -1
  23. package/dist/esm/core/UrlStateManager.mjs +1 -1
  24. package/dist/esm/core/UrlStateManager.mjs.map +1 -1
  25. package/package.json +8 -8
@@ -1,5 +1,5 @@
1
1
 
2
- //#region \0@oxc-project+runtime@0.124.0/helpers/decorate.js
2
+ //#region \0@oxc-project+runtime@0.126.0/helpers/decorate.js
3
3
  function __decorate(decorators, target, key, desc) {
4
4
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5
5
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
- const require_decorate = require('../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.cjs');
3
+ const require_decorate = require('../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.cjs');
4
4
  const require_components_ContentSelectorWrapper = require('./ContentSelectorWrapper.cjs');
5
5
  const require_components_EditedContent = require('./EditedContent.cjs');
6
6
  const require_components_IntlayerEditor = require('./IntlayerEditor.cjs');
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
3
  const require_messageKey = require('../messageKey.cjs');
4
4
  const require_core_globalManager = require('../core/globalManager.cjs');
5
- const require_decorate = require('../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.cjs');
5
+ const require_decorate = require('../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.cjs');
6
6
  let lit = require("lit");
7
7
  let lit_decorators_js = require("lit/decorators.js");
8
8
  let _intlayer_core_utils = require("@intlayer/core/utils");
@@ -156,14 +156,14 @@ var IntlayerContentSelectorWrapperElement = class extends lit.LitElement {
156
156
  }
157
157
  _handleHover(e) {
158
158
  e.stopPropagation();
159
- require_core_globalManager.getGlobalEditorManager()?.messenger.send(`${require_messageKey.MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`, {
159
+ require_core_globalManager.getGlobalEditorManager()?.messenger.send(`${"INTLAYER_HOVERED_CONTENT_CHANGED"}/post`, {
160
160
  dictionaryKey: this.dictionaryKey,
161
161
  keyPath: this._getFilteredKeyPath()
162
162
  });
163
163
  }
164
164
  _handleUnhover(e) {
165
165
  e.stopPropagation();
166
- require_core_globalManager.getGlobalEditorManager()?.messenger.send(`${require_messageKey.MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`, null);
166
+ require_core_globalManager.getGlobalEditorManager()?.messenger.send(`${"INTLAYER_HOVERED_CONTENT_CHANGED"}/post`, null);
167
167
  }
168
168
  render() {
169
169
  if (!this._isInIframe || !this._editorEnabled) return lit.html`<slot></slot>`;
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelectorWrapper.cjs","names":["LitElement","getGlobalEditorManager","onGlobalEditorManagerChange","NodeTypes","MessageKey"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { css, html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic Lit element that wraps content with the Intlayer editor\n * selection UI. It replaces the per-framework ContentSelectorWrapper components\n * (Vue, Svelte, Solid, Preact).\n *\n * It reads from the global EditorStateManager singleton (set by initEditorClient)\n * and conditionally renders <intlayer-content-selector> around its slot content\n * when the editor is active and the app is running inside an iframe.\n *\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} dictionary-key - The dictionary key owning this content node\n */\nexport class IntlayerContentSelectorWrapperElement extends LitElement {\n static styles = css`\n :host {\n display: contents;\n }\n `;\n\n @property({ type: String, attribute: 'key-path' }) keyPathJson = '[]';\n @property({ type: String, attribute: 'dictionary-key' }) dictionaryKey = '';\n\n @state() private _editorEnabled = false;\n @state() private _isInIframe = false;\n @state() private _isSelected = false;\n @state() private _editedValue: ContentNode | undefined = undefined;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEnabled: (() => void) | null = null;\n private _unsubFocused: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n connectedCallback(): void {\n super.connectedCallback();\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this._teardown();\n }\n\n updated(changedProperties: Map<string, unknown>): void {\n if (\n changedProperties.has('keyPathJson') ||\n changedProperties.has('dictionaryKey')\n ) {\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n // Keep listening for manager changes (handles stop + re-init cycles)\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editorEnabled = false;\n this._isSelected = false;\n this._editedValue = undefined;\n }\n });\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._editorEnabled = manager.editorEnabled.value ?? false;\n this._updateIsSelected(manager.focusedContent.value);\n this._updateEditedValue(manager);\n\n const handleEnabledChange = (e: Event) => {\n this._editorEnabled = (e as CustomEvent<boolean>).detail;\n };\n const handleFocusedChange = (e: Event) => {\n this._updateIsSelected((e as CustomEvent<FileContent | null>).detail);\n };\n const handleEditedContentChange = () => {\n this._updateEditedValue(manager);\n };\n\n manager.editorEnabled.addEventListener('change', handleEnabledChange);\n manager.focusedContent.addEventListener('change', handleFocusedChange);\n manager.editedContent.addEventListener('change', handleEditedContentChange);\n\n this._unsubEnabled = () =>\n manager.editorEnabled.removeEventListener('change', handleEnabledChange);\n this._unsubFocused = () =>\n manager.focusedContent.removeEventListener('change', handleFocusedChange);\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener(\n 'change',\n handleEditedContentChange\n );\n }\n\n private _getRawKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this.keyPathJson) as KeyPath[];\n } catch {\n return [];\n }\n }\n\n private _getFilteredKeyPath(): KeyPath[] {\n return this._getRawKeyPath().filter(\n (k) => k.type !== NodeTypes.TRANSLATION\n );\n }\n\n private _updateEditedValue(manager: EditorStateManager): void {\n const filteredKeyPath = this._getFilteredKeyPath();\n if (!this.dictionaryKey || filteredKeyPath.length === 0) {\n this._editedValue = undefined;\n return;\n }\n\n // Node types whose display requires framework-level rendering (markdown,\n // HTML, insertion, file): do not override the slot — the framework handles\n // those. Only plain / translated strings can be substituted here.\n const rawKeyPath = this._getRawKeyPath();\n const lastStepType = rawKeyPath[rawKeyPath.length - 1]?.type;\n if (\n lastStepType === NodeTypes.MARKDOWN ||\n lastStepType === NodeTypes.HTML ||\n lastStepType === NodeTypes.INSERTION ||\n lastStepType === NodeTypes.FILE\n ) {\n this._editedValue = undefined;\n return;\n }\n\n let value = manager.getContentValue(this.dictionaryKey, filteredKeyPath);\n\n // getContentNodeByKeyPath resolves translation nodes only at intermediate\n // steps, not the final leaf. Resolve manually when the returned value is\n // still a translation object (happens when Translation steps are filtered\n // out and the leaf IS the translation object).\n if (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n value.nodeType === NodeTypes.TRANSLATION\n ) {\n const locale = manager.currentLocale.value as string | undefined;\n // TypedNodeModel<Translation, …> structurally satisfies TypedNode<BaseNode>\n // (both have nodeType), so this narrowing cast is sound.\n const node = value as TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentNode>\n >;\n value = locale ? node[NodeTypes.TRANSLATION][locale] : undefined;\n }\n\n this._editedValue = value;\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n return;\n }\n const keyPath = this._getFilteredKeyPath();\n this._isSelected =\n focusedContent.dictionaryKey === this.dictionaryKey &&\n (focusedContent.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(focusedContent.keyPath ?? [], keyPath);\n }\n\n private _handlePress(e: Event): void {\n // Stop propagation so nested wrappers don't also fire (composed + bubbles)\n e.stopPropagation();\n const manager = getGlobalEditorManager();\n if (!manager) return;\n manager.focusedContent.set({\n dictionaryKey: this.dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n });\n }\n\n private _handleHover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n { dictionaryKey: this.dictionaryKey, keyPath: this._getFilteredKeyPath() }\n );\n }\n\n private _handleUnhover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n null\n );\n }\n\n render() {\n if (!this._isInIframe || !this._editorEnabled) {\n return html`<slot></slot>`;\n }\n\n const editedValue = this._editedValue;\n const displayedContent =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean'\n ? editedValue\n : html`<slot></slot>`;\n\n return html`\n <intlayer-content-selector\n ?is-selecting=${this._isSelected}\n @intlayer:press=${this._handlePress}\n @intlayer:hover=${this._handleHover}\n @intlayer:unhover=${this._handleUnhover}\n >\n ${displayedContent}\n </intlayer-content-selector>\n `;\n }\n}\n\nexport const defineIntlayerContentSelectorWrapper = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-content-selector-wrapper')) {\n customElements.define(\n 'intlayer-content-selector-wrapper',\n IntlayerContentSelectorWrapperElement\n );\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAa,wCAAb,cAA2DA,eAAW;;;qBAOH;uBACQ;wBAEvC;qBACH;qBACA;sBAC0B;uBAEZ;uBACA;uBACA;6BACM;;;gBAjBnC,OAAG;;;;;;CAmBnB,oBAA0B;AACxB,QAAM,mBAAmB;AACzB,MAAI,OAAO,WAAW,YACpB,MAAK,cAAc,OAAO,SAAS,OAAO;AAE5C,OAAK,qBAAqB;;CAG5B,uBAA6B;AAC3B,QAAM,sBAAsB;AAC5B,OAAK,WAAW;;CAGlB,QAAQ,mBAA+C;AACrD,MACE,kBAAkB,IAAI,cAAc,IACpC,kBAAkB,IAAI,gBAAgB,EACtC;GACA,MAAM,UAAUC,mDAAwB;AACxC,OAAI,QAAS,MAAK,mBAAmB,QAAQ;;;CAIjD,AAAQ,YAAkB;AACxB,OAAK,iBAAiB;AACtB,OAAK,iBAAiB;AACtB,OAAK,iBAAiB;AACtB,OAAK,uBAAuB;AAC5B,OAAK,gBAAgB;AACrB,OAAK,gBAAgB;AACrB,OAAK,gBAAgB;AACrB,OAAK,sBAAsB;;CAG7B,AAAQ,sBAA4B;EAClC,MAAM,UAAUA,mDAAwB;AACxC,MAAI,QACF,MAAK,2BAA2B,QAAQ;AAG1C,OAAK,gBAAgBC,wDAA6B,MAAM;AACtD,QAAK,iBAAiB;AACtB,QAAK,iBAAiB;AACtB,QAAK,uBAAuB;AAC5B,QAAK,gBAAgB;AACrB,QAAK,gBAAgB;AACrB,QAAK,sBAAsB;AAC3B,OAAI,EACF,MAAK,2BAA2B,EAAE;QAC7B;AACL,SAAK,iBAAiB;AACtB,SAAK,cAAc;AACnB,SAAK,eAAe;;IAEtB;;CAGJ,AAAQ,2BAA2B,SAAmC;AACpE,OAAK,iBAAiB,QAAQ,cAAc,SAAS;AACrD,OAAK,kBAAkB,QAAQ,eAAe,MAAM;AACpD,OAAK,mBAAmB,QAAQ;EAEhC,MAAM,uBAAuB,MAAa;AACxC,QAAK,iBAAkB,EAA2B;;EAEpD,MAAM,uBAAuB,MAAa;AACxC,QAAK,kBAAmB,EAAsC,OAAO;;EAEvE,MAAM,kCAAkC;AACtC,QAAK,mBAAmB,QAAQ;;AAGlC,UAAQ,cAAc,iBAAiB,UAAU,oBAAoB;AACrE,UAAQ,eAAe,iBAAiB,UAAU,oBAAoB;AACtE,UAAQ,cAAc,iBAAiB,UAAU,0BAA0B;AAE3E,OAAK,sBACH,QAAQ,cAAc,oBAAoB,UAAU,oBAAoB;AAC1E,OAAK,sBACH,QAAQ,eAAe,oBAAoB,UAAU,oBAAoB;AAC3E,OAAK,4BACH,QAAQ,cAAc,oBACpB,UACA,0BACD;;CAGL,AAAQ,iBAA4B;AAClC,MAAI;AACF,UAAO,KAAK,MAAM,KAAK,YAAY;UAC7B;AACN,UAAO,EAAE;;;CAIb,AAAQ,sBAAiC;AACvC,SAAO,KAAK,gBAAgB,CAAC,QAC1B,MAAM,EAAE,SAASC,yBAAU,YAC7B;;CAGH,AAAQ,mBAAmB,SAAmC;EAC5D,MAAM,kBAAkB,KAAK,qBAAqB;AAClD,MAAI,CAAC,KAAK,iBAAiB,gBAAgB,WAAW,GAAG;AACvD,QAAK,eAAe;AACpB;;EAMF,MAAM,aAAa,KAAK,gBAAgB;EACxC,MAAM,eAAe,WAAW,WAAW,SAAS,IAAI;AACxD,MACE,iBAAiBA,yBAAU,YAC3B,iBAAiBA,yBAAU,QAC3B,iBAAiBA,yBAAU,aAC3B,iBAAiBA,yBAAU,MAC3B;AACA,QAAK,eAAe;AACpB;;EAGF,IAAI,QAAQ,QAAQ,gBAAgB,KAAK,eAAe,gBAAgB;AAMxE,MACE,UAAU,QACV,UAAU,UACV,OAAO,UAAU,YACjB,MAAM,aAAaA,yBAAU,aAC7B;GACA,MAAM,SAAS,QAAQ,cAAc;AAOrC,WAAQ,SAJK,MAISA,yBAAU,aAAa,UAAU;;AAGzD,OAAK,eAAe;;CAGtB,AAAQ,kBACN,gBACM;AACN,MAAI,CAAC,gBAAgB;AACnB,QAAK,cAAc;AACnB;;EAEF,MAAM,UAAU,KAAK,qBAAqB;AAC1C,OAAK,cACH,eAAe,kBAAkB,KAAK,kBACrC,eAAe,SAAS,UAAU,KAAK,6CAC1B,eAAe,WAAW,EAAE,EAAE,QAAQ;;CAGxD,AAAQ,aAAa,GAAgB;AAEnC,IAAE,iBAAiB;EACnB,MAAM,UAAUF,mDAAwB;AACxC,MAAI,CAAC,QAAS;AACd,UAAQ,eAAe,IAAI;GACzB,eAAe,KAAK;GACpB,SAAS,KAAK,qBAAqB;GACpC,CAAC;;CAGJ,AAAQ,aAAa,GAAgB;AACnC,IAAE,iBAAiB;AACnB,qDAAwB,EAAE,UAAU,KAClC,GAAGG,8BAAW,iCAAiC,QAC/C;GAAE,eAAe,KAAK;GAAe,SAAS,KAAK,qBAAqB;GAAE,CAC3E;;CAGH,AAAQ,eAAe,GAAgB;AACrC,IAAE,iBAAiB;AACnB,qDAAwB,EAAE,UAAU,KAClC,GAAGA,8BAAW,iCAAiC,QAC/C,KACD;;CAGH,SAAS;AACP,MAAI,CAAC,KAAK,eAAe,CAAC,KAAK,eAC7B,QAAO,QAAI;EAGb,MAAM,cAAc,KAAK;EACzB,MAAM,mBACJ,OAAO,gBAAgB,YACvB,OAAO,gBAAgB,YACvB,OAAO,gBAAgB,YACnB,cACA,QAAI;AAEV,SAAO,QAAI;;wBAES,KAAK,YAAY;0BACf,KAAK,aAAa;0BAClB,KAAK,aAAa;4BAChB,KAAK,eAAe;;UAEtC,iBAAiB;;;;;6DA9Nf;CAAE,MAAM;CAAQ,WAAW;CAAY,CAAC;6DACxC;CAAE,MAAM;CAAQ,WAAW;CAAkB,CAAC;2DAEhD;2DACA;2DACA;2DACA;AA8NV,MAAa,6CAAmD;AAC9D,KAAI,OAAO,mBAAmB,YAAa;AAE3C,KAAI,CAAC,eAAe,IAAI,oCAAoC,CAC1D,gBAAe,OACb,qCACA,sCACD"}
1
+ {"version":3,"file":"ContentSelectorWrapper.cjs","names":["LitElement","getGlobalEditorManager","onGlobalEditorManagerChange","NodeTypes"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { css, html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic Lit element that wraps content with the Intlayer editor\n * selection UI. It replaces the per-framework ContentSelectorWrapper components\n * (Vue, Svelte, Solid, Preact).\n *\n * It reads from the global EditorStateManager singleton (set by initEditorClient)\n * and conditionally renders <intlayer-content-selector> around its slot content\n * when the editor is active and the app is running inside an iframe.\n *\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} dictionary-key - The dictionary key owning this content node\n */\nexport class IntlayerContentSelectorWrapperElement extends LitElement {\n static styles = css`\n :host {\n display: contents;\n }\n `;\n\n @property({ type: String, attribute: 'key-path' }) keyPathJson = '[]';\n @property({ type: String, attribute: 'dictionary-key' }) dictionaryKey = '';\n\n @state() private _editorEnabled = false;\n @state() private _isInIframe = false;\n @state() private _isSelected = false;\n @state() private _editedValue: ContentNode | undefined = undefined;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEnabled: (() => void) | null = null;\n private _unsubFocused: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n connectedCallback(): void {\n super.connectedCallback();\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this._teardown();\n }\n\n updated(changedProperties: Map<string, unknown>): void {\n if (\n changedProperties.has('keyPathJson') ||\n changedProperties.has('dictionaryKey')\n ) {\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n // Keep listening for manager changes (handles stop + re-init cycles)\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editorEnabled = false;\n this._isSelected = false;\n this._editedValue = undefined;\n }\n });\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._editorEnabled = manager.editorEnabled.value ?? false;\n this._updateIsSelected(manager.focusedContent.value);\n this._updateEditedValue(manager);\n\n const handleEnabledChange = (e: Event) => {\n this._editorEnabled = (e as CustomEvent<boolean>).detail;\n };\n const handleFocusedChange = (e: Event) => {\n this._updateIsSelected((e as CustomEvent<FileContent | null>).detail);\n };\n const handleEditedContentChange = () => {\n this._updateEditedValue(manager);\n };\n\n manager.editorEnabled.addEventListener('change', handleEnabledChange);\n manager.focusedContent.addEventListener('change', handleFocusedChange);\n manager.editedContent.addEventListener('change', handleEditedContentChange);\n\n this._unsubEnabled = () =>\n manager.editorEnabled.removeEventListener('change', handleEnabledChange);\n this._unsubFocused = () =>\n manager.focusedContent.removeEventListener('change', handleFocusedChange);\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener(\n 'change',\n handleEditedContentChange\n );\n }\n\n private _getRawKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this.keyPathJson) as KeyPath[];\n } catch {\n return [];\n }\n }\n\n private _getFilteredKeyPath(): KeyPath[] {\n return this._getRawKeyPath().filter(\n (k) => k.type !== NodeTypes.TRANSLATION\n );\n }\n\n private _updateEditedValue(manager: EditorStateManager): void {\n const filteredKeyPath = this._getFilteredKeyPath();\n if (!this.dictionaryKey || filteredKeyPath.length === 0) {\n this._editedValue = undefined;\n return;\n }\n\n // Node types whose display requires framework-level rendering (markdown,\n // HTML, insertion, file): do not override the slot — the framework handles\n // those. Only plain / translated strings can be substituted here.\n const rawKeyPath = this._getRawKeyPath();\n const lastStepType = rawKeyPath[rawKeyPath.length - 1]?.type;\n if (\n lastStepType === NodeTypes.MARKDOWN ||\n lastStepType === NodeTypes.HTML ||\n lastStepType === NodeTypes.INSERTION ||\n lastStepType === NodeTypes.FILE\n ) {\n this._editedValue = undefined;\n return;\n }\n\n let value = manager.getContentValue(this.dictionaryKey, filteredKeyPath);\n\n // getContentNodeByKeyPath resolves translation nodes only at intermediate\n // steps, not the final leaf. Resolve manually when the returned value is\n // still a translation object (happens when Translation steps are filtered\n // out and the leaf IS the translation object).\n if (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n value.nodeType === NodeTypes.TRANSLATION\n ) {\n const locale = manager.currentLocale.value as string | undefined;\n // TypedNodeModel<Translation, …> structurally satisfies TypedNode<BaseNode>\n // (both have nodeType), so this narrowing cast is sound.\n const node = value as TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentNode>\n >;\n value = locale ? node[NodeTypes.TRANSLATION][locale] : undefined;\n }\n\n this._editedValue = value;\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n return;\n }\n const keyPath = this._getFilteredKeyPath();\n this._isSelected =\n focusedContent.dictionaryKey === this.dictionaryKey &&\n (focusedContent.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(focusedContent.keyPath ?? [], keyPath);\n }\n\n private _handlePress(e: Event): void {\n // Stop propagation so nested wrappers don't also fire (composed + bubbles)\n e.stopPropagation();\n const manager = getGlobalEditorManager();\n if (!manager) return;\n manager.focusedContent.set({\n dictionaryKey: this.dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n });\n }\n\n private _handleHover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n { dictionaryKey: this.dictionaryKey, keyPath: this._getFilteredKeyPath() }\n );\n }\n\n private _handleUnhover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n null\n );\n }\n\n render() {\n if (!this._isInIframe || !this._editorEnabled) {\n return html`<slot></slot>`;\n }\n\n const editedValue = this._editedValue;\n const displayedContent =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean'\n ? editedValue\n : html`<slot></slot>`;\n\n return html`\n <intlayer-content-selector\n ?is-selecting=${this._isSelected}\n @intlayer:press=${this._handlePress}\n @intlayer:hover=${this._handleHover}\n @intlayer:unhover=${this._handleUnhover}\n >\n ${displayedContent}\n </intlayer-content-selector>\n `;\n }\n}\n\nexport const defineIntlayerContentSelectorWrapper = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-content-selector-wrapper')) {\n customElements.define(\n 'intlayer-content-selector-wrapper',\n IntlayerContentSelectorWrapperElement\n );\n }\n};\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
3
  const require_core_globalManager = require('../core/globalManager.cjs');
4
- const require_decorate = require('../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.cjs');
4
+ const require_decorate = require('../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.cjs');
5
5
  let lit = require("lit");
6
6
  let lit_decorators_js = require("lit/decorators.js");
7
7
  let _intlayer_core_interpreter = require("@intlayer/core/interpreter");
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
3
  const require_core_globalManager = require('../core/globalManager.cjs');
4
- const require_decorate = require('../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.cjs');
4
+ const require_decorate = require('../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.cjs');
5
5
  const require_core_initEditorClient = require('../core/initEditorClient.cjs');
6
6
  let lit = require("lit");
7
7
  let lit_decorators_js = require("lit/decorators.js");
@@ -26,24 +26,24 @@ var EditorStateManager = class {
26
26
  this._mode = config.mode;
27
27
  this._configuration = config.configuration;
28
28
  this.messenger = new require_core_CrossFrameMessenger.CrossFrameMessenger(config.messenger);
29
- this.editorEnabled = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_EDITOR_ENABLED, this.messenger, {
29
+ this.editorEnabled = new require_core_CrossFrameStateManager.CrossFrameStateManager("INTLAYER_EDITOR_ENABLED", this.messenger, {
30
30
  emit: false,
31
31
  receive: true,
32
32
  initialValue: false
33
33
  });
34
- this.focusedContent = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED, this.messenger, {
34
+ this.focusedContent = new require_core_CrossFrameStateManager.CrossFrameStateManager("INTLAYER_FOCUSED_CONTENT_CHANGED", this.messenger, {
35
35
  emit: true,
36
36
  receive: true,
37
37
  initialValue: null
38
38
  });
39
- this.localeDictionaries = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED, this.messenger);
40
- this.editedContent = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_EDITED_CONTENT_CHANGED, this.messenger);
41
- this.configuration = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_CONFIGURATION, this.messenger, {
39
+ this.localeDictionaries = new require_core_CrossFrameStateManager.CrossFrameStateManager("INTLAYER_LOCALE_DICTIONARIES_CHANGED", this.messenger);
40
+ this.editedContent = new require_core_CrossFrameStateManager.CrossFrameStateManager("INTLAYER_EDITED_CONTENT_CHANGED", this.messenger);
41
+ this.configuration = new require_core_CrossFrameStateManager.CrossFrameStateManager("INTLAYER_CONFIGURATION", this.messenger, {
42
42
  emit: true,
43
43
  receive: false,
44
44
  ...config.configuration ? { initialValue: config.configuration } : {}
45
45
  });
46
- this.currentLocale = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_CURRENT_LOCALE, this.messenger, {
46
+ this.currentLocale = new require_core_CrossFrameStateManager.CrossFrameStateManager("INTLAYER_CURRENT_LOCALE", this.messenger, {
47
47
  emit: config.mode === "client",
48
48
  receive: config.mode === "editor"
49
49
  });
@@ -62,7 +62,7 @@ var EditorStateManager = class {
62
62
  this._urlManager.start();
63
63
  this._iframeInterceptor.startInterceptor();
64
64
  this._loadDictionaries();
65
- this.messenger.send(`${require_messageKey.MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);
65
+ this.messenger.send(`${"INTLAYER_EDITED_CONTENT_CHANGED"}/get`);
66
66
  if (this._configuration?.editor?.enabled !== false) this._setupActivationHandshake();
67
67
  } else {
68
68
  this._iframeInterceptor.startMerger();
@@ -93,7 +93,7 @@ var EditorStateManager = class {
93
93
  */
94
94
  pingClient() {
95
95
  if (this._mode !== "editor") return;
96
- this.messenger.send(require_messageKey.MessageKey.INTLAYER_ARE_YOU_THERE);
96
+ this.messenger.send("INTLAYER_ARE_YOU_THERE");
97
97
  }
98
98
  setFocusedContentKeyPath(keyPath) {
99
99
  const filtered = keyPath.filter((key) => key.type !== _intlayer_types_nodeType.TRANSLATION);
@@ -218,29 +218,29 @@ var EditorStateManager = class {
218
218
  * Also pings the client immediately in case it loaded before the editor.
219
219
  */
220
220
  _setupEditorHandshake() {
221
- this._unsubClientReady = this.messenger.subscribe(require_messageKey.MessageKey.INTLAYER_CLIENT_READY, () => {
221
+ this._unsubClientReady = this.messenger.subscribe("INTLAYER_CLIENT_READY", () => {
222
222
  this.editorEnabled.set(true);
223
- this.messenger.send(require_messageKey.MessageKey.INTLAYER_EDITOR_ACTIVATE);
223
+ this.messenger.send("INTLAYER_EDITOR_ACTIVATE");
224
224
  });
225
- this.messenger.send(require_messageKey.MessageKey.INTLAYER_ARE_YOU_THERE);
225
+ this.messenger.send("INTLAYER_ARE_YOU_THERE");
226
226
  }
227
227
  _setupActivationHandshake() {
228
- this.messenger.send(require_messageKey.MessageKey.INTLAYER_CLIENT_READY);
229
- this._unsubAreYouThere = this.messenger.subscribe(require_messageKey.MessageKey.INTLAYER_ARE_YOU_THERE, () => {
230
- this.messenger.send(require_messageKey.MessageKey.INTLAYER_CLIENT_READY);
228
+ this.messenger.send("INTLAYER_CLIENT_READY");
229
+ this._unsubAreYouThere = this.messenger.subscribe("INTLAYER_ARE_YOU_THERE", () => {
230
+ this.messenger.send("INTLAYER_CLIENT_READY");
231
231
  });
232
- this._unsubActivate = this.messenger.subscribe(require_messageKey.MessageKey.INTLAYER_EDITOR_ACTIVATE, () => {
232
+ this._unsubActivate = this.messenger.subscribe("INTLAYER_EDITOR_ACTIVATE", () => {
233
233
  this.editorEnabled.set(true);
234
234
  this._broadcastData();
235
235
  });
236
236
  }
237
237
  _broadcastData() {
238
238
  const configVal = this.configuration.value;
239
- if (configVal) this.messenger.send(`${require_messageKey.MessageKey.INTLAYER_CONFIGURATION}/post`, configVal);
239
+ if (configVal) this.messenger.send(`${"INTLAYER_CONFIGURATION"}/post`, configVal);
240
240
  const localeVal = this.currentLocale.value;
241
- if (localeVal) this.messenger.send(`${require_messageKey.MessageKey.INTLAYER_CURRENT_LOCALE}/post`, localeVal);
241
+ if (localeVal) this.messenger.send(`${"INTLAYER_CURRENT_LOCALE"}/post`, localeVal);
242
242
  const dicts = this.localeDictionaries.value;
243
- if (dicts) this.messenger.send(`${require_messageKey.MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`, dicts);
243
+ if (dicts) this.messenger.send(`${"INTLAYER_LOCALE_DICTIONARIES_CHANGED"}/post`, dicts);
244
244
  }
245
245
  async _loadDictionaries() {
246
246
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"EditorStateManager.cjs","names":["CrossFrameMessenger","CrossFrameStateManager","MessageKey","UrlStateManager","IframeClickInterceptor","NodeTypes"],"sources":["../../../src/core/EditorStateManager.ts"],"sourcesContent":["import {\n editDictionaryByKeyPath,\n getContentNodeByKeyPath,\n renameContentNodeByKeyPath,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n ContentNode,\n Dictionary,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { MessageKey } from '../messageKey';\nimport {\n CrossFrameMessenger,\n type MessengerConfig,\n} from './CrossFrameMessenger';\nimport { CrossFrameStateManager } from './CrossFrameStateManager';\nimport { IframeClickInterceptor } from './IframeClickInterceptor';\nimport { UrlStateManager } from './UrlStateManager';\n\nexport type DictionaryContent = Record<LocalDictionaryId, Dictionary>;\n\nexport type FileContent = {\n dictionaryKey: string;\n dictionaryLocalId?: LocalDictionaryId;\n keyPath?: KeyPath[];\n};\n\nexport type EditorStateManagerConfig = {\n /** 'client' = the app running inside the iframe; 'editor' = the editor wrapping the app */\n mode: 'editor' | 'client';\n /** Cross-frame messaging configuration */\n messenger: MessengerConfig;\n /** Optional initial Intlayer configuration to broadcast */\n configuration?: IntlayerConfig;\n};\n\n/**\n * EditorStateManager is the single entry point for all Intlayer editor state.\n * It is framework-agnostic: instantiate one instance at the root of the application\n * and subscribe to its EventTarget-based events from any framework adapter.\n *\n * Replaces all context providers, hooks and store files across React, Preact,\n * Solid, Svelte, and Vue integrations.\n */\nexport class EditorStateManager {\n readonly messenger: CrossFrameMessenger;\n readonly editorEnabled: CrossFrameStateManager<boolean>;\n readonly focusedContent: CrossFrameStateManager<FileContent | null>;\n readonly localeDictionaries: CrossFrameStateManager<DictionaryContent>;\n readonly editedContent: CrossFrameStateManager<DictionaryContent>;\n readonly configuration: CrossFrameStateManager<IntlayerConfig>;\n readonly currentLocale: CrossFrameStateManager<Locale | undefined>;\n\n private readonly _urlManager: UrlStateManager;\n private readonly _iframeInterceptor: IframeClickInterceptor;\n private readonly _mode: 'editor' | 'client';\n private readonly _configuration: IntlayerConfig | undefined;\n\n // Client-mode handshake subscribers\n private _unsubAreYouThere: (() => void) | null = null;\n private _unsubActivate: (() => void) | null = null;\n // Editor-mode handshake subscriber\n private _unsubClientReady: (() => void) | null = null;\n\n constructor(config: EditorStateManagerConfig) {\n this._mode = config.mode;\n this._configuration = config.configuration;\n\n this.messenger = new CrossFrameMessenger(config.messenger);\n\n this.editorEnabled = new CrossFrameStateManager<boolean>(\n MessageKey.INTLAYER_EDITOR_ENABLED,\n this.messenger,\n { emit: false, receive: true, initialValue: false }\n );\n\n this.focusedContent = new CrossFrameStateManager<FileContent | null>(\n MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED,\n this.messenger,\n { emit: true, receive: true, initialValue: null }\n );\n\n this.localeDictionaries = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED,\n this.messenger\n );\n\n this.editedContent = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_EDITED_CONTENT_CHANGED,\n this.messenger\n );\n\n this.configuration = new CrossFrameStateManager<IntlayerConfig>(\n MessageKey.INTLAYER_CONFIGURATION,\n this.messenger,\n {\n emit: true,\n receive: false,\n ...(config.configuration ? { initialValue: config.configuration } : {}),\n }\n );\n\n // Client emits its locale to the editor; editor receives it.\n this.currentLocale = new CrossFrameStateManager<Locale>(\n MessageKey.INTLAYER_CURRENT_LOCALE,\n this.messenger,\n {\n emit: config.mode === 'client',\n receive: config.mode === 'editor',\n }\n );\n\n this._urlManager = new UrlStateManager(this.messenger);\n this._iframeInterceptor = new IframeClickInterceptor(this.messenger);\n }\n\n start(): void {\n this.messenger.start();\n this.editorEnabled.start();\n this.focusedContent.start();\n this.localeDictionaries.start();\n this.editedContent.start();\n this.configuration.start();\n this.currentLocale.start();\n\n if (this._mode === 'client') {\n this._urlManager.start();\n this._iframeInterceptor.startInterceptor();\n this._loadDictionaries();\n // Request current edited content from the editor\n this.messenger.send(`${MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);\n // Activation handshake: only participate if editor.enabled !== false\n if (this._configuration?.editor?.enabled !== false) {\n this._setupActivationHandshake();\n }\n } else {\n this._iframeInterceptor.startMerger();\n this._setupEditorHandshake();\n }\n }\n\n stop(): void {\n this._unsubAreYouThere?.();\n this._unsubActivate?.();\n this._unsubClientReady?.();\n this._unsubAreYouThere = null;\n this._unsubActivate = null;\n this._unsubClientReady = null;\n this.messenger.stop();\n this.editorEnabled.stop();\n this.focusedContent.stop();\n this.localeDictionaries.stop();\n this.editedContent.stop();\n this.configuration.stop();\n this.currentLocale.stop();\n this._urlManager.stop();\n this._iframeInterceptor.stopInterceptor();\n this._iframeInterceptor.stopMerger();\n }\n\n // ─── Handshake helpers ───────────────────────────────────────────────────────\n\n /**\n * EDITOR mode: re-send ARE_YOU_THERE to attempt re-connection with the client.\n * Call this when the user clicks \"Enable Editor\" or when the iframe reloads.\n */\n pingClient(): void {\n if (this._mode !== 'editor') return;\n\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n // ─── Focus helpers ──────────────────────────────────────────────────────────\n\n setFocusedContentKeyPath(keyPath: KeyPath[]): void {\n const filtered = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n const prev = this.focusedContent.value;\n\n if (!prev) return;\n\n this.focusedContent.set({ ...prev, keyPath: filtered });\n }\n\n // ─── Dictionary record helpers ───────────────────────────────────────────\n\n setLocaleDictionary(dictionary: Dictionary): void {\n if (!dictionary.localId) return;\n const current = this.localeDictionaries.value ?? {};\n\n this.localeDictionaries.set({\n ...current,\n [dictionary.localId as LocalDictionaryId]: dictionary,\n });\n }\n\n // ─── Edited content helpers ───────────────────────────────────────────────\n\n setEditedDictionary(newDict: Dictionary): void {\n if (!newDict.localId) {\n console.error('setEditedDictionary: missing localId', newDict);\n return;\n }\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [newDict.localId as LocalDictionaryId]: newDict,\n });\n }\n\n setEditedContent(\n localDictionaryId: LocalDictionaryId,\n newValue: Dictionary['content']\n ): void {\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: newValue,\n },\n });\n }\n\n addContent(\n localDictionaryId: LocalDictionaryId,\n newValue: ContentNode,\n keyPath: KeyPath[] = [],\n overwrite = true\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n\n let newKeyPath = keyPath;\n if (!overwrite) {\n let index = 0;\n\n const otherKeyPath = keyPath.slice(0, -1);\n const lastKeyPath = keyPath[keyPath.length - 1];\n\n let finalKey = lastKeyPath.key;\n\n while (\n typeof getContentNodeByKeyPath(currentContent, newKeyPath) !==\n 'undefined'\n ) {\n index++;\n finalKey =\n index === 0 ? lastKeyPath.key : `${lastKeyPath.key} (${index})`;\n newKeyPath = [\n ...otherKeyPath,\n { ...lastKeyPath, key: finalKey } as KeyPath,\n ];\n }\n }\n\n const updatedContent = editDictionaryByKeyPath(\n currentContent,\n newKeyPath,\n newValue\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updatedContent as Dictionary['content'],\n },\n });\n }\n\n renameContent(\n localDictionaryId: LocalDictionaryId,\n newKey: KeyPath['key'],\n keyPath: KeyPath[] = []\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const updated = renameContentNodeByKeyPath(currentContent, newKey, keyPath);\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updated as Dictionary['content'],\n },\n });\n }\n\n removeContent(\n localDictionaryId: LocalDictionaryId,\n keyPath: KeyPath[]\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const initialContent = getContentNodeByKeyPath(originalContent, keyPath);\n const restored = editDictionaryByKeyPath(\n currentContent,\n keyPath,\n initialContent\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: restored as Dictionary['content'],\n },\n });\n }\n\n restoreContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const updated = { ...current };\n\n delete updated[localDictionaryId];\n\n this.editedContent.set(updated);\n }\n\n clearContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const filtered = { ...current };\n\n delete filtered[localDictionaryId];\n\n this.editedContent.set(filtered);\n }\n\n clearAllContent(): void {\n this.editedContent.set({});\n }\n\n getContentValue(\n localDictionaryIdOrKey: LocalDictionaryId | string,\n keyPath: KeyPath[]\n ): ContentNode | undefined {\n const edited = this.editedContent.value;\n if (!edited) return undefined;\n\n const filteredKeyPath = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n\n // Only use edited content entries whose localId is known to this client.\n // This prevents stale edits from other apps (different framework demos) from\n // being applied when the editor sends back its stored editedContent.\n const localeDicts = this.localeDictionaries.value;\n\n const isDictionaryId =\n localDictionaryIdOrKey.includes(':local:') ||\n localDictionaryIdOrKey.includes(':remote:');\n\n if (isDictionaryId) {\n // If localeDictionaries is loaded, verify this localId belongs to us\n if (localeDicts && !(localDictionaryIdOrKey in localeDicts)) {\n return undefined;\n }\n const content =\n edited[localDictionaryIdOrKey as LocalDictionaryId]?.content ?? {};\n\n return getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n }\n\n const matchingIds = Object.keys(edited).filter(\n (key) =>\n key.startsWith(`${localDictionaryIdOrKey}:`) &&\n // If localeDictionaries is loaded, only include known localIds\n (!localeDicts || key in localeDicts)\n );\n\n for (const localId of matchingIds) {\n const content = edited[localId as LocalDictionaryId]?.content ?? {};\n const node = getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n if (node) return node;\n }\n\n return undefined;\n }\n\n /**\n * EDITOR mode: listen for CLIENT_READY and respond with EDITOR_ACTIVATE.\n * Also pings the client immediately in case it loaded before the editor.\n */\n private _setupEditorHandshake(): void {\n // When the client announces it is ready, activate it\n this._unsubClientReady = this.messenger.subscribe(\n MessageKey.INTLAYER_CLIENT_READY,\n () => {\n this.editorEnabled.set(true);\n this.messenger.send(MessageKey.INTLAYER_EDITOR_ACTIVATE);\n }\n );\n\n // Ping any already-running client (covers editor-opens-after-client scenario)\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n private _setupActivationHandshake(): void {\n // Announce to the editor that the client is ready\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n\n // Respond to \"are you there?\" pings from the editor\n this._unsubAreYouThere = this.messenger.subscribe(\n MessageKey.INTLAYER_ARE_YOU_THERE,\n () => {\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n }\n );\n\n // When the editor activates us, enable the selector and broadcast state\n this._unsubActivate = this.messenger.subscribe(\n MessageKey.INTLAYER_EDITOR_ACTIVATE,\n () => {\n this.editorEnabled.set(true);\n this._broadcastData();\n }\n );\n }\n\n private _broadcastData(): void {\n const configVal = this.configuration.value;\n\n if (configVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CONFIGURATION}/post`,\n configVal\n );\n }\n const localeVal = this.currentLocale.value;\n\n if (localeVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CURRENT_LOCALE}/post`,\n localeVal\n );\n }\n const dicts = this.localeDictionaries.value;\n\n if (dicts) {\n this.messenger.send(\n `${MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`,\n dicts\n );\n }\n }\n\n private async _loadDictionaries(): Promise<void> {\n try {\n const mod = await import('@intlayer/unmerged-dictionaries-entry');\n const unmergedDictionaries = mod.getUnmergedDictionaries();\n const dictionariesList = Object.fromEntries(\n Object.values(unmergedDictionaries)\n .flat()\n .map((dictionary) => [dictionary.localId, dictionary])\n ) as DictionaryContent;\n\n this.localeDictionaries.set(dictionariesList);\n\n // If the editor already activated us before dictionaries finished loading,\n // re-broadcast now so the editor receives the dictionaries.\n if (this.editorEnabled.value) {\n this._broadcastData();\n }\n } catch (e) {\n // Dynamic entry not available (expected in editor mode or when not configured)\n console.warn('[intlayer] Failed to load unmerged dictionaries:', e);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EditorStateManager.cjs","names":["CrossFrameMessenger","CrossFrameStateManager","UrlStateManager","IframeClickInterceptor","NodeTypes"],"sources":["../../../src/core/EditorStateManager.ts"],"sourcesContent":["import {\n editDictionaryByKeyPath,\n getContentNodeByKeyPath,\n renameContentNodeByKeyPath,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n ContentNode,\n Dictionary,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { MessageKey } from '../messageKey';\nimport {\n CrossFrameMessenger,\n type MessengerConfig,\n} from './CrossFrameMessenger';\nimport { CrossFrameStateManager } from './CrossFrameStateManager';\nimport { IframeClickInterceptor } from './IframeClickInterceptor';\nimport { UrlStateManager } from './UrlStateManager';\n\nexport type DictionaryContent = Record<LocalDictionaryId, Dictionary>;\n\nexport type FileContent = {\n dictionaryKey: string;\n dictionaryLocalId?: LocalDictionaryId;\n keyPath?: KeyPath[];\n};\n\nexport type EditorStateManagerConfig = {\n /** 'client' = the app running inside the iframe; 'editor' = the editor wrapping the app */\n mode: 'editor' | 'client';\n /** Cross-frame messaging configuration */\n messenger: MessengerConfig;\n /** Optional initial Intlayer configuration to broadcast */\n configuration?: IntlayerConfig;\n};\n\n/**\n * EditorStateManager is the single entry point for all Intlayer editor state.\n * It is framework-agnostic: instantiate one instance at the root of the application\n * and subscribe to its EventTarget-based events from any framework adapter.\n *\n * Replaces all context providers, hooks and store files across React, Preact,\n * Solid, Svelte, and Vue integrations.\n */\nexport class EditorStateManager {\n readonly messenger: CrossFrameMessenger;\n readonly editorEnabled: CrossFrameStateManager<boolean>;\n readonly focusedContent: CrossFrameStateManager<FileContent | null>;\n readonly localeDictionaries: CrossFrameStateManager<DictionaryContent>;\n readonly editedContent: CrossFrameStateManager<DictionaryContent>;\n readonly configuration: CrossFrameStateManager<IntlayerConfig>;\n readonly currentLocale: CrossFrameStateManager<Locale | undefined>;\n\n private readonly _urlManager: UrlStateManager;\n private readonly _iframeInterceptor: IframeClickInterceptor;\n private readonly _mode: 'editor' | 'client';\n private readonly _configuration: IntlayerConfig | undefined;\n\n // Client-mode handshake subscribers\n private _unsubAreYouThere: (() => void) | null = null;\n private _unsubActivate: (() => void) | null = null;\n // Editor-mode handshake subscriber\n private _unsubClientReady: (() => void) | null = null;\n\n constructor(config: EditorStateManagerConfig) {\n this._mode = config.mode;\n this._configuration = config.configuration;\n\n this.messenger = new CrossFrameMessenger(config.messenger);\n\n this.editorEnabled = new CrossFrameStateManager<boolean>(\n MessageKey.INTLAYER_EDITOR_ENABLED,\n this.messenger,\n { emit: false, receive: true, initialValue: false }\n );\n\n this.focusedContent = new CrossFrameStateManager<FileContent | null>(\n MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED,\n this.messenger,\n { emit: true, receive: true, initialValue: null }\n );\n\n this.localeDictionaries = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED,\n this.messenger\n );\n\n this.editedContent = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_EDITED_CONTENT_CHANGED,\n this.messenger\n );\n\n this.configuration = new CrossFrameStateManager<IntlayerConfig>(\n MessageKey.INTLAYER_CONFIGURATION,\n this.messenger,\n {\n emit: true,\n receive: false,\n ...(config.configuration ? { initialValue: config.configuration } : {}),\n }\n );\n\n // Client emits its locale to the editor; editor receives it.\n this.currentLocale = new CrossFrameStateManager<Locale>(\n MessageKey.INTLAYER_CURRENT_LOCALE,\n this.messenger,\n {\n emit: config.mode === 'client',\n receive: config.mode === 'editor',\n }\n );\n\n this._urlManager = new UrlStateManager(this.messenger);\n this._iframeInterceptor = new IframeClickInterceptor(this.messenger);\n }\n\n start(): void {\n this.messenger.start();\n this.editorEnabled.start();\n this.focusedContent.start();\n this.localeDictionaries.start();\n this.editedContent.start();\n this.configuration.start();\n this.currentLocale.start();\n\n if (this._mode === 'client') {\n this._urlManager.start();\n this._iframeInterceptor.startInterceptor();\n this._loadDictionaries();\n // Request current edited content from the editor\n this.messenger.send(`${MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);\n // Activation handshake: only participate if editor.enabled !== false\n if (this._configuration?.editor?.enabled !== false) {\n this._setupActivationHandshake();\n }\n } else {\n this._iframeInterceptor.startMerger();\n this._setupEditorHandshake();\n }\n }\n\n stop(): void {\n this._unsubAreYouThere?.();\n this._unsubActivate?.();\n this._unsubClientReady?.();\n this._unsubAreYouThere = null;\n this._unsubActivate = null;\n this._unsubClientReady = null;\n this.messenger.stop();\n this.editorEnabled.stop();\n this.focusedContent.stop();\n this.localeDictionaries.stop();\n this.editedContent.stop();\n this.configuration.stop();\n this.currentLocale.stop();\n this._urlManager.stop();\n this._iframeInterceptor.stopInterceptor();\n this._iframeInterceptor.stopMerger();\n }\n\n // ─── Handshake helpers ───────────────────────────────────────────────────────\n\n /**\n * EDITOR mode: re-send ARE_YOU_THERE to attempt re-connection with the client.\n * Call this when the user clicks \"Enable Editor\" or when the iframe reloads.\n */\n pingClient(): void {\n if (this._mode !== 'editor') return;\n\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n // ─── Focus helpers ──────────────────────────────────────────────────────────\n\n setFocusedContentKeyPath(keyPath: KeyPath[]): void {\n const filtered = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n const prev = this.focusedContent.value;\n\n if (!prev) return;\n\n this.focusedContent.set({ ...prev, keyPath: filtered });\n }\n\n // ─── Dictionary record helpers ───────────────────────────────────────────\n\n setLocaleDictionary(dictionary: Dictionary): void {\n if (!dictionary.localId) return;\n const current = this.localeDictionaries.value ?? {};\n\n this.localeDictionaries.set({\n ...current,\n [dictionary.localId as LocalDictionaryId]: dictionary,\n });\n }\n\n // ─── Edited content helpers ───────────────────────────────────────────────\n\n setEditedDictionary(newDict: Dictionary): void {\n if (!newDict.localId) {\n console.error('setEditedDictionary: missing localId', newDict);\n return;\n }\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [newDict.localId as LocalDictionaryId]: newDict,\n });\n }\n\n setEditedContent(\n localDictionaryId: LocalDictionaryId,\n newValue: Dictionary['content']\n ): void {\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: newValue,\n },\n });\n }\n\n addContent(\n localDictionaryId: LocalDictionaryId,\n newValue: ContentNode,\n keyPath: KeyPath[] = [],\n overwrite = true\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n\n let newKeyPath = keyPath;\n if (!overwrite) {\n let index = 0;\n\n const otherKeyPath = keyPath.slice(0, -1);\n const lastKeyPath = keyPath[keyPath.length - 1];\n\n let finalKey = lastKeyPath.key;\n\n while (\n typeof getContentNodeByKeyPath(currentContent, newKeyPath) !==\n 'undefined'\n ) {\n index++;\n finalKey =\n index === 0 ? lastKeyPath.key : `${lastKeyPath.key} (${index})`;\n newKeyPath = [\n ...otherKeyPath,\n { ...lastKeyPath, key: finalKey } as KeyPath,\n ];\n }\n }\n\n const updatedContent = editDictionaryByKeyPath(\n currentContent,\n newKeyPath,\n newValue\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updatedContent as Dictionary['content'],\n },\n });\n }\n\n renameContent(\n localDictionaryId: LocalDictionaryId,\n newKey: KeyPath['key'],\n keyPath: KeyPath[] = []\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const updated = renameContentNodeByKeyPath(currentContent, newKey, keyPath);\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updated as Dictionary['content'],\n },\n });\n }\n\n removeContent(\n localDictionaryId: LocalDictionaryId,\n keyPath: KeyPath[]\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const initialContent = getContentNodeByKeyPath(originalContent, keyPath);\n const restored = editDictionaryByKeyPath(\n currentContent,\n keyPath,\n initialContent\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: restored as Dictionary['content'],\n },\n });\n }\n\n restoreContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const updated = { ...current };\n\n delete updated[localDictionaryId];\n\n this.editedContent.set(updated);\n }\n\n clearContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const filtered = { ...current };\n\n delete filtered[localDictionaryId];\n\n this.editedContent.set(filtered);\n }\n\n clearAllContent(): void {\n this.editedContent.set({});\n }\n\n getContentValue(\n localDictionaryIdOrKey: LocalDictionaryId | string,\n keyPath: KeyPath[]\n ): ContentNode | undefined {\n const edited = this.editedContent.value;\n if (!edited) return undefined;\n\n const filteredKeyPath = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n\n // Only use edited content entries whose localId is known to this client.\n // This prevents stale edits from other apps (different framework demos) from\n // being applied when the editor sends back its stored editedContent.\n const localeDicts = this.localeDictionaries.value;\n\n const isDictionaryId =\n localDictionaryIdOrKey.includes(':local:') ||\n localDictionaryIdOrKey.includes(':remote:');\n\n if (isDictionaryId) {\n // If localeDictionaries is loaded, verify this localId belongs to us\n if (localeDicts && !(localDictionaryIdOrKey in localeDicts)) {\n return undefined;\n }\n const content =\n edited[localDictionaryIdOrKey as LocalDictionaryId]?.content ?? {};\n\n return getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n }\n\n const matchingIds = Object.keys(edited).filter(\n (key) =>\n key.startsWith(`${localDictionaryIdOrKey}:`) &&\n // If localeDictionaries is loaded, only include known localIds\n (!localeDicts || key in localeDicts)\n );\n\n for (const localId of matchingIds) {\n const content = edited[localId as LocalDictionaryId]?.content ?? {};\n const node = getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n if (node) return node;\n }\n\n return undefined;\n }\n\n /**\n * EDITOR mode: listen for CLIENT_READY and respond with EDITOR_ACTIVATE.\n * Also pings the client immediately in case it loaded before the editor.\n */\n private _setupEditorHandshake(): void {\n // When the client announces it is ready, activate it\n this._unsubClientReady = this.messenger.subscribe(\n MessageKey.INTLAYER_CLIENT_READY,\n () => {\n this.editorEnabled.set(true);\n this.messenger.send(MessageKey.INTLAYER_EDITOR_ACTIVATE);\n }\n );\n\n // Ping any already-running client (covers editor-opens-after-client scenario)\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n private _setupActivationHandshake(): void {\n // Announce to the editor that the client is ready\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n\n // Respond to \"are you there?\" pings from the editor\n this._unsubAreYouThere = this.messenger.subscribe(\n MessageKey.INTLAYER_ARE_YOU_THERE,\n () => {\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n }\n );\n\n // When the editor activates us, enable the selector and broadcast state\n this._unsubActivate = this.messenger.subscribe(\n MessageKey.INTLAYER_EDITOR_ACTIVATE,\n () => {\n this.editorEnabled.set(true);\n this._broadcastData();\n }\n );\n }\n\n private _broadcastData(): void {\n const configVal = this.configuration.value;\n\n if (configVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CONFIGURATION}/post`,\n configVal\n );\n }\n const localeVal = this.currentLocale.value;\n\n if (localeVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CURRENT_LOCALE}/post`,\n localeVal\n );\n }\n const dicts = this.localeDictionaries.value;\n\n if (dicts) {\n this.messenger.send(\n `${MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`,\n dicts\n );\n }\n }\n\n private async _loadDictionaries(): Promise<void> {\n try {\n const mod = await import('@intlayer/unmerged-dictionaries-entry');\n const unmergedDictionaries = mod.getUnmergedDictionaries();\n const dictionariesList = Object.fromEntries(\n Object.values(unmergedDictionaries)\n .flat()\n .map((dictionary) => [dictionary.localId, dictionary])\n ) as DictionaryContent;\n\n this.localeDictionaries.set(dictionariesList);\n\n // If the editor already activated us before dictionaries finished loading,\n // re-broadcast now so the editor receives the dictionaries.\n if (this.editorEnabled.value) {\n this._broadcastData();\n }\n } catch (e) {\n // Dynamic entry not available (expected in editor mode or when not configured)\n console.warn('[intlayer] Failed to load unmerged dictionaries:', e);\n }\n }\n}\n"],"mappings":"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"}
@@ -21,13 +21,13 @@ var IframeClickInterceptor = class {
21
21
  startInterceptor() {
22
22
  if (typeof window === "undefined") return;
23
23
  this._mousedownHandler = () => {
24
- this._messenger.send(require_messageKey.MessageKey.INTLAYER_IFRAME_CLICKED);
24
+ this._messenger.send("INTLAYER_IFRAME_CLICKED");
25
25
  };
26
26
  window.addEventListener("mousedown", this._mousedownHandler);
27
27
  }
28
28
  /** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */
29
29
  startMerger() {
30
- this._unsubscribeMerge = this._messenger.subscribe(require_messageKey.MessageKey.INTLAYER_IFRAME_CLICKED, require_mergeIframeClick.mergeIframeClick);
30
+ this._unsubscribeMerge = this._messenger.subscribe("INTLAYER_IFRAME_CLICKED", require_mergeIframeClick.mergeIframeClick);
31
31
  }
32
32
  stopInterceptor() {
33
33
  if (this._mousedownHandler) {
@@ -1 +1 @@
1
- {"version":3,"file":"IframeClickInterceptor.cjs","names":["MessageKey","mergeIframeClick"],"sources":["../../../src/core/IframeClickInterceptor.ts"],"sourcesContent":["import { mergeIframeClick } from '../mergeIframeClick';\nimport { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * IframeClickInterceptor handles click events across iframe boundaries.\n *\n * - startInterceptor(): called in the client (iframe) — broadcasts mousedown to parent\n * - startMerger(): called in the editor (parent) — merges received clicks into DOM events\n *\n * Replaces useIframeClickInterceptor / useIframeClickMerger across all frameworks.\n */\nexport class IframeClickInterceptor {\n private readonly _messenger: CrossFrameMessenger;\n private _mousedownHandler: EventListener | null = null;\n private _unsubscribeMerge: (() => void) | null = null;\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n /** Called on the client side (inside iframe). Broadcasts click events to parent. */\n startInterceptor(): void {\n if (typeof window === 'undefined') return;\n this._mousedownHandler = () => {\n this._messenger.send(MessageKey.INTLAYER_IFRAME_CLICKED);\n };\n window.addEventListener('mousedown', this._mousedownHandler);\n }\n\n /** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */\n startMerger(): void {\n this._unsubscribeMerge = this._messenger.subscribe(\n MessageKey.INTLAYER_IFRAME_CLICKED,\n mergeIframeClick as (data: unknown) => void\n );\n }\n\n stopInterceptor(): void {\n if (this._mousedownHandler) {\n window.removeEventListener('mousedown', this._mousedownHandler);\n this._mousedownHandler = null;\n }\n }\n\n stopMerger(): void {\n this._unsubscribeMerge?.();\n this._unsubscribeMerge = null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,IAAa,yBAAb,MAAoC;CAKlC,YAAY,WAAgC;2BAHM;2BACD;AAG/C,OAAK,aAAa;;;CAIpB,mBAAyB;AACvB,MAAI,OAAO,WAAW,YAAa;AACnC,OAAK,0BAA0B;AAC7B,QAAK,WAAW,KAAKA,8BAAW,wBAAwB;;AAE1D,SAAO,iBAAiB,aAAa,KAAK,kBAAkB;;;CAI9D,cAAoB;AAClB,OAAK,oBAAoB,KAAK,WAAW,UACvCA,8BAAW,yBACXC,0CACD;;CAGH,kBAAwB;AACtB,MAAI,KAAK,mBAAmB;AAC1B,UAAO,oBAAoB,aAAa,KAAK,kBAAkB;AAC/D,QAAK,oBAAoB;;;CAI7B,aAAmB;AACjB,OAAK,qBAAqB;AAC1B,OAAK,oBAAoB"}
1
+ {"version":3,"file":"IframeClickInterceptor.cjs","names":["mergeIframeClick"],"sources":["../../../src/core/IframeClickInterceptor.ts"],"sourcesContent":["import { mergeIframeClick } from '../mergeIframeClick';\nimport { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * IframeClickInterceptor handles click events across iframe boundaries.\n *\n * - startInterceptor(): called in the client (iframe) — broadcasts mousedown to parent\n * - startMerger(): called in the editor (parent) — merges received clicks into DOM events\n *\n * Replaces useIframeClickInterceptor / useIframeClickMerger across all frameworks.\n */\nexport class IframeClickInterceptor {\n private readonly _messenger: CrossFrameMessenger;\n private _mousedownHandler: EventListener | null = null;\n private _unsubscribeMerge: (() => void) | null = null;\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n /** Called on the client side (inside iframe). Broadcasts click events to parent. */\n startInterceptor(): void {\n if (typeof window === 'undefined') return;\n this._mousedownHandler = () => {\n this._messenger.send(MessageKey.INTLAYER_IFRAME_CLICKED);\n };\n window.addEventListener('mousedown', this._mousedownHandler);\n }\n\n /** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */\n startMerger(): void {\n this._unsubscribeMerge = this._messenger.subscribe(\n MessageKey.INTLAYER_IFRAME_CLICKED,\n mergeIframeClick as (data: unknown) => void\n );\n }\n\n stopInterceptor(): void {\n if (this._mousedownHandler) {\n window.removeEventListener('mousedown', this._mousedownHandler);\n this._mousedownHandler = null;\n }\n }\n\n stopMerger(): void {\n this._unsubscribeMerge?.();\n this._unsubscribeMerge = null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,IAAa,yBAAb,MAAoC;CAKlC,YAAY,WAAgC;2BAHM;2BACD;AAG/C,OAAK,aAAa;;;CAIpB,mBAAyB;AACvB,MAAI,OAAO,WAAW,YAAa;AACnC,OAAK,0BAA0B;AAC7B,QAAK,WAAW,+BAAwC;;AAE1D,SAAO,iBAAiB,aAAa,KAAK,kBAAkB;;;CAI9D,cAAoB;AAClB,OAAK,oBAAoB,KAAK,WAAW,qCAEvCA,0CACD;;CAGH,kBAAwB;AACtB,MAAI,KAAK,mBAAmB;AAC1B,UAAO,oBAAoB,aAAa,KAAK,kBAAkB;AAC/D,QAAK,oBAAoB;;;CAI7B,aAAmB;AACjB,OAAK,qBAAqB;AAC1B,OAAK,oBAAoB"}
@@ -18,7 +18,7 @@ var UrlStateManager = class {
18
18
  start() {
19
19
  if (typeof window === "undefined") return;
20
20
  const updateURLState = () => {
21
- this._messenger.send(`${require_messageKey.MessageKey.INTLAYER_URL_CHANGE}/post`, window.location.pathname);
21
+ this._messenger.send(`${"INTLAYER_URL_CHANGE"}/post`, window.location.pathname);
22
22
  };
23
23
  this._originalPushState = history.pushState;
24
24
  this._originalReplaceState = history.replaceState;
@@ -1 +1 @@
1
- {"version":3,"file":"UrlStateManager.cjs","names":["MessageKey"],"sources":["../../../src/core/UrlStateManager.ts"],"sourcesContent":["import { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * UrlStateManager patches window.history and broadcasts URL path changes\n * across frames via postMessage.\n *\n * Replaces useCrossURLPathSetter / useCrossURLPathState across all frameworks.\n */\nexport class UrlStateManager {\n private readonly _messenger: CrossFrameMessenger;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n private _listeners: Array<[string, EventListener]> = [];\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n start(): void {\n if (typeof window === 'undefined') return;\n\n const updateURLState = () => {\n this._messenger.send(\n `${MessageKey.INTLAYER_URL_CHANGE}/post`,\n window.location.pathname\n );\n };\n\n this._originalPushState = history.pushState;\n this._originalReplaceState = history.replaceState;\n\n const injectLocationChange = (method: typeof history.pushState) =>\n function (\n this: typeof history,\n ...args: Parameters<typeof history.pushState>\n ) {\n method.apply(this, args);\n window.dispatchEvent(new Event('locationchange'));\n };\n\n history.pushState = injectLocationChange(\n this._originalPushState\n ) as typeof history.pushState;\n history.replaceState = injectLocationChange(\n this._originalReplaceState\n ) as typeof history.replaceState;\n\n for (const eventName of [\n 'locationchange',\n 'popstate',\n 'hashchange',\n 'load',\n ] as const) {\n const listener = updateURLState as EventListener;\n window.addEventListener(eventName, listener);\n this._listeners.push([eventName, listener]);\n }\n\n updateURLState();\n }\n\n stop(): void {\n if (typeof window === 'undefined') return;\n\n for (const [eventName, listener] of this._listeners) {\n window.removeEventListener(eventName, listener);\n }\n this._listeners = [];\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n}\n"],"mappings":";;;;;;;;;;AASA,IAAa,kBAAb,MAA6B;CAM3B,YAAY,WAAgC;4BAJkB;+BACM;oBACf,EAAE;AAGrD,OAAK,aAAa;;CAGpB,QAAc;AACZ,MAAI,OAAO,WAAW,YAAa;EAEnC,MAAM,uBAAuB;AAC3B,QAAK,WAAW,KACd,GAAGA,8BAAW,oBAAoB,QAClC,OAAO,SAAS,SACjB;;AAGH,OAAK,qBAAqB,QAAQ;AAClC,OAAK,wBAAwB,QAAQ;EAErC,MAAM,wBAAwB,WAC5B,SAEE,GAAG,MACH;AACA,UAAO,MAAM,MAAM,KAAK;AACxB,UAAO,cAAc,IAAI,MAAM,iBAAiB,CAAC;;AAGrD,UAAQ,YAAY,qBAClB,KAAK,mBACN;AACD,UAAQ,eAAe,qBACrB,KAAK,sBACN;AAED,OAAK,MAAM,aAAa;GACtB;GACA;GACA;GACA;GACD,EAAW;GACV,MAAM,WAAW;AACjB,UAAO,iBAAiB,WAAW,SAAS;AAC5C,QAAK,WAAW,KAAK,CAAC,WAAW,SAAS,CAAC;;AAG7C,kBAAgB;;CAGlB,OAAa;AACX,MAAI,OAAO,WAAW,YAAa;AAEnC,OAAK,MAAM,CAAC,WAAW,aAAa,KAAK,WACvC,QAAO,oBAAoB,WAAW,SAAS;AAEjD,OAAK,aAAa,EAAE;AAEpB,MAAI,KAAK,oBAAoB;AAC3B,WAAQ,YAAY,KAAK;AACzB,QAAK,qBAAqB;;AAE5B,MAAI,KAAK,uBAAuB;AAC9B,WAAQ,eAAe,KAAK;AAC5B,QAAK,wBAAwB"}
1
+ {"version":3,"file":"UrlStateManager.cjs","names":[],"sources":["../../../src/core/UrlStateManager.ts"],"sourcesContent":["import { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * UrlStateManager patches window.history and broadcasts URL path changes\n * across frames via postMessage.\n *\n * Replaces useCrossURLPathSetter / useCrossURLPathState across all frameworks.\n */\nexport class UrlStateManager {\n private readonly _messenger: CrossFrameMessenger;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n private _listeners: Array<[string, EventListener]> = [];\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n start(): void {\n if (typeof window === 'undefined') return;\n\n const updateURLState = () => {\n this._messenger.send(\n `${MessageKey.INTLAYER_URL_CHANGE}/post`,\n window.location.pathname\n );\n };\n\n this._originalPushState = history.pushState;\n this._originalReplaceState = history.replaceState;\n\n const injectLocationChange = (method: typeof history.pushState) =>\n function (\n this: typeof history,\n ...args: Parameters<typeof history.pushState>\n ) {\n method.apply(this, args);\n window.dispatchEvent(new Event('locationchange'));\n };\n\n history.pushState = injectLocationChange(\n this._originalPushState\n ) as typeof history.pushState;\n history.replaceState = injectLocationChange(\n this._originalReplaceState\n ) as typeof history.replaceState;\n\n for (const eventName of [\n 'locationchange',\n 'popstate',\n 'hashchange',\n 'load',\n ] as const) {\n const listener = updateURLState as EventListener;\n window.addEventListener(eventName, listener);\n this._listeners.push([eventName, listener]);\n }\n\n updateURLState();\n }\n\n stop(): void {\n if (typeof window === 'undefined') return;\n\n for (const [eventName, listener] of this._listeners) {\n window.removeEventListener(eventName, listener);\n }\n this._listeners = [];\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n}\n"],"mappings":";;;;;;;;;;AASA,IAAa,kBAAb,MAA6B;CAM3B,YAAY,WAAgC;4BAJkB;+BACM;oBACf,EAAE;AAGrD,OAAK,aAAa;;CAGpB,QAAc;AACZ,MAAI,OAAO,WAAW,YAAa;EAEnC,MAAM,uBAAuB;AAC3B,QAAK,WAAW,KACd,yBAAkC,QAClC,OAAO,SAAS,SACjB;;AAGH,OAAK,qBAAqB,QAAQ;AAClC,OAAK,wBAAwB,QAAQ;EAErC,MAAM,wBAAwB,WAC5B,SAEE,GAAG,MACH;AACA,UAAO,MAAM,MAAM,KAAK;AACxB,UAAO,cAAc,IAAI,MAAM,iBAAiB,CAAC;;AAGrD,UAAQ,YAAY,qBAClB,KAAK,mBACN;AACD,UAAQ,eAAe,qBACrB,KAAK,sBACN;AAED,OAAK,MAAM,aAAa;GACtB;GACA;GACA;GACA;GACD,EAAW;GACV,MAAM,WAAW;AACjB,UAAO,iBAAiB,WAAW,SAAS;AAC5C,QAAK,WAAW,KAAK,CAAC,WAAW,SAAS,CAAC;;AAG7C,kBAAgB;;CAGlB,OAAa;AACX,MAAI,OAAO,WAAW,YAAa;AAEnC,OAAK,MAAM,CAAC,WAAW,aAAa,KAAK,WACvC,QAAO,oBAAoB,WAAW,SAAS;AAEjD,OAAK,aAAa,EAAE;AAEpB,MAAI,KAAK,oBAAoB;AAC3B,WAAQ,YAAY,KAAK;AACzB,QAAK,qBAAqB;;AAE5B,MAAI,KAAK,uBAAuB;AAC9B,WAAQ,eAAe,KAAK;AAC5B,QAAK,wBAAwB"}
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.124.0/helpers/decorate.js
1
+ //#region \0@oxc-project+runtime@0.126.0/helpers/decorate.js
2
2
  function __decorate(decorators, target, key, desc) {
3
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1,4 +1,4 @@
1
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.mjs";
1
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.mjs";
2
2
  import { defineIntlayerContentSelectorWrapper } from "./ContentSelectorWrapper.mjs";
3
3
  import { defineIntlayerEditedContent } from "./EditedContent.mjs";
4
4
  import { defineIntlayerEditorElement } from "./IntlayerEditor.mjs";
@@ -1,6 +1,6 @@
1
1
  import { MessageKey } from "../messageKey.mjs";
2
2
  import { getGlobalEditorManager, onGlobalEditorManagerChange } from "../core/globalManager.mjs";
3
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.mjs";
3
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.mjs";
4
4
  import { LitElement, css, html } from "lit";
5
5
  import { property, state } from "lit/decorators.js";
6
6
  import { isSameKeyPath } from "@intlayer/core/utils";
@@ -153,14 +153,14 @@ var IntlayerContentSelectorWrapperElement = class extends LitElement {
153
153
  }
154
154
  _handleHover(e) {
155
155
  e.stopPropagation();
156
- getGlobalEditorManager()?.messenger.send(`${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`, {
156
+ getGlobalEditorManager()?.messenger.send(`${"INTLAYER_HOVERED_CONTENT_CHANGED"}/post`, {
157
157
  dictionaryKey: this.dictionaryKey,
158
158
  keyPath: this._getFilteredKeyPath()
159
159
  });
160
160
  }
161
161
  _handleUnhover(e) {
162
162
  e.stopPropagation();
163
- getGlobalEditorManager()?.messenger.send(`${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`, null);
163
+ getGlobalEditorManager()?.messenger.send(`${"INTLAYER_HOVERED_CONTENT_CHANGED"}/post`, null);
164
164
  }
165
165
  render() {
166
166
  if (!this._isInIframe || !this._editorEnabled) return html`<slot></slot>`;
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelectorWrapper.mjs","names":[],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { css, html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic Lit element that wraps content with the Intlayer editor\n * selection UI. It replaces the per-framework ContentSelectorWrapper components\n * (Vue, Svelte, Solid, Preact).\n *\n * It reads from the global EditorStateManager singleton (set by initEditorClient)\n * and conditionally renders <intlayer-content-selector> around its slot content\n * when the editor is active and the app is running inside an iframe.\n *\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} dictionary-key - The dictionary key owning this content node\n */\nexport class IntlayerContentSelectorWrapperElement extends LitElement {\n static styles = css`\n :host {\n display: contents;\n }\n `;\n\n @property({ type: String, attribute: 'key-path' }) keyPathJson = '[]';\n @property({ type: String, attribute: 'dictionary-key' }) dictionaryKey = '';\n\n @state() private _editorEnabled = false;\n @state() private _isInIframe = false;\n @state() private _isSelected = false;\n @state() private _editedValue: ContentNode | undefined = undefined;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEnabled: (() => void) | null = null;\n private _unsubFocused: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n connectedCallback(): void {\n super.connectedCallback();\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this._teardown();\n }\n\n updated(changedProperties: Map<string, unknown>): void {\n if (\n changedProperties.has('keyPathJson') ||\n changedProperties.has('dictionaryKey')\n ) {\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n // Keep listening for manager changes (handles stop + re-init cycles)\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editorEnabled = false;\n this._isSelected = false;\n this._editedValue = undefined;\n }\n });\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._editorEnabled = manager.editorEnabled.value ?? false;\n this._updateIsSelected(manager.focusedContent.value);\n this._updateEditedValue(manager);\n\n const handleEnabledChange = (e: Event) => {\n this._editorEnabled = (e as CustomEvent<boolean>).detail;\n };\n const handleFocusedChange = (e: Event) => {\n this._updateIsSelected((e as CustomEvent<FileContent | null>).detail);\n };\n const handleEditedContentChange = () => {\n this._updateEditedValue(manager);\n };\n\n manager.editorEnabled.addEventListener('change', handleEnabledChange);\n manager.focusedContent.addEventListener('change', handleFocusedChange);\n manager.editedContent.addEventListener('change', handleEditedContentChange);\n\n this._unsubEnabled = () =>\n manager.editorEnabled.removeEventListener('change', handleEnabledChange);\n this._unsubFocused = () =>\n manager.focusedContent.removeEventListener('change', handleFocusedChange);\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener(\n 'change',\n handleEditedContentChange\n );\n }\n\n private _getRawKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this.keyPathJson) as KeyPath[];\n } catch {\n return [];\n }\n }\n\n private _getFilteredKeyPath(): KeyPath[] {\n return this._getRawKeyPath().filter(\n (k) => k.type !== NodeTypes.TRANSLATION\n );\n }\n\n private _updateEditedValue(manager: EditorStateManager): void {\n const filteredKeyPath = this._getFilteredKeyPath();\n if (!this.dictionaryKey || filteredKeyPath.length === 0) {\n this._editedValue = undefined;\n return;\n }\n\n // Node types whose display requires framework-level rendering (markdown,\n // HTML, insertion, file): do not override the slot — the framework handles\n // those. Only plain / translated strings can be substituted here.\n const rawKeyPath = this._getRawKeyPath();\n const lastStepType = rawKeyPath[rawKeyPath.length - 1]?.type;\n if (\n lastStepType === NodeTypes.MARKDOWN ||\n lastStepType === NodeTypes.HTML ||\n lastStepType === NodeTypes.INSERTION ||\n lastStepType === NodeTypes.FILE\n ) {\n this._editedValue = undefined;\n return;\n }\n\n let value = manager.getContentValue(this.dictionaryKey, filteredKeyPath);\n\n // getContentNodeByKeyPath resolves translation nodes only at intermediate\n // steps, not the final leaf. Resolve manually when the returned value is\n // still a translation object (happens when Translation steps are filtered\n // out and the leaf IS the translation object).\n if (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n value.nodeType === NodeTypes.TRANSLATION\n ) {\n const locale = manager.currentLocale.value as string | undefined;\n // TypedNodeModel<Translation, …> structurally satisfies TypedNode<BaseNode>\n // (both have nodeType), so this narrowing cast is sound.\n const node = value as TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentNode>\n >;\n value = locale ? node[NodeTypes.TRANSLATION][locale] : undefined;\n }\n\n this._editedValue = value;\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n return;\n }\n const keyPath = this._getFilteredKeyPath();\n this._isSelected =\n focusedContent.dictionaryKey === this.dictionaryKey &&\n (focusedContent.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(focusedContent.keyPath ?? [], keyPath);\n }\n\n private _handlePress(e: Event): void {\n // Stop propagation so nested wrappers don't also fire (composed + bubbles)\n e.stopPropagation();\n const manager = getGlobalEditorManager();\n if (!manager) return;\n manager.focusedContent.set({\n dictionaryKey: this.dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n });\n }\n\n private _handleHover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n { dictionaryKey: this.dictionaryKey, keyPath: this._getFilteredKeyPath() }\n );\n }\n\n private _handleUnhover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n null\n );\n }\n\n render() {\n if (!this._isInIframe || !this._editorEnabled) {\n return html`<slot></slot>`;\n }\n\n const editedValue = this._editedValue;\n const displayedContent =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean'\n ? editedValue\n : html`<slot></slot>`;\n\n return html`\n <intlayer-content-selector\n ?is-selecting=${this._isSelected}\n @intlayer:press=${this._handlePress}\n @intlayer:hover=${this._handleHover}\n @intlayer:unhover=${this._handleUnhover}\n >\n ${displayedContent}\n </intlayer-content-selector>\n `;\n }\n}\n\nexport const defineIntlayerContentSelectorWrapper = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-content-selector-wrapper')) {\n customElements.define(\n 'intlayer-content-selector-wrapper',\n IntlayerContentSelectorWrapperElement\n );\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ContentSelectorWrapper.mjs","names":[],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { css, html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic Lit element that wraps content with the Intlayer editor\n * selection UI. It replaces the per-framework ContentSelectorWrapper components\n * (Vue, Svelte, Solid, Preact).\n *\n * It reads from the global EditorStateManager singleton (set by initEditorClient)\n * and conditionally renders <intlayer-content-selector> around its slot content\n * when the editor is active and the app is running inside an iframe.\n *\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} dictionary-key - The dictionary key owning this content node\n */\nexport class IntlayerContentSelectorWrapperElement extends LitElement {\n static styles = css`\n :host {\n display: contents;\n }\n `;\n\n @property({ type: String, attribute: 'key-path' }) keyPathJson = '[]';\n @property({ type: String, attribute: 'dictionary-key' }) dictionaryKey = '';\n\n @state() private _editorEnabled = false;\n @state() private _isInIframe = false;\n @state() private _isSelected = false;\n @state() private _editedValue: ContentNode | undefined = undefined;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEnabled: (() => void) | null = null;\n private _unsubFocused: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n connectedCallback(): void {\n super.connectedCallback();\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this._teardown();\n }\n\n updated(changedProperties: Map<string, unknown>): void {\n if (\n changedProperties.has('keyPathJson') ||\n changedProperties.has('dictionaryKey')\n ) {\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n // Keep listening for manager changes (handles stop + re-init cycles)\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editorEnabled = false;\n this._isSelected = false;\n this._editedValue = undefined;\n }\n });\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._editorEnabled = manager.editorEnabled.value ?? false;\n this._updateIsSelected(manager.focusedContent.value);\n this._updateEditedValue(manager);\n\n const handleEnabledChange = (e: Event) => {\n this._editorEnabled = (e as CustomEvent<boolean>).detail;\n };\n const handleFocusedChange = (e: Event) => {\n this._updateIsSelected((e as CustomEvent<FileContent | null>).detail);\n };\n const handleEditedContentChange = () => {\n this._updateEditedValue(manager);\n };\n\n manager.editorEnabled.addEventListener('change', handleEnabledChange);\n manager.focusedContent.addEventListener('change', handleFocusedChange);\n manager.editedContent.addEventListener('change', handleEditedContentChange);\n\n this._unsubEnabled = () =>\n manager.editorEnabled.removeEventListener('change', handleEnabledChange);\n this._unsubFocused = () =>\n manager.focusedContent.removeEventListener('change', handleFocusedChange);\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener(\n 'change',\n handleEditedContentChange\n );\n }\n\n private _getRawKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this.keyPathJson) as KeyPath[];\n } catch {\n return [];\n }\n }\n\n private _getFilteredKeyPath(): KeyPath[] {\n return this._getRawKeyPath().filter(\n (k) => k.type !== NodeTypes.TRANSLATION\n );\n }\n\n private _updateEditedValue(manager: EditorStateManager): void {\n const filteredKeyPath = this._getFilteredKeyPath();\n if (!this.dictionaryKey || filteredKeyPath.length === 0) {\n this._editedValue = undefined;\n return;\n }\n\n // Node types whose display requires framework-level rendering (markdown,\n // HTML, insertion, file): do not override the slot — the framework handles\n // those. Only plain / translated strings can be substituted here.\n const rawKeyPath = this._getRawKeyPath();\n const lastStepType = rawKeyPath[rawKeyPath.length - 1]?.type;\n if (\n lastStepType === NodeTypes.MARKDOWN ||\n lastStepType === NodeTypes.HTML ||\n lastStepType === NodeTypes.INSERTION ||\n lastStepType === NodeTypes.FILE\n ) {\n this._editedValue = undefined;\n return;\n }\n\n let value = manager.getContentValue(this.dictionaryKey, filteredKeyPath);\n\n // getContentNodeByKeyPath resolves translation nodes only at intermediate\n // steps, not the final leaf. Resolve manually when the returned value is\n // still a translation object (happens when Translation steps are filtered\n // out and the leaf IS the translation object).\n if (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n value.nodeType === NodeTypes.TRANSLATION\n ) {\n const locale = manager.currentLocale.value as string | undefined;\n // TypedNodeModel<Translation, …> structurally satisfies TypedNode<BaseNode>\n // (both have nodeType), so this narrowing cast is sound.\n const node = value as TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentNode>\n >;\n value = locale ? node[NodeTypes.TRANSLATION][locale] : undefined;\n }\n\n this._editedValue = value;\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n return;\n }\n const keyPath = this._getFilteredKeyPath();\n this._isSelected =\n focusedContent.dictionaryKey === this.dictionaryKey &&\n (focusedContent.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(focusedContent.keyPath ?? [], keyPath);\n }\n\n private _handlePress(e: Event): void {\n // Stop propagation so nested wrappers don't also fire (composed + bubbles)\n e.stopPropagation();\n const manager = getGlobalEditorManager();\n if (!manager) return;\n manager.focusedContent.set({\n dictionaryKey: this.dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n });\n }\n\n private _handleHover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n { dictionaryKey: this.dictionaryKey, keyPath: this._getFilteredKeyPath() }\n );\n }\n\n private _handleUnhover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n null\n );\n }\n\n render() {\n if (!this._isInIframe || !this._editorEnabled) {\n return html`<slot></slot>`;\n }\n\n const editedValue = this._editedValue;\n const displayedContent =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean'\n ? editedValue\n : html`<slot></slot>`;\n\n return html`\n <intlayer-content-selector\n ?is-selecting=${this._isSelected}\n @intlayer:press=${this._handlePress}\n @intlayer:hover=${this._handleHover}\n @intlayer:unhover=${this._handleUnhover}\n >\n ${displayedContent}\n </intlayer-content-selector>\n `;\n }\n}\n\nexport const defineIntlayerContentSelectorWrapper = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-content-selector-wrapper')) {\n customElements.define(\n 'intlayer-content-selector-wrapper',\n IntlayerContentSelectorWrapperElement\n );\n }\n};\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { getGlobalEditorManager, onGlobalEditorManagerChange } from "../core/globalManager.mjs";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.mjs";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.mjs";
3
3
  import { LitElement, css, html } from "lit";
4
4
  import { property, state } from "lit/decorators.js";
5
5
  import { getBasePlugins, getContent } from "@intlayer/core/interpreter";
@@ -1,5 +1,5 @@
1
1
  import { getGlobalEditorManager, onGlobalEditorManagerChange } from "../core/globalManager.mjs";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.mjs";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.mjs";
3
3
  import { initEditorClient, stopEditorClient } from "../core/initEditorClient.mjs";
4
4
  import { LitElement } from "lit";
5
5
  import { property } from "lit/decorators.js";
@@ -23,24 +23,24 @@ var EditorStateManager = class {
23
23
  this._mode = config.mode;
24
24
  this._configuration = config.configuration;
25
25
  this.messenger = new CrossFrameMessenger(config.messenger);
26
- this.editorEnabled = new CrossFrameStateManager(MessageKey.INTLAYER_EDITOR_ENABLED, this.messenger, {
26
+ this.editorEnabled = new CrossFrameStateManager("INTLAYER_EDITOR_ENABLED", this.messenger, {
27
27
  emit: false,
28
28
  receive: true,
29
29
  initialValue: false
30
30
  });
31
- this.focusedContent = new CrossFrameStateManager(MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED, this.messenger, {
31
+ this.focusedContent = new CrossFrameStateManager("INTLAYER_FOCUSED_CONTENT_CHANGED", this.messenger, {
32
32
  emit: true,
33
33
  receive: true,
34
34
  initialValue: null
35
35
  });
36
- this.localeDictionaries = new CrossFrameStateManager(MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED, this.messenger);
37
- this.editedContent = new CrossFrameStateManager(MessageKey.INTLAYER_EDITED_CONTENT_CHANGED, this.messenger);
38
- this.configuration = new CrossFrameStateManager(MessageKey.INTLAYER_CONFIGURATION, this.messenger, {
36
+ this.localeDictionaries = new CrossFrameStateManager("INTLAYER_LOCALE_DICTIONARIES_CHANGED", this.messenger);
37
+ this.editedContent = new CrossFrameStateManager("INTLAYER_EDITED_CONTENT_CHANGED", this.messenger);
38
+ this.configuration = new CrossFrameStateManager("INTLAYER_CONFIGURATION", this.messenger, {
39
39
  emit: true,
40
40
  receive: false,
41
41
  ...config.configuration ? { initialValue: config.configuration } : {}
42
42
  });
43
- this.currentLocale = new CrossFrameStateManager(MessageKey.INTLAYER_CURRENT_LOCALE, this.messenger, {
43
+ this.currentLocale = new CrossFrameStateManager("INTLAYER_CURRENT_LOCALE", this.messenger, {
44
44
  emit: config.mode === "client",
45
45
  receive: config.mode === "editor"
46
46
  });
@@ -59,7 +59,7 @@ var EditorStateManager = class {
59
59
  this._urlManager.start();
60
60
  this._iframeInterceptor.startInterceptor();
61
61
  this._loadDictionaries();
62
- this.messenger.send(`${MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);
62
+ this.messenger.send(`${"INTLAYER_EDITED_CONTENT_CHANGED"}/get`);
63
63
  if (this._configuration?.editor?.enabled !== false) this._setupActivationHandshake();
64
64
  } else {
65
65
  this._iframeInterceptor.startMerger();
@@ -90,7 +90,7 @@ var EditorStateManager = class {
90
90
  */
91
91
  pingClient() {
92
92
  if (this._mode !== "editor") return;
93
- this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);
93
+ this.messenger.send("INTLAYER_ARE_YOU_THERE");
94
94
  }
95
95
  setFocusedContentKeyPath(keyPath) {
96
96
  const filtered = keyPath.filter((key) => key.type !== NodeTypes.TRANSLATION);
@@ -215,29 +215,29 @@ var EditorStateManager = class {
215
215
  * Also pings the client immediately in case it loaded before the editor.
216
216
  */
217
217
  _setupEditorHandshake() {
218
- this._unsubClientReady = this.messenger.subscribe(MessageKey.INTLAYER_CLIENT_READY, () => {
218
+ this._unsubClientReady = this.messenger.subscribe("INTLAYER_CLIENT_READY", () => {
219
219
  this.editorEnabled.set(true);
220
- this.messenger.send(MessageKey.INTLAYER_EDITOR_ACTIVATE);
220
+ this.messenger.send("INTLAYER_EDITOR_ACTIVATE");
221
221
  });
222
- this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);
222
+ this.messenger.send("INTLAYER_ARE_YOU_THERE");
223
223
  }
224
224
  _setupActivationHandshake() {
225
- this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);
226
- this._unsubAreYouThere = this.messenger.subscribe(MessageKey.INTLAYER_ARE_YOU_THERE, () => {
227
- this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);
225
+ this.messenger.send("INTLAYER_CLIENT_READY");
226
+ this._unsubAreYouThere = this.messenger.subscribe("INTLAYER_ARE_YOU_THERE", () => {
227
+ this.messenger.send("INTLAYER_CLIENT_READY");
228
228
  });
229
- this._unsubActivate = this.messenger.subscribe(MessageKey.INTLAYER_EDITOR_ACTIVATE, () => {
229
+ this._unsubActivate = this.messenger.subscribe("INTLAYER_EDITOR_ACTIVATE", () => {
230
230
  this.editorEnabled.set(true);
231
231
  this._broadcastData();
232
232
  });
233
233
  }
234
234
  _broadcastData() {
235
235
  const configVal = this.configuration.value;
236
- if (configVal) this.messenger.send(`${MessageKey.INTLAYER_CONFIGURATION}/post`, configVal);
236
+ if (configVal) this.messenger.send(`${"INTLAYER_CONFIGURATION"}/post`, configVal);
237
237
  const localeVal = this.currentLocale.value;
238
- if (localeVal) this.messenger.send(`${MessageKey.INTLAYER_CURRENT_LOCALE}/post`, localeVal);
238
+ if (localeVal) this.messenger.send(`${"INTLAYER_CURRENT_LOCALE"}/post`, localeVal);
239
239
  const dicts = this.localeDictionaries.value;
240
- if (dicts) this.messenger.send(`${MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`, dicts);
240
+ if (dicts) this.messenger.send(`${"INTLAYER_LOCALE_DICTIONARIES_CHANGED"}/post`, dicts);
241
241
  }
242
242
  async _loadDictionaries() {
243
243
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"EditorStateManager.mjs","names":[],"sources":["../../../src/core/EditorStateManager.ts"],"sourcesContent":["import {\n editDictionaryByKeyPath,\n getContentNodeByKeyPath,\n renameContentNodeByKeyPath,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n ContentNode,\n Dictionary,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { MessageKey } from '../messageKey';\nimport {\n CrossFrameMessenger,\n type MessengerConfig,\n} from './CrossFrameMessenger';\nimport { CrossFrameStateManager } from './CrossFrameStateManager';\nimport { IframeClickInterceptor } from './IframeClickInterceptor';\nimport { UrlStateManager } from './UrlStateManager';\n\nexport type DictionaryContent = Record<LocalDictionaryId, Dictionary>;\n\nexport type FileContent = {\n dictionaryKey: string;\n dictionaryLocalId?: LocalDictionaryId;\n keyPath?: KeyPath[];\n};\n\nexport type EditorStateManagerConfig = {\n /** 'client' = the app running inside the iframe; 'editor' = the editor wrapping the app */\n mode: 'editor' | 'client';\n /** Cross-frame messaging configuration */\n messenger: MessengerConfig;\n /** Optional initial Intlayer configuration to broadcast */\n configuration?: IntlayerConfig;\n};\n\n/**\n * EditorStateManager is the single entry point for all Intlayer editor state.\n * It is framework-agnostic: instantiate one instance at the root of the application\n * and subscribe to its EventTarget-based events from any framework adapter.\n *\n * Replaces all context providers, hooks and store files across React, Preact,\n * Solid, Svelte, and Vue integrations.\n */\nexport class EditorStateManager {\n readonly messenger: CrossFrameMessenger;\n readonly editorEnabled: CrossFrameStateManager<boolean>;\n readonly focusedContent: CrossFrameStateManager<FileContent | null>;\n readonly localeDictionaries: CrossFrameStateManager<DictionaryContent>;\n readonly editedContent: CrossFrameStateManager<DictionaryContent>;\n readonly configuration: CrossFrameStateManager<IntlayerConfig>;\n readonly currentLocale: CrossFrameStateManager<Locale | undefined>;\n\n private readonly _urlManager: UrlStateManager;\n private readonly _iframeInterceptor: IframeClickInterceptor;\n private readonly _mode: 'editor' | 'client';\n private readonly _configuration: IntlayerConfig | undefined;\n\n // Client-mode handshake subscribers\n private _unsubAreYouThere: (() => void) | null = null;\n private _unsubActivate: (() => void) | null = null;\n // Editor-mode handshake subscriber\n private _unsubClientReady: (() => void) | null = null;\n\n constructor(config: EditorStateManagerConfig) {\n this._mode = config.mode;\n this._configuration = config.configuration;\n\n this.messenger = new CrossFrameMessenger(config.messenger);\n\n this.editorEnabled = new CrossFrameStateManager<boolean>(\n MessageKey.INTLAYER_EDITOR_ENABLED,\n this.messenger,\n { emit: false, receive: true, initialValue: false }\n );\n\n this.focusedContent = new CrossFrameStateManager<FileContent | null>(\n MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED,\n this.messenger,\n { emit: true, receive: true, initialValue: null }\n );\n\n this.localeDictionaries = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED,\n this.messenger\n );\n\n this.editedContent = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_EDITED_CONTENT_CHANGED,\n this.messenger\n );\n\n this.configuration = new CrossFrameStateManager<IntlayerConfig>(\n MessageKey.INTLAYER_CONFIGURATION,\n this.messenger,\n {\n emit: true,\n receive: false,\n ...(config.configuration ? { initialValue: config.configuration } : {}),\n }\n );\n\n // Client emits its locale to the editor; editor receives it.\n this.currentLocale = new CrossFrameStateManager<Locale>(\n MessageKey.INTLAYER_CURRENT_LOCALE,\n this.messenger,\n {\n emit: config.mode === 'client',\n receive: config.mode === 'editor',\n }\n );\n\n this._urlManager = new UrlStateManager(this.messenger);\n this._iframeInterceptor = new IframeClickInterceptor(this.messenger);\n }\n\n start(): void {\n this.messenger.start();\n this.editorEnabled.start();\n this.focusedContent.start();\n this.localeDictionaries.start();\n this.editedContent.start();\n this.configuration.start();\n this.currentLocale.start();\n\n if (this._mode === 'client') {\n this._urlManager.start();\n this._iframeInterceptor.startInterceptor();\n this._loadDictionaries();\n // Request current edited content from the editor\n this.messenger.send(`${MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);\n // Activation handshake: only participate if editor.enabled !== false\n if (this._configuration?.editor?.enabled !== false) {\n this._setupActivationHandshake();\n }\n } else {\n this._iframeInterceptor.startMerger();\n this._setupEditorHandshake();\n }\n }\n\n stop(): void {\n this._unsubAreYouThere?.();\n this._unsubActivate?.();\n this._unsubClientReady?.();\n this._unsubAreYouThere = null;\n this._unsubActivate = null;\n this._unsubClientReady = null;\n this.messenger.stop();\n this.editorEnabled.stop();\n this.focusedContent.stop();\n this.localeDictionaries.stop();\n this.editedContent.stop();\n this.configuration.stop();\n this.currentLocale.stop();\n this._urlManager.stop();\n this._iframeInterceptor.stopInterceptor();\n this._iframeInterceptor.stopMerger();\n }\n\n // ─── Handshake helpers ───────────────────────────────────────────────────────\n\n /**\n * EDITOR mode: re-send ARE_YOU_THERE to attempt re-connection with the client.\n * Call this when the user clicks \"Enable Editor\" or when the iframe reloads.\n */\n pingClient(): void {\n if (this._mode !== 'editor') return;\n\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n // ─── Focus helpers ──────────────────────────────────────────────────────────\n\n setFocusedContentKeyPath(keyPath: KeyPath[]): void {\n const filtered = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n const prev = this.focusedContent.value;\n\n if (!prev) return;\n\n this.focusedContent.set({ ...prev, keyPath: filtered });\n }\n\n // ─── Dictionary record helpers ───────────────────────────────────────────\n\n setLocaleDictionary(dictionary: Dictionary): void {\n if (!dictionary.localId) return;\n const current = this.localeDictionaries.value ?? {};\n\n this.localeDictionaries.set({\n ...current,\n [dictionary.localId as LocalDictionaryId]: dictionary,\n });\n }\n\n // ─── Edited content helpers ───────────────────────────────────────────────\n\n setEditedDictionary(newDict: Dictionary): void {\n if (!newDict.localId) {\n console.error('setEditedDictionary: missing localId', newDict);\n return;\n }\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [newDict.localId as LocalDictionaryId]: newDict,\n });\n }\n\n setEditedContent(\n localDictionaryId: LocalDictionaryId,\n newValue: Dictionary['content']\n ): void {\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: newValue,\n },\n });\n }\n\n addContent(\n localDictionaryId: LocalDictionaryId,\n newValue: ContentNode,\n keyPath: KeyPath[] = [],\n overwrite = true\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n\n let newKeyPath = keyPath;\n if (!overwrite) {\n let index = 0;\n\n const otherKeyPath = keyPath.slice(0, -1);\n const lastKeyPath = keyPath[keyPath.length - 1];\n\n let finalKey = lastKeyPath.key;\n\n while (\n typeof getContentNodeByKeyPath(currentContent, newKeyPath) !==\n 'undefined'\n ) {\n index++;\n finalKey =\n index === 0 ? lastKeyPath.key : `${lastKeyPath.key} (${index})`;\n newKeyPath = [\n ...otherKeyPath,\n { ...lastKeyPath, key: finalKey } as KeyPath,\n ];\n }\n }\n\n const updatedContent = editDictionaryByKeyPath(\n currentContent,\n newKeyPath,\n newValue\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updatedContent as Dictionary['content'],\n },\n });\n }\n\n renameContent(\n localDictionaryId: LocalDictionaryId,\n newKey: KeyPath['key'],\n keyPath: KeyPath[] = []\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const updated = renameContentNodeByKeyPath(currentContent, newKey, keyPath);\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updated as Dictionary['content'],\n },\n });\n }\n\n removeContent(\n localDictionaryId: LocalDictionaryId,\n keyPath: KeyPath[]\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const initialContent = getContentNodeByKeyPath(originalContent, keyPath);\n const restored = editDictionaryByKeyPath(\n currentContent,\n keyPath,\n initialContent\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: restored as Dictionary['content'],\n },\n });\n }\n\n restoreContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const updated = { ...current };\n\n delete updated[localDictionaryId];\n\n this.editedContent.set(updated);\n }\n\n clearContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const filtered = { ...current };\n\n delete filtered[localDictionaryId];\n\n this.editedContent.set(filtered);\n }\n\n clearAllContent(): void {\n this.editedContent.set({});\n }\n\n getContentValue(\n localDictionaryIdOrKey: LocalDictionaryId | string,\n keyPath: KeyPath[]\n ): ContentNode | undefined {\n const edited = this.editedContent.value;\n if (!edited) return undefined;\n\n const filteredKeyPath = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n\n // Only use edited content entries whose localId is known to this client.\n // This prevents stale edits from other apps (different framework demos) from\n // being applied when the editor sends back its stored editedContent.\n const localeDicts = this.localeDictionaries.value;\n\n const isDictionaryId =\n localDictionaryIdOrKey.includes(':local:') ||\n localDictionaryIdOrKey.includes(':remote:');\n\n if (isDictionaryId) {\n // If localeDictionaries is loaded, verify this localId belongs to us\n if (localeDicts && !(localDictionaryIdOrKey in localeDicts)) {\n return undefined;\n }\n const content =\n edited[localDictionaryIdOrKey as LocalDictionaryId]?.content ?? {};\n\n return getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n }\n\n const matchingIds = Object.keys(edited).filter(\n (key) =>\n key.startsWith(`${localDictionaryIdOrKey}:`) &&\n // If localeDictionaries is loaded, only include known localIds\n (!localeDicts || key in localeDicts)\n );\n\n for (const localId of matchingIds) {\n const content = edited[localId as LocalDictionaryId]?.content ?? {};\n const node = getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n if (node) return node;\n }\n\n return undefined;\n }\n\n /**\n * EDITOR mode: listen for CLIENT_READY and respond with EDITOR_ACTIVATE.\n * Also pings the client immediately in case it loaded before the editor.\n */\n private _setupEditorHandshake(): void {\n // When the client announces it is ready, activate it\n this._unsubClientReady = this.messenger.subscribe(\n MessageKey.INTLAYER_CLIENT_READY,\n () => {\n this.editorEnabled.set(true);\n this.messenger.send(MessageKey.INTLAYER_EDITOR_ACTIVATE);\n }\n );\n\n // Ping any already-running client (covers editor-opens-after-client scenario)\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n private _setupActivationHandshake(): void {\n // Announce to the editor that the client is ready\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n\n // Respond to \"are you there?\" pings from the editor\n this._unsubAreYouThere = this.messenger.subscribe(\n MessageKey.INTLAYER_ARE_YOU_THERE,\n () => {\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n }\n );\n\n // When the editor activates us, enable the selector and broadcast state\n this._unsubActivate = this.messenger.subscribe(\n MessageKey.INTLAYER_EDITOR_ACTIVATE,\n () => {\n this.editorEnabled.set(true);\n this._broadcastData();\n }\n );\n }\n\n private _broadcastData(): void {\n const configVal = this.configuration.value;\n\n if (configVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CONFIGURATION}/post`,\n configVal\n );\n }\n const localeVal = this.currentLocale.value;\n\n if (localeVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CURRENT_LOCALE}/post`,\n localeVal\n );\n }\n const dicts = this.localeDictionaries.value;\n\n if (dicts) {\n this.messenger.send(\n `${MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`,\n dicts\n );\n }\n }\n\n private async _loadDictionaries(): Promise<void> {\n try {\n const mod = await import('@intlayer/unmerged-dictionaries-entry');\n const unmergedDictionaries = mod.getUnmergedDictionaries();\n const dictionariesList = Object.fromEntries(\n Object.values(unmergedDictionaries)\n .flat()\n .map((dictionary) => [dictionary.localId, dictionary])\n ) as DictionaryContent;\n\n this.localeDictionaries.set(dictionariesList);\n\n // If the editor already activated us before dictionaries finished loading,\n // re-broadcast now so the editor receives the dictionaries.\n if (this.editorEnabled.value) {\n this._broadcastData();\n }\n } catch (e) {\n // Dynamic entry not available (expected in editor mode or when not configured)\n console.warn('[intlayer] Failed to load unmerged dictionaries:', e);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EditorStateManager.mjs","names":[],"sources":["../../../src/core/EditorStateManager.ts"],"sourcesContent":["import {\n editDictionaryByKeyPath,\n getContentNodeByKeyPath,\n renameContentNodeByKeyPath,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n ContentNode,\n Dictionary,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { MessageKey } from '../messageKey';\nimport {\n CrossFrameMessenger,\n type MessengerConfig,\n} from './CrossFrameMessenger';\nimport { CrossFrameStateManager } from './CrossFrameStateManager';\nimport { IframeClickInterceptor } from './IframeClickInterceptor';\nimport { UrlStateManager } from './UrlStateManager';\n\nexport type DictionaryContent = Record<LocalDictionaryId, Dictionary>;\n\nexport type FileContent = {\n dictionaryKey: string;\n dictionaryLocalId?: LocalDictionaryId;\n keyPath?: KeyPath[];\n};\n\nexport type EditorStateManagerConfig = {\n /** 'client' = the app running inside the iframe; 'editor' = the editor wrapping the app */\n mode: 'editor' | 'client';\n /** Cross-frame messaging configuration */\n messenger: MessengerConfig;\n /** Optional initial Intlayer configuration to broadcast */\n configuration?: IntlayerConfig;\n};\n\n/**\n * EditorStateManager is the single entry point for all Intlayer editor state.\n * It is framework-agnostic: instantiate one instance at the root of the application\n * and subscribe to its EventTarget-based events from any framework adapter.\n *\n * Replaces all context providers, hooks and store files across React, Preact,\n * Solid, Svelte, and Vue integrations.\n */\nexport class EditorStateManager {\n readonly messenger: CrossFrameMessenger;\n readonly editorEnabled: CrossFrameStateManager<boolean>;\n readonly focusedContent: CrossFrameStateManager<FileContent | null>;\n readonly localeDictionaries: CrossFrameStateManager<DictionaryContent>;\n readonly editedContent: CrossFrameStateManager<DictionaryContent>;\n readonly configuration: CrossFrameStateManager<IntlayerConfig>;\n readonly currentLocale: CrossFrameStateManager<Locale | undefined>;\n\n private readonly _urlManager: UrlStateManager;\n private readonly _iframeInterceptor: IframeClickInterceptor;\n private readonly _mode: 'editor' | 'client';\n private readonly _configuration: IntlayerConfig | undefined;\n\n // Client-mode handshake subscribers\n private _unsubAreYouThere: (() => void) | null = null;\n private _unsubActivate: (() => void) | null = null;\n // Editor-mode handshake subscriber\n private _unsubClientReady: (() => void) | null = null;\n\n constructor(config: EditorStateManagerConfig) {\n this._mode = config.mode;\n this._configuration = config.configuration;\n\n this.messenger = new CrossFrameMessenger(config.messenger);\n\n this.editorEnabled = new CrossFrameStateManager<boolean>(\n MessageKey.INTLAYER_EDITOR_ENABLED,\n this.messenger,\n { emit: false, receive: true, initialValue: false }\n );\n\n this.focusedContent = new CrossFrameStateManager<FileContent | null>(\n MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED,\n this.messenger,\n { emit: true, receive: true, initialValue: null }\n );\n\n this.localeDictionaries = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED,\n this.messenger\n );\n\n this.editedContent = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_EDITED_CONTENT_CHANGED,\n this.messenger\n );\n\n this.configuration = new CrossFrameStateManager<IntlayerConfig>(\n MessageKey.INTLAYER_CONFIGURATION,\n this.messenger,\n {\n emit: true,\n receive: false,\n ...(config.configuration ? { initialValue: config.configuration } : {}),\n }\n );\n\n // Client emits its locale to the editor; editor receives it.\n this.currentLocale = new CrossFrameStateManager<Locale>(\n MessageKey.INTLAYER_CURRENT_LOCALE,\n this.messenger,\n {\n emit: config.mode === 'client',\n receive: config.mode === 'editor',\n }\n );\n\n this._urlManager = new UrlStateManager(this.messenger);\n this._iframeInterceptor = new IframeClickInterceptor(this.messenger);\n }\n\n start(): void {\n this.messenger.start();\n this.editorEnabled.start();\n this.focusedContent.start();\n this.localeDictionaries.start();\n this.editedContent.start();\n this.configuration.start();\n this.currentLocale.start();\n\n if (this._mode === 'client') {\n this._urlManager.start();\n this._iframeInterceptor.startInterceptor();\n this._loadDictionaries();\n // Request current edited content from the editor\n this.messenger.send(`${MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);\n // Activation handshake: only participate if editor.enabled !== false\n if (this._configuration?.editor?.enabled !== false) {\n this._setupActivationHandshake();\n }\n } else {\n this._iframeInterceptor.startMerger();\n this._setupEditorHandshake();\n }\n }\n\n stop(): void {\n this._unsubAreYouThere?.();\n this._unsubActivate?.();\n this._unsubClientReady?.();\n this._unsubAreYouThere = null;\n this._unsubActivate = null;\n this._unsubClientReady = null;\n this.messenger.stop();\n this.editorEnabled.stop();\n this.focusedContent.stop();\n this.localeDictionaries.stop();\n this.editedContent.stop();\n this.configuration.stop();\n this.currentLocale.stop();\n this._urlManager.stop();\n this._iframeInterceptor.stopInterceptor();\n this._iframeInterceptor.stopMerger();\n }\n\n // ─── Handshake helpers ───────────────────────────────────────────────────────\n\n /**\n * EDITOR mode: re-send ARE_YOU_THERE to attempt re-connection with the client.\n * Call this when the user clicks \"Enable Editor\" or when the iframe reloads.\n */\n pingClient(): void {\n if (this._mode !== 'editor') return;\n\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n // ─── Focus helpers ──────────────────────────────────────────────────────────\n\n setFocusedContentKeyPath(keyPath: KeyPath[]): void {\n const filtered = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n const prev = this.focusedContent.value;\n\n if (!prev) return;\n\n this.focusedContent.set({ ...prev, keyPath: filtered });\n }\n\n // ─── Dictionary record helpers ───────────────────────────────────────────\n\n setLocaleDictionary(dictionary: Dictionary): void {\n if (!dictionary.localId) return;\n const current = this.localeDictionaries.value ?? {};\n\n this.localeDictionaries.set({\n ...current,\n [dictionary.localId as LocalDictionaryId]: dictionary,\n });\n }\n\n // ─── Edited content helpers ───────────────────────────────────────────────\n\n setEditedDictionary(newDict: Dictionary): void {\n if (!newDict.localId) {\n console.error('setEditedDictionary: missing localId', newDict);\n return;\n }\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [newDict.localId as LocalDictionaryId]: newDict,\n });\n }\n\n setEditedContent(\n localDictionaryId: LocalDictionaryId,\n newValue: Dictionary['content']\n ): void {\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: newValue,\n },\n });\n }\n\n addContent(\n localDictionaryId: LocalDictionaryId,\n newValue: ContentNode,\n keyPath: KeyPath[] = [],\n overwrite = true\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n\n let newKeyPath = keyPath;\n if (!overwrite) {\n let index = 0;\n\n const otherKeyPath = keyPath.slice(0, -1);\n const lastKeyPath = keyPath[keyPath.length - 1];\n\n let finalKey = lastKeyPath.key;\n\n while (\n typeof getContentNodeByKeyPath(currentContent, newKeyPath) !==\n 'undefined'\n ) {\n index++;\n finalKey =\n index === 0 ? lastKeyPath.key : `${lastKeyPath.key} (${index})`;\n newKeyPath = [\n ...otherKeyPath,\n { ...lastKeyPath, key: finalKey } as KeyPath,\n ];\n }\n }\n\n const updatedContent = editDictionaryByKeyPath(\n currentContent,\n newKeyPath,\n newValue\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updatedContent as Dictionary['content'],\n },\n });\n }\n\n renameContent(\n localDictionaryId: LocalDictionaryId,\n newKey: KeyPath['key'],\n keyPath: KeyPath[] = []\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const updated = renameContentNodeByKeyPath(currentContent, newKey, keyPath);\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updated as Dictionary['content'],\n },\n });\n }\n\n removeContent(\n localDictionaryId: LocalDictionaryId,\n keyPath: KeyPath[]\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const initialContent = getContentNodeByKeyPath(originalContent, keyPath);\n const restored = editDictionaryByKeyPath(\n currentContent,\n keyPath,\n initialContent\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: restored as Dictionary['content'],\n },\n });\n }\n\n restoreContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const updated = { ...current };\n\n delete updated[localDictionaryId];\n\n this.editedContent.set(updated);\n }\n\n clearContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const filtered = { ...current };\n\n delete filtered[localDictionaryId];\n\n this.editedContent.set(filtered);\n }\n\n clearAllContent(): void {\n this.editedContent.set({});\n }\n\n getContentValue(\n localDictionaryIdOrKey: LocalDictionaryId | string,\n keyPath: KeyPath[]\n ): ContentNode | undefined {\n const edited = this.editedContent.value;\n if (!edited) return undefined;\n\n const filteredKeyPath = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n\n // Only use edited content entries whose localId is known to this client.\n // This prevents stale edits from other apps (different framework demos) from\n // being applied when the editor sends back its stored editedContent.\n const localeDicts = this.localeDictionaries.value;\n\n const isDictionaryId =\n localDictionaryIdOrKey.includes(':local:') ||\n localDictionaryIdOrKey.includes(':remote:');\n\n if (isDictionaryId) {\n // If localeDictionaries is loaded, verify this localId belongs to us\n if (localeDicts && !(localDictionaryIdOrKey in localeDicts)) {\n return undefined;\n }\n const content =\n edited[localDictionaryIdOrKey as LocalDictionaryId]?.content ?? {};\n\n return getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n }\n\n const matchingIds = Object.keys(edited).filter(\n (key) =>\n key.startsWith(`${localDictionaryIdOrKey}:`) &&\n // If localeDictionaries is loaded, only include known localIds\n (!localeDicts || key in localeDicts)\n );\n\n for (const localId of matchingIds) {\n const content = edited[localId as LocalDictionaryId]?.content ?? {};\n const node = getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n if (node) return node;\n }\n\n return undefined;\n }\n\n /**\n * EDITOR mode: listen for CLIENT_READY and respond with EDITOR_ACTIVATE.\n * Also pings the client immediately in case it loaded before the editor.\n */\n private _setupEditorHandshake(): void {\n // When the client announces it is ready, activate it\n this._unsubClientReady = this.messenger.subscribe(\n MessageKey.INTLAYER_CLIENT_READY,\n () => {\n this.editorEnabled.set(true);\n this.messenger.send(MessageKey.INTLAYER_EDITOR_ACTIVATE);\n }\n );\n\n // Ping any already-running client (covers editor-opens-after-client scenario)\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n private _setupActivationHandshake(): void {\n // Announce to the editor that the client is ready\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n\n // Respond to \"are you there?\" pings from the editor\n this._unsubAreYouThere = this.messenger.subscribe(\n MessageKey.INTLAYER_ARE_YOU_THERE,\n () => {\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n }\n );\n\n // When the editor activates us, enable the selector and broadcast state\n this._unsubActivate = this.messenger.subscribe(\n MessageKey.INTLAYER_EDITOR_ACTIVATE,\n () => {\n this.editorEnabled.set(true);\n this._broadcastData();\n }\n );\n }\n\n private _broadcastData(): void {\n const configVal = this.configuration.value;\n\n if (configVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CONFIGURATION}/post`,\n configVal\n );\n }\n const localeVal = this.currentLocale.value;\n\n if (localeVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CURRENT_LOCALE}/post`,\n localeVal\n );\n }\n const dicts = this.localeDictionaries.value;\n\n if (dicts) {\n this.messenger.send(\n `${MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`,\n dicts\n );\n }\n }\n\n private async _loadDictionaries(): Promise<void> {\n try {\n const mod = await import('@intlayer/unmerged-dictionaries-entry');\n const unmergedDictionaries = mod.getUnmergedDictionaries();\n const dictionariesList = Object.fromEntries(\n Object.values(unmergedDictionaries)\n .flat()\n .map((dictionary) => [dictionary.localId, dictionary])\n ) as DictionaryContent;\n\n this.localeDictionaries.set(dictionariesList);\n\n // If the editor already activated us before dictionaries finished loading,\n // re-broadcast now so the editor receives the dictionaries.\n if (this.editorEnabled.value) {\n this._broadcastData();\n }\n } catch (e) {\n // Dynamic entry not available (expected in editor mode or when not configured)\n console.warn('[intlayer] Failed to load unmerged dictionaries:', e);\n }\n }\n}\n"],"mappings":"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"}
@@ -20,13 +20,13 @@ var IframeClickInterceptor = class {
20
20
  startInterceptor() {
21
21
  if (typeof window === "undefined") return;
22
22
  this._mousedownHandler = () => {
23
- this._messenger.send(MessageKey.INTLAYER_IFRAME_CLICKED);
23
+ this._messenger.send("INTLAYER_IFRAME_CLICKED");
24
24
  };
25
25
  window.addEventListener("mousedown", this._mousedownHandler);
26
26
  }
27
27
  /** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */
28
28
  startMerger() {
29
- this._unsubscribeMerge = this._messenger.subscribe(MessageKey.INTLAYER_IFRAME_CLICKED, mergeIframeClick);
29
+ this._unsubscribeMerge = this._messenger.subscribe("INTLAYER_IFRAME_CLICKED", mergeIframeClick);
30
30
  }
31
31
  stopInterceptor() {
32
32
  if (this._mousedownHandler) {
@@ -1 +1 @@
1
- {"version":3,"file":"IframeClickInterceptor.mjs","names":[],"sources":["../../../src/core/IframeClickInterceptor.ts"],"sourcesContent":["import { mergeIframeClick } from '../mergeIframeClick';\nimport { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * IframeClickInterceptor handles click events across iframe boundaries.\n *\n * - startInterceptor(): called in the client (iframe) — broadcasts mousedown to parent\n * - startMerger(): called in the editor (parent) — merges received clicks into DOM events\n *\n * Replaces useIframeClickInterceptor / useIframeClickMerger across all frameworks.\n */\nexport class IframeClickInterceptor {\n private readonly _messenger: CrossFrameMessenger;\n private _mousedownHandler: EventListener | null = null;\n private _unsubscribeMerge: (() => void) | null = null;\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n /** Called on the client side (inside iframe). Broadcasts click events to parent. */\n startInterceptor(): void {\n if (typeof window === 'undefined') return;\n this._mousedownHandler = () => {\n this._messenger.send(MessageKey.INTLAYER_IFRAME_CLICKED);\n };\n window.addEventListener('mousedown', this._mousedownHandler);\n }\n\n /** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */\n startMerger(): void {\n this._unsubscribeMerge = this._messenger.subscribe(\n MessageKey.INTLAYER_IFRAME_CLICKED,\n mergeIframeClick as (data: unknown) => void\n );\n }\n\n stopInterceptor(): void {\n if (this._mousedownHandler) {\n window.removeEventListener('mousedown', this._mousedownHandler);\n this._mousedownHandler = null;\n }\n }\n\n stopMerger(): void {\n this._unsubscribeMerge?.();\n this._unsubscribeMerge = null;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,IAAa,yBAAb,MAAoC;CAKlC,YAAY,WAAgC;2BAHM;2BACD;AAG/C,OAAK,aAAa;;;CAIpB,mBAAyB;AACvB,MAAI,OAAO,WAAW,YAAa;AACnC,OAAK,0BAA0B;AAC7B,QAAK,WAAW,KAAK,WAAW,wBAAwB;;AAE1D,SAAO,iBAAiB,aAAa,KAAK,kBAAkB;;;CAI9D,cAAoB;AAClB,OAAK,oBAAoB,KAAK,WAAW,UACvC,WAAW,yBACX,iBACD;;CAGH,kBAAwB;AACtB,MAAI,KAAK,mBAAmB;AAC1B,UAAO,oBAAoB,aAAa,KAAK,kBAAkB;AAC/D,QAAK,oBAAoB;;;CAI7B,aAAmB;AACjB,OAAK,qBAAqB;AAC1B,OAAK,oBAAoB"}
1
+ {"version":3,"file":"IframeClickInterceptor.mjs","names":[],"sources":["../../../src/core/IframeClickInterceptor.ts"],"sourcesContent":["import { mergeIframeClick } from '../mergeIframeClick';\nimport { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * IframeClickInterceptor handles click events across iframe boundaries.\n *\n * - startInterceptor(): called in the client (iframe) — broadcasts mousedown to parent\n * - startMerger(): called in the editor (parent) — merges received clicks into DOM events\n *\n * Replaces useIframeClickInterceptor / useIframeClickMerger across all frameworks.\n */\nexport class IframeClickInterceptor {\n private readonly _messenger: CrossFrameMessenger;\n private _mousedownHandler: EventListener | null = null;\n private _unsubscribeMerge: (() => void) | null = null;\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n /** Called on the client side (inside iframe). Broadcasts click events to parent. */\n startInterceptor(): void {\n if (typeof window === 'undefined') return;\n this._mousedownHandler = () => {\n this._messenger.send(MessageKey.INTLAYER_IFRAME_CLICKED);\n };\n window.addEventListener('mousedown', this._mousedownHandler);\n }\n\n /** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */\n startMerger(): void {\n this._unsubscribeMerge = this._messenger.subscribe(\n MessageKey.INTLAYER_IFRAME_CLICKED,\n mergeIframeClick as (data: unknown) => void\n );\n }\n\n stopInterceptor(): void {\n if (this._mousedownHandler) {\n window.removeEventListener('mousedown', this._mousedownHandler);\n this._mousedownHandler = null;\n }\n }\n\n stopMerger(): void {\n this._unsubscribeMerge?.();\n this._unsubscribeMerge = null;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,IAAa,yBAAb,MAAoC;CAKlC,YAAY,WAAgC;2BAHM;2BACD;AAG/C,OAAK,aAAa;;;CAIpB,mBAAyB;AACvB,MAAI,OAAO,WAAW,YAAa;AACnC,OAAK,0BAA0B;AAC7B,QAAK,WAAW,+BAAwC;;AAE1D,SAAO,iBAAiB,aAAa,KAAK,kBAAkB;;;CAI9D,cAAoB;AAClB,OAAK,oBAAoB,KAAK,WAAW,qCAEvC,iBACD;;CAGH,kBAAwB;AACtB,MAAI,KAAK,mBAAmB;AAC1B,UAAO,oBAAoB,aAAa,KAAK,kBAAkB;AAC/D,QAAK,oBAAoB;;;CAI7B,aAAmB;AACjB,OAAK,qBAAqB;AAC1B,OAAK,oBAAoB"}
@@ -17,7 +17,7 @@ var UrlStateManager = class {
17
17
  start() {
18
18
  if (typeof window === "undefined") return;
19
19
  const updateURLState = () => {
20
- this._messenger.send(`${MessageKey.INTLAYER_URL_CHANGE}/post`, window.location.pathname);
20
+ this._messenger.send(`${"INTLAYER_URL_CHANGE"}/post`, window.location.pathname);
21
21
  };
22
22
  this._originalPushState = history.pushState;
23
23
  this._originalReplaceState = history.replaceState;
@@ -1 +1 @@
1
- {"version":3,"file":"UrlStateManager.mjs","names":[],"sources":["../../../src/core/UrlStateManager.ts"],"sourcesContent":["import { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * UrlStateManager patches window.history and broadcasts URL path changes\n * across frames via postMessage.\n *\n * Replaces useCrossURLPathSetter / useCrossURLPathState across all frameworks.\n */\nexport class UrlStateManager {\n private readonly _messenger: CrossFrameMessenger;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n private _listeners: Array<[string, EventListener]> = [];\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n start(): void {\n if (typeof window === 'undefined') return;\n\n const updateURLState = () => {\n this._messenger.send(\n `${MessageKey.INTLAYER_URL_CHANGE}/post`,\n window.location.pathname\n );\n };\n\n this._originalPushState = history.pushState;\n this._originalReplaceState = history.replaceState;\n\n const injectLocationChange = (method: typeof history.pushState) =>\n function (\n this: typeof history,\n ...args: Parameters<typeof history.pushState>\n ) {\n method.apply(this, args);\n window.dispatchEvent(new Event('locationchange'));\n };\n\n history.pushState = injectLocationChange(\n this._originalPushState\n ) as typeof history.pushState;\n history.replaceState = injectLocationChange(\n this._originalReplaceState\n ) as typeof history.replaceState;\n\n for (const eventName of [\n 'locationchange',\n 'popstate',\n 'hashchange',\n 'load',\n ] as const) {\n const listener = updateURLState as EventListener;\n window.addEventListener(eventName, listener);\n this._listeners.push([eventName, listener]);\n }\n\n updateURLState();\n }\n\n stop(): void {\n if (typeof window === 'undefined') return;\n\n for (const [eventName, listener] of this._listeners) {\n window.removeEventListener(eventName, listener);\n }\n this._listeners = [];\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n}\n"],"mappings":";;;;;;;;;AASA,IAAa,kBAAb,MAA6B;CAM3B,YAAY,WAAgC;4BAJkB;+BACM;oBACf,EAAE;AAGrD,OAAK,aAAa;;CAGpB,QAAc;AACZ,MAAI,OAAO,WAAW,YAAa;EAEnC,MAAM,uBAAuB;AAC3B,QAAK,WAAW,KACd,GAAG,WAAW,oBAAoB,QAClC,OAAO,SAAS,SACjB;;AAGH,OAAK,qBAAqB,QAAQ;AAClC,OAAK,wBAAwB,QAAQ;EAErC,MAAM,wBAAwB,WAC5B,SAEE,GAAG,MACH;AACA,UAAO,MAAM,MAAM,KAAK;AACxB,UAAO,cAAc,IAAI,MAAM,iBAAiB,CAAC;;AAGrD,UAAQ,YAAY,qBAClB,KAAK,mBACN;AACD,UAAQ,eAAe,qBACrB,KAAK,sBACN;AAED,OAAK,MAAM,aAAa;GACtB;GACA;GACA;GACA;GACD,EAAW;GACV,MAAM,WAAW;AACjB,UAAO,iBAAiB,WAAW,SAAS;AAC5C,QAAK,WAAW,KAAK,CAAC,WAAW,SAAS,CAAC;;AAG7C,kBAAgB;;CAGlB,OAAa;AACX,MAAI,OAAO,WAAW,YAAa;AAEnC,OAAK,MAAM,CAAC,WAAW,aAAa,KAAK,WACvC,QAAO,oBAAoB,WAAW,SAAS;AAEjD,OAAK,aAAa,EAAE;AAEpB,MAAI,KAAK,oBAAoB;AAC3B,WAAQ,YAAY,KAAK;AACzB,QAAK,qBAAqB;;AAE5B,MAAI,KAAK,uBAAuB;AAC9B,WAAQ,eAAe,KAAK;AAC5B,QAAK,wBAAwB"}
1
+ {"version":3,"file":"UrlStateManager.mjs","names":[],"sources":["../../../src/core/UrlStateManager.ts"],"sourcesContent":["import { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * UrlStateManager patches window.history and broadcasts URL path changes\n * across frames via postMessage.\n *\n * Replaces useCrossURLPathSetter / useCrossURLPathState across all frameworks.\n */\nexport class UrlStateManager {\n private readonly _messenger: CrossFrameMessenger;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n private _listeners: Array<[string, EventListener]> = [];\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n start(): void {\n if (typeof window === 'undefined') return;\n\n const updateURLState = () => {\n this._messenger.send(\n `${MessageKey.INTLAYER_URL_CHANGE}/post`,\n window.location.pathname\n );\n };\n\n this._originalPushState = history.pushState;\n this._originalReplaceState = history.replaceState;\n\n const injectLocationChange = (method: typeof history.pushState) =>\n function (\n this: typeof history,\n ...args: Parameters<typeof history.pushState>\n ) {\n method.apply(this, args);\n window.dispatchEvent(new Event('locationchange'));\n };\n\n history.pushState = injectLocationChange(\n this._originalPushState\n ) as typeof history.pushState;\n history.replaceState = injectLocationChange(\n this._originalReplaceState\n ) as typeof history.replaceState;\n\n for (const eventName of [\n 'locationchange',\n 'popstate',\n 'hashchange',\n 'load',\n ] as const) {\n const listener = updateURLState as EventListener;\n window.addEventListener(eventName, listener);\n this._listeners.push([eventName, listener]);\n }\n\n updateURLState();\n }\n\n stop(): void {\n if (typeof window === 'undefined') return;\n\n for (const [eventName, listener] of this._listeners) {\n window.removeEventListener(eventName, listener);\n }\n this._listeners = [];\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n}\n"],"mappings":";;;;;;;;;AASA,IAAa,kBAAb,MAA6B;CAM3B,YAAY,WAAgC;4BAJkB;+BACM;oBACf,EAAE;AAGrD,OAAK,aAAa;;CAGpB,QAAc;AACZ,MAAI,OAAO,WAAW,YAAa;EAEnC,MAAM,uBAAuB;AAC3B,QAAK,WAAW,KACd,yBAAkC,QAClC,OAAO,SAAS,SACjB;;AAGH,OAAK,qBAAqB,QAAQ;AAClC,OAAK,wBAAwB,QAAQ;EAErC,MAAM,wBAAwB,WAC5B,SAEE,GAAG,MACH;AACA,UAAO,MAAM,MAAM,KAAK;AACxB,UAAO,cAAc,IAAI,MAAM,iBAAiB,CAAC;;AAGrD,UAAQ,YAAY,qBAClB,KAAK,mBACN;AACD,UAAQ,eAAe,qBACrB,KAAK,sBACN;AAED,OAAK,MAAM,aAAa;GACtB;GACA;GACA;GACA;GACD,EAAW;GACV,MAAM,WAAW;AACjB,UAAO,iBAAiB,WAAW,SAAS;AAC5C,QAAK,WAAW,KAAK,CAAC,WAAW,SAAS,CAAC;;AAG7C,kBAAgB;;CAGlB,OAAa;AACX,MAAI,OAAO,WAAW,YAAa;AAEnC,OAAK,MAAM,CAAC,WAAW,aAAa,KAAK,WACvC,QAAO,oBAAoB,WAAW,SAAS;AAEjD,OAAK,aAAa,EAAE;AAEpB,MAAI,KAAK,oBAAoB;AAC3B,WAAQ,YAAY,KAAK;AACzB,QAAK,qBAAqB;;AAE5B,MAAI,KAAK,uBAAuB;AAC9B,WAAQ,eAAe,KAAK;AAC5B,QAAK,wBAAwB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/editor",
3
- "version": "8.7.3",
3
+ "version": "8.7.4",
4
4
  "private": false,
5
5
  "description": "Provides the utilities to interface the application with the Intlayer editor and manipulate dictionaries",
6
6
  "keywords": [
@@ -75,20 +75,20 @@
75
75
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
76
76
  },
77
77
  "dependencies": {
78
- "@intlayer/config": "8.7.3",
79
- "@intlayer/core": "8.7.3",
80
- "@intlayer/types": "8.7.3",
81
- "@intlayer/unmerged-dictionaries-entry": "8.7.3",
78
+ "@intlayer/config": "8.7.4",
79
+ "@intlayer/core": "8.7.4",
80
+ "@intlayer/types": "8.7.4",
81
+ "@intlayer/unmerged-dictionaries-entry": "8.7.4",
82
82
  "lit": "^3.3.2"
83
83
  },
84
84
  "devDependencies": {
85
- "@types/node": "25.5.2",
85
+ "@types/node": "25.6.0",
86
86
  "@utils/ts-config": "1.0.4",
87
87
  "@utils/ts-config-types": "1.0.4",
88
88
  "@utils/tsdown-config": "1.0.4",
89
89
  "rimraf": "6.1.3",
90
- "tsdown": "0.21.8",
91
- "typescript": "6.0.2",
90
+ "tsdown": "0.21.9",
91
+ "typescript": "6.0.3",
92
92
  "vitest": "4.1.4"
93
93
  },
94
94
  "engines": {