@lionrapid/core 0.2.0 → 0.3.0-beta.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
@@ -38,7 +38,6 @@ __export(index_exports, {
38
38
  CacheStorageRepository: () => CacheStorageRepository,
39
39
  Colors: () => Colors,
40
40
  ContentValidator: () => ContentValidator,
41
- DefaultStyleMapper: () => DefaultStyleMapper,
42
41
  EventEmitter: () => EventEmitter,
43
42
  HTMLSerializer: () => HTMLSerializer,
44
43
  HandlerChain: () => HandlerChain,
@@ -57,7 +56,9 @@ __export(index_exports, {
57
56
  TranslationLockedError: () => TranslationLockedError,
58
57
  TranslationNotFoundError: () => TranslationNotFoundError,
59
58
  VERSION: () => VERSION,
59
+ VOID_TAGS: () => VOID_TAGS,
60
60
  createPackageLogger: () => createPackageLogger,
61
+ extractTagName: () => extractTagName,
61
62
  isClientError: () => isClientError,
62
63
  isRetryableError: () => isRetryableError,
63
64
  isTranslationError: () => isTranslationError,
@@ -65,7 +66,9 @@ __export(index_exports, {
65
66
  measure: () => measure,
66
67
  parseKeyPath: () => parseKeyPath,
67
68
  prepareTranslationRequest: () => prepareTranslationRequest,
68
- resolveNestedKey: () => resolveNestedKey
69
+ readRawStyle: () => readRawStyle,
70
+ resolveNestedKey: () => resolveNestedKey,
71
+ serializeOpeningTag: () => serializeOpeningTag
69
72
  });
70
73
  module.exports = __toCommonJS(index_exports);
71
74
 
@@ -2225,153 +2228,51 @@ var NetworkHandler = class {
2225
2228
  }
2226
2229
  };
2227
2230
 
2228
- // src/content/safe-href.ts
2229
- var SAFE_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
2230
- var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:", "sms:"]);
2231
- function safeHref(href) {
2232
- if (!href) return "#";
2233
- const trimmed = href.trim();
2234
- if (!trimmed) return "#";
2235
- if (trimmed.startsWith("//")) {
2236
- return "#";
2237
- }
2238
- if (trimmed.startsWith("/") || trimmed.startsWith("?") || trimmed.startsWith("#") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
2239
- return trimmed;
2240
- }
2241
- const schemeMatch = SAFE_SCHEME.exec(trimmed);
2242
- if (schemeMatch) {
2243
- const scheme = schemeMatch[0].toLowerCase();
2244
- return ALLOWED_SCHEMES.has(scheme) ? trimmed : "#";
2245
- }
2246
- return trimmed;
2231
+ // src/content/canonical-tag.ts
2232
+ var VOID_TAGS = /* @__PURE__ */ new Set([
2233
+ "area",
2234
+ "base",
2235
+ "br",
2236
+ "col",
2237
+ "embed",
2238
+ "hr",
2239
+ "img",
2240
+ "input",
2241
+ "link",
2242
+ "meta",
2243
+ "param",
2244
+ "source",
2245
+ "track",
2246
+ "wbr"
2247
+ ]);
2248
+ function escapeAttrValue(value) {
2249
+ return value.replace(/"/g, """);
2247
2250
  }
2248
-
2249
- // src/content/style-mapper/DefaultStyleMapper.ts
2250
- var DefaultStyleMapper = class {
2251
- constructor() {
2252
- /**
2253
- * Element → Style mapping
2254
- * Maps HTML element names to their style property
2255
- */
2256
- __publicField(this, "elementToStyles", {
2257
- b: "bold",
2258
- strong: "bold",
2259
- i: "italic",
2260
- em: "italic",
2261
- u: "underline",
2262
- s: "strikethrough",
2263
- del: "strikethrough",
2264
- strike: "strikethrough",
2265
- code: "code",
2266
- mark: "highlight",
2267
- sub: "subscript",
2268
- sup: "superscript"
2269
- });
2270
- /**
2271
- * Style → Element mapping (reverse)
2272
- * Uses simple, familiar elements for developer convenience
2273
- * Also accepts element names directly (strong, em, etc.)
2274
- */
2275
- __publicField(this, "styleToElement", {
2276
- // Semantic style names → simple elements
2277
- bold: "b",
2278
- italic: "i",
2279
- underline: "u",
2280
- strikethrough: "s",
2281
- code: "code",
2282
- highlight: "mark",
2283
- subscript: "sub",
2284
- superscript: "sup",
2285
- // Element names (for servers that store styles as element names)
2286
- strong: "b",
2287
- b: "b",
2288
- em: "i",
2289
- i: "i",
2290
- u: "u",
2291
- s: "s",
2292
- del: "s",
2293
- strike: "s",
2294
- mark: "mark",
2295
- sub: "sub",
2296
- sup: "sup"
2297
- });
2298
- }
2299
- /**
2300
- * Convert HTML element to style properties
2301
- *
2302
- * @param element - Tag name (e.g., "b", "a", "strong")
2303
- * @param attrs - Element attributes (e.g., { href: "/url" })
2304
- * @returns Array of style strings
2305
- */
2306
- mapElementToStyles(element, attrs) {
2307
- const styles = [];
2308
- const tag = element.toLowerCase();
2309
- const mappedStyle = this.elementToStyles[tag];
2310
- if (mappedStyle) {
2311
- styles.push(mappedStyle);
2312
- }
2313
- if (tag === "a" && attrs.href) {
2314
- styles.push(`hyperlink:${attrs.href}`);
2315
- }
2316
- return styles;
2317
- }
2318
- /**
2319
- * Convert style properties to HTML element
2320
- *
2321
- * Priority order:
2322
- * 1. Hyperlink (becomes wrapper element with href)
2323
- * 2. First recognized style property
2324
- * 3. Fallback to span
2325
- *
2326
- * @param styles - Array of style strings
2327
- * @returns Element info for serialization
2328
- */
2329
- mapStylesToElement(styles) {
2330
- for (const style of styles) {
2331
- if (style.startsWith("hyperlink:")) {
2332
- return {
2333
- element: "a",
2334
- attrs: { href: safeHref(style.slice("hyperlink:".length)) }
2335
- };
2336
- }
2337
- }
2338
- for (const style of styles) {
2339
- const element = this.styleToElement[style];
2340
- if (element) {
2341
- return {
2342
- element,
2343
- attrs: {}
2344
- };
2345
- }
2346
- }
2347
- return { element: "span", attrs: {} };
2251
+ function serializeOpeningTag(tagName, attrs) {
2252
+ const tag = tagName.toLowerCase();
2253
+ let result = `<${tag}`;
2254
+ for (const [name, value] of Object.entries(attrs)) {
2255
+ result += ` ${name.toLowerCase()}="${escapeAttrValue(value)}"`;
2256
+ }
2257
+ result += ">";
2258
+ return result;
2259
+ }
2260
+ function extractTagName(rawOpenTag) {
2261
+ const end = rawOpenTag.search(/[\s>]/);
2262
+ return rawOpenTag.slice(1, end === -1 ? rawOpenTag.length : end).toLowerCase();
2263
+ }
2264
+ function readRawStyle(styles) {
2265
+ for (const s of styles) {
2266
+ if (s.startsWith("raw:")) return s.slice(4);
2348
2267
  }
2349
- };
2268
+ return null;
2269
+ }
2350
2270
 
2351
2271
  // src/content/parser/MarkupParser.ts
2352
2272
  var MarkupParser = class {
2353
- constructor(options = {}) {
2354
- __publicField(this, "styleMapper");
2355
- this.styleMapper = options.styleMapper ?? new DefaultStyleMapper();
2356
- }
2357
- /**
2358
- * Parse HTML string into ContentUnit
2359
- *
2360
- * Handles two input formats:
2361
- * 1. HTML tags: `<b>text</b>` → converts to indexed `<1>text</1>` with styles
2362
- * 2. Already indexed: `<1>text</1>` → passes through as-is (from server)
2363
- *
2364
- * @param input - HTML string with inline elements or already indexed
2365
- * @returns ContentUnit with indexed tags and styles
2366
- */
2367
2273
  parse(input) {
2368
- const hasIndexedTags = /<\/?[0-9]+>/.test(input);
2369
- if (hasIndexedTags) {
2370
- return {
2371
- text: input,
2372
- styles: {}
2373
- // Empty - styles determined by children
2374
- };
2274
+ if (/<\/?[0-9]+>/.test(input)) {
2275
+ return { text: input, styles: {} };
2375
2276
  }
2376
2277
  const styles = {};
2377
2278
  let tagIndex = 0;
@@ -2379,88 +2280,45 @@ var MarkupParser = class {
2379
2280
  let result = "";
2380
2281
  let pos = 0;
2381
2282
  while (pos < input.length) {
2382
- const tagMatch = this.matchTag(input, pos);
2383
- if (!tagMatch) {
2283
+ const tag = this.matchTag(input, pos);
2284
+ if (!tag) {
2384
2285
  result += input.slice(pos);
2385
2286
  break;
2386
2287
  }
2387
- result += input.slice(pos, tagMatch.startIndex);
2388
- if (tagMatch.isClosing) {
2389
- const matchingOpen = this.findMatchingOpenTag(
2390
- tagStack,
2391
- tagMatch.tag.toLowerCase()
2392
- );
2393
- if (matchingOpen !== null) {
2394
- result += `</${matchingOpen}>`;
2395
- const stackIndex = tagStack.findIndex(
2396
- (t) => t.index === matchingOpen
2397
- );
2398
- if (stackIndex !== -1) {
2399
- tagStack.splice(stackIndex, 1);
2288
+ result += input.slice(pos, tag.startIndex);
2289
+ if (tag.isClosing) {
2290
+ let matchIdx = null;
2291
+ for (let i = tagStack.length - 1; i >= 0; i--) {
2292
+ if (tagStack[i].tag === tag.tag.toLowerCase()) {
2293
+ matchIdx = tagStack[i].index;
2294
+ tagStack.splice(i, 1);
2295
+ break;
2400
2296
  }
2401
- } else {
2402
- result += input.slice(tagMatch.startIndex, tagMatch.endIndex);
2403
2297
  }
2404
- } else if (tagMatch.isSelfClosing) {
2405
- tagIndex++;
2406
- const styleList = this.styleMapper.mapElementToStyles(
2407
- tagMatch.tag,
2408
- tagMatch.attrs
2409
- );
2410
- if (styleList.length > 0) {
2411
- styles[String(tagIndex)] = styleList;
2412
- result += `<${tagIndex}/>`;
2298
+ if (matchIdx !== null) {
2299
+ result += `</${matchIdx}>`;
2413
2300
  }
2414
2301
  } else {
2415
- const styleList = this.styleMapper.mapElementToStyles(
2416
- tagMatch.tag,
2417
- tagMatch.attrs
2418
- );
2419
- if (styleList.length > 0) {
2420
- tagIndex++;
2421
- styles[String(tagIndex)] = styleList;
2422
- result += `<${tagIndex}>`;
2423
- tagStack.push({ tag: tagMatch.tag.toLowerCase(), index: tagIndex });
2302
+ const tagName = tag.tag.toLowerCase();
2303
+ const isVoid = VOID_TAGS.has(tagName);
2304
+ const isSelfClosing = tag.isSelfClosing || isVoid;
2305
+ tagIndex++;
2306
+ styles[String(tagIndex)] = [`raw:${serializeOpeningTag(tagName, tag.attrs)}`];
2307
+ if (isSelfClosing) {
2308
+ result += `<${tagIndex}/>`;
2424
2309
  } else {
2425
- const closingIndex = this.findClosingTagIndex(
2426
- input,
2427
- tagMatch.endIndex,
2428
- tagMatch.tag
2429
- );
2430
- if (closingIndex !== -1) {
2431
- const innerContent = input.slice(tagMatch.endIndex, closingIndex);
2432
- const innerUnit = this.parse(innerContent);
2433
- const baseIndex = tagIndex;
2434
- for (const [key, value] of Object.entries(innerUnit.styles)) {
2435
- const adjustedKey = String(baseIndex + parseInt(key, 10));
2436
- styles[adjustedKey] = value;
2437
- }
2438
- result += innerUnit.text.replace(/<(\/?)\d+>/g, (match, slash) => {
2439
- const num = parseInt(match.replace(/[</>]/g, ""), 10);
2440
- return `<${slash}${baseIndex + num}>`;
2441
- });
2442
- const closingMatch = this.matchTag(input, closingIndex);
2443
- pos = closingMatch ? closingMatch.endIndex : closingIndex;
2444
- const maxInnerIndex = Object.keys(innerUnit.styles).length > 0 ? Math.max(...Object.keys(innerUnit.styles).map(Number)) : 0;
2445
- tagIndex = baseIndex + maxInnerIndex;
2446
- continue;
2447
- }
2310
+ result += `<${tagIndex}>`;
2311
+ tagStack.push({ tag: tagName, index: tagIndex });
2448
2312
  }
2449
2313
  }
2450
- pos = tagMatch.endIndex;
2314
+ pos = tag.endIndex;
2451
2315
  }
2452
2316
  while (tagStack.length > 0) {
2453
2317
  const unclosed = tagStack.pop();
2454
2318
  result += `</${unclosed.index}>`;
2455
2319
  }
2456
- return {
2457
- text: result,
2458
- styles
2459
- };
2320
+ return { text: result, styles };
2460
2321
  }
2461
- /**
2462
- * Match an HTML tag at the given position
2463
- */
2464
2322
  matchTag(input, startPos) {
2465
2323
  const tagStart = input.indexOf("<", startPos);
2466
2324
  if (tagStart === -1) return null;
@@ -2483,9 +2341,6 @@ var MarkupParser = class {
2483
2341
  endIndex: tagStart + fullMatch.length
2484
2342
  };
2485
2343
  }
2486
- /**
2487
- * Parse HTML attributes string into key-value pairs
2488
- */
2489
2344
  parseAttributes(attrsString) {
2490
2345
  const attrs = {};
2491
2346
  if (!attrsString.trim()) return attrs;
@@ -2493,60 +2348,14 @@ var MarkupParser = class {
2493
2348
  let match;
2494
2349
  while ((match = attrPattern.exec(attrsString)) !== null) {
2495
2350
  const [, name, doubleQuoted, singleQuoted, unquoted] = match;
2496
- const value = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
2497
- attrs[name.toLowerCase()] = value;
2351
+ attrs[name.toLowerCase()] = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
2498
2352
  }
2499
2353
  return attrs;
2500
2354
  }
2501
- /**
2502
- * Find matching opening tag in the stack
2503
- */
2504
- findMatchingOpenTag(stack, tagName) {
2505
- for (let i = stack.length - 1; i >= 0; i--) {
2506
- if (stack[i].tag === tagName) {
2507
- return stack[i].index;
2508
- }
2509
- }
2510
- return null;
2511
- }
2512
- /**
2513
- * Find the closing tag index for a given opening tag
2514
- */
2515
- findClosingTagIndex(input, startPos, tagName) {
2516
- const lowerTag = tagName.toLowerCase();
2517
- let depth = 1;
2518
- let pos = startPos;
2519
- while (pos < input.length && depth > 0) {
2520
- const tagMatch = this.matchTag(input, pos);
2521
- if (!tagMatch) break;
2522
- if (tagMatch.tag.toLowerCase() === lowerTag) {
2523
- if (tagMatch.isClosing) {
2524
- depth--;
2525
- if (depth === 0) {
2526
- return tagMatch.startIndex;
2527
- }
2528
- } else if (!tagMatch.isSelfClosing) {
2529
- depth++;
2530
- }
2531
- }
2532
- pos = tagMatch.endIndex;
2533
- }
2534
- return -1;
2535
- }
2536
2355
  };
2537
2356
 
2538
2357
  // src/content/serializer/HTMLSerializer.ts
2539
2358
  var HTMLSerializer = class {
2540
- constructor(options = {}) {
2541
- __publicField(this, "styleMapper");
2542
- this.styleMapper = options.styleMapper ?? new DefaultStyleMapper();
2543
- }
2544
- /**
2545
- * Serialize ContentUnit to HTML string
2546
- *
2547
- * @param unit - ContentUnit with indexed tags and styles
2548
- * @returns HTML string with semantic elements
2549
- */
2550
2359
  serialize(unit) {
2551
2360
  const { text, styles } = unit;
2552
2361
  const tagPattern = /<(\/?)?(\d+)(\/)?>/g;
@@ -2555,40 +2364,25 @@ var HTMLSerializer = class {
2555
2364
  let match;
2556
2365
  while ((match = tagPattern.exec(text)) !== null) {
2557
2366
  result += text.slice(lastIndex, match.index);
2558
- const [fullMatch, closingSlash, indexStr, selfClosingSlash] = match;
2559
- const tagIndex = indexStr;
2560
- const styleList = styles[tagIndex] || [];
2561
- if (closingSlash) {
2562
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2563
- result += `</${mapping.element}>`;
2564
- } else if (selfClosingSlash) {
2565
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2566
- const attrs = this.formatAttributes(mapping.attrs);
2567
- result += `<${mapping.element}${attrs} />`;
2367
+ const [fullMatch, closingSlash, indexStr] = match;
2368
+ const styleList = styles[indexStr] ?? [];
2369
+ const rawOpen = readRawStyle(styleList);
2370
+ if (rawOpen) {
2371
+ const tagName = extractTagName(rawOpen);
2372
+ const isVoid = VOID_TAGS.has(tagName);
2373
+ if (closingSlash) {
2374
+ if (!isVoid) result += `</${tagName}>`;
2375
+ } else {
2376
+ result += rawOpen;
2377
+ }
2568
2378
  } else {
2569
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2570
- const attrs = this.formatAttributes(mapping.attrs);
2571
- result += `<${mapping.element}${attrs}>`;
2379
+ result += fullMatch;
2572
2380
  }
2573
2381
  lastIndex = match.index + fullMatch.length;
2574
2382
  }
2575
2383
  result += text.slice(lastIndex);
2576
2384
  return result;
2577
2385
  }
2578
- /**
2579
- * Format attributes object into HTML attribute string
2580
- */
2581
- formatAttributes(attrs) {
2582
- const entries = Object.entries(attrs);
2583
- if (entries.length === 0) return "";
2584
- return " " + entries.map(([key, value]) => `${key}="${this.escapeAttributeValue(value)}"`).join(" ");
2585
- }
2586
- /**
2587
- * Escape special characters in attribute values
2588
- */
2589
- escapeAttributeValue(value) {
2590
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2591
- }
2592
2386
  };
2593
2387
 
2594
2388
  // src/cache/CacheManager.ts
@@ -2822,7 +2616,7 @@ var CacheManager = class {
2822
2616
  };
2823
2617
 
2824
2618
  // src/lionRapid.ts
2825
- var INDEXED_TAG_REGEX = /<\/?[0-9]+>/;
2619
+ var INDEXED_TAG_REGEX = /<\/?[0-9]+\/?>/;
2826
2620
  var HTML_TAG_REGEX = /<[a-z][a-z0-9]*(?:\s+[^>]*)?\/?>/i;
2827
2621
  var LionRapidCore = class {
2828
2622
  constructor(eventEmitter, handlerChain, defaultLocale = "en", fallbackLocale = "en", defaultNamespace) {
@@ -3047,7 +2841,7 @@ var LionRapidCore = class {
3047
2841
  */
3048
2842
  processIndexedValue(value, request) {
3049
2843
  const processedText = this.processTranslationValue(value, request);
3050
- return processedText.replace(/<\/?[0-9]+>/g, "");
2844
+ return processedText.replace(/<\/?[0-9]+\/?>/g, "");
3051
2845
  }
3052
2846
  /**
3053
2847
  * 📦 Get styles for a translation key from namespace cache (fallback)
@@ -3093,7 +2887,7 @@ var LionRapidCore = class {
3093
2887
  try {
3094
2888
  const unit = this.markupParser.parse(value);
3095
2889
  const processedText = this.processTranslationValue(unit.text, request);
3096
- return processedText.replace(/<\/?[0-9]+>/g, "");
2890
+ return processedText.replace(/<\/?[0-9]+\/?>/g, "");
3097
2891
  } catch (error) {
3098
2892
  logger.warn(`ContentUnit processing failed for "${value}":`, error);
3099
2893
  return this.processTranslationValue(value, request);
@@ -3162,7 +2956,7 @@ var LionRapidCore = class {
3162
2956
  * @example
3163
2957
  * ```typescript
3164
2958
  * const unit = i18n.getContentUnit('terms', {}, 'Read our <b>Terms</b>.');
3165
- * // → { text: "Read our <1>Terms</1>.", styles: {"1": ["bold"]} }
2959
+ * // → { text: "Read our <1>Terms</1>.", styles: {"1": ["raw:<b>"]} }
3166
2960
  * ```
3167
2961
  */
3168
2962
  getContentUnit(key, params, defaultValue) {
@@ -3771,7 +3565,7 @@ var _NetworkRepository = class _NetworkRepository {
3771
3565
  logger.debug("API base URL is not set");
3772
3566
  throw new NetworkError("API base URL is not set");
3773
3567
  }
3774
- const url = this.buildUrl("getTranslation", { key });
3568
+ const url = this.withDraftsParam(this.buildUrl("getTranslation", { key }));
3775
3569
  logger.debug(`API GET: ${url}`);
3776
3570
  try {
3777
3571
  const response = await this.makeRequest(url, "GET");
@@ -3807,7 +3601,7 @@ var _NetworkRepository = class _NetworkRepository {
3807
3601
  logger.debug("API base URL is not set");
3808
3602
  throw new NetworkError("API base URL is not set");
3809
3603
  }
3810
- const url = this.buildUrl("getTranslation", { key });
3604
+ const url = this.withDraftsParam(this.buildUrl("getTranslation", { key }));
3811
3605
  logger.debug(`API GET ContentUnit: ${url}`);
3812
3606
  try {
3813
3607
  const response = await this.makeRequest(url, "GET");
@@ -3848,7 +3642,7 @@ var _NetworkRepository = class _NetworkRepository {
3848
3642
  logger.debug("API base URL is not set");
3849
3643
  throw new NetworkError("API base URL is not set");
3850
3644
  }
3851
- const url = this.buildUrl("getNamespace", { locale, namespace });
3645
+ const url = this.withDraftsParam(this.buildUrl("getNamespace", { locale, namespace }));
3852
3646
  logger.debug(`API GET Namespace: ${url}`);
3853
3647
  const namespaceKey = `${locale}:${namespace}`;
3854
3648
  try {
@@ -3962,7 +3756,7 @@ var _NetworkRepository = class _NetworkRepository {
3962
3756
  logger.debug("API base URL is not set");
3963
3757
  throw new NetworkError("API base URL is not set");
3964
3758
  }
3965
- const url = this.buildUrl("getNamespace", { locale, namespace });
3759
+ const url = this.withDraftsParam(this.buildUrl("getNamespace", { locale, namespace }));
3966
3760
  logger.debug(`API GET Namespace ContentUnits: ${url}`);
3967
3761
  const namespaceKey = `${locale}:${namespace}`;
3968
3762
  try {
@@ -4174,6 +3968,26 @@ var _NetworkRepository = class _NetworkRepository {
4174
3968
  }
4175
3969
  return `${baseUrl}${path}`;
4176
3970
  }
3971
+ /**
3972
+ * Check whether the current page is running inside an active visual-editor
3973
+ * session. The editor JS sets `__lr_editor_active=1` as a session cookie
3974
+ * when its JWT is established; we read it here so client-side translation
3975
+ * fetches request draft content too, matching what the edge proxy does
3976
+ * server-side. Outside a browser (Node/SSR) this is always false.
3977
+ */
3978
+ isEditorPreviewSession() {
3979
+ if (typeof document === "undefined") return false;
3980
+ return document.cookie.split("; ").some((c) => c === "__lr_editor_active=1");
3981
+ }
3982
+ /**
3983
+ * Append `include_drafts=true` to a built URL when an editor session is
3984
+ * active. Leaves the URL unchanged for normal visitors so the delivery
3985
+ * gate continues to apply in production.
3986
+ */
3987
+ withDraftsParam(url) {
3988
+ if (!this.isEditorPreviewSession()) return url;
3989
+ return url + (url.includes("?") ? "&" : "?") + "include_drafts=true";
3990
+ }
4177
3991
  /**
4178
3992
  * Make HTTP request with timeout and headers
4179
3993
  */