@mdzip/editor 1.3.13 → 1.3.15
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/library-info.d.ts +2 -2
- package/dist/library-info.js +2 -2
- package/dist/mermaid.d.ts.map +1 -1
- package/dist/mermaid.js +26 -2
- package/dist/mermaid.js.map +1 -1
- package/dist/view-css.d.ts.map +1 -1
- package/dist/view-css.js +26 -3
- package/dist/view-css.js.map +1 -1
- package/dist/view.d.ts +14 -0
- package/dist/view.d.ts.map +1 -1
- package/dist/view.js +196 -11
- package/dist/view.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +19 -6
- package/dist/workspace.js.map +1 -1
- package/package.json +3 -2
package/dist/view.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands';
|
|
2
2
|
import { markdown } from '@codemirror/lang-markdown';
|
|
3
3
|
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
4
|
+
import { closeSearchPanel, openSearchPanel, search, searchKeymap } from '@codemirror/search';
|
|
4
5
|
import { Compartment, EditorState } from '@codemirror/state';
|
|
5
6
|
import { Decoration, EditorView, MatchDecorator, ViewPlugin, dropCursor, keymap, lineNumbers } from '@codemirror/view';
|
|
6
7
|
import { tags } from '@lezer/highlight';
|
|
7
|
-
import { Bold, ChevronDown, ChevronRight, ClipboardPaste, ClipboardType, Code, Columns2, Copy, Eraser, Eye, File, FileBraces, FileImage, Folder, FolderOpen, Hash, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, Highlighter, ImagePlus, Info, Italic, Link2Off, Link, List, ListOrdered, CornerDownLeft, Moon, PackagePlus, PanelLeft, Pilcrow, Quote, Save, Scissors, SquareCode, SquarePen, Strikethrough, Sun, TextSelect, ZoomIn } from 'lucide';
|
|
8
|
+
import { Bold, ChevronDown, ChevronRight, ClipboardPaste, ClipboardType, Code, Columns2, Copy, Eraser, Eye, File, FileBraces, FileImage, Folder, FolderOpen, Hash, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, Highlighter, ImagePlus, Info, Italic, Link2Off, Link, List, ListOrdered, CornerDownLeft, Moon, PackagePlus, PanelLeft, Pilcrow, Quote, Save, Scissors, Search, SquareCode, SquarePen, Strikethrough, Sun, TextSelect, ZoomIn } from 'lucide';
|
|
8
9
|
import { browserClipboardHasImage, readBrowserClipboardImage } from './browser.js';
|
|
9
10
|
import { MdzipAssetSession, mdzipArchiveSourceId, sniffImageSize } from './asset-cache.js';
|
|
10
11
|
import { MD_MARKDOWN_ICON } from './icons/md-markdown.js';
|
|
@@ -35,6 +36,7 @@ const CONVERT_TO_MDZ_ICON_HTML = lucideIcon(PackagePlus, `${TOOLBAR_ICON_CLASS}
|
|
|
35
36
|
const PREVIEW_ICON_HTML = lucideIcon(Eye, TOOLBAR_ICON_CLASS);
|
|
36
37
|
const SPLIT_ICON_HTML = lucideIcon(Columns2, TOOLBAR_ICON_CLASS);
|
|
37
38
|
const SAVE_ICON_HTML = lucideIcon(Save, TOOLBAR_ICON_CLASS);
|
|
39
|
+
const SEARCH_ICON_HTML = lucideIcon(Search, TOOLBAR_ICON_CLASS);
|
|
38
40
|
const ZOOM_ICON_HTML = lucideIcon(ZoomIn, TOOLBAR_ICON_CLASS);
|
|
39
41
|
const DARK_THEME_ICON_HTML = lucideIcon(Moon, TOOLBAR_ICON_CLASS);
|
|
40
42
|
const LIGHT_THEME_ICON_HTML = lucideIcon(Sun, TOOLBAR_ICON_CLASS);
|
|
@@ -179,7 +181,8 @@ const CONTROL_PRESETS = {
|
|
|
179
181
|
zoom: false,
|
|
180
182
|
colorScheme: false,
|
|
181
183
|
orphanActions: false,
|
|
182
|
-
fileActions: false
|
|
184
|
+
fileActions: false,
|
|
185
|
+
search: false
|
|
183
186
|
},
|
|
184
187
|
viewer: {
|
|
185
188
|
preset: 'viewer',
|
|
@@ -193,7 +196,8 @@ const CONTROL_PRESETS = {
|
|
|
193
196
|
zoom: true,
|
|
194
197
|
colorScheme: true,
|
|
195
198
|
orphanActions: false,
|
|
196
|
-
fileActions: false
|
|
199
|
+
fileActions: false,
|
|
200
|
+
search: true
|
|
197
201
|
},
|
|
198
202
|
'standalone-editor': {
|
|
199
203
|
preset: 'standalone-editor',
|
|
@@ -207,7 +211,8 @@ const CONTROL_PRESETS = {
|
|
|
207
211
|
zoom: true,
|
|
208
212
|
colorScheme: true,
|
|
209
213
|
orphanActions: true,
|
|
210
|
-
fileActions: true
|
|
214
|
+
fileActions: true,
|
|
215
|
+
search: true
|
|
211
216
|
},
|
|
212
217
|
'hosted-editor': {
|
|
213
218
|
preset: 'hosted-editor',
|
|
@@ -221,7 +226,8 @@ const CONTROL_PRESETS = {
|
|
|
221
226
|
zoom: true,
|
|
222
227
|
colorScheme: true,
|
|
223
228
|
orphanActions: true,
|
|
224
|
-
fileActions: true
|
|
229
|
+
fileActions: true,
|
|
230
|
+
search: true
|
|
225
231
|
}
|
|
226
232
|
};
|
|
227
233
|
export function resolveMdzipControlPolicy(controls) {
|
|
@@ -367,6 +373,73 @@ const mdzipEditorTheme = EditorView.theme({
|
|
|
367
373
|
color: 'var(--mdzip-muted-foreground-color)',
|
|
368
374
|
opacity: '0.65',
|
|
369
375
|
},
|
|
376
|
+
'.cm-panels': {
|
|
377
|
+
background: 'var(--mdzip-widget-background-color)',
|
|
378
|
+
color: 'var(--mdzip-editor-foreground-color)',
|
|
379
|
+
zIndex: '2',
|
|
380
|
+
},
|
|
381
|
+
'.cm-panels.cm-panels-top': {
|
|
382
|
+
borderBottom: '1px solid var(--mdzip-border-color)',
|
|
383
|
+
},
|
|
384
|
+
'.cm-search': {
|
|
385
|
+
display: 'flex',
|
|
386
|
+
flexWrap: 'wrap',
|
|
387
|
+
alignItems: 'center',
|
|
388
|
+
gap: '8px',
|
|
389
|
+
padding: '10px 14px',
|
|
390
|
+
fontSize: '13px',
|
|
391
|
+
},
|
|
392
|
+
// CodeMirror separates the find and replace rows with a bare <br>; in a
|
|
393
|
+
// flex container that collapses to zero width instead of breaking the
|
|
394
|
+
// line, so force it to take the full row.
|
|
395
|
+
'.cm-search br': {
|
|
396
|
+
flexBasis: '100%',
|
|
397
|
+
height: '0',
|
|
398
|
+
},
|
|
399
|
+
'.cm-search label': {
|
|
400
|
+
display: 'inline-flex',
|
|
401
|
+
alignItems: 'center',
|
|
402
|
+
gap: '5px',
|
|
403
|
+
color: 'var(--mdzip-muted-foreground-color)',
|
|
404
|
+
},
|
|
405
|
+
'.cm-search input[type="checkbox"]': {
|
|
406
|
+
width: '14px',
|
|
407
|
+
height: '14px',
|
|
408
|
+
accentColor: 'var(--mdzip-accent-color)',
|
|
409
|
+
},
|
|
410
|
+
'.cm-textfield': {
|
|
411
|
+
background: 'var(--mdzip-editor-background-color)',
|
|
412
|
+
color: 'var(--mdzip-editor-foreground-color)',
|
|
413
|
+
border: '1px solid var(--mdzip-border-color)',
|
|
414
|
+
borderRadius: '5px',
|
|
415
|
+
padding: '5px 9px',
|
|
416
|
+
fontSize: 'inherit',
|
|
417
|
+
width: '220px',
|
|
418
|
+
},
|
|
419
|
+
'.cm-textfield:focus-visible': {
|
|
420
|
+
outline: '1px solid var(--mdzip-focus-outline-color)',
|
|
421
|
+
outlineOffset: '-1px',
|
|
422
|
+
},
|
|
423
|
+
'.cm-button': {
|
|
424
|
+
background: 'var(--mdzip-widget-background-color)',
|
|
425
|
+
backgroundImage: 'none',
|
|
426
|
+
color: 'var(--mdzip-control-foreground-color)',
|
|
427
|
+
border: '1px solid var(--mdzip-border-color)',
|
|
428
|
+
borderRadius: '5px',
|
|
429
|
+
padding: '5px 12px',
|
|
430
|
+
fontSize: 'inherit',
|
|
431
|
+
cursor: 'pointer',
|
|
432
|
+
},
|
|
433
|
+
'.cm-button:hover': {
|
|
434
|
+
background: 'var(--mdzip-control-hover-background-color)',
|
|
435
|
+
},
|
|
436
|
+
'.cm-searchMatch': {
|
|
437
|
+
backgroundColor: 'rgba(255, 214, 0, 0.35)',
|
|
438
|
+
},
|
|
439
|
+
'.cm-searchMatch-selected': {
|
|
440
|
+
backgroundColor: 'var(--mdzip-accent-color)',
|
|
441
|
+
color: 'var(--mdzip-accent-foreground-color)',
|
|
442
|
+
},
|
|
370
443
|
});
|
|
371
444
|
const mdzipMarkdownHighlight = HighlightStyle.define([
|
|
372
445
|
{ tag: [tags.heading1, tags.heading2, tags.heading3, tags.heading4, tags.heading5, tags.heading6],
|
|
@@ -398,6 +471,27 @@ const hardBreakMarkerHighlight = ViewPlugin.fromClass(class {
|
|
|
398
471
|
}, {
|
|
399
472
|
decorations: value => value.decorations
|
|
400
473
|
});
|
|
474
|
+
// Matches raw HTML tags other than <br> (which hardBreakMarkerMatcher already
|
|
475
|
+
// covers) so authors can visually distinguish raw HTML from Markdown prose.
|
|
476
|
+
// Excludes autolinks like <https://example.com> — those have no space before
|
|
477
|
+
// '>' and no closing '/', so the tag-name-only branch requires the char after
|
|
478
|
+
// the name to be '>' directly, which a URL's ':' never satisfies.
|
|
479
|
+
const htmlTagMarkerMatcher = new MatchDecorator({
|
|
480
|
+
regexp: /<\/?(?!br\b)[a-zA-Z][a-zA-Z0-9-]*(?:\s[^<>]*)?\/?>/gi,
|
|
481
|
+
decoration: Decoration.mark({ class: 'mdzip-hard-break-marker' })
|
|
482
|
+
});
|
|
483
|
+
const htmlTagMarkerHighlight = ViewPlugin.fromClass(class {
|
|
484
|
+
constructor(view) {
|
|
485
|
+
this.decorations = htmlTagMarkerMatcher.createDeco(view);
|
|
486
|
+
}
|
|
487
|
+
update(update) {
|
|
488
|
+
if (update.docChanged || update.viewportChanged) {
|
|
489
|
+
this.decorations = htmlTagMarkerMatcher.updateDeco(update, this.decorations);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}, {
|
|
493
|
+
decorations: value => value.decorations
|
|
494
|
+
});
|
|
401
495
|
function injectStyles(doc) {
|
|
402
496
|
const existing = doc.querySelector(`style[${STYLE_ATTR}]`);
|
|
403
497
|
if (existing) {
|
|
@@ -532,6 +626,35 @@ function navNodeOrder(a, b) {
|
|
|
532
626
|
return 1;
|
|
533
627
|
return a.name.localeCompare(b.name);
|
|
534
628
|
}
|
|
629
|
+
// Browsers apply raw HTML width/height attributes as presentational sizing
|
|
630
|
+
// hints, but the preview's `img { height: auto }` rule (for responsive
|
|
631
|
+
// scaling) overrides that hint via the normal CSS cascade — so an author's
|
|
632
|
+
// `<img height="300">` was silently ignored. Re-applying numeric, unitless
|
|
633
|
+
// width/height attributes as inline pixel styles restores that sizing
|
|
634
|
+
// without touching an author-supplied inline style.
|
|
635
|
+
function applyRawHtmlImageSizeAttributes(image) {
|
|
636
|
+
const width = image.getAttribute('width');
|
|
637
|
+
if (width && /^\d+$/.test(width) && !image.style.width) {
|
|
638
|
+
image.style.width = `${width}px`;
|
|
639
|
+
}
|
|
640
|
+
const height = image.getAttribute('height');
|
|
641
|
+
if (height && /^\d+$/.test(height) && !image.style.height) {
|
|
642
|
+
image.style.height = `${height}px`;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// Maps the legacy raw HTML `align="left"|"right"` attribute (or the editor's
|
|
646
|
+
// own wrap classes) to a layout direction, for images that skip archive
|
|
647
|
+
// hydration — external, data, and fragment sources — and so never get a slot.
|
|
648
|
+
function rawHtmlImageAlignClass(image) {
|
|
649
|
+
const align = image.getAttribute('align')?.toLowerCase();
|
|
650
|
+
if (align === 'left' || image.classList.contains('mdzip-image-wrap-left')) {
|
|
651
|
+
return 'mdzip-image-left';
|
|
652
|
+
}
|
|
653
|
+
if (align === 'right' || image.classList.contains('mdzip-image-wrap-right')) {
|
|
654
|
+
return 'mdzip-image-right';
|
|
655
|
+
}
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
535
658
|
export class MdzipWorkspaceView {
|
|
536
659
|
constructor(container, options = {}) {
|
|
537
660
|
this.workspace = null;
|
|
@@ -574,6 +697,7 @@ export class MdzipWorkspaceView {
|
|
|
574
697
|
this.cmEditor = null;
|
|
575
698
|
this.readOnlyCompartment = new Compartment();
|
|
576
699
|
this.lineNumbersCompartment = new Compartment();
|
|
700
|
+
this.searchCompartment = new Compartment();
|
|
577
701
|
this.updatingCm = false;
|
|
578
702
|
this.syncing = false;
|
|
579
703
|
this.markdownExtensions = [];
|
|
@@ -630,6 +754,7 @@ export class MdzipWorkspaceView {
|
|
|
630
754
|
this.elSourceBtn = q('[data-ref="source-btn"]');
|
|
631
755
|
this.elSourceIcon = q('[data-ref="source-icon"]');
|
|
632
756
|
this.elSaveBtn = q('[data-ref="save-btn"]');
|
|
757
|
+
this.elSearchBtn = q('[data-ref="search-btn"]');
|
|
633
758
|
this.elZoomBtn = q('[data-ref="zoom-btn"]');
|
|
634
759
|
this.elThemeControls = q('[data-ref="theme-controls"]');
|
|
635
760
|
this.elDarkThemeBtn = q('[data-ref="dark-theme-btn"]');
|
|
@@ -907,6 +1032,40 @@ export class MdzipWorkspaceView {
|
|
|
907
1032
|
this.applyMarkdownFormat(command);
|
|
908
1033
|
return true;
|
|
909
1034
|
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Opens CodeMirror's find/replace panel for the current document. Unlike
|
|
1037
|
+
* {@link executeCommand}, this works in read-only (Viewer) hosts too —
|
|
1038
|
+
* searching doesn't require edit access, only a visible source pane. If
|
|
1039
|
+
* the current layout is preview-only, switches to split/source first so
|
|
1040
|
+
* the panel has somewhere to render.
|
|
1041
|
+
*/
|
|
1042
|
+
async openSearch() {
|
|
1043
|
+
if (!this.controlPolicy.search) {
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
const snapshot = this.workspace?.snapshot();
|
|
1047
|
+
if (!snapshot || snapshot.currentPathType !== 'markdown') {
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
if (this.layout === 'preview') {
|
|
1051
|
+
const nextLayout = this.controlPolicy.layout.split
|
|
1052
|
+
? 'split'
|
|
1053
|
+
: this.controlPolicy.layout.source ? 'source' : null;
|
|
1054
|
+
if (!nextLayout) {
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
await this.setLayout(nextLayout);
|
|
1058
|
+
}
|
|
1059
|
+
const editor = await this.ensureCmEditor(true);
|
|
1060
|
+
if (!editor) {
|
|
1061
|
+
return false;
|
|
1062
|
+
}
|
|
1063
|
+
editor.focus();
|
|
1064
|
+
return openSearchPanel(editor);
|
|
1065
|
+
}
|
|
1066
|
+
closeSearch() {
|
|
1067
|
+
return this.cmEditor ? closeSearchPanel(this.cmEditor) : false;
|
|
1068
|
+
}
|
|
910
1069
|
async convertToMdz() {
|
|
911
1070
|
if (!this.workspace || this.workspace.mode === 'read-only') {
|
|
912
1071
|
return false;
|
|
@@ -982,12 +1141,21 @@ export class MdzipWorkspaceView {
|
|
|
982
1141
|
setControls(controls) {
|
|
983
1142
|
const next = resolveMdzipControlPolicy(controls);
|
|
984
1143
|
const lineNumbersChanged = next.lineNumbers !== this.controlPolicy.lineNumbers;
|
|
1144
|
+
const searchChanged = next.search !== this.controlPolicy.search;
|
|
985
1145
|
this.controlPolicy = next;
|
|
986
1146
|
if (lineNumbersChanged && this.cmEditor) {
|
|
987
1147
|
this.cmEditor.dispatch({
|
|
988
1148
|
effects: this.lineNumbersCompartment.reconfigure(next.lineNumbers ? lineNumbers() : [])
|
|
989
1149
|
});
|
|
990
1150
|
}
|
|
1151
|
+
if (searchChanged && this.cmEditor) {
|
|
1152
|
+
if (!next.search) {
|
|
1153
|
+
closeSearchPanel(this.cmEditor);
|
|
1154
|
+
}
|
|
1155
|
+
this.cmEditor.dispatch({
|
|
1156
|
+
effects: this.searchCompartment.reconfigure(next.search ? [search({ top: true }), keymap.of(searchKeymap)] : [])
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
991
1159
|
const snapshot = this.workspace?.snapshot();
|
|
992
1160
|
if (snapshot) {
|
|
993
1161
|
this.layout = this.validLayoutForSnapshot(this.layout, snapshot);
|
|
@@ -1186,9 +1354,14 @@ export class MdzipWorkspaceView {
|
|
|
1186
1354
|
const document = this.elPreviewContent.ownerDocument;
|
|
1187
1355
|
const pending = [];
|
|
1188
1356
|
for (const image of Array.from(this.elPreviewContent.querySelectorAll('img'))) {
|
|
1357
|
+
applyRawHtmlImageSizeAttributes(image);
|
|
1189
1358
|
const source = image.getAttribute('src');
|
|
1190
1359
|
// Leave external, protocol-relative, data, and fragment URLs untouched.
|
|
1191
1360
|
if (!source || /^(?:[a-z][a-z\d+.-]*:|\/\/|#)/i.test(source)) {
|
|
1361
|
+
const alignClass = rawHtmlImageAlignClass(image);
|
|
1362
|
+
if (alignClass) {
|
|
1363
|
+
image.classList.add(alignClass);
|
|
1364
|
+
}
|
|
1192
1365
|
continue;
|
|
1193
1366
|
}
|
|
1194
1367
|
// Drop the archive-relative src so the browser does not fetch the bad
|
|
@@ -1203,11 +1376,11 @@ export class MdzipWorkspaceView {
|
|
|
1203
1376
|
slot.className = animateImageHydration
|
|
1204
1377
|
? 'mdzip-image-slot'
|
|
1205
1378
|
: 'mdzip-image-slot mdzip-image-open mdzip-image-animation-off';
|
|
1206
|
-
const
|
|
1207
|
-
if (
|
|
1379
|
+
const alignClass = rawHtmlImageAlignClass(image);
|
|
1380
|
+
if (alignClass === 'mdzip-image-left') {
|
|
1208
1381
|
slot.classList.add('mdzip-image-align-left');
|
|
1209
1382
|
}
|
|
1210
|
-
else if (
|
|
1383
|
+
else if (alignClass === 'mdzip-image-right') {
|
|
1211
1384
|
slot.classList.add('mdzip-image-align-right');
|
|
1212
1385
|
}
|
|
1213
1386
|
image.parentNode?.insertBefore(slot, image);
|
|
@@ -1579,6 +1752,7 @@ export class MdzipWorkspaceView {
|
|
|
1579
1752
|
doc: initialText,
|
|
1580
1753
|
extensions: [
|
|
1581
1754
|
this.lineNumbersCompartment.of(this.controlPolicy.lineNumbers ? lineNumbers() : []),
|
|
1755
|
+
this.searchCompartment.of(this.controlPolicy.search ? [search({ top: true }), keymap.of(searchKeymap)] : []),
|
|
1582
1756
|
history(),
|
|
1583
1757
|
keymap.of([
|
|
1584
1758
|
{ key: 'Mod-b', run: () => self.runFormatShortcut('bold') },
|
|
@@ -1589,6 +1763,7 @@ export class MdzipWorkspaceView {
|
|
|
1589
1763
|
markdown(),
|
|
1590
1764
|
syntaxHighlighting(mdzipMarkdownHighlight),
|
|
1591
1765
|
hardBreakMarkerHighlight,
|
|
1766
|
+
htmlTagMarkerHighlight,
|
|
1592
1767
|
EditorView.lineWrapping,
|
|
1593
1768
|
dropCursor(),
|
|
1594
1769
|
mdzipEditorTheme,
|
|
@@ -1682,24 +1857,28 @@ export class MdzipWorkspaceView {
|
|
|
1682
1857
|
const showSaveControl = this.controlPolicy.save && snapshot.mode !== 'read-only';
|
|
1683
1858
|
const showZoomControl = this.controlPolicy.zoom;
|
|
1684
1859
|
const showColorSchemeControl = this.controlPolicy.colorScheme;
|
|
1860
|
+
const showSearchControl = this.controlPolicy.search
|
|
1861
|
+
&& canShowSource && snapshot.currentPathType === 'markdown';
|
|
1685
1862
|
const showEditControls = canEdit
|
|
1686
1863
|
&& snapshot.currentPathType === 'markdown'
|
|
1687
1864
|
&& this.layout !== 'preview'
|
|
1688
1865
|
&& hasFormattingControls(this.controlPolicy.formatting);
|
|
1689
1866
|
const showToolbar = this.controlPolicy.toolbar
|
|
1690
|
-
&& (showNavigationControl || showLayoutControls
|
|
1691
|
-
||
|
|
1867
|
+
&& (showNavigationControl || showLayoutControls || showSaveControl
|
|
1868
|
+
|| showZoomControl || showColorSchemeControl || showSearchControl || showEditControls);
|
|
1692
1869
|
this.elDocumentStrip.hidden = !showTitleControl;
|
|
1693
1870
|
this.elToolbar.hidden = !showToolbar;
|
|
1694
1871
|
this.elToolbarLeft.hidden = !showNavigationControl;
|
|
1695
1872
|
this.elEditToolbar.hidden = !showEditControls;
|
|
1696
1873
|
this.elLayoutControls.hidden = !showLayoutControls;
|
|
1697
|
-
this.elToolbarControls.hidden = !showSaveControl && !showZoomControl
|
|
1874
|
+
this.elToolbarControls.hidden = !showSaveControl && !showZoomControl
|
|
1875
|
+
&& !showColorSchemeControl && !showSearchControl;
|
|
1698
1876
|
this.elNavBtn.hidden = !showNavigationControl;
|
|
1699
1877
|
this.elPreviewBtn.hidden = !this.controlPolicy.layout.preview;
|
|
1700
1878
|
this.elSplitBtn.hidden = !this.controlPolicy.layout.split;
|
|
1701
1879
|
this.elSourceBtn.hidden = !this.controlPolicy.layout.source;
|
|
1702
1880
|
this.elSaveBtn.hidden = !showSaveControl;
|
|
1881
|
+
this.elSearchBtn.hidden = !showSearchControl;
|
|
1703
1882
|
this.elZoomBtn.hidden = !showZoomControl;
|
|
1704
1883
|
this.elThemeControls.hidden = !showColorSchemeControl;
|
|
1705
1884
|
this.elRoot.style.setProperty('--mdz-zoom', String(this.zoom));
|
|
@@ -1984,6 +2163,9 @@ export class MdzipWorkspaceView {
|
|
|
1984
2163
|
void this.save();
|
|
1985
2164
|
}
|
|
1986
2165
|
});
|
|
2166
|
+
this.elSearchBtn.addEventListener('click', () => {
|
|
2167
|
+
void this.openSearch();
|
|
2168
|
+
});
|
|
1987
2169
|
this.elZoomBtn.addEventListener('click', (e) => {
|
|
1988
2170
|
if (!this.controlPolicy.zoom) {
|
|
1989
2171
|
return;
|
|
@@ -4199,6 +4381,9 @@ const SHELL_HTML = `
|
|
|
4199
4381
|
</div>
|
|
4200
4382
|
|
|
4201
4383
|
<div class="toolbar-controls" data-ref="toolbar-controls">
|
|
4384
|
+
<button type="button" class="icon-toggle" data-ref="search-btn" title="Find/replace (Mod-F)" aria-label="Find/replace">
|
|
4385
|
+
${SEARCH_ICON_HTML}
|
|
4386
|
+
</button>
|
|
4202
4387
|
<button type="button" class="icon-toggle" data-ref="save-btn" title="Save" aria-label="Save">
|
|
4203
4388
|
${SAVE_ICON_HTML}
|
|
4204
4389
|
</button>
|