@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
@@ -27,7 +27,6 @@ var LionRapid = (() => {
27
27
  CacheStorageRepository: () => CacheStorageRepository,
28
28
  Colors: () => Colors,
29
29
  ContentValidator: () => ContentValidator,
30
- DefaultStyleMapper: () => DefaultStyleMapper,
31
30
  EventEmitter: () => EventEmitter,
32
31
  HTMLSerializer: () => HTMLSerializer,
33
32
  HandlerChain: () => HandlerChain,
@@ -46,7 +45,9 @@ var LionRapid = (() => {
46
45
  TranslationLockedError: () => TranslationLockedError,
47
46
  TranslationNotFoundError: () => TranslationNotFoundError,
48
47
  VERSION: () => VERSION,
48
+ VOID_TAGS: () => VOID_TAGS,
49
49
  createPackageLogger: () => createPackageLogger,
50
+ extractTagName: () => extractTagName,
50
51
  isClientError: () => isClientError,
51
52
  isRetryableError: () => isRetryableError,
52
53
  isTranslationError: () => isTranslationError,
@@ -54,7 +55,9 @@ var LionRapid = (() => {
54
55
  measure: () => measure,
55
56
  parseKeyPath: () => parseKeyPath,
56
57
  prepareTranslationRequest: () => prepareTranslationRequest,
57
- resolveNestedKey: () => resolveNestedKey
58
+ readRawStyle: () => readRawStyle,
59
+ resolveNestedKey: () => resolveNestedKey,
60
+ serializeOpeningTag: () => serializeOpeningTag
58
61
  });
59
62
 
60
63
  // src/config/logger.config.ts
@@ -2213,153 +2216,51 @@ var LionRapid = (() => {
2213
2216
  }
2214
2217
  };
2215
2218
 
2216
- // src/content/safe-href.ts
2217
- var SAFE_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
2218
- var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:", "sms:"]);
2219
- function safeHref(href) {
2220
- if (!href) return "#";
2221
- const trimmed = href.trim();
2222
- if (!trimmed) return "#";
2223
- if (trimmed.startsWith("//")) {
2224
- return "#";
2225
- }
2226
- if (trimmed.startsWith("/") || trimmed.startsWith("?") || trimmed.startsWith("#") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
2227
- return trimmed;
2228
- }
2229
- const schemeMatch = SAFE_SCHEME.exec(trimmed);
2230
- if (schemeMatch) {
2231
- const scheme = schemeMatch[0].toLowerCase();
2232
- return ALLOWED_SCHEMES.has(scheme) ? trimmed : "#";
2233
- }
2234
- return trimmed;
2219
+ // src/content/canonical-tag.ts
2220
+ var VOID_TAGS = /* @__PURE__ */ new Set([
2221
+ "area",
2222
+ "base",
2223
+ "br",
2224
+ "col",
2225
+ "embed",
2226
+ "hr",
2227
+ "img",
2228
+ "input",
2229
+ "link",
2230
+ "meta",
2231
+ "param",
2232
+ "source",
2233
+ "track",
2234
+ "wbr"
2235
+ ]);
2236
+ function escapeAttrValue(value) {
2237
+ return value.replace(/"/g, """);
2235
2238
  }
2236
-
2237
- // src/content/style-mapper/DefaultStyleMapper.ts
2238
- var DefaultStyleMapper = class {
2239
- constructor() {
2240
- /**
2241
- * Element → Style mapping
2242
- * Maps HTML element names to their style property
2243
- */
2244
- __publicField(this, "elementToStyles", {
2245
- b: "bold",
2246
- strong: "bold",
2247
- i: "italic",
2248
- em: "italic",
2249
- u: "underline",
2250
- s: "strikethrough",
2251
- del: "strikethrough",
2252
- strike: "strikethrough",
2253
- code: "code",
2254
- mark: "highlight",
2255
- sub: "subscript",
2256
- sup: "superscript"
2257
- });
2258
- /**
2259
- * Style → Element mapping (reverse)
2260
- * Uses simple, familiar elements for developer convenience
2261
- * Also accepts element names directly (strong, em, etc.)
2262
- */
2263
- __publicField(this, "styleToElement", {
2264
- // Semantic style names → simple elements
2265
- bold: "b",
2266
- italic: "i",
2267
- underline: "u",
2268
- strikethrough: "s",
2269
- code: "code",
2270
- highlight: "mark",
2271
- subscript: "sub",
2272
- superscript: "sup",
2273
- // Element names (for servers that store styles as element names)
2274
- strong: "b",
2275
- b: "b",
2276
- em: "i",
2277
- i: "i",
2278
- u: "u",
2279
- s: "s",
2280
- del: "s",
2281
- strike: "s",
2282
- mark: "mark",
2283
- sub: "sub",
2284
- sup: "sup"
2285
- });
2286
- }
2287
- /**
2288
- * Convert HTML element to style properties
2289
- *
2290
- * @param element - Tag name (e.g., "b", "a", "strong")
2291
- * @param attrs - Element attributes (e.g., { href: "/url" })
2292
- * @returns Array of style strings
2293
- */
2294
- mapElementToStyles(element, attrs) {
2295
- const styles = [];
2296
- const tag = element.toLowerCase();
2297
- const mappedStyle = this.elementToStyles[tag];
2298
- if (mappedStyle) {
2299
- styles.push(mappedStyle);
2300
- }
2301
- if (tag === "a" && attrs.href) {
2302
- styles.push(`hyperlink:${attrs.href}`);
2303
- }
2304
- return styles;
2305
- }
2306
- /**
2307
- * Convert style properties to HTML element
2308
- *
2309
- * Priority order:
2310
- * 1. Hyperlink (becomes wrapper element with href)
2311
- * 2. First recognized style property
2312
- * 3. Fallback to span
2313
- *
2314
- * @param styles - Array of style strings
2315
- * @returns Element info for serialization
2316
- */
2317
- mapStylesToElement(styles) {
2318
- for (const style of styles) {
2319
- if (style.startsWith("hyperlink:")) {
2320
- return {
2321
- element: "a",
2322
- attrs: { href: safeHref(style.slice("hyperlink:".length)) }
2323
- };
2324
- }
2325
- }
2326
- for (const style of styles) {
2327
- const element = this.styleToElement[style];
2328
- if (element) {
2329
- return {
2330
- element,
2331
- attrs: {}
2332
- };
2333
- }
2334
- }
2335
- return { element: "span", attrs: {} };
2239
+ function serializeOpeningTag(tagName, attrs) {
2240
+ const tag = tagName.toLowerCase();
2241
+ let result = `<${tag}`;
2242
+ for (const [name, value] of Object.entries(attrs)) {
2243
+ result += ` ${name.toLowerCase()}="${escapeAttrValue(value)}"`;
2244
+ }
2245
+ result += ">";
2246
+ return result;
2247
+ }
2248
+ function extractTagName(rawOpenTag) {
2249
+ const end = rawOpenTag.search(/[\s>]/);
2250
+ return rawOpenTag.slice(1, end === -1 ? rawOpenTag.length : end).toLowerCase();
2251
+ }
2252
+ function readRawStyle(styles) {
2253
+ for (const s of styles) {
2254
+ if (s.startsWith("raw:")) return s.slice(4);
2336
2255
  }
2337
- };
2256
+ return null;
2257
+ }
2338
2258
 
2339
2259
  // src/content/parser/MarkupParser.ts
2340
2260
  var MarkupParser = class {
2341
- constructor(options = {}) {
2342
- __publicField(this, "styleMapper");
2343
- this.styleMapper = options.styleMapper ?? new DefaultStyleMapper();
2344
- }
2345
- /**
2346
- * Parse HTML string into ContentUnit
2347
- *
2348
- * Handles two input formats:
2349
- * 1. HTML tags: `<b>text</b>` → converts to indexed `<1>text</1>` with styles
2350
- * 2. Already indexed: `<1>text</1>` → passes through as-is (from server)
2351
- *
2352
- * @param input - HTML string with inline elements or already indexed
2353
- * @returns ContentUnit with indexed tags and styles
2354
- */
2355
2261
  parse(input) {
2356
- const hasIndexedTags = /<\/?[0-9]+>/.test(input);
2357
- if (hasIndexedTags) {
2358
- return {
2359
- text: input,
2360
- styles: {}
2361
- // Empty - styles determined by children
2362
- };
2262
+ if (/<\/?[0-9]+>/.test(input)) {
2263
+ return { text: input, styles: {} };
2363
2264
  }
2364
2265
  const styles = {};
2365
2266
  let tagIndex = 0;
@@ -2367,88 +2268,45 @@ var LionRapid = (() => {
2367
2268
  let result = "";
2368
2269
  let pos = 0;
2369
2270
  while (pos < input.length) {
2370
- const tagMatch = this.matchTag(input, pos);
2371
- if (!tagMatch) {
2271
+ const tag = this.matchTag(input, pos);
2272
+ if (!tag) {
2372
2273
  result += input.slice(pos);
2373
2274
  break;
2374
2275
  }
2375
- result += input.slice(pos, tagMatch.startIndex);
2376
- if (tagMatch.isClosing) {
2377
- const matchingOpen = this.findMatchingOpenTag(
2378
- tagStack,
2379
- tagMatch.tag.toLowerCase()
2380
- );
2381
- if (matchingOpen !== null) {
2382
- result += `</${matchingOpen}>`;
2383
- const stackIndex = tagStack.findIndex(
2384
- (t) => t.index === matchingOpen
2385
- );
2386
- if (stackIndex !== -1) {
2387
- tagStack.splice(stackIndex, 1);
2276
+ result += input.slice(pos, tag.startIndex);
2277
+ if (tag.isClosing) {
2278
+ let matchIdx = null;
2279
+ for (let i = tagStack.length - 1; i >= 0; i--) {
2280
+ if (tagStack[i].tag === tag.tag.toLowerCase()) {
2281
+ matchIdx = tagStack[i].index;
2282
+ tagStack.splice(i, 1);
2283
+ break;
2388
2284
  }
2389
- } else {
2390
- result += input.slice(tagMatch.startIndex, tagMatch.endIndex);
2391
2285
  }
2392
- } else if (tagMatch.isSelfClosing) {
2393
- tagIndex++;
2394
- const styleList = this.styleMapper.mapElementToStyles(
2395
- tagMatch.tag,
2396
- tagMatch.attrs
2397
- );
2398
- if (styleList.length > 0) {
2399
- styles[String(tagIndex)] = styleList;
2400
- result += `<${tagIndex}/>`;
2286
+ if (matchIdx !== null) {
2287
+ result += `</${matchIdx}>`;
2401
2288
  }
2402
2289
  } else {
2403
- const styleList = this.styleMapper.mapElementToStyles(
2404
- tagMatch.tag,
2405
- tagMatch.attrs
2406
- );
2407
- if (styleList.length > 0) {
2408
- tagIndex++;
2409
- styles[String(tagIndex)] = styleList;
2410
- result += `<${tagIndex}>`;
2411
- tagStack.push({ tag: tagMatch.tag.toLowerCase(), index: tagIndex });
2290
+ const tagName = tag.tag.toLowerCase();
2291
+ const isVoid = VOID_TAGS.has(tagName);
2292
+ const isSelfClosing = tag.isSelfClosing || isVoid;
2293
+ tagIndex++;
2294
+ styles[String(tagIndex)] = [`raw:${serializeOpeningTag(tagName, tag.attrs)}`];
2295
+ if (isSelfClosing) {
2296
+ result += `<${tagIndex}/>`;
2412
2297
  } else {
2413
- const closingIndex = this.findClosingTagIndex(
2414
- input,
2415
- tagMatch.endIndex,
2416
- tagMatch.tag
2417
- );
2418
- if (closingIndex !== -1) {
2419
- const innerContent = input.slice(tagMatch.endIndex, closingIndex);
2420
- const innerUnit = this.parse(innerContent);
2421
- const baseIndex = tagIndex;
2422
- for (const [key, value] of Object.entries(innerUnit.styles)) {
2423
- const adjustedKey = String(baseIndex + parseInt(key, 10));
2424
- styles[adjustedKey] = value;
2425
- }
2426
- result += innerUnit.text.replace(/<(\/?)\d+>/g, (match, slash) => {
2427
- const num = parseInt(match.replace(/[</>]/g, ""), 10);
2428
- return `<${slash}${baseIndex + num}>`;
2429
- });
2430
- const closingMatch = this.matchTag(input, closingIndex);
2431
- pos = closingMatch ? closingMatch.endIndex : closingIndex;
2432
- const maxInnerIndex = Object.keys(innerUnit.styles).length > 0 ? Math.max(...Object.keys(innerUnit.styles).map(Number)) : 0;
2433
- tagIndex = baseIndex + maxInnerIndex;
2434
- continue;
2435
- }
2298
+ result += `<${tagIndex}>`;
2299
+ tagStack.push({ tag: tagName, index: tagIndex });
2436
2300
  }
2437
2301
  }
2438
- pos = tagMatch.endIndex;
2302
+ pos = tag.endIndex;
2439
2303
  }
2440
2304
  while (tagStack.length > 0) {
2441
2305
  const unclosed = tagStack.pop();
2442
2306
  result += `</${unclosed.index}>`;
2443
2307
  }
2444
- return {
2445
- text: result,
2446
- styles
2447
- };
2308
+ return { text: result, styles };
2448
2309
  }
2449
- /**
2450
- * Match an HTML tag at the given position
2451
- */
2452
2310
  matchTag(input, startPos) {
2453
2311
  const tagStart = input.indexOf("<", startPos);
2454
2312
  if (tagStart === -1) return null;
@@ -2471,9 +2329,6 @@ var LionRapid = (() => {
2471
2329
  endIndex: tagStart + fullMatch.length
2472
2330
  };
2473
2331
  }
2474
- /**
2475
- * Parse HTML attributes string into key-value pairs
2476
- */
2477
2332
  parseAttributes(attrsString) {
2478
2333
  const attrs = {};
2479
2334
  if (!attrsString.trim()) return attrs;
@@ -2481,60 +2336,14 @@ var LionRapid = (() => {
2481
2336
  let match;
2482
2337
  while ((match = attrPattern.exec(attrsString)) !== null) {
2483
2338
  const [, name, doubleQuoted, singleQuoted, unquoted] = match;
2484
- const value = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
2485
- attrs[name.toLowerCase()] = value;
2339
+ attrs[name.toLowerCase()] = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
2486
2340
  }
2487
2341
  return attrs;
2488
2342
  }
2489
- /**
2490
- * Find matching opening tag in the stack
2491
- */
2492
- findMatchingOpenTag(stack, tagName) {
2493
- for (let i = stack.length - 1; i >= 0; i--) {
2494
- if (stack[i].tag === tagName) {
2495
- return stack[i].index;
2496
- }
2497
- }
2498
- return null;
2499
- }
2500
- /**
2501
- * Find the closing tag index for a given opening tag
2502
- */
2503
- findClosingTagIndex(input, startPos, tagName) {
2504
- const lowerTag = tagName.toLowerCase();
2505
- let depth = 1;
2506
- let pos = startPos;
2507
- while (pos < input.length && depth > 0) {
2508
- const tagMatch = this.matchTag(input, pos);
2509
- if (!tagMatch) break;
2510
- if (tagMatch.tag.toLowerCase() === lowerTag) {
2511
- if (tagMatch.isClosing) {
2512
- depth--;
2513
- if (depth === 0) {
2514
- return tagMatch.startIndex;
2515
- }
2516
- } else if (!tagMatch.isSelfClosing) {
2517
- depth++;
2518
- }
2519
- }
2520
- pos = tagMatch.endIndex;
2521
- }
2522
- return -1;
2523
- }
2524
2343
  };
2525
2344
 
2526
2345
  // src/content/serializer/HTMLSerializer.ts
2527
2346
  var HTMLSerializer = class {
2528
- constructor(options = {}) {
2529
- __publicField(this, "styleMapper");
2530
- this.styleMapper = options.styleMapper ?? new DefaultStyleMapper();
2531
- }
2532
- /**
2533
- * Serialize ContentUnit to HTML string
2534
- *
2535
- * @param unit - ContentUnit with indexed tags and styles
2536
- * @returns HTML string with semantic elements
2537
- */
2538
2347
  serialize(unit) {
2539
2348
  const { text, styles } = unit;
2540
2349
  const tagPattern = /<(\/?)?(\d+)(\/)?>/g;
@@ -2543,40 +2352,25 @@ var LionRapid = (() => {
2543
2352
  let match;
2544
2353
  while ((match = tagPattern.exec(text)) !== null) {
2545
2354
  result += text.slice(lastIndex, match.index);
2546
- const [fullMatch, closingSlash, indexStr, selfClosingSlash] = match;
2547
- const tagIndex = indexStr;
2548
- const styleList = styles[tagIndex] || [];
2549
- if (closingSlash) {
2550
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2551
- result += `</${mapping.element}>`;
2552
- } else if (selfClosingSlash) {
2553
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2554
- const attrs = this.formatAttributes(mapping.attrs);
2555
- result += `<${mapping.element}${attrs} />`;
2355
+ const [fullMatch, closingSlash, indexStr] = match;
2356
+ const styleList = styles[indexStr] ?? [];
2357
+ const rawOpen = readRawStyle(styleList);
2358
+ if (rawOpen) {
2359
+ const tagName = extractTagName(rawOpen);
2360
+ const isVoid = VOID_TAGS.has(tagName);
2361
+ if (closingSlash) {
2362
+ if (!isVoid) result += `</${tagName}>`;
2363
+ } else {
2364
+ result += rawOpen;
2365
+ }
2556
2366
  } else {
2557
- const mapping = this.styleMapper.mapStylesToElement(styleList);
2558
- const attrs = this.formatAttributes(mapping.attrs);
2559
- result += `<${mapping.element}${attrs}>`;
2367
+ result += fullMatch;
2560
2368
  }
2561
2369
  lastIndex = match.index + fullMatch.length;
2562
2370
  }
2563
2371
  result += text.slice(lastIndex);
2564
2372
  return result;
2565
2373
  }
2566
- /**
2567
- * Format attributes object into HTML attribute string
2568
- */
2569
- formatAttributes(attrs) {
2570
- const entries = Object.entries(attrs);
2571
- if (entries.length === 0) return "";
2572
- return " " + entries.map(([key, value]) => `${key}="${this.escapeAttributeValue(value)}"`).join(" ");
2573
- }
2574
- /**
2575
- * Escape special characters in attribute values
2576
- */
2577
- escapeAttributeValue(value) {
2578
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2579
- }
2580
2374
  };
2581
2375
 
2582
2376
  // src/cache/CacheManager.ts
@@ -2810,7 +2604,7 @@ var LionRapid = (() => {
2810
2604
  };
2811
2605
 
2812
2606
  // src/lionRapid.ts
2813
- var INDEXED_TAG_REGEX = /<\/?[0-9]+>/;
2607
+ var INDEXED_TAG_REGEX = /<\/?[0-9]+\/?>/;
2814
2608
  var HTML_TAG_REGEX = /<[a-z][a-z0-9]*(?:\s+[^>]*)?\/?>/i;
2815
2609
  var LionRapidCore = class {
2816
2610
  constructor(eventEmitter, handlerChain, defaultLocale = "en", fallbackLocale = "en", defaultNamespace) {
@@ -3035,7 +2829,7 @@ var LionRapid = (() => {
3035
2829
  */
3036
2830
  processIndexedValue(value, request) {
3037
2831
  const processedText = this.processTranslationValue(value, request);
3038
- return processedText.replace(/<\/?[0-9]+>/g, "");
2832
+ return processedText.replace(/<\/?[0-9]+\/?>/g, "");
3039
2833
  }
3040
2834
  /**
3041
2835
  * 📦 Get styles for a translation key from namespace cache (fallback)
@@ -3081,7 +2875,7 @@ var LionRapid = (() => {
3081
2875
  try {
3082
2876
  const unit = this.markupParser.parse(value);
3083
2877
  const processedText = this.processTranslationValue(unit.text, request);
3084
- return processedText.replace(/<\/?[0-9]+>/g, "");
2878
+ return processedText.replace(/<\/?[0-9]+\/?>/g, "");
3085
2879
  } catch (error) {
3086
2880
  logger.warn(`ContentUnit processing failed for "${value}":`, error);
3087
2881
  return this.processTranslationValue(value, request);
@@ -3150,7 +2944,7 @@ var LionRapid = (() => {
3150
2944
  * @example
3151
2945
  * ```typescript
3152
2946
  * const unit = i18n.getContentUnit('terms', {}, 'Read our <b>Terms</b>.');
3153
- * // → { text: "Read our <1>Terms</1>.", styles: {"1": ["bold"]} }
2947
+ * // → { text: "Read our <1>Terms</1>.", styles: {"1": ["raw:<b>"]} }
3154
2948
  * ```
3155
2949
  */
3156
2950
  getContentUnit(key, params, defaultValue) {
@@ -3759,7 +3553,7 @@ var LionRapid = (() => {
3759
3553
  logger.debug("API base URL is not set");
3760
3554
  throw new NetworkError("API base URL is not set");
3761
3555
  }
3762
- const url = this.buildUrl("getTranslation", { key });
3556
+ const url = this.withDraftsParam(this.buildUrl("getTranslation", { key }));
3763
3557
  logger.debug(`API GET: ${url}`);
3764
3558
  try {
3765
3559
  const response = await this.makeRequest(url, "GET");
@@ -3795,7 +3589,7 @@ var LionRapid = (() => {
3795
3589
  logger.debug("API base URL is not set");
3796
3590
  throw new NetworkError("API base URL is not set");
3797
3591
  }
3798
- const url = this.buildUrl("getTranslation", { key });
3592
+ const url = this.withDraftsParam(this.buildUrl("getTranslation", { key }));
3799
3593
  logger.debug(`API GET ContentUnit: ${url}`);
3800
3594
  try {
3801
3595
  const response = await this.makeRequest(url, "GET");
@@ -3836,7 +3630,7 @@ var LionRapid = (() => {
3836
3630
  logger.debug("API base URL is not set");
3837
3631
  throw new NetworkError("API base URL is not set");
3838
3632
  }
3839
- const url = this.buildUrl("getNamespace", { locale, namespace });
3633
+ const url = this.withDraftsParam(this.buildUrl("getNamespace", { locale, namespace }));
3840
3634
  logger.debug(`API GET Namespace: ${url}`);
3841
3635
  const namespaceKey = `${locale}:${namespace}`;
3842
3636
  try {
@@ -3950,7 +3744,7 @@ var LionRapid = (() => {
3950
3744
  logger.debug("API base URL is not set");
3951
3745
  throw new NetworkError("API base URL is not set");
3952
3746
  }
3953
- const url = this.buildUrl("getNamespace", { locale, namespace });
3747
+ const url = this.withDraftsParam(this.buildUrl("getNamespace", { locale, namespace }));
3954
3748
  logger.debug(`API GET Namespace ContentUnits: ${url}`);
3955
3749
  const namespaceKey = `${locale}:${namespace}`;
3956
3750
  try {
@@ -4162,6 +3956,26 @@ var LionRapid = (() => {
4162
3956
  }
4163
3957
  return `${baseUrl}${path}`;
4164
3958
  }
3959
+ /**
3960
+ * Check whether the current page is running inside an active visual-editor
3961
+ * session. The editor JS sets `__lr_editor_active=1` as a session cookie
3962
+ * when its JWT is established; we read it here so client-side translation
3963
+ * fetches request draft content too, matching what the edge proxy does
3964
+ * server-side. Outside a browser (Node/SSR) this is always false.
3965
+ */
3966
+ isEditorPreviewSession() {
3967
+ if (typeof document === "undefined") return false;
3968
+ return document.cookie.split("; ").some((c) => c === "__lr_editor_active=1");
3969
+ }
3970
+ /**
3971
+ * Append `include_drafts=true` to a built URL when an editor session is
3972
+ * active. Leaves the URL unchanged for normal visitors so the delivery
3973
+ * gate continues to apply in production.
3974
+ */
3975
+ withDraftsParam(url) {
3976
+ if (!this.isEditorPreviewSession()) return url;
3977
+ return url + (url.includes("?") ? "&" : "?") + "include_drafts=true";
3978
+ }
4165
3979
  /**
4166
3980
  * Make HTTP request with timeout and headers
4167
3981
  */