@mdzip/editor 1.4.4 → 1.4.5

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/dist/view.js CHANGED
@@ -12,9 +12,9 @@ import { MdzipAssetSession, mdzipArchiveSourceId, sniffImageSize } from './asset
12
12
  import { MD_MARKDOWN_ICON } from './icons/md-markdown.js';
13
13
  import { MDZIP_RUNTIME_LIBRARIES } from './library-info.js';
14
14
  import { MdzipWorkspaceService, extensionForMime, normalizeArchivePath, relativeArchivePath } from './workspace.js';
15
- import { buildMdzipNavTree, canEditMdzipPath, escapeHtml, isOrphanedMdzipAsset, mdzipEntryIconKind, isMdzipManifestPath, resolveMdzipArchiveLinkTarget, renderMdzipPreviewHtml } from './workspace-view.js';
15
+ import { buildMdzipNavTree, canEditMdzipPath, escapeHtml, isOrphanedMdzipAsset, mdzipEntryIconKind, isMdzipManifestPath, isMdzipWorkspaceRelativeLink, resolveMdzipArchiveLinkTarget, renderMdzipPreviewHtml } from './workspace-view.js';
16
16
  import { escapeMarkdownImageAlt, findImageReferenceAtOffset, formatImageEditMarkdown } from './image-edit.js';
17
- import { MdzipRenderingService, defaultSafeMarkdownRenderer, groupTokensIntoChunks, chunkSourceKey, tokenEmbedsImage, tokenIsHeading } from './rendering.js';
17
+ import { MdzipRenderingService, defaultSafeMarkdownRenderer, groupTokensIntoChunks, chunkSourceKey, collectMdzipHeadingIds, MDZIP_HEADING_ID_PREFIX, tokenEmbedsImage, tokenIsHeading } from './rendering.js';
18
18
  import { mdzipFrontMatterExtension } from './front-matter-extension.js';
19
19
  import { WORKSPACE_CSS } from './view-css.js';
20
20
  const STYLE_ATTR = 'data-mdzip-ws-styles';
@@ -746,19 +746,19 @@ depth = 0) {
746
746
  : isImageFile(node.entry.path)
747
747
  ? IMAGE_ICON_HTML
748
748
  : FILE_ICON_HTML;
749
- const orphanBtnHtml = isOrphaned && options.allowOrphanActions ? `
750
- <span class="nav-orphan-button" role="button" tabindex="0"
751
- title="Orphaned asset" aria-label="Orphaned asset actions"
752
- data-orphan-path="${safePath}">
753
- ${ORPHAN_ICON_HTML}
749
+ const orphanBtnHtml = isOrphaned && options.allowOrphanActions ? `
750
+ <span class="nav-orphan-button" role="button" tabindex="0"
751
+ title="Orphaned asset" aria-label="Orphaned asset actions"
752
+ data-orphan-path="${safePath}">
753
+ ${ORPHAN_ICON_HTML}
754
754
  </span>` : '';
755
755
  const draggable = options.allowDrag && !isManifest;
756
- return `<button type="button" class="${classes}" title="${title}"
757
- data-nav-path="${safePath}" data-orphan="${isOrphaned ? 'true' : ''}"${draggable ? ' draggable="true"' : ''}>
758
- ${guides}<span class="nav-caret"></span>
759
- <span class="nav-file-icon ${iconKind}">${iconHtml}</span>
760
- ${orphanBtnHtml}
761
- <span class="nav-label">${safeName}</span>
756
+ return `<button type="button" class="${classes}" title="${title}"
757
+ data-nav-path="${safePath}" data-orphan="${isOrphaned ? 'true' : ''}"${draggable ? ' draggable="true"' : ''}>
758
+ ${guides}<span class="nav-caret"></span>
759
+ <span class="nav-file-icon ${iconKind}">${iconHtml}</span>
760
+ ${orphanBtnHtml}
761
+ <span class="nav-label">${safeName}</span>
762
762
  </button>`;
763
763
  }
764
764
  const childTrail = depth === 0 ? [] : [...trail, !isLast];
@@ -766,14 +766,14 @@ depth = 0) {
766
766
  .map((child, index) => renderNavNode(child, state, options, childTrail, index === node.children.length - 1, depth + 1))
767
767
  .join('');
768
768
  const pending = options.pendingFolders.has(node.path.toLowerCase());
769
- return `<details class="nav-directory${pending ? ' pending-folder' : ''}" open data-nav-dir="${escapeHtml(node.path)}">
770
- <summary${pending ? ` title="${escapeHtml(node.path)} — not saved until it contains a file"` : ''}>
771
- ${guides}<span class="nav-caret" aria-hidden="true"></span>
772
- <span class="nav-folder-icon closed">${FOLDER_CLOSED_ICON_HTML}</span>
773
- <span class="nav-folder-icon open">${FOLDER_OPEN_ICON_HTML}</span>
774
- <span class="nav-label">${escapeHtml(node.name)}</span>
775
- </summary>
776
- <div class="nav-directory-children">${children}</div>
769
+ return `<details class="nav-directory${pending ? ' pending-folder' : ''}" open data-nav-dir="${escapeHtml(node.path)}">
770
+ <summary${pending ? ` title="${escapeHtml(node.path)} — not saved until it contains a file"` : ''}>
771
+ ${guides}<span class="nav-caret" aria-hidden="true"></span>
772
+ <span class="nav-folder-icon closed">${FOLDER_CLOSED_ICON_HTML}</span>
773
+ <span class="nav-folder-icon open">${FOLDER_OPEN_ICON_HTML}</span>
774
+ <span class="nav-label">${escapeHtml(node.name)}</span>
775
+ </summary>
776
+ <div class="nav-directory-children">${children}</div>
777
777
  </details>`;
778
778
  }
779
779
  // Inserts view-local pending (not yet saved) folders into the nav tree so they
@@ -1002,6 +1002,10 @@ export class MdzipWorkspaceView {
1002
1002
  // cold-start, progressive, eager, mermaid-triggered or not) already calls
1003
1003
  // once its content is back in the DOM.
1004
1004
  this.pendingPreviewScrollRestore = null;
1005
+ // A #fragment from a link into another document (`other.md#heading`), held
1006
+ // until that document's preview has mounted so there is a heading to scroll
1007
+ // to. Consumed by firePreviewRendered.
1008
+ this.pendingAnchorFragment = null;
1005
1009
  // Guards syncScrollToPreviewBottom against overlapping drains: set to the
1006
1010
  // chunkedRenderState generation currently being force-drained, null when
1007
1011
  // none is in flight. A second bottom-edge sync that arrives mid-drain
@@ -2220,7 +2224,7 @@ export class MdzipWorkspaceView {
2220
2224
  let cursor = middleStart;
2221
2225
  let mountedRoots = [];
2222
2226
  while (cursor < middleEnd) {
2223
- const batch = await this.renderAndMountChunkBatch(records, cursor, context, generation, insertBeforeNode);
2227
+ const batch = await this.renderAndMountChunkBatch(records, cursor, context, generation, insertBeforeNode, middleEnd);
2224
2228
  if (generation !== this.previewGeneration || context.signal.aborted)
2225
2229
  return;
2226
2230
  if (batch.cursor === cursor)
@@ -2390,7 +2394,12 @@ export class MdzipWorkspaceView {
2390
2394
  * included here; see {@link mountChunkBatch} and
2391
2395
  * {@link drainRemainingChunks} for the two different ways callers pace it.
2392
2396
  */
2393
- async renderAndMountChunkBatch(records, startCursor, context, generation, insertBeforeNode = null) {
2397
+ async renderAndMountChunkBatch(records, startCursor, context, generation, insertBeforeNode = null,
2398
+ // Exclusive upper bound. Reconciliation mounts just its replacement
2399
+ // "middle"; the records after it are surviving suffix chunks that are
2400
+ // already in the DOM, and running on into them would mount each a second
2401
+ // time (orphaning the first copy's root, which nothing would ever remove).
2402
+ endCursor = records.length) {
2394
2403
  const BATCH_CHAR_BUDGET = 4000;
2395
2404
  const BATCH_TIME_BUDGET_MS = 10;
2396
2405
  const clock = this.elPreviewContent.ownerDocument.defaultView?.performance ?? performance;
@@ -2399,7 +2408,7 @@ export class MdzipWorkspaceView {
2399
2408
  let renderedChars = 0;
2400
2409
  const mountedRoots = [];
2401
2410
  const batchStartCursor = startCursor;
2402
- while (cursor < records.length
2411
+ while (cursor < Math.min(endCursor, records.length)
2403
2412
  && renderedChars < BATCH_CHAR_BUDGET
2404
2413
  && (mountedRoots.length === 0 || clock.now() - batchStart < BATCH_TIME_BUDGET_MS)) {
2405
2414
  if (generation !== this.previewGeneration || context.signal.aborted)
@@ -2798,6 +2807,7 @@ export class MdzipWorkspaceView {
2798
2807
  return;
2799
2808
  }
2800
2809
  this.restorePendingPreviewScroll(generation);
2810
+ this.flushPendingAnchorSoon();
2801
2811
  try {
2802
2812
  this.options.onPreviewRendered?.(snapshot);
2803
2813
  }
@@ -3819,7 +3829,27 @@ export class MdzipWorkspaceView {
3819
3829
  const clampedTop = Math.max(margin, Math.min(naturalTop, win.innerHeight - margin - submenu.offsetHeight));
3820
3830
  submenu.style.top = `${clampedTop - rect.top}px`;
3821
3831
  });
3832
+ // Chromium treats a Shift+mousedown as "extend the selection", right
3833
+ // button included, so Shift+right-click would first stretch the selection
3834
+ // from the caret to the click — and the native menu reports no misspelled
3835
+ // word (hence no suggestions) unless the selection is collapsed or just
3836
+ // the word. Cancelling that mousedown leaves the selection alone and lets
3837
+ // the browser resolve the word under the pointer itself. Capture phase so
3838
+ // it runs before CodeMirror's own handlers; focus is restored by hand
3839
+ // since the cancelled mousedown no longer moves it.
3840
+ this.elEditorHost.addEventListener('mousedown', (e) => {
3841
+ if (e.button === 2 && e.shiftKey) {
3842
+ e.preventDefault();
3843
+ this.cmEditor?.focus();
3844
+ }
3845
+ }, true);
3822
3846
  this.elEditorHost.addEventListener('contextmenu', (e) => {
3847
+ // Shift+right-click bypasses our own formatting menu so the host's
3848
+ // native context menu (spell-check suggestions, etc.) shows instead —
3849
+ // without this, preventDefault() below suppresses it unconditionally.
3850
+ if (e.shiftKey) {
3851
+ return;
3852
+ }
3823
3853
  if (!this.controlPolicy.contextMenu.editor) {
3824
3854
  return;
3825
3855
  }
@@ -3927,12 +3957,24 @@ export class MdzipWorkspaceView {
3927
3957
  if (!link || !snapshot) {
3928
3958
  return;
3929
3959
  }
3930
- const targetPath = resolveMdzipArchiveLinkTarget(link.getAttribute('href') ?? '', snapshot.currentPath, snapshot.content.paths);
3931
- if (!targetPath) {
3960
+ const href = link.getAttribute('href') ?? '';
3961
+ if (href.startsWith('#')) {
3962
+ event.preventDefault();
3963
+ void this.scrollPreviewToAnchor(href.slice(1));
3932
3964
  return;
3933
3965
  }
3934
- event.preventDefault();
3935
- void this.openPath(targetPath);
3966
+ const targetPath = resolveMdzipArchiveLinkTarget(href, snapshot.currentPath, snapshot.content.paths);
3967
+ if (targetPath) {
3968
+ event.preventDefault();
3969
+ const hashIndex = href.indexOf('#');
3970
+ const fragment = hashIndex >= 0 ? href.slice(hashIndex + 1) : '';
3971
+ void this.openPathAndScrollToAnchor(targetPath, fragment);
3972
+ return;
3973
+ }
3974
+ if (this.options.onUnresolvedLinkClick && isMdzipWorkspaceRelativeLink(href)) {
3975
+ event.preventDefault();
3976
+ this.options.onUnresolvedLinkClick(href, snapshot);
3977
+ }
3936
3978
  });
3937
3979
  this.elRoot.addEventListener('pointerover', (event) => this.handleTooltipPointer(event));
3938
3980
  this.elRoot.addEventListener('pointerout', (event) => {
@@ -3966,6 +4008,99 @@ export class MdzipWorkspaceView {
3966
4008
  element.removeAttribute('title');
3967
4009
  });
3968
4010
  }
4011
+ async openPathAndScrollToAnchor(path, fragment) {
4012
+ const generationBefore = this.previewGeneration;
4013
+ this.pendingAnchorFragment = fragment || null;
4014
+ await this.openPath(path);
4015
+ // No new render started (already on that document, or the open failed):
4016
+ // nothing will call firePreviewRendered, so apply the anchor now.
4017
+ if (this.pendingAnchorFragment !== null && this.previewGeneration === generationBefore) {
4018
+ const pending = this.pendingAnchorFragment;
4019
+ this.pendingAnchorFragment = null;
4020
+ await this.scrollPreviewToAnchor(pending);
4021
+ }
4022
+ }
4023
+ // Deferred a frame: firePreviewRendered runs inside the first chunk batch's
4024
+ // completion callback, before the lazy-continuation sentinel for the rest is
4025
+ // armed. Scrolling then (which may need to drain those chunks) would race it.
4026
+ flushPendingAnchorSoon() {
4027
+ if (this.pendingAnchorFragment === null) {
4028
+ return;
4029
+ }
4030
+ const generation = this.previewGeneration;
4031
+ requestAnimationFrame(() => {
4032
+ if (generation !== this.previewGeneration || this.pendingAnchorFragment === null) {
4033
+ return;
4034
+ }
4035
+ const pending = this.pendingAnchorFragment;
4036
+ this.pendingAnchorFragment = null;
4037
+ void this.scrollPreviewToAnchor(pending);
4038
+ });
4039
+ }
4040
+ findPreviewAnchor(fragment) {
4041
+ const wanted = new Set([fragment, MDZIP_HEADING_ID_PREFIX + fragment]);
4042
+ const wantedLower = new Set([...wanted].map((value) => value.toLowerCase()));
4043
+ let caseInsensitiveMatch = null;
4044
+ for (const element of Array.from(this.elPreviewContent.querySelectorAll('[id],[name]'))) {
4045
+ for (const value of [element.getAttribute('id'), element.getAttribute('name')]) {
4046
+ if (!value)
4047
+ continue;
4048
+ if (wanted.has(value))
4049
+ return element;
4050
+ if (!caseInsensitiveMatch && wantedLower.has(value.toLowerCase()))
4051
+ caseInsensitiveMatch = element;
4052
+ }
4053
+ }
4054
+ return caseInsensitiveMatch;
4055
+ }
4056
+ recordMayContainAnchor(record, fragment) {
4057
+ const lower = fragment.toLowerCase();
4058
+ if (collectMdzipHeadingIds(record.tokens).some((id) => id.toLowerCase() === lower)) {
4059
+ return true;
4060
+ }
4061
+ // An explicit <a id="x"> / <a name="x"> anchor written in the source.
4062
+ const escaped = fragment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
4063
+ return new RegExp(`(?:id|name)\\s*=\\s*["']${escaped}["']`, 'i').test(record.key);
4064
+ }
4065
+ /**
4066
+ * Scrolls the preview to the heading (or explicit id/name anchor) a
4067
+ * `#fragment` link names. Under progressive rendering the target may sit in
4068
+ * a chunk that hasn't been mounted yet, so this mounts everything up to and
4069
+ * including that chunk first. Returns whether an anchor was found.
4070
+ */
4071
+ async scrollPreviewToAnchor(rawFragment) {
4072
+ let fragment = rawFragment;
4073
+ try {
4074
+ fragment = decodeURIComponent(rawFragment);
4075
+ }
4076
+ catch {
4077
+ // Malformed escape — use it as written.
4078
+ }
4079
+ let target = fragment ? this.findPreviewAnchor(fragment) : null;
4080
+ const pending = this.chunkedRenderState;
4081
+ if (!target && fragment && pending && pending.cursor < pending.records.length) {
4082
+ const index = pending.records.findIndex((record, i) => i >= pending.cursor && this.recordMayContainAnchor(record, fragment));
4083
+ if (index >= 0) {
4084
+ await this.drainRemainingChunks(pending, () => { }, new AbortController().signal, index);
4085
+ const remaining = this.chunkedRenderState;
4086
+ if (remaining && remaining.generation === pending.generation && !remaining.sentinelHandle
4087
+ && remaining.cursor < remaining.records.length) {
4088
+ this.armChunkSentinel(remaining.records, remaining.cursor, remaining.context, remaining.generation, remaining.animateImageHydration);
4089
+ }
4090
+ target = this.findPreviewAnchor(fragment);
4091
+ }
4092
+ }
4093
+ const pane = this.elPreviewPane;
4094
+ if (!target) {
4095
+ // `#` and `#top` mean the top of the document, as in a browser.
4096
+ if (!fragment || fragment.toLowerCase() === 'top') {
4097
+ pane.scrollTop = 0;
4098
+ }
4099
+ return false;
4100
+ }
4101
+ pane.scrollTop += target.getBoundingClientRect().top - pane.getBoundingClientRect().top;
4102
+ return true;
4103
+ }
3969
4104
  async openPath(path) {
3970
4105
  if (!this.workspace) {
3971
4106
  return;
@@ -4733,7 +4868,7 @@ export class MdzipWorkspaceView {
4733
4868
  // spelling suggestions live — there's no API to read the browser's
4734
4869
  // dictionary suggestions into a custom menu. Point at the escape hatch
4735
4870
  // instead of silently dropping the feature.
4736
- if (editable) {
4871
+ if (editable && this.options.showSpellingSuggestionsHint !== false) {
4737
4872
  groups.push([{
4738
4873
  action: 'editor-spelling-suggestions-hint',
4739
4874
  label: 'Spelling Suggestions',
@@ -5256,7 +5391,7 @@ export class MdzipWorkspaceView {
5256
5391
  * pass over every image at the end avoids the pile-up entirely, without
5257
5392
  * making Copy All wait on it.
5258
5393
  */
5259
- async drainRemainingChunks(state, onProgress, signal) {
5394
+ async drainRemainingChunks(state, onProgress, signal, stopAfterRecordIndex) {
5260
5395
  const { records, context, generation, animateImageHydration } = state;
5261
5396
  // Draining is instead of the scroll-driven continuation, not alongside
5262
5397
  // it — tearing down any armed sentinel first stops the two from racing
@@ -5271,7 +5406,9 @@ export class MdzipWorkspaceView {
5271
5406
  }
5272
5407
  const allPending = [];
5273
5408
  try {
5274
- while (this.chunkedRenderState?.generation === generation && this.chunkedRenderState.cursor < records.length) {
5409
+ while (this.chunkedRenderState?.generation === generation
5410
+ && this.chunkedRenderState.cursor < records.length
5411
+ && (stopAfterRecordIndex === undefined || this.chunkedRenderState.cursor <= stopAfterRecordIndex)) {
5275
5412
  if (signal.aborted || generation !== this.previewGeneration || context.signal.aborted)
5276
5413
  return;
5277
5414
  const cursor = this.chunkedRenderState.cursor;
@@ -6587,325 +6724,325 @@ function formatMetadataValue(value) {
6587
6724
  }
6588
6725
  return 'Not available';
6589
6726
  }
6590
- const SHELL_HTML = `
6591
- <section class="mdzip-root">
6592
- <div class="document-strip" data-ref="document-strip" hidden>
6593
- <button type="button" class="title-button" data-ref="title-btn"></button>
6594
- <button type="button" class="document-info-button" data-ref="document-info-btn"
6595
- title="Document information" aria-label="Document information">
6596
- ${INFO_ICON_HTML}
6597
- </button>
6598
- </div>
6599
-
6600
- <header class="toolbar" data-ref="toolbar" hidden>
6601
- <div class="toolbar-start">
6602
- <div class="toolbar-left">
6603
- <button type="button" class="icon-toggle nav-toggle" data-ref="nav-btn" title="Toggle contents" aria-label="Toggle contents">
6604
- ${NAV_TOGGLE_ICON_HTML}
6605
- </button>
6606
- </div>
6607
-
6608
- <div class="edit-toolbar" data-ref="edit-toolbar" role="toolbar" aria-label="Markdown formatting">
6609
- <div class="edit-toolbar-group">
6610
- <button type="button" data-format="bold" data-format-control="bold" title="Bold" aria-label="Bold">${BOLD_ICON_HTML}</button>
6611
- <button type="button" data-format="italic" data-format-control="italic" title="Italic" aria-label="Italic">${ITALIC_ICON_HTML}</button>
6612
- <button type="button" data-format="strike" data-format-control="strikethrough" title="Strikethrough" aria-label="Strikethrough">${STRIKE_ICON_HTML}</button>
6613
- <div class="format-menu" data-format-control="headings">
6614
- <button type="button" class="format-menu-toggle" data-format-menu-toggle
6615
- aria-label="Heading" aria-haspopup="menu" aria-expanded="false">
6616
- ${HEADING_ICON_HTML}${CHEVRON_ICON_HTML}
6617
- </button>
6618
- <div class="format-menu-popover" data-format-menu role="menu" aria-label="Heading level" hidden>
6619
- <button type="button" role="menuitem" data-format="paragraph">Paragraph</button>
6620
- <button type="button" role="menuitem" data-format="heading-1" data-heading-level="1"><strong>H1</strong> Heading 1</button>
6621
- <button type="button" role="menuitem" data-format="heading-2" data-heading-level="2"><strong>H2</strong> Heading 2</button>
6622
- <button type="button" role="menuitem" data-format="heading-3" data-heading-level="3"><strong>H3</strong> Heading 3</button>
6623
- <button type="button" role="menuitem" data-format="heading-4" data-heading-level="4"><strong>H4</strong> Heading 4</button>
6624
- <button type="button" role="menuitem" data-format="heading-5" data-heading-level="5"><strong>H5</strong> Heading 5</button>
6625
- <button type="button" role="menuitem" data-format="heading-6" data-heading-level="6"><strong>H6</strong> Heading 6</button>
6626
- </div>
6627
- </div>
6628
- </div>
6629
- <span class="edit-toolbar-divider" aria-hidden="true"></span>
6630
- <div class="edit-toolbar-group">
6631
- <button type="button" data-format="bullet-list" data-format-control="bulletList" title="Bulleted list" aria-label="Bulleted list">${BULLET_LIST_ICON_HTML}</button>
6632
- <button type="button" data-format="ordered-list" data-format-control="orderedList" title="Numbered list" aria-label="Numbered list">${ORDERED_LIST_ICON_HTML}</button>
6633
- <div class="format-menu" data-format-control="code">
6634
- <button type="button" class="format-menu-toggle" data-format-menu-toggle
6635
- aria-label="Code" aria-haspopup="menu" aria-expanded="false">
6636
- ${CODE_ICON_HTML}${CHEVRON_ICON_HTML}
6637
- </button>
6638
- <div class="format-menu-popover" data-format-menu role="menu" aria-label="Code format" hidden>
6639
- <button type="button" role="menuitem" data-format="code" data-code-kind="inline">Inline code</button>
6640
- <button type="button" role="menuitem" data-format="code-block" data-code-kind="block">Code block</button>
6641
- </div>
6642
- </div>
6643
- <button type="button" data-format="quote" data-format-control="blockquote" title="Blockquote" aria-label="Blockquote">${QUOTE_ICON_HTML}</button>
6644
- <button type="button" data-format="line-break" data-format-control="lineBreak" title="Line break" aria-label="Line break">${LINE_BREAK_ICON_HTML}</button>
6645
- </div>
6646
- <span class="edit-toolbar-divider" aria-hidden="true"></span>
6647
- <div class="edit-toolbar-group">
6648
- <button type="button" data-format="link" data-format-control="link" title="Link" aria-label="Link">${LINK_ICON_HTML}</button>
6649
- <button type="button" data-format="image" data-format-control="image" title="Image" aria-label="Image">${IMAGE_FORMAT_ICON_HTML}</button>
6650
- <input type="file" data-ref="image-input" accept="image/*" hidden />
6651
- </div>
6652
- </div>
6653
- </div>
6654
-
6655
- <div class="toolbar-buttons view-mode-toggle-group" data-ref="layout-controls" role="group" aria-label="Editor layout">
6656
- <button type="button" class="icon-toggle view-mode-toggle" data-ref="source-btn" title="Edit" aria-label="Edit" aria-pressed="false">
6657
- <span class="commandbar-flex-container" data-ref="source-icon">${SOURCE_EDIT_ICON_HTML}</span>
6658
- </button>
6659
- <button type="button" class="icon-toggle view-mode-toggle" data-ref="split-btn" title="Split" aria-label="Split" aria-pressed="false">
6660
- <span class="commandbar-flex-container">
6661
- ${SPLIT_ICON_HTML}
6662
- </span>
6663
- </button>
6664
- <button type="button" class="icon-toggle view-mode-toggle" data-ref="preview-btn" title="View" aria-label="View" aria-pressed="false">
6665
- <span class="commandbar-flex-container">
6666
- ${PREVIEW_ICON_HTML}
6667
- </span>
6668
- </button>
6669
- </div>
6670
-
6671
- <div class="toolbar-controls" data-ref="toolbar-controls">
6672
- <button type="button" class="icon-toggle" data-ref="search-btn" title="Find/replace (Mod-F)" aria-label="Find/replace">
6673
- ${SEARCH_ICON_HTML}
6674
- </button>
6675
- <button type="button" class="icon-toggle" data-ref="save-btn" title="Save" aria-label="Save">
6676
- ${SAVE_ICON_HTML}
6677
- </button>
6678
- <button type="button" class="icon-toggle zoom-toggle" data-ref="zoom-btn" title="Zoom controls" aria-label="Zoom controls">
6679
- ${ZOOM_ICON_HTML}
6680
- </button>
6681
- <div class="theme-toggle-group" data-ref="theme-controls" role="group" aria-label="Color scheme">
6682
- <button type="button" class="icon-toggle theme-toggle" data-ref="dark-theme-btn"
6683
- title="Dark mode" aria-label="Dark mode" aria-pressed="false">
6684
- ${DARK_THEME_ICON_HTML}
6685
- </button>
6686
- <button type="button" class="icon-toggle theme-toggle" data-ref="light-theme-btn"
6687
- title="Light mode" aria-label="Light mode" aria-pressed="false">
6688
- ${LIGHT_THEME_ICON_HTML}
6689
- </button>
6690
- </div>
6691
- <div class="zoom-popover" data-ref="zoom-popover" hidden role="group" aria-label="Zoom">
6692
- <span class="zoom-level" data-ref="zoom-level">100%</span>
6693
- <span class="zoom-stepper">
6694
- <button type="button" data-action="zoom-out" title="Zoom out" aria-label="Zoom out">-</button>
6695
- <button type="button" data-action="zoom-in" title="Zoom in" aria-label="Zoom in">+</button>
6696
- </span>
6697
- <button type="button" class="zoom-reset" data-action="zoom-reset" title="Reset zoom">Reset</button>
6698
- </div>
6699
- </div>
6700
- </header>
6701
-
6702
- <div class="workspace-shell" data-ref="workspace-shell" hidden>
6703
- <aside class="nav-pane" data-ref="nav-pane" aria-label="Package contents">
6704
- <div class="nav-tree" data-ref="nav-tree"></div>
6705
- </aside>
6706
- <div class="nav-resizer" data-ref="nav-resizer"
6707
- role="separator" aria-orientation="vertical" aria-label="Resize contents pane"></div>
6708
-
6709
- <div class="pane-stack" data-ref="pane-stack">
6710
- <section class="pane edit-pane" data-ref="edit-pane">
6711
- <div class="editor-host" data-ref="editor-host"></div>
6712
- </section>
6713
- <div class="split-resizer" data-ref="split-resizer"
6714
- role="separator" aria-orientation="vertical" aria-label="Resize split panes"></div>
6715
- <section class="pane preview-pane" data-ref="preview-pane" tabindex="-1">
6716
- <article class="preview-content" data-ref="preview-content"></article>
6717
- </section>
6718
- <section class="pane entry-pane" data-ref="entry-pane"></section>
6719
- </div>
6720
- </div>
6721
-
6722
- <div class="title-dialog-backdrop" data-ref="title-dialog" hidden
6723
- role="dialog" aria-modal="true" aria-labelledby="mdzip-title-dialog-heading">
6724
- <div class="title-dialog">
6725
- <h3 id="mdzip-title-dialog-heading">Set Document Title</h3>
6726
- <p>This is the package-level title stored in manifest.json.</p>
6727
- <input type="text" maxlength="120" data-ref="title-input" aria-label="Document title" />
6728
- <p class="title-dialog-validation" data-ref="title-validation" hidden>Title cannot be empty.</p>
6729
- <p>If unset, consumers may fall back to entry point, filename, or first heading.</p>
6730
- <div class="title-dialog-actions">
6731
- <button type="button" class="reset-title" data-ref="title-reset-btn">Reset</button>
6732
- <button type="button" data-action="cancel-title">Cancel</button>
6733
- <button type="button" class="save-title" data-ref="title-save-btn">Save</button>
6734
- </div>
6735
- </div>
6736
- </div>
6737
-
6738
- <div class="title-dialog-backdrop" data-ref="conversion-dialog" hidden
6739
- role="dialog" aria-modal="true" aria-labelledby="mdzip-conversion-dialog-heading">
6740
- <div class="title-dialog">
6741
- <h3 id="mdzip-conversion-dialog-heading">Convert to MDZ?</h3>
6742
- <p>
6743
- Regular Markdown files contain only text. Convert this document to an
6744
- MDZ package to add images, package navigation, and document metadata.
6745
- </p>
6746
- <p>The next save will produce an .mdz file.</p>
6747
- <div class="title-dialog-actions">
6748
- <button type="button" data-action="cancel-conversion">Cancel</button>
6749
- <button type="button" class="save-title" data-ref="conversion-confirm-btn">Convert</button>
6750
- </div>
6751
- </div>
6752
- </div>
6753
-
6754
- <div class="title-dialog-backdrop" data-ref="image-insert-dialog" hidden
6755
- role="dialog" aria-modal="true" aria-labelledby="mdzip-image-insert-dialog-heading">
6756
- <div class="title-dialog image-insert-dialog">
6757
- <h3 id="mdzip-image-insert-dialog-heading">Insert Image</h3>
6758
- <p>Choose the Markdown inserted for this image.</p>
6759
- <fieldset class="image-insert-options">
6760
- <legend>Output</legend>
6761
- <label>
6762
- <input type="radio" name="mdzip-image-insert-mode" value="markdown"
6763
- data-ref="image-insert-mode-markdown" checked />
6764
- Markdown image
6765
- </label>
6766
- <label>
6767
- <input type="radio" name="mdzip-image-insert-mode" value="html"
6768
- data-ref="image-insert-mode-html" />
6769
- HTML &lt;img&gt; with sizing
6770
- </label>
6771
- </fieldset>
6772
- <label class="image-insert-field">
6773
- <span>Alt text</span>
6774
- <input type="text" data-ref="image-insert-alt" />
6775
- </label>
6776
- <div class="image-insert-grid">
6777
- <label class="image-insert-field">
6778
- <span>Size by</span>
6779
- <select data-ref="image-insert-size-mode">
6780
- <option value="original">Original size</option>
6781
- <option value="width" selected>Width</option>
6782
- <option value="height">Height</option>
6783
- <option value="percent">Percent</option>
6784
- </select>
6785
- </label>
6786
- <label class="image-insert-field">
6787
- <span>Value</span>
6788
- <input type="number" min="1" step="1" data-ref="image-insert-size-value" />
6789
- </label>
6790
- </div>
6791
- <label class="image-insert-field">
6792
- <span>Position</span>
6793
- <select data-ref="image-insert-position">
6794
- <option value="inline">Inline/default</option>
6795
- <option value="left">Left</option>
6796
- <option value="center">Center</option>
6797
- <option value="right">Right</option>
6798
- <option value="wrap-left">Wrap left</option>
6799
- <option value="wrap-right">Wrap right</option>
6800
- </select>
6801
- </label>
6802
- <div class="title-dialog-actions">
6803
- <button type="button" data-action="cancel-image-insert">Cancel</button>
6804
- <button type="button" class="save-title" data-ref="image-insert-confirm-btn">Insert</button>
6805
- </div>
6806
- </div>
6807
- </div>
6808
-
6809
- <div class="title-dialog-backdrop" data-ref="pack-files-dialog" hidden
6810
- role="dialog" aria-modal="true" aria-labelledby="mdzip-pack-files-dialog-heading">
6811
- <div class="title-dialog pack-files-dialog">
6812
- <h3 id="mdzip-pack-files-dialog-heading">Package Markdown Files</h3>
6813
- <p>These files contain more than one Markdown document. Choose how to package them.</p>
6814
- <fieldset class="pack-files-options">
6815
- <legend>Mode</legend>
6816
- <label>
6817
- <input type="radio" name="mdzip-pack-files-mode" value="document"
6818
- data-ref="pack-files-mode-document" checked />
6819
- Document — one primary page; the rest are attached
6820
- </label>
6821
- <label>
6822
- <input type="radio" name="mdzip-pack-files-mode" value="project"
6823
- data-ref="pack-files-mode-project" />
6824
- Project — a set of related documents
6825
- </label>
6826
- </fieldset>
6827
- <label class="pack-files-field">
6828
- <span>Entry point</span>
6829
- <select data-ref="pack-files-entry"></select>
6830
- </label>
6831
- <div class="title-dialog-actions">
6832
- <button type="button" data-action="cancel-pack-files">Cancel</button>
6833
- <button type="button" class="save-title" data-ref="pack-files-confirm-btn">Package</button>
6834
- </div>
6835
- </div>
6836
- </div>
6837
-
6838
- <div class="title-dialog-backdrop" data-ref="copy-render-dialog" hidden
6839
- role="dialog" aria-modal="true" aria-labelledby="mdzip-copy-render-dialog-heading">
6840
- <div class="title-dialog copy-render-dialog">
6841
- <h3 id="mdzip-copy-render-dialog-heading" data-ref="copy-render-heading">Copying document...</h3>
6842
- <div data-ref="copy-render-progress-section">
6843
- <p data-ref="copy-render-progress-text">Rendering the full document...</p>
6844
- <div class="copy-render-progress-track">
6845
- <div class="copy-render-progress-bar" data-ref="copy-render-progress-bar"></div>
6846
- </div>
6847
- </div>
6848
- <p data-ref="copy-render-ready-text" hidden>
6849
- The document is ready. Browsers only allow a clipboard write right after a click, so this needs one more —
6850
- click Copy to finish.
6851
- </p>
6852
- <p data-ref="copy-render-done-text" hidden></p>
6853
- <div class="title-dialog-actions">
6854
- <button type="button" data-action="cancel-copy-render" data-ref="copy-render-cancel-btn">Cancel</button>
6855
- <button type="button" data-action="cancel-copy-ready" data-ref="copy-render-ready-cancel-btn" hidden>Cancel</button>
6856
- <button type="button" class="save-title" data-action="confirm-copy-ready" data-ref="copy-render-confirm-btn" hidden>Copy</button>
6857
- <button type="button" class="save-title" data-action="dismiss-copy-render" data-ref="copy-render-dismiss-btn" hidden>Dismiss</button>
6858
- </div>
6859
- </div>
6860
- </div>
6861
-
6862
- <div class="title-dialog-backdrop" data-ref="metadata-dialog" hidden
6863
- role="dialog" aria-modal="true" aria-labelledby="mdzip-metadata-dialog-heading">
6864
- <div class="title-dialog metadata-dialog">
6865
- <h3 id="mdzip-metadata-dialog-heading">Document Information</h3>
6866
- <dl data-ref="metadata-list"></dl>
6867
- <h4>Libraries</h4>
6868
- <dl data-ref="library-list"></dl>
6869
- <div class="title-dialog-actions">
6870
- <button type="button" class="save-title" data-action="close-metadata">Close</button>
6871
- </div>
6872
- </div>
6873
- </div>
6874
-
6875
- <div class="title-dialog-backdrop" data-ref="name-dialog" hidden
6876
- role="dialog" aria-modal="true" aria-labelledby="mdzip-name-dialog-heading">
6877
- <div class="title-dialog">
6878
- <h3 id="mdzip-name-dialog-heading" data-ref="name-dialog-heading">New Markdown File</h3>
6879
- <input type="text" maxlength="260" data-ref="name-input" aria-label="File name" />
6880
- <p class="title-dialog-validation" data-ref="name-validation" hidden></p>
6881
- <div class="title-dialog-actions">
6882
- <button type="button" data-action="cancel-name">Cancel</button>
6883
- <button type="button" class="save-title" data-ref="name-confirm-btn">Create</button>
6884
- </div>
6885
- </div>
6886
- </div>
6887
-
6888
- <div class="title-dialog-backdrop" data-ref="delete-dialog" hidden
6889
- role="dialog" aria-modal="true" aria-labelledby="mdzip-delete-dialog-heading">
6890
- <div class="title-dialog">
6891
- <h3 id="mdzip-delete-dialog-heading">Delete File?</h3>
6892
- <p data-ref="delete-dialog-text"></p>
6893
- <div class="title-dialog-actions">
6894
- <button type="button" data-action="cancel-delete">Cancel</button>
6895
- <button type="button" class="danger-action" data-ref="delete-confirm-btn">Delete</button>
6896
- </div>
6897
- </div>
6898
- </div>
6899
-
6900
- <input type="file" data-ref="replace-input" hidden />
6901
-
6902
- <div class="nav-context-menu" data-ref="nav-menu" hidden role="menu"></div>
6903
-
6904
- <div class="mdzip-tooltip" data-ref="tooltip" role="tooltip" hidden></div>
6905
-
6906
- <div class="mdzip-copy-toast" data-ref="copy-toast" role="status" aria-live="polite" hidden></div>
6907
-
6908
- <p class="mdzip-empty" data-ref="empty-state">No MDZip workspace loaded.</p>
6909
- </section>
6727
+ const SHELL_HTML = `
6728
+ <section class="mdzip-root">
6729
+ <div class="document-strip" data-ref="document-strip" hidden>
6730
+ <button type="button" class="title-button" data-ref="title-btn"></button>
6731
+ <button type="button" class="document-info-button" data-ref="document-info-btn"
6732
+ title="Document information" aria-label="Document information">
6733
+ ${INFO_ICON_HTML}
6734
+ </button>
6735
+ </div>
6736
+
6737
+ <header class="toolbar" data-ref="toolbar" hidden>
6738
+ <div class="toolbar-start">
6739
+ <div class="toolbar-left">
6740
+ <button type="button" class="icon-toggle nav-toggle" data-ref="nav-btn" title="Toggle contents" aria-label="Toggle contents">
6741
+ ${NAV_TOGGLE_ICON_HTML}
6742
+ </button>
6743
+ </div>
6744
+
6745
+ <div class="edit-toolbar" data-ref="edit-toolbar" role="toolbar" aria-label="Markdown formatting">
6746
+ <div class="edit-toolbar-group">
6747
+ <button type="button" data-format="bold" data-format-control="bold" title="Bold" aria-label="Bold">${BOLD_ICON_HTML}</button>
6748
+ <button type="button" data-format="italic" data-format-control="italic" title="Italic" aria-label="Italic">${ITALIC_ICON_HTML}</button>
6749
+ <button type="button" data-format="strike" data-format-control="strikethrough" title="Strikethrough" aria-label="Strikethrough">${STRIKE_ICON_HTML}</button>
6750
+ <div class="format-menu" data-format-control="headings">
6751
+ <button type="button" class="format-menu-toggle" data-format-menu-toggle
6752
+ aria-label="Heading" aria-haspopup="menu" aria-expanded="false">
6753
+ ${HEADING_ICON_HTML}${CHEVRON_ICON_HTML}
6754
+ </button>
6755
+ <div class="format-menu-popover" data-format-menu role="menu" aria-label="Heading level" hidden>
6756
+ <button type="button" role="menuitem" data-format="paragraph">Paragraph</button>
6757
+ <button type="button" role="menuitem" data-format="heading-1" data-heading-level="1"><strong>H1</strong> Heading 1</button>
6758
+ <button type="button" role="menuitem" data-format="heading-2" data-heading-level="2"><strong>H2</strong> Heading 2</button>
6759
+ <button type="button" role="menuitem" data-format="heading-3" data-heading-level="3"><strong>H3</strong> Heading 3</button>
6760
+ <button type="button" role="menuitem" data-format="heading-4" data-heading-level="4"><strong>H4</strong> Heading 4</button>
6761
+ <button type="button" role="menuitem" data-format="heading-5" data-heading-level="5"><strong>H5</strong> Heading 5</button>
6762
+ <button type="button" role="menuitem" data-format="heading-6" data-heading-level="6"><strong>H6</strong> Heading 6</button>
6763
+ </div>
6764
+ </div>
6765
+ </div>
6766
+ <span class="edit-toolbar-divider" aria-hidden="true"></span>
6767
+ <div class="edit-toolbar-group">
6768
+ <button type="button" data-format="bullet-list" data-format-control="bulletList" title="Bulleted list" aria-label="Bulleted list">${BULLET_LIST_ICON_HTML}</button>
6769
+ <button type="button" data-format="ordered-list" data-format-control="orderedList" title="Numbered list" aria-label="Numbered list">${ORDERED_LIST_ICON_HTML}</button>
6770
+ <div class="format-menu" data-format-control="code">
6771
+ <button type="button" class="format-menu-toggle" data-format-menu-toggle
6772
+ aria-label="Code" aria-haspopup="menu" aria-expanded="false">
6773
+ ${CODE_ICON_HTML}${CHEVRON_ICON_HTML}
6774
+ </button>
6775
+ <div class="format-menu-popover" data-format-menu role="menu" aria-label="Code format" hidden>
6776
+ <button type="button" role="menuitem" data-format="code" data-code-kind="inline">Inline code</button>
6777
+ <button type="button" role="menuitem" data-format="code-block" data-code-kind="block">Code block</button>
6778
+ </div>
6779
+ </div>
6780
+ <button type="button" data-format="quote" data-format-control="blockquote" title="Blockquote" aria-label="Blockquote">${QUOTE_ICON_HTML}</button>
6781
+ <button type="button" data-format="line-break" data-format-control="lineBreak" title="Line break" aria-label="Line break">${LINE_BREAK_ICON_HTML}</button>
6782
+ </div>
6783
+ <span class="edit-toolbar-divider" aria-hidden="true"></span>
6784
+ <div class="edit-toolbar-group">
6785
+ <button type="button" data-format="link" data-format-control="link" title="Link" aria-label="Link">${LINK_ICON_HTML}</button>
6786
+ <button type="button" data-format="image" data-format-control="image" title="Image" aria-label="Image">${IMAGE_FORMAT_ICON_HTML}</button>
6787
+ <input type="file" data-ref="image-input" accept="image/*" hidden />
6788
+ </div>
6789
+ </div>
6790
+ </div>
6791
+
6792
+ <div class="toolbar-buttons view-mode-toggle-group" data-ref="layout-controls" role="group" aria-label="Editor layout">
6793
+ <button type="button" class="icon-toggle view-mode-toggle" data-ref="source-btn" title="Edit" aria-label="Edit" aria-pressed="false">
6794
+ <span class="commandbar-flex-container" data-ref="source-icon">${SOURCE_EDIT_ICON_HTML}</span>
6795
+ </button>
6796
+ <button type="button" class="icon-toggle view-mode-toggle" data-ref="split-btn" title="Split" aria-label="Split" aria-pressed="false">
6797
+ <span class="commandbar-flex-container">
6798
+ ${SPLIT_ICON_HTML}
6799
+ </span>
6800
+ </button>
6801
+ <button type="button" class="icon-toggle view-mode-toggle" data-ref="preview-btn" title="View" aria-label="View" aria-pressed="false">
6802
+ <span class="commandbar-flex-container">
6803
+ ${PREVIEW_ICON_HTML}
6804
+ </span>
6805
+ </button>
6806
+ </div>
6807
+
6808
+ <div class="toolbar-controls" data-ref="toolbar-controls">
6809
+ <button type="button" class="icon-toggle" data-ref="search-btn" title="Find/replace (Mod-F)" aria-label="Find/replace">
6810
+ ${SEARCH_ICON_HTML}
6811
+ </button>
6812
+ <button type="button" class="icon-toggle" data-ref="save-btn" title="Save" aria-label="Save">
6813
+ ${SAVE_ICON_HTML}
6814
+ </button>
6815
+ <button type="button" class="icon-toggle zoom-toggle" data-ref="zoom-btn" title="Zoom controls" aria-label="Zoom controls">
6816
+ ${ZOOM_ICON_HTML}
6817
+ </button>
6818
+ <div class="theme-toggle-group" data-ref="theme-controls" role="group" aria-label="Color scheme">
6819
+ <button type="button" class="icon-toggle theme-toggle" data-ref="dark-theme-btn"
6820
+ title="Dark mode" aria-label="Dark mode" aria-pressed="false">
6821
+ ${DARK_THEME_ICON_HTML}
6822
+ </button>
6823
+ <button type="button" class="icon-toggle theme-toggle" data-ref="light-theme-btn"
6824
+ title="Light mode" aria-label="Light mode" aria-pressed="false">
6825
+ ${LIGHT_THEME_ICON_HTML}
6826
+ </button>
6827
+ </div>
6828
+ <div class="zoom-popover" data-ref="zoom-popover" hidden role="group" aria-label="Zoom">
6829
+ <span class="zoom-level" data-ref="zoom-level">100%</span>
6830
+ <span class="zoom-stepper">
6831
+ <button type="button" data-action="zoom-out" title="Zoom out" aria-label="Zoom out">-</button>
6832
+ <button type="button" data-action="zoom-in" title="Zoom in" aria-label="Zoom in">+</button>
6833
+ </span>
6834
+ <button type="button" class="zoom-reset" data-action="zoom-reset" title="Reset zoom">Reset</button>
6835
+ </div>
6836
+ </div>
6837
+ </header>
6838
+
6839
+ <div class="workspace-shell" data-ref="workspace-shell" hidden>
6840
+ <aside class="nav-pane" data-ref="nav-pane" aria-label="Package contents">
6841
+ <div class="nav-tree" data-ref="nav-tree"></div>
6842
+ </aside>
6843
+ <div class="nav-resizer" data-ref="nav-resizer"
6844
+ role="separator" aria-orientation="vertical" aria-label="Resize contents pane"></div>
6845
+
6846
+ <div class="pane-stack" data-ref="pane-stack">
6847
+ <section class="pane edit-pane" data-ref="edit-pane">
6848
+ <div class="editor-host" data-ref="editor-host"></div>
6849
+ </section>
6850
+ <div class="split-resizer" data-ref="split-resizer"
6851
+ role="separator" aria-orientation="vertical" aria-label="Resize split panes"></div>
6852
+ <section class="pane preview-pane" data-ref="preview-pane" tabindex="-1">
6853
+ <article class="preview-content" data-ref="preview-content"></article>
6854
+ </section>
6855
+ <section class="pane entry-pane" data-ref="entry-pane"></section>
6856
+ </div>
6857
+ </div>
6858
+
6859
+ <div class="title-dialog-backdrop" data-ref="title-dialog" hidden
6860
+ role="dialog" aria-modal="true" aria-labelledby="mdzip-title-dialog-heading">
6861
+ <div class="title-dialog">
6862
+ <h3 id="mdzip-title-dialog-heading">Set Document Title</h3>
6863
+ <p>This is the package-level title stored in manifest.json.</p>
6864
+ <input type="text" maxlength="120" data-ref="title-input" aria-label="Document title" />
6865
+ <p class="title-dialog-validation" data-ref="title-validation" hidden>Title cannot be empty.</p>
6866
+ <p>If unset, consumers may fall back to entry point, filename, or first heading.</p>
6867
+ <div class="title-dialog-actions">
6868
+ <button type="button" class="reset-title" data-ref="title-reset-btn">Reset</button>
6869
+ <button type="button" data-action="cancel-title">Cancel</button>
6870
+ <button type="button" class="save-title" data-ref="title-save-btn">Save</button>
6871
+ </div>
6872
+ </div>
6873
+ </div>
6874
+
6875
+ <div class="title-dialog-backdrop" data-ref="conversion-dialog" hidden
6876
+ role="dialog" aria-modal="true" aria-labelledby="mdzip-conversion-dialog-heading">
6877
+ <div class="title-dialog">
6878
+ <h3 id="mdzip-conversion-dialog-heading">Convert to MDZ?</h3>
6879
+ <p>
6880
+ Regular Markdown files contain only text. Convert this document to an
6881
+ MDZ package to add images, package navigation, and document metadata.
6882
+ </p>
6883
+ <p>The next save will produce an .mdz file.</p>
6884
+ <div class="title-dialog-actions">
6885
+ <button type="button" data-action="cancel-conversion">Cancel</button>
6886
+ <button type="button" class="save-title" data-ref="conversion-confirm-btn">Convert</button>
6887
+ </div>
6888
+ </div>
6889
+ </div>
6890
+
6891
+ <div class="title-dialog-backdrop" data-ref="image-insert-dialog" hidden
6892
+ role="dialog" aria-modal="true" aria-labelledby="mdzip-image-insert-dialog-heading">
6893
+ <div class="title-dialog image-insert-dialog">
6894
+ <h3 id="mdzip-image-insert-dialog-heading">Insert Image</h3>
6895
+ <p>Choose the Markdown inserted for this image.</p>
6896
+ <fieldset class="image-insert-options">
6897
+ <legend>Output</legend>
6898
+ <label>
6899
+ <input type="radio" name="mdzip-image-insert-mode" value="markdown"
6900
+ data-ref="image-insert-mode-markdown" checked />
6901
+ Markdown image
6902
+ </label>
6903
+ <label>
6904
+ <input type="radio" name="mdzip-image-insert-mode" value="html"
6905
+ data-ref="image-insert-mode-html" />
6906
+ HTML &lt;img&gt; with sizing
6907
+ </label>
6908
+ </fieldset>
6909
+ <label class="image-insert-field">
6910
+ <span>Alt text</span>
6911
+ <input type="text" data-ref="image-insert-alt" />
6912
+ </label>
6913
+ <div class="image-insert-grid">
6914
+ <label class="image-insert-field">
6915
+ <span>Size by</span>
6916
+ <select data-ref="image-insert-size-mode">
6917
+ <option value="original">Original size</option>
6918
+ <option value="width" selected>Width</option>
6919
+ <option value="height">Height</option>
6920
+ <option value="percent">Percent</option>
6921
+ </select>
6922
+ </label>
6923
+ <label class="image-insert-field">
6924
+ <span>Value</span>
6925
+ <input type="number" min="1" step="1" data-ref="image-insert-size-value" />
6926
+ </label>
6927
+ </div>
6928
+ <label class="image-insert-field">
6929
+ <span>Position</span>
6930
+ <select data-ref="image-insert-position">
6931
+ <option value="inline">Inline/default</option>
6932
+ <option value="left">Left</option>
6933
+ <option value="center">Center</option>
6934
+ <option value="right">Right</option>
6935
+ <option value="wrap-left">Wrap left</option>
6936
+ <option value="wrap-right">Wrap right</option>
6937
+ </select>
6938
+ </label>
6939
+ <div class="title-dialog-actions">
6940
+ <button type="button" data-action="cancel-image-insert">Cancel</button>
6941
+ <button type="button" class="save-title" data-ref="image-insert-confirm-btn">Insert</button>
6942
+ </div>
6943
+ </div>
6944
+ </div>
6945
+
6946
+ <div class="title-dialog-backdrop" data-ref="pack-files-dialog" hidden
6947
+ role="dialog" aria-modal="true" aria-labelledby="mdzip-pack-files-dialog-heading">
6948
+ <div class="title-dialog pack-files-dialog">
6949
+ <h3 id="mdzip-pack-files-dialog-heading">Package Markdown Files</h3>
6950
+ <p>These files contain more than one Markdown document. Choose how to package them.</p>
6951
+ <fieldset class="pack-files-options">
6952
+ <legend>Mode</legend>
6953
+ <label>
6954
+ <input type="radio" name="mdzip-pack-files-mode" value="document"
6955
+ data-ref="pack-files-mode-document" checked />
6956
+ Document — one primary page; the rest are attached
6957
+ </label>
6958
+ <label>
6959
+ <input type="radio" name="mdzip-pack-files-mode" value="project"
6960
+ data-ref="pack-files-mode-project" />
6961
+ Project — a set of related documents
6962
+ </label>
6963
+ </fieldset>
6964
+ <label class="pack-files-field">
6965
+ <span>Entry point</span>
6966
+ <select data-ref="pack-files-entry"></select>
6967
+ </label>
6968
+ <div class="title-dialog-actions">
6969
+ <button type="button" data-action="cancel-pack-files">Cancel</button>
6970
+ <button type="button" class="save-title" data-ref="pack-files-confirm-btn">Package</button>
6971
+ </div>
6972
+ </div>
6973
+ </div>
6974
+
6975
+ <div class="title-dialog-backdrop" data-ref="copy-render-dialog" hidden
6976
+ role="dialog" aria-modal="true" aria-labelledby="mdzip-copy-render-dialog-heading">
6977
+ <div class="title-dialog copy-render-dialog">
6978
+ <h3 id="mdzip-copy-render-dialog-heading" data-ref="copy-render-heading">Copying document...</h3>
6979
+ <div data-ref="copy-render-progress-section">
6980
+ <p data-ref="copy-render-progress-text">Rendering the full document...</p>
6981
+ <div class="copy-render-progress-track">
6982
+ <div class="copy-render-progress-bar" data-ref="copy-render-progress-bar"></div>
6983
+ </div>
6984
+ </div>
6985
+ <p data-ref="copy-render-ready-text" hidden>
6986
+ The document is ready. Browsers only allow a clipboard write right after a click, so this needs one more —
6987
+ click Copy to finish.
6988
+ </p>
6989
+ <p data-ref="copy-render-done-text" hidden></p>
6990
+ <div class="title-dialog-actions">
6991
+ <button type="button" data-action="cancel-copy-render" data-ref="copy-render-cancel-btn">Cancel</button>
6992
+ <button type="button" data-action="cancel-copy-ready" data-ref="copy-render-ready-cancel-btn" hidden>Cancel</button>
6993
+ <button type="button" class="save-title" data-action="confirm-copy-ready" data-ref="copy-render-confirm-btn" hidden>Copy</button>
6994
+ <button type="button" class="save-title" data-action="dismiss-copy-render" data-ref="copy-render-dismiss-btn" hidden>Dismiss</button>
6995
+ </div>
6996
+ </div>
6997
+ </div>
6998
+
6999
+ <div class="title-dialog-backdrop" data-ref="metadata-dialog" hidden
7000
+ role="dialog" aria-modal="true" aria-labelledby="mdzip-metadata-dialog-heading">
7001
+ <div class="title-dialog metadata-dialog">
7002
+ <h3 id="mdzip-metadata-dialog-heading">Document Information</h3>
7003
+ <dl data-ref="metadata-list"></dl>
7004
+ <h4>Libraries</h4>
7005
+ <dl data-ref="library-list"></dl>
7006
+ <div class="title-dialog-actions">
7007
+ <button type="button" class="save-title" data-action="close-metadata">Close</button>
7008
+ </div>
7009
+ </div>
7010
+ </div>
7011
+
7012
+ <div class="title-dialog-backdrop" data-ref="name-dialog" hidden
7013
+ role="dialog" aria-modal="true" aria-labelledby="mdzip-name-dialog-heading">
7014
+ <div class="title-dialog">
7015
+ <h3 id="mdzip-name-dialog-heading" data-ref="name-dialog-heading">New Markdown File</h3>
7016
+ <input type="text" maxlength="260" data-ref="name-input" aria-label="File name" />
7017
+ <p class="title-dialog-validation" data-ref="name-validation" hidden></p>
7018
+ <div class="title-dialog-actions">
7019
+ <button type="button" data-action="cancel-name">Cancel</button>
7020
+ <button type="button" class="save-title" data-ref="name-confirm-btn">Create</button>
7021
+ </div>
7022
+ </div>
7023
+ </div>
7024
+
7025
+ <div class="title-dialog-backdrop" data-ref="delete-dialog" hidden
7026
+ role="dialog" aria-modal="true" aria-labelledby="mdzip-delete-dialog-heading">
7027
+ <div class="title-dialog">
7028
+ <h3 id="mdzip-delete-dialog-heading">Delete File?</h3>
7029
+ <p data-ref="delete-dialog-text"></p>
7030
+ <div class="title-dialog-actions">
7031
+ <button type="button" data-action="cancel-delete">Cancel</button>
7032
+ <button type="button" class="danger-action" data-ref="delete-confirm-btn">Delete</button>
7033
+ </div>
7034
+ </div>
7035
+ </div>
7036
+
7037
+ <input type="file" data-ref="replace-input" hidden />
7038
+
7039
+ <div class="nav-context-menu" data-ref="nav-menu" hidden role="menu"></div>
7040
+
7041
+ <div class="mdzip-tooltip" data-ref="tooltip" role="tooltip" hidden></div>
7042
+
7043
+ <div class="mdzip-copy-toast" data-ref="copy-toast" role="status" aria-live="polite" hidden></div>
7044
+
7045
+ <p class="mdzip-empty" data-ref="empty-state">No MDZip workspace loaded.</p>
7046
+ </section>
6910
7047
  `;
6911
7048
  //# sourceMappingURL=view.js.map