@localess/react 3.3.0-dev.20260708090119 → 3.3.0-dev.20260708124505

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -122,7 +122,7 @@ localessInit({
122
122
 
123
123
  ## `LocalessComponent`
124
124
 
125
- `LocalessComponent` is a dynamic renderer that maps Localess content data to your registered React components by schema key. It automatically applies Visual Editor attributes when sync is enabled.
125
+ `LocalessComponent` is a dynamic renderer that maps Localess content data to your registered React components by schema key. It always applies Visual Editor attributes (`data-ll-id` / `data-ll-schema`) — they're inert outside the Visual Editor iframe.
126
126
 
127
127
  ```tsx
128
128
  import { LocalessComponent } from "@localess/react";
@@ -424,20 +424,17 @@ If you manage content state yourself without `useLocaless` or `LocalessDocument`
424
424
  'use client';
425
425
 
426
426
  import { useEffect, useState } from "react";
427
- import { LocalessComponent, localessEditable, isSyncEnabled, isBrowser } from "@localess/react";
427
+ import { LocalessComponent, localessEditable, localessSyncOn } from "@localess/react";
428
428
  import type { Content, Page } from "./.localess/localess";
429
429
 
430
430
  export function PageClient({ initialContent }: { initialContent: Content<Page> }) {
431
431
  const [pageData, setPageData] = useState(initialContent.data);
432
432
 
433
433
  useEffect(() => {
434
- if (isSyncEnabled() && isBrowser() && window.localess) {
435
- window.localess.on(['input', 'change'], (event) => {
436
- if (event.type === 'input' || event.type === 'change') {
437
- setPageData(event.data);
438
- }
439
- });
440
- }
434
+ // No-op if sync isn't enabled/usable; `event` is narrowed to the 'input' | 'change' variant
435
+ localessSyncOn(['input', 'change'], (event) => {
436
+ setPageData(event.data);
437
+ });
441
438
  // No cleanup needed: window.localess has no .off() method
442
439
  }, []);
443
440
 
@@ -465,6 +462,8 @@ export function PageClient({ initialContent }: { initialContent: Content<Page> }
465
462
 
466
463
  > `window.localess` only exposes `.on()` and `.onChange()` — there is no `.off()` method.
467
464
 
465
+ `localessSyncOn(event, callback)` wraps `.on()`; `localessSyncOnChange(callback)` wraps `.onChange()` — equivalent to `localessSyncOn(['input', 'change'], callback)`, firing only for content-change events (`callback` receives the `input`/`change` variant, not the full `EventToApp` union). Both handle the `isSyncEnabled()` check and the `localessSyncReady()` wait internally.
466
+
468
467
  ---
469
468
 
470
469
  ## Full Example — SPA / Default (`@localess/react`)
@@ -582,7 +581,7 @@ Full control over state and sync subscription. Use when you need custom logic ar
582
581
  'use client';
583
582
 
584
583
  import { useEffect, useState } from "react";
585
- import { LocalessComponent, localessEditable, isSyncEnabled, isBrowser } from "@localess/react";
584
+ import { LocalessComponent, localessEditable, localessSyncOn } from "@localess/react";
586
585
  import type { Content, Page } from "./.localess/localess";
587
586
 
588
587
  export function PageClientManual({
@@ -594,13 +593,10 @@ export function PageClientManual({
594
593
  const [pageData, setPageData] = useState(initialContent.data);
595
594
 
596
595
  useEffect(() => {
597
- if (isSyncEnabled() && isBrowser() && window.localess) {
598
- window.localess.on(['input', 'change'], (event) => {
599
- if (event.type === 'input' || event.type === 'change') {
600
- setPageData(event.data);
601
- }
602
- });
603
- }
596
+ // No-op if sync isn't enabled/usable; `event` is narrowed to the 'input' | 'change' variant
597
+ localessSyncOn(['input', 'change'], (event) => {
598
+ setPageData(event.data);
599
+ });
604
600
  // No cleanup needed: window.localess has no .off() method
605
601
  }, []);
606
602
 
@@ -760,7 +756,7 @@ The table below shows which symbols are available in each export.
760
756
  | `useLocaless` | ✅ | ❌ | ✅ |
761
757
  | `localessEditable` / `localessEditableField` | ✅ | ❌ | ✅ |
762
758
  | `isBrowser` / `isIframe` | ✅ | ❌ | ✅ |
763
- | `isSyncEnabled` | ✅ | ❌ | ✅ |
759
+ | `isSyncEnabled` / `localessSyncOn` / `localessSyncOnChange` / `localessSyncReady` | ✅ | ❌ | ✅ |
764
760
  | Sync event types (`LocalessSync`, `EventToApp`, …) | ✅ | ❌ | ✅ |
765
761
 
766
762
  ---
package/SKILL.md CHANGED
@@ -76,7 +76,7 @@ import { LocalessComponent } from "@localess/react";
76
76
 
77
77
  1. Read `data._schema` as the component registry key
78
78
  2. Look up registered component by that key
79
- 3. If found → render component with `data`, `links`, `references`; when sync is enabled, also injects `data-ll-id` and `data-ll-schema` as props (user components should spread `{...localessEditable(data)}` on their root element)
79
+ 3. If found → render component with `data`, `links`, `references`; always injects `data-ll-id` and `data-ll-schema` as props (harmless outside the Visual Editor iframe; user components should spread `{...localessEditable(data)}` on their root element)
80
80
  4. If not found → try `fallbackComponent`
81
81
  5. If no fallback → render error message
82
82
 
@@ -155,13 +155,14 @@ import { localessEditable, localessEditableField } from "@localess/react";
155
155
 
156
156
  ## Visual Editor Sync
157
157
 
158
- `localessInit()` is the **single entry point** for Visual Editor integration. Setting `enableSync: true` handles everything: injecting the sync script, registering components, and enabling editable attribute injection on `LocalessComponent`.
158
+ `localessInit()` is the **single entry point** for Visual Editor integration. Setting `enableSync: true` injects the sync script and enables live event subscription; component registration is independent of it.
159
159
 
160
160
  ### What `enableSync: true` activates
161
161
 
162
162
  1. Injects the Localess sync script (`loadLocalessSync`) into `<head>`
163
- 2. Makes `LocalessComponent` inject `data-ll-id` and `data-ll-schema` on rendered elements
164
- 3. Makes `localessEditable()` / `localessEditableField()` emit their `data-ll-*` attributes (no-op when sync is off)
163
+ 2. Makes `isSyncEnabled()` and therefore `useLocaless`, `LocalessDocument`, `localessSyncOn`, `localessSyncOnChange` actually subscribe to live editor events
164
+
165
+ `LocalessComponent`'s `data-ll-id` / `data-ll-schema` injection and `localessEditable()` / `localessEditableField()` are unconditional — they always emit their `data-ll-*` attributes regardless of `enableSync`. They're inert outside the Visual Editor iframe, so leaving them on in production is harmless (though sync itself should stay off — see below).
165
166
 
166
167
  ```typescript
167
168
  localessInit({
@@ -246,22 +247,17 @@ If you manage content state yourself without `useLocaless` or `LocalessDocument`
246
247
  'use client';
247
248
 
248
249
  import { useEffect, useState } from "react";
249
- import { LocalessComponent, localessEditable, isSyncEnabled, localessSyncReady } from "@localess/react";
250
+ import { LocalessComponent, localessEditable, localessSyncOn } from "@localess/react";
250
251
  import type { Content, Page } from "./.localess/localess";
251
252
 
252
253
  export function PageClient({ initialContent }: { initialContent: Content<Page> }) {
253
254
  const [pageData, setPageData] = useState(initialContent.data);
254
255
 
255
256
  useEffect(() => {
256
- if (isSyncEnabled()) {
257
- localessSyncReady().then(() => {
258
- window.localess?.on(['input', 'change'], (event) => {
259
- if (event.type === 'input' || event.type === 'change') {
260
- setPageData(event.data);
261
- }
262
- });
263
- });
264
- }
257
+ // No-op if sync isn't enabled/usable; `event` is narrowed to the 'input' | 'change' variant
258
+ localessSyncOn(['input', 'change'], (event) => {
259
+ setPageData(event.data);
260
+ });
265
261
  // No cleanup needed: window.localess has no .off() method
266
262
  }, []);
267
263
 
@@ -294,6 +290,8 @@ export function PageClient({ initialContent }: { initialContent: Content<Page> }
294
290
 
295
291
  > `window.localess` only exposes `.on()` and `.onChange()` — there is no `.off()`.
296
292
 
293
+ `localessSyncOn(event, callback)` wraps `.on()`; `localessSyncOnChange(callback)` wraps `.onChange()` — equivalent to `localessSyncOn(['input', 'change'], callback)`, firing only for content-change events (`callback` receives the `input`/`change` variant, not the full `EventToApp` union). Both handle the `isSyncEnabled()` check and the `localessSyncReady()` wait internally.
294
+
297
295
  ### Pattern: Split Server/Client Components (Next.js App Router)
298
296
 
299
297
  **Preload data server-side** and pass it to the Client Component. The page renders immediately with server data — no loading flash — and Visual Editor sync kicks in on top.
@@ -365,7 +363,7 @@ export default async function HomePage({ params }: { params: Promise<{ locale?:
365
363
  'use client';
366
364
 
367
365
  import { useEffect, useState } from "react";
368
- import { LocalessComponent, localessEditable, isSyncEnabled, localessSyncReady } from "@localess/react";
366
+ import { LocalessComponent, localessEditable, localessSyncOn } from "@localess/react";
369
367
  import type { Content, Page } from "./.localess/localess";
370
368
 
371
369
  export function PageClient({ initialContent }: { initialContent: Content<Page> }) {
@@ -373,15 +371,10 @@ export function PageClient({ initialContent }: { initialContent: Content<Page> }
373
371
  const [pageData, setPageData] = useState(initialContent.data);
374
372
 
375
373
  useEffect(() => {
376
- if (isSyncEnabled()) {
377
- localessSyncReady().then(() => {
378
- window.localess?.on(['input', 'change'], (event) => {
379
- if (event.type === 'input' || event.type === 'change') {
380
- setPageData(event.data);
381
- }
382
- });
383
- });
384
- }
374
+ // No-op if sync isn't enabled/usable; `event` is narrowed to the 'input' | 'change' variant
375
+ localessSyncOn(['input', 'change'], (event) => {
376
+ setPageData(event.data);
377
+ });
385
378
  // No cleanup needed: window.localess has no .off() method
386
379
  }, []);
387
380
 
@@ -623,7 +616,7 @@ export { localessInit, getLocalessClient }
623
616
 
624
617
  // Component registry
625
618
  export { registerComponent, unregisterComponent, setComponents, getComponent }
626
- export { setFallbackComponent, getFallbackComponent, isSyncEnabled }
619
+ export { setFallbackComponent, getFallbackComponent, isSyncEnabled, localessSyncOn, localessSyncOnChange, localessSyncReady }
627
620
 
628
621
  // Rendering
629
622
  export { LocalessComponent } // Dynamic schema-to-component renderer
@@ -647,7 +640,7 @@ export { isBrowser, isServer, isIframe }
647
640
  // Types (re-exported from @localess/client + local)
648
641
  export type { AssetTransformParams } // Image transform params for resolveAsset
649
642
  export type { LocalessClient, LocalessOptions }
650
- export type { LocalessSync, EventToApp, EventCallback, EventToAppType }
643
+ export type { LocalessSync, EventToApp, EventToAppOf, EventCallback, EventToAppType }
651
644
  export type {
652
645
  Content, ContentData, ContentMetadata, ContentDataSchema, ContentDataField,
653
646
  ContentAsset, ContentRichText, ContentLink, ContentReference,
@@ -30,8 +30,8 @@ export type LocalessComponentProps<T extends ContentData = ContentData> = {
30
30
  * Dynamic schema-to-component renderer for use in SPA and client-side contexts.
31
31
  *
32
32
  * Looks up `data._schema` in the component registry (set via `localessInit` or `setComponents`),
33
- * renders the matched component, and when Visual Editor sync is active — automatically
34
- * spreads `localessEditable` attributes on the root element for live targeting.
33
+ * renders the matched component, and always spreads `localessEditable` attributes on the root
34
+ * element (`data-ll-id` / `data-ll-schema`) so the Visual Editor can target it for live editing.
35
35
  *
36
36
  * Falls back to the `fallbackComponent` (if registered) when the schema key is not found,
37
37
  * or renders an inline error message as a last resort.
@@ -1 +1 @@
1
- const e=require("../../console.js"),t=require("../state.js"),n=require("./localess-component.js");let r=require("react"),i=require("react/jsx-runtime");var a=(0,r.forwardRef)(({document:a},o)=>{let[s,c]=(0,r.useState)(a.data);return(0,r.useEffect)(()=>{t.isSyncEnabled()&&t.localessSyncReady().then(()=>{window.localess?.on([`input`,`change`],e=>{(e.type===`change`||e.type===`input`)&&c(e.data)})})},[]),s?(0,i.jsx)(n.LocalessComponent,{ref:o,data:s,assets:a.assets,links:a.links,references:a.references}):(console.error(`LocalessDocument property %cdocument.data%c is not provided.`,e.FONT_BOLD,e.FONT_NORMAL),(0,i.jsxs)(`div`,{children:[`LocalessDocument property `,(0,i.jsx)(`b`,{children:`document.data`}),` is not provided.`]}))});exports.LocalessDocument=a;
1
+ const e=require("../../console.js"),t=require("../state.js"),n=require("./localess-component.js");let r=require("react"),i=require("react/jsx-runtime");var a=(0,r.forwardRef)(({document:a},o)=>{let[s,c]=(0,r.useState)(a.data);return(0,r.useEffect)(()=>{t.localessSyncOn([`input`,`change`],e=>{c(e.data)})},[]),s?(0,i.jsx)(n.LocalessComponent,{ref:o,data:s,assets:a.assets,links:a.links,references:a.references}):(console.error(`LocalessDocument property %cdocument.data%c is not provided.`,e.FONT_BOLD,e.FONT_NORMAL),(0,i.jsxs)(`div`,{children:[`LocalessDocument property `,(0,i.jsx)(`b`,{children:`document.data`}),` is not provided.`]}))});exports.LocalessDocument=a;
@@ -1,28 +1,26 @@
1
1
  import { FONT_BOLD as e, FONT_NORMAL as t } from "../../console.mjs";
2
- import { isSyncEnabled as n, localessSyncReady as r } from "../state.mjs";
3
- import { LocalessComponent as i } from "./localess-component.mjs";
4
- import { forwardRef as a, useEffect as o, useState as s } from "react";
5
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
2
+ import { localessSyncOn as n } from "../state.mjs";
3
+ import { LocalessComponent as r } from "./localess-component.mjs";
4
+ import { forwardRef as i, useEffect as a, useState as o } from "react";
5
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
6
  //#region src/core/components/localess-document.tsx
7
- var u = a(({ document: a }, u) => {
8
- let [d, f] = s(a.data);
9
- return o(() => {
10
- n() && r().then(() => {
11
- window.localess?.on(["input", "change"], (e) => {
12
- (e.type === "change" || e.type === "input") && f(e.data);
13
- });
7
+ var l = i(({ document: i }, l) => {
8
+ let [u, d] = o(i.data);
9
+ return a(() => {
10
+ n(["input", "change"], (e) => {
11
+ d(e.data);
14
12
  });
15
- }, []), d ? /* @__PURE__ */ c(i, {
16
- ref: u,
17
- data: d,
18
- assets: a.assets,
19
- links: a.links,
20
- references: a.references
21
- }) : (console.error("LocalessDocument property %cdocument.data%c is not provided.", e, t), /* @__PURE__ */ l("div", { children: [
13
+ }, []), u ? /* @__PURE__ */ s(r, {
14
+ ref: l,
15
+ data: u,
16
+ assets: i.assets,
17
+ links: i.links,
18
+ references: i.references
19
+ }) : (console.error("LocalessDocument property %cdocument.data%c is not provided.", e, t), /* @__PURE__ */ c("div", { children: [
22
20
  "LocalessDocument property ",
23
- /* @__PURE__ */ c("b", { children: "document.data" }),
21
+ /* @__PURE__ */ s("b", { children: "document.data" }),
24
22
  " is not provided."
25
23
  ] }));
26
24
  });
27
25
  //#endregion
28
- export { u as LocalessDocument };
26
+ export { l as LocalessDocument };
@@ -1 +1 @@
1
- const e=require("../state.js");let t=require("react");var n=(n,r={})=>{let[i,a]=(0,t.useState)(),o=e.getLocalessClient(),s;return s=Array.isArray(n)?n.join(`/`):n,(0,t.useEffect)(()=>{async function t(){let t=await o.getContentBySlug(s,r);a(t),e.isSyncEnabled()&&(await e.localessSyncReady(),window.localess?.on([`input`,`change`],e=>{(e.type===`change`||e.type===`input`)&&a({...t,data:e.data})}))}t()},[n,r,o]),i};exports.useLocaless=n;
1
+ const e=require("../state.js");let t=require("react");var n=(n,r={})=>{let[i,a]=(0,t.useState)(),o=e.getLocalessClient(),s;return s=Array.isArray(n)?n.join(`/`):n,(0,t.useEffect)(()=>{async function t(){let t=await o.getContentBySlug(s,r);a(t),e.localessSyncOn([`input`,`change`],e=>{a({...t,data:e.data})})}t()},[n,r,o]),i};exports.useLocaless=n;
@@ -1,24 +1,24 @@
1
- import { getLocalessClient as e, isSyncEnabled as t, localessSyncReady as n } from "../state.mjs";
2
- import { useEffect as r, useState as i } from "react";
1
+ import { getLocalessClient as e, localessSyncOn as t } from "../state.mjs";
2
+ import { useEffect as n, useState as r } from "react";
3
3
  //#region src/core/hooks/use-localess.ts
4
- var a = (a, o = {}) => {
5
- let [s, c] = i(), l = e(), u;
6
- return u = Array.isArray(a) ? a.join("/") : a, r(() => {
4
+ var i = (i, a = {}) => {
5
+ let [o, s] = r(), c = e(), l;
6
+ return l = Array.isArray(i) ? i.join("/") : i, n(() => {
7
7
  async function e() {
8
- let e = await l.getContentBySlug(u, o);
9
- c(e), t() && (await n(), window.localess?.on(["input", "change"], (t) => {
10
- (t.type === "change" || t.type === "input") && c({
8
+ let e = await c.getContentBySlug(l, a);
9
+ s(e), t(["input", "change"], (t) => {
10
+ s({
11
11
  ...e,
12
12
  data: t.data
13
13
  });
14
- }));
14
+ });
15
15
  }
16
16
  e();
17
17
  }, [
18
+ i,
18
19
  a,
19
- o,
20
- l
21
- ]), s;
20
+ c
21
+ ]), o;
22
22
  };
23
23
  //#endregion
24
- export { a as useLocaless };
24
+ export { i as useLocaless };
@@ -1 +1 @@
1
- export type { EventCallback, EventToApp, EventToAppType, LocalessSync } from '@localess/client';
1
+ export type { EventCallback, EventToApp, EventToAppOf, EventToAppType, LocalessSync } from '@localess/client';
@@ -1,4 +1,4 @@
1
- import { AssetTransformParams, LocalessClient } from '@localess/client';
1
+ import { AssetTransformParams, EventToAppOf, EventToAppType, LocalessClient } from '@localess/client';
2
2
  import { default as React } from 'react';
3
3
  import { ContentAsset, LocalessOptions } from './models';
4
4
  /**
@@ -107,8 +107,8 @@ export declare function getFallbackComponent(): React.ElementType | undefined;
107
107
  *
108
108
  * Self-sufficient: callers don't need to separately check {@link isBrowser} or {@link isIframe}.
109
109
  *
110
- * Used internally by {@link LocalessComponent}, {@link LocalessDocument}, and {@link useLocaless}
111
- * to decide whether to inject editable attributes and subscribe to sync events.
110
+ * Used internally by {@link localessSyncOn} and {@link localessSyncOnChange} to decide whether to
111
+ * subscribe to sync events and therefore indirectly by {@link LocalessDocument} and {@link useLocaless}.
112
112
  *
113
113
  * @returns `true` if sync is enabled and usable, `false` otherwise.
114
114
  */
@@ -120,8 +120,9 @@ export declare function isSyncEnabled(): boolean;
120
120
  * loaded. Never rejects — a failed script load is logged via {@link loadLocalessSync} and resolves
121
121
  * anyway, since the rest of the app functions without live editing.
122
122
  *
123
- * Use this before subscribing to `window.localess.on(...)` to avoid a race where the listener is
124
- * attached before the sync script has run.
123
+ * Use this before subscribing directly to `window.localess.on(...)` to avoid a race where the
124
+ * listener is attached before the sync script has run. Prefer {@link localessSyncOn} instead —
125
+ * it wraps this exact pattern.
125
126
  *
126
127
  * @returns A promise that resolves when sync is ready (or immediately, if not applicable).
127
128
  *
@@ -137,6 +138,43 @@ export declare function isSyncEnabled(): boolean;
137
138
  * ```
138
139
  */
139
140
  export declare function localessSyncReady(): Promise<void>;
141
+ /**
142
+ * Subscribes to Visual Editor sync event(s), handling the `isSyncEnabled()` check and the
143
+ * {@link localessSyncReady} wait internally so call sites don't have to.
144
+ *
145
+ * No-op if sync isn't enabled or usable in the current context (see {@link isSyncEnabled}).
146
+ *
147
+ * @param event - A single event type or array of event types to subscribe to.
148
+ * @param callback - Called with the event, narrowed to the variant(s) matching `event`.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * useEffect(() => {
153
+ * localessSyncOn(['input', 'change'], event => setContentData(event.data));
154
+ * }, []);
155
+ * ```
156
+ */
157
+ export declare function localessSyncOn<T extends EventToAppType>(event: T | T[], callback: (event: EventToAppOf<T>) => void): void;
158
+ /**
159
+ * Subscribes to content-change Visual Editor sync events (`input` and `change`), handling the
160
+ * `isSyncEnabled()` check and the {@link localessSyncReady} wait internally so call sites don't
161
+ * have to.
162
+ *
163
+ * Equivalent to `localessSyncOn(['input', 'change'], callback)` (mirrors `window.localess.onChange`).
164
+ * For other event types (`save`, `publish`, `pong`, `enterSchema`, `hoverSchema`), use {@link localessSyncOn}.
165
+ *
166
+ * No-op if sync isn't enabled or usable in the current context (see {@link isSyncEnabled}).
167
+ *
168
+ * @param callback - Called with the `input`/`change` event.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * useEffect(() => {
173
+ * localessSyncOnChange(event => setContentData(event.data));
174
+ * }, []);
175
+ * ```
176
+ */
177
+ export declare function localessSyncOnChange(callback: (event: EventToAppOf<'change' | 'input'>) => void): void;
140
178
  export declare function getOrigin(): string;
141
179
  /**
142
180
  * Resolves a {@link ContentAsset} to its full URL string.
@@ -1 +1 @@
1
- const e=require("../console.js");let t=require("@localess/client");var n=void 0,r=void 0,i={},a=void 0,o=!1,s=void 0,c=``;function l(e){let{components:l,fallbackComponent:u,enableSync:d,...f}=e;return r=(0,t.localessClient)(f),n=f.origin,c=`${e.origin}/api/v1/spaces/${e.spaceId}/assets/`,i=l||{},a=u,d&&(o=!0,s=(0,t.loadLocalessSync)(f.origin).catch(e=>{console.error(`[Localess] Failed to load sync script.`,e)})),r}function u(){if(!r)throw console.error(`[Localess] No client found. Please check if the Localess is initialized. Use localessInit function.`),Error(`[Localess] No client found.`);return r}function d(e,t){i[e]=t}function f(e){delete i[e]}function p(e){i=e}function m(t){if(Object.hasOwn(i,t))return i[t];console.error(`[Localess] component %c${t}%c can't be found.`,e.FONT_BOLD,e.FONT_NORMAL)}function h(e){a=e}function g(){return a}function _(){return o&&(0,t.isBrowser)()&&(0,t.isIframe)()}function v(){return s??Promise.resolve()}function y(){if(!n)throw console.error(`[Localess] No origin found. Please check if the Localess is initialized. Use localessInit function.`),Error(`[Localess] No origin found.`);return n}function b(e,n){let r=`${c}${e.uri}`,i=(0,t.buildAssetQueryString)(n);return i?`${r}?${i}`:r}exports.getComponent=m,exports.getFallbackComponent=g,exports.getLocalessClient=u,exports.getOrigin=y,exports.isSyncEnabled=_,exports.localessInit=l,exports.localessSyncReady=v,exports.registerComponent=d,exports.resolveAsset=b,exports.setComponents=p,exports.setFallbackComponent=h,exports.unregisterComponent=f;
1
+ const e=require("../console.js");let t=require("@localess/client");var n=void 0,r=void 0,i={},a=void 0,o=!1,s=void 0,c=``;function l(e){let{components:l,fallbackComponent:u,enableSync:d,...f}=e;return r=(0,t.localessClient)(f),n=f.origin,c=`${e.origin}/api/v1/spaces/${e.spaceId}/assets/`,i=l||{},a=u,d&&(o=!0,s=(0,t.loadLocalessSync)(f.origin).catch(e=>{console.error(`[Localess] Failed to load sync script.`,e)})),r}function u(){if(!r)throw console.error(`[Localess] No client found. Please check if the Localess is initialized. Use localessInit function.`),Error(`[Localess] No client found.`);return r}function d(e,t){i[e]=t}function f(e){delete i[e]}function p(e){i=e}function m(t){if(Object.hasOwn(i,t))return i[t];console.error(`[Localess] component %c${t}%c can't be found.`,e.FONT_BOLD,e.FONT_NORMAL)}function h(e){a=e}function g(){return a}function _(){return o&&(0,t.isBrowser)()&&(0,t.isIframe)()}function v(){return s??Promise.resolve()}function y(e,t){_()&&v().then(()=>{window.localess?.on(e,t)})}function b(e){_()&&v().then(()=>{window.localess?.onChange(e)})}function x(){if(!n)throw console.error(`[Localess] No origin found. Please check if the Localess is initialized. Use localessInit function.`),Error(`[Localess] No origin found.`);return n}function S(e,n){let r=`${c}${e.uri}`,i=(0,t.buildAssetQueryString)(n);return i?`${r}?${i}`:r}exports.getComponent=m,exports.getFallbackComponent=g,exports.getLocalessClient=u,exports.getOrigin=x,exports.isSyncEnabled=_,exports.localessInit=l,exports.localessSyncOn=y,exports.localessSyncOnChange=b,exports.localessSyncReady=v,exports.registerComponent=d,exports.resolveAsset=S,exports.setComponents=p,exports.setFallbackComponent=h,exports.unregisterComponent=f;
@@ -37,13 +37,23 @@ function S() {
37
37
  function C() {
38
38
  return f ?? Promise.resolve();
39
39
  }
40
- function w() {
40
+ function w(e, t) {
41
+ S() && C().then(() => {
42
+ window.localess?.on(e, t);
43
+ });
44
+ }
45
+ function T(e) {
46
+ S() && C().then(() => {
47
+ window.localess?.onChange(e);
48
+ });
49
+ }
50
+ function E() {
41
51
  if (!s) throw console.error("[Localess] No origin found. Please check if the Localess is initialized. Use localessInit function."), Error("[Localess] No origin found.");
42
52
  return s;
43
53
  }
44
- function T(e, t) {
54
+ function D(e, t) {
45
55
  let r = `${p}${e.uri}`, i = n(t);
46
56
  return i ? `${r}?${i}` : r;
47
57
  }
48
58
  //#endregion
49
- export { y as getComponent, x as getFallbackComponent, h as getLocalessClient, w as getOrigin, S as isSyncEnabled, m as localessInit, C as localessSyncReady, g as registerComponent, T as resolveAsset, v as setComponents, b as setFallbackComponent, _ as unregisterComponent };
59
+ export { y as getComponent, x as getFallbackComponent, h as getLocalessClient, E as getOrigin, S as isSyncEnabled, m as localessInit, w as localessSyncOn, T as localessSyncOnChange, C as localessSyncReady, g as registerComponent, D as resolveAsset, v as setComponents, b as setFallbackComponent, _ as unregisterComponent };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core/state.js"),t=require("./core/utils/link.util.js"),n=require("./core/components/localess-component.js"),r=require("./core/components/localess-document.js"),i=require("./core/hooks/use-localess.js"),a=require("./core/richtext.js");let o=require("@localess/client");exports.LocalessComponent=n.LocalessComponent,exports.LocalessDocument=r.LocalessDocument,exports.findLink=t.findLink,exports.getComponent=e.getComponent,exports.getFallbackComponent=e.getFallbackComponent,exports.getLocalessClient=e.getLocalessClient,exports.getOrigin=e.getOrigin,Object.defineProperty(exports,"isBrowser",{enumerable:!0,get:function(){return o.isBrowser}}),Object.defineProperty(exports,"isIframe",{enumerable:!0,get:function(){return o.isIframe}}),Object.defineProperty(exports,"isServer",{enumerable:!0,get:function(){return o.isServer}}),exports.isSyncEnabled=e.isSyncEnabled,Object.defineProperty(exports,"localessEditable",{enumerable:!0,get:function(){return o.localessEditable}}),Object.defineProperty(exports,"localessEditableField",{enumerable:!0,get:function(){return o.localessEditableField}}),exports.localessInit=e.localessInit,exports.localessSyncReady=e.localessSyncReady,exports.registerComponent=e.registerComponent,exports.renderRichTextToReact=a.renderRichTextToReact,exports.resolveAsset=e.resolveAsset,exports.setComponents=e.setComponents,exports.setFallbackComponent=e.setFallbackComponent,exports.unregisterComponent=e.unregisterComponent,exports.useLocaless=i.useLocaless;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core/state.js"),t=require("./core/utils/link.util.js"),n=require("./core/components/localess-component.js"),r=require("./core/components/localess-document.js"),i=require("./core/hooks/use-localess.js"),a=require("./core/richtext.js");let o=require("@localess/client");exports.LocalessComponent=n.LocalessComponent,exports.LocalessDocument=r.LocalessDocument,exports.findLink=t.findLink,exports.getComponent=e.getComponent,exports.getFallbackComponent=e.getFallbackComponent,exports.getLocalessClient=e.getLocalessClient,exports.getOrigin=e.getOrigin,Object.defineProperty(exports,"isBrowser",{enumerable:!0,get:function(){return o.isBrowser}}),Object.defineProperty(exports,"isIframe",{enumerable:!0,get:function(){return o.isIframe}}),Object.defineProperty(exports,"isServer",{enumerable:!0,get:function(){return o.isServer}}),exports.isSyncEnabled=e.isSyncEnabled,Object.defineProperty(exports,"localessEditable",{enumerable:!0,get:function(){return o.localessEditable}}),Object.defineProperty(exports,"localessEditableField",{enumerable:!0,get:function(){return o.localessEditableField}}),exports.localessInit=e.localessInit,exports.localessSyncOn=e.localessSyncOn,exports.localessSyncOnChange=e.localessSyncOnChange,exports.localessSyncReady=e.localessSyncReady,exports.registerComponent=e.registerComponent,exports.renderRichTextToReact=a.renderRichTextToReact,exports.resolveAsset=e.resolveAsset,exports.setComponents=e.setComponents,exports.setFallbackComponent=e.setFallbackComponent,exports.unregisterComponent=e.unregisterComponent,exports.useLocaless=i.useLocaless;
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { getComponent as e, getFallbackComponent as t, getLocalessClient as n, getOrigin as r, isSyncEnabled as i, localessInit as a, localessSyncReady as o, registerComponent as s, resolveAsset as c, setComponents as l, setFallbackComponent as u, unregisterComponent as d } from "./core/state.mjs";
2
- import { findLink as f } from "./core/utils/link.util.mjs";
3
- import { isBrowser as p, isIframe as m, isServer as h, localessEditable as g, localessEditableField as _ } from "./core/utils/index.mjs";
4
- import { LocalessComponent as v } from "./core/components/localess-component.mjs";
5
- import { LocalessDocument as y } from "./core/components/localess-document.mjs";
6
- import { useLocaless as b } from "./core/hooks/use-localess.mjs";
7
- import { renderRichTextToReact as x } from "./core/richtext.mjs";
8
- export { v as LocalessComponent, y as LocalessDocument, f as findLink, e as getComponent, t as getFallbackComponent, n as getLocalessClient, r as getOrigin, p as isBrowser, m as isIframe, h as isServer, i as isSyncEnabled, g as localessEditable, _ as localessEditableField, a as localessInit, o as localessSyncReady, s as registerComponent, x as renderRichTextToReact, c as resolveAsset, l as setComponents, u as setFallbackComponent, d as unregisterComponent, b as useLocaless };
1
+ import { getComponent as e, getFallbackComponent as t, getLocalessClient as n, getOrigin as r, isSyncEnabled as i, localessInit as a, localessSyncOn as o, localessSyncOnChange as s, localessSyncReady as c, registerComponent as l, resolveAsset as u, setComponents as d, setFallbackComponent as f, unregisterComponent as p } from "./core/state.mjs";
2
+ import { findLink as m } from "./core/utils/link.util.mjs";
3
+ import { isBrowser as h, isIframe as g, isServer as _, localessEditable as v, localessEditableField as y } from "./core/utils/index.mjs";
4
+ import { LocalessComponent as b } from "./core/components/localess-component.mjs";
5
+ import { LocalessDocument as x } from "./core/components/localess-document.mjs";
6
+ import { useLocaless as S } from "./core/hooks/use-localess.mjs";
7
+ import { renderRichTextToReact as C } from "./core/richtext.mjs";
8
+ export { b as LocalessComponent, x as LocalessDocument, m as findLink, e as getComponent, t as getFallbackComponent, n as getLocalessClient, r as getOrigin, h as isBrowser, g as isIframe, _ as isServer, i as isSyncEnabled, v as localessEditable, y as localessEditableField, a as localessInit, o as localessSyncOn, s as localessSyncOnChange, c as localessSyncReady, l as registerComponent, C as renderRichTextToReact, u as resolveAsset, d as setComponents, f as setFallbackComponent, p as unregisterComponent, S as useLocaless };
@@ -25,6 +25,6 @@
25
25
  * NOT compatible with Next.js `output: 'export'` — use `@localess/react/ssr` for static exports.
26
26
  */
27
27
  export * from '../core/components/localess-component';
28
- export { isSyncEnabled, localessSyncReady } from '../core/state';
28
+ export { isSyncEnabled, localessSyncOn, localessSyncOnChange, localessSyncReady } from '../core/state';
29
29
  export * from '../ssr';
30
30
  export * from './localess-document';
package/dist/rsc/index.js CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../core/state.js"),t=require("../core/utils/link.util.js"),n=require("../core/components/localess-component.js"),r=require("../core/richtext.js"),i=require("../ssr/localess-component.js"),a=require("../ssr/localess-document.js"),o=require("./localess-document.js");let s=require("@localess/client");exports.LocalessComponent=n.LocalessComponent,exports.LocalessDocument=o.LocalessDocument,exports.LocalessServerComponent=i.LocalessServerComponent,exports.LocalessServerDocument=a.LocalessServerDocument,exports.findLink=t.findLink,exports.getComponent=e.getComponent,exports.getFallbackComponent=e.getFallbackComponent,exports.getLocalessClient=e.getLocalessClient,Object.defineProperty(exports,"isBrowser",{enumerable:!0,get:function(){return s.isBrowser}}),Object.defineProperty(exports,"isIframe",{enumerable:!0,get:function(){return s.isIframe}}),Object.defineProperty(exports,"isServer",{enumerable:!0,get:function(){return s.isServer}}),exports.isSyncEnabled=e.isSyncEnabled,Object.defineProperty(exports,"localessEditable",{enumerable:!0,get:function(){return s.localessEditable}}),Object.defineProperty(exports,"localessEditableField",{enumerable:!0,get:function(){return s.localessEditableField}}),exports.localessInit=e.localessInit,exports.localessSyncReady=e.localessSyncReady,exports.registerComponent=e.registerComponent,exports.renderRichTextToReact=r.renderRichTextToReact,exports.resolveAsset=e.resolveAsset,exports.unregisterComponent=e.unregisterComponent;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../core/state.js"),t=require("../core/utils/link.util.js"),n=require("../core/components/localess-component.js"),r=require("../core/richtext.js"),i=require("../ssr/localess-component.js"),a=require("../ssr/localess-document.js"),o=require("./localess-document.js");let s=require("@localess/client");exports.LocalessComponent=n.LocalessComponent,exports.LocalessDocument=o.LocalessDocument,exports.LocalessServerComponent=i.LocalessServerComponent,exports.LocalessServerDocument=a.LocalessServerDocument,exports.findLink=t.findLink,exports.getComponent=e.getComponent,exports.getFallbackComponent=e.getFallbackComponent,exports.getLocalessClient=e.getLocalessClient,Object.defineProperty(exports,"isBrowser",{enumerable:!0,get:function(){return s.isBrowser}}),Object.defineProperty(exports,"isIframe",{enumerable:!0,get:function(){return s.isIframe}}),Object.defineProperty(exports,"isServer",{enumerable:!0,get:function(){return s.isServer}}),exports.isSyncEnabled=e.isSyncEnabled,Object.defineProperty(exports,"localessEditable",{enumerable:!0,get:function(){return s.localessEditable}}),Object.defineProperty(exports,"localessEditableField",{enumerable:!0,get:function(){return s.localessEditableField}}),exports.localessInit=e.localessInit,exports.localessSyncOn=e.localessSyncOn,exports.localessSyncOnChange=e.localessSyncOnChange,exports.localessSyncReady=e.localessSyncReady,exports.registerComponent=e.registerComponent,exports.renderRichTextToReact=r.renderRichTextToReact,exports.resolveAsset=e.resolveAsset,exports.unregisterComponent=e.unregisterComponent;
@@ -1,9 +1,9 @@
1
- import { getComponent as e, getFallbackComponent as t, getLocalessClient as n, isSyncEnabled as r, localessInit as i, localessSyncReady as a, registerComponent as o, resolveAsset as s, unregisterComponent as c } from "../core/state.mjs";
2
- import { findLink as l } from "../core/utils/link.util.mjs";
3
- import { isBrowser as u, isIframe as d, isServer as f, localessEditable as p, localessEditableField as m } from "../core/utils/index.mjs";
4
- import { LocalessComponent as h } from "../core/components/localess-component.mjs";
5
- import { renderRichTextToReact as g } from "../core/richtext.mjs";
6
- import { LocalessServerComponent as _ } from "../ssr/localess-component.mjs";
7
- import { LocalessServerDocument as v } from "../ssr/localess-document.mjs";
8
- import { LocalessDocument as y } from "./localess-document.mjs";
9
- export { h as LocalessComponent, y as LocalessDocument, _ as LocalessServerComponent, v as LocalessServerDocument, l as findLink, e as getComponent, t as getFallbackComponent, n as getLocalessClient, u as isBrowser, d as isIframe, f as isServer, r as isSyncEnabled, p as localessEditable, m as localessEditableField, i as localessInit, a as localessSyncReady, o as registerComponent, g as renderRichTextToReact, s as resolveAsset, c as unregisterComponent };
1
+ import { getComponent as e, getFallbackComponent as t, getLocalessClient as n, isSyncEnabled as r, localessInit as i, localessSyncOn as a, localessSyncOnChange as o, localessSyncReady as s, registerComponent as c, resolveAsset as l, unregisterComponent as u } from "../core/state.mjs";
2
+ import { findLink as d } from "../core/utils/link.util.mjs";
3
+ import { isBrowser as f, isIframe as p, isServer as m, localessEditable as h, localessEditableField as g } from "../core/utils/index.mjs";
4
+ import { LocalessComponent as _ } from "../core/components/localess-component.mjs";
5
+ import { renderRichTextToReact as v } from "../core/richtext.mjs";
6
+ import { LocalessServerComponent as y } from "../ssr/localess-component.mjs";
7
+ import { LocalessServerDocument as b } from "../ssr/localess-document.mjs";
8
+ import { LocalessDocument as x } from "./localess-document.mjs";
9
+ export { _ as LocalessComponent, x as LocalessDocument, y as LocalessServerComponent, b as LocalessServerDocument, d as findLink, e as getComponent, t as getFallbackComponent, n as getLocalessClient, f as isBrowser, p as isIframe, m as isServer, r as isSyncEnabled, h as localessEditable, g as localessEditableField, i as localessInit, a as localessSyncOn, o as localessSyncOnChange, s as localessSyncReady, c as registerComponent, v as renderRichTextToReact, l as resolveAsset, u as unregisterComponent };
@@ -1 +1 @@
1
- "use client";let e=require("react"),t=require("@localess/client");var n=n=>(console.info(`LocalessSync:init`),(0,e.useEffect)(()=>{async function e(){n.enableSync&&(0,t.isBrowser)()&&(0,t.isIframe)()&&(await(0,t.loadLocalessSync)(n.origin),window.localess?.on([`input`,`change`],e=>{console.info(`LocalessSync:change:`,e),(e.type===`change`||e.type===`input`)&&(n.document.data=e.data)}))}e()},[]),null);exports.LocalessSync=n;
1
+ "use client";const e=require("../core/state.js");let t=require("react"),n=require("@localess/client");var r=r=>(console.info(`LocalessSync:init`),(0,t.useEffect)(()=>{async function t(){r.enableSync&&(0,n.isBrowser)()&&(0,n.isIframe)()&&(await(0,n.loadLocalessSync)(r.origin),e.localessSyncOnChange(e=>{console.info(`LocalessSync:change:`,e),r.document.data=e.data}))}t()},[]),null);exports.LocalessSync=r;
@@ -1,15 +1,16 @@
1
1
  "use client";
2
- import { isBrowser as e, isIframe as t } from "../core/utils/index.mjs";
3
- import { useEffect as n } from "react";
4
- import { loadLocalessSync as r } from "@localess/client";
2
+ import { localessSyncOnChange as e } from "../core/state.mjs";
3
+ import { isBrowser as t, isIframe as n } from "../core/utils/index.mjs";
4
+ import { useEffect as r } from "react";
5
+ import { loadLocalessSync as i } from "@localess/client";
5
6
  //#region src/rsc/localess-sync.tsx
6
- var i = (i) => (console.info("LocalessSync:init"), n(() => {
7
- async function n() {
8
- i.enableSync && e() && t() && (await r(i.origin), window.localess?.on(["input", "change"], (e) => {
9
- console.info("LocalessSync:change:", e), (e.type === "change" || e.type === "input") && (i.document.data = e.data);
7
+ var a = (a) => (console.info("LocalessSync:init"), r(() => {
8
+ async function r() {
9
+ a.enableSync && t() && n() && (await i(a.origin), e((e) => {
10
+ console.info("LocalessSync:change:", e), a.document.data = e.data;
10
11
  }));
11
12
  }
12
- n();
13
+ r();
13
14
  }, []), null);
14
15
  //#endregion
15
- export { i as LocalessSync };
16
+ export { a as LocalessSync };
@@ -22,8 +22,8 @@
22
22
  * - LocalessDocument (requires 'use client')
23
23
  * - useLocaless (requires 'use client')
24
24
  * - localessEditable / localessEditableField / isBrowser / isIframe (browser-only)
25
- * - isSyncEnabled (not meaningful without live editing)
26
- * - Sync event types (LocalessSync, EventToApp, EventCallback, EventToAppType)
25
+ * - isSyncEnabled / localessSyncOn / localessSyncOnChange (not meaningful without live editing)
26
+ * - Sync event types (LocalessSync, EventToApp, EventToAppOf, EventCallback, EventToAppType)
27
27
  */
28
28
  export type * from '../core/models';
29
29
  export { renderRichTextToReact } from '../core/richtext';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@localess/react",
3
- "version": "3.3.0-dev.20260708090119",
3
+ "version": "3.3.0-dev.20260708124505",
4
4
  "description": "ReactJS JavaScript/TypeScript SDK for Localess's API.",
5
5
  "keywords": [
6
6
  "localess",
@@ -61,7 +61,7 @@
61
61
  "react-dom": "^17 || ^18 || ^19"
62
62
  },
63
63
  "dependencies": {
64
- "@localess/client": "3.3.0-dev.20260708090119",
64
+ "@localess/client": "3.3.0-dev.20260708124505",
65
65
  "@tiptap/extension-bold": "^3.22.5",
66
66
  "@tiptap/extension-bullet-list": "^3.22.5",
67
67
  "@tiptap/extension-code": "^3.22.5",