editor-ts 0.0.12 → 0.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "editor-ts",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "TypeScript library for editing HTML content with JSON representation",
5
5
  "module": "index.ts",
6
6
  "main": "index.ts",
@@ -4,6 +4,10 @@ export type IframeCanvasBuildOptions = {
4
4
  title: string;
5
5
  css: string;
6
6
  htmlBody: string;
7
+ headHtml?: string;
8
+ stylesheets?: string[];
9
+ inlineStyles?: string[];
10
+ baseHref?: string;
7
11
  };
8
12
 
9
13
  function buildWysiwygCss(): string {
@@ -644,10 +648,24 @@ function iframeWysiwygScript() {
644
648
  }
645
649
 
646
650
  export function buildIframeCanvasSrcdoc(options: IframeCanvasBuildOptions): string {
647
- const { title, css, htmlBody } = options;
651
+ const { title, css, htmlBody, headHtml, stylesheets, inlineStyles, baseHref } = options;
648
652
 
649
653
  // Serialize a real function body into the iframe.
650
654
  const scriptSource = `(${iframeWysiwygScript.toString()})();`;
655
+ const safeScriptSource = scriptSource.replace(/<\/script>/gi, '<\\/script>');
656
+
657
+ const stylesheetTags = (stylesheets ?? [])
658
+ .filter((href) => typeof href === 'string' && href.trim().length > 0)
659
+ .map((href) => ` <link rel="stylesheet" href="${href}">`)
660
+ .join('\n');
661
+
662
+ const extraInlineStyles = (inlineStyles ?? [])
663
+ .filter((style) => typeof style === 'string' && style.trim().length > 0)
664
+ .map((style) => ` <style>${style}</style>`)
665
+ .join('\n');
666
+
667
+ const baseTag = baseHref && baseHref.trim().length > 0 ? ` <base href="${baseHref}">` : '';
668
+ const headHtmlBlock = headHtml ? `\n${headHtml}` : '';
651
669
 
652
670
  return `<!DOCTYPE html>
653
671
  <html>
@@ -655,18 +673,26 @@ export function buildIframeCanvasSrcdoc(options: IframeCanvasBuildOptions): stri
655
673
  <meta charset="UTF-8">
656
674
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
657
675
  <title>${title}</title>
658
- <style>${css}</style>
676
+ ${baseTag}
677
+ ${stylesheetTags}
678
+ <style data-editorts="page-css">${css}</style>
679
+ ${extraInlineStyles}
659
680
  <style>${buildWysiwygCss()}</style>
681
+ ${headHtmlBlock}
660
682
  </head>
661
683
  ${htmlBody}
662
- <script>${scriptSource}</script>
684
+ <script>${safeScriptSource}</script>
663
685
  </html>`;
664
686
  }
665
687
 
666
- export function buildIframeCanvasSrcdocFromPage(page: Page): string {
688
+ export function buildIframeCanvasSrcdocFromPage(
689
+ page: Page,
690
+ options?: Pick<IframeCanvasBuildOptions, 'headHtml' | 'stylesheets' | 'inlineStyles' | 'baseHref'>
691
+ ): string {
667
692
  return buildIframeCanvasSrcdoc({
668
693
  title: page.getTitle(),
669
694
  css: page.getCSS(),
670
695
  htmlBody: page.getHTML(),
696
+ ...options,
671
697
  });
672
698
  }
package/src/core/init.ts CHANGED
@@ -1391,7 +1391,7 @@ export function init(config: InitConfig): EditorTsEditor {
1391
1391
 
1392
1392
  // Build iframe content with WYSIWYG
1393
1393
  // NOTE: this must be built on-demand so refresh() reflects current Page state.
1394
- const buildIframeContent = () => buildIframeCanvasSrcdocFromPage(page);
1394
+ const buildIframeContent = () => buildIframeCanvasSrcdocFromPage(page, config.iframe);
1395
1395
 
1396
1396
 
1397
1397
  // Load content into iframe
@@ -2297,7 +2297,9 @@ export function init(config: InitConfig): EditorTsEditor {
2297
2297
  await workspace.fs.writeFile('page.json', save(), { isModelContentChange: true });
2298
2298
  }
2299
2299
 
2300
- const styleEl = iframe.contentDocument?.querySelector('head style') as HTMLStyleElement | null;
2300
+ const styleEl =
2301
+ (iframe.contentDocument?.querySelector('head style[data-editorts="page-css"]') as HTMLStyleElement | null)
2302
+ ?? (iframe.contentDocument?.querySelector('head style') as HTMLStyleElement | null);
2301
2303
  if (styleEl) styleEl.textContent = nextCss;
2302
2304
 
2303
2305
  await commitSnapshot({ source: 'user', message: 'edit style' });
@@ -2323,7 +2325,9 @@ export function init(config: InitConfig): EditorTsEditor {
2323
2325
  await workspace.fs.writeFile('page.json', save(), { isModelContentChange: true });
2324
2326
  }
2325
2327
 
2326
- const styleEl = iframe.contentDocument?.querySelector('head style') as HTMLStyleElement | null;
2328
+ const styleEl =
2329
+ (iframe.contentDocument?.querySelector('head style[data-editorts="page-css"]') as HTMLStyleElement | null)
2330
+ ?? (iframe.contentDocument?.querySelector('head style') as HTMLStyleElement | null);
2327
2331
  if (styleEl) styleEl.textContent = nextCss;
2328
2332
 
2329
2333
  await commitSnapshot({ source: 'user', message: 'clear style' });
package/src/types.ts CHANGED
@@ -305,6 +305,9 @@ export interface InitConfig {
305
305
  // Required: iframe element ID (user creates this in their HTML)
306
306
  iframeId: string;
307
307
 
308
+ /** Optional: iframe head configuration (runtime only). */
309
+ iframe?: IframeHeadConfig;
310
+
308
311
  // Optional: vim mode defaults to false
309
312
  vimMode?: boolean;
310
313
 
@@ -578,6 +581,17 @@ export interface InitConfig {
578
581
  storage?: StorageConfig;
579
582
  }
580
583
 
584
+ export interface IframeHeadConfig {
585
+ /** Raw HTML injected before </head>. */
586
+ headHtml?: string;
587
+ /** External stylesheet URLs injected as <link rel="stylesheet">. */
588
+ stylesheets?: string[];
589
+ /** Additional inline <style> contents injected in <head>. */
590
+ inlineStyles?: string[];
591
+ /** Optional <base href="..."> for resolving relative URLs. */
592
+ baseHref?: string;
593
+ }
594
+
581
595
  export type ShortcutDefinition = {
582
596
  key: string;
583
597
  action: () => void | Promise<void>;