@lionrapid/core 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/browser/auto-translator.d.ts.map +1 -1
  2. package/dist/browser/auto-translator.js +15 -4
  3. package/dist/browser/auto-translator.js.map +1 -1
  4. package/dist/browser/block-extractor.d.ts.map +1 -1
  5. package/dist/browser/block-extractor.js +6 -0
  6. package/dist/browser/block-extractor.js.map +1 -1
  7. package/dist/browser/editor/editor-api.d.ts +31 -1
  8. package/dist/browser/editor/editor-api.d.ts.map +1 -1
  9. package/dist/browser/editor/editor-api.js +43 -1
  10. package/dist/browser/editor/editor-api.js.map +1 -1
  11. package/dist/browser/editor/editor-init.js +25 -0
  12. package/dist/browser/editor/editor-init.js.map +1 -1
  13. package/dist/browser/editor/editor-styles.d.ts +1 -1
  14. package/dist/browser/editor/editor-styles.d.ts.map +1 -1
  15. package/dist/browser/editor/editor-styles.js +55 -0
  16. package/dist/browser/editor/editor-styles.js.map +1 -1
  17. package/dist/browser/editor/live-preview.d.ts.map +1 -1
  18. package/dist/browser/editor/live-preview.js +20 -3
  19. package/dist/browser/editor/live-preview.js.map +1 -1
  20. package/dist/browser/editor/sidebar.d.ts +20 -1
  21. package/dist/browser/editor/sidebar.d.ts.map +1 -1
  22. package/dist/browser/editor/sidebar.js +147 -9
  23. package/dist/browser/editor/sidebar.js.map +1 -1
  24. package/dist/browser/lionrapid.auto.dev.js +125 -311
  25. package/dist/browser/lionrapid.auto.dev.js.map +3 -3
  26. package/dist/browser/lionrapid.auto.js +125 -311
  27. package/dist/browser/lionrapid.auto.js.map +3 -3
  28. package/dist/browser/lionrapid.auto.min.js +3 -3
  29. package/dist/browser/lionrapid.auto.min.js.map +4 -4
  30. package/dist/browser/lionrapid.editor.js +274 -181
  31. package/dist/browser/lionrapid.editor.js.map +3 -3
  32. package/dist/browser/lionrapid.editor.min.js +58 -3
  33. package/dist/browser/lionrapid.editor.min.js.map +4 -4
  34. package/dist/browser/lionrapid.esm.js +108 -294
  35. package/dist/browser/lionrapid.esm.js.map +3 -3
  36. package/dist/browser/lionrapid.esm.min.js +2 -2
  37. package/dist/browser/lionrapid.esm.min.js.map +4 -4
  38. package/dist/browser/lionrapid.iife.dev.js +108 -294
  39. package/dist/browser/lionrapid.iife.dev.js.map +3 -3
  40. package/dist/browser/lionrapid.iife.js +108 -294
  41. package/dist/browser/lionrapid.iife.js.map +3 -3
  42. package/dist/browser/lionrapid.iife.min.js +2 -2
  43. package/dist/browser/lionrapid.iife.min.js.map +4 -4
  44. package/dist/browser/lionrapid.umd.js +108 -294
  45. package/dist/browser/lionrapid.umd.js.map +3 -3
  46. package/dist/browser/lionrapid.umd.min.js +2 -2
  47. package/dist/browser/lionrapid.umd.min.js.map +4 -4
  48. package/dist/browser/markup-parser.d.ts +9 -5
  49. package/dist/browser/markup-parser.d.ts.map +1 -1
  50. package/dist/browser/markup-parser.js +19 -23
  51. package/dist/browser/markup-parser.js.map +1 -1
  52. package/dist/content/canonical-tag.d.ts +34 -0
  53. package/dist/content/canonical-tag.d.ts.map +1 -0
  54. package/dist/content/canonical-tag.js +57 -0
  55. package/dist/content/canonical-tag.js.map +1 -0
  56. package/dist/content/index.d.ts +1 -4
  57. package/dist/content/index.d.ts.map +1 -1
  58. package/dist/content/index.js +3 -1
  59. package/dist/content/index.js.map +1 -1
  60. package/dist/content/parser/MarkupParser.d.ts +9 -49
  61. package/dist/content/parser/MarkupParser.d.ts.map +1 -1
  62. package/dist/content/parser/MarkupParser.js +37 -157
  63. package/dist/content/parser/MarkupParser.js.map +1 -1
  64. package/dist/content/serializer/HTMLSerializer.d.ts +10 -43
  65. package/dist/content/serializer/HTMLSerializer.d.ts.map +1 -1
  66. package/dist/content/serializer/HTMLSerializer.js +29 -69
  67. package/dist/content/serializer/HTMLSerializer.js.map +1 -1
  68. package/dist/content/types.d.ts +21 -12
  69. package/dist/content/types.d.ts.map +1 -1
  70. package/dist/content/validator/ContentValidator.d.ts +2 -2
  71. package/dist/content/validator/ContentValidator.js +2 -2
  72. package/dist/index.d.ts +2 -3
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +4 -1
  75. package/dist/index.js.map +1 -1
  76. package/dist/lionRapid.d.ts +1 -1
  77. package/dist/lionRapid.js +4 -4
  78. package/dist/lionRapid.js.map +1 -1
  79. package/dist/repositories/network-repository.d.ts +14 -0
  80. package/dist/repositories/network-repository.d.ts.map +1 -1
  81. package/dist/repositories/network-repository.js +26 -4
  82. package/dist/repositories/network-repository.js.map +1 -1
  83. package/package.json +1 -1
  84. package/dist/content/safe-href.d.ts +0 -22
  85. package/dist/content/safe-href.d.ts.map +0 -1
  86. package/dist/content/safe-href.js +0 -48
  87. package/dist/content/safe-href.js.map +0 -1
  88. package/dist/content/style-mapper/DefaultStyleMapper.d.ts +0 -59
  89. package/dist/content/style-mapper/DefaultStyleMapper.d.ts.map +0 -1
  90. package/dist/content/style-mapper/DefaultStyleMapper.js +0 -132
  91. package/dist/content/style-mapper/DefaultStyleMapper.js.map +0 -1
  92. package/dist/content/style-mapper/IStyleMapper.d.ts +0 -50
  93. package/dist/content/style-mapper/IStyleMapper.d.ts.map +0 -1
  94. package/dist/content/style-mapper/IStyleMapper.js +0 -6
  95. package/dist/content/style-mapper/IStyleMapper.js.map +0 -1
@@ -2138,153 +2138,51 @@ var LionRapid = (() => {
2138
2138
  }
2139
2139
  };
2140
2140
 
2141
- // src/content/safe-href.ts
2142
- var SAFE_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
2143
- var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:", "sms:"]);
2144
- function safeHref(href) {
2145
- if (!href) return "#";
2146
- const trimmed = href.trim();
2147
- if (!trimmed) return "#";
2148
- if (trimmed.startsWith("//")) {
2149
- return "#";
2150
- }
2151
- if (trimmed.startsWith("/") || trimmed.startsWith("?") || trimmed.startsWith("#") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
2152
- return trimmed;
2153
- }
2154
- const schemeMatch = SAFE_SCHEME.exec(trimmed);
2155
- if (schemeMatch) {
2156
- const scheme = schemeMatch[0].toLowerCase();
2157
- return ALLOWED_SCHEMES.has(scheme) ? trimmed : "#";
2158
- }
2159
- return trimmed;
2141
+ // src/content/canonical-tag.ts
2142
+ var VOID_TAGS = /* @__PURE__ */ new Set([
2143
+ "area",
2144
+ "base",
2145
+ "br",
2146
+ "col",
2147
+ "embed",
2148
+ "hr",
2149
+ "img",
2150
+ "input",
2151
+ "link",
2152
+ "meta",
2153
+ "param",
2154
+ "source",
2155
+ "track",
2156
+ "wbr"
2157
+ ]);
2158
+ function escapeAttrValue(value) {
2159
+ return value.replace(/"/g, """);
2160
2160
  }
2161
-
2162
- // src/content/style-mapper/DefaultStyleMapper.ts
2163
- var DefaultStyleMapper = class {
2164
- constructor() {
2165
- /**
2166
- * Element → Style mapping
2167
- * Maps HTML element names to their style property
2168
- */
2169
- __publicField(this, "elementToStyles", {
2170
- b: "bold",
2171
- strong: "bold",
2172
- i: "italic",
2173
- em: "italic",
2174
- u: "underline",
2175
- s: "strikethrough",
2176
- del: "strikethrough",
2177
- strike: "strikethrough",
2178
- code: "code",
2179
- mark: "highlight",
2180
- sub: "subscript",
2181
- sup: "superscript"
2182
- });
2183
- /**
2184
- * Style → Element mapping (reverse)
2185
- * Uses simple, familiar elements for developer convenience
2186
- * Also accepts element names directly (strong, em, etc.)
2187
- */
2188
- __publicField(this, "styleToElement", {
2189
- // Semantic style names → simple elements
2190
- bold: "b",
2191
- italic: "i",
2192
- underline: "u",
2193
- strikethrough: "s",
2194
- code: "code",
2195
- highlight: "mark",
2196
- subscript: "sub",
2197
- superscript: "sup",
2198
- // Element names (for servers that store styles as element names)
2199
- strong: "b",
2200
- b: "b",
2201
- em: "i",
2202
- i: "i",
2203
- u: "u",
2204
- s: "s",
2205
- del: "s",
2206
- strike: "s",
2207
- mark: "mark",
2208
- sub: "sub",
2209
- sup: "sup"
2210
- });
2211
- }
2212
- /**
2213
- * Convert HTML element to style properties
2214
- *
2215
- * @param element - Tag name (e.g., "b", "a", "strong")
2216
- * @param attrs - Element attributes (e.g., { href: "/url" })
2217
- * @returns Array of style strings
2218
- */
2219
- mapElementToStyles(element, attrs) {
2220
- const styles = [];
2221
- const tag = element.toLowerCase();
2222
- const mappedStyle = this.elementToStyles[tag];
2223
- if (mappedStyle) {
2224
- styles.push(mappedStyle);
2225
- }
2226
- if (tag === "a" && attrs.href) {
2227
- styles.push(`hyperlink:${attrs.href}`);
2228
- }
2229
- return styles;
2230
- }
2231
- /**
2232
- * Convert style properties to HTML element
2233
- *
2234
- * Priority order:
2235
- * 1. Hyperlink (becomes wrapper element with href)
2236
- * 2. First recognized style property
2237
- * 3. Fallback to span
2238
- *
2239
- * @param styles - Array of style strings
2240
- * @returns Element info for serialization
2241
- */
2242
- mapStylesToElement(styles) {
2243
- for (const style of styles) {
2244
- if (style.startsWith("hyperlink:")) {
2245
- return {
2246
- element: "a",
2247
- attrs: { href: safeHref(style.slice("hyperlink:".length)) }
2248
- };
2249
- }
2250
- }
2251
- for (const style of styles) {
2252
- const element = this.styleToElement[style];
2253
- if (element) {
2254
- return {
2255
- element,
2256
- attrs: {}
2257
- };
2258
- }
2259
- }
2260
- return { element: "span", attrs: {} };
2161
+ function serializeOpeningTag(tagName, attrs) {
2162
+ const tag = tagName.toLowerCase();
2163
+ let result = `<${tag}`;
2164
+ for (const [name, value] of Object.entries(attrs)) {
2165
+ result += ` ${name.toLowerCase()}="${escapeAttrValue(value)}"`;
2166
+ }
2167
+ result += ">";
2168
+ return result;
2169
+ }
2170
+ function extractTagName(rawOpenTag) {
2171
+ const end = rawOpenTag.search(/[\s>]/);
2172
+ return rawOpenTag.slice(1, end === -1 ? rawOpenTag.length : end).toLowerCase();
2173
+ }
2174
+ function readRawStyle(styles) {
2175
+ for (const s of styles) {
2176
+ if (s.startsWith("raw:")) return s.slice(4);
2261
2177
  }
2262
- };
2178
+ return null;
2179
+ }
2263
2180
 
2264
2181
  // src/content/parser/MarkupParser.ts
2265
2182
  var MarkupParser = class {
2266
- constructor(options = {}) {
2267
- __publicField(this, "styleMapper");
2268
- this.styleMapper = options.styleMapper ?? new DefaultStyleMapper();
2269
- }
2270
- /**
2271
- * Parse HTML string into ContentUnit
2272
- *
2273
- * Handles two input formats:
2274
- * 1. HTML tags: `<b>text</b>` → converts to indexed `<1>text</1>` with styles
2275
- * 2. Already indexed: `<1>text</1>` → passes through as-is (from server)
2276
- *
2277
- * @param input - HTML string with inline elements or already indexed
2278
- * @returns ContentUnit with indexed tags and styles
2279
- */
2280
2183
  parse(input) {
2281
- const hasIndexedTags = /<\/?[0-9]+>/.test(input);
2282
- if (hasIndexedTags) {
2283
- return {
2284
- text: input,
2285
- styles: {}
2286
- // Empty - styles determined by children
2287
- };
2184
+ if (/<\/?[0-9]+>/.test(input)) {
2185
+ return { text: input, styles: {} };
2288
2186
  }
2289
2187
  const styles = {};
2290
2188
  let tagIndex = 0;
@@ -2292,88 +2190,45 @@ var LionRapid = (() => {
2292
2190
  let result = "";
2293
2191
  let pos = 0;
2294
2192
  while (pos < input.length) {
2295
- const tagMatch = this.matchTag(input, pos);
2296
- if (!tagMatch) {
2193
+ const tag = this.matchTag(input, pos);
2194
+ if (!tag) {
2297
2195
  result += input.slice(pos);
2298
2196
  break;
2299
2197
  }
2300
- result += input.slice(pos, tagMatch.startIndex);
2301
- if (tagMatch.isClosing) {
2302
- const matchingOpen = this.findMatchingOpenTag(
2303
- tagStack,
2304
- tagMatch.tag.toLowerCase()
2305
- );
2306
- if (matchingOpen !== null) {
2307
- result += `</${matchingOpen}>`;
2308
- const stackIndex = tagStack.findIndex(
2309
- (t) => t.index === matchingOpen
2310
- );
2311
- if (stackIndex !== -1) {
2312
- tagStack.splice(stackIndex, 1);
2198
+ result += input.slice(pos, tag.startIndex);
2199
+ if (tag.isClosing) {
2200
+ let matchIdx = null;
2201
+ for (let i = tagStack.length - 1; i >= 0; i--) {
2202
+ if (tagStack[i].tag === tag.tag.toLowerCase()) {
2203
+ matchIdx = tagStack[i].index;
2204
+ tagStack.splice(i, 1);
2205
+ break;
2313
2206
  }
2314
- } else {
2315
- result += input.slice(tagMatch.startIndex, tagMatch.endIndex);
2316
2207
  }
2317
- } else if (tagMatch.isSelfClosing) {
2318
- tagIndex++;
2319
- const styleList = this.styleMapper.mapElementToStyles(
2320
- tagMatch.tag,
2321
- tagMatch.attrs
2322
- );
2323
- if (styleList.length > 0) {
2324
- styles[String(tagIndex)] = styleList;
2325
- result += `<${tagIndex}/>`;
2208
+ if (matchIdx !== null) {
2209
+ result += `</${matchIdx}>`;
2326
2210
  }
2327
2211
  } else {
2328
- const styleList = this.styleMapper.mapElementToStyles(
2329
- tagMatch.tag,
2330
- tagMatch.attrs
2331
- );
2332
- if (styleList.length > 0) {
2333
- tagIndex++;
2334
- styles[String(tagIndex)] = styleList;
2335
- result += `<${tagIndex}>`;
2336
- tagStack.push({ tag: tagMatch.tag.toLowerCase(), index: tagIndex });
2212
+ const tagName = tag.tag.toLowerCase();
2213
+ const isVoid = VOID_TAGS.has(tagName);
2214
+ const isSelfClosing = tag.isSelfClosing || isVoid;
2215
+ tagIndex++;
2216
+ styles[String(tagIndex)] = [`raw:${serializeOpeningTag(tagName, tag.attrs)}`];
2217
+ if (isSelfClosing) {
2218
+ result += `<${tagIndex}/>`;
2337
2219
  } else {
2338
- const closingIndex = this.findClosingTagIndex(
2339
- input,
2340
- tagMatch.endIndex,
2341
- tagMatch.tag
2342
- );
2343
- if (closingIndex !== -1) {
2344
- const innerContent = input.slice(tagMatch.endIndex, closingIndex);
2345
- const innerUnit = this.parse(innerContent);
2346
- const baseIndex = tagIndex;
2347
- for (const [key, value] of Object.entries(innerUnit.styles)) {
2348
- const adjustedKey = String(baseIndex + parseInt(key, 10));
2349
- styles[adjustedKey] = value;
2350
- }
2351
- result += innerUnit.text.replace(/<(\/?)\d+>/g, (match, slash) => {
2352
- const num = parseInt(match.replace(/[</>]/g, ""), 10);
2353
- return `<${slash}${baseIndex + num}>`;
2354
- });
2355
- const closingMatch = this.matchTag(input, closingIndex);
2356
- pos = closingMatch ? closingMatch.endIndex : closingIndex;
2357
- const maxInnerIndex = Object.keys(innerUnit.styles).length > 0 ? Math.max(...Object.keys(innerUnit.styles).map(Number)) : 0;
2358
- tagIndex = baseIndex + maxInnerIndex;
2359
- continue;
2360
- }
2220
+ result += `<${tagIndex}>`;
2221
+ tagStack.push({ tag: tagName, index: tagIndex });
2361
2222
  }
2362
2223
  }
2363
- pos = tagMatch.endIndex;
2224
+ pos = tag.endIndex;
2364
2225
  }
2365
2226
  while (tagStack.length > 0) {
2366
2227
  const unclosed = tagStack.pop();
2367
2228
  result += `</${unclosed.index}>`;
2368
2229
  }
2369
- return {
2370
- text: result,
2371
- styles
2372
- };
2230
+ return { text: result, styles };
2373
2231
  }
2374
- /**
2375
- * Match an HTML tag at the given position
2376
- */
2377
2232
  matchTag(input, startPos) {
2378
2233
  const tagStart = input.indexOf("<", startPos);
2379
2234
  if (tagStart === -1) return null;
@@ -2396,9 +2251,6 @@ var LionRapid = (() => {
2396
2251
  endIndex: tagStart + fullMatch.length
2397
2252
  };
2398
2253
  }
2399
- /**
2400
- * Parse HTML attributes string into key-value pairs
2401
- */
2402
2254
  parseAttributes(attrsString) {
2403
2255
  const attrs = {};
2404
2256
  if (!attrsString.trim()) return attrs;
@@ -2406,60 +2258,14 @@ var LionRapid = (() => {
2406
2258
  let match;
2407
2259
  while ((match = attrPattern.exec(attrsString)) !== null) {
2408
2260
  const [, name, doubleQuoted, singleQuoted, unquoted] = match;
2409
- const value = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
2410
- attrs[name.toLowerCase()] = value;
2261
+ attrs[name.toLowerCase()] = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
2411
2262
  }
2412
2263
  return attrs;
2413
2264
  }
2414
- /**
2415
- * Find matching opening tag in the stack
2416
- */
2417
- findMatchingOpenTag(stack, tagName) {
2418
- for (let i = stack.length - 1; i >= 0; i--) {
2419
- if (stack[i].tag === tagName) {
2420
- return stack[i].index;
2421
- }
2422
- }
2423
- return null;
2424
- }
2425
- /**
2426
- * Find the closing tag index for a given opening tag
2427
- */
2428
- findClosingTagIndex(input, startPos, tagName) {
2429
- const lowerTag = tagName.toLowerCase();
2430
- let depth = 1;
2431
- let pos = startPos;
2432
- while (pos < input.length && depth > 0) {
2433
- const tagMatch = this.matchTag(input, pos);
2434
- if (!tagMatch) break;
2435
- if (tagMatch.tag.toLowerCase() === lowerTag) {
2436
- if (tagMatch.isClosing) {
2437
- depth--;
2438
- if (depth === 0) {
2439
- return tagMatch.startIndex;
2440
- }
2441
- } else if (!tagMatch.isSelfClosing) {
2442
- depth++;
2443
- }
2444
- }
2445
- pos = tagMatch.endIndex;
2446
- }
2447
- return -1;
2448
- }
2449
2265
  };
2450
2266
 
2451
2267
  // src/content/serializer/HTMLSerializer.ts
2452
2268
  var HTMLSerializer = class {
2453
- constructor(options = {}) {
2454
- __publicField(this, "styleMapper");
2455
- this.styleMapper = options.styleMapper ?? new DefaultStyleMapper();
2456
- }
2457
- /**
2458
- * Serialize ContentUnit to HTML string
2459
- *
2460
- * @param unit - ContentUnit with indexed tags and styles
2461
- * @returns HTML string with semantic elements
2462
- */
2463
2269
  serialize(unit) {
2464
2270
  const { text, styles } = unit;
2465
2271
  const tagPattern = /<(\/?)?(\d+)(\/)?>/g;
@@ -2468,40 +2274,25 @@ var LionRapid = (() => {
2468
2274
  let match;
2469
2275
  while ((match = tagPattern.exec(text)) !== null) {
2470
2276
  result += text.slice(lastIndex, match.index);
2471
- const [fullMatch, closingSlash, indexStr, selfClosingSlash] = match;
2472
- const tagIndex = indexStr;
2473
- const styleList = styles[tagIndex] || [];
2474
- if (closingSlash) {
2475
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2476
- result += `</${mapping.element}>`;
2477
- } else if (selfClosingSlash) {
2478
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2479
- const attrs = this.formatAttributes(mapping.attrs);
2480
- result += `<${mapping.element}${attrs} />`;
2277
+ const [fullMatch, closingSlash, indexStr] = match;
2278
+ const styleList = styles[indexStr] ?? [];
2279
+ const rawOpen = readRawStyle(styleList);
2280
+ if (rawOpen) {
2281
+ const tagName = extractTagName(rawOpen);
2282
+ const isVoid = VOID_TAGS.has(tagName);
2283
+ if (closingSlash) {
2284
+ if (!isVoid) result += `</${tagName}>`;
2285
+ } else {
2286
+ result += rawOpen;
2287
+ }
2481
2288
  } else {
2482
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2483
- const attrs = this.formatAttributes(mapping.attrs);
2484
- result += `<${mapping.element}${attrs}>`;
2289
+ result += fullMatch;
2485
2290
  }
2486
2291
  lastIndex = match.index + fullMatch.length;
2487
2292
  }
2488
2293
  result += text.slice(lastIndex);
2489
2294
  return result;
2490
2295
  }
2491
- /**
2492
- * Format attributes object into HTML attribute string
2493
- */
2494
- formatAttributes(attrs) {
2495
- const entries = Object.entries(attrs);
2496
- if (entries.length === 0) return "";
2497
- return " " + entries.map(([key, value]) => `${key}="${this.escapeAttributeValue(value)}"`).join(" ");
2498
- }
2499
- /**
2500
- * Escape special characters in attribute values
2501
- */
2502
- escapeAttributeValue(value) {
2503
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2504
- }
2505
2296
  };
2506
2297
 
2507
2298
  // src/cache/CacheManager.ts
@@ -2735,7 +2526,7 @@ var LionRapid = (() => {
2735
2526
  };
2736
2527
 
2737
2528
  // src/lionRapid.ts
2738
- var INDEXED_TAG_REGEX = /<\/?[0-9]+>/;
2529
+ var INDEXED_TAG_REGEX = /<\/?[0-9]+\/?>/;
2739
2530
  var HTML_TAG_REGEX = /<[a-z][a-z0-9]*(?:\s+[^>]*)?\/?>/i;
2740
2531
  var LionRapidCore = class {
2741
2532
  constructor(eventEmitter, handlerChain, defaultLocale = "en", fallbackLocale = "en", defaultNamespace) {
@@ -2960,7 +2751,7 @@ var LionRapid = (() => {
2960
2751
  */
2961
2752
  processIndexedValue(value, request) {
2962
2753
  const processedText = this.processTranslationValue(value, request);
2963
- return processedText.replace(/<\/?[0-9]+>/g, "");
2754
+ return processedText.replace(/<\/?[0-9]+\/?>/g, "");
2964
2755
  }
2965
2756
  /**
2966
2757
  * 📦 Get styles for a translation key from namespace cache (fallback)
@@ -3006,7 +2797,7 @@ var LionRapid = (() => {
3006
2797
  try {
3007
2798
  const unit = this.markupParser.parse(value);
3008
2799
  const processedText = this.processTranslationValue(unit.text, request);
3009
- return processedText.replace(/<\/?[0-9]+>/g, "");
2800
+ return processedText.replace(/<\/?[0-9]+\/?>/g, "");
3010
2801
  } catch (error) {
3011
2802
  logger.warn(`ContentUnit processing failed for "${value}":`, error);
3012
2803
  return this.processTranslationValue(value, request);
@@ -3075,7 +2866,7 @@ var LionRapid = (() => {
3075
2866
  * @example
3076
2867
  * ```typescript
3077
2868
  * const unit = i18n.getContentUnit('terms', {}, 'Read our <b>Terms</b>.');
3078
- * // → { text: "Read our <1>Terms</1>.", styles: {"1": ["bold"]} }
2869
+ * // → { text: "Read our <1>Terms</1>.", styles: {"1": ["raw:<b>"]} }
3079
2870
  * ```
3080
2871
  */
3081
2872
  getContentUnit(key, params, defaultValue) {
@@ -3684,7 +3475,7 @@ var LionRapid = (() => {
3684
3475
  logger.debug("API base URL is not set");
3685
3476
  throw new NetworkError("API base URL is not set");
3686
3477
  }
3687
- const url = this.buildUrl("getTranslation", { key });
3478
+ const url = this.withDraftsParam(this.buildUrl("getTranslation", { key }));
3688
3479
  logger.debug(`API GET: ${url}`);
3689
3480
  try {
3690
3481
  const response = await this.makeRequest(url, "GET");
@@ -3720,7 +3511,7 @@ var LionRapid = (() => {
3720
3511
  logger.debug("API base URL is not set");
3721
3512
  throw new NetworkError("API base URL is not set");
3722
3513
  }
3723
- const url = this.buildUrl("getTranslation", { key });
3514
+ const url = this.withDraftsParam(this.buildUrl("getTranslation", { key }));
3724
3515
  logger.debug(`API GET ContentUnit: ${url}`);
3725
3516
  try {
3726
3517
  const response = await this.makeRequest(url, "GET");
@@ -3761,7 +3552,7 @@ var LionRapid = (() => {
3761
3552
  logger.debug("API base URL is not set");
3762
3553
  throw new NetworkError("API base URL is not set");
3763
3554
  }
3764
- const url = this.buildUrl("getNamespace", { locale, namespace });
3555
+ const url = this.withDraftsParam(this.buildUrl("getNamespace", { locale, namespace }));
3765
3556
  logger.debug(`API GET Namespace: ${url}`);
3766
3557
  const namespaceKey = `${locale}:${namespace}`;
3767
3558
  try {
@@ -3875,7 +3666,7 @@ var LionRapid = (() => {
3875
3666
  logger.debug("API base URL is not set");
3876
3667
  throw new NetworkError("API base URL is not set");
3877
3668
  }
3878
- const url = this.buildUrl("getNamespace", { locale, namespace });
3669
+ const url = this.withDraftsParam(this.buildUrl("getNamespace", { locale, namespace }));
3879
3670
  logger.debug(`API GET Namespace ContentUnits: ${url}`);
3880
3671
  const namespaceKey = `${locale}:${namespace}`;
3881
3672
  try {
@@ -4087,6 +3878,26 @@ var LionRapid = (() => {
4087
3878
  }
4088
3879
  return `${baseUrl}${path}`;
4089
3880
  }
3881
+ /**
3882
+ * Check whether the current page is running inside an active visual-editor
3883
+ * session. The editor JS sets `__lr_editor_active=1` as a session cookie
3884
+ * when its JWT is established; we read it here so client-side translation
3885
+ * fetches request draft content too, matching what the edge proxy does
3886
+ * server-side. Outside a browser (Node/SSR) this is always false.
3887
+ */
3888
+ isEditorPreviewSession() {
3889
+ if (typeof document === "undefined") return false;
3890
+ return document.cookie.split("; ").some((c) => c === "__lr_editor_active=1");
3891
+ }
3892
+ /**
3893
+ * Append `include_drafts=true` to a built URL when an editor session is
3894
+ * active. Leaves the URL unchanged for normal visitors so the delivery
3895
+ * gate continues to apply in production.
3896
+ */
3897
+ withDraftsParam(url) {
3898
+ if (!this.isEditorPreviewSession()) return url;
3899
+ return url + (url.includes("?") ? "&" : "?") + "include_drafts=true";
3900
+ }
4090
3901
  /**
4091
3902
  * Make HTTP request with timeout and headers
4092
3903
  */
@@ -4262,7 +4073,6 @@ var LionRapid = (() => {
4262
4073
  };
4263
4074
 
4264
4075
  // src/browser/markup-parser.ts
4265
- var styleMapper = new DefaultStyleMapper();
4266
4076
  function parseBlockElement(element) {
4267
4077
  const styles = {};
4268
4078
  let tagIndex = 0;
@@ -4280,22 +4090,17 @@ var LionRapid = (() => {
4280
4090
  const attr = el.attributes[i];
4281
4091
  attrs[attr.name.toLowerCase()] = attr.value;
4282
4092
  }
4283
- const styleList = styleMapper.mapElementToStyles(tag, attrs);
4284
- if (styleList.length > 0) {
4285
- tagIndex++;
4286
- const idx = tagIndex;
4287
- styles[String(idx)] = styleList;
4288
- let inner = "";
4289
- for (let i = 0; i < node.childNodes.length; i++) {
4290
- inner += walk(node.childNodes[i]);
4291
- }
4292
- return `<${idx}>${inner}</${idx}>`;
4093
+ tagIndex++;
4094
+ const idx = tagIndex;
4095
+ styles[String(idx)] = [`raw:${serializeOpeningTag(tag, attrs)}`];
4096
+ if (VOID_TAGS.has(tag)) {
4097
+ return `<${idx}/>`;
4293
4098
  }
4294
- let result = "";
4099
+ let inner = "";
4295
4100
  for (let i = 0; i < node.childNodes.length; i++) {
4296
- result += walk(node.childNodes[i]);
4101
+ inner += walk(node.childNodes[i]);
4297
4102
  }
4298
- return result;
4103
+ return `<${idx}>${inner}</${idx}>`;
4299
4104
  }
4300
4105
  let text = "";
4301
4106
  for (let i = 0; i < element.childNodes.length; i++) {
@@ -4450,8 +4255,14 @@ var LionRapid = (() => {
4450
4255
  // Server already translated
4451
4256
  "data-lionrapid-client",
4452
4257
  // Client already translated
4453
- "data-no-translate"
4258
+ "data-no-translate",
4454
4259
  // Explicitly marked to skip
4260
+ // Edge proxy already rendered this block with the correct translation
4261
+ // and styles. Skip in normal extraction so the client-side auto-translator
4262
+ // can't overwrite the styled DOM with a stale (or plain-text) cache entry.
4263
+ // The editor passes includeTranslated=true to bypass this and re-extract
4264
+ // edge-rendered blocks for inspection.
4265
+ "data-lr-block"
4455
4266
  ];
4456
4267
  var BlockExtractor = class {
4457
4268
  constructor(excludePatterns = {}, excludeSelectors = [], minTextLength = 2) {
@@ -4792,11 +4603,14 @@ var LionRapid = (() => {
4792
4603
  if (!element.hasAttribute("data-lr-original")) {
4793
4604
  element.setAttribute("data-lr-original", element.innerHTML);
4794
4605
  }
4795
- if (cached.styles && Object.keys(cached.styles).length > 0) {
4796
- const translatedUnit = { text: cached.value, styles: cached.styles };
4797
- element.innerHTML = htmlSerializer.serialize(translatedUnit);
4606
+ const cacheHasStyles = cached.styles && Object.keys(cached.styles).length > 0;
4607
+ const sourceHasStyles = Object.keys(unit.styles).length > 0;
4608
+ const hasIndexedMarkers = /<\/?[0-9]+\/?>/.test(cached.value);
4609
+ if (sourceHasStyles && !hasIndexedMarkers) {
4798
4610
  } else {
4799
- element.textContent = cached.value;
4611
+ const styles = cacheHasStyles ? cached.styles : unit.styles;
4612
+ const translatedUnit = { text: cached.value, styles };
4613
+ element.innerHTML = htmlSerializer.serialize(translatedUnit);
4800
4614
  }
4801
4615
  element.setAttribute("data-lr-block", key);
4802
4616
  element.setAttribute("data-lionrapid-client", "1");