@intlayer/editor 8.9.1 → 8.9.3

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 (29) hide show
  1. package/dist/cjs/compareUrls.cjs.map +1 -1
  2. package/dist/cjs/components/ContentSelector.cjs.map +1 -1
  3. package/dist/cjs/components/ContentSelectorWrapper.cjs.map +1 -1
  4. package/dist/cjs/components/EditedContent.cjs.map +1 -1
  5. package/dist/cjs/components/IntlayerEditor.cjs.map +1 -1
  6. package/dist/cjs/core/CrossFrameMessenger.cjs.map +1 -1
  7. package/dist/cjs/core/CrossFrameStateManager.cjs.map +1 -1
  8. package/dist/cjs/core/EditorStateManager.cjs.map +1 -1
  9. package/dist/cjs/core/IframeClickInterceptor.cjs.map +1 -1
  10. package/dist/cjs/core/UrlStateManager.cjs.map +1 -1
  11. package/dist/cjs/core/globalManager.cjs.map +1 -1
  12. package/dist/cjs/core/initEditorClient.cjs.map +1 -1
  13. package/dist/cjs/mergeIframeClick.cjs.map +1 -1
  14. package/dist/cjs/messageKey.cjs.map +1 -1
  15. package/dist/esm/compareUrls.mjs.map +1 -1
  16. package/dist/esm/components/ContentSelector.mjs.map +1 -1
  17. package/dist/esm/components/ContentSelectorWrapper.mjs.map +1 -1
  18. package/dist/esm/components/EditedContent.mjs.map +1 -1
  19. package/dist/esm/components/IntlayerEditor.mjs.map +1 -1
  20. package/dist/esm/core/CrossFrameMessenger.mjs.map +1 -1
  21. package/dist/esm/core/CrossFrameStateManager.mjs.map +1 -1
  22. package/dist/esm/core/EditorStateManager.mjs.map +1 -1
  23. package/dist/esm/core/IframeClickInterceptor.mjs.map +1 -1
  24. package/dist/esm/core/UrlStateManager.mjs.map +1 -1
  25. package/dist/esm/core/globalManager.mjs.map +1 -1
  26. package/dist/esm/core/initEditorClient.mjs.map +1 -1
  27. package/dist/esm/mergeIframeClick.mjs.map +1 -1
  28. package/dist/esm/messageKey.mjs.map +1 -1
  29. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"compareUrls.cjs","names":[],"sources":["../../src/compareUrls.ts"],"sourcesContent":["/**\n * Compare two URLs for equality.\n * This function is used to determine if a message originates from the same origin.\n *\n * ```js\n * // Example usage\n * console.log(compareUrls(\"http://localhost:5173/\", \"http://localhost:5173\")); // true\n * console.log(compareUrls(\"http://localhost:5173\", \"http://localhost:5173?myParam=true\")); // true\n * console.log(compareUrls(\"http://localhost:5173/subpath\", \"http://localhost:5173\")); // true\n * console.log(compareUrls(\"http://localhost:5172\", \"http://localhost:5173\")); // false\n * ```\n *\n * @param url1 - The first URL to compare.\n * @param url2 - The second URL to compare.\n * @returns Whether the two URLs are equal.\n */\nexport const compareUrls = (url1: string, url2: string): boolean => {\n try {\n const parsedUrl1 = new URL(url1);\n const parsedUrl2 = new URL(url2);\n\n // Compare protocol, hostname, and port\n if (\n parsedUrl1.protocol !== parsedUrl2.protocol ||\n parsedUrl1.hostname !== parsedUrl2.hostname ||\n parsedUrl1.port !== parsedUrl2.port\n ) {\n return false;\n }\n\n // One URL should not have a subpath while the other does\n const path1 = parsedUrl1.pathname.replace(/\\/$/, ''); // Remove trailing slash\n const path2 = parsedUrl2.pathname.replace(/\\/$/, '');\n\n if (path1 !== '' && path2 !== '' && path1 !== path2) {\n return false;\n }\n\n return true;\n } catch (error) {\n console.error('Invalid URL(s)', error, { url1, url2 });\n return false;\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA,MAAa,eAAe,MAAc,SAA0B;AAClE,KAAI;EACF,MAAM,aAAa,IAAI,IAAI,KAAK;EAChC,MAAM,aAAa,IAAI,IAAI,KAAK;AAGhC,MACE,WAAW,aAAa,WAAW,YACnC,WAAW,aAAa,WAAW,YACnC,WAAW,SAAS,WAAW,KAE/B,QAAO;EAIT,MAAM,QAAQ,WAAW,SAAS,QAAQ,OAAO,GAAG;EACpD,MAAM,QAAQ,WAAW,SAAS,QAAQ,OAAO,GAAG;AAEpD,MAAI,UAAU,MAAM,UAAU,MAAM,UAAU,MAC5C,QAAO;AAGT,SAAO;UACA,OAAO;AACd,UAAQ,MAAM,kBAAkB,OAAO;GAAE;GAAM;GAAM,CAAC;AACtD,SAAO"}
1
+ {"version":3,"file":"compareUrls.cjs","names":[],"sources":["../../src/compareUrls.ts"],"sourcesContent":["/**\n * Compare two URLs for equality.\n * This function is used to determine if a message originates from the same origin.\n *\n * ```js\n * // Example usage\n * console.log(compareUrls(\"http://localhost:5173/\", \"http://localhost:5173\")); // true\n * console.log(compareUrls(\"http://localhost:5173\", \"http://localhost:5173?myParam=true\")); // true\n * console.log(compareUrls(\"http://localhost:5173/subpath\", \"http://localhost:5173\")); // true\n * console.log(compareUrls(\"http://localhost:5172\", \"http://localhost:5173\")); // false\n * ```\n *\n * @param url1 - The first URL to compare.\n * @param url2 - The second URL to compare.\n * @returns Whether the two URLs are equal.\n */\nexport const compareUrls = (url1: string, url2: string): boolean => {\n try {\n const parsedUrl1 = new URL(url1);\n const parsedUrl2 = new URL(url2);\n\n // Compare protocol, hostname, and port\n if (\n parsedUrl1.protocol !== parsedUrl2.protocol ||\n parsedUrl1.hostname !== parsedUrl2.hostname ||\n parsedUrl1.port !== parsedUrl2.port\n ) {\n return false;\n }\n\n // One URL should not have a subpath while the other does\n const path1 = parsedUrl1.pathname.replace(/\\/$/, ''); // Remove trailing slash\n const path2 = parsedUrl2.pathname.replace(/\\/$/, '');\n\n if (path1 !== '' && path2 !== '' && path1 !== path2) {\n return false;\n }\n\n return true;\n } catch (error) {\n console.error('Invalid URL(s)', error, { url1, url2 });\n return false;\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA,MAAa,eAAe,MAAc,SAA0B;CAClE,IAAI;EACF,MAAM,aAAa,IAAI,IAAI,KAAK;EAChC,MAAM,aAAa,IAAI,IAAI,KAAK;EAGhC,IACE,WAAW,aAAa,WAAW,YACnC,WAAW,aAAa,WAAW,YACnC,WAAW,SAAS,WAAW,MAE/B,OAAO;EAIT,MAAM,QAAQ,WAAW,SAAS,QAAQ,OAAO,GAAG;EACpD,MAAM,QAAQ,WAAW,SAAS,QAAQ,OAAO,GAAG;EAEpD,IAAI,UAAU,MAAM,UAAU,MAAM,UAAU,OAC5C,OAAO;EAGT,OAAO;UACA,OAAO;EACd,QAAQ,MAAM,kBAAkB,OAAO;GAAE;GAAM;GAAM,CAAC;EACtD,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelector.cjs","names":[],"sources":["../../../src/components/ContentSelector.ts"],"sourcesContent":["import { defineIntlayerContentSelectorWrapper } from './ContentSelectorWrapper';\nimport { defineIntlayerEditedContent } from './EditedContent';\nimport { defineIntlayerEditorElement } from './IntlayerEditor';\n\nconst DEFAULT_PRESS_DURATION = 250;\n\nconst STYLES = `\n :host {\n display: contents;\n }\n\n .wrapper {\n display: inline-block;\n cursor: pointer;\n user-select: none;\n border-radius: 0.375rem;\n outline-width: 2px;\n outline-offset: 4px;\n outline-style: solid;\n outline-color: transparent;\n transition: all 100ms 50ms ease-in-out;\n }\n\n .wrapper[data-active] {\n outline-color: inherit;\n }\n`;\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector>\n *\n * A framework-agnostic web component that wraps content with Intlayer editor\n * selection UI (hover outline, long-press to select, click-outside to deselect).\n *\n * @fires intlayer:press - Fired after a long press (pressDuration ms). Bubbles.\n * @fires intlayer:hover - Fired on mouseenter. Bubbles.\n * @fires intlayer:unhover - Fired on mouseleave / mouseup. Bubbles.\n * @fires intlayer:click-outside - Fired when a click occurs outside the element. Bubbles.\n *\n * @prop {boolean} isSelecting - Whether this element is currently selected (external state)\n * @prop {number} pressDuration - Long-press threshold in ms. Default: 250\n */\nexport class IntlayerContentSelectorElement extends _HTMLElement {\n private _isSelecting = false;\n private _pressDuration = DEFAULT_PRESS_DURATION;\n private _isHovered = false;\n private _isSelectingState = false;\n private _wrapper: HTMLSpanElement;\n private _pressTimer: ReturnType<typeof setTimeout> | null = null;\n private _clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['is-selecting', 'press-duration'];\n }\n\n get isSelecting(): boolean {\n return this._isSelecting;\n }\n set isSelecting(v: boolean) {\n this._isSelecting = v;\n this._updateActiveState();\n }\n\n get pressDuration(): number {\n return this._pressDuration;\n }\n set pressDuration(v: number) {\n this._pressDuration = v;\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = STYLES;\n shadow.appendChild(style);\n\n const wrapper = document.createElement('span');\n wrapper.className = 'wrapper';\n wrapper.setAttribute('role', 'button');\n wrapper.setAttribute('tabindex', '0');\n wrapper.appendChild(document.createElement('slot'));\n shadow.appendChild(wrapper);\n this._wrapper = wrapper;\n\n wrapper.addEventListener('mousedown', () => this._handleMouseDown());\n wrapper.addEventListener('mouseup', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('mouseleave', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('mouseenter', () => this._handleMouseEnter());\n wrapper.addEventListener('click', (e) => this._handleClick(e));\n wrapper.addEventListener('touchstart', () => this._handleMouseDown());\n wrapper.addEventListener('touchend', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('touchcancel', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('blur', () => this._handleBlur());\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'is-selecting') {\n this._isSelecting = newVal !== null;\n this._updateActiveState();\n } else if (name === 'press-duration') {\n this._pressDuration =\n newVal !== null ? parseInt(newVal, 10) : DEFAULT_PRESS_DURATION;\n }\n }\n\n connectedCallback(): void {\n this._clickOutsideHandler = (e: MouseEvent) => {\n // composedPath() pierces shadow boundaries\n if (!e.composedPath().includes(this)) {\n this._isSelectingState = false;\n this._dispatch('intlayer:click-outside');\n this._updateActiveState();\n }\n };\n document.addEventListener('mousedown', this._clickOutsideHandler);\n }\n\n disconnectedCallback(): void {\n if (this._clickOutsideHandler) {\n document.removeEventListener('mousedown', this._clickOutsideHandler);\n this._clickOutsideHandler = null;\n }\n this._clearPressTimer();\n }\n\n private _updateActiveState(): void {\n const isActive =\n this._isSelecting || this._isSelectingState || this._isHovered;\n if (isActive) {\n this._wrapper.setAttribute('data-active', '');\n } else {\n this._wrapper.removeAttribute('data-active');\n }\n }\n\n private _clearPressTimer(): void {\n if (this._pressTimer !== null) {\n clearTimeout(this._pressTimer);\n this._pressTimer = null;\n }\n }\n\n private _dispatch(eventName: string): void {\n this.dispatchEvent(\n new CustomEvent(eventName, { bubbles: true, composed: true })\n );\n }\n\n private _handleMouseDown(): void {\n this._clearPressTimer();\n this._pressTimer = setTimeout(() => {\n this._isSelectingState = true;\n this._updateActiveState();\n this._dispatch('intlayer:press');\n }, this._pressDuration);\n }\n\n private _handleMouseEnter(): void {\n this._isHovered = true;\n this._updateActiveState();\n this._dispatch('intlayer:hover');\n }\n\n private _handleMouseUpOrLeave(): void {\n if (this._isHovered) {\n this._isHovered = false;\n this._dispatch('intlayer:unhover');\n }\n this._clearPressTimer();\n this._updateActiveState();\n }\n\n private _handleClick(e: MouseEvent): void {\n if (this._isSelecting || this._isSelectingState) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n\n private _handleBlur(): void {\n this._isSelectingState = false;\n this._updateActiveState();\n }\n}\n\n/**\n * Register all Intlayer custom elements.\n * Call this once at application startup (inside IntlayerEditorProvider or similar).\n * Safe to call multiple times — only registers elements that are not yet defined.\n */\nexport const defineIntlayerElements = (): void => {\n if (typeof customElements === 'undefined') return;\n if (!customElements.get('intlayer-content-selector')) {\n customElements.define(\n 'intlayer-content-selector',\n IntlayerContentSelectorElement\n );\n }\n defineIntlayerContentSelectorWrapper();\n defineIntlayerEditedContent();\n defineIntlayerEditorElement();\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ContentSelector.cjs","names":[],"sources":["../../../src/components/ContentSelector.ts"],"sourcesContent":["import { defineIntlayerContentSelectorWrapper } from './ContentSelectorWrapper';\nimport { defineIntlayerEditedContent } from './EditedContent';\nimport { defineIntlayerEditorElement } from './IntlayerEditor';\n\nconst DEFAULT_PRESS_DURATION = 250;\n\nconst STYLES = `\n :host {\n display: contents;\n }\n\n .wrapper {\n display: inline-block;\n cursor: pointer;\n user-select: none;\n border-radius: 0.375rem;\n outline-width: 2px;\n outline-offset: 4px;\n outline-style: solid;\n outline-color: transparent;\n transition: all 100ms 50ms ease-in-out;\n }\n\n .wrapper[data-active] {\n outline-color: inherit;\n }\n`;\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector>\n *\n * A framework-agnostic web component that wraps content with Intlayer editor\n * selection UI (hover outline, long-press to select, click-outside to deselect).\n *\n * @fires intlayer:press - Fired after a long press (pressDuration ms). Bubbles.\n * @fires intlayer:hover - Fired on mouseenter. Bubbles.\n * @fires intlayer:unhover - Fired on mouseleave / mouseup. Bubbles.\n * @fires intlayer:click-outside - Fired when a click occurs outside the element. Bubbles.\n *\n * @prop {boolean} isSelecting - Whether this element is currently selected (external state)\n * @prop {number} pressDuration - Long-press threshold in ms. Default: 250\n */\nexport class IntlayerContentSelectorElement extends _HTMLElement {\n private _isSelecting = false;\n private _pressDuration = DEFAULT_PRESS_DURATION;\n private _isHovered = false;\n private _isSelectingState = false;\n private _wrapper: HTMLSpanElement;\n private _pressTimer: ReturnType<typeof setTimeout> | null = null;\n private _clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['is-selecting', 'press-duration'];\n }\n\n get isSelecting(): boolean {\n return this._isSelecting;\n }\n set isSelecting(v: boolean) {\n this._isSelecting = v;\n this._updateActiveState();\n }\n\n get pressDuration(): number {\n return this._pressDuration;\n }\n set pressDuration(v: number) {\n this._pressDuration = v;\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = STYLES;\n shadow.appendChild(style);\n\n const wrapper = document.createElement('span');\n wrapper.className = 'wrapper';\n wrapper.setAttribute('role', 'button');\n wrapper.setAttribute('tabindex', '0');\n wrapper.appendChild(document.createElement('slot'));\n shadow.appendChild(wrapper);\n this._wrapper = wrapper;\n\n wrapper.addEventListener('mousedown', () => this._handleMouseDown());\n wrapper.addEventListener('mouseup', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('mouseleave', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('mouseenter', () => this._handleMouseEnter());\n wrapper.addEventListener('click', (e) => this._handleClick(e));\n wrapper.addEventListener('touchstart', () => this._handleMouseDown());\n wrapper.addEventListener('touchend', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('touchcancel', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('blur', () => this._handleBlur());\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'is-selecting') {\n this._isSelecting = newVal !== null;\n this._updateActiveState();\n } else if (name === 'press-duration') {\n this._pressDuration =\n newVal !== null ? parseInt(newVal, 10) : DEFAULT_PRESS_DURATION;\n }\n }\n\n connectedCallback(): void {\n this._clickOutsideHandler = (e: MouseEvent) => {\n // composedPath() pierces shadow boundaries\n if (!e.composedPath().includes(this)) {\n this._isSelectingState = false;\n this._dispatch('intlayer:click-outside');\n this._updateActiveState();\n }\n };\n document.addEventListener('mousedown', this._clickOutsideHandler);\n }\n\n disconnectedCallback(): void {\n if (this._clickOutsideHandler) {\n document.removeEventListener('mousedown', this._clickOutsideHandler);\n this._clickOutsideHandler = null;\n }\n this._clearPressTimer();\n }\n\n private _updateActiveState(): void {\n const isActive =\n this._isSelecting || this._isSelectingState || this._isHovered;\n if (isActive) {\n this._wrapper.setAttribute('data-active', '');\n } else {\n this._wrapper.removeAttribute('data-active');\n }\n }\n\n private _clearPressTimer(): void {\n if (this._pressTimer !== null) {\n clearTimeout(this._pressTimer);\n this._pressTimer = null;\n }\n }\n\n private _dispatch(eventName: string): void {\n this.dispatchEvent(\n new CustomEvent(eventName, { bubbles: true, composed: true })\n );\n }\n\n private _handleMouseDown(): void {\n this._clearPressTimer();\n this._pressTimer = setTimeout(() => {\n this._isSelectingState = true;\n this._updateActiveState();\n this._dispatch('intlayer:press');\n }, this._pressDuration);\n }\n\n private _handleMouseEnter(): void {\n this._isHovered = true;\n this._updateActiveState();\n this._dispatch('intlayer:hover');\n }\n\n private _handleMouseUpOrLeave(): void {\n if (this._isHovered) {\n this._isHovered = false;\n this._dispatch('intlayer:unhover');\n }\n this._clearPressTimer();\n this._updateActiveState();\n }\n\n private _handleClick(e: MouseEvent): void {\n if (this._isSelecting || this._isSelectingState) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n\n private _handleBlur(): void {\n this._isSelectingState = false;\n this._updateActiveState();\n }\n}\n\n/**\n * Register all Intlayer custom elements.\n * Call this once at application startup (inside IntlayerEditorProvider or similar).\n * Safe to call multiple times — only registers elements that are not yet defined.\n */\nexport const defineIntlayerElements = (): void => {\n if (typeof customElements === 'undefined') return;\n if (!customElements.get('intlayer-content-selector')) {\n customElements.define(\n 'intlayer-content-selector',\n IntlayerContentSelectorElement\n );\n }\n defineIntlayerContentSelectorWrapper();\n defineIntlayerEditedContent();\n defineIntlayerEditorElement();\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelectorWrapper.cjs","names":["getGlobalEditorManager","NodeTypes","node","onGlobalEditorManagerChange"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\n\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 type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\ntype RenderState = 'simple' | 'wrapped-slot' | 'wrapped-text';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic web component 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 _HTMLElement {\n private _keyPathJson = '[]';\n private _dictionaryKey = '';\n private _editorEnabled = false;\n private _isInIframe = false;\n private _isSelected = false;\n private _editedValue: ContentNode | undefined = undefined;\n\n private _renderState: RenderState | null = null;\n private _selector: HTMLElement | null = null;\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 static get observedAttributes(): string[] {\n return ['key-path', 'dictionary-key'];\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'key-path') {\n this._keyPathJson = newVal ?? '[]';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n } else if (name === 'dictionary-key') {\n this._dictionaryKey = newVal ?? '';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n connectedCallback(): void {\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n this._render();\n }\n\n disconnectedCallback(): void {\n this._teardown();\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 _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 (keyPath) => keyPath.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 this._render();\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 this._render();\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 as { nodeType?: unknown }).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 this._render();\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n this._updateSelectorAttr();\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 this._updateSelectorAttr();\n }\n\n private _updateSelectorAttr(): void {\n if (!this._selector) return;\n if (this._isSelected) {\n this._selector.setAttribute('is-selecting', '');\n } else {\n this._selector.removeAttribute('is-selecting');\n }\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 this._render();\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 this._render();\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 _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 {\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n }\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 private _render(): void {\n const useWrapper = this._isInIframe && this._editorEnabled;\n const editedValue = this._editedValue;\n const isSimpleValue =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean';\n\n const newState: RenderState = !useWrapper\n ? 'simple'\n : isSimpleValue\n ? 'wrapped-text'\n : 'wrapped-slot';\n\n if (this._renderState !== newState) {\n this._rebuildContent(newState);\n return;\n }\n\n // Structure unchanged — update only dynamic parts\n if (newState !== 'simple' && this._selector) {\n this._updateSelectorAttr();\n if (\n newState === 'wrapped-text' &&\n this._selector.firstChild?.nodeType === Node.TEXT_NODE\n ) {\n (this._selector.firstChild as Text).data = String(editedValue);\n }\n }\n }\n\n private _rebuildContent(state: RenderState): void {\n const shadow = this.shadowRoot!;\n // Remove all nodes except the style element (first child)\n while (shadow.childNodes.length > 1) {\n shadow.removeChild(shadow.lastChild!);\n }\n this._selector = null;\n\n if (state === 'simple') {\n shadow.appendChild(document.createElement('slot'));\n } else {\n const selector = document.createElement('intlayer-content-selector');\n this._selector = selector;\n if (this._isSelected) selector.setAttribute('is-selecting', '');\n selector.addEventListener('intlayer:press', (e) => this._handlePress(e));\n selector.addEventListener('intlayer:hover', (e) => this._handleHover(e));\n selector.addEventListener('intlayer:unhover', (e) =>\n this._handleUnhover(e)\n );\n\n if (state === 'wrapped-text') {\n selector.appendChild(\n document.createTextNode(String(this._editedValue))\n );\n } else {\n selector.appendChild(document.createElement('slot'));\n }\n shadow.appendChild(selector);\n }\n\n this._renderState = state;\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.cjs","names":["getGlobalEditorManager","NodeTypes","node","onGlobalEditorManagerChange"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\n\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 type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\ntype RenderState = 'simple' | 'wrapped-slot' | 'wrapped-text';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic web component 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 _HTMLElement {\n private _keyPathJson = '[]';\n private _dictionaryKey = '';\n private _editorEnabled = false;\n private _isInIframe = false;\n private _isSelected = false;\n private _editedValue: ContentNode | undefined = undefined;\n\n private _renderState: RenderState | null = null;\n private _selector: HTMLElement | null = null;\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 static get observedAttributes(): string[] {\n return ['key-path', 'dictionary-key'];\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'key-path') {\n this._keyPathJson = newVal ?? '[]';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n } else if (name === 'dictionary-key') {\n this._dictionaryKey = newVal ?? '';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n connectedCallback(): void {\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n this._render();\n }\n\n disconnectedCallback(): void {\n this._teardown();\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 _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 (keyPath) => keyPath.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 this._render();\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 this._render();\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 as { nodeType?: unknown }).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 this._render();\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n this._updateSelectorAttr();\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 this._updateSelectorAttr();\n }\n\n private _updateSelectorAttr(): void {\n if (!this._selector) return;\n if (this._isSelected) {\n this._selector.setAttribute('is-selecting', '');\n } else {\n this._selector.removeAttribute('is-selecting');\n }\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 this._render();\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 this._render();\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 _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 {\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n }\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 private _render(): void {\n const useWrapper = this._isInIframe && this._editorEnabled;\n const editedValue = this._editedValue;\n const isSimpleValue =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean';\n\n const newState: RenderState = !useWrapper\n ? 'simple'\n : isSimpleValue\n ? 'wrapped-text'\n : 'wrapped-slot';\n\n if (this._renderState !== newState) {\n this._rebuildContent(newState);\n return;\n }\n\n // Structure unchanged — update only dynamic parts\n if (newState !== 'simple' && this._selector) {\n this._updateSelectorAttr();\n if (\n newState === 'wrapped-text' &&\n this._selector.firstChild?.nodeType === Node.TEXT_NODE\n ) {\n (this._selector.firstChild as Text).data = String(editedValue);\n }\n }\n }\n\n private _rebuildContent(state: RenderState): void {\n const shadow = this.shadowRoot!;\n // Remove all nodes except the style element (first child)\n while (shadow.childNodes.length > 1) {\n shadow.removeChild(shadow.lastChild!);\n }\n this._selector = null;\n\n if (state === 'simple') {\n shadow.appendChild(document.createElement('slot'));\n } else {\n const selector = document.createElement('intlayer-content-selector');\n this._selector = selector;\n if (this._isSelected) selector.setAttribute('is-selecting', '');\n selector.addEventListener('intlayer:press', (e) => this._handlePress(e));\n selector.addEventListener('intlayer:hover', (e) => this._handleHover(e));\n selector.addEventListener('intlayer:unhover', (e) =>\n this._handleUnhover(e)\n );\n\n if (state === 'wrapped-text') {\n selector.appendChild(\n document.createTextNode(String(this._editedValue))\n );\n } else {\n selector.appendChild(document.createElement('slot'));\n }\n shadow.appendChild(selector);\n }\n\n this._renderState = state;\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 +1 @@
1
- {"version":3,"file":"EditedContent.cjs","names":["getGlobalEditorManager","onGlobalEditorManagerChange"],"sources":["../../../src/components/EditedContent.ts"],"sourcesContent":["import { getBasePlugins, getContent } from '@intlayer/core/interpreter';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { EditorStateManager } from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-edited-content>\n *\n * Framework-agnostic web component that displays edited content from the Intlayer\n * editor. When the editor has an edited value for the given dictionary key and\n * key path, it renders the edited value; otherwise it renders the original\n * content via a slot.\n *\n * Always wraps content in <intlayer-content-selector-wrapper> for selection UI.\n *\n * @attr {string} dictionary-key - The dictionary key owning this content node\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} locale - The current locale string\n */\nexport class IntlayerEditedContentElement extends _HTMLElement {\n private _dictionaryKey = '';\n private _keyPathJson = '[]';\n private _locale = '';\n private _editedText: string | null = null;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n private _selectorWrapper: HTMLElement;\n private _slot: HTMLSlotElement;\n\n static get observedAttributes(): string[] {\n return ['dictionary-key', 'key-path', 'locale'];\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n this._selectorWrapper.setAttribute('dictionary-key', v);\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n this._selectorWrapper.setAttribute('key-path', v);\n }\n\n get locale(): string {\n return this._locale;\n }\n set locale(v: string) {\n this._locale = v;\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n\n this._selectorWrapper = document.createElement(\n 'intlayer-content-selector-wrapper'\n );\n this._slot = document.createElement('slot') as HTMLSlotElement;\n this._selectorWrapper.appendChild(this._slot);\n shadow.appendChild(this._selectorWrapper);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n const val = newVal ?? '';\n if (name === 'dictionary-key') {\n this._dictionaryKey = val;\n this._selectorWrapper.setAttribute('dictionary-key', val);\n } else if (name === 'key-path') {\n this._keyPathJson = val || '[]';\n this._selectorWrapper.setAttribute('key-path', this._keyPathJson);\n } else if (name === 'locale') {\n this._locale = val;\n }\n }\n\n connectedCallback(): void {\n this._subscribeToManager();\n }\n\n disconnectedCallback(): void {\n this._teardown();\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEditedContent = null;\n }\n\n private _getKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this._keyPathJson);\n } catch {\n return [];\n }\n }\n\n private _render(): void {\n while (this._selectorWrapper.firstChild) {\n this._selectorWrapper.removeChild(this._selectorWrapper.firstChild);\n }\n if (this._editedText !== null) {\n this._selectorWrapper.appendChild(\n document.createTextNode(this._editedText)\n );\n } else {\n this._selectorWrapper.appendChild(this._slot);\n }\n }\n\n private _resolveEditedText(manager: EditorStateManager): void {\n const keyPath = this._getKeyPath();\n const editedValue = manager.getContentValue(this._dictionaryKey, keyPath);\n\n if (editedValue === undefined || editedValue === null) {\n this._editedText = null;\n this._render();\n return;\n }\n\n if (typeof editedValue === 'string' || typeof editedValue === 'number') {\n this._editedText = String(editedValue);\n this._render();\n return;\n }\n\n if (typeof editedValue === 'object') {\n const locale = this._locale || undefined;\n const transformed = getContent(\n editedValue,\n {\n locale: locale as any,\n dictionaryKey: this._dictionaryKey,\n keyPath,\n },\n getBasePlugins(locale)\n );\n if (typeof transformed === 'string' || typeof transformed === 'number') {\n this._editedText = String(transformed);\n } else {\n console.error(\n `[intlayer-edited-content] Incorrect edited content format. Expected string. Value: ${JSON.stringify(transformed)}`\n );\n this._editedText = null;\n }\n this._render();\n return;\n }\n\n this._editedText = null;\n this._render();\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._resolveEditedText(manager);\n\n const handleChange = () => this._resolveEditedText(manager);\n manager.editedContent.addEventListener('change', handleChange);\n\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener('change', handleChange);\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEditedContent?.();\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editedText = null;\n this._render();\n }\n });\n }\n}\n\nexport const defineIntlayerEditedContent = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-edited-content')) {\n customElements.define(\n 'intlayer-edited-content',\n IntlayerEditedContentElement\n );\n }\n};\n"],"mappings":";;;;;;AAQA,MAAM,eACJ,OAAO,gBAAgB,cACnB,cACC,MAAM;;;;;;;;;;;;;;;AAgBb,IAAa,+BAAb,cAAkD,aAAa;CAC7D,AAAQ,iBAAiB;CACzB,AAAQ,eAAe;CACvB,AAAQ,UAAU;CAClB,AAAQ,cAA6B;CAErC,AAAQ,gBAAqC;CAC7C,AAAQ,sBAA2C;CAEnD,AAAQ;CACR,AAAQ;CAER,WAAW,qBAA+B;AACxC,SAAO;GAAC;GAAkB;GAAY;GAAS;;CAGjD,IAAI,gBAAwB;AAC1B,SAAO,KAAK;;CAEd,IAAI,cAAc,GAAW;AAC3B,OAAK,iBAAiB;AACtB,OAAK,iBAAiB,aAAa,kBAAkB,EAAE;;CAGzD,IAAI,cAAsB;AACxB,SAAO,KAAK;;CAEd,IAAI,YAAY,GAAW;AACzB,OAAK,eAAe;AACpB,OAAK,iBAAiB,aAAa,YAAY,EAAE;;CAGnD,IAAI,SAAiB;AACnB,SAAO,KAAK;;CAEd,IAAI,OAAO,GAAW;AACpB,OAAK,UAAU;;CAGjB,cAAc;AACZ,SAAO;EACP,MAAM,SAAS,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;EAElD,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,QAAM,cAAc;AACpB,SAAO,YAAY,MAAM;AAEzB,OAAK,mBAAmB,SAAS,cAC/B,oCACD;AACD,OAAK,QAAQ,SAAS,cAAc,OAAO;AAC3C,OAAK,iBAAiB,YAAY,KAAK,MAAM;AAC7C,SAAO,YAAY,KAAK,iBAAiB;;CAG3C,yBACE,MACA,SACA,QACM;EACN,MAAM,MAAM,UAAU;AACtB,MAAI,SAAS,kBAAkB;AAC7B,QAAK,iBAAiB;AACtB,QAAK,iBAAiB,aAAa,kBAAkB,IAAI;aAChD,SAAS,YAAY;AAC9B,QAAK,eAAe,OAAO;AAC3B,QAAK,iBAAiB,aAAa,YAAY,KAAK,aAAa;aACxD,SAAS,SAClB,MAAK,UAAU;;CAInB,oBAA0B;AACxB,OAAK,qBAAqB;;CAG5B,uBAA6B;AAC3B,OAAK,WAAW;;CAGlB,AAAQ,YAAkB;AACxB,OAAK,iBAAiB;AACtB,OAAK,uBAAuB;AAC5B,OAAK,gBAAgB;AACrB,OAAK,sBAAsB;;CAG7B,AAAQ,cAAyB;AAC/B,MAAI;AACF,UAAO,KAAK,MAAM,KAAK,aAAa;UAC9B;AACN,UAAO,EAAE;;;CAIb,AAAQ,UAAgB;AACtB,SAAO,KAAK,iBAAiB,WAC3B,MAAK,iBAAiB,YAAY,KAAK,iBAAiB,WAAW;AAErE,MAAI,KAAK,gBAAgB,KACvB,MAAK,iBAAiB,YACpB,SAAS,eAAe,KAAK,YAAY,CAC1C;MAED,MAAK,iBAAiB,YAAY,KAAK,MAAM;;CAIjD,AAAQ,mBAAmB,SAAmC;EAC5D,MAAM,UAAU,KAAK,aAAa;EAClC,MAAM,cAAc,QAAQ,gBAAgB,KAAK,gBAAgB,QAAQ;AAEzE,MAAI,gBAAgB,UAAa,gBAAgB,MAAM;AACrD,QAAK,cAAc;AACnB,QAAK,SAAS;AACd;;AAGF,MAAI,OAAO,gBAAgB,YAAY,OAAO,gBAAgB,UAAU;AACtE,QAAK,cAAc,OAAO,YAAY;AACtC,QAAK,SAAS;AACd;;AAGF,MAAI,OAAO,gBAAgB,UAAU;GACnC,MAAM,SAAS,KAAK,WAAW;GAC/B,MAAM,yDACJ,aACA;IACU;IACR,eAAe,KAAK;IACpB;IACD,iDACc,OAAO,CACvB;AACD,OAAI,OAAO,gBAAgB,YAAY,OAAO,gBAAgB,SAC5D,MAAK,cAAc,OAAO,YAAY;QACjC;AACL,YAAQ,MACN,sFAAsF,KAAK,UAAU,YAAY,GAClH;AACD,SAAK,cAAc;;AAErB,QAAK,SAAS;AACd;;AAGF,OAAK,cAAc;AACnB,OAAK,SAAS;;CAGhB,AAAQ,2BAA2B,SAAmC;AACpE,OAAK,mBAAmB,QAAQ;EAEhC,MAAM,qBAAqB,KAAK,mBAAmB,QAAQ;AAC3D,UAAQ,cAAc,iBAAiB,UAAU,aAAa;AAE9D,OAAK,4BACH,QAAQ,cAAc,oBAAoB,UAAU,aAAa;;CAGrE,AAAQ,sBAA4B;EAClC,MAAM,UAAUA,mDAAwB;AACxC,MAAI,QACF,MAAK,2BAA2B,QAAQ;AAG1C,OAAK,gBAAgBC,wDAA6B,MAAM;AACtD,QAAK,uBAAuB;AAC5B,QAAK,sBAAsB;AAC3B,OAAI,EACF,MAAK,2BAA2B,EAAE;QAC7B;AACL,SAAK,cAAc;AACnB,SAAK,SAAS;;IAEhB;;;AAIN,MAAa,oCAA0C;AACrD,KAAI,OAAO,mBAAmB,YAAa;AAE3C,KAAI,CAAC,eAAe,IAAI,0BAA0B,CAChD,gBAAe,OACb,2BACA,6BACD"}
1
+ {"version":3,"file":"EditedContent.cjs","names":["getGlobalEditorManager","onGlobalEditorManagerChange"],"sources":["../../../src/components/EditedContent.ts"],"sourcesContent":["import { getBasePlugins, getContent } from '@intlayer/core/interpreter';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { EditorStateManager } from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-edited-content>\n *\n * Framework-agnostic web component that displays edited content from the Intlayer\n * editor. When the editor has an edited value for the given dictionary key and\n * key path, it renders the edited value; otherwise it renders the original\n * content via a slot.\n *\n * Always wraps content in <intlayer-content-selector-wrapper> for selection UI.\n *\n * @attr {string} dictionary-key - The dictionary key owning this content node\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} locale - The current locale string\n */\nexport class IntlayerEditedContentElement extends _HTMLElement {\n private _dictionaryKey = '';\n private _keyPathJson = '[]';\n private _locale = '';\n private _editedText: string | null = null;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n private _selectorWrapper: HTMLElement;\n private _slot: HTMLSlotElement;\n\n static get observedAttributes(): string[] {\n return ['dictionary-key', 'key-path', 'locale'];\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n this._selectorWrapper.setAttribute('dictionary-key', v);\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n this._selectorWrapper.setAttribute('key-path', v);\n }\n\n get locale(): string {\n return this._locale;\n }\n set locale(v: string) {\n this._locale = v;\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n\n this._selectorWrapper = document.createElement(\n 'intlayer-content-selector-wrapper'\n );\n this._slot = document.createElement('slot') as HTMLSlotElement;\n this._selectorWrapper.appendChild(this._slot);\n shadow.appendChild(this._selectorWrapper);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n const val = newVal ?? '';\n if (name === 'dictionary-key') {\n this._dictionaryKey = val;\n this._selectorWrapper.setAttribute('dictionary-key', val);\n } else if (name === 'key-path') {\n this._keyPathJson = val || '[]';\n this._selectorWrapper.setAttribute('key-path', this._keyPathJson);\n } else if (name === 'locale') {\n this._locale = val;\n }\n }\n\n connectedCallback(): void {\n this._subscribeToManager();\n }\n\n disconnectedCallback(): void {\n this._teardown();\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEditedContent = null;\n }\n\n private _getKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this._keyPathJson);\n } catch {\n return [];\n }\n }\n\n private _render(): void {\n while (this._selectorWrapper.firstChild) {\n this._selectorWrapper.removeChild(this._selectorWrapper.firstChild);\n }\n if (this._editedText !== null) {\n this._selectorWrapper.appendChild(\n document.createTextNode(this._editedText)\n );\n } else {\n this._selectorWrapper.appendChild(this._slot);\n }\n }\n\n private _resolveEditedText(manager: EditorStateManager): void {\n const keyPath = this._getKeyPath();\n const editedValue = manager.getContentValue(this._dictionaryKey, keyPath);\n\n if (editedValue === undefined || editedValue === null) {\n this._editedText = null;\n this._render();\n return;\n }\n\n if (typeof editedValue === 'string' || typeof editedValue === 'number') {\n this._editedText = String(editedValue);\n this._render();\n return;\n }\n\n if (typeof editedValue === 'object') {\n const locale = this._locale || undefined;\n const transformed = getContent(\n editedValue,\n {\n locale: locale as any,\n dictionaryKey: this._dictionaryKey,\n keyPath,\n },\n getBasePlugins(locale)\n );\n if (typeof transformed === 'string' || typeof transformed === 'number') {\n this._editedText = String(transformed);\n } else {\n console.error(\n `[intlayer-edited-content] Incorrect edited content format. Expected string. Value: ${JSON.stringify(transformed)}`\n );\n this._editedText = null;\n }\n this._render();\n return;\n }\n\n this._editedText = null;\n this._render();\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._resolveEditedText(manager);\n\n const handleChange = () => this._resolveEditedText(manager);\n manager.editedContent.addEventListener('change', handleChange);\n\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener('change', handleChange);\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEditedContent?.();\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editedText = null;\n this._render();\n }\n });\n }\n}\n\nexport const defineIntlayerEditedContent = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-edited-content')) {\n customElements.define(\n 'intlayer-edited-content',\n IntlayerEditedContentElement\n );\n }\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"IntlayerEditor.cjs","names":["getGlobalEditorManager","onGlobalEditorManagerChange"],"sources":["../../../src/components/IntlayerEditor.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { initEditorClient, stopEditorClient } from '../core/initEditorClient';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-editor>\n *\n * A framework-agnostic web component that manages the Intlayer editor singleton\n * lifecycle and keeps the current locale in sync with the EditorStateManager.\n *\n * Drop this element once at the root of your application to activate the editor.\n * It renders no UI.\n *\n * @prop {IntlayerConfig} configuration - The Intlayer config (required; set as JS property, not attribute)\n * @prop {string} locale - The active application locale (attribute/property)\n *\n * @example\n * // Svelte\n * <intlayer-editor .configuration={config} locale={$locale} />\n *\n * // Vue\n * <intlayer-editor :configuration=\"config\" :locale=\"currentLocale\" />\n */\nexport class IntlayerEditorElement extends _HTMLElement {\n private _configuration: IntlayerConfig | undefined = undefined;\n private _locale: string | undefined = undefined;\n private _initialized = false;\n private _unsubManager: (() => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['locale'];\n }\n\n get configuration(): IntlayerConfig | undefined {\n return this._configuration;\n }\n set configuration(v: IntlayerConfig | undefined) {\n this._configuration = v;\n if (!this._initialized) this._init();\n }\n\n get locale(): string | undefined {\n return this._locale;\n }\n set locale(v: string | undefined) {\n this._locale = v;\n if (v && this._initialized) this._syncLocale(v);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'locale' && newVal !== null) {\n this._locale = newVal;\n if (this._initialized) this._syncLocale(newVal);\n }\n }\n\n connectedCallback(): void {\n this._init();\n }\n\n disconnectedCallback(): void {\n this._unsubManager?.();\n this._unsubManager = null;\n if (this._initialized) {\n stopEditorClient();\n this._initialized = false;\n }\n }\n\n private _init(): void {\n if (this._initialized) return;\n initEditorClient();\n this._initialized = true;\n if (this._locale) this._syncLocale(this._locale);\n }\n\n private _syncLocale(locale: string): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n manager.currentLocale.set(locale as Locale);\n } else {\n // Manager may not be ready yet — wait for it\n this._unsubManager?.();\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n if (m) {\n this._unsubManager?.();\n this._unsubManager = null;\n m.currentLocale.set(locale as Locale);\n }\n });\n }\n }\n}\n\nexport const defineIntlayerEditorElement = (): void => {\n if (typeof customElements === 'undefined') return;\n if (!customElements.get('intlayer-editor')) {\n customElements.define('intlayer-editor', IntlayerEditorElement);\n }\n};\n"],"mappings":";;;;;AAQA,MAAM,eACJ,OAAO,gBAAgB,cACnB,cACC,MAAM;;;;;;;;;;;;;;;;;;;;AAqBb,IAAa,wBAAb,cAA2C,aAAa;CACtD,AAAQ,iBAA6C;CACrD,AAAQ,UAA8B;CACtC,AAAQ,eAAe;CACvB,AAAQ,gBAAqC;CAE7C,WAAW,qBAA+B;AACxC,SAAO,CAAC,SAAS;;CAGnB,IAAI,gBAA4C;AAC9C,SAAO,KAAK;;CAEd,IAAI,cAAc,GAA+B;AAC/C,OAAK,iBAAiB;AACtB,MAAI,CAAC,KAAK,aAAc,MAAK,OAAO;;CAGtC,IAAI,SAA6B;AAC/B,SAAO,KAAK;;CAEd,IAAI,OAAO,GAAuB;AAChC,OAAK,UAAU;AACf,MAAI,KAAK,KAAK,aAAc,MAAK,YAAY,EAAE;;CAGjD,yBACE,MACA,SACA,QACM;AACN,MAAI,SAAS,YAAY,WAAW,MAAM;AACxC,QAAK,UAAU;AACf,OAAI,KAAK,aAAc,MAAK,YAAY,OAAO;;;CAInD,oBAA0B;AACxB,OAAK,OAAO;;CAGd,uBAA6B;AAC3B,OAAK,iBAAiB;AACtB,OAAK,gBAAgB;AACrB,MAAI,KAAK,cAAc;AACrB,mDAAkB;AAClB,QAAK,eAAe;;;CAIxB,AAAQ,QAAc;AACpB,MAAI,KAAK,aAAc;AACvB,kDAAkB;AAClB,OAAK,eAAe;AACpB,MAAI,KAAK,QAAS,MAAK,YAAY,KAAK,QAAQ;;CAGlD,AAAQ,YAAY,QAAsB;EACxC,MAAM,UAAUA,mDAAwB;AACxC,MAAI,QACF,SAAQ,cAAc,IAAI,OAAiB;OACtC;AAEL,QAAK,iBAAiB;AACtB,QAAK,gBAAgBC,wDAA6B,MAAM;AACtD,QAAI,GAAG;AACL,UAAK,iBAAiB;AACtB,UAAK,gBAAgB;AACrB,OAAE,cAAc,IAAI,OAAiB;;KAEvC;;;;AAKR,MAAa,oCAA0C;AACrD,KAAI,OAAO,mBAAmB,YAAa;AAC3C,KAAI,CAAC,eAAe,IAAI,kBAAkB,CACxC,gBAAe,OAAO,mBAAmB,sBAAsB"}
1
+ {"version":3,"file":"IntlayerEditor.cjs","names":["getGlobalEditorManager","onGlobalEditorManagerChange"],"sources":["../../../src/components/IntlayerEditor.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { initEditorClient, stopEditorClient } from '../core/initEditorClient';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-editor>\n *\n * A framework-agnostic web component that manages the Intlayer editor singleton\n * lifecycle and keeps the current locale in sync with the EditorStateManager.\n *\n * Drop this element once at the root of your application to activate the editor.\n * It renders no UI.\n *\n * @prop {IntlayerConfig} configuration - The Intlayer config (required; set as JS property, not attribute)\n * @prop {string} locale - The active application locale (attribute/property)\n *\n * @example\n * // Svelte\n * <intlayer-editor .configuration={config} locale={$locale} />\n *\n * // Vue\n * <intlayer-editor :configuration=\"config\" :locale=\"currentLocale\" />\n */\nexport class IntlayerEditorElement extends _HTMLElement {\n private _configuration: IntlayerConfig | undefined = undefined;\n private _locale: string | undefined = undefined;\n private _initialized = false;\n private _unsubManager: (() => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['locale'];\n }\n\n get configuration(): IntlayerConfig | undefined {\n return this._configuration;\n }\n set configuration(v: IntlayerConfig | undefined) {\n this._configuration = v;\n if (!this._initialized) this._init();\n }\n\n get locale(): string | undefined {\n return this._locale;\n }\n set locale(v: string | undefined) {\n this._locale = v;\n if (v && this._initialized) this._syncLocale(v);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'locale' && newVal !== null) {\n this._locale = newVal;\n if (this._initialized) this._syncLocale(newVal);\n }\n }\n\n connectedCallback(): void {\n this._init();\n }\n\n disconnectedCallback(): void {\n this._unsubManager?.();\n this._unsubManager = null;\n if (this._initialized) {\n stopEditorClient();\n this._initialized = false;\n }\n }\n\n private _init(): void {\n if (this._initialized) return;\n initEditorClient();\n this._initialized = true;\n if (this._locale) this._syncLocale(this._locale);\n }\n\n private _syncLocale(locale: string): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n manager.currentLocale.set(locale as Locale);\n } else {\n // Manager may not be ready yet — wait for it\n this._unsubManager?.();\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n if (m) {\n this._unsubManager?.();\n this._unsubManager = null;\n m.currentLocale.set(locale as Locale);\n }\n });\n }\n }\n}\n\nexport const defineIntlayerEditorElement = (): void => {\n if (typeof customElements === 'undefined') return;\n if (!customElements.get('intlayer-editor')) {\n customElements.define('intlayer-editor', IntlayerEditorElement);\n }\n};\n"],"mappings":";;;;;AAQA,MAAM,eACJ,OAAO,gBAAgB,cACnB,cACC,MAAM;;;;;;;;;;;;;;;;;;;;AAqBb,IAAa,wBAAb,cAA2C,aAAa;CACtD,AAAQ,iBAA6C;CACrD,AAAQ,UAA8B;CACtC,AAAQ,eAAe;CACvB,AAAQ,gBAAqC;CAE7C,WAAW,qBAA+B;EACxC,OAAO,CAAC,SAAS;;CAGnB,IAAI,gBAA4C;EAC9C,OAAO,KAAK;;CAEd,IAAI,cAAc,GAA+B;EAC/C,KAAK,iBAAiB;EACtB,IAAI,CAAC,KAAK,cAAc,KAAK,OAAO;;CAGtC,IAAI,SAA6B;EAC/B,OAAO,KAAK;;CAEd,IAAI,OAAO,GAAuB;EAChC,KAAK,UAAU;EACf,IAAI,KAAK,KAAK,cAAc,KAAK,YAAY,EAAE;;CAGjD,yBACE,MACA,SACA,QACM;EACN,IAAI,SAAS,YAAY,WAAW,MAAM;GACxC,KAAK,UAAU;GACf,IAAI,KAAK,cAAc,KAAK,YAAY,OAAO;;;CAInD,oBAA0B;EACxB,KAAK,OAAO;;CAGd,uBAA6B;EAC3B,KAAK,iBAAiB;EACtB,KAAK,gBAAgB;EACrB,IAAI,KAAK,cAAc;GACrB,gDAAkB;GAClB,KAAK,eAAe;;;CAIxB,AAAQ,QAAc;EACpB,IAAI,KAAK,cAAc;EACvB,gDAAkB;EAClB,KAAK,eAAe;EACpB,IAAI,KAAK,SAAS,KAAK,YAAY,KAAK,QAAQ;;CAGlD,AAAQ,YAAY,QAAsB;EACxC,MAAM,UAAUA,mDAAwB;EACxC,IAAI,SACF,QAAQ,cAAc,IAAI,OAAiB;OACtC;GAEL,KAAK,iBAAiB;GACtB,KAAK,gBAAgBC,wDAA6B,MAAM;IACtD,IAAI,GAAG;KACL,KAAK,iBAAiB;KACtB,KAAK,gBAAgB;KACrB,EAAE,cAAc,IAAI,OAAiB;;KAEvC;;;;AAKR,MAAa,oCAA0C;CACrD,IAAI,OAAO,mBAAmB,aAAa;CAC3C,IAAI,CAAC,eAAe,IAAI,kBAAkB,EACxC,eAAe,OAAO,mBAAmB,sBAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"CrossFrameMessenger.cjs","names":["compareUrls"],"sources":["../../../src/core/CrossFrameMessenger.ts"],"sourcesContent":["import { compareUrls } from '../compareUrls';\n\nconst randomUUID = (): string => Math.random().toString(36).slice(2);\n\nexport type MessagePayload = {\n type: string;\n data?: unknown;\n senderId?: string;\n /** Unique ID per send() call — used to deduplicate when the same payload arrives via multiple target origins */\n messageId?: string;\n};\n\nexport type MessengerConfig = {\n /**\n * Origins allowed to send messages to this instance.\n * Use '*' to allow all origins (not recommended for production).\n */\n allowedOrigins: string[];\n /**\n * Function used to send messages to other frames.\n * Injected so the messenger is agnostic to the frame topology.\n */\n postMessageFn: (payload: MessagePayload, origin: string) => void;\n};\n\ntype MessageHandler<T = unknown> = (data: T, senderId?: string) => void;\n\n/**\n * CrossFrameMessenger manages all cross-frame postMessage communication.\n * It owns a single window message listener and routes incoming messages to\n * type-specific subscribers.\n *\n * Replaces CommunicatorContext + useCrossFrameMessageListener across all frameworks.\n */\nexport class CrossFrameMessenger {\n readonly senderId: string;\n private readonly _config: MessengerConfig;\n private readonly _subscribers = new Map<string, Set<MessageHandler>>();\n\n private _windowHandler: ((event: MessageEvent) => void) | null = null;\n /** Tracks recently processed messageIds to discard duplicates (same payload sent to multiple origins) */\n private readonly _seenMessageIds = new Set<string>();\n\n constructor(config: MessengerConfig) {\n this._config = config;\n this.senderId = randomUUID();\n }\n\n /** Start listening for incoming messages on window. */\n start(): void {\n if (typeof window === 'undefined') return;\n if (this._windowHandler) return;\n this._windowHandler = (event: MessageEvent<MessagePayload>) => {\n this._handleMessage(event);\n };\n window.addEventListener('message', this._windowHandler);\n }\n\n /** Stop listening and clean up. */\n stop(): void {\n if (this._windowHandler) {\n window.removeEventListener('message', this._windowHandler);\n this._windowHandler = null;\n }\n }\n\n /** Send a message payload to all configured target origins. */\n send(type: string, data?: unknown): void {\n const payload: MessagePayload = {\n type,\n data,\n senderId: this.senderId,\n messageId: randomUUID(),\n };\n\n for (const origin of this._config.allowedOrigins) {\n if (origin) {\n this._config.postMessageFn(payload, origin);\n }\n }\n }\n\n /**\n * Subscribe to messages of a given type.\n * Returns an unsubscribe function.\n */\n subscribe<T = unknown>(type: string, handler: MessageHandler<T>): () => void {\n if (!this._subscribers.has(type)) {\n this._subscribers.set(type, new Set());\n }\n this._subscribers.get(type)!.add(handler as MessageHandler);\n return () => {\n this._subscribers.get(type)?.delete(handler as MessageHandler);\n };\n }\n\n private _handleMessage(event: MessageEvent<MessagePayload>): void {\n const payload = event.data;\n if (!payload || typeof payload !== 'object') return;\n\n const { type, data, senderId: msgSenderId, messageId } = payload;\n if (!type || typeof type !== 'string') return;\n\n // Ignore messages originating from this instance\n if (msgSenderId === this.senderId) return;\n\n // Deduplicate: same messageId may arrive multiple times when the sender\n // posts to multiple target origins (one per allowedOrigin)\n if (messageId) {\n if (this._seenMessageIds.has(messageId)) return;\n this._seenMessageIds.add(messageId);\n // Keep the set bounded — clear when it exceeds 200 entries\n if (this._seenMessageIds.size > 200) this._seenMessageIds.clear();\n }\n\n // Validate message origin\n const { allowedOrigins } = this._config;\n const isAllowed =\n !allowedOrigins ||\n allowedOrigins.length === 0 ||\n allowedOrigins.includes('*') ||\n allowedOrigins\n .filter((url) => Boolean(url) && url !== '')\n .some((url) => compareUrls(url, event.origin));\n\n if (!isAllowed) return;\n\n const handlers = this._subscribers.get(type);\n\n if (handlers) {\n for (const handler of handlers) {\n handler(data, msgSenderId);\n }\n }\n }\n}\n"],"mappings":";;;;AAEA,MAAM,mBAA2B,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE;;;;;;;;AAgCpE,IAAa,sBAAb,MAAiC;CAC/B,AAAS;CACT,AAAiB;CACjB,AAAiB,+BAAe,IAAI,KAAkC;CAEtE,AAAQ,iBAAyD;;CAEjE,AAAiB,kCAAkB,IAAI,KAAa;CAEpD,YAAY,QAAyB;AACnC,OAAK,UAAU;AACf,OAAK,WAAW,YAAY;;;CAI9B,QAAc;AACZ,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,KAAK,eAAgB;AACzB,OAAK,kBAAkB,UAAwC;AAC7D,QAAK,eAAe,MAAM;;AAE5B,SAAO,iBAAiB,WAAW,KAAK,eAAe;;;CAIzD,OAAa;AACX,MAAI,KAAK,gBAAgB;AACvB,UAAO,oBAAoB,WAAW,KAAK,eAAe;AAC1D,QAAK,iBAAiB;;;;CAK1B,KAAK,MAAc,MAAsB;EACvC,MAAM,UAA0B;GAC9B;GACA;GACA,UAAU,KAAK;GACf,WAAW,YAAY;GACxB;AAED,OAAK,MAAM,UAAU,KAAK,QAAQ,eAChC,KAAI,OACF,MAAK,QAAQ,cAAc,SAAS,OAAO;;;;;;CASjD,UAAuB,MAAc,SAAwC;AAC3E,MAAI,CAAC,KAAK,aAAa,IAAI,KAAK,CAC9B,MAAK,aAAa,IAAI,sBAAM,IAAI,KAAK,CAAC;AAExC,OAAK,aAAa,IAAI,KAAK,CAAE,IAAI,QAA0B;AAC3D,eAAa;AACX,QAAK,aAAa,IAAI,KAAK,EAAE,OAAO,QAA0B;;;CAIlE,AAAQ,eAAe,OAA2C;EAChE,MAAM,UAAU,MAAM;AACtB,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU;EAE7C,MAAM,EAAE,MAAM,MAAM,UAAU,aAAa,cAAc;AACzD,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAGvC,MAAI,gBAAgB,KAAK,SAAU;AAInC,MAAI,WAAW;AACb,OAAI,KAAK,gBAAgB,IAAI,UAAU,CAAE;AACzC,QAAK,gBAAgB,IAAI,UAAU;AAEnC,OAAI,KAAK,gBAAgB,OAAO,IAAK,MAAK,gBAAgB,OAAO;;EAInE,MAAM,EAAE,mBAAmB,KAAK;AAShC,MAAI,EAPF,CAAC,kBACD,eAAe,WAAW,KAC1B,eAAe,SAAS,IAAI,IAC5B,eACG,QAAQ,QAAQ,QAAQ,IAAI,IAAI,QAAQ,GAAG,CAC3C,MAAM,QAAQA,gCAAY,KAAK,MAAM,OAAO,CAAC,EAElC;EAEhB,MAAM,WAAW,KAAK,aAAa,IAAI,KAAK;AAE5C,MAAI,SACF,MAAK,MAAM,WAAW,SACpB,SAAQ,MAAM,YAAY"}
1
+ {"version":3,"file":"CrossFrameMessenger.cjs","names":["compareUrls"],"sources":["../../../src/core/CrossFrameMessenger.ts"],"sourcesContent":["import { compareUrls } from '../compareUrls';\n\nconst randomUUID = (): string => Math.random().toString(36).slice(2);\n\nexport type MessagePayload = {\n type: string;\n data?: unknown;\n senderId?: string;\n /** Unique ID per send() call — used to deduplicate when the same payload arrives via multiple target origins */\n messageId?: string;\n};\n\nexport type MessengerConfig = {\n /**\n * Origins allowed to send messages to this instance.\n * Use '*' to allow all origins (not recommended for production).\n */\n allowedOrigins: string[];\n /**\n * Function used to send messages to other frames.\n * Injected so the messenger is agnostic to the frame topology.\n */\n postMessageFn: (payload: MessagePayload, origin: string) => void;\n};\n\ntype MessageHandler<T = unknown> = (data: T, senderId?: string) => void;\n\n/**\n * CrossFrameMessenger manages all cross-frame postMessage communication.\n * It owns a single window message listener and routes incoming messages to\n * type-specific subscribers.\n *\n * Replaces CommunicatorContext + useCrossFrameMessageListener across all frameworks.\n */\nexport class CrossFrameMessenger {\n readonly senderId: string;\n private readonly _config: MessengerConfig;\n private readonly _subscribers = new Map<string, Set<MessageHandler>>();\n\n private _windowHandler: ((event: MessageEvent) => void) | null = null;\n /** Tracks recently processed messageIds to discard duplicates (same payload sent to multiple origins) */\n private readonly _seenMessageIds = new Set<string>();\n\n constructor(config: MessengerConfig) {\n this._config = config;\n this.senderId = randomUUID();\n }\n\n /** Start listening for incoming messages on window. */\n start(): void {\n if (typeof window === 'undefined') return;\n if (this._windowHandler) return;\n this._windowHandler = (event: MessageEvent<MessagePayload>) => {\n this._handleMessage(event);\n };\n window.addEventListener('message', this._windowHandler);\n }\n\n /** Stop listening and clean up. */\n stop(): void {\n if (this._windowHandler) {\n window.removeEventListener('message', this._windowHandler);\n this._windowHandler = null;\n }\n }\n\n /** Send a message payload to all configured target origins. */\n send(type: string, data?: unknown): void {\n const payload: MessagePayload = {\n type,\n data,\n senderId: this.senderId,\n messageId: randomUUID(),\n };\n\n for (const origin of this._config.allowedOrigins) {\n if (origin) {\n this._config.postMessageFn(payload, origin);\n }\n }\n }\n\n /**\n * Subscribe to messages of a given type.\n * Returns an unsubscribe function.\n */\n subscribe<T = unknown>(type: string, handler: MessageHandler<T>): () => void {\n if (!this._subscribers.has(type)) {\n this._subscribers.set(type, new Set());\n }\n this._subscribers.get(type)!.add(handler as MessageHandler);\n return () => {\n this._subscribers.get(type)?.delete(handler as MessageHandler);\n };\n }\n\n private _handleMessage(event: MessageEvent<MessagePayload>): void {\n const payload = event.data;\n if (!payload || typeof payload !== 'object') return;\n\n const { type, data, senderId: msgSenderId, messageId } = payload;\n if (!type || typeof type !== 'string') return;\n\n // Ignore messages originating from this instance\n if (msgSenderId === this.senderId) return;\n\n // Deduplicate: same messageId may arrive multiple times when the sender\n // posts to multiple target origins (one per allowedOrigin)\n if (messageId) {\n if (this._seenMessageIds.has(messageId)) return;\n this._seenMessageIds.add(messageId);\n // Keep the set bounded — clear when it exceeds 200 entries\n if (this._seenMessageIds.size > 200) this._seenMessageIds.clear();\n }\n\n // Validate message origin\n const { allowedOrigins } = this._config;\n const isAllowed =\n !allowedOrigins ||\n allowedOrigins.length === 0 ||\n allowedOrigins.includes('*') ||\n allowedOrigins\n .filter((url) => Boolean(url) && url !== '')\n .some((url) => compareUrls(url, event.origin));\n\n if (!isAllowed) return;\n\n const handlers = this._subscribers.get(type);\n\n if (handlers) {\n for (const handler of handlers) {\n handler(data, msgSenderId);\n }\n }\n }\n}\n"],"mappings":";;;;AAEA,MAAM,mBAA2B,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE;;;;;;;;AAgCpE,IAAa,sBAAb,MAAiC;CAC/B,AAAS;CACT,AAAiB;CACjB,AAAiB,+BAAe,IAAI,KAAkC;CAEtE,AAAQ,iBAAyD;;CAEjE,AAAiB,kCAAkB,IAAI,KAAa;CAEpD,YAAY,QAAyB;EACnC,KAAK,UAAU;EACf,KAAK,WAAW,YAAY;;;CAI9B,QAAc;EACZ,IAAI,OAAO,WAAW,aAAa;EACnC,IAAI,KAAK,gBAAgB;EACzB,KAAK,kBAAkB,UAAwC;GAC7D,KAAK,eAAe,MAAM;;EAE5B,OAAO,iBAAiB,WAAW,KAAK,eAAe;;;CAIzD,OAAa;EACX,IAAI,KAAK,gBAAgB;GACvB,OAAO,oBAAoB,WAAW,KAAK,eAAe;GAC1D,KAAK,iBAAiB;;;;CAK1B,KAAK,MAAc,MAAsB;EACvC,MAAM,UAA0B;GAC9B;GACA;GACA,UAAU,KAAK;GACf,WAAW,YAAY;GACxB;EAED,KAAK,MAAM,UAAU,KAAK,QAAQ,gBAChC,IAAI,QACF,KAAK,QAAQ,cAAc,SAAS,OAAO;;;;;;CASjD,UAAuB,MAAc,SAAwC;EAC3E,IAAI,CAAC,KAAK,aAAa,IAAI,KAAK,EAC9B,KAAK,aAAa,IAAI,sBAAM,IAAI,KAAK,CAAC;EAExC,KAAK,aAAa,IAAI,KAAK,CAAE,IAAI,QAA0B;EAC3D,aAAa;GACX,KAAK,aAAa,IAAI,KAAK,EAAE,OAAO,QAA0B;;;CAIlE,AAAQ,eAAe,OAA2C;EAChE,MAAM,UAAU,MAAM;EACtB,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU;EAE7C,MAAM,EAAE,MAAM,MAAM,UAAU,aAAa,cAAc;EACzD,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;EAGvC,IAAI,gBAAgB,KAAK,UAAU;EAInC,IAAI,WAAW;GACb,IAAI,KAAK,gBAAgB,IAAI,UAAU,EAAE;GACzC,KAAK,gBAAgB,IAAI,UAAU;GAEnC,IAAI,KAAK,gBAAgB,OAAO,KAAK,KAAK,gBAAgB,OAAO;;EAInE,MAAM,EAAE,mBAAmB,KAAK;EAShC,IAAI,EAPF,CAAC,kBACD,eAAe,WAAW,KAC1B,eAAe,SAAS,IAAI,IAC5B,eACG,QAAQ,QAAQ,QAAQ,IAAI,IAAI,QAAQ,GAAG,CAC3C,MAAM,QAAQA,gCAAY,KAAK,MAAM,OAAO,CAAC,GAElC;EAEhB,MAAM,WAAW,KAAK,aAAa,IAAI,KAAK;EAE5C,IAAI,UACF,KAAK,MAAM,WAAW,UACpB,QAAQ,MAAM,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"CrossFrameStateManager.cjs","names":[],"sources":["../../../src/core/CrossFrameStateManager.ts"],"sourcesContent":["import type { CrossFrameMessenger } from './CrossFrameMessenger';\n\nexport type CrossFrameStateOptions = {\n /** Whether to broadcast state changes to other frames. Default: true */\n emit?: boolean;\n /** Whether to listen for state updates from other frames. Default: true */\n receive?: boolean;\n};\n\n/**\n * CrossFrameStateManager synchronizes a single named value across frames using\n * the postMessage API via CrossFrameMessenger.\n *\n * Protocol:\n * `{key}/post` — broadcast a new value\n * `{key}/get` — request the current value from other frames\n *\n * Replaces useCrossFrameState across all frameworks.\n *\n * @fires change — CustomEvent<T> dispatched whenever the value changes (local set or received)\n */\nexport class CrossFrameStateManager<T> extends EventTarget {\n private _value: T | undefined;\n private readonly _key: string;\n private readonly _messenger: CrossFrameMessenger;\n private readonly _options: Required<CrossFrameStateOptions>;\n private readonly _unsubscribers: Array<() => void> = [];\n\n constructor(\n key: string,\n messenger: CrossFrameMessenger,\n options: CrossFrameStateOptions & { initialValue?: T } = {}\n ) {\n super();\n this._key = key;\n this._messenger = messenger;\n this._options = {\n emit: options.emit ?? true,\n receive: options.receive ?? true,\n };\n if (options.initialValue !== undefined) {\n this._value = options.initialValue;\n }\n }\n\n get value(): T | undefined {\n return this._value;\n }\n\n /** Update the value locally and broadcast it to other frames if emit is enabled. */\n set(newValue: T): void {\n this._value = newValue;\n this.dispatchEvent(new CustomEvent<T>('change', { detail: newValue }));\n if (this._options.emit) {\n this._messenger.send(`${this._key}/post`, newValue);\n }\n }\n\n /**\n * Start listening for incoming state updates and responding to /get requests.\n * If receive=true and no initial value is set, sends a /get to request the current value.\n */\n start(): void {\n if (this._options.receive) {\n const unsub = this._messenger.subscribe<T>(\n `${this._key}/post`,\n (data) => {\n this._value = data;\n this.dispatchEvent(new CustomEvent<T>('change', { detail: data }));\n }\n );\n this._unsubscribers.push(unsub);\n }\n\n if (this._options.emit) {\n // Respond to /get requests by broadcasting current value\n const unsub = this._messenger.subscribe(\n `${this._key}/get`,\n (_, originSenderId) => {\n if (originSenderId === this._messenger.senderId) return;\n if (this._value === undefined) return;\n this._messenger.send(`${this._key}/post`, this._value);\n }\n );\n this._unsubscribers.push(unsub);\n }\n\n // If receiving and no initial value, request it from other frames\n if (this._options.receive && this._value === undefined) {\n this._messenger.send(`${this._key}/get`);\n }\n }\n\n /** Stop all listeners. */\n stop(): void {\n for (const unsub of this._unsubscribers) unsub();\n this._unsubscribers.length = 0;\n }\n\n /** Broadcast the current value to all frames (useful after reconnect). */\n postCurrentValue(): void {\n if (this._value !== undefined) {\n this._messenger.send(`${this._key}/post`, this._value);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAqBA,IAAa,yBAAb,cAA+C,YAAY;CACzD,AAAQ;CACR,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB,iBAAoC,EAAE;CAEvD,YACE,KACA,WACA,UAAyD,EAAE,EAC3D;AACA,SAAO;AACP,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,WAAW;GACd,MAAM,QAAQ,QAAQ;GACtB,SAAS,QAAQ,WAAW;GAC7B;AACD,MAAI,QAAQ,iBAAiB,OAC3B,MAAK,SAAS,QAAQ;;CAI1B,IAAI,QAAuB;AACzB,SAAO,KAAK;;;CAId,IAAI,UAAmB;AACrB,OAAK,SAAS;AACd,OAAK,cAAc,IAAI,YAAe,UAAU,EAAE,QAAQ,UAAU,CAAC,CAAC;AACtE,MAAI,KAAK,SAAS,KAChB,MAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,SAAS;;;;;;CAQvD,QAAc;AACZ,MAAI,KAAK,SAAS,SAAS;GACzB,MAAM,QAAQ,KAAK,WAAW,UAC5B,GAAG,KAAK,KAAK,SACZ,SAAS;AACR,SAAK,SAAS;AACd,SAAK,cAAc,IAAI,YAAe,UAAU,EAAE,QAAQ,MAAM,CAAC,CAAC;KAErE;AACD,QAAK,eAAe,KAAK,MAAM;;AAGjC,MAAI,KAAK,SAAS,MAAM;GAEtB,MAAM,QAAQ,KAAK,WAAW,UAC5B,GAAG,KAAK,KAAK,QACZ,GAAG,mBAAmB;AACrB,QAAI,mBAAmB,KAAK,WAAW,SAAU;AACjD,QAAI,KAAK,WAAW,OAAW;AAC/B,SAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,KAAK,OAAO;KAEzD;AACD,QAAK,eAAe,KAAK,MAAM;;AAIjC,MAAI,KAAK,SAAS,WAAW,KAAK,WAAW,OAC3C,MAAK,WAAW,KAAK,GAAG,KAAK,KAAK,MAAM;;;CAK5C,OAAa;AACX,OAAK,MAAM,SAAS,KAAK,eAAgB,QAAO;AAChD,OAAK,eAAe,SAAS;;;CAI/B,mBAAyB;AACvB,MAAI,KAAK,WAAW,OAClB,MAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,KAAK,OAAO"}
1
+ {"version":3,"file":"CrossFrameStateManager.cjs","names":[],"sources":["../../../src/core/CrossFrameStateManager.ts"],"sourcesContent":["import type { CrossFrameMessenger } from './CrossFrameMessenger';\n\nexport type CrossFrameStateOptions = {\n /** Whether to broadcast state changes to other frames. Default: true */\n emit?: boolean;\n /** Whether to listen for state updates from other frames. Default: true */\n receive?: boolean;\n};\n\n/**\n * CrossFrameStateManager synchronizes a single named value across frames using\n * the postMessage API via CrossFrameMessenger.\n *\n * Protocol:\n * `{key}/post` — broadcast a new value\n * `{key}/get` — request the current value from other frames\n *\n * Replaces useCrossFrameState across all frameworks.\n *\n * @fires change — CustomEvent<T> dispatched whenever the value changes (local set or received)\n */\nexport class CrossFrameStateManager<T> extends EventTarget {\n private _value: T | undefined;\n private readonly _key: string;\n private readonly _messenger: CrossFrameMessenger;\n private readonly _options: Required<CrossFrameStateOptions>;\n private readonly _unsubscribers: Array<() => void> = [];\n\n constructor(\n key: string,\n messenger: CrossFrameMessenger,\n options: CrossFrameStateOptions & { initialValue?: T } = {}\n ) {\n super();\n this._key = key;\n this._messenger = messenger;\n this._options = {\n emit: options.emit ?? true,\n receive: options.receive ?? true,\n };\n if (options.initialValue !== undefined) {\n this._value = options.initialValue;\n }\n }\n\n get value(): T | undefined {\n return this._value;\n }\n\n /** Update the value locally and broadcast it to other frames if emit is enabled. */\n set(newValue: T): void {\n this._value = newValue;\n this.dispatchEvent(new CustomEvent<T>('change', { detail: newValue }));\n if (this._options.emit) {\n this._messenger.send(`${this._key}/post`, newValue);\n }\n }\n\n /**\n * Start listening for incoming state updates and responding to /get requests.\n * If receive=true and no initial value is set, sends a /get to request the current value.\n */\n start(): void {\n if (this._options.receive) {\n const unsub = this._messenger.subscribe<T>(\n `${this._key}/post`,\n (data) => {\n this._value = data;\n this.dispatchEvent(new CustomEvent<T>('change', { detail: data }));\n }\n );\n this._unsubscribers.push(unsub);\n }\n\n if (this._options.emit) {\n // Respond to /get requests by broadcasting current value\n const unsub = this._messenger.subscribe(\n `${this._key}/get`,\n (_, originSenderId) => {\n if (originSenderId === this._messenger.senderId) return;\n if (this._value === undefined) return;\n this._messenger.send(`${this._key}/post`, this._value);\n }\n );\n this._unsubscribers.push(unsub);\n }\n\n // If receiving and no initial value, request it from other frames\n if (this._options.receive && this._value === undefined) {\n this._messenger.send(`${this._key}/get`);\n }\n }\n\n /** Stop all listeners. */\n stop(): void {\n for (const unsub of this._unsubscribers) unsub();\n this._unsubscribers.length = 0;\n }\n\n /** Broadcast the current value to all frames (useful after reconnect). */\n postCurrentValue(): void {\n if (this._value !== undefined) {\n this._messenger.send(`${this._key}/post`, this._value);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAqBA,IAAa,yBAAb,cAA+C,YAAY;CACzD,AAAQ;CACR,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB,iBAAoC,EAAE;CAEvD,YACE,KACA,WACA,UAAyD,EAAE,EAC3D;EACA,OAAO;EACP,KAAK,OAAO;EACZ,KAAK,aAAa;EAClB,KAAK,WAAW;GACd,MAAM,QAAQ,QAAQ;GACtB,SAAS,QAAQ,WAAW;GAC7B;EACD,IAAI,QAAQ,iBAAiB,QAC3B,KAAK,SAAS,QAAQ;;CAI1B,IAAI,QAAuB;EACzB,OAAO,KAAK;;;CAId,IAAI,UAAmB;EACrB,KAAK,SAAS;EACd,KAAK,cAAc,IAAI,YAAe,UAAU,EAAE,QAAQ,UAAU,CAAC,CAAC;EACtE,IAAI,KAAK,SAAS,MAChB,KAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,SAAS;;;;;;CAQvD,QAAc;EACZ,IAAI,KAAK,SAAS,SAAS;GACzB,MAAM,QAAQ,KAAK,WAAW,UAC5B,GAAG,KAAK,KAAK,SACZ,SAAS;IACR,KAAK,SAAS;IACd,KAAK,cAAc,IAAI,YAAe,UAAU,EAAE,QAAQ,MAAM,CAAC,CAAC;KAErE;GACD,KAAK,eAAe,KAAK,MAAM;;EAGjC,IAAI,KAAK,SAAS,MAAM;GAEtB,MAAM,QAAQ,KAAK,WAAW,UAC5B,GAAG,KAAK,KAAK,QACZ,GAAG,mBAAmB;IACrB,IAAI,mBAAmB,KAAK,WAAW,UAAU;IACjD,IAAI,KAAK,WAAW,QAAW;IAC/B,KAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,KAAK,OAAO;KAEzD;GACD,KAAK,eAAe,KAAK,MAAM;;EAIjC,IAAI,KAAK,SAAS,WAAW,KAAK,WAAW,QAC3C,KAAK,WAAW,KAAK,GAAG,KAAK,KAAK,MAAM;;;CAK5C,OAAa;EACX,KAAK,MAAM,SAAS,KAAK,gBAAgB,OAAO;EAChD,KAAK,eAAe,SAAS;;;CAI/B,mBAAyB;EACvB,IAAI,KAAK,WAAW,QAClB,KAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,KAAK,OAAO"}
@@ -1 +1 @@
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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"}
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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"}
@@ -1 +1 @@
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;CAClC,AAAiB;CACjB,AAAQ,oBAA0C;CAClD,AAAQ,oBAAyC;CAEjD,YAAY,WAAgC;AAC1C,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"}
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;CAClC,AAAiB;CACjB,AAAQ,oBAA0C;CAClD,AAAQ,oBAAyC;CAEjD,YAAY,WAAgC;EAC1C,KAAK,aAAa;;;CAIpB,mBAAyB;EACvB,IAAI,OAAO,WAAW,aAAa;EACnC,KAAK,0BAA0B;GAC7B,KAAK,WAAW,+BAAwC;;EAE1D,OAAO,iBAAiB,aAAa,KAAK,kBAAkB;;;CAI9D,cAAoB;EAClB,KAAK,oBAAoB,KAAK,WAAW,qCAEvCA,0CACD;;CAGH,kBAAwB;EACtB,IAAI,KAAK,mBAAmB;GAC1B,OAAO,oBAAoB,aAAa,KAAK,kBAAkB;GAC/D,KAAK,oBAAoB;;;CAI7B,aAAmB;EACjB,KAAK,qBAAqB;EAC1B,KAAK,oBAAoB"}
@@ -1 +1 @@
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;CAC3B,AAAiB;CACjB,AAAQ,qBAAsD;CAC9D,AAAQ,wBAA4D;CACpE,AAAQ,aAA6C,EAAE;CAEvD,YAAY,WAAgC;AAC1C,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
+ {"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;CAC3B,AAAiB;CACjB,AAAQ,qBAAsD;CAC9D,AAAQ,wBAA4D;CACpE,AAAQ,aAA6C,EAAE;CAEvD,YAAY,WAAgC;EAC1C,KAAK,aAAa;;CAGpB,QAAc;EACZ,IAAI,OAAO,WAAW,aAAa;EAEnC,MAAM,uBAAuB;GAC3B,KAAK,WAAW,KACd,yBAAkC,QAClC,OAAO,SAAS,SACjB;;EAGH,KAAK,qBAAqB,QAAQ;EAClC,KAAK,wBAAwB,QAAQ;EAErC,MAAM,wBAAwB,WAC5B,SAEE,GAAG,MACH;GACA,OAAO,MAAM,MAAM,KAAK;GACxB,OAAO,cAAc,IAAI,MAAM,iBAAiB,CAAC;;EAGrD,QAAQ,YAAY,qBAClB,KAAK,mBACN;EACD,QAAQ,eAAe,qBACrB,KAAK,sBACN;EAED,KAAK,MAAM,aAAa;GACtB;GACA;GACA;GACA;GACD,EAAW;GACV,MAAM,WAAW;GACjB,OAAO,iBAAiB,WAAW,SAAS;GAC5C,KAAK,WAAW,KAAK,CAAC,WAAW,SAAS,CAAC;;EAG7C,gBAAgB;;CAGlB,OAAa;EACX,IAAI,OAAO,WAAW,aAAa;EAEnC,KAAK,MAAM,CAAC,WAAW,aAAa,KAAK,YACvC,OAAO,oBAAoB,WAAW,SAAS;EAEjD,KAAK,aAAa,EAAE;EAEpB,IAAI,KAAK,oBAAoB;GAC3B,QAAQ,YAAY,KAAK;GACzB,KAAK,qBAAqB;;EAE5B,IAAI,KAAK,uBAAuB;GAC9B,QAAQ,eAAe,KAAK;GAC5B,KAAK,wBAAwB"}
@@ -1 +1 @@
1
- {"version":3,"file":"globalManager.cjs","names":["windowGlobals","customEvent"],"sources":["../../../src/core/globalManager.ts"],"sourcesContent":["import type { EditorStateManager } from './EditorStateManager';\n\n/**\n * Type for storing custom properties on the global window object.\n * Used to safely attach singleton instances across multiple module instances.\n */\ntype WindowWithEditorGlobals = typeof window & {\n [MANAGER_KEY]?: EditorStateManager | null;\n [EVENTS_KEY]?: EventTarget;\n [key: string]: unknown;\n};\n\n/**\n * Keys on window used to share the singleton across multiple module instances.\n * When bundlers include @intlayer/editor more than once (e.g. framework package +\n * app), each copy has its own module-level variables. Using window ensures they\n * all read/write the same manager and event-target.\n */\nconst MANAGER_KEY = '__intlayer_editor_manager__';\nconst EVENTS_KEY = '__intlayer_editor_manager_events__';\n\n/**\n * Retrieves or creates a shared EventTarget for global editor manager events.\n * In browser environments, stores the EventTarget on window to ensure sharing\n * across multiple module instances. In SSR environments, creates a fresh\n * EventTarget (no sharing needed on server-side).\n *\n * @returns The shared EventTarget instance for dispatching manager change events\n * @private\n */\nconst getEventTarget = (): EventTarget => {\n if (typeof window === 'undefined') {\n // SSR fallback — create a fresh one (no sharing needed server-side)\n return new EventTarget();\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n if (!windowGlobals[EVENTS_KEY]) {\n windowGlobals[EVENTS_KEY] = new EventTarget();\n }\n\n return windowGlobals[EVENTS_KEY] as EventTarget;\n};\n\n/**\n * Retrieves the global editor state manager instance.\n * Returns null if no manager has been set or in SSR environments where\n * window is undefined.\n *\n * @returns The global EditorStateManager instance or null if not set\n */\nexport const getGlobalEditorManager = (): EditorStateManager | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n const manager = windowGlobals[MANAGER_KEY];\n\n return manager ?? null;\n};\n\n/**\n * Sets the global editor state manager instance and notifies all listeners\n * of the change through a CustomEvent.\n *\n * @param manager - The EditorStateManager instance to set globally, or null to clear it\n */\nexport const setGlobalEditorManager = (\n manager: EditorStateManager | null\n): void => {\n if (typeof window !== 'undefined') {\n const windowGlobals = window as WindowWithEditorGlobals;\n windowGlobals[MANAGER_KEY] = manager;\n }\n\n const eventTarget = getEventTarget();\n\n eventTarget.dispatchEvent(\n new CustomEvent<EditorStateManager | null>('change', { detail: manager })\n );\n};\n\n/**\n * Registers a callback function to be invoked whenever the global editor\n * manager changes. Useful for reactive updates across the application.\n *\n * @param changeCallback - Function to invoke with the new manager state whenever it changes\n * @returns An unsubscribe function that removes the listener when called\n *\n * @example\n * ```typescript\n * const unsubscribe = onGlobalEditorManagerChange((manager) => {\n * console.log('Manager updated:', manager);\n * });\n *\n * // Later, clean up the listener\n * unsubscribe();\n * ```\n */\nexport const onGlobalEditorManagerChange = (\n changeCallback: (manager: EditorStateManager | null) => void\n): (() => void) => {\n const eventTarget = getEventTarget();\n\n const eventHandler = (event: Event) => {\n const customEvent = event as CustomEvent<EditorStateManager | null>;\n changeCallback(customEvent.detail);\n };\n\n eventTarget.addEventListener('change', eventHandler);\n\n return () => {\n eventTarget.removeEventListener('change', eventHandler);\n };\n};\n"],"mappings":";;;;;;;;;AAkBA,MAAM,cAAc;AACpB,MAAM,aAAa;;;;;;;;;;AAWnB,MAAM,uBAAoC;AACxC,KAAI,OAAO,WAAW,YAEpB,QAAO,IAAI,aAAa;CAG1B,MAAM,gBAAgB;AACtB,KAAI,CAAC,cAAc,YACjB,eAAc,cAAc,IAAI,aAAa;AAG/C,QAAO,cAAc;;;;;;;;;AAUvB,MAAa,+BAA0D;AACrE,KAAI,OAAO,WAAW,YACpB,QAAO;AAMT,QAFgBA,OAAc,gBAEZ;;;;;;;;AASpB,MAAa,0BACX,YACS;AACT,KAAI,OAAO,WAAW,aAAa;EACjC,MAAM,gBAAgB;AACtB,gBAAc,eAAe;;AAK/B,CAFoB,gBAET,CAAC,cACV,IAAI,YAAuC,UAAU,EAAE,QAAQ,SAAS,CAAC,CAC1E;;;;;;;;;;;;;;;;;;;AAoBH,MAAa,+BACX,mBACiB;CACjB,MAAM,cAAc,gBAAgB;CAEpC,MAAM,gBAAgB,UAAiB;AAErC,iBAAeC,MAAY,OAAO;;AAGpC,aAAY,iBAAiB,UAAU,aAAa;AAEpD,cAAa;AACX,cAAY,oBAAoB,UAAU,aAAa"}
1
+ {"version":3,"file":"globalManager.cjs","names":["windowGlobals","customEvent"],"sources":["../../../src/core/globalManager.ts"],"sourcesContent":["import type { EditorStateManager } from './EditorStateManager';\n\n/**\n * Type for storing custom properties on the global window object.\n * Used to safely attach singleton instances across multiple module instances.\n */\ntype WindowWithEditorGlobals = typeof window & {\n [MANAGER_KEY]?: EditorStateManager | null;\n [EVENTS_KEY]?: EventTarget;\n [key: string]: unknown;\n};\n\n/**\n * Keys on window used to share the singleton across multiple module instances.\n * When bundlers include @intlayer/editor more than once (e.g. framework package +\n * app), each copy has its own module-level variables. Using window ensures they\n * all read/write the same manager and event-target.\n */\nconst MANAGER_KEY = '__intlayer_editor_manager__';\nconst EVENTS_KEY = '__intlayer_editor_manager_events__';\n\n/**\n * Retrieves or creates a shared EventTarget for global editor manager events.\n * In browser environments, stores the EventTarget on window to ensure sharing\n * across multiple module instances. In SSR environments, creates a fresh\n * EventTarget (no sharing needed on server-side).\n *\n * @returns The shared EventTarget instance for dispatching manager change events\n * @private\n */\nconst getEventTarget = (): EventTarget => {\n if (typeof window === 'undefined') {\n // SSR fallback — create a fresh one (no sharing needed server-side)\n return new EventTarget();\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n if (!windowGlobals[EVENTS_KEY]) {\n windowGlobals[EVENTS_KEY] = new EventTarget();\n }\n\n return windowGlobals[EVENTS_KEY] as EventTarget;\n};\n\n/**\n * Retrieves the global editor state manager instance.\n * Returns null if no manager has been set or in SSR environments where\n * window is undefined.\n *\n * @returns The global EditorStateManager instance or null if not set\n */\nexport const getGlobalEditorManager = (): EditorStateManager | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n const manager = windowGlobals[MANAGER_KEY];\n\n return manager ?? null;\n};\n\n/**\n * Sets the global editor state manager instance and notifies all listeners\n * of the change through a CustomEvent.\n *\n * @param manager - The EditorStateManager instance to set globally, or null to clear it\n */\nexport const setGlobalEditorManager = (\n manager: EditorStateManager | null\n): void => {\n if (typeof window !== 'undefined') {\n const windowGlobals = window as WindowWithEditorGlobals;\n windowGlobals[MANAGER_KEY] = manager;\n }\n\n const eventTarget = getEventTarget();\n\n eventTarget.dispatchEvent(\n new CustomEvent<EditorStateManager | null>('change', { detail: manager })\n );\n};\n\n/**\n * Registers a callback function to be invoked whenever the global editor\n * manager changes. Useful for reactive updates across the application.\n *\n * @param changeCallback - Function to invoke with the new manager state whenever it changes\n * @returns An unsubscribe function that removes the listener when called\n *\n * @example\n * ```typescript\n * const unsubscribe = onGlobalEditorManagerChange((manager) => {\n * console.log('Manager updated:', manager);\n * });\n *\n * // Later, clean up the listener\n * unsubscribe();\n * ```\n */\nexport const onGlobalEditorManagerChange = (\n changeCallback: (manager: EditorStateManager | null) => void\n): (() => void) => {\n const eventTarget = getEventTarget();\n\n const eventHandler = (event: Event) => {\n const customEvent = event as CustomEvent<EditorStateManager | null>;\n changeCallback(customEvent.detail);\n };\n\n eventTarget.addEventListener('change', eventHandler);\n\n return () => {\n eventTarget.removeEventListener('change', eventHandler);\n };\n};\n"],"mappings":";;;;;;;;;AAkBA,MAAM,cAAc;AACpB,MAAM,aAAa;;;;;;;;;;AAWnB,MAAM,uBAAoC;CACxC,IAAI,OAAO,WAAW,aAEpB,OAAO,IAAI,aAAa;CAG1B,MAAM,gBAAgB;CACtB,IAAI,CAAC,cAAc,aACjB,cAAc,cAAc,IAAI,aAAa;CAG/C,OAAO,cAAc;;;;;;;;;AAUvB,MAAa,+BAA0D;CACrE,IAAI,OAAO,WAAW,aACpB,OAAO;CAMT,OAFgBA,OAAc,gBAEZ;;;;;;;;AASpB,MAAa,0BACX,YACS;CACT,IAAI,OAAO,WAAW,aAAa;EACjC,MAAM,gBAAgB;EACtB,cAAc,eAAe;;CAK/B,AAFoB,gBAET,CAAC,cACV,IAAI,YAAuC,UAAU,EAAE,QAAQ,SAAS,CAAC,CAC1E;;;;;;;;;;;;;;;;;;;AAoBH,MAAa,+BACX,mBACiB;CACjB,MAAM,cAAc,gBAAgB;CAEpC,MAAM,gBAAgB,UAAiB;EAErC,eAAeC,MAAY,OAAO;;CAGpC,YAAY,iBAAiB,UAAU,aAAa;CAEpD,aAAa;EACX,YAAY,oBAAoB,UAAU,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"initEditorClient.cjs","names":["editor","getGlobalEditorManager","EditorStateManager"],"sources":["../../../src/core/initEditorClient.ts"],"sourcesContent":["import { default as configuration, editor } from '@intlayer/config/built';\nimport { defineIntlayerElements } from '../components';\nimport type { MessengerConfig } from './CrossFrameMessenger';\nimport { EditorStateManager } from './EditorStateManager';\nimport {\n getGlobalEditorManager,\n setGlobalEditorManager,\n} from './globalManager';\n\nexport const buildClientMessengerConfig = (): MessengerConfig => {\n return {\n allowedOrigins: [editor?.editorURL, editor?.cmsURL].filter(\n Boolean\n ) as string[],\n postMessageFn: (payload: unknown, origin: string) => {\n if (typeof window === 'undefined') return;\n\n const isInIframe = window.self !== window.top;\n\n if (!isInIframe) return;\n window.parent?.postMessage(payload, origin);\n },\n };\n};\n\n/** Reference count — tracks how many providers have called initEditorClient. */\nlet _clientRefCount = 0;\n\n/**\n * Initialize the Intlayer editor client singleton.\n * Safe to call multiple times — returns the existing manager if already initialized.\n * Increments a reference counter so nested providers don't destroy the manager\n * prematurely when the inner provider unmounts.\n */\nexport const initEditorClient = (): EditorStateManager => {\n _clientRefCount++;\n\n const existing = getGlobalEditorManager();\n if (existing) return existing;\n\n const manager = new EditorStateManager({\n mode: 'client',\n messenger: buildClientMessengerConfig(),\n configuration,\n });\n\n setGlobalEditorManager(manager);\n defineIntlayerElements();\n manager.start();\n\n return manager;\n};\n\n/**\n * Decrement the reference count and stop the global editor client singleton\n * only when the last provider unmounts.\n */\nexport const stopEditorClient = (): void => {\n _clientRefCount = Math.max(0, _clientRefCount - 1);\n\n if (_clientRefCount > 0) return;\n\n const manager = getGlobalEditorManager();\n manager?.stop();\n setGlobalEditorManager(null);\n};\n"],"mappings":";;;;;;;;;AASA,MAAa,mCAAoD;AAC/D,QAAO;EACL,gBAAgB,CAACA,+BAAQ,WAAWA,+BAAQ,OAAO,CAAC,OAClD,QACD;EACD,gBAAgB,SAAkB,WAAmB;AACnD,OAAI,OAAO,WAAW,YAAa;AAInC,OAAI,EAFe,OAAO,SAAS,OAAO,KAEzB;AACjB,UAAO,QAAQ,YAAY,SAAS,OAAO;;EAE9C;;;AAIH,IAAI,kBAAkB;;;;;;;AAQtB,MAAa,yBAA6C;AACxD;CAEA,MAAM,WAAWC,mDAAwB;AACzC,KAAI,SAAU,QAAO;CAErB,MAAM,UAAU,IAAIC,mDAAmB;EACrC,MAAM;EACN,WAAW,4BAA4B;EACvC;EACD,CAAC;AAEF,mDAAuB,QAAQ;AAC/B,4DAAwB;AACxB,SAAQ,OAAO;AAEf,QAAO;;;;;;AAOT,MAAa,yBAA+B;AAC1C,mBAAkB,KAAK,IAAI,GAAG,kBAAkB,EAAE;AAElD,KAAI,kBAAkB,EAAG;AAGzB,CADgBD,mDACT,EAAE,MAAM;AACf,mDAAuB,KAAK"}
1
+ {"version":3,"file":"initEditorClient.cjs","names":["editor","getGlobalEditorManager","EditorStateManager"],"sources":["../../../src/core/initEditorClient.ts"],"sourcesContent":["import { default as configuration, editor } from '@intlayer/config/built';\nimport { defineIntlayerElements } from '../components';\nimport type { MessengerConfig } from './CrossFrameMessenger';\nimport { EditorStateManager } from './EditorStateManager';\nimport {\n getGlobalEditorManager,\n setGlobalEditorManager,\n} from './globalManager';\n\nexport const buildClientMessengerConfig = (): MessengerConfig => {\n return {\n allowedOrigins: [editor?.editorURL, editor?.cmsURL].filter(\n Boolean\n ) as string[],\n postMessageFn: (payload: unknown, origin: string) => {\n if (typeof window === 'undefined') return;\n\n const isInIframe = window.self !== window.top;\n\n if (!isInIframe) return;\n window.parent?.postMessage(payload, origin);\n },\n };\n};\n\n/** Reference count — tracks how many providers have called initEditorClient. */\nlet _clientRefCount = 0;\n\n/**\n * Initialize the Intlayer editor client singleton.\n * Safe to call multiple times — returns the existing manager if already initialized.\n * Increments a reference counter so nested providers don't destroy the manager\n * prematurely when the inner provider unmounts.\n */\nexport const initEditorClient = (): EditorStateManager => {\n _clientRefCount++;\n\n const existing = getGlobalEditorManager();\n if (existing) return existing;\n\n const manager = new EditorStateManager({\n mode: 'client',\n messenger: buildClientMessengerConfig(),\n configuration,\n });\n\n setGlobalEditorManager(manager);\n defineIntlayerElements();\n manager.start();\n\n return manager;\n};\n\n/**\n * Decrement the reference count and stop the global editor client singleton\n * only when the last provider unmounts.\n */\nexport const stopEditorClient = (): void => {\n _clientRefCount = Math.max(0, _clientRefCount - 1);\n\n if (_clientRefCount > 0) return;\n\n const manager = getGlobalEditorManager();\n manager?.stop();\n setGlobalEditorManager(null);\n};\n"],"mappings":";;;;;;;;;AASA,MAAa,mCAAoD;CAC/D,OAAO;EACL,gBAAgB,CAACA,+BAAQ,WAAWA,+BAAQ,OAAO,CAAC,OAClD,QACD;EACD,gBAAgB,SAAkB,WAAmB;GACnD,IAAI,OAAO,WAAW,aAAa;GAInC,IAAI,EAFe,OAAO,SAAS,OAAO,MAEzB;GACjB,OAAO,QAAQ,YAAY,SAAS,OAAO;;EAE9C;;;AAIH,IAAI,kBAAkB;;;;;;;AAQtB,MAAa,yBAA6C;CACxD;CAEA,MAAM,WAAWC,mDAAwB;CACzC,IAAI,UAAU,OAAO;CAErB,MAAM,UAAU,IAAIC,mDAAmB;EACrC,MAAM;EACN,WAAW,4BAA4B;EACvC;EACD,CAAC;CAEF,kDAAuB,QAAQ;CAC/B,2DAAwB;CACxB,QAAQ,OAAO;CAEf,OAAO;;;;;;AAOT,MAAa,yBAA+B;CAC1C,kBAAkB,KAAK,IAAI,GAAG,kBAAkB,EAAE;CAElD,IAAI,kBAAkB,GAAG;CAGzB,AADgBD,mDACT,EAAE,MAAM;CACf,kDAAuB,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"mergeIframeClick.cjs","names":[],"sources":["../../src/mergeIframeClick.ts"],"sourcesContent":["// Listener for messages from the iframe\nexport const mergeIframeClick = (event: MessageEvent) => {\n // Simulate or merge the iframe message with a click event\n const simulatedMouseDownEvent = new MouseEvent('mousedown', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n const simulatedClickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n\n // Optionally attach additional properties from the iframe message\n Object.assign(simulatedClickEvent, { iframeData: event });\n Object.assign(simulatedMouseDownEvent, { iframeData: event });\n\n // Dispatch the simulated click event on the window or a specific element\n window.dispatchEvent(simulatedClickEvent);\n window.dispatchEvent(simulatedMouseDownEvent);\n};\n"],"mappings":";;;AACA,MAAa,oBAAoB,UAAwB;CAEvD,MAAM,0BAA0B,IAAI,WAAW,aAAa;EAC1D,SAAS;EACT,YAAY;EACZ,MAAM;EACP,CAAC;CACF,MAAM,sBAAsB,IAAI,WAAW,SAAS;EAClD,SAAS;EACT,YAAY;EACZ,MAAM;EACP,CAAC;AAGF,QAAO,OAAO,qBAAqB,EAAE,YAAY,OAAO,CAAC;AACzD,QAAO,OAAO,yBAAyB,EAAE,YAAY,OAAO,CAAC;AAG7D,QAAO,cAAc,oBAAoB;AACzC,QAAO,cAAc,wBAAwB"}
1
+ {"version":3,"file":"mergeIframeClick.cjs","names":[],"sources":["../../src/mergeIframeClick.ts"],"sourcesContent":["// Listener for messages from the iframe\nexport const mergeIframeClick = (event: MessageEvent) => {\n // Simulate or merge the iframe message with a click event\n const simulatedMouseDownEvent = new MouseEvent('mousedown', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n const simulatedClickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n\n // Optionally attach additional properties from the iframe message\n Object.assign(simulatedClickEvent, { iframeData: event });\n Object.assign(simulatedMouseDownEvent, { iframeData: event });\n\n // Dispatch the simulated click event on the window or a specific element\n window.dispatchEvent(simulatedClickEvent);\n window.dispatchEvent(simulatedMouseDownEvent);\n};\n"],"mappings":";;;AACA,MAAa,oBAAoB,UAAwB;CAEvD,MAAM,0BAA0B,IAAI,WAAW,aAAa;EAC1D,SAAS;EACT,YAAY;EACZ,MAAM;EACP,CAAC;CACF,MAAM,sBAAsB,IAAI,WAAW,SAAS;EAClD,SAAS;EACT,YAAY;EACZ,MAAM;EACP,CAAC;CAGF,OAAO,OAAO,qBAAqB,EAAE,YAAY,OAAO,CAAC;CACzD,OAAO,OAAO,yBAAyB,EAAE,YAAY,OAAO,CAAC;CAG7D,OAAO,cAAc,oBAAoB;CACzC,OAAO,cAAc,wBAAwB"}
@@ -1 +1 @@
1
- {"version":3,"file":"messageKey.cjs","names":[],"sources":["../../src/messageKey.ts"],"sourcesContent":["export enum MessageKey {\n /** Client → editor: announces the client is ready (also used as response to ARE_YOU_THERE) */\n INTLAYER_CLIENT_READY = 'INTLAYER_CLIENT_READY',\n /** Editor → client: asks if the client is still alive */\n INTLAYER_ARE_YOU_THERE = 'INTLAYER_ARE_YOU_THERE',\n /** Editor → client: instructs the client to activate the editor selector */\n INTLAYER_EDITOR_ACTIVATE = 'INTLAYER_EDITOR_ACTIVATE',\n /** Client → editor: instructs the editor that the client is enabled */\n INTLAYER_EDITOR_ENABLED = 'INTLAYER_EDITOR_ENABLED',\n\n /** Client → editor: send information related the app */\n INTLAYER_CONFIGURATION = 'INTLAYER_CONFIGURATION',\n INTLAYER_CURRENT_LOCALE = 'INTLAYER_CURRENT_LOCALE',\n INTLAYER_URL_CHANGE = 'INTLAYER_URL_CHANGE',\n\n /** Client → editor: load and update content */\n INTLAYER_LOCALE_DICTIONARIES_CHANGED = 'INTLAYER_LOCALE_DICTIONARIES_CHANGED',\n INTLAYER_DISTANT_DICTIONARIES_CHANGED = 'INTLAYER_DISTANT_DICTIONARIES_CHANGED',\n\n /** Client → editor & Editor → client: Update focus and changed content */\n INTLAYER_HOVERED_CONTENT_CHANGED = 'INTLAYER_HOVERED_CONTENT_CHANGED',\n INTLAYER_FOCUSED_CONTENT_CHANGED = 'INTLAYER_FOCUSED_CONTENT_CHANGED',\n INTLAYER_EDITED_CONTENT_CHANGED = 'INTLAYER_EDITED_CONTENT_CHANGED',\n\n /** Client → editor: Helper to sync iframe client reactivity on click client */\n INTLAYER_IFRAME_CLICKED = 'INTLAYER_IFRAME_CLICKED',\n}\n"],"mappings":";;;AAAA,IAAY,aAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAGA;AACA;AACA;;AAGA;AACA;;AAGA;AACA;AACA;;AAGA;;KACD"}
1
+ {"version":3,"file":"messageKey.cjs","names":[],"sources":["../../src/messageKey.ts"],"sourcesContent":["export enum MessageKey {\n /** Client → editor: announces the client is ready (also used as response to ARE_YOU_THERE) */\n INTLAYER_CLIENT_READY = 'INTLAYER_CLIENT_READY',\n /** Editor → client: asks if the client is still alive */\n INTLAYER_ARE_YOU_THERE = 'INTLAYER_ARE_YOU_THERE',\n /** Editor → client: instructs the client to activate the editor selector */\n INTLAYER_EDITOR_ACTIVATE = 'INTLAYER_EDITOR_ACTIVATE',\n /** Client → editor: instructs the editor that the client is enabled */\n INTLAYER_EDITOR_ENABLED = 'INTLAYER_EDITOR_ENABLED',\n\n /** Client → editor: send information related the app */\n INTLAYER_CONFIGURATION = 'INTLAYER_CONFIGURATION',\n INTLAYER_CURRENT_LOCALE = 'INTLAYER_CURRENT_LOCALE',\n INTLAYER_URL_CHANGE = 'INTLAYER_URL_CHANGE',\n\n /** Client → editor: load and update content */\n INTLAYER_LOCALE_DICTIONARIES_CHANGED = 'INTLAYER_LOCALE_DICTIONARIES_CHANGED',\n INTLAYER_DISTANT_DICTIONARIES_CHANGED = 'INTLAYER_DISTANT_DICTIONARIES_CHANGED',\n\n /** Client → editor & Editor → client: Update focus and changed content */\n INTLAYER_HOVERED_CONTENT_CHANGED = 'INTLAYER_HOVERED_CONTENT_CHANGED',\n INTLAYER_FOCUSED_CONTENT_CHANGED = 'INTLAYER_FOCUSED_CONTENT_CHANGED',\n INTLAYER_EDITED_CONTENT_CHANGED = 'INTLAYER_EDITED_CONTENT_CHANGED',\n\n /** Client → editor: Helper to sync iframe client reactivity on click client */\n INTLAYER_IFRAME_CLICKED = 'INTLAYER_IFRAME_CLICKED',\n}\n"],"mappings":";;;AAAA,IAAY,aAAL;;CAEL;;CAEA;;CAEA;;CAEA;;CAGA;CACA;CACA;;CAGA;CACA;;CAGA;CACA;CACA;;CAGA;;KACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"compareUrls.mjs","names":[],"sources":["../../src/compareUrls.ts"],"sourcesContent":["/**\n * Compare two URLs for equality.\n * This function is used to determine if a message originates from the same origin.\n *\n * ```js\n * // Example usage\n * console.log(compareUrls(\"http://localhost:5173/\", \"http://localhost:5173\")); // true\n * console.log(compareUrls(\"http://localhost:5173\", \"http://localhost:5173?myParam=true\")); // true\n * console.log(compareUrls(\"http://localhost:5173/subpath\", \"http://localhost:5173\")); // true\n * console.log(compareUrls(\"http://localhost:5172\", \"http://localhost:5173\")); // false\n * ```\n *\n * @param url1 - The first URL to compare.\n * @param url2 - The second URL to compare.\n * @returns Whether the two URLs are equal.\n */\nexport const compareUrls = (url1: string, url2: string): boolean => {\n try {\n const parsedUrl1 = new URL(url1);\n const parsedUrl2 = new URL(url2);\n\n // Compare protocol, hostname, and port\n if (\n parsedUrl1.protocol !== parsedUrl2.protocol ||\n parsedUrl1.hostname !== parsedUrl2.hostname ||\n parsedUrl1.port !== parsedUrl2.port\n ) {\n return false;\n }\n\n // One URL should not have a subpath while the other does\n const path1 = parsedUrl1.pathname.replace(/\\/$/, ''); // Remove trailing slash\n const path2 = parsedUrl2.pathname.replace(/\\/$/, '');\n\n if (path1 !== '' && path2 !== '' && path1 !== path2) {\n return false;\n }\n\n return true;\n } catch (error) {\n console.error('Invalid URL(s)', error, { url1, url2 });\n return false;\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,MAAa,eAAe,MAAc,SAA0B;AAClE,KAAI;EACF,MAAM,aAAa,IAAI,IAAI,KAAK;EAChC,MAAM,aAAa,IAAI,IAAI,KAAK;AAGhC,MACE,WAAW,aAAa,WAAW,YACnC,WAAW,aAAa,WAAW,YACnC,WAAW,SAAS,WAAW,KAE/B,QAAO;EAIT,MAAM,QAAQ,WAAW,SAAS,QAAQ,OAAO,GAAG;EACpD,MAAM,QAAQ,WAAW,SAAS,QAAQ,OAAO,GAAG;AAEpD,MAAI,UAAU,MAAM,UAAU,MAAM,UAAU,MAC5C,QAAO;AAGT,SAAO;UACA,OAAO;AACd,UAAQ,MAAM,kBAAkB,OAAO;GAAE;GAAM;GAAM,CAAC;AACtD,SAAO"}
1
+ {"version":3,"file":"compareUrls.mjs","names":[],"sources":["../../src/compareUrls.ts"],"sourcesContent":["/**\n * Compare two URLs for equality.\n * This function is used to determine if a message originates from the same origin.\n *\n * ```js\n * // Example usage\n * console.log(compareUrls(\"http://localhost:5173/\", \"http://localhost:5173\")); // true\n * console.log(compareUrls(\"http://localhost:5173\", \"http://localhost:5173?myParam=true\")); // true\n * console.log(compareUrls(\"http://localhost:5173/subpath\", \"http://localhost:5173\")); // true\n * console.log(compareUrls(\"http://localhost:5172\", \"http://localhost:5173\")); // false\n * ```\n *\n * @param url1 - The first URL to compare.\n * @param url2 - The second URL to compare.\n * @returns Whether the two URLs are equal.\n */\nexport const compareUrls = (url1: string, url2: string): boolean => {\n try {\n const parsedUrl1 = new URL(url1);\n const parsedUrl2 = new URL(url2);\n\n // Compare protocol, hostname, and port\n if (\n parsedUrl1.protocol !== parsedUrl2.protocol ||\n parsedUrl1.hostname !== parsedUrl2.hostname ||\n parsedUrl1.port !== parsedUrl2.port\n ) {\n return false;\n }\n\n // One URL should not have a subpath while the other does\n const path1 = parsedUrl1.pathname.replace(/\\/$/, ''); // Remove trailing slash\n const path2 = parsedUrl2.pathname.replace(/\\/$/, '');\n\n if (path1 !== '' && path2 !== '' && path1 !== path2) {\n return false;\n }\n\n return true;\n } catch (error) {\n console.error('Invalid URL(s)', error, { url1, url2 });\n return false;\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,MAAa,eAAe,MAAc,SAA0B;CAClE,IAAI;EACF,MAAM,aAAa,IAAI,IAAI,KAAK;EAChC,MAAM,aAAa,IAAI,IAAI,KAAK;EAGhC,IACE,WAAW,aAAa,WAAW,YACnC,WAAW,aAAa,WAAW,YACnC,WAAW,SAAS,WAAW,MAE/B,OAAO;EAIT,MAAM,QAAQ,WAAW,SAAS,QAAQ,OAAO,GAAG;EACpD,MAAM,QAAQ,WAAW,SAAS,QAAQ,OAAO,GAAG;EAEpD,IAAI,UAAU,MAAM,UAAU,MAAM,UAAU,OAC5C,OAAO;EAGT,OAAO;UACA,OAAO;EACd,QAAQ,MAAM,kBAAkB,OAAO;GAAE;GAAM;GAAM,CAAC;EACtD,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelector.mjs","names":[],"sources":["../../../src/components/ContentSelector.ts"],"sourcesContent":["import { defineIntlayerContentSelectorWrapper } from './ContentSelectorWrapper';\nimport { defineIntlayerEditedContent } from './EditedContent';\nimport { defineIntlayerEditorElement } from './IntlayerEditor';\n\nconst DEFAULT_PRESS_DURATION = 250;\n\nconst STYLES = `\n :host {\n display: contents;\n }\n\n .wrapper {\n display: inline-block;\n cursor: pointer;\n user-select: none;\n border-radius: 0.375rem;\n outline-width: 2px;\n outline-offset: 4px;\n outline-style: solid;\n outline-color: transparent;\n transition: all 100ms 50ms ease-in-out;\n }\n\n .wrapper[data-active] {\n outline-color: inherit;\n }\n`;\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector>\n *\n * A framework-agnostic web component that wraps content with Intlayer editor\n * selection UI (hover outline, long-press to select, click-outside to deselect).\n *\n * @fires intlayer:press - Fired after a long press (pressDuration ms). Bubbles.\n * @fires intlayer:hover - Fired on mouseenter. Bubbles.\n * @fires intlayer:unhover - Fired on mouseleave / mouseup. Bubbles.\n * @fires intlayer:click-outside - Fired when a click occurs outside the element. Bubbles.\n *\n * @prop {boolean} isSelecting - Whether this element is currently selected (external state)\n * @prop {number} pressDuration - Long-press threshold in ms. Default: 250\n */\nexport class IntlayerContentSelectorElement extends _HTMLElement {\n private _isSelecting = false;\n private _pressDuration = DEFAULT_PRESS_DURATION;\n private _isHovered = false;\n private _isSelectingState = false;\n private _wrapper: HTMLSpanElement;\n private _pressTimer: ReturnType<typeof setTimeout> | null = null;\n private _clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['is-selecting', 'press-duration'];\n }\n\n get isSelecting(): boolean {\n return this._isSelecting;\n }\n set isSelecting(v: boolean) {\n this._isSelecting = v;\n this._updateActiveState();\n }\n\n get pressDuration(): number {\n return this._pressDuration;\n }\n set pressDuration(v: number) {\n this._pressDuration = v;\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = STYLES;\n shadow.appendChild(style);\n\n const wrapper = document.createElement('span');\n wrapper.className = 'wrapper';\n wrapper.setAttribute('role', 'button');\n wrapper.setAttribute('tabindex', '0');\n wrapper.appendChild(document.createElement('slot'));\n shadow.appendChild(wrapper);\n this._wrapper = wrapper;\n\n wrapper.addEventListener('mousedown', () => this._handleMouseDown());\n wrapper.addEventListener('mouseup', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('mouseleave', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('mouseenter', () => this._handleMouseEnter());\n wrapper.addEventListener('click', (e) => this._handleClick(e));\n wrapper.addEventListener('touchstart', () => this._handleMouseDown());\n wrapper.addEventListener('touchend', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('touchcancel', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('blur', () => this._handleBlur());\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'is-selecting') {\n this._isSelecting = newVal !== null;\n this._updateActiveState();\n } else if (name === 'press-duration') {\n this._pressDuration =\n newVal !== null ? parseInt(newVal, 10) : DEFAULT_PRESS_DURATION;\n }\n }\n\n connectedCallback(): void {\n this._clickOutsideHandler = (e: MouseEvent) => {\n // composedPath() pierces shadow boundaries\n if (!e.composedPath().includes(this)) {\n this._isSelectingState = false;\n this._dispatch('intlayer:click-outside');\n this._updateActiveState();\n }\n };\n document.addEventListener('mousedown', this._clickOutsideHandler);\n }\n\n disconnectedCallback(): void {\n if (this._clickOutsideHandler) {\n document.removeEventListener('mousedown', this._clickOutsideHandler);\n this._clickOutsideHandler = null;\n }\n this._clearPressTimer();\n }\n\n private _updateActiveState(): void {\n const isActive =\n this._isSelecting || this._isSelectingState || this._isHovered;\n if (isActive) {\n this._wrapper.setAttribute('data-active', '');\n } else {\n this._wrapper.removeAttribute('data-active');\n }\n }\n\n private _clearPressTimer(): void {\n if (this._pressTimer !== null) {\n clearTimeout(this._pressTimer);\n this._pressTimer = null;\n }\n }\n\n private _dispatch(eventName: string): void {\n this.dispatchEvent(\n new CustomEvent(eventName, { bubbles: true, composed: true })\n );\n }\n\n private _handleMouseDown(): void {\n this._clearPressTimer();\n this._pressTimer = setTimeout(() => {\n this._isSelectingState = true;\n this._updateActiveState();\n this._dispatch('intlayer:press');\n }, this._pressDuration);\n }\n\n private _handleMouseEnter(): void {\n this._isHovered = true;\n this._updateActiveState();\n this._dispatch('intlayer:hover');\n }\n\n private _handleMouseUpOrLeave(): void {\n if (this._isHovered) {\n this._isHovered = false;\n this._dispatch('intlayer:unhover');\n }\n this._clearPressTimer();\n this._updateActiveState();\n }\n\n private _handleClick(e: MouseEvent): void {\n if (this._isSelecting || this._isSelectingState) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n\n private _handleBlur(): void {\n this._isSelectingState = false;\n this._updateActiveState();\n }\n}\n\n/**\n * Register all Intlayer custom elements.\n * Call this once at application startup (inside IntlayerEditorProvider or similar).\n * Safe to call multiple times — only registers elements that are not yet defined.\n */\nexport const defineIntlayerElements = (): void => {\n if (typeof customElements === 'undefined') return;\n if (!customElements.get('intlayer-content-selector')) {\n customElements.define(\n 'intlayer-content-selector',\n IntlayerContentSelectorElement\n );\n }\n defineIntlayerContentSelectorWrapper();\n defineIntlayerEditedContent();\n defineIntlayerEditorElement();\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ContentSelector.mjs","names":[],"sources":["../../../src/components/ContentSelector.ts"],"sourcesContent":["import { defineIntlayerContentSelectorWrapper } from './ContentSelectorWrapper';\nimport { defineIntlayerEditedContent } from './EditedContent';\nimport { defineIntlayerEditorElement } from './IntlayerEditor';\n\nconst DEFAULT_PRESS_DURATION = 250;\n\nconst STYLES = `\n :host {\n display: contents;\n }\n\n .wrapper {\n display: inline-block;\n cursor: pointer;\n user-select: none;\n border-radius: 0.375rem;\n outline-width: 2px;\n outline-offset: 4px;\n outline-style: solid;\n outline-color: transparent;\n transition: all 100ms 50ms ease-in-out;\n }\n\n .wrapper[data-active] {\n outline-color: inherit;\n }\n`;\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector>\n *\n * A framework-agnostic web component that wraps content with Intlayer editor\n * selection UI (hover outline, long-press to select, click-outside to deselect).\n *\n * @fires intlayer:press - Fired after a long press (pressDuration ms). Bubbles.\n * @fires intlayer:hover - Fired on mouseenter. Bubbles.\n * @fires intlayer:unhover - Fired on mouseleave / mouseup. Bubbles.\n * @fires intlayer:click-outside - Fired when a click occurs outside the element. Bubbles.\n *\n * @prop {boolean} isSelecting - Whether this element is currently selected (external state)\n * @prop {number} pressDuration - Long-press threshold in ms. Default: 250\n */\nexport class IntlayerContentSelectorElement extends _HTMLElement {\n private _isSelecting = false;\n private _pressDuration = DEFAULT_PRESS_DURATION;\n private _isHovered = false;\n private _isSelectingState = false;\n private _wrapper: HTMLSpanElement;\n private _pressTimer: ReturnType<typeof setTimeout> | null = null;\n private _clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['is-selecting', 'press-duration'];\n }\n\n get isSelecting(): boolean {\n return this._isSelecting;\n }\n set isSelecting(v: boolean) {\n this._isSelecting = v;\n this._updateActiveState();\n }\n\n get pressDuration(): number {\n return this._pressDuration;\n }\n set pressDuration(v: number) {\n this._pressDuration = v;\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = STYLES;\n shadow.appendChild(style);\n\n const wrapper = document.createElement('span');\n wrapper.className = 'wrapper';\n wrapper.setAttribute('role', 'button');\n wrapper.setAttribute('tabindex', '0');\n wrapper.appendChild(document.createElement('slot'));\n shadow.appendChild(wrapper);\n this._wrapper = wrapper;\n\n wrapper.addEventListener('mousedown', () => this._handleMouseDown());\n wrapper.addEventListener('mouseup', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('mouseleave', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('mouseenter', () => this._handleMouseEnter());\n wrapper.addEventListener('click', (e) => this._handleClick(e));\n wrapper.addEventListener('touchstart', () => this._handleMouseDown());\n wrapper.addEventListener('touchend', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('touchcancel', () => this._handleMouseUpOrLeave());\n wrapper.addEventListener('blur', () => this._handleBlur());\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'is-selecting') {\n this._isSelecting = newVal !== null;\n this._updateActiveState();\n } else if (name === 'press-duration') {\n this._pressDuration =\n newVal !== null ? parseInt(newVal, 10) : DEFAULT_PRESS_DURATION;\n }\n }\n\n connectedCallback(): void {\n this._clickOutsideHandler = (e: MouseEvent) => {\n // composedPath() pierces shadow boundaries\n if (!e.composedPath().includes(this)) {\n this._isSelectingState = false;\n this._dispatch('intlayer:click-outside');\n this._updateActiveState();\n }\n };\n document.addEventListener('mousedown', this._clickOutsideHandler);\n }\n\n disconnectedCallback(): void {\n if (this._clickOutsideHandler) {\n document.removeEventListener('mousedown', this._clickOutsideHandler);\n this._clickOutsideHandler = null;\n }\n this._clearPressTimer();\n }\n\n private _updateActiveState(): void {\n const isActive =\n this._isSelecting || this._isSelectingState || this._isHovered;\n if (isActive) {\n this._wrapper.setAttribute('data-active', '');\n } else {\n this._wrapper.removeAttribute('data-active');\n }\n }\n\n private _clearPressTimer(): void {\n if (this._pressTimer !== null) {\n clearTimeout(this._pressTimer);\n this._pressTimer = null;\n }\n }\n\n private _dispatch(eventName: string): void {\n this.dispatchEvent(\n new CustomEvent(eventName, { bubbles: true, composed: true })\n );\n }\n\n private _handleMouseDown(): void {\n this._clearPressTimer();\n this._pressTimer = setTimeout(() => {\n this._isSelectingState = true;\n this._updateActiveState();\n this._dispatch('intlayer:press');\n }, this._pressDuration);\n }\n\n private _handleMouseEnter(): void {\n this._isHovered = true;\n this._updateActiveState();\n this._dispatch('intlayer:hover');\n }\n\n private _handleMouseUpOrLeave(): void {\n if (this._isHovered) {\n this._isHovered = false;\n this._dispatch('intlayer:unhover');\n }\n this._clearPressTimer();\n this._updateActiveState();\n }\n\n private _handleClick(e: MouseEvent): void {\n if (this._isSelecting || this._isSelectingState) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n\n private _handleBlur(): void {\n this._isSelectingState = false;\n this._updateActiveState();\n }\n}\n\n/**\n * Register all Intlayer custom elements.\n * Call this once at application startup (inside IntlayerEditorProvider or similar).\n * Safe to call multiple times — only registers elements that are not yet defined.\n */\nexport const defineIntlayerElements = (): void => {\n if (typeof customElements === 'undefined') return;\n if (!customElements.get('intlayer-content-selector')) {\n customElements.define(\n 'intlayer-content-selector',\n IntlayerContentSelectorElement\n );\n }\n defineIntlayerContentSelectorWrapper();\n defineIntlayerEditedContent();\n defineIntlayerEditorElement();\n};\n"],"mappings":";;;;;AAIA,MAAM,yBAAyB;AAE/B,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;AAsBf,MAAM,eACJ,OAAO,gBAAgB,cACnB,cACC,MAAM;;;;;;;;;;;;;;;AAgBb,IAAa,iCAAb,cAAoD,aAAa;CAC/D,AAAQ,eAAe;CACvB,AAAQ,iBAAiB;CACzB,AAAQ,aAAa;CACrB,AAAQ,oBAAoB;CAC5B,AAAQ;CACR,AAAQ,cAAoD;CAC5D,AAAQ,uBAAyD;CAEjE,WAAW,qBAA+B;EACxC,OAAO,CAAC,gBAAgB,iBAAiB;;CAG3C,IAAI,cAAuB;EACzB,OAAO,KAAK;;CAEd,IAAI,YAAY,GAAY;EAC1B,KAAK,eAAe;EACpB,KAAK,oBAAoB;;CAG3B,IAAI,gBAAwB;EAC1B,OAAO,KAAK;;CAEd,IAAI,cAAc,GAAW;EAC3B,KAAK,iBAAiB;;CAGxB,cAAc;EACZ,OAAO;EACP,MAAM,SAAS,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;EAElD,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,cAAc;EACpB,OAAO,YAAY,MAAM;EAEzB,MAAM,UAAU,SAAS,cAAc,OAAO;EAC9C,QAAQ,YAAY;EACpB,QAAQ,aAAa,QAAQ,SAAS;EACtC,QAAQ,aAAa,YAAY,IAAI;EACrC,QAAQ,YAAY,SAAS,cAAc,OAAO,CAAC;EACnD,OAAO,YAAY,QAAQ;EAC3B,KAAK,WAAW;EAEhB,QAAQ,iBAAiB,mBAAmB,KAAK,kBAAkB,CAAC;EACpE,QAAQ,iBAAiB,iBAAiB,KAAK,uBAAuB,CAAC;EACvE,QAAQ,iBAAiB,oBAAoB,KAAK,uBAAuB,CAAC;EAC1E,QAAQ,iBAAiB,oBAAoB,KAAK,mBAAmB,CAAC;EACtE,QAAQ,iBAAiB,UAAU,MAAM,KAAK,aAAa,EAAE,CAAC;EAC9D,QAAQ,iBAAiB,oBAAoB,KAAK,kBAAkB,CAAC;EACrE,QAAQ,iBAAiB,kBAAkB,KAAK,uBAAuB,CAAC;EACxE,QAAQ,iBAAiB,qBAAqB,KAAK,uBAAuB,CAAC;EAC3E,QAAQ,iBAAiB,cAAc,KAAK,aAAa,CAAC;;CAG5D,yBACE,MACA,SACA,QACM;EACN,IAAI,SAAS,gBAAgB;GAC3B,KAAK,eAAe,WAAW;GAC/B,KAAK,oBAAoB;SACpB,IAAI,SAAS,kBAClB,KAAK,iBACH,WAAW,OAAO,SAAS,QAAQ,GAAG,GAAG;;CAI/C,oBAA0B;EACxB,KAAK,wBAAwB,MAAkB;GAE7C,IAAI,CAAC,EAAE,cAAc,CAAC,SAAS,KAAK,EAAE;IACpC,KAAK,oBAAoB;IACzB,KAAK,UAAU,yBAAyB;IACxC,KAAK,oBAAoB;;;EAG7B,SAAS,iBAAiB,aAAa,KAAK,qBAAqB;;CAGnE,uBAA6B;EAC3B,IAAI,KAAK,sBAAsB;GAC7B,SAAS,oBAAoB,aAAa,KAAK,qBAAqB;GACpE,KAAK,uBAAuB;;EAE9B,KAAK,kBAAkB;;CAGzB,AAAQ,qBAA2B;EAGjC,IADE,KAAK,gBAAgB,KAAK,qBAAqB,KAAK,YAEpD,KAAK,SAAS,aAAa,eAAe,GAAG;OAE7C,KAAK,SAAS,gBAAgB,cAAc;;CAIhD,AAAQ,mBAAyB;EAC/B,IAAI,KAAK,gBAAgB,MAAM;GAC7B,aAAa,KAAK,YAAY;GAC9B,KAAK,cAAc;;;CAIvB,AAAQ,UAAU,WAAyB;EACzC,KAAK,cACH,IAAI,YAAY,WAAW;GAAE,SAAS;GAAM,UAAU;GAAM,CAAC,CAC9D;;CAGH,AAAQ,mBAAyB;EAC/B,KAAK,kBAAkB;EACvB,KAAK,cAAc,iBAAiB;GAClC,KAAK,oBAAoB;GACzB,KAAK,oBAAoB;GACzB,KAAK,UAAU,iBAAiB;KAC/B,KAAK,eAAe;;CAGzB,AAAQ,oBAA0B;EAChC,KAAK,aAAa;EAClB,KAAK,oBAAoB;EACzB,KAAK,UAAU,iBAAiB;;CAGlC,AAAQ,wBAA8B;EACpC,IAAI,KAAK,YAAY;GACnB,KAAK,aAAa;GAClB,KAAK,UAAU,mBAAmB;;EAEpC,KAAK,kBAAkB;EACvB,KAAK,oBAAoB;;CAG3B,AAAQ,aAAa,GAAqB;EACxC,IAAI,KAAK,gBAAgB,KAAK,mBAAmB;GAC/C,EAAE,gBAAgB;GAClB,EAAE,iBAAiB;;;CAIvB,AAAQ,cAAoB;EAC1B,KAAK,oBAAoB;EACzB,KAAK,oBAAoB;;;;;;;;AAS7B,MAAa,+BAAqC;CAChD,IAAI,OAAO,mBAAmB,aAAa;CAC3C,IAAI,CAAC,eAAe,IAAI,4BAA4B,EAClD,eAAe,OACb,6BACA,+BACD;CAEH,sCAAsC;CACtC,6BAA6B;CAC7B,6BAA6B"}
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelectorWrapper.mjs","names":["node"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\n\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 type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\ntype RenderState = 'simple' | 'wrapped-slot' | 'wrapped-text';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic web component 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 _HTMLElement {\n private _keyPathJson = '[]';\n private _dictionaryKey = '';\n private _editorEnabled = false;\n private _isInIframe = false;\n private _isSelected = false;\n private _editedValue: ContentNode | undefined = undefined;\n\n private _renderState: RenderState | null = null;\n private _selector: HTMLElement | null = null;\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 static get observedAttributes(): string[] {\n return ['key-path', 'dictionary-key'];\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'key-path') {\n this._keyPathJson = newVal ?? '[]';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n } else if (name === 'dictionary-key') {\n this._dictionaryKey = newVal ?? '';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n connectedCallback(): void {\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n this._render();\n }\n\n disconnectedCallback(): void {\n this._teardown();\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 _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 (keyPath) => keyPath.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 this._render();\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 this._render();\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 as { nodeType?: unknown }).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 this._render();\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n this._updateSelectorAttr();\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 this._updateSelectorAttr();\n }\n\n private _updateSelectorAttr(): void {\n if (!this._selector) return;\n if (this._isSelected) {\n this._selector.setAttribute('is-selecting', '');\n } else {\n this._selector.removeAttribute('is-selecting');\n }\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 this._render();\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 this._render();\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 _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 {\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n }\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 private _render(): void {\n const useWrapper = this._isInIframe && this._editorEnabled;\n const editedValue = this._editedValue;\n const isSimpleValue =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean';\n\n const newState: RenderState = !useWrapper\n ? 'simple'\n : isSimpleValue\n ? 'wrapped-text'\n : 'wrapped-slot';\n\n if (this._renderState !== newState) {\n this._rebuildContent(newState);\n return;\n }\n\n // Structure unchanged — update only dynamic parts\n if (newState !== 'simple' && this._selector) {\n this._updateSelectorAttr();\n if (\n newState === 'wrapped-text' &&\n this._selector.firstChild?.nodeType === Node.TEXT_NODE\n ) {\n (this._selector.firstChild as Text).data = String(editedValue);\n }\n }\n }\n\n private _rebuildContent(state: RenderState): void {\n const shadow = this.shadowRoot!;\n // Remove all nodes except the style element (first child)\n while (shadow.childNodes.length > 1) {\n shadow.removeChild(shadow.lastChild!);\n }\n this._selector = null;\n\n if (state === 'simple') {\n shadow.appendChild(document.createElement('slot'));\n } else {\n const selector = document.createElement('intlayer-content-selector');\n this._selector = selector;\n if (this._isSelected) selector.setAttribute('is-selecting', '');\n selector.addEventListener('intlayer:press', (e) => this._handlePress(e));\n selector.addEventListener('intlayer:hover', (e) => this._handleHover(e));\n selector.addEventListener('intlayer:unhover', (e) =>\n this._handleUnhover(e)\n );\n\n if (state === 'wrapped-text') {\n selector.appendChild(\n document.createTextNode(String(this._editedValue))\n );\n } else {\n selector.appendChild(document.createElement('slot'));\n }\n shadow.appendChild(selector);\n }\n\n this._renderState = state;\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":["node"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\n\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 type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\ntype RenderState = 'simple' | 'wrapped-slot' | 'wrapped-text';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic web component 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 _HTMLElement {\n private _keyPathJson = '[]';\n private _dictionaryKey = '';\n private _editorEnabled = false;\n private _isInIframe = false;\n private _isSelected = false;\n private _editedValue: ContentNode | undefined = undefined;\n\n private _renderState: RenderState | null = null;\n private _selector: HTMLElement | null = null;\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 static get observedAttributes(): string[] {\n return ['key-path', 'dictionary-key'];\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'key-path') {\n this._keyPathJson = newVal ?? '[]';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n } else if (name === 'dictionary-key') {\n this._dictionaryKey = newVal ?? '';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n connectedCallback(): void {\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n this._render();\n }\n\n disconnectedCallback(): void {\n this._teardown();\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 _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 (keyPath) => keyPath.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 this._render();\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 this._render();\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 as { nodeType?: unknown }).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 this._render();\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n this._updateSelectorAttr();\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 this._updateSelectorAttr();\n }\n\n private _updateSelectorAttr(): void {\n if (!this._selector) return;\n if (this._isSelected) {\n this._selector.setAttribute('is-selecting', '');\n } else {\n this._selector.removeAttribute('is-selecting');\n }\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 this._render();\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 this._render();\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 _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 {\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n }\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 private _render(): void {\n const useWrapper = this._isInIframe && this._editorEnabled;\n const editedValue = this._editedValue;\n const isSimpleValue =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean';\n\n const newState: RenderState = !useWrapper\n ? 'simple'\n : isSimpleValue\n ? 'wrapped-text'\n : 'wrapped-slot';\n\n if (this._renderState !== newState) {\n this._rebuildContent(newState);\n return;\n }\n\n // Structure unchanged — update only dynamic parts\n if (newState !== 'simple' && this._selector) {\n this._updateSelectorAttr();\n if (\n newState === 'wrapped-text' &&\n this._selector.firstChild?.nodeType === Node.TEXT_NODE\n ) {\n (this._selector.firstChild as Text).data = String(editedValue);\n }\n }\n }\n\n private _rebuildContent(state: RenderState): void {\n const shadow = this.shadowRoot!;\n // Remove all nodes except the style element (first child)\n while (shadow.childNodes.length > 1) {\n shadow.removeChild(shadow.lastChild!);\n }\n this._selector = null;\n\n if (state === 'simple') {\n shadow.appendChild(document.createElement('slot'));\n } else {\n const selector = document.createElement('intlayer-content-selector');\n this._selector = selector;\n if (this._isSelected) selector.setAttribute('is-selecting', '');\n selector.addEventListener('intlayer:press', (e) => this._handlePress(e));\n selector.addEventListener('intlayer:hover', (e) => this._handleHover(e));\n selector.addEventListener('intlayer:unhover', (e) =>\n this._handleUnhover(e)\n );\n\n if (state === 'wrapped-text') {\n selector.appendChild(\n document.createTextNode(String(this._editedValue))\n );\n } else {\n selector.appendChild(document.createElement('slot'));\n }\n shadow.appendChild(selector);\n }\n\n this._renderState = state;\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":";;;;;;AAkBA,MAAM,eACJ,OAAO,gBAAgB,cACnB,cACC,MAAM;;;;;;;;;;;;;;;AAgBb,IAAa,wCAAb,cAA2D,aAAa;CACtE,AAAQ,eAAe;CACvB,AAAQ,iBAAiB;CACzB,AAAQ,iBAAiB;CACzB,AAAQ,cAAc;CACtB,AAAQ,cAAc;CACtB,AAAQ,eAAwC;CAEhD,AAAQ,eAAmC;CAC3C,AAAQ,YAAgC;CAExC,AAAQ,gBAAqC;CAC7C,AAAQ,gBAAqC;CAC7C,AAAQ,gBAAqC;CAC7C,AAAQ,sBAA2C;CAEnD,WAAW,qBAA+B;EACxC,OAAO,CAAC,YAAY,iBAAiB;;CAGvC,IAAI,cAAsB;EACxB,OAAO,KAAK;;CAEd,IAAI,YAAY,GAAW;EACzB,KAAK,eAAe;EACpB,MAAM,UAAU,wBAAwB;EACxC,IAAI,SAAS,KAAK,mBAAmB,QAAQ;;CAG/C,IAAI,gBAAwB;EAC1B,OAAO,KAAK;;CAEd,IAAI,cAAc,GAAW;EAC3B,KAAK,iBAAiB;EACtB,MAAM,UAAU,wBAAwB;EACxC,IAAI,SAAS,KAAK,mBAAmB,QAAQ;;CAG/C,cAAc;EACZ,OAAO;EACP,MAAM,SAAS,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;EAClD,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,cAAc;EACpB,OAAO,YAAY,MAAM;;CAG3B,yBACE,MACA,SACA,QACM;EACN,IAAI,SAAS,YAAY;GACvB,KAAK,eAAe,UAAU;GAC9B,MAAM,UAAU,wBAAwB;GACxC,IAAI,SAAS,KAAK,mBAAmB,QAAQ;SACxC,IAAI,SAAS,kBAAkB;GACpC,KAAK,iBAAiB,UAAU;GAChC,MAAM,UAAU,wBAAwB;GACxC,IAAI,SAAS,KAAK,mBAAmB,QAAQ;;;CAIjD,oBAA0B;EACxB,IAAI,OAAO,WAAW,aACpB,KAAK,cAAc,OAAO,SAAS,OAAO;EAE5C,KAAK,qBAAqB;EAC1B,KAAK,SAAS;;CAGhB,uBAA6B;EAC3B,KAAK,WAAW;;CAGlB,AAAQ,YAAkB;EACxB,KAAK,iBAAiB;EACtB,KAAK,iBAAiB;EACtB,KAAK,iBAAiB;EACtB,KAAK,uBAAuB;EAC5B,KAAK,gBAAgB;EACrB,KAAK,gBAAgB;EACrB,KAAK,gBAAgB;EACrB,KAAK,sBAAsB;;CAG7B,AAAQ,iBAA4B;EAClC,IAAI;GACF,OAAO,KAAK,MAAM,KAAK,aAAa;UAC9B;GACN,OAAO,EAAE;;;CAIb,AAAQ,sBAAiC;EACvC,OAAO,KAAK,gBAAgB,CAAC,QAC1B,YAAY,QAAQ,SAAS,UAAU,YACzC;;CAGH,AAAQ,mBAAmB,SAAmC;EAC5D,MAAM,kBAAkB,KAAK,qBAAqB;EAClD,IAAI,CAAC,KAAK,kBAAkB,gBAAgB,WAAW,GAAG;GACxD,KAAK,eAAe;GACpB,KAAK,SAAS;GACd;;EAMF,MAAM,aAAa,KAAK,gBAAgB;EACxC,MAAM,eAAe,WAAW,WAAW,SAAS,IAAI;EACxD,IACE,iBAAiB,UAAU,YAC3B,iBAAiB,UAAU,QAC3B,iBAAiB,UAAU,aAC3B,iBAAiB,UAAU,MAC3B;GACA,KAAK,eAAe;GACpB,KAAK,SAAS;GACd;;EAGF,IAAI,QAAQ,QAAQ,gBAAgB,KAAK,gBAAgB,gBAAgB;EAMzE,IACE,UAAU,QACV,UAAU,UACV,OAAO,UAAU,YAChB,MAAiC,aAAa,UAAU,aACzD;GACA,MAAM,SAAS,QAAQ,cAAc;GAOrC,QAAQ,SAASA,MAAK,UAAU,aAAa,UAAU;;EAGzD,KAAK,eAAe;EACpB,KAAK,SAAS;;CAGhB,AAAQ,kBACN,gBACM;EACN,IAAI,CAAC,gBAAgB;GACnB,KAAK,cAAc;GACnB,KAAK,qBAAqB;GAC1B;;EAEF,MAAM,UAAU,KAAK,qBAAqB;EAC1C,KAAK,cACH,eAAe,kBAAkB,KAAK,mBACrC,eAAe,SAAS,UAAU,KAAK,KACxC,cAAc,eAAe,WAAW,EAAE,EAAE,QAAQ;EACtD,KAAK,qBAAqB;;CAG5B,AAAQ,sBAA4B;EAClC,IAAI,CAAC,KAAK,WAAW;EACrB,IAAI,KAAK,aACP,KAAK,UAAU,aAAa,gBAAgB,GAAG;OAE/C,KAAK,UAAU,gBAAgB,eAAe;;CAIlD,AAAQ,sBAA4B;EAClC,MAAM,UAAU,wBAAwB;EACxC,IAAI,SACF,KAAK,2BAA2B,QAAQ;EAG1C,KAAK,gBAAgB,6BAA6B,MAAM;GACtD,KAAK,iBAAiB;GACtB,KAAK,iBAAiB;GACtB,KAAK,uBAAuB;GAC5B,KAAK,gBAAgB;GACrB,KAAK,gBAAgB;GACrB,KAAK,sBAAsB;GAC3B,IAAI,GACF,KAAK,2BAA2B,EAAE;QAC7B;IACL,KAAK,iBAAiB;IACtB,KAAK,cAAc;IACnB,KAAK,eAAe;IACpB,KAAK,SAAS;;IAEhB;;CAGJ,AAAQ,2BAA2B,SAAmC;EACpE,KAAK,iBAAiB,QAAQ,cAAc,SAAS;EACrD,KAAK,kBAAkB,QAAQ,eAAe,MAAM;EACpD,KAAK,mBAAmB,QAAQ;EAEhC,MAAM,uBAAuB,MAAa;GACxC,KAAK,iBAAkB,EAA2B;GAClD,KAAK,SAAS;;EAEhB,MAAM,uBAAuB,MAAa;GACxC,KAAK,kBAAmB,EAAsC,OAAO;;EAEvE,MAAM,kCAAkC;GACtC,KAAK,mBAAmB,QAAQ;;EAGlC,QAAQ,cAAc,iBAAiB,UAAU,oBAAoB;EACrE,QAAQ,eAAe,iBAAiB,UAAU,oBAAoB;EACtE,QAAQ,cAAc,iBAAiB,UAAU,0BAA0B;EAE3E,KAAK,sBACH,QAAQ,cAAc,oBAAoB,UAAU,oBAAoB;EAC1E,KAAK,sBACH,QAAQ,eAAe,oBAAoB,UAAU,oBAAoB;EAC3E,KAAK,4BACH,QAAQ,cAAc,oBACpB,UACA,0BACD;;CAGL,AAAQ,aAAa,GAAgB;EAEnC,EAAE,iBAAiB;EACnB,MAAM,UAAU,wBAAwB;EACxC,IAAI,CAAC,SAAS;EACd,QAAQ,eAAe,IAAI;GACzB,eAAe,KAAK;GACpB,SAAS,KAAK,qBAAqB;GACpC,CAAC;;CAGJ,AAAQ,aAAa,GAAgB;EACnC,EAAE,iBAAiB;EACnB,wBAAwB,EAAE,UAAU,KAClC,sCAA+C,QAC/C;GACE,eAAe,KAAK;GACpB,SAAS,KAAK,qBAAqB;GACpC,CACF;;CAGH,AAAQ,eAAe,GAAgB;EACrC,EAAE,iBAAiB;EACnB,wBAAwB,EAAE,UAAU,KAClC,sCAA+C,QAC/C,KACD;;CAGH,AAAQ,UAAgB;EACtB,MAAM,aAAa,KAAK,eAAe,KAAK;EAC5C,MAAM,cAAc,KAAK;EAMzB,MAAM,WAAwB,CAAC,aAC3B,WALF,OAAO,gBAAgB,YACvB,OAAO,gBAAgB,YACvB,OAAO,gBAAgB,YAKnB,iBACA;EAEN,IAAI,KAAK,iBAAiB,UAAU;GAClC,KAAK,gBAAgB,SAAS;GAC9B;;EAIF,IAAI,aAAa,YAAY,KAAK,WAAW;GAC3C,KAAK,qBAAqB;GAC1B,IACE,aAAa,kBACb,KAAK,UAAU,YAAY,aAAa,KAAK,WAE7C,AAAC,KAAK,UAAU,WAAoB,OAAO,OAAO,YAAY;;;CAKpE,AAAQ,gBAAgB,OAA0B;EAChD,MAAM,SAAS,KAAK;EAEpB,OAAO,OAAO,WAAW,SAAS,GAChC,OAAO,YAAY,OAAO,UAAW;EAEvC,KAAK,YAAY;EAEjB,IAAI,UAAU,UACZ,OAAO,YAAY,SAAS,cAAc,OAAO,CAAC;OAC7C;GACL,MAAM,WAAW,SAAS,cAAc,4BAA4B;GACpE,KAAK,YAAY;GACjB,IAAI,KAAK,aAAa,SAAS,aAAa,gBAAgB,GAAG;GAC/D,SAAS,iBAAiB,mBAAmB,MAAM,KAAK,aAAa,EAAE,CAAC;GACxE,SAAS,iBAAiB,mBAAmB,MAAM,KAAK,aAAa,EAAE,CAAC;GACxE,SAAS,iBAAiB,qBAAqB,MAC7C,KAAK,eAAe,EAAE,CACvB;GAED,IAAI,UAAU,gBACZ,SAAS,YACP,SAAS,eAAe,OAAO,KAAK,aAAa,CAAC,CACnD;QAED,SAAS,YAAY,SAAS,cAAc,OAAO,CAAC;GAEtD,OAAO,YAAY,SAAS;;EAG9B,KAAK,eAAe;;;AAIxB,MAAa,6CAAmD;CAC9D,IAAI,OAAO,mBAAmB,aAAa;CAE3C,IAAI,CAAC,eAAe,IAAI,oCAAoC,EAC1D,eAAe,OACb,qCACA,sCACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"EditedContent.mjs","names":[],"sources":["../../../src/components/EditedContent.ts"],"sourcesContent":["import { getBasePlugins, getContent } from '@intlayer/core/interpreter';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { EditorStateManager } from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-edited-content>\n *\n * Framework-agnostic web component that displays edited content from the Intlayer\n * editor. When the editor has an edited value for the given dictionary key and\n * key path, it renders the edited value; otherwise it renders the original\n * content via a slot.\n *\n * Always wraps content in <intlayer-content-selector-wrapper> for selection UI.\n *\n * @attr {string} dictionary-key - The dictionary key owning this content node\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} locale - The current locale string\n */\nexport class IntlayerEditedContentElement extends _HTMLElement {\n private _dictionaryKey = '';\n private _keyPathJson = '[]';\n private _locale = '';\n private _editedText: string | null = null;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n private _selectorWrapper: HTMLElement;\n private _slot: HTMLSlotElement;\n\n static get observedAttributes(): string[] {\n return ['dictionary-key', 'key-path', 'locale'];\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n this._selectorWrapper.setAttribute('dictionary-key', v);\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n this._selectorWrapper.setAttribute('key-path', v);\n }\n\n get locale(): string {\n return this._locale;\n }\n set locale(v: string) {\n this._locale = v;\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n\n this._selectorWrapper = document.createElement(\n 'intlayer-content-selector-wrapper'\n );\n this._slot = document.createElement('slot') as HTMLSlotElement;\n this._selectorWrapper.appendChild(this._slot);\n shadow.appendChild(this._selectorWrapper);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n const val = newVal ?? '';\n if (name === 'dictionary-key') {\n this._dictionaryKey = val;\n this._selectorWrapper.setAttribute('dictionary-key', val);\n } else if (name === 'key-path') {\n this._keyPathJson = val || '[]';\n this._selectorWrapper.setAttribute('key-path', this._keyPathJson);\n } else if (name === 'locale') {\n this._locale = val;\n }\n }\n\n connectedCallback(): void {\n this._subscribeToManager();\n }\n\n disconnectedCallback(): void {\n this._teardown();\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEditedContent = null;\n }\n\n private _getKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this._keyPathJson);\n } catch {\n return [];\n }\n }\n\n private _render(): void {\n while (this._selectorWrapper.firstChild) {\n this._selectorWrapper.removeChild(this._selectorWrapper.firstChild);\n }\n if (this._editedText !== null) {\n this._selectorWrapper.appendChild(\n document.createTextNode(this._editedText)\n );\n } else {\n this._selectorWrapper.appendChild(this._slot);\n }\n }\n\n private _resolveEditedText(manager: EditorStateManager): void {\n const keyPath = this._getKeyPath();\n const editedValue = manager.getContentValue(this._dictionaryKey, keyPath);\n\n if (editedValue === undefined || editedValue === null) {\n this._editedText = null;\n this._render();\n return;\n }\n\n if (typeof editedValue === 'string' || typeof editedValue === 'number') {\n this._editedText = String(editedValue);\n this._render();\n return;\n }\n\n if (typeof editedValue === 'object') {\n const locale = this._locale || undefined;\n const transformed = getContent(\n editedValue,\n {\n locale: locale as any,\n dictionaryKey: this._dictionaryKey,\n keyPath,\n },\n getBasePlugins(locale)\n );\n if (typeof transformed === 'string' || typeof transformed === 'number') {\n this._editedText = String(transformed);\n } else {\n console.error(\n `[intlayer-edited-content] Incorrect edited content format. Expected string. Value: ${JSON.stringify(transformed)}`\n );\n this._editedText = null;\n }\n this._render();\n return;\n }\n\n this._editedText = null;\n this._render();\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._resolveEditedText(manager);\n\n const handleChange = () => this._resolveEditedText(manager);\n manager.editedContent.addEventListener('change', handleChange);\n\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener('change', handleChange);\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEditedContent?.();\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editedText = null;\n this._render();\n }\n });\n }\n}\n\nexport const defineIntlayerEditedContent = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-edited-content')) {\n customElements.define(\n 'intlayer-edited-content',\n IntlayerEditedContentElement\n );\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"EditedContent.mjs","names":[],"sources":["../../../src/components/EditedContent.ts"],"sourcesContent":["import { getBasePlugins, getContent } from '@intlayer/core/interpreter';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { EditorStateManager } from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-edited-content>\n *\n * Framework-agnostic web component that displays edited content from the Intlayer\n * editor. When the editor has an edited value for the given dictionary key and\n * key path, it renders the edited value; otherwise it renders the original\n * content via a slot.\n *\n * Always wraps content in <intlayer-content-selector-wrapper> for selection UI.\n *\n * @attr {string} dictionary-key - The dictionary key owning this content node\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} locale - The current locale string\n */\nexport class IntlayerEditedContentElement extends _HTMLElement {\n private _dictionaryKey = '';\n private _keyPathJson = '[]';\n private _locale = '';\n private _editedText: string | null = null;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n private _selectorWrapper: HTMLElement;\n private _slot: HTMLSlotElement;\n\n static get observedAttributes(): string[] {\n return ['dictionary-key', 'key-path', 'locale'];\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n this._selectorWrapper.setAttribute('dictionary-key', v);\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n this._selectorWrapper.setAttribute('key-path', v);\n }\n\n get locale(): string {\n return this._locale;\n }\n set locale(v: string) {\n this._locale = v;\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n\n this._selectorWrapper = document.createElement(\n 'intlayer-content-selector-wrapper'\n );\n this._slot = document.createElement('slot') as HTMLSlotElement;\n this._selectorWrapper.appendChild(this._slot);\n shadow.appendChild(this._selectorWrapper);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n const val = newVal ?? '';\n if (name === 'dictionary-key') {\n this._dictionaryKey = val;\n this._selectorWrapper.setAttribute('dictionary-key', val);\n } else if (name === 'key-path') {\n this._keyPathJson = val || '[]';\n this._selectorWrapper.setAttribute('key-path', this._keyPathJson);\n } else if (name === 'locale') {\n this._locale = val;\n }\n }\n\n connectedCallback(): void {\n this._subscribeToManager();\n }\n\n disconnectedCallback(): void {\n this._teardown();\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEditedContent = null;\n }\n\n private _getKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this._keyPathJson);\n } catch {\n return [];\n }\n }\n\n private _render(): void {\n while (this._selectorWrapper.firstChild) {\n this._selectorWrapper.removeChild(this._selectorWrapper.firstChild);\n }\n if (this._editedText !== null) {\n this._selectorWrapper.appendChild(\n document.createTextNode(this._editedText)\n );\n } else {\n this._selectorWrapper.appendChild(this._slot);\n }\n }\n\n private _resolveEditedText(manager: EditorStateManager): void {\n const keyPath = this._getKeyPath();\n const editedValue = manager.getContentValue(this._dictionaryKey, keyPath);\n\n if (editedValue === undefined || editedValue === null) {\n this._editedText = null;\n this._render();\n return;\n }\n\n if (typeof editedValue === 'string' || typeof editedValue === 'number') {\n this._editedText = String(editedValue);\n this._render();\n return;\n }\n\n if (typeof editedValue === 'object') {\n const locale = this._locale || undefined;\n const transformed = getContent(\n editedValue,\n {\n locale: locale as any,\n dictionaryKey: this._dictionaryKey,\n keyPath,\n },\n getBasePlugins(locale)\n );\n if (typeof transformed === 'string' || typeof transformed === 'number') {\n this._editedText = String(transformed);\n } else {\n console.error(\n `[intlayer-edited-content] Incorrect edited content format. Expected string. Value: ${JSON.stringify(transformed)}`\n );\n this._editedText = null;\n }\n this._render();\n return;\n }\n\n this._editedText = null;\n this._render();\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._resolveEditedText(manager);\n\n const handleChange = () => this._resolveEditedText(manager);\n manager.editedContent.addEventListener('change', handleChange);\n\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener('change', handleChange);\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEditedContent?.();\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editedText = null;\n this._render();\n }\n });\n }\n}\n\nexport const defineIntlayerEditedContent = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-edited-content')) {\n customElements.define(\n 'intlayer-edited-content',\n IntlayerEditedContentElement\n );\n }\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"IntlayerEditor.mjs","names":[],"sources":["../../../src/components/IntlayerEditor.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { initEditorClient, stopEditorClient } from '../core/initEditorClient';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-editor>\n *\n * A framework-agnostic web component that manages the Intlayer editor singleton\n * lifecycle and keeps the current locale in sync with the EditorStateManager.\n *\n * Drop this element once at the root of your application to activate the editor.\n * It renders no UI.\n *\n * @prop {IntlayerConfig} configuration - The Intlayer config (required; set as JS property, not attribute)\n * @prop {string} locale - The active application locale (attribute/property)\n *\n * @example\n * // Svelte\n * <intlayer-editor .configuration={config} locale={$locale} />\n *\n * // Vue\n * <intlayer-editor :configuration=\"config\" :locale=\"currentLocale\" />\n */\nexport class IntlayerEditorElement extends _HTMLElement {\n private _configuration: IntlayerConfig | undefined = undefined;\n private _locale: string | undefined = undefined;\n private _initialized = false;\n private _unsubManager: (() => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['locale'];\n }\n\n get configuration(): IntlayerConfig | undefined {\n return this._configuration;\n }\n set configuration(v: IntlayerConfig | undefined) {\n this._configuration = v;\n if (!this._initialized) this._init();\n }\n\n get locale(): string | undefined {\n return this._locale;\n }\n set locale(v: string | undefined) {\n this._locale = v;\n if (v && this._initialized) this._syncLocale(v);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'locale' && newVal !== null) {\n this._locale = newVal;\n if (this._initialized) this._syncLocale(newVal);\n }\n }\n\n connectedCallback(): void {\n this._init();\n }\n\n disconnectedCallback(): void {\n this._unsubManager?.();\n this._unsubManager = null;\n if (this._initialized) {\n stopEditorClient();\n this._initialized = false;\n }\n }\n\n private _init(): void {\n if (this._initialized) return;\n initEditorClient();\n this._initialized = true;\n if (this._locale) this._syncLocale(this._locale);\n }\n\n private _syncLocale(locale: string): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n manager.currentLocale.set(locale as Locale);\n } else {\n // Manager may not be ready yet — wait for it\n this._unsubManager?.();\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n if (m) {\n this._unsubManager?.();\n this._unsubManager = null;\n m.currentLocale.set(locale as Locale);\n }\n });\n }\n }\n}\n\nexport const defineIntlayerEditorElement = (): void => {\n if (typeof customElements === 'undefined') return;\n if (!customElements.get('intlayer-editor')) {\n customElements.define('intlayer-editor', IntlayerEditorElement);\n }\n};\n"],"mappings":";;;;AAQA,MAAM,eACJ,OAAO,gBAAgB,cACnB,cACC,MAAM;;;;;;;;;;;;;;;;;;;;AAqBb,IAAa,wBAAb,cAA2C,aAAa;CACtD,AAAQ,iBAA6C;CACrD,AAAQ,UAA8B;CACtC,AAAQ,eAAe;CACvB,AAAQ,gBAAqC;CAE7C,WAAW,qBAA+B;AACxC,SAAO,CAAC,SAAS;;CAGnB,IAAI,gBAA4C;AAC9C,SAAO,KAAK;;CAEd,IAAI,cAAc,GAA+B;AAC/C,OAAK,iBAAiB;AACtB,MAAI,CAAC,KAAK,aAAc,MAAK,OAAO;;CAGtC,IAAI,SAA6B;AAC/B,SAAO,KAAK;;CAEd,IAAI,OAAO,GAAuB;AAChC,OAAK,UAAU;AACf,MAAI,KAAK,KAAK,aAAc,MAAK,YAAY,EAAE;;CAGjD,yBACE,MACA,SACA,QACM;AACN,MAAI,SAAS,YAAY,WAAW,MAAM;AACxC,QAAK,UAAU;AACf,OAAI,KAAK,aAAc,MAAK,YAAY,OAAO;;;CAInD,oBAA0B;AACxB,OAAK,OAAO;;CAGd,uBAA6B;AAC3B,OAAK,iBAAiB;AACtB,OAAK,gBAAgB;AACrB,MAAI,KAAK,cAAc;AACrB,qBAAkB;AAClB,QAAK,eAAe;;;CAIxB,AAAQ,QAAc;AACpB,MAAI,KAAK,aAAc;AACvB,oBAAkB;AAClB,OAAK,eAAe;AACpB,MAAI,KAAK,QAAS,MAAK,YAAY,KAAK,QAAQ;;CAGlD,AAAQ,YAAY,QAAsB;EACxC,MAAM,UAAU,wBAAwB;AACxC,MAAI,QACF,SAAQ,cAAc,IAAI,OAAiB;OACtC;AAEL,QAAK,iBAAiB;AACtB,QAAK,gBAAgB,6BAA6B,MAAM;AACtD,QAAI,GAAG;AACL,UAAK,iBAAiB;AACtB,UAAK,gBAAgB;AACrB,OAAE,cAAc,IAAI,OAAiB;;KAEvC;;;;AAKR,MAAa,oCAA0C;AACrD,KAAI,OAAO,mBAAmB,YAAa;AAC3C,KAAI,CAAC,eAAe,IAAI,kBAAkB,CACxC,gBAAe,OAAO,mBAAmB,sBAAsB"}
1
+ {"version":3,"file":"IntlayerEditor.mjs","names":[],"sources":["../../../src/components/IntlayerEditor.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { initEditorClient, stopEditorClient } from '../core/initEditorClient';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-editor>\n *\n * A framework-agnostic web component that manages the Intlayer editor singleton\n * lifecycle and keeps the current locale in sync with the EditorStateManager.\n *\n * Drop this element once at the root of your application to activate the editor.\n * It renders no UI.\n *\n * @prop {IntlayerConfig} configuration - The Intlayer config (required; set as JS property, not attribute)\n * @prop {string} locale - The active application locale (attribute/property)\n *\n * @example\n * // Svelte\n * <intlayer-editor .configuration={config} locale={$locale} />\n *\n * // Vue\n * <intlayer-editor :configuration=\"config\" :locale=\"currentLocale\" />\n */\nexport class IntlayerEditorElement extends _HTMLElement {\n private _configuration: IntlayerConfig | undefined = undefined;\n private _locale: string | undefined = undefined;\n private _initialized = false;\n private _unsubManager: (() => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['locale'];\n }\n\n get configuration(): IntlayerConfig | undefined {\n return this._configuration;\n }\n set configuration(v: IntlayerConfig | undefined) {\n this._configuration = v;\n if (!this._initialized) this._init();\n }\n\n get locale(): string | undefined {\n return this._locale;\n }\n set locale(v: string | undefined) {\n this._locale = v;\n if (v && this._initialized) this._syncLocale(v);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'locale' && newVal !== null) {\n this._locale = newVal;\n if (this._initialized) this._syncLocale(newVal);\n }\n }\n\n connectedCallback(): void {\n this._init();\n }\n\n disconnectedCallback(): void {\n this._unsubManager?.();\n this._unsubManager = null;\n if (this._initialized) {\n stopEditorClient();\n this._initialized = false;\n }\n }\n\n private _init(): void {\n if (this._initialized) return;\n initEditorClient();\n this._initialized = true;\n if (this._locale) this._syncLocale(this._locale);\n }\n\n private _syncLocale(locale: string): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n manager.currentLocale.set(locale as Locale);\n } else {\n // Manager may not be ready yet — wait for it\n this._unsubManager?.();\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n if (m) {\n this._unsubManager?.();\n this._unsubManager = null;\n m.currentLocale.set(locale as Locale);\n }\n });\n }\n }\n}\n\nexport const defineIntlayerEditorElement = (): void => {\n if (typeof customElements === 'undefined') return;\n if (!customElements.get('intlayer-editor')) {\n customElements.define('intlayer-editor', IntlayerEditorElement);\n }\n};\n"],"mappings":";;;;AAQA,MAAM,eACJ,OAAO,gBAAgB,cACnB,cACC,MAAM;;;;;;;;;;;;;;;;;;;;AAqBb,IAAa,wBAAb,cAA2C,aAAa;CACtD,AAAQ,iBAA6C;CACrD,AAAQ,UAA8B;CACtC,AAAQ,eAAe;CACvB,AAAQ,gBAAqC;CAE7C,WAAW,qBAA+B;EACxC,OAAO,CAAC,SAAS;;CAGnB,IAAI,gBAA4C;EAC9C,OAAO,KAAK;;CAEd,IAAI,cAAc,GAA+B;EAC/C,KAAK,iBAAiB;EACtB,IAAI,CAAC,KAAK,cAAc,KAAK,OAAO;;CAGtC,IAAI,SAA6B;EAC/B,OAAO,KAAK;;CAEd,IAAI,OAAO,GAAuB;EAChC,KAAK,UAAU;EACf,IAAI,KAAK,KAAK,cAAc,KAAK,YAAY,EAAE;;CAGjD,yBACE,MACA,SACA,QACM;EACN,IAAI,SAAS,YAAY,WAAW,MAAM;GACxC,KAAK,UAAU;GACf,IAAI,KAAK,cAAc,KAAK,YAAY,OAAO;;;CAInD,oBAA0B;EACxB,KAAK,OAAO;;CAGd,uBAA6B;EAC3B,KAAK,iBAAiB;EACtB,KAAK,gBAAgB;EACrB,IAAI,KAAK,cAAc;GACrB,kBAAkB;GAClB,KAAK,eAAe;;;CAIxB,AAAQ,QAAc;EACpB,IAAI,KAAK,cAAc;EACvB,kBAAkB;EAClB,KAAK,eAAe;EACpB,IAAI,KAAK,SAAS,KAAK,YAAY,KAAK,QAAQ;;CAGlD,AAAQ,YAAY,QAAsB;EACxC,MAAM,UAAU,wBAAwB;EACxC,IAAI,SACF,QAAQ,cAAc,IAAI,OAAiB;OACtC;GAEL,KAAK,iBAAiB;GACtB,KAAK,gBAAgB,6BAA6B,MAAM;IACtD,IAAI,GAAG;KACL,KAAK,iBAAiB;KACtB,KAAK,gBAAgB;KACrB,EAAE,cAAc,IAAI,OAAiB;;KAEvC;;;;AAKR,MAAa,oCAA0C;CACrD,IAAI,OAAO,mBAAmB,aAAa;CAC3C,IAAI,CAAC,eAAe,IAAI,kBAAkB,EACxC,eAAe,OAAO,mBAAmB,sBAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"CrossFrameMessenger.mjs","names":[],"sources":["../../../src/core/CrossFrameMessenger.ts"],"sourcesContent":["import { compareUrls } from '../compareUrls';\n\nconst randomUUID = (): string => Math.random().toString(36).slice(2);\n\nexport type MessagePayload = {\n type: string;\n data?: unknown;\n senderId?: string;\n /** Unique ID per send() call — used to deduplicate when the same payload arrives via multiple target origins */\n messageId?: string;\n};\n\nexport type MessengerConfig = {\n /**\n * Origins allowed to send messages to this instance.\n * Use '*' to allow all origins (not recommended for production).\n */\n allowedOrigins: string[];\n /**\n * Function used to send messages to other frames.\n * Injected so the messenger is agnostic to the frame topology.\n */\n postMessageFn: (payload: MessagePayload, origin: string) => void;\n};\n\ntype MessageHandler<T = unknown> = (data: T, senderId?: string) => void;\n\n/**\n * CrossFrameMessenger manages all cross-frame postMessage communication.\n * It owns a single window message listener and routes incoming messages to\n * type-specific subscribers.\n *\n * Replaces CommunicatorContext + useCrossFrameMessageListener across all frameworks.\n */\nexport class CrossFrameMessenger {\n readonly senderId: string;\n private readonly _config: MessengerConfig;\n private readonly _subscribers = new Map<string, Set<MessageHandler>>();\n\n private _windowHandler: ((event: MessageEvent) => void) | null = null;\n /** Tracks recently processed messageIds to discard duplicates (same payload sent to multiple origins) */\n private readonly _seenMessageIds = new Set<string>();\n\n constructor(config: MessengerConfig) {\n this._config = config;\n this.senderId = randomUUID();\n }\n\n /** Start listening for incoming messages on window. */\n start(): void {\n if (typeof window === 'undefined') return;\n if (this._windowHandler) return;\n this._windowHandler = (event: MessageEvent<MessagePayload>) => {\n this._handleMessage(event);\n };\n window.addEventListener('message', this._windowHandler);\n }\n\n /** Stop listening and clean up. */\n stop(): void {\n if (this._windowHandler) {\n window.removeEventListener('message', this._windowHandler);\n this._windowHandler = null;\n }\n }\n\n /** Send a message payload to all configured target origins. */\n send(type: string, data?: unknown): void {\n const payload: MessagePayload = {\n type,\n data,\n senderId: this.senderId,\n messageId: randomUUID(),\n };\n\n for (const origin of this._config.allowedOrigins) {\n if (origin) {\n this._config.postMessageFn(payload, origin);\n }\n }\n }\n\n /**\n * Subscribe to messages of a given type.\n * Returns an unsubscribe function.\n */\n subscribe<T = unknown>(type: string, handler: MessageHandler<T>): () => void {\n if (!this._subscribers.has(type)) {\n this._subscribers.set(type, new Set());\n }\n this._subscribers.get(type)!.add(handler as MessageHandler);\n return () => {\n this._subscribers.get(type)?.delete(handler as MessageHandler);\n };\n }\n\n private _handleMessage(event: MessageEvent<MessagePayload>): void {\n const payload = event.data;\n if (!payload || typeof payload !== 'object') return;\n\n const { type, data, senderId: msgSenderId, messageId } = payload;\n if (!type || typeof type !== 'string') return;\n\n // Ignore messages originating from this instance\n if (msgSenderId === this.senderId) return;\n\n // Deduplicate: same messageId may arrive multiple times when the sender\n // posts to multiple target origins (one per allowedOrigin)\n if (messageId) {\n if (this._seenMessageIds.has(messageId)) return;\n this._seenMessageIds.add(messageId);\n // Keep the set bounded — clear when it exceeds 200 entries\n if (this._seenMessageIds.size > 200) this._seenMessageIds.clear();\n }\n\n // Validate message origin\n const { allowedOrigins } = this._config;\n const isAllowed =\n !allowedOrigins ||\n allowedOrigins.length === 0 ||\n allowedOrigins.includes('*') ||\n allowedOrigins\n .filter((url) => Boolean(url) && url !== '')\n .some((url) => compareUrls(url, event.origin));\n\n if (!isAllowed) return;\n\n const handlers = this._subscribers.get(type);\n\n if (handlers) {\n for (const handler of handlers) {\n handler(data, msgSenderId);\n }\n }\n }\n}\n"],"mappings":";;;AAEA,MAAM,mBAA2B,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE;;;;;;;;AAgCpE,IAAa,sBAAb,MAAiC;CAC/B,AAAS;CACT,AAAiB;CACjB,AAAiB,+BAAe,IAAI,KAAkC;CAEtE,AAAQ,iBAAyD;;CAEjE,AAAiB,kCAAkB,IAAI,KAAa;CAEpD,YAAY,QAAyB;AACnC,OAAK,UAAU;AACf,OAAK,WAAW,YAAY;;;CAI9B,QAAc;AACZ,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,KAAK,eAAgB;AACzB,OAAK,kBAAkB,UAAwC;AAC7D,QAAK,eAAe,MAAM;;AAE5B,SAAO,iBAAiB,WAAW,KAAK,eAAe;;;CAIzD,OAAa;AACX,MAAI,KAAK,gBAAgB;AACvB,UAAO,oBAAoB,WAAW,KAAK,eAAe;AAC1D,QAAK,iBAAiB;;;;CAK1B,KAAK,MAAc,MAAsB;EACvC,MAAM,UAA0B;GAC9B;GACA;GACA,UAAU,KAAK;GACf,WAAW,YAAY;GACxB;AAED,OAAK,MAAM,UAAU,KAAK,QAAQ,eAChC,KAAI,OACF,MAAK,QAAQ,cAAc,SAAS,OAAO;;;;;;CASjD,UAAuB,MAAc,SAAwC;AAC3E,MAAI,CAAC,KAAK,aAAa,IAAI,KAAK,CAC9B,MAAK,aAAa,IAAI,sBAAM,IAAI,KAAK,CAAC;AAExC,OAAK,aAAa,IAAI,KAAK,CAAE,IAAI,QAA0B;AAC3D,eAAa;AACX,QAAK,aAAa,IAAI,KAAK,EAAE,OAAO,QAA0B;;;CAIlE,AAAQ,eAAe,OAA2C;EAChE,MAAM,UAAU,MAAM;AACtB,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU;EAE7C,MAAM,EAAE,MAAM,MAAM,UAAU,aAAa,cAAc;AACzD,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAGvC,MAAI,gBAAgB,KAAK,SAAU;AAInC,MAAI,WAAW;AACb,OAAI,KAAK,gBAAgB,IAAI,UAAU,CAAE;AACzC,QAAK,gBAAgB,IAAI,UAAU;AAEnC,OAAI,KAAK,gBAAgB,OAAO,IAAK,MAAK,gBAAgB,OAAO;;EAInE,MAAM,EAAE,mBAAmB,KAAK;AAShC,MAAI,EAPF,CAAC,kBACD,eAAe,WAAW,KAC1B,eAAe,SAAS,IAAI,IAC5B,eACG,QAAQ,QAAQ,QAAQ,IAAI,IAAI,QAAQ,GAAG,CAC3C,MAAM,QAAQ,YAAY,KAAK,MAAM,OAAO,CAAC,EAElC;EAEhB,MAAM,WAAW,KAAK,aAAa,IAAI,KAAK;AAE5C,MAAI,SACF,MAAK,MAAM,WAAW,SACpB,SAAQ,MAAM,YAAY"}
1
+ {"version":3,"file":"CrossFrameMessenger.mjs","names":[],"sources":["../../../src/core/CrossFrameMessenger.ts"],"sourcesContent":["import { compareUrls } from '../compareUrls';\n\nconst randomUUID = (): string => Math.random().toString(36).slice(2);\n\nexport type MessagePayload = {\n type: string;\n data?: unknown;\n senderId?: string;\n /** Unique ID per send() call — used to deduplicate when the same payload arrives via multiple target origins */\n messageId?: string;\n};\n\nexport type MessengerConfig = {\n /**\n * Origins allowed to send messages to this instance.\n * Use '*' to allow all origins (not recommended for production).\n */\n allowedOrigins: string[];\n /**\n * Function used to send messages to other frames.\n * Injected so the messenger is agnostic to the frame topology.\n */\n postMessageFn: (payload: MessagePayload, origin: string) => void;\n};\n\ntype MessageHandler<T = unknown> = (data: T, senderId?: string) => void;\n\n/**\n * CrossFrameMessenger manages all cross-frame postMessage communication.\n * It owns a single window message listener and routes incoming messages to\n * type-specific subscribers.\n *\n * Replaces CommunicatorContext + useCrossFrameMessageListener across all frameworks.\n */\nexport class CrossFrameMessenger {\n readonly senderId: string;\n private readonly _config: MessengerConfig;\n private readonly _subscribers = new Map<string, Set<MessageHandler>>();\n\n private _windowHandler: ((event: MessageEvent) => void) | null = null;\n /** Tracks recently processed messageIds to discard duplicates (same payload sent to multiple origins) */\n private readonly _seenMessageIds = new Set<string>();\n\n constructor(config: MessengerConfig) {\n this._config = config;\n this.senderId = randomUUID();\n }\n\n /** Start listening for incoming messages on window. */\n start(): void {\n if (typeof window === 'undefined') return;\n if (this._windowHandler) return;\n this._windowHandler = (event: MessageEvent<MessagePayload>) => {\n this._handleMessage(event);\n };\n window.addEventListener('message', this._windowHandler);\n }\n\n /** Stop listening and clean up. */\n stop(): void {\n if (this._windowHandler) {\n window.removeEventListener('message', this._windowHandler);\n this._windowHandler = null;\n }\n }\n\n /** Send a message payload to all configured target origins. */\n send(type: string, data?: unknown): void {\n const payload: MessagePayload = {\n type,\n data,\n senderId: this.senderId,\n messageId: randomUUID(),\n };\n\n for (const origin of this._config.allowedOrigins) {\n if (origin) {\n this._config.postMessageFn(payload, origin);\n }\n }\n }\n\n /**\n * Subscribe to messages of a given type.\n * Returns an unsubscribe function.\n */\n subscribe<T = unknown>(type: string, handler: MessageHandler<T>): () => void {\n if (!this._subscribers.has(type)) {\n this._subscribers.set(type, new Set());\n }\n this._subscribers.get(type)!.add(handler as MessageHandler);\n return () => {\n this._subscribers.get(type)?.delete(handler as MessageHandler);\n };\n }\n\n private _handleMessage(event: MessageEvent<MessagePayload>): void {\n const payload = event.data;\n if (!payload || typeof payload !== 'object') return;\n\n const { type, data, senderId: msgSenderId, messageId } = payload;\n if (!type || typeof type !== 'string') return;\n\n // Ignore messages originating from this instance\n if (msgSenderId === this.senderId) return;\n\n // Deduplicate: same messageId may arrive multiple times when the sender\n // posts to multiple target origins (one per allowedOrigin)\n if (messageId) {\n if (this._seenMessageIds.has(messageId)) return;\n this._seenMessageIds.add(messageId);\n // Keep the set bounded — clear when it exceeds 200 entries\n if (this._seenMessageIds.size > 200) this._seenMessageIds.clear();\n }\n\n // Validate message origin\n const { allowedOrigins } = this._config;\n const isAllowed =\n !allowedOrigins ||\n allowedOrigins.length === 0 ||\n allowedOrigins.includes('*') ||\n allowedOrigins\n .filter((url) => Boolean(url) && url !== '')\n .some((url) => compareUrls(url, event.origin));\n\n if (!isAllowed) return;\n\n const handlers = this._subscribers.get(type);\n\n if (handlers) {\n for (const handler of handlers) {\n handler(data, msgSenderId);\n }\n }\n }\n}\n"],"mappings":";;;AAEA,MAAM,mBAA2B,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE;;;;;;;;AAgCpE,IAAa,sBAAb,MAAiC;CAC/B,AAAS;CACT,AAAiB;CACjB,AAAiB,+BAAe,IAAI,KAAkC;CAEtE,AAAQ,iBAAyD;;CAEjE,AAAiB,kCAAkB,IAAI,KAAa;CAEpD,YAAY,QAAyB;EACnC,KAAK,UAAU;EACf,KAAK,WAAW,YAAY;;;CAI9B,QAAc;EACZ,IAAI,OAAO,WAAW,aAAa;EACnC,IAAI,KAAK,gBAAgB;EACzB,KAAK,kBAAkB,UAAwC;GAC7D,KAAK,eAAe,MAAM;;EAE5B,OAAO,iBAAiB,WAAW,KAAK,eAAe;;;CAIzD,OAAa;EACX,IAAI,KAAK,gBAAgB;GACvB,OAAO,oBAAoB,WAAW,KAAK,eAAe;GAC1D,KAAK,iBAAiB;;;;CAK1B,KAAK,MAAc,MAAsB;EACvC,MAAM,UAA0B;GAC9B;GACA;GACA,UAAU,KAAK;GACf,WAAW,YAAY;GACxB;EAED,KAAK,MAAM,UAAU,KAAK,QAAQ,gBAChC,IAAI,QACF,KAAK,QAAQ,cAAc,SAAS,OAAO;;;;;;CASjD,UAAuB,MAAc,SAAwC;EAC3E,IAAI,CAAC,KAAK,aAAa,IAAI,KAAK,EAC9B,KAAK,aAAa,IAAI,sBAAM,IAAI,KAAK,CAAC;EAExC,KAAK,aAAa,IAAI,KAAK,CAAE,IAAI,QAA0B;EAC3D,aAAa;GACX,KAAK,aAAa,IAAI,KAAK,EAAE,OAAO,QAA0B;;;CAIlE,AAAQ,eAAe,OAA2C;EAChE,MAAM,UAAU,MAAM;EACtB,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU;EAE7C,MAAM,EAAE,MAAM,MAAM,UAAU,aAAa,cAAc;EACzD,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;EAGvC,IAAI,gBAAgB,KAAK,UAAU;EAInC,IAAI,WAAW;GACb,IAAI,KAAK,gBAAgB,IAAI,UAAU,EAAE;GACzC,KAAK,gBAAgB,IAAI,UAAU;GAEnC,IAAI,KAAK,gBAAgB,OAAO,KAAK,KAAK,gBAAgB,OAAO;;EAInE,MAAM,EAAE,mBAAmB,KAAK;EAShC,IAAI,EAPF,CAAC,kBACD,eAAe,WAAW,KAC1B,eAAe,SAAS,IAAI,IAC5B,eACG,QAAQ,QAAQ,QAAQ,IAAI,IAAI,QAAQ,GAAG,CAC3C,MAAM,QAAQ,YAAY,KAAK,MAAM,OAAO,CAAC,GAElC;EAEhB,MAAM,WAAW,KAAK,aAAa,IAAI,KAAK;EAE5C,IAAI,UACF,KAAK,MAAM,WAAW,UACpB,QAAQ,MAAM,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"CrossFrameStateManager.mjs","names":[],"sources":["../../../src/core/CrossFrameStateManager.ts"],"sourcesContent":["import type { CrossFrameMessenger } from './CrossFrameMessenger';\n\nexport type CrossFrameStateOptions = {\n /** Whether to broadcast state changes to other frames. Default: true */\n emit?: boolean;\n /** Whether to listen for state updates from other frames. Default: true */\n receive?: boolean;\n};\n\n/**\n * CrossFrameStateManager synchronizes a single named value across frames using\n * the postMessage API via CrossFrameMessenger.\n *\n * Protocol:\n * `{key}/post` — broadcast a new value\n * `{key}/get` — request the current value from other frames\n *\n * Replaces useCrossFrameState across all frameworks.\n *\n * @fires change — CustomEvent<T> dispatched whenever the value changes (local set or received)\n */\nexport class CrossFrameStateManager<T> extends EventTarget {\n private _value: T | undefined;\n private readonly _key: string;\n private readonly _messenger: CrossFrameMessenger;\n private readonly _options: Required<CrossFrameStateOptions>;\n private readonly _unsubscribers: Array<() => void> = [];\n\n constructor(\n key: string,\n messenger: CrossFrameMessenger,\n options: CrossFrameStateOptions & { initialValue?: T } = {}\n ) {\n super();\n this._key = key;\n this._messenger = messenger;\n this._options = {\n emit: options.emit ?? true,\n receive: options.receive ?? true,\n };\n if (options.initialValue !== undefined) {\n this._value = options.initialValue;\n }\n }\n\n get value(): T | undefined {\n return this._value;\n }\n\n /** Update the value locally and broadcast it to other frames if emit is enabled. */\n set(newValue: T): void {\n this._value = newValue;\n this.dispatchEvent(new CustomEvent<T>('change', { detail: newValue }));\n if (this._options.emit) {\n this._messenger.send(`${this._key}/post`, newValue);\n }\n }\n\n /**\n * Start listening for incoming state updates and responding to /get requests.\n * If receive=true and no initial value is set, sends a /get to request the current value.\n */\n start(): void {\n if (this._options.receive) {\n const unsub = this._messenger.subscribe<T>(\n `${this._key}/post`,\n (data) => {\n this._value = data;\n this.dispatchEvent(new CustomEvent<T>('change', { detail: data }));\n }\n );\n this._unsubscribers.push(unsub);\n }\n\n if (this._options.emit) {\n // Respond to /get requests by broadcasting current value\n const unsub = this._messenger.subscribe(\n `${this._key}/get`,\n (_, originSenderId) => {\n if (originSenderId === this._messenger.senderId) return;\n if (this._value === undefined) return;\n this._messenger.send(`${this._key}/post`, this._value);\n }\n );\n this._unsubscribers.push(unsub);\n }\n\n // If receiving and no initial value, request it from other frames\n if (this._options.receive && this._value === undefined) {\n this._messenger.send(`${this._key}/get`);\n }\n }\n\n /** Stop all listeners. */\n stop(): void {\n for (const unsub of this._unsubscribers) unsub();\n this._unsubscribers.length = 0;\n }\n\n /** Broadcast the current value to all frames (useful after reconnect). */\n postCurrentValue(): void {\n if (this._value !== undefined) {\n this._messenger.send(`${this._key}/post`, this._value);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAqBA,IAAa,yBAAb,cAA+C,YAAY;CACzD,AAAQ;CACR,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB,iBAAoC,EAAE;CAEvD,YACE,KACA,WACA,UAAyD,EAAE,EAC3D;AACA,SAAO;AACP,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,WAAW;GACd,MAAM,QAAQ,QAAQ;GACtB,SAAS,QAAQ,WAAW;GAC7B;AACD,MAAI,QAAQ,iBAAiB,OAC3B,MAAK,SAAS,QAAQ;;CAI1B,IAAI,QAAuB;AACzB,SAAO,KAAK;;;CAId,IAAI,UAAmB;AACrB,OAAK,SAAS;AACd,OAAK,cAAc,IAAI,YAAe,UAAU,EAAE,QAAQ,UAAU,CAAC,CAAC;AACtE,MAAI,KAAK,SAAS,KAChB,MAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,SAAS;;;;;;CAQvD,QAAc;AACZ,MAAI,KAAK,SAAS,SAAS;GACzB,MAAM,QAAQ,KAAK,WAAW,UAC5B,GAAG,KAAK,KAAK,SACZ,SAAS;AACR,SAAK,SAAS;AACd,SAAK,cAAc,IAAI,YAAe,UAAU,EAAE,QAAQ,MAAM,CAAC,CAAC;KAErE;AACD,QAAK,eAAe,KAAK,MAAM;;AAGjC,MAAI,KAAK,SAAS,MAAM;GAEtB,MAAM,QAAQ,KAAK,WAAW,UAC5B,GAAG,KAAK,KAAK,QACZ,GAAG,mBAAmB;AACrB,QAAI,mBAAmB,KAAK,WAAW,SAAU;AACjD,QAAI,KAAK,WAAW,OAAW;AAC/B,SAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,KAAK,OAAO;KAEzD;AACD,QAAK,eAAe,KAAK,MAAM;;AAIjC,MAAI,KAAK,SAAS,WAAW,KAAK,WAAW,OAC3C,MAAK,WAAW,KAAK,GAAG,KAAK,KAAK,MAAM;;;CAK5C,OAAa;AACX,OAAK,MAAM,SAAS,KAAK,eAAgB,QAAO;AAChD,OAAK,eAAe,SAAS;;;CAI/B,mBAAyB;AACvB,MAAI,KAAK,WAAW,OAClB,MAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,KAAK,OAAO"}
1
+ {"version":3,"file":"CrossFrameStateManager.mjs","names":[],"sources":["../../../src/core/CrossFrameStateManager.ts"],"sourcesContent":["import type { CrossFrameMessenger } from './CrossFrameMessenger';\n\nexport type CrossFrameStateOptions = {\n /** Whether to broadcast state changes to other frames. Default: true */\n emit?: boolean;\n /** Whether to listen for state updates from other frames. Default: true */\n receive?: boolean;\n};\n\n/**\n * CrossFrameStateManager synchronizes a single named value across frames using\n * the postMessage API via CrossFrameMessenger.\n *\n * Protocol:\n * `{key}/post` — broadcast a new value\n * `{key}/get` — request the current value from other frames\n *\n * Replaces useCrossFrameState across all frameworks.\n *\n * @fires change — CustomEvent<T> dispatched whenever the value changes (local set or received)\n */\nexport class CrossFrameStateManager<T> extends EventTarget {\n private _value: T | undefined;\n private readonly _key: string;\n private readonly _messenger: CrossFrameMessenger;\n private readonly _options: Required<CrossFrameStateOptions>;\n private readonly _unsubscribers: Array<() => void> = [];\n\n constructor(\n key: string,\n messenger: CrossFrameMessenger,\n options: CrossFrameStateOptions & { initialValue?: T } = {}\n ) {\n super();\n this._key = key;\n this._messenger = messenger;\n this._options = {\n emit: options.emit ?? true,\n receive: options.receive ?? true,\n };\n if (options.initialValue !== undefined) {\n this._value = options.initialValue;\n }\n }\n\n get value(): T | undefined {\n return this._value;\n }\n\n /** Update the value locally and broadcast it to other frames if emit is enabled. */\n set(newValue: T): void {\n this._value = newValue;\n this.dispatchEvent(new CustomEvent<T>('change', { detail: newValue }));\n if (this._options.emit) {\n this._messenger.send(`${this._key}/post`, newValue);\n }\n }\n\n /**\n * Start listening for incoming state updates and responding to /get requests.\n * If receive=true and no initial value is set, sends a /get to request the current value.\n */\n start(): void {\n if (this._options.receive) {\n const unsub = this._messenger.subscribe<T>(\n `${this._key}/post`,\n (data) => {\n this._value = data;\n this.dispatchEvent(new CustomEvent<T>('change', { detail: data }));\n }\n );\n this._unsubscribers.push(unsub);\n }\n\n if (this._options.emit) {\n // Respond to /get requests by broadcasting current value\n const unsub = this._messenger.subscribe(\n `${this._key}/get`,\n (_, originSenderId) => {\n if (originSenderId === this._messenger.senderId) return;\n if (this._value === undefined) return;\n this._messenger.send(`${this._key}/post`, this._value);\n }\n );\n this._unsubscribers.push(unsub);\n }\n\n // If receiving and no initial value, request it from other frames\n if (this._options.receive && this._value === undefined) {\n this._messenger.send(`${this._key}/get`);\n }\n }\n\n /** Stop all listeners. */\n stop(): void {\n for (const unsub of this._unsubscribers) unsub();\n this._unsubscribers.length = 0;\n }\n\n /** Broadcast the current value to all frames (useful after reconnect). */\n postCurrentValue(): void {\n if (this._value !== undefined) {\n this._messenger.send(`${this._key}/post`, this._value);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAqBA,IAAa,yBAAb,cAA+C,YAAY;CACzD,AAAQ;CACR,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB,iBAAoC,EAAE;CAEvD,YACE,KACA,WACA,UAAyD,EAAE,EAC3D;EACA,OAAO;EACP,KAAK,OAAO;EACZ,KAAK,aAAa;EAClB,KAAK,WAAW;GACd,MAAM,QAAQ,QAAQ;GACtB,SAAS,QAAQ,WAAW;GAC7B;EACD,IAAI,QAAQ,iBAAiB,QAC3B,KAAK,SAAS,QAAQ;;CAI1B,IAAI,QAAuB;EACzB,OAAO,KAAK;;;CAId,IAAI,UAAmB;EACrB,KAAK,SAAS;EACd,KAAK,cAAc,IAAI,YAAe,UAAU,EAAE,QAAQ,UAAU,CAAC,CAAC;EACtE,IAAI,KAAK,SAAS,MAChB,KAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,SAAS;;;;;;CAQvD,QAAc;EACZ,IAAI,KAAK,SAAS,SAAS;GACzB,MAAM,QAAQ,KAAK,WAAW,UAC5B,GAAG,KAAK,KAAK,SACZ,SAAS;IACR,KAAK,SAAS;IACd,KAAK,cAAc,IAAI,YAAe,UAAU,EAAE,QAAQ,MAAM,CAAC,CAAC;KAErE;GACD,KAAK,eAAe,KAAK,MAAM;;EAGjC,IAAI,KAAK,SAAS,MAAM;GAEtB,MAAM,QAAQ,KAAK,WAAW,UAC5B,GAAG,KAAK,KAAK,QACZ,GAAG,mBAAmB;IACrB,IAAI,mBAAmB,KAAK,WAAW,UAAU;IACjD,IAAI,KAAK,WAAW,QAAW;IAC/B,KAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,KAAK,OAAO;KAEzD;GACD,KAAK,eAAe,KAAK,MAAM;;EAIjC,IAAI,KAAK,SAAS,WAAW,KAAK,WAAW,QAC3C,KAAK,WAAW,KAAK,GAAG,KAAK,KAAK,MAAM;;;CAK5C,OAAa;EACX,KAAK,MAAM,SAAS,KAAK,gBAAgB,OAAO;EAChD,KAAK,eAAe,SAAS;;;CAI/B,mBAAyB;EACvB,IAAI,KAAK,WAAW,QAClB,KAAK,WAAW,KAAK,GAAG,KAAK,KAAK,QAAQ,KAAK,OAAO"}
@@ -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":";;;;;;;;;;;;;;;;;AAgDA,IAAa,qBAAb,MAAgC;CAC9B,AAAS;CACT,AAAS;CACT,AAAS;CACT,AAAS;CACT,AAAS;CACT,AAAS;CACT,AAAS;CAET,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAGjB,AAAQ,oBAAyC;CACjD,AAAQ,iBAAsC;CAE9C,AAAQ,oBAAyC;CAEjD,YAAY,QAAkC;EAC5C,KAAK,QAAQ,OAAO;EACpB,KAAK,iBAAiB,OAAO;EAE7B,KAAK,YAAY,IAAI,oBAAoB,OAAO,UAAU;EAE1D,KAAK,gBAAgB,IAAI,kDAEvB,KAAK,WACL;GAAE,MAAM;GAAO,SAAS;GAAM,cAAc;GAAO,CACpD;EAED,KAAK,iBAAiB,IAAI,2DAExB,KAAK,WACL;GAAE,MAAM;GAAM,SAAS;GAAM,cAAc;GAAM,CAClD;EAED,KAAK,qBAAqB,IAAI,+DAE5B,KAAK,UACN;EAED,KAAK,gBAAgB,IAAI,0DAEvB,KAAK,UACN;EAED,KAAK,gBAAgB,IAAI,iDAEvB,KAAK,WACL;GACE,MAAM;GACN,SAAS;GACT,GAAI,OAAO,gBAAgB,EAAE,cAAc,OAAO,eAAe,GAAG,EAAE;GACvE,CACF;EAGD,KAAK,gBAAgB,IAAI,kDAEvB,KAAK,WACL;GACE,MAAM,OAAO,SAAS;GACtB,SAAS,OAAO,SAAS;GAC1B,CACF;EAED,KAAK,cAAc,IAAI,gBAAgB,KAAK,UAAU;EACtD,KAAK,qBAAqB,IAAI,uBAAuB,KAAK,UAAU;;CAGtE,QAAc;EACZ,KAAK,UAAU,OAAO;EACtB,KAAK,cAAc,OAAO;EAC1B,KAAK,eAAe,OAAO;EAC3B,KAAK,mBAAmB,OAAO;EAC/B,KAAK,cAAc,OAAO;EAC1B,KAAK,cAAc,OAAO;EAC1B,KAAK,cAAc,OAAO;EAE1B,IAAI,KAAK,UAAU,UAAU;GAC3B,KAAK,YAAY,OAAO;GACxB,KAAK,mBAAmB,kBAAkB;GAC1C,KAAK,mBAAmB;GAExB,KAAK,UAAU,KAAK,qCAA8C,MAAM;GAExE,IAAI,KAAK,gBAAgB,QAAQ,YAAY,OAC3C,KAAK,2BAA2B;SAE7B;GACL,KAAK,mBAAmB,aAAa;GACrC,KAAK,uBAAuB;;;CAIhC,OAAa;EACX,KAAK,qBAAqB;EAC1B,KAAK,kBAAkB;EACvB,KAAK,qBAAqB;EAC1B,KAAK,oBAAoB;EACzB,KAAK,iBAAiB;EACtB,KAAK,oBAAoB;EACzB,KAAK,UAAU,MAAM;EACrB,KAAK,cAAc,MAAM;EACzB,KAAK,eAAe,MAAM;EAC1B,KAAK,mBAAmB,MAAM;EAC9B,KAAK,cAAc,MAAM;EACzB,KAAK,cAAc,MAAM;EACzB,KAAK,cAAc,MAAM;EACzB,KAAK,YAAY,MAAM;EACvB,KAAK,mBAAmB,iBAAiB;EACzC,KAAK,mBAAmB,YAAY;;;;;;CAStC,aAAmB;EACjB,IAAI,KAAK,UAAU,UAAU;EAE7B,KAAK,UAAU,8BAAuC;;CAKxD,yBAAyB,SAA0B;EACjD,MAAM,WAAW,QAAQ,QACtB,QAAQ,IAAI,SAAS,UAAU,YACjC;EACD,MAAM,OAAO,KAAK,eAAe;EAEjC,IAAI,CAAC,MAAM;EAEX,KAAK,eAAe,IAAI;GAAE,GAAG;GAAM,SAAS;GAAU,CAAC;;CAKzD,oBAAoB,YAA8B;EAChD,IAAI,CAAC,WAAW,SAAS;EACzB,MAAM,UAAU,KAAK,mBAAmB,SAAS,EAAE;EAEnD,KAAK,mBAAmB,IAAI;GAC1B,GAAG;IACF,WAAW,UAA+B;GAC5C,CAAC;;CAKJ,oBAAoB,SAA2B;EAC7C,IAAI,CAAC,QAAQ,SAAS;GACpB,QAAQ,MAAM,wCAAwC,QAAQ;GAC9D;;EAEF,MAAM,UAAU,KAAK,cAAc,SAAS,EAAE;EAE9C,KAAK,cAAc,IAAI;GACrB,GAAG;IACF,QAAQ,UAA+B;GACzC,CAAC;;CAGJ,iBACE,mBACA,UACM;EACN,MAAM,UAAU,KAAK,cAAc,SAAS,EAAE;EAE9C,KAAK,cAAc,IAAI;GACrB,GAAG;IACF,oBAAoB;IACnB,GAAG,QAAQ;IACX,SAAS;IACV;GACF,CAAC;;CAGJ,WACE,mBACA,UACA,UAAqB,EAAE,EACvB,YAAY,MACN;EACN,MAAM,UAAU,KAAK,cAAc,SAAS,EAAE;EAG9C,MAAM,mBAFc,KAAK,mBAAmB,SAAS,EAAE,EAEnB,oBAAoB;EACxD,MAAM,iBAAiB,gBACrB,QAAQ,oBAAoB,WAAW,gBACxC;EAED,IAAI,aAAa;EACjB,IAAI,CAAC,WAAW;GACd,IAAI,QAAQ;GAEZ,MAAM,eAAe,QAAQ,MAAM,GAAG,GAAG;GACzC,MAAM,cAAc,QAAQ,QAAQ,SAAS;GAE7C,IAAI,WAAW,YAAY;GAE3B,OACE,OAAO,wBAAwB,gBAAgB,WAAW,KAC1D,aACA;IACA;IACA,WACE,UAAU,IAAI,YAAY,MAAM,GAAG,YAAY,IAAI,IAAI,MAAM;IAC/D,aAAa,CACX,GAAG,cACH;KAAE,GAAG;KAAa,KAAK;KAAU,CAClC;;;EAIL,MAAM,iBAAiB,wBACrB,gBACA,YACA,SACD;EAED,KAAK,cAAc,IAAI;GACrB,GAAG;IACF,oBAAoB;IACnB,GAAG,QAAQ;IACX,SAAS;IACV;GACF,CAAC;;CAGJ,cACE,mBACA,QACA,UAAqB,EAAE,EACjB;EACN,MAAM,UAAU,KAAK,cAAc,SAAS,EAAE;EAE9C,MAAM,mBADc,KAAK,mBAAmB,SAAS,EAAE,EACnB,oBAAoB;EAIxD,MAAM,UAAU,2BAHO,gBACrB,QAAQ,oBAAoB,WAAW,gBAEgB,EAAE,QAAQ,QAAQ;EAE3E,KAAK,cAAc,IAAI;GACrB,GAAG;IACF,oBAAoB;IACnB,GAAG,QAAQ;IACX,SAAS;IACV;GACF,CAAC;;CAGJ,cACE,mBACA,SACM;EACN,MAAM,UAAU,KAAK,cAAc,SAAS,EAAE;EAE9C,MAAM,mBADc,KAAK,mBAAmB,SAAS,EAAE,EACnB,oBAAoB;EAKxD,MAAM,WAAW,wBAJM,gBACrB,QAAQ,oBAAoB,WAAW,gBAIzB,EACd,SAHqB,wBAAwB,iBAAiB,QAIhD,CACf;EAED,KAAK,cAAc,IAAI;GACrB,GAAG;IACF,oBAAoB;IACnB,GAAG,QAAQ;IACX,SAAS;IACV;GACF,CAAC;;CAGJ,eAAe,mBAA4C;EAEzD,MAAM,UAAU,EAAE,GADF,KAAK,cAAc,SAAS,EAAE,EAChB;EAE9B,OAAO,QAAQ;EAEf,KAAK,cAAc,IAAI,QAAQ;;CAGjC,aAAa,mBAA4C;EAEvD,MAAM,WAAW,EAAE,GADH,KAAK,cAAc,SAAS,EAAE,EACf;EAE/B,OAAO,SAAS;EAEhB,KAAK,cAAc,IAAI,SAAS;;CAGlC,kBAAwB;EACtB,KAAK,cAAc,IAAI,EAAE,CAAC;;CAG5B,gBACE,wBACA,SACyB;EACzB,MAAM,SAAS,KAAK,cAAc;EAClC,IAAI,CAAC,QAAQ,OAAO;EAEpB,MAAM,kBAAkB,QAAQ,QAC7B,QAAQ,IAAI,SAAS,UAAU,YACjC;EAKD,MAAM,cAAc,KAAK,mBAAmB;EAM5C,IAHE,uBAAuB,SAAS,UAAU,IAC1C,uBAAuB,SAAS,WAAW,EAEzB;GAElB,IAAI,eAAe,EAAE,0BAA0B,cAC7C;GAKF,OAAO,wBAFL,OAAO,yBAA8C,WAAW,EAAE,EAIlE,iBACA,KAAK,cAAc,MACpB;;EAGH,MAAM,cAAc,OAAO,KAAK,OAAO,CAAC,QACrC,QACC,IAAI,WAAW,GAAG,uBAAuB,GAAG,KAE3C,CAAC,eAAe,OAAO,aAC3B;EAED,KAAK,MAAM,WAAW,aAAa;GAEjC,MAAM,OAAO,wBADG,OAAO,UAA+B,WAAW,EAAE,EAGjE,iBACA,KAAK,cAAc,MACpB;GACD,IAAI,MAAM,OAAO;;;;;;;CAUrB,AAAQ,wBAA8B;EAEpC,KAAK,oBAAoB,KAAK,UAAU,yCAEhC;GACJ,KAAK,cAAc,IAAI,KAAK;GAC5B,KAAK,UAAU,gCAAyC;IAE3D;EAGD,KAAK,UAAU,8BAAuC;;CAGxD,AAAQ,4BAAkC;EAExC,KAAK,UAAU,6BAAsC;EAGrD,KAAK,oBAAoB,KAAK,UAAU,0CAEhC;GACJ,KAAK,UAAU,6BAAsC;IAExD;EAGD,KAAK,iBAAiB,KAAK,UAAU,4CAE7B;GACJ,KAAK,cAAc,IAAI,KAAK;GAC5B,KAAK,gBAAgB;IAExB;;CAGH,AAAQ,iBAAuB;EAC7B,MAAM,YAAY,KAAK,cAAc;EAErC,IAAI,WACF,KAAK,UAAU,KACb,4BAAqC,QACrC,UACD;EAEH,MAAM,YAAY,KAAK,cAAc;EAErC,IAAI,WACF,KAAK,UAAU,KACb,6BAAsC,QACtC,UACD;EAEH,MAAM,QAAQ,KAAK,mBAAmB;EAEtC,IAAI,OACF,KAAK,UAAU,KACb,0CAAmD,QACnD,MACD;;CAIL,MAAc,oBAAmC;EAC/C,IAAI;GAEF,MAAM,wBAAuB,MADX,OAAO,0CACQ,yBAAyB;GAC1D,MAAM,mBAAmB,OAAO,YAC9B,OAAO,OAAO,qBAAqB,CAChC,MAAM,CACN,KAAK,eAAe,CAAC,WAAW,SAAS,WAAW,CAAC,CACzD;GAED,KAAK,mBAAmB,IAAI,iBAAiB;GAI7C,IAAI,KAAK,cAAc,OACrB,KAAK,gBAAgB;WAEhB,GAAG;GAEV,QAAQ,KAAK,oDAAoD,EAAE"}
@@ -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;CAClC,AAAiB;CACjB,AAAQ,oBAA0C;CAClD,AAAQ,oBAAyC;CAEjD,YAAY,WAAgC;AAC1C,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"}
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;CAClC,AAAiB;CACjB,AAAQ,oBAA0C;CAClD,AAAQ,oBAAyC;CAEjD,YAAY,WAAgC;EAC1C,KAAK,aAAa;;;CAIpB,mBAAyB;EACvB,IAAI,OAAO,WAAW,aAAa;EACnC,KAAK,0BAA0B;GAC7B,KAAK,WAAW,+BAAwC;;EAE1D,OAAO,iBAAiB,aAAa,KAAK,kBAAkB;;;CAI9D,cAAoB;EAClB,KAAK,oBAAoB,KAAK,WAAW,qCAEvC,iBACD;;CAGH,kBAAwB;EACtB,IAAI,KAAK,mBAAmB;GAC1B,OAAO,oBAAoB,aAAa,KAAK,kBAAkB;GAC/D,KAAK,oBAAoB;;;CAI7B,aAAmB;EACjB,KAAK,qBAAqB;EAC1B,KAAK,oBAAoB"}
@@ -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;CAC3B,AAAiB;CACjB,AAAQ,qBAAsD;CAC9D,AAAQ,wBAA4D;CACpE,AAAQ,aAA6C,EAAE;CAEvD,YAAY,WAAgC;AAC1C,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
+ {"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;CAC3B,AAAiB;CACjB,AAAQ,qBAAsD;CAC9D,AAAQ,wBAA4D;CACpE,AAAQ,aAA6C,EAAE;CAEvD,YAAY,WAAgC;EAC1C,KAAK,aAAa;;CAGpB,QAAc;EACZ,IAAI,OAAO,WAAW,aAAa;EAEnC,MAAM,uBAAuB;GAC3B,KAAK,WAAW,KACd,yBAAkC,QAClC,OAAO,SAAS,SACjB;;EAGH,KAAK,qBAAqB,QAAQ;EAClC,KAAK,wBAAwB,QAAQ;EAErC,MAAM,wBAAwB,WAC5B,SAEE,GAAG,MACH;GACA,OAAO,MAAM,MAAM,KAAK;GACxB,OAAO,cAAc,IAAI,MAAM,iBAAiB,CAAC;;EAGrD,QAAQ,YAAY,qBAClB,KAAK,mBACN;EACD,QAAQ,eAAe,qBACrB,KAAK,sBACN;EAED,KAAK,MAAM,aAAa;GACtB;GACA;GACA;GACA;GACD,EAAW;GACV,MAAM,WAAW;GACjB,OAAO,iBAAiB,WAAW,SAAS;GAC5C,KAAK,WAAW,KAAK,CAAC,WAAW,SAAS,CAAC;;EAG7C,gBAAgB;;CAGlB,OAAa;EACX,IAAI,OAAO,WAAW,aAAa;EAEnC,KAAK,MAAM,CAAC,WAAW,aAAa,KAAK,YACvC,OAAO,oBAAoB,WAAW,SAAS;EAEjD,KAAK,aAAa,EAAE;EAEpB,IAAI,KAAK,oBAAoB;GAC3B,QAAQ,YAAY,KAAK;GACzB,KAAK,qBAAqB;;EAE5B,IAAI,KAAK,uBAAuB;GAC9B,QAAQ,eAAe,KAAK;GAC5B,KAAK,wBAAwB"}
@@ -1 +1 @@
1
- {"version":3,"file":"globalManager.mjs","names":["windowGlobals","customEvent"],"sources":["../../../src/core/globalManager.ts"],"sourcesContent":["import type { EditorStateManager } from './EditorStateManager';\n\n/**\n * Type for storing custom properties on the global window object.\n * Used to safely attach singleton instances across multiple module instances.\n */\ntype WindowWithEditorGlobals = typeof window & {\n [MANAGER_KEY]?: EditorStateManager | null;\n [EVENTS_KEY]?: EventTarget;\n [key: string]: unknown;\n};\n\n/**\n * Keys on window used to share the singleton across multiple module instances.\n * When bundlers include @intlayer/editor more than once (e.g. framework package +\n * app), each copy has its own module-level variables. Using window ensures they\n * all read/write the same manager and event-target.\n */\nconst MANAGER_KEY = '__intlayer_editor_manager__';\nconst EVENTS_KEY = '__intlayer_editor_manager_events__';\n\n/**\n * Retrieves or creates a shared EventTarget for global editor manager events.\n * In browser environments, stores the EventTarget on window to ensure sharing\n * across multiple module instances. In SSR environments, creates a fresh\n * EventTarget (no sharing needed on server-side).\n *\n * @returns The shared EventTarget instance for dispatching manager change events\n * @private\n */\nconst getEventTarget = (): EventTarget => {\n if (typeof window === 'undefined') {\n // SSR fallback — create a fresh one (no sharing needed server-side)\n return new EventTarget();\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n if (!windowGlobals[EVENTS_KEY]) {\n windowGlobals[EVENTS_KEY] = new EventTarget();\n }\n\n return windowGlobals[EVENTS_KEY] as EventTarget;\n};\n\n/**\n * Retrieves the global editor state manager instance.\n * Returns null if no manager has been set or in SSR environments where\n * window is undefined.\n *\n * @returns The global EditorStateManager instance or null if not set\n */\nexport const getGlobalEditorManager = (): EditorStateManager | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n const manager = windowGlobals[MANAGER_KEY];\n\n return manager ?? null;\n};\n\n/**\n * Sets the global editor state manager instance and notifies all listeners\n * of the change through a CustomEvent.\n *\n * @param manager - The EditorStateManager instance to set globally, or null to clear it\n */\nexport const setGlobalEditorManager = (\n manager: EditorStateManager | null\n): void => {\n if (typeof window !== 'undefined') {\n const windowGlobals = window as WindowWithEditorGlobals;\n windowGlobals[MANAGER_KEY] = manager;\n }\n\n const eventTarget = getEventTarget();\n\n eventTarget.dispatchEvent(\n new CustomEvent<EditorStateManager | null>('change', { detail: manager })\n );\n};\n\n/**\n * Registers a callback function to be invoked whenever the global editor\n * manager changes. Useful for reactive updates across the application.\n *\n * @param changeCallback - Function to invoke with the new manager state whenever it changes\n * @returns An unsubscribe function that removes the listener when called\n *\n * @example\n * ```typescript\n * const unsubscribe = onGlobalEditorManagerChange((manager) => {\n * console.log('Manager updated:', manager);\n * });\n *\n * // Later, clean up the listener\n * unsubscribe();\n * ```\n */\nexport const onGlobalEditorManagerChange = (\n changeCallback: (manager: EditorStateManager | null) => void\n): (() => void) => {\n const eventTarget = getEventTarget();\n\n const eventHandler = (event: Event) => {\n const customEvent = event as CustomEvent<EditorStateManager | null>;\n changeCallback(customEvent.detail);\n };\n\n eventTarget.addEventListener('change', eventHandler);\n\n return () => {\n eventTarget.removeEventListener('change', eventHandler);\n };\n};\n"],"mappings":";;;;;;;AAkBA,MAAM,cAAc;AACpB,MAAM,aAAa;;;;;;;;;;AAWnB,MAAM,uBAAoC;AACxC,KAAI,OAAO,WAAW,YAEpB,QAAO,IAAI,aAAa;CAG1B,MAAM,gBAAgB;AACtB,KAAI,CAAC,cAAc,YACjB,eAAc,cAAc,IAAI,aAAa;AAG/C,QAAO,cAAc;;;;;;;;;AAUvB,MAAa,+BAA0D;AACrE,KAAI,OAAO,WAAW,YACpB,QAAO;AAMT,QAFgBA,OAAc,gBAEZ;;;;;;;;AASpB,MAAa,0BACX,YACS;AACT,KAAI,OAAO,WAAW,aAAa;EACjC,MAAM,gBAAgB;AACtB,gBAAc,eAAe;;AAK/B,CAFoB,gBAET,CAAC,cACV,IAAI,YAAuC,UAAU,EAAE,QAAQ,SAAS,CAAC,CAC1E;;;;;;;;;;;;;;;;;;;AAoBH,MAAa,+BACX,mBACiB;CACjB,MAAM,cAAc,gBAAgB;CAEpC,MAAM,gBAAgB,UAAiB;AAErC,iBAAeC,MAAY,OAAO;;AAGpC,aAAY,iBAAiB,UAAU,aAAa;AAEpD,cAAa;AACX,cAAY,oBAAoB,UAAU,aAAa"}
1
+ {"version":3,"file":"globalManager.mjs","names":["windowGlobals","customEvent"],"sources":["../../../src/core/globalManager.ts"],"sourcesContent":["import type { EditorStateManager } from './EditorStateManager';\n\n/**\n * Type for storing custom properties on the global window object.\n * Used to safely attach singleton instances across multiple module instances.\n */\ntype WindowWithEditorGlobals = typeof window & {\n [MANAGER_KEY]?: EditorStateManager | null;\n [EVENTS_KEY]?: EventTarget;\n [key: string]: unknown;\n};\n\n/**\n * Keys on window used to share the singleton across multiple module instances.\n * When bundlers include @intlayer/editor more than once (e.g. framework package +\n * app), each copy has its own module-level variables. Using window ensures they\n * all read/write the same manager and event-target.\n */\nconst MANAGER_KEY = '__intlayer_editor_manager__';\nconst EVENTS_KEY = '__intlayer_editor_manager_events__';\n\n/**\n * Retrieves or creates a shared EventTarget for global editor manager events.\n * In browser environments, stores the EventTarget on window to ensure sharing\n * across multiple module instances. In SSR environments, creates a fresh\n * EventTarget (no sharing needed on server-side).\n *\n * @returns The shared EventTarget instance for dispatching manager change events\n * @private\n */\nconst getEventTarget = (): EventTarget => {\n if (typeof window === 'undefined') {\n // SSR fallback — create a fresh one (no sharing needed server-side)\n return new EventTarget();\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n if (!windowGlobals[EVENTS_KEY]) {\n windowGlobals[EVENTS_KEY] = new EventTarget();\n }\n\n return windowGlobals[EVENTS_KEY] as EventTarget;\n};\n\n/**\n * Retrieves the global editor state manager instance.\n * Returns null if no manager has been set or in SSR environments where\n * window is undefined.\n *\n * @returns The global EditorStateManager instance or null if not set\n */\nexport const getGlobalEditorManager = (): EditorStateManager | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n const manager = windowGlobals[MANAGER_KEY];\n\n return manager ?? null;\n};\n\n/**\n * Sets the global editor state manager instance and notifies all listeners\n * of the change through a CustomEvent.\n *\n * @param manager - The EditorStateManager instance to set globally, or null to clear it\n */\nexport const setGlobalEditorManager = (\n manager: EditorStateManager | null\n): void => {\n if (typeof window !== 'undefined') {\n const windowGlobals = window as WindowWithEditorGlobals;\n windowGlobals[MANAGER_KEY] = manager;\n }\n\n const eventTarget = getEventTarget();\n\n eventTarget.dispatchEvent(\n new CustomEvent<EditorStateManager | null>('change', { detail: manager })\n );\n};\n\n/**\n * Registers a callback function to be invoked whenever the global editor\n * manager changes. Useful for reactive updates across the application.\n *\n * @param changeCallback - Function to invoke with the new manager state whenever it changes\n * @returns An unsubscribe function that removes the listener when called\n *\n * @example\n * ```typescript\n * const unsubscribe = onGlobalEditorManagerChange((manager) => {\n * console.log('Manager updated:', manager);\n * });\n *\n * // Later, clean up the listener\n * unsubscribe();\n * ```\n */\nexport const onGlobalEditorManagerChange = (\n changeCallback: (manager: EditorStateManager | null) => void\n): (() => void) => {\n const eventTarget = getEventTarget();\n\n const eventHandler = (event: Event) => {\n const customEvent = event as CustomEvent<EditorStateManager | null>;\n changeCallback(customEvent.detail);\n };\n\n eventTarget.addEventListener('change', eventHandler);\n\n return () => {\n eventTarget.removeEventListener('change', eventHandler);\n };\n};\n"],"mappings":";;;;;;;AAkBA,MAAM,cAAc;AACpB,MAAM,aAAa;;;;;;;;;;AAWnB,MAAM,uBAAoC;CACxC,IAAI,OAAO,WAAW,aAEpB,OAAO,IAAI,aAAa;CAG1B,MAAM,gBAAgB;CACtB,IAAI,CAAC,cAAc,aACjB,cAAc,cAAc,IAAI,aAAa;CAG/C,OAAO,cAAc;;;;;;;;;AAUvB,MAAa,+BAA0D;CACrE,IAAI,OAAO,WAAW,aACpB,OAAO;CAMT,OAFgBA,OAAc,gBAEZ;;;;;;;;AASpB,MAAa,0BACX,YACS;CACT,IAAI,OAAO,WAAW,aAAa;EACjC,MAAM,gBAAgB;EACtB,cAAc,eAAe;;CAK/B,AAFoB,gBAET,CAAC,cACV,IAAI,YAAuC,UAAU,EAAE,QAAQ,SAAS,CAAC,CAC1E;;;;;;;;;;;;;;;;;;;AAoBH,MAAa,+BACX,mBACiB;CACjB,MAAM,cAAc,gBAAgB;CAEpC,MAAM,gBAAgB,UAAiB;EAErC,eAAeC,MAAY,OAAO;;CAGpC,YAAY,iBAAiB,UAAU,aAAa;CAEpD,aAAa;EACX,YAAY,oBAAoB,UAAU,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"initEditorClient.mjs","names":[],"sources":["../../../src/core/initEditorClient.ts"],"sourcesContent":["import { default as configuration, editor } from '@intlayer/config/built';\nimport { defineIntlayerElements } from '../components';\nimport type { MessengerConfig } from './CrossFrameMessenger';\nimport { EditorStateManager } from './EditorStateManager';\nimport {\n getGlobalEditorManager,\n setGlobalEditorManager,\n} from './globalManager';\n\nexport const buildClientMessengerConfig = (): MessengerConfig => {\n return {\n allowedOrigins: [editor?.editorURL, editor?.cmsURL].filter(\n Boolean\n ) as string[],\n postMessageFn: (payload: unknown, origin: string) => {\n if (typeof window === 'undefined') return;\n\n const isInIframe = window.self !== window.top;\n\n if (!isInIframe) return;\n window.parent?.postMessage(payload, origin);\n },\n };\n};\n\n/** Reference count — tracks how many providers have called initEditorClient. */\nlet _clientRefCount = 0;\n\n/**\n * Initialize the Intlayer editor client singleton.\n * Safe to call multiple times — returns the existing manager if already initialized.\n * Increments a reference counter so nested providers don't destroy the manager\n * prematurely when the inner provider unmounts.\n */\nexport const initEditorClient = (): EditorStateManager => {\n _clientRefCount++;\n\n const existing = getGlobalEditorManager();\n if (existing) return existing;\n\n const manager = new EditorStateManager({\n mode: 'client',\n messenger: buildClientMessengerConfig(),\n configuration,\n });\n\n setGlobalEditorManager(manager);\n defineIntlayerElements();\n manager.start();\n\n return manager;\n};\n\n/**\n * Decrement the reference count and stop the global editor client singleton\n * only when the last provider unmounts.\n */\nexport const stopEditorClient = (): void => {\n _clientRefCount = Math.max(0, _clientRefCount - 1);\n\n if (_clientRefCount > 0) return;\n\n const manager = getGlobalEditorManager();\n manager?.stop();\n setGlobalEditorManager(null);\n};\n"],"mappings":";;;;;;AASA,MAAa,mCAAoD;AAC/D,QAAO;EACL,gBAAgB,CAAC,QAAQ,WAAW,QAAQ,OAAO,CAAC,OAClD,QACD;EACD,gBAAgB,SAAkB,WAAmB;AACnD,OAAI,OAAO,WAAW,YAAa;AAInC,OAAI,EAFe,OAAO,SAAS,OAAO,KAEzB;AACjB,UAAO,QAAQ,YAAY,SAAS,OAAO;;EAE9C;;;AAIH,IAAI,kBAAkB;;;;;;;AAQtB,MAAa,yBAA6C;AACxD;CAEA,MAAM,WAAW,wBAAwB;AACzC,KAAI,SAAU,QAAO;CAErB,MAAM,UAAU,IAAI,mBAAmB;EACrC,MAAM;EACN,WAAW,4BAA4B;EACvC;EACD,CAAC;AAEF,wBAAuB,QAAQ;AAC/B,yBAAwB;AACxB,SAAQ,OAAO;AAEf,QAAO;;;;;;AAOT,MAAa,yBAA+B;AAC1C,mBAAkB,KAAK,IAAI,GAAG,kBAAkB,EAAE;AAElD,KAAI,kBAAkB,EAAG;AAGzB,CADgB,wBACT,EAAE,MAAM;AACf,wBAAuB,KAAK"}
1
+ {"version":3,"file":"initEditorClient.mjs","names":[],"sources":["../../../src/core/initEditorClient.ts"],"sourcesContent":["import { default as configuration, editor } from '@intlayer/config/built';\nimport { defineIntlayerElements } from '../components';\nimport type { MessengerConfig } from './CrossFrameMessenger';\nimport { EditorStateManager } from './EditorStateManager';\nimport {\n getGlobalEditorManager,\n setGlobalEditorManager,\n} from './globalManager';\n\nexport const buildClientMessengerConfig = (): MessengerConfig => {\n return {\n allowedOrigins: [editor?.editorURL, editor?.cmsURL].filter(\n Boolean\n ) as string[],\n postMessageFn: (payload: unknown, origin: string) => {\n if (typeof window === 'undefined') return;\n\n const isInIframe = window.self !== window.top;\n\n if (!isInIframe) return;\n window.parent?.postMessage(payload, origin);\n },\n };\n};\n\n/** Reference count — tracks how many providers have called initEditorClient. */\nlet _clientRefCount = 0;\n\n/**\n * Initialize the Intlayer editor client singleton.\n * Safe to call multiple times — returns the existing manager if already initialized.\n * Increments a reference counter so nested providers don't destroy the manager\n * prematurely when the inner provider unmounts.\n */\nexport const initEditorClient = (): EditorStateManager => {\n _clientRefCount++;\n\n const existing = getGlobalEditorManager();\n if (existing) return existing;\n\n const manager = new EditorStateManager({\n mode: 'client',\n messenger: buildClientMessengerConfig(),\n configuration,\n });\n\n setGlobalEditorManager(manager);\n defineIntlayerElements();\n manager.start();\n\n return manager;\n};\n\n/**\n * Decrement the reference count and stop the global editor client singleton\n * only when the last provider unmounts.\n */\nexport const stopEditorClient = (): void => {\n _clientRefCount = Math.max(0, _clientRefCount - 1);\n\n if (_clientRefCount > 0) return;\n\n const manager = getGlobalEditorManager();\n manager?.stop();\n setGlobalEditorManager(null);\n};\n"],"mappings":";;;;;;AASA,MAAa,mCAAoD;CAC/D,OAAO;EACL,gBAAgB,CAAC,QAAQ,WAAW,QAAQ,OAAO,CAAC,OAClD,QACD;EACD,gBAAgB,SAAkB,WAAmB;GACnD,IAAI,OAAO,WAAW,aAAa;GAInC,IAAI,EAFe,OAAO,SAAS,OAAO,MAEzB;GACjB,OAAO,QAAQ,YAAY,SAAS,OAAO;;EAE9C;;;AAIH,IAAI,kBAAkB;;;;;;;AAQtB,MAAa,yBAA6C;CACxD;CAEA,MAAM,WAAW,wBAAwB;CACzC,IAAI,UAAU,OAAO;CAErB,MAAM,UAAU,IAAI,mBAAmB;EACrC,MAAM;EACN,WAAW,4BAA4B;EACvC;EACD,CAAC;CAEF,uBAAuB,QAAQ;CAC/B,wBAAwB;CACxB,QAAQ,OAAO;CAEf,OAAO;;;;;;AAOT,MAAa,yBAA+B;CAC1C,kBAAkB,KAAK,IAAI,GAAG,kBAAkB,EAAE;CAElD,IAAI,kBAAkB,GAAG;CAGzB,AADgB,wBACT,EAAE,MAAM;CACf,uBAAuB,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"mergeIframeClick.mjs","names":[],"sources":["../../src/mergeIframeClick.ts"],"sourcesContent":["// Listener for messages from the iframe\nexport const mergeIframeClick = (event: MessageEvent) => {\n // Simulate or merge the iframe message with a click event\n const simulatedMouseDownEvent = new MouseEvent('mousedown', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n const simulatedClickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n\n // Optionally attach additional properties from the iframe message\n Object.assign(simulatedClickEvent, { iframeData: event });\n Object.assign(simulatedMouseDownEvent, { iframeData: event });\n\n // Dispatch the simulated click event on the window or a specific element\n window.dispatchEvent(simulatedClickEvent);\n window.dispatchEvent(simulatedMouseDownEvent);\n};\n"],"mappings":";AACA,MAAa,oBAAoB,UAAwB;CAEvD,MAAM,0BAA0B,IAAI,WAAW,aAAa;EAC1D,SAAS;EACT,YAAY;EACZ,MAAM;EACP,CAAC;CACF,MAAM,sBAAsB,IAAI,WAAW,SAAS;EAClD,SAAS;EACT,YAAY;EACZ,MAAM;EACP,CAAC;AAGF,QAAO,OAAO,qBAAqB,EAAE,YAAY,OAAO,CAAC;AACzD,QAAO,OAAO,yBAAyB,EAAE,YAAY,OAAO,CAAC;AAG7D,QAAO,cAAc,oBAAoB;AACzC,QAAO,cAAc,wBAAwB"}
1
+ {"version":3,"file":"mergeIframeClick.mjs","names":[],"sources":["../../src/mergeIframeClick.ts"],"sourcesContent":["// Listener for messages from the iframe\nexport const mergeIframeClick = (event: MessageEvent) => {\n // Simulate or merge the iframe message with a click event\n const simulatedMouseDownEvent = new MouseEvent('mousedown', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n const simulatedClickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n\n // Optionally attach additional properties from the iframe message\n Object.assign(simulatedClickEvent, { iframeData: event });\n Object.assign(simulatedMouseDownEvent, { iframeData: event });\n\n // Dispatch the simulated click event on the window or a specific element\n window.dispatchEvent(simulatedClickEvent);\n window.dispatchEvent(simulatedMouseDownEvent);\n};\n"],"mappings":";AACA,MAAa,oBAAoB,UAAwB;CAEvD,MAAM,0BAA0B,IAAI,WAAW,aAAa;EAC1D,SAAS;EACT,YAAY;EACZ,MAAM;EACP,CAAC;CACF,MAAM,sBAAsB,IAAI,WAAW,SAAS;EAClD,SAAS;EACT,YAAY;EACZ,MAAM;EACP,CAAC;CAGF,OAAO,OAAO,qBAAqB,EAAE,YAAY,OAAO,CAAC;CACzD,OAAO,OAAO,yBAAyB,EAAE,YAAY,OAAO,CAAC;CAG7D,OAAO,cAAc,oBAAoB;CACzC,OAAO,cAAc,wBAAwB"}
@@ -1 +1 @@
1
- {"version":3,"file":"messageKey.mjs","names":[],"sources":["../../src/messageKey.ts"],"sourcesContent":["export enum MessageKey {\n /** Client → editor: announces the client is ready (also used as response to ARE_YOU_THERE) */\n INTLAYER_CLIENT_READY = 'INTLAYER_CLIENT_READY',\n /** Editor → client: asks if the client is still alive */\n INTLAYER_ARE_YOU_THERE = 'INTLAYER_ARE_YOU_THERE',\n /** Editor → client: instructs the client to activate the editor selector */\n INTLAYER_EDITOR_ACTIVATE = 'INTLAYER_EDITOR_ACTIVATE',\n /** Client → editor: instructs the editor that the client is enabled */\n INTLAYER_EDITOR_ENABLED = 'INTLAYER_EDITOR_ENABLED',\n\n /** Client → editor: send information related the app */\n INTLAYER_CONFIGURATION = 'INTLAYER_CONFIGURATION',\n INTLAYER_CURRENT_LOCALE = 'INTLAYER_CURRENT_LOCALE',\n INTLAYER_URL_CHANGE = 'INTLAYER_URL_CHANGE',\n\n /** Client → editor: load and update content */\n INTLAYER_LOCALE_DICTIONARIES_CHANGED = 'INTLAYER_LOCALE_DICTIONARIES_CHANGED',\n INTLAYER_DISTANT_DICTIONARIES_CHANGED = 'INTLAYER_DISTANT_DICTIONARIES_CHANGED',\n\n /** Client → editor & Editor → client: Update focus and changed content */\n INTLAYER_HOVERED_CONTENT_CHANGED = 'INTLAYER_HOVERED_CONTENT_CHANGED',\n INTLAYER_FOCUSED_CONTENT_CHANGED = 'INTLAYER_FOCUSED_CONTENT_CHANGED',\n INTLAYER_EDITED_CONTENT_CHANGED = 'INTLAYER_EDITED_CONTENT_CHANGED',\n\n /** Client → editor: Helper to sync iframe client reactivity on click client */\n INTLAYER_IFRAME_CLICKED = 'INTLAYER_IFRAME_CLICKED',\n}\n"],"mappings":";AAAA,IAAY,aAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAGA;AACA;AACA;;AAGA;AACA;;AAGA;AACA;AACA;;AAGA;;KACD"}
1
+ {"version":3,"file":"messageKey.mjs","names":[],"sources":["../../src/messageKey.ts"],"sourcesContent":["export enum MessageKey {\n /** Client → editor: announces the client is ready (also used as response to ARE_YOU_THERE) */\n INTLAYER_CLIENT_READY = 'INTLAYER_CLIENT_READY',\n /** Editor → client: asks if the client is still alive */\n INTLAYER_ARE_YOU_THERE = 'INTLAYER_ARE_YOU_THERE',\n /** Editor → client: instructs the client to activate the editor selector */\n INTLAYER_EDITOR_ACTIVATE = 'INTLAYER_EDITOR_ACTIVATE',\n /** Client → editor: instructs the editor that the client is enabled */\n INTLAYER_EDITOR_ENABLED = 'INTLAYER_EDITOR_ENABLED',\n\n /** Client → editor: send information related the app */\n INTLAYER_CONFIGURATION = 'INTLAYER_CONFIGURATION',\n INTLAYER_CURRENT_LOCALE = 'INTLAYER_CURRENT_LOCALE',\n INTLAYER_URL_CHANGE = 'INTLAYER_URL_CHANGE',\n\n /** Client → editor: load and update content */\n INTLAYER_LOCALE_DICTIONARIES_CHANGED = 'INTLAYER_LOCALE_DICTIONARIES_CHANGED',\n INTLAYER_DISTANT_DICTIONARIES_CHANGED = 'INTLAYER_DISTANT_DICTIONARIES_CHANGED',\n\n /** Client → editor & Editor → client: Update focus and changed content */\n INTLAYER_HOVERED_CONTENT_CHANGED = 'INTLAYER_HOVERED_CONTENT_CHANGED',\n INTLAYER_FOCUSED_CONTENT_CHANGED = 'INTLAYER_FOCUSED_CONTENT_CHANGED',\n INTLAYER_EDITED_CONTENT_CHANGED = 'INTLAYER_EDITED_CONTENT_CHANGED',\n\n /** Client → editor: Helper to sync iframe client reactivity on click client */\n INTLAYER_IFRAME_CLICKED = 'INTLAYER_IFRAME_CLICKED',\n}\n"],"mappings":";AAAA,IAAY,aAAL;;CAEL;;CAEA;;CAEA;;CAEA;;CAGA;CACA;CACA;;CAGA;CACA;;CAGA;CACA;CACA;;CAGA;;KACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/editor",
3
- "version": "8.9.1",
3
+ "version": "8.9.3",
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,18 +75,18 @@
75
75
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
76
76
  },
77
77
  "dependencies": {
78
- "@intlayer/config": "8.9.1",
79
- "@intlayer/core": "8.9.1",
80
- "@intlayer/types": "8.9.1",
81
- "@intlayer/unmerged-dictionaries-entry": "8.9.1"
78
+ "@intlayer/config": "8.9.3",
79
+ "@intlayer/core": "8.9.3",
80
+ "@intlayer/types": "8.9.3",
81
+ "@intlayer/unmerged-dictionaries-entry": "8.9.3"
82
82
  },
83
83
  "devDependencies": {
84
- "@types/node": "25.6.0",
84
+ "@types/node": "25.6.1",
85
85
  "@utils/ts-config": "1.0.4",
86
86
  "@utils/ts-config-types": "1.0.4",
87
87
  "@utils/tsdown-config": "1.0.4",
88
88
  "rimraf": "6.1.3",
89
- "tsdown": "0.21.10",
89
+ "tsdown": "0.22.00",
90
90
  "typescript": "6.0.3",
91
91
  "vitest": "4.1.5"
92
92
  },