markupeditor 0.9.26 → 0.9.28
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/markup-editor.js +584 -318
- package/package.json +1 -1
- package/styles/markup.css +9 -8
- package/styles/toolbar.css +17 -7
package/dist/markup-editor.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const sheet$4 = new CSSStyleSheet();sheet$4.replaceSync("/**\n The CSS here was adapted from the ProseMirror example.\n The adaptations mainly consisted of removing styling when it interfered with\n markup.css in some way. These changes were commented-out and moved to the end of\n this file. Top-level Prosemirror styling was brought to the top of the file.\n Styling from search.css in prosemirror-search were added and modified.\n */\n\n .ProseMirror {\n position: relative;\n padding: 4px 8px 4px 8px;\n line-height: 1.2;\n outline: none;\n word-wrap: break-word;\n white-space: pre-wrap;\n white-space: break-spaces;\n font-variant-ligatures: none;\n }\n \n .ProseMirror pre {\n white-space: pre-wrap;\n }\n \n .ProseMirror li {\n position: relative;\n }\n \n .ProseMirror-hideselection *::selection { background: transparent; }\n .ProseMirror-hideselection *::-moz-selection { background: transparent; }\n .ProseMirror-hideselection { caret-color: transparent; }\n \n /* See https://github.com/ProseMirror/prosemirror/issues/1421#issuecomment-1759320191 */\n .ProseMirror [draggable][contenteditable=false] { user-select: text }\n \n .ProseMirror-selectednode {\n outline: 2px solid #8cf;\n }\n \n /* Make sure li selections wrap around markers */\n \n li.ProseMirror-selectednode {\n outline: none;\n }\n \n li.ProseMirror-selectednode:after {\n content: \"\";\n position: absolute;\n left: -32px;\n right: -2px; top: -2px; bottom: -2px;\n border: 2px solid #8cf;\n pointer-events: none;\n }\n \n /* Protect against generic img rules */\n \n img.ProseMirror-separator {\n display: inline !important;\n border: none !important;\n margin: 0 !important;\n }\n .ProseMirror-textblock-dropdown {\n min-width: 3em;\n }\n \n .ProseMirror-icon svg {\n fill: currentColor;\n height: 1em;\n }\n \n .ProseMirror-icon span {\n vertical-align: text-top;\n }\n .ProseMirror-gapcursor {\n display: none;\n pointer-events: none;\n position: absolute;\n }\n \n .ProseMirror-gapcursor:after {\n content: \"\";\n display: block;\n position: absolute;\n top: -2px;\n width: 20px;\n border-top: 1px solid black;\n animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;\n }\n \n @keyframes ProseMirror-cursor-blink {\n to {\n visibility: hidden;\n }\n }\n \n .ProseMirror-focused .ProseMirror-gapcursor {\n display: block;\n }\n \n .ProseMirror-prompt {\n background: white;\n padding: 5px 10px 5px 15px;\n border: 1px solid silver;\n position: fixed;\n border-radius: 3px;\n z-index: 11;\n box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2);\n }\n \n .ProseMirror-prompt h5 {\n margin: 0;\n font-weight: normal;\n font-size: 100%;\n color: #444;\n }\n \n .ProseMirror-prompt input[type=\"text\"],\n .ProseMirror-prompt textarea {\n background: #eee;\n border: none;\n outline: none;\n }\n \n .ProseMirror-prompt input[type=\"text\"] {\n padding: 0 4px;\n }\n \n .ProseMirror-prompt-close {\n position: absolute;\n left: 2px; top: 1px;\n color: #666;\n border: none; background: transparent; padding: 0;\n }\n \n .ProseMirror-prompt-close:after {\n content: \"✕\";\n font-size: 12px;\n }\n \n .ProseMirror-invalid {\n background: #ffc;\n border: 1px solid #cc7;\n border-radius: 4px;\n padding: 5px 10px;\n position: absolute;\n min-width: 10em;\n }\n \n .ProseMirror-prompt-buttons {\n margin-top: 5px;\n /* display: none; */\n }\n \n /*\n \n #editor, .editor {\n border-radius: 4px;\n border: 2px solid rgba(0, 0, 0, 0.2);\n }\n \n .ProseMirror-example-setup-style hr {\n padding: 2px 10px;\n border: none;\n margin: 1em 0;\n }\n \n .ProseMirror-example-setup-style hr:after {\n content: \"\";\n display: block;\n height: 1px;\n background-color: silver;\n line-height: 2px;\n }\n \n .ProseMirror ul, .ProseMirror ol {\n padding-left: 30px;\n }\n \n .ProseMirror blockquote {\n padding-left: 1em;\n border-left: 3px solid #eee;\n margin-left: 0; margin-right: 0;\n }\n \n .ProseMirror-example-setup-style img {\n cursor: default;\n }\n \n .ProseMirror p { margin-bottom: 1em }\n */\n ");
|
|
2
2
|
|
|
3
|
-
const sheet$3 = new CSSStyleSheet();sheet$3.replaceSync("#editor, .editor {\n font-family: system-ui, sans-serif;\n background: white;\n color: black;\n background-clip: padding-box;\n margin: -8px; /* Set so that the .ProseMirror padding starts from edges */\n --padBottom: 0; /* MU.padBottom() sets this value in px based on fullHeight */\n padding-block: 0 var(--padBottom);\n overflow-x: clip;\n overflow-y: scroll;\n height: 100vh;\n padding-right: 12px;\n}\n@media (prefers-color-scheme: dark) {\n #editor, .editor {\n background: black;\n color: white;\n }\n}\n\nbody {\n font-family: system-ui, sans-serif;\n}\n\na {\n color: blue;\n text-decoration: none;\n}\n@media (prefers-color-scheme: dark) {\n a {\n color: #4183c4;\n }\n}\n\na:hover {\n text-decoration: underline;\n}\n\np, pre {\n font-size: 1.0rem;\n}\n\np, ul, ol, dl, table, pre {\n margin: 0 0 15px;\n}\n\nul, ol {\n padding-left: 30px;\n}\n\nh1 {\n font-size: 2.5rem;\n}\n\nh2 {\n font-size: 2.0rem;\n}\n\nh3 {\n font-size: 1.5rem;\n}\n\nh4 {\n font-size: 1.2rem;\n}\n\nh5 {\n font-size: 1.0rem;\n}\n\nh6 {\n font-size: .83rem;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-weight: bold;\n margin: 0 0 10px 0;\n}\n\nh1 + p, h2 + p, h3 + p {\n margin-top: 10px;\n}\n\ntable {\n table-layout: fixed;\n border-collapse: collapse;\n width: 100%;\n}\n\ntable th, table td {\n padding: 4px;\n}\n\ntable th {\n font-weight: normal; /* The default is bold, but we want to use paragraph styles and formatting */\n}\n\n/* Make the styling compact inside of a table */\ntable p, table h1, table h2, table h3, table h4, table h5, table h6 {\n margin: 0;\n}\n\n/* Table bordering options */\n.bordered-table-none {\n border: none;\n}\n\n.bordered-table-outer, .bordered-table-header, .bordered-table-cell {\n border: 1px solid #DDD;\n}\n\n.bordered-table-header th { /* border th not thead to refresh properly */\n border: 1px solid #DDD;\n}\n\n.bordered-table-cell th, .bordered-table-cell td {\n border: 1px solid #DDD;\n}\n\n/* Default table bordering is same as .bordered-table-cell but is only used when not specified */\ntable:not(.bordered-table-none, .bordered-table-outer, .bordered-table-header, .bordered-table-cell) {\n border: 1px solid #DDD;\n}\n\ntable:not(.bordered-table-none, .bordered-table-outer, .bordered-table-header, .bordered-table-cell) td {\n border: 1px solid #DDD;\n}\n\ntable:not(.bordered-table-none, .bordered-table-outer, .bordered-table-header, .bordered-table-cell) th {\n border: 1px solid #DDD;\n}\n\nli p {\n margin: 0px 0;\n}\n\nli > ul, li > ol {\n margin-bottom: 0;\n}\n\nli > h1, li > h2, li > h3, li > h4, li > h5, li > h6 {\n margin-bottom: 0;\n}\n\nblockquote {\n margin-right: 0px; /* Because nested blockquotes just keep getting narrower */\n}\n\ncode {\n overflow-x: scroll;\n display: block;\n background-color: #F8F8F8;\n border-radius: 3px;\n font-family: 'SF Mono', SFMono-Regular, ui-monospace, 'DejaVu Sans Mono', Menlo, Consolas, monospace;\n white-space: pre;\n}\n@media (prefers-color-scheme: dark) {\n code {\n background-color: #808080;\n }\n}\n\np code, h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {\n display: inline;\n}\n\n.resize-container {\n position: relative;\n display: inline-block;\n margin: 0;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\n/* Provide a slightly darkened or lightened overlay while search is active */\n.searching {\n background-color: rgba(0, 0, 0, 0.10);\n}\n@media (prefers-color-scheme: dark) {\n .searching {\n background: rgba(255, 255, 255, 0.10);\n }\n}\n\n.resize-container img {\n display: block;\n outline: 1px black dashed;\n outline-offset: 4px;\n outline-width: 1px;\n}\n@media (prefers-color-scheme: dark) {\n .resize-container img {\n outline: 1px white dashed;\n }\n}\n\n.resize-handle-nw,\n.resize-handle-ne,\n.resize-handle-sw,\n.resize-handle-se {\n position: absolute;\n display: block;\n width: 6px;\n height: 6px;\n outline: 1px black solid;\n background: white;\n z-index: 999;\n}\n@media (prefers-color-scheme: dark) {\n .resize-handle-nw,\n .resize-handle-ne,\n .resize-handle-sw,\n .resize-handle-se {\n outline: 1px white solid;\n background: black;\n }\n}\n\n/* A transparent child for each resize-handle that expands the clickable area */\n.resize-handle-nw:after,\n.resize-handle-ne:after,\n.resize-handle-sw:after,\n.resize-handle-se:after {\n content: \"\";\n position: absolute;\n left: -5px;\n top: -5px;\n width: 16px;\n height: 16px;\n}\n\n/* The *-resize cursors do not work, at least in MacCatalyst.\n * Still specifying them below. They default to a pointer instead\n */\n.resize-handle-nw {\n cursor: nw-resize;\n top: -8px;\n left: -7px;\n}\n\n.resize-handle-ne {\n cursor: ne-resize;\n top: -8px;\n right: -7px;\n}\n\n.resize-handle-sw {\n cursor: sw-resize;\n bottom: -8px;\n left: -7px;\n}\n\n.resize-handle-se {\n cursor: se-resize;\n bottom: -8px;\n right: -7px;\n}\n\n.placeholder[placeholder]:before {\n content: attr(placeholder);\n position: absolute;\n color: #ccc;\n}\n\n/* Scoped to :has() rather than styling pre unconditionally, since a code_block's\n <pre> should only become a positioning context when it's actually carrying the\n language
|
|
3
|
+
const sheet$3 = new CSSStyleSheet();sheet$3.replaceSync("#editor, .editor {\n font-family: system-ui, sans-serif;\n background: white;\n color: black;\n background-clip: padding-box;\n margin: -8px; /* Set so that the .ProseMirror padding starts from edges */\n --padBottom: 0; /* MU.padBottom() sets this value in px based on fullHeight */\n padding-block: 0 var(--padBottom);\n overflow-x: clip;\n overflow-y: scroll;\n height: 100vh;\n padding-right: 12px;\n}\n@media (prefers-color-scheme: dark) {\n #editor, .editor {\n background: black;\n color: white;\n }\n}\n\nbody {\n font-family: system-ui, sans-serif;\n}\n\na {\n color: blue;\n text-decoration: none;\n}\n@media (prefers-color-scheme: dark) {\n a {\n color: #4183c4;\n }\n}\n\na:hover {\n text-decoration: underline;\n}\n\np, pre {\n font-size: 1.0rem;\n}\n\np, ul, ol, dl, table, pre {\n margin: 0 0 15px;\n}\n\nul, ol {\n padding-left: 30px;\n}\n\nh1 {\n font-size: 2.5rem;\n}\n\nh2 {\n font-size: 2.0rem;\n}\n\nh3 {\n font-size: 1.5rem;\n}\n\nh4 {\n font-size: 1.2rem;\n}\n\nh5 {\n font-size: 1.0rem;\n}\n\nh6 {\n font-size: .83rem;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-weight: bold;\n margin: 0 0 10px 0;\n}\n\nh1 + p, h2 + p, h3 + p {\n margin-top: 10px;\n}\n\ntable {\n table-layout: fixed;\n border-collapse: collapse;\n width: 100%;\n}\n\ntable th, table td {\n padding: 4px;\n}\n\ntable th {\n font-weight: normal; /* The default is bold, but we want to use paragraph styles and formatting */\n}\n\n/* Make the styling compact inside of a table */\ntable p, table h1, table h2, table h3, table h4, table h5, table h6 {\n margin: 0;\n}\n\n/* Table bordering options */\n.bordered-table-none {\n border: none;\n}\n\n.bordered-table-outer, .bordered-table-header, .bordered-table-cell {\n border: 1px solid #DDD;\n}\n\n.bordered-table-header th { /* border th not thead to refresh properly */\n border: 1px solid #DDD;\n}\n\n.bordered-table-cell th, .bordered-table-cell td {\n border: 1px solid #DDD;\n}\n\n/* Default table bordering is same as .bordered-table-cell but is only used when not specified */\ntable:not(.bordered-table-none, .bordered-table-outer, .bordered-table-header, .bordered-table-cell) {\n border: 1px solid #DDD;\n}\n\ntable:not(.bordered-table-none, .bordered-table-outer, .bordered-table-header, .bordered-table-cell) td {\n border: 1px solid #DDD;\n}\n\ntable:not(.bordered-table-none, .bordered-table-outer, .bordered-table-header, .bordered-table-cell) th {\n border: 1px solid #DDD;\n}\n\nli p {\n margin: 0px 0;\n}\n\nli > ul, li > ol {\n margin-bottom: 0;\n}\n\nli > h1, li > h2, li > h3, li > h4, li > h5, li > h6 {\n margin-bottom: 0;\n}\n\nblockquote {\n margin-right: 0px; /* Because nested blockquotes just keep getting narrower */\n}\n\ncode {\n overflow-x: scroll;\n display: block;\n background-color: #F8F8F8;\n border-radius: 3px;\n font-family: 'SF Mono', SFMono-Regular, ui-monospace, 'DejaVu Sans Mono', Menlo, Consolas, monospace;\n white-space: pre;\n}\n@media (prefers-color-scheme: dark) {\n code {\n background-color: #808080;\n }\n}\n\np code, h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {\n display: inline;\n}\n\n.resize-container {\n position: relative;\n display: inline-block;\n margin: 0;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\n/* Provide a slightly darkened or lightened overlay while search is active */\n.searching {\n background-color: rgba(0, 0, 0, 0.10);\n}\n@media (prefers-color-scheme: dark) {\n .searching {\n background: rgba(255, 255, 255, 0.10);\n }\n}\n\n.resize-container img {\n display: block;\n outline: 1px black dashed;\n outline-offset: 4px;\n outline-width: 1px;\n}\n@media (prefers-color-scheme: dark) {\n .resize-container img {\n outline: 1px white dashed;\n }\n}\n\n.resize-handle-nw,\n.resize-handle-ne,\n.resize-handle-sw,\n.resize-handle-se {\n position: absolute;\n display: block;\n width: 6px;\n height: 6px;\n outline: 1px black solid;\n background: white;\n z-index: 999;\n}\n@media (prefers-color-scheme: dark) {\n .resize-handle-nw,\n .resize-handle-ne,\n .resize-handle-sw,\n .resize-handle-se {\n outline: 1px white solid;\n background: black;\n }\n}\n\n/* A transparent child for each resize-handle that expands the clickable area */\n.resize-handle-nw:after,\n.resize-handle-ne:after,\n.resize-handle-sw:after,\n.resize-handle-se:after {\n content: \"\";\n position: absolute;\n left: -5px;\n top: -5px;\n width: 16px;\n height: 16px;\n}\n\n/* The *-resize cursors do not work, at least in MacCatalyst.\n * Still specifying them below. They default to a pointer instead\n */\n.resize-handle-nw {\n cursor: nw-resize;\n top: -8px;\n left: -7px;\n}\n\n.resize-handle-ne {\n cursor: ne-resize;\n top: -8px;\n right: -7px;\n}\n\n.resize-handle-sw {\n cursor: sw-resize;\n bottom: -8px;\n left: -7px;\n}\n\n.resize-handle-se {\n cursor: se-resize;\n bottom: -8px;\n right: -7px;\n}\n\n.placeholder[placeholder]:before {\n content: attr(placeholder);\n position: absolute;\n color: #ccc;\n}\n\n/* Scoped to :has() rather than styling pre unconditionally, since a code_block's\n <pre> should only become a positioning context when it's actually carrying the\n language tab (highlightCode on, block selected) — not for every code block. */\npre:has(.Markup-code-language-tab) {\n position: relative;\n}\n\n.Markup-code-language-tab {\n position: absolute;\n bottom: 100%;\n right: 0;\n font-size: 0.75rem;\n padding: 2px 6px;\n border: none;\n border-radius: 4px 4px 0 0;\n cursor: pointer;\n opacity: 0.6;\n color: white;\n background: var(--Markup-accent-color, blue);\n}\n\n.Markup-code-language-tab:hover {\n opacity: 0.9;\n}\n\n/* Applied (setup/index.js, CodeView.hasRoomAbove) when the code_block is too close to\n the top of the view for the tab to fit above it — attach to the bottom instead. */\n.Markup-code-language-tab-below {\n bottom: auto;\n top: 100%;\n border-radius: 0 0 4px 4px;\n}\n\n/* Matches .Markup-menuitem-active's selected-button color scheme. */\n@media (prefers-color-scheme: dark) {\n .Markup-code-language-tab {\n background: var(--Markup-accent-color, lightblue);\n color: black;\n }\n}\n\n/* Classes set by prosemirror-search module, modified for MarkupEditor */\n/* Note the dark and light mode are the same */\n\n.ProseMirror-search-match {\n background-color: yellow;\n color: black;\n}\n\n.ProseMirror-active-search-match {\n background-color: orange;\n color: black;\n outline: 1px orangered solid;\n z-index: 2;\n}");
|
|
4
4
|
|
|
5
|
-
const sheet$2 = new CSSStyleSheet();sheet$2.replaceSync("/* Spacing at the top */\n/* Note that toolbar-overlay below is also set to these values */\n\n.Markup-searchbar-showing {\n height: 38px;\n top: 76px;\n}\n\n/* The wrapper lets us automatically embed a toolbar and scroll under it */\n.Markup-toolbar-wrapper {\n margin-right: -12px; /* Prevent side scroll even when menu fits */\n height: inherit;\n overflow-y: scroll;\n position: relative; \n}\n\n/* We add Markup-prompt-showing to the wrapper to prevent scroll, and remove it when done */\n.Markup-prompt-showing {\n overflow-y: hidden;\n}\n\n/* Z-indexes in one place */\n\n.body-content { \n z-index: 1; \n}\n\n.Markup-prompt-overlay { \n z-index: 2; \n}\n\n.Markup-prompt, .Markup-selection {\n z-index: 3;\n}\n\n.Markup-searchbar {\n z-index: 4;\n}\n\n.Markup-toolbar, .Markup-toolbar-more {\n z-index: 5;\n}\n\n.Markup-toolbar-overlay { \n z-index: 6; \n}\n\n.Markup-menu-dropdown-menu {\n z-index: 7;\n}\n\n/* Toolbar, menu item, and dropdown styling */\n\n.Markup-toolbar {\n display: inline-flex;\n position: sticky;\n overflow: visible;\n width: 100%;\n font-size: var(--Markup-button-font-size, 24px);\n vertical-align: middle;\n border-top-left-radius: inherit;\n border-top-right-radius: inherit;\n top: 0;\n max-height: calc(var(--Markup-button-size, 28px) + 10px);\n padding: 2px 8px;\n color: var(--Markup-accent-color, blue);\n background: var(--Markup-toolbar-bg, rgba(250, 249, 246, 0.95));\n border-bottom: 1px solid var(--Markup-border-color, lightgray);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-toolbar {\n color: var(--Markup-accent-color, lightblue);\n background: var(--Markup-toolbar-bg, rgba(40, 40, 43, 0.9));\n border-bottom: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n.Markup-toolbar-hidden {\n display: none;\n}\n\n.Markup-menuitem {\n display: inline-block;\n flex-shrink: 0; /* Otherwise, the drop-downs with icons shrink */\n overflow-y: visible;\n vertical-align: middle;\n min-width: var(--Markup-button-size, 28px);\n height: var(--Markup-button-size, 28px);\n cursor: pointer;\n margin-right: 4px;\n border-radius: 4px;\n color: var(--Markup-accent-color, blue);\n border: 1px solid var(--Markup-accent-color, blue);\n background: var(--Markup-button-bg, white);\n fill: var(--Markup-accent-color, blue);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menuitem {\n color: var(--Markup-accent-color, lightblue);\n border: 1px solid var(--Markup-accent-color, lightblue);\n /* border: 1px solid #4183c4; */\n background: var(--Markup-button-bg, black);\n fill: var(--Markup-accent-color, lightblue);\n }\n}\n\n.Markup-menuitem-active {\n fill: white;\n color: white;\n background: var(--Markup-accent-color, blue);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menuitem-active {\n fill: black;\n color: black;\n background: var(--Markup-accent-color, lightblue);\n }\n}\n\n.Markup-menuitem-disabled {\n cursor: default;\n opacity: .3;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menuitem-disabled {\n opacity: .6;\n }\n}\n\n.Markup-icon {\n display: inline-block;\n width: var(--Markup-button-size, 28px);\n height: var(--Markup-button-size, 28px);\n border: 0;\n}\n\n.Markup-icon span {\n vertical-align: middle;\n}\n\n.Markup-icon svg {\n width: var(--Markup-button-font-size, 24px);\n height: var(--Markup-button-font-size, 24px);\n padding: 2px;\n border: 0;\n fill: inherit;\n}\n\n.Markup-menuseparator {\n display: inline-block;\n vertical-align: middle;\n height: var(--Markup-button-size, 28px);\n border-right: 1px solid var(--Markup-border-color, lightgray);\n margin-right: 4px;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menuseparator {\n border-right: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n.Markup-menu-disabled .Markup-icon {\n cursor: default;\n}\n\n.Markup-menu-dropdown, .Markup-menu-dropdown-menu {\n background: inherit;\n text-align: left;\n white-space: nowrap;\n}\n\n.Markup-menu-dropdown span {\n vertical-align: middle;\n}\n\n.Markup-menu-dropdown-icon span {\n vertical-align: middle;\n}\n\n.Markup-menu-dropdown-wrap {\n display: flex;\n justify-content: space-between;\n padding: 0 4px;\n height: inherit;\n border-radius: 4px;\n background: inherit;\n}\n\n.Markup-menu-dropdown-icon-wrap {\n display: flex;\n justify-content: space-between;\n padding-right: 4px;\n width: 1.8em;\n height: inherit;\n border-radius: 4px;\n background: inherit;\n}\n\n.Markup-menu-dropdown-icon-wrap-noindicator {\n display: inline-flex;\n padding-right: 4px;\n width: 1em;\n height: inherit;\n border-radius: 4px;\n background: inherit;\n}\n\n/* For the paragraph style dropdown with a label, make the font smaller */\n.Markup-menu-dropdown {\n display: flex;\n justify-content: space-between;\n align-items: center;\n box-sizing: border-box;\n font-size: 0.8em;\n width: inherit;\n height: inherit;\n background: inherit;\n position: absolute;\n}\n\n/* But for the indicator, bump size up to match the icon-style dropdown */\n.Markup-menu-dropdown .Markup-menu-dropdown-indicator {\n font-size: 1.25em;\n}\n\n.Markup-menu-dropdown-icon {\n display: inline-flex;\n justify-content: space-between;\n font-size: inherit;\n width: inherit;\n cursor: pointer;\n position: absolute;\n}\n\n.Markup-menu-dropdown-indicator {\n opacity: .6;\n}\n\n.Markup-tooltip .Markup-menu {\n width: -webkit-fit-content;\n width: fit-content;\n white-space: pre;\n}\n\n.Markup-menu-dropdown-menu, .Markup-menu-submenu {\n position: absolute;\n background: inherit;\n padding: 2px;\n background: inherit;\n border: 1px solid var(--Markup-border-color, gray);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-dropdown-menu, .Markup-menu-submenu {\n border: 1px solid var(--Markup-border-color, lightgray);\n }\n}\n\n.Markup-menu-dropdown-menu {\n margin-top: calc(1em + 6px);\n min-width: 4em;\n}\n\n.Markup-menu-dropdown-item {\n cursor: pointer;\n padding: 2px 8px 2px 4px;\n}\n\n/* Used to overlfow and ellipsis-truncate text in Dropdown menu items */\n.Markup-menuitem-clipped {\n white-space: nowrap; /* Required for text-overflow */\n overflow-x: hidden; /* Required for text-overflow */\n max-width: 30em;\n text-overflow: ellipsis;\n}\n\n.Markup-menu-dropdown-item:hover, .Markup-menu-submenu-wrap:hover {\n background: var(--Markup-hover-bg, #f2f2f2);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-dropdown-item:hover, .Markup-menu-submenu-wrap:hover {\n background: var(--Markup-hover-bg, rgb(80, 80, 80));\n }\n}\n\n.Markup-menu-dropdown-item p, \n.Markup-menu-dropdown-item h1, \n.Markup-menu-dropdown-item h2, \n.Markup-menu-dropdown-item h3, \n.Markup-menu-dropdown-item h4, \n.Markup-menu-dropdown-item h5, \n.Markup-menu-dropdown-item h6, \n.Markup-menu-dropdown-item pre {\n padding: 0;\n margin: 0;\n min-height: 24px;\n}\n\n.Markup-stylelabel {\n display: flex;\n justify-content: space-between;\n align-items: center;\n white-space: pre;\n width: 100%;\n}\n\n.Markup-stylelabel-keymap {\n display: flex;\n justify-content: flex-end;\n font-size: 1.0rem;\n font-weight: normal;\n color: gray;\n}\n\n.Markup-menu-submenu-wrap:hover .Markup-menu-submenu, .Markup-menu-submenu-wrap-active .Markup-menu-submenu {\n display: block;\n}\n\n.Markup-menu-submenu-wrap {\n position: relative;\n margin-right: -4px;\n background: inherit;\n}\n\n.Markup-menu-submenu {\n display: none;\n min-width: 4em;\n left: 100%;\n top: -5px;\n max-height: 80vh;\n overflow-y: auto;\n /* background cannot be inherited because of hover highlighting in dropdown */\n background: var(--Markup-button-bg, white);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-submenu {\n /* background cannot be inherited because of hover highlighting in dropdown */\n background: var(--Markup-button-bg, black);\n }\n}\n\n.Markup-menu-submenu-label:after {\n content: \"\";\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n border-left: 4px solid currentColor;\n opacity: .6;\n position: absolute;\n right: 4px;\n top: calc(50% - 4px);\n}\n\n.Markup-selection {\n position: absolute;\n border: 1px solid var(--Markup-accent-color, blue);\n background: transparent;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-selection {\n border: 1px solid var(--Markup-accent-color, lightblue);\n }\n}\n\n/* Toolbar-more styling */\n\n.Markup-toolbar-more {\n display: inline-flex;\n position: sticky;\n overflow: visible;\n width: 100%;\n font-size: var(--Markup-button-font-size, 24px);\n vertical-align: middle;\n border-top-left-radius: inherit;\n border-top-right-radius: inherit;\n top: calc(var(--Markup-button-size, 28px) + 7px);\n max-height: calc(var(--Markup-button-size, 28px) + 10px);\n padding: 2px 8px;\n color: var(--Markup-accent-color, blue);\n background: var(--Markup-toolbar-bg, rgba(250, 249, 246, 0.95));\n border-bottom: 1px solid var(--Markup-border-color, lightgray);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-toolbar-more {\n color: var(--Markup-accent-color, lightblue);\n background: var(--Markup-toolbar-bg, rgba(40, 40, 43, 0.9));\n border-bottom: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n/* Searchbar styling */\n\n.Markup-searchbar {\n display: inline-block;\n font-size: var(--Markup-button-font-size, 24px);\n vertical-align: middle;\n position: sticky;\n top: calc(var(--Markup-button-size, 28px) + 7px);\n width: 100%;\n line-height: 30px;\n height: 37px;\n color: var(--Markup-accent-color, blue);\n background: var(--Markup-toolbar-bg, rgba(250, 249, 246, 0.95));\n border-bottom: 1px solid var(--Markup-border-color, lightgray);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n overflow: hidden;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-searchbar {\n color: var(--Markup-accent-color, lightblue);\n background: var(--Markup-toolbar-bg, rgba(40, 40, 43, 0.9));\n border-bottom: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n.Markup-searchbar input {\n appearance: none; /* Otherwise, in Safari/WebKit, the height is small and fixed */\n position: relative;\n font-size: 18px;\n line-height: 30px;\n height: 30px;\n width: calc(100% - 128px);\n margin: 0 8px;\n padding: 0 4px;\n outline: none;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid lightgray;\n border-radius: 6px;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-searchbar input {\n color: rgb(250, 249, 246);\n background: rgb(40, 40, 43);\n border: 1px solid gray;\n }\n}\n\n.Markup-searchbar .Markup-searchbar-status {\n position: absolute;\n display: flex;\n justify-content: flex-end;\n align-items: center;\n white-space: nowrap;\n font-size: 12px;\n overflow-x: visible;\n height: 28px;\n top: 5px;\n left: calc(100% - 13em);\n width: 1px;\n color: gray;\n user-select: none;\n}\n\n/* Dialog/prompt styling for insert/edit actions */\n\n.Markup-prompt-overlay {\n\tposition: fixed;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground-color: rgba(0, 0, 0, 0.2);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt-overlay {\n background-color: rgba(255, 255, 255, 0.2);\n }\n}\n\n.Markup-toolbar-overlay {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\tbackground-color: rgba(0, 0, 0, 0.1);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-toolbar-overlay {\n background-color: rgba(255, 255, 255, 0.1);\n }\n}\n\n.Markup-toolbar-overlay.Markup-searchbar-hidden {\n height: 38px;\n}\n\n.Markup-toolbar-overlay.Markup-searchbar-showing {\n height: 76px;\n}\n\n.Markup-prompt {\n margin: 0;\n font-size: 18px;\n background: white;\n padding: 8px;\n border: 1px solid silver;\n position: absolute;\n border-radius: 3px;\n box-shadow: -.5px 2px 5px rgba(0, 0, 0, 0.2);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt {\n background: black;\n box-shadow: -.5px 2px 5px rgba(255, 255, 255, 0.8);\n }\n}\n\n.Markup-prompt-link {\n height: 96px;\n width: 300px;\n}\n\n.Markup-prompt-image {\n height: 130px;\n width: 300px;\n}\n\n.Markup-prompt-language {\n height: 96px;\n width: 300px;\n}\n\n.Markup-prompt img {\n display: inline-block;\n vertical-align: middle;\n min-width: 28px;\n height: 28px;\n margin-right: 4px;\n}\n\n.Markup-prompt p {\n margin: 0 0 4px 0;\n font-weight: normal;\n color: black;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt p {\n color: white;\n }\n}\n\n.Markup-prompt input[type=\"text\"] {\n font-size: 100%;\n line-height: 28px;\n margin-top: 2px;\n padding: 1px 4px;\n width: 100%;\n appearance: none;\n background: #eee;\n outline: none;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid lightgray;\n border-radius: 6px;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt input[type=\"text\"] {\n color: rgb(250, 249, 246);\n background: rgb(40, 40, 43);\n border: 1px solid gray;\n }\n}\n\n.Markup-prompt input.invalid {\n color: red;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt input.invalid {\n color: pink;\n }\n}\n\n.Markup-prompt-buttons {\n display: flex;\n vertical-align: middle;\n justify-content: space-between;\n justify-items: center;\n line-height: 28px;\n height: 28px;\n margin-top: 8px;\n margin-bottom: 8px;\n}\n\n.Markup-prompt-buttongroup {\n flex-shrink: 0;\n}\n\n.Markup-prompt-buttons img {\n aspect-ratio: auto;\n height: auto;\n object-fit: contain;\n}\n\n.Markup-prompt-buttons .Markup-menuitem {\n display: inline-flex;\n padding-right: 4px;\n padding-left: 4px;\n}\n\n/* The buttongroup contains OK and Cancel menuitems on the right */\n.Markup-prompt-buttongroup .Markup-menuitem {\n margin-right: 0;\n margin-left: 4px;\n}\n\n/* .Markup-menu-tablesizer is a specialized submenu */\n.Markup-menu-tablesizer {\n display: none;\n grid-template-columns: repeat(4, 1fr);\n grid-auto-rows: 18px;\n row-gap: 1px;\n column-gap: 1px;\n padding: 0;\n left: 100%;\n top: -5px;\n /* background cannot be inherited because of hover highlighting in dropdown */\n background: var(--Markup-button-bg, white);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-tablesizer {\n /* background cannot be inherited because of hover highlighting in dropdown */\n background: var(--Markup-button-bg, black);\n }\n}\n\n.Markup-menu-submenu-wrap:hover .Markup-menu-tablesizer, .Markup-menu-submenu-wrap-active .Markup-menu-tablesizer {\n display: grid;\n}\n\n.Markup-menu-dropdown-menu, .Markup-menu-tablesizer {\n position: absolute;\n background: inherit;\n padding: 2px;\n background: inherit;\n border: 1px solid var(--Markup-border-color, gray);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-dropdown-menu, .Markup-menu-submenu {\n border: 1px solid var(--Markup-border-color, lightgray);\n }\n}\n\n.Markup-menu-tablesizer .Markup-menu-dropdown-item {\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid var(--Markup-accent-color, blue);\n width: 18px;\n height: 18px;\n padding: 0;\n font-size: 13px;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-tablesizer .Markup-menu-dropdown-item {\n border: 1px solid var(--Markup-accent-color, lightblue);\n }\n}\n\n.Markup-menu-tablesizer .Markup-menuitem-active {\n height: 13px;\n padding-top: 3px;\n fill: black;\n color: black;\n background: var(--Markup-accent-color, blue);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-tablesizer .Markup-menuitem-active {\n fill: black;\n color: black;\n background: var(--Markup-accent-color, lightblue);\n }\n}");
|
|
5
|
+
const sheet$2 = new CSSStyleSheet();sheet$2.replaceSync("/* Spacing at the top */\n/* Note that toolbar-overlay below is also set to these values */\n\n.Markup-searchbar-showing {\n height: 38px;\n top: 76px;\n}\n\n/* The wrapper lets us automatically embed a toolbar and scroll under it */\n.Markup-toolbar-wrapper {\n margin-right: -12px; /* Prevent side scroll even when menu fits */\n height: inherit;\n overflow-y: scroll;\n position: relative; \n}\n\n/* We add Markup-prompt-showing to the wrapper to prevent scroll, and remove it when done */\n.Markup-prompt-showing {\n overflow-y: hidden;\n}\n\n/* Z-indexes in one place */\n\n.body-content { \n z-index: 1; \n}\n\n.Markup-prompt-overlay { \n z-index: 2; \n}\n\n.Markup-prompt, .Markup-selection {\n z-index: 3;\n}\n\n.Markup-searchbar {\n z-index: 4;\n}\n\n.Markup-toolbar, .Markup-toolbar-more {\n z-index: 5;\n}\n\n.Markup-toolbar-overlay { \n z-index: 6; \n}\n\n.Markup-menu-dropdown-menu {\n z-index: 7;\n}\n\n/* Toolbar, menu item, and dropdown styling */\n\n.Markup-toolbar {\n display: inline-flex;\n position: sticky;\n overflow: visible;\n width: 100%;\n font-size: var(--Markup-button-font-size, 24px);\n vertical-align: middle;\n border-top-left-radius: inherit;\n border-top-right-radius: inherit;\n top: 0;\n max-height: calc(var(--Markup-button-size, 28px) + 10px);\n padding: 2px 8px;\n color: var(--Markup-accent-color, blue);\n background: var(--Markup-toolbar-bg, rgba(250, 249, 246, 0.95));\n border-bottom: 1px solid var(--Markup-border-color, lightgray);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-toolbar {\n color: var(--Markup-accent-color, lightblue);\n background: var(--Markup-toolbar-bg, rgba(40, 40, 43, 0.9));\n border-bottom: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n.Markup-toolbar-hidden {\n display: none;\n}\n\n.Markup-menuitem {\n display: inline-block;\n flex-shrink: 0; /* Otherwise, the drop-downs with icons shrink */\n overflow-y: visible;\n vertical-align: middle;\n min-width: var(--Markup-button-size, 28px);\n height: var(--Markup-button-size, 28px);\n cursor: pointer;\n margin-right: 4px;\n border-radius: 4px;\n color: var(--Markup-accent-color, blue);\n border: 1px solid var(--Markup-accent-color, blue);\n background: var(--Markup-button-bg, white);\n fill: var(--Markup-accent-color, blue);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menuitem {\n color: var(--Markup-accent-color, lightblue);\n border: 1px solid var(--Markup-accent-color, lightblue);\n /* border: 1px solid #4183c4; */\n background: var(--Markup-button-bg, black);\n fill: var(--Markup-accent-color, lightblue);\n }\n}\n\n.Markup-menuitem-active {\n fill: white;\n color: white;\n background: var(--Markup-accent-color, blue);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menuitem-active {\n fill: black;\n color: black;\n background: var(--Markup-accent-color, lightblue);\n }\n}\n\n.Markup-menuitem-disabled {\n cursor: default;\n opacity: .3;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menuitem-disabled {\n opacity: .6;\n }\n}\n\n.Markup-icon {\n display: inline-block;\n width: var(--Markup-button-size, 28px);\n height: var(--Markup-button-size, 28px);\n border: 0;\n}\n\n.Markup-icon span {\n vertical-align: middle;\n}\n\n.Markup-icon svg {\n width: var(--Markup-button-font-size, 24px);\n height: var(--Markup-button-font-size, 24px);\n padding: 2px;\n border: 0;\n fill: inherit;\n}\n\n.Markup-menuseparator {\n display: inline-block;\n vertical-align: middle;\n height: var(--Markup-button-size, 28px);\n border-right: 1px solid var(--Markup-border-color, lightgray);\n margin-right: 4px;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menuseparator {\n border-right: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n.Markup-menu-disabled .Markup-icon {\n cursor: default;\n}\n\n.Markup-menu-dropdown, .Markup-menu-dropdown-menu {\n background: inherit;\n text-align: left;\n white-space: nowrap;\n}\n\n.Markup-menu-dropdown span {\n vertical-align: middle;\n}\n\n.Markup-menu-dropdown-icon span {\n vertical-align: middle;\n}\n\n.Markup-menu-dropdown-wrap {\n display: flex;\n justify-content: space-between;\n padding: 0 4px;\n height: inherit;\n border-radius: 4px;\n background: inherit;\n}\n\n.Markup-menu-dropdown-icon-wrap {\n display: flex;\n justify-content: space-between;\n padding-right: 4px;\n width: 1.8em;\n height: inherit;\n border-radius: 4px;\n background: inherit;\n}\n\n.Markup-menu-dropdown-icon-wrap-noindicator {\n display: inline-flex;\n padding-right: 4px;\n width: 1em;\n height: inherit;\n border-radius: 4px;\n background: inherit;\n}\n\n/* For the paragraph style dropdown with a label, make the font smaller */\n.Markup-menu-dropdown {\n display: flex;\n justify-content: space-between;\n align-items: center;\n box-sizing: border-box;\n font-size: 0.8em;\n width: inherit;\n height: inherit;\n background: inherit;\n position: absolute;\n}\n\n/* But for the indicator, bump size up to match the icon-style dropdown */\n.Markup-menu-dropdown .Markup-menu-dropdown-indicator {\n font-size: 1.25em;\n}\n\n.Markup-menu-dropdown-icon {\n display: inline-flex;\n justify-content: space-between;\n font-size: inherit;\n width: inherit;\n cursor: pointer;\n position: absolute;\n}\n\n.Markup-menu-dropdown-indicator {\n opacity: .6;\n}\n\n.Markup-tooltip .Markup-menu {\n width: -webkit-fit-content;\n width: fit-content;\n white-space: pre;\n}\n\n.Markup-menu-dropdown-menu, .Markup-menu-submenu {\n position: absolute;\n background: inherit;\n padding: 2px;\n background: inherit;\n border: 1px solid var(--Markup-border-color, gray);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-dropdown-menu, .Markup-menu-submenu {\n border: 1px solid var(--Markup-border-color, lightgray);\n }\n}\n\n.Markup-menu-dropdown-menu {\n margin-top: calc(1em + 6px);\n min-width: 4em;\n}\n\n.Markup-menu-dropdown-item {\n cursor: pointer;\n padding: 2px 8px 2px 4px;\n}\n\n.Markup-menu-dropdown-separator {\n border-top: 1px solid var(--Markup-border-color, lightgray);\n margin: 4px 0;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-dropdown-separator {\n border-top: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n/* Used to overlfow and ellipsis-truncate text in Dropdown menu items */\n.Markup-menuitem-clipped {\n white-space: nowrap; /* Required for text-overflow */\n overflow-x: hidden; /* Required for text-overflow */\n max-width: 30em;\n text-overflow: ellipsis;\n}\n\n.Markup-menu-dropdown-item:hover, .Markup-menu-submenu-wrap:hover {\n background: var(--Markup-hover-bg, #f2f2f2);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-dropdown-item:hover, .Markup-menu-submenu-wrap:hover {\n background: var(--Markup-hover-bg, rgb(80, 80, 80));\n }\n}\n\n.Markup-menu-dropdown-item p, \n.Markup-menu-dropdown-item h1, \n.Markup-menu-dropdown-item h2, \n.Markup-menu-dropdown-item h3, \n.Markup-menu-dropdown-item h4, \n.Markup-menu-dropdown-item h5, \n.Markup-menu-dropdown-item h6, \n.Markup-menu-dropdown-item pre {\n padding: 0;\n margin: 0;\n min-height: 24px;\n}\n\n.Markup-stylelabel {\n display: flex;\n justify-content: space-between;\n align-items: center;\n white-space: pre;\n width: 100%;\n}\n\n.Markup-stylelabel-keymap {\n display: flex;\n justify-content: flex-end;\n font-size: 1.0rem;\n font-weight: normal;\n color: gray;\n}\n\n.Markup-menu-submenu-wrap:hover .Markup-menu-submenu, .Markup-menu-submenu-wrap-active .Markup-menu-submenu {\n display: block;\n}\n\n.Markup-menu-submenu-wrap {\n position: relative;\n margin-right: -4px;\n background: inherit;\n}\n\n.Markup-menu-submenu {\n display: none;\n min-width: 4em;\n left: 100%;\n top: -5px;\n max-height: 80vh;\n overflow-y: auto;\n /* background cannot be inherited because of hover highlighting in dropdown */\n background: var(--Markup-button-bg, white);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-submenu {\n /* background cannot be inherited because of hover highlighting in dropdown */\n background: var(--Markup-button-bg, black);\n }\n}\n\n.Markup-menu-submenu-label:after {\n content: \"\";\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n border-left: 4px solid currentColor;\n opacity: .6;\n position: absolute;\n right: 4px;\n top: calc(50% - 4px);\n}\n\n.Markup-selection {\n position: absolute;\n border: 1px solid var(--Markup-accent-color, blue);\n background: transparent;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-selection {\n border: 1px solid var(--Markup-accent-color, lightblue);\n }\n}\n\n/* Toolbar-more styling */\n\n.Markup-toolbar-more {\n display: inline-flex;\n position: sticky;\n overflow: visible;\n width: 100%;\n font-size: var(--Markup-button-font-size, 24px);\n vertical-align: middle;\n border-top-left-radius: inherit;\n border-top-right-radius: inherit;\n top: calc(var(--Markup-button-size, 28px) + 7px);\n max-height: calc(var(--Markup-button-size, 28px) + 10px);\n padding: 2px 8px;\n color: var(--Markup-accent-color, blue);\n background: var(--Markup-toolbar-bg, rgba(250, 249, 246, 0.95));\n border-bottom: 1px solid var(--Markup-border-color, lightgray);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-toolbar-more {\n color: var(--Markup-accent-color, lightblue);\n background: var(--Markup-toolbar-bg, rgba(40, 40, 43, 0.9));\n border-bottom: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n/* Searchbar styling */\n\n.Markup-searchbar {\n display: inline-block;\n font-size: var(--Markup-button-font-size, 24px);\n vertical-align: middle;\n position: sticky;\n top: calc(var(--Markup-button-size, 28px) + 7px);\n width: 100%;\n line-height: 30px;\n height: 37px;\n color: var(--Markup-accent-color, blue);\n background: var(--Markup-toolbar-bg, rgba(250, 249, 246, 0.95));\n border-bottom: 1px solid var(--Markup-border-color, lightgray);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n overflow: hidden;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-searchbar {\n color: var(--Markup-accent-color, lightblue);\n background: var(--Markup-toolbar-bg, rgba(40, 40, 43, 0.9));\n border-bottom: 1px solid var(--Markup-border-color, gray);\n }\n}\n\n.Markup-searchbar input {\n appearance: none; /* Otherwise, in Safari/WebKit, the height is small and fixed */\n position: relative;\n font-size: 18px;\n line-height: 30px;\n height: 30px;\n width: calc(100% - 128px);\n margin: 0 8px;\n padding: 0 4px;\n outline: none;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid lightgray;\n border-radius: 6px;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-searchbar input {\n color: rgb(250, 249, 246);\n background: rgb(40, 40, 43);\n border: 1px solid gray;\n }\n}\n\n.Markup-searchbar .Markup-searchbar-status {\n position: absolute;\n display: flex;\n justify-content: flex-end;\n align-items: center;\n white-space: nowrap;\n font-size: 12px;\n overflow-x: visible;\n height: 28px;\n top: 5px;\n left: calc(100% - 13em);\n width: 1px;\n color: gray;\n user-select: none;\n}\n\n/* Dialog/prompt styling for insert/edit actions */\n\n.Markup-prompt-overlay {\n\tposition: fixed;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground-color: rgba(0, 0, 0, 0.2);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt-overlay {\n background-color: rgba(255, 255, 255, 0.2);\n }\n}\n\n.Markup-toolbar-overlay {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\tbackground-color: rgba(0, 0, 0, 0.1);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-toolbar-overlay {\n background-color: rgba(255, 255, 255, 0.1);\n }\n}\n\n.Markup-toolbar-overlay.Markup-searchbar-hidden {\n height: 38px;\n}\n\n.Markup-toolbar-overlay.Markup-searchbar-showing {\n height: 76px;\n}\n\n.Markup-prompt {\n margin: 0;\n font-size: 18px;\n background: white;\n padding: 8px;\n border: 1px solid silver;\n position: absolute;\n border-radius: 3px;\n box-shadow: -.5px 2px 5px rgba(0, 0, 0, 0.2);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt {\n background: black;\n box-shadow: -.5px 2px 5px rgba(255, 255, 255, 0.8);\n }\n}\n\n.Markup-prompt-link {\n height: calc(68px + var(--Markup-button-size, 28px)); /* 96 px by default */\n width: 300px;\n}\n\n.Markup-prompt-image {\n height: calc(102px + var(--Markup-button-size, 28px)); /* 130 px by default */\n width: 300px;\n}\n\n.Markup-prompt-language {\n height: calc(68px + var(--Markup-button-size, 28px)); /* 96 px by default */\n width: 300px;\n}\n\n.Markup-prompt img {\n display: inline-block;\n vertical-align: middle;\n min-width: var(--Markup-button-size, 28px);\n height: var(--Markup-button-size, 28px);\n margin-right: 4px;\n}\n\n.Markup-prompt p {\n margin: 0 0 4px 0;\n font-weight: normal;\n color: black;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt p {\n color: white;\n }\n}\n\n.Markup-prompt input[type=\"text\"] {\n font-size: 100%;\n line-height: 28px;\n margin-top: 2px;\n padding: 1px 4px;\n width: 100%;\n appearance: none;\n background: #eee;\n outline: none;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid lightgray;\n border-radius: 6px;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt input[type=\"text\"] {\n color: rgb(250, 249, 246);\n background: rgb(40, 40, 43);\n border: 1px solid gray;\n }\n}\n\n.Markup-prompt input.invalid {\n color: red;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-prompt input.invalid {\n color: pink;\n }\n}\n\n.Markup-prompt-buttons {\n display: flex;\n vertical-align: middle;\n justify-content: space-between;\n justify-items: center;\n line-height: var(--Markup-button-size, 28px);\n height: var(--Markup-button-size, 28px);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n\n.Markup-prompt-buttongroup {\n flex-shrink: 0;\n}\n\n.Markup-prompt-buttons img {\n aspect-ratio: auto;\n height: auto;\n object-fit: contain;\n}\n\n.Markup-prompt-buttons .Markup-menuitem {\n display: inline-flex;\n padding-right: 4px;\n padding-left: 4px;\n}\n\n/* The buttongroup contains OK and Cancel menuitems on the right */\n.Markup-prompt-buttongroup .Markup-menuitem {\n margin-right: 0;\n margin-left: 4px;\n}\n\n/* .Markup-menu-tablesizer is a specialized submenu */\n.Markup-menu-tablesizer {\n display: none;\n grid-template-columns: repeat(4, 1fr);\n grid-auto-rows: 18px;\n row-gap: 1px;\n column-gap: 1px;\n padding: 0;\n left: 100%;\n top: -5px;\n /* background cannot be inherited because of hover highlighting in dropdown */\n background: var(--Markup-button-bg, white);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-tablesizer {\n /* background cannot be inherited because of hover highlighting in dropdown */\n background: var(--Markup-button-bg, black);\n }\n}\n\n.Markup-menu-submenu-wrap:hover .Markup-menu-tablesizer, .Markup-menu-submenu-wrap-active .Markup-menu-tablesizer {\n display: grid;\n}\n\n.Markup-menu-dropdown-menu, .Markup-menu-tablesizer {\n position: absolute;\n background: inherit;\n padding: 2px;\n background: inherit;\n border: 1px solid var(--Markup-border-color, gray);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-dropdown-menu, .Markup-menu-submenu {\n border: 1px solid var(--Markup-border-color, lightgray);\n }\n}\n\n.Markup-menu-tablesizer .Markup-menu-dropdown-item {\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid var(--Markup-accent-color, blue);\n width: 18px;\n height: 18px;\n padding: 0;\n font-size: 13px;\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-tablesizer .Markup-menu-dropdown-item {\n border: 1px solid var(--Markup-accent-color, lightblue);\n }\n}\n\n.Markup-menu-tablesizer .Markup-menuitem-active {\n height: 13px;\n padding-top: 3px;\n fill: black;\n color: black;\n background: var(--Markup-accent-color, blue);\n}\n@media (prefers-color-scheme: dark) {\n .Markup-menu-tablesizer .Markup-menuitem-active {\n fill: black;\n color: black;\n background: var(--Markup-accent-color, lightblue);\n }\n}");
|
|
6
6
|
|
|
7
7
|
const sheet$1 = new CSSStyleSheet();sheet$1.replaceSync("pre code.hljs {\n display: block;\n overflow-x: auto;\n padding: 1em\n}\ncode.hljs {\n padding: 3px 5px\n}\n/*!\n Theme: GitHub\n Description: Light theme as seen on github.com\n Author: github.com\n Maintainer: @Hirse\n Updated: 2021-05-15\n\n Outdated base version: https://github.com/primer/github-syntax-light\n Current colors taken from GitHub's CSS\n*/\n.hljs {\n color: #24292e;\n background: #ffffff\n}\n.hljs-doctag,\n.hljs-keyword,\n.hljs-meta .hljs-keyword,\n.hljs-template-tag,\n.hljs-template-variable,\n.hljs-type,\n.hljs-variable.language_ {\n /* prettylights-syntax-keyword */\n color: #d73a49\n}\n.hljs-title,\n.hljs-title.class_,\n.hljs-title.class_.inherited__,\n.hljs-title.function_ {\n /* prettylights-syntax-entity */\n color: #6f42c1\n}\n.hljs-attr,\n.hljs-attribute,\n.hljs-literal,\n.hljs-meta,\n.hljs-number,\n.hljs-operator,\n.hljs-variable,\n.hljs-selector-attr,\n.hljs-selector-class,\n.hljs-selector-id {\n /* prettylights-syntax-constant */\n color: #005cc5\n}\n.hljs-regexp,\n.hljs-string,\n.hljs-meta .hljs-string {\n /* prettylights-syntax-string */\n color: #032f62\n}\n.hljs-built_in,\n.hljs-symbol {\n /* prettylights-syntax-variable */\n color: #e36209\n}\n.hljs-comment,\n.hljs-code,\n.hljs-formula {\n /* prettylights-syntax-comment */\n color: #6a737d\n}\n.hljs-name,\n.hljs-quote,\n.hljs-selector-tag,\n.hljs-selector-pseudo {\n /* prettylights-syntax-entity-tag */\n color: #22863a\n}\n.hljs-subst {\n /* prettylights-syntax-storage-modifier-import */\n color: #24292e\n}\n.hljs-section {\n /* prettylights-syntax-markup-heading */\n color: #005cc5;\n font-weight: bold\n}\n.hljs-bullet {\n /* prettylights-syntax-markup-list */\n color: #735c0f\n}\n.hljs-emphasis {\n /* prettylights-syntax-markup-italic */\n color: #24292e;\n font-style: italic\n}\n.hljs-strong {\n /* prettylights-syntax-markup-bold */\n color: #24292e;\n font-weight: bold\n}\n.hljs-addition {\n /* prettylights-syntax-markup-inserted */\n color: #22863a;\n background-color: #f0fff4\n}\n.hljs-deletion {\n /* prettylights-syntax-markup-deleted */\n color: #b31d28;\n background-color: #ffeef0\n}\n.hljs-char.escape_,\n.hljs-link,\n.hljs-params,\n.hljs-property,\n.hljs-punctuation,\n.hljs-tag {\n /* purposely ignored */\n \n}");
|
|
8
8
|
|
|
@@ -236,7 +236,7 @@ class Registry {
|
|
|
236
236
|
/**
|
|
237
237
|
* Add `plugin` to the registry, keyed by `name`.
|
|
238
238
|
*
|
|
239
|
-
* @param {object} plugin Plugin object with { name,
|
|
239
|
+
* @param {object} plugin Plugin object with string values for { name, type, filename }.
|
|
240
240
|
* @param {string} name The key used to retrieve and invoke the plugin.
|
|
241
241
|
*/
|
|
242
242
|
registerPlugin(plugin, name) {
|
|
@@ -263,33 +263,19 @@ class Registry {
|
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
/**
|
|
266
|
-
* Return
|
|
267
|
-
* Each entry contains
|
|
266
|
+
* Return an array of registered plugins that match type, all if not specified.
|
|
267
|
+
* Each entry contains {name, type, filename}
|
|
268
268
|
*
|
|
269
|
-
* @returns {Array<{
|
|
269
|
+
* @returns {Array<{name: string, type: string, filename: string}>}
|
|
270
270
|
*/
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
271
|
+
getPlugins(type) {
|
|
272
|
+
if (type) {
|
|
273
|
+
return Array.from(this._plugins.values()).filter(plugin => plugin.type === type)
|
|
274
|
+
} else {
|
|
275
|
+
return Array.from(this._plugins.values())
|
|
275
276
|
}
|
|
276
|
-
return manifest
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
/**
|
|
280
|
-
* Invoke `action` on the named plugin, passing `...args`.
|
|
281
|
-
* Returns null if the plugin is not found or does not define the action.
|
|
282
|
-
*
|
|
283
|
-
* @param {string} name The key used to identify the plugin.
|
|
284
|
-
* @param {string} action The action to invoke (e.g. 'export', 'import').
|
|
285
|
-
* @param {...*} args Arguments forwarded to the plugin action.
|
|
286
|
-
* @returns {*|null}
|
|
287
|
-
*/
|
|
288
|
-
invokePlugin(name, action, ...args) {
|
|
289
|
-
const plugin = this._plugins.get(name);
|
|
290
|
-
if (!plugin || typeof plugin[action] !== 'function') return null
|
|
291
|
-
return plugin[action](...args)
|
|
292
|
-
}
|
|
293
279
|
}
|
|
294
280
|
|
|
295
281
|
/**
|
|
@@ -369,29 +355,174 @@ function registerDelegate(delegate, name) {_registry.registerDelegate.bind(_regi
|
|
|
369
355
|
*/
|
|
370
356
|
function registerMessageHandler(handler, name) {_registry.registerMessageHandler.bind(_registry)(handler, name);}
|
|
371
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Add the `editor` to the registry.
|
|
360
|
+
*
|
|
361
|
+
* When we register an editor, it becomes the active editor.
|
|
362
|
+
*
|
|
363
|
+
* @param {MarkupEditor} editor The MarkupEditor to be added
|
|
364
|
+
*/
|
|
372
365
|
const registerEditor = _registry.registerEditor.bind(_registry);
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Remove the `editor` from the registry.
|
|
369
|
+
*
|
|
370
|
+
* @param {MarkupEditor} editor The MarkupEditor to be removed
|
|
371
|
+
*/
|
|
373
372
|
const unregisterEditor = _registry.unregisterEditor.bind(_registry);
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Remove the `delegate` from the registry.
|
|
376
|
+
*
|
|
377
|
+
* @param {MarkupDelegate | object} delegate A MarkupDelegate or appropriate object.
|
|
378
|
+
* @param {string} name A name used to retrieve the `delegate`.
|
|
379
|
+
*/
|
|
374
380
|
_registry.unregisterDelegate.bind(_registry);
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Return the `delegate` with `name`.
|
|
384
|
+
*
|
|
385
|
+
* @param {string} name A name used to retrieve the `delegate`.
|
|
386
|
+
* @returns {MarkupDelegate | object | undefined}
|
|
387
|
+
*/
|
|
375
388
|
const getDelegate = _registry.getDelegate.bind(_registry);
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Remove the `config` from the registry.
|
|
392
|
+
*
|
|
393
|
+
* @param {ToolbarConfig | KeymapConfig | BehaviorConfig | object} config One of the "configs" from the MarkupEditor API or appropriate object.
|
|
394
|
+
* @param {string} name A name used to retrieve the `config`.
|
|
395
|
+
*/
|
|
376
396
|
_registry.unregisterConfig.bind(_registry);
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Return the `config` with `name`.
|
|
400
|
+
*
|
|
401
|
+
* @param {string} name A name used to retrieve the `config`.
|
|
402
|
+
* @returns {ToolbarConfig | KeymapConfig | BehaviorConfig | object | undefined}
|
|
403
|
+
*/
|
|
377
404
|
const getConfig = _registry.getConfig.bind(_registry);
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Remove the `handler` from the registry.
|
|
408
|
+
*
|
|
409
|
+
* @param {MessageHandler | object} handler An instance of MessageHandler or appropriate object.
|
|
410
|
+
* @param {string} name A name used to retrieve the `handler`.
|
|
411
|
+
*/
|
|
378
412
|
_registry.unregisterMessageHandler.bind(_registry);
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Return the `handler` with `name`.
|
|
416
|
+
*
|
|
417
|
+
* @param {string} name A name used to retrieve the `handler`.
|
|
418
|
+
* @returns {MessageHandler | object | undefined}
|
|
419
|
+
*/
|
|
379
420
|
const getMessageHandler = _registry.getMessageHandler.bind(_registry);
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Remove the `toolbar` augmentation from the registry.
|
|
424
|
+
*
|
|
425
|
+
* @param {object} toolbar A toolbar that holds `cmdItems`.
|
|
426
|
+
* @param {string} name A name used to retrieve the `toolbar`.
|
|
427
|
+
*/
|
|
380
428
|
_registry.unregisterAugmentation.bind(_registry);
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Return the `toolbar` whose `menuItems` will be
|
|
432
|
+
* either prepended or appended to the normal MarkupEditor toolbar.
|
|
433
|
+
*
|
|
434
|
+
* @param {string} name A name used to retrieve the `toolbar`.
|
|
435
|
+
* @returns {object | undefined}
|
|
436
|
+
*/
|
|
381
437
|
const getAugmentation = _registry.getAugmentation.bind(_registry);
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Return the active editor with `muId` of the registry's `activeMuId`.
|
|
441
|
+
*
|
|
442
|
+
* @returns {MarkupEditor | undefined}
|
|
443
|
+
*/
|
|
382
444
|
_registry.activeEditor.bind(_registry);
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Return the active editor's `document` (could be the shadow root).
|
|
448
|
+
*
|
|
449
|
+
* @returns {Document | ShadowRoot | undefined}
|
|
450
|
+
*/
|
|
383
451
|
const activeDocument = _registry.activeDocument.bind(_registry);
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Set `activeMuId` based on `document` (could be a shadow root).
|
|
455
|
+
*
|
|
456
|
+
* @param {Document | ShadowRoot} document
|
|
457
|
+
*/
|
|
384
458
|
const setActiveDocument = _registry.setActiveDocument.bind(_registry);
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Return the active editor's element (typically with focus).
|
|
462
|
+
*
|
|
463
|
+
* @returns {Element | undefined}
|
|
464
|
+
*/
|
|
385
465
|
const activeEditorElement = _registry.activeEditorElement.bind(_registry);
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Return the active editor's `messageHandler`.
|
|
469
|
+
*
|
|
470
|
+
* @returns {MessageHandler | object | undefined}
|
|
471
|
+
*/
|
|
386
472
|
_registry.activeMessageHandler.bind(_registry);
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Return the active editor's instance of Searcher, `searcher`.
|
|
476
|
+
*
|
|
477
|
+
* @returns {object | undefined}
|
|
478
|
+
*/
|
|
387
479
|
const activeSearcher = _registry.activeSearcher.bind(_registry);
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Return the cached ID of the selected contentEditable element.
|
|
483
|
+
*
|
|
484
|
+
* @returns {string | undefined}
|
|
485
|
+
*/
|
|
388
486
|
const selectedID = _registry.selectedID.bind(_registry);
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Set/cache the ID of the selected contentEditable element to `string`.
|
|
490
|
+
*
|
|
491
|
+
* @param {string} string
|
|
492
|
+
*/
|
|
389
493
|
const setSelectedID = _registry.setSelectedID.bind(_registry);
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Add `plugin` to the registry, keyed by `name`.
|
|
497
|
+
*
|
|
498
|
+
* @param {object} plugin Plugin object with string values for { name, type, filename }.
|
|
499
|
+
* @param {string} name The key used to retrieve and invoke the plugin.
|
|
500
|
+
*/
|
|
390
501
|
const registerPlugin = _registry.registerPlugin.bind(_registry);
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Remove the plugin with `name` from the registry.
|
|
505
|
+
*
|
|
506
|
+
* @param {string} name The key used to identify the plugin.
|
|
507
|
+
*/
|
|
508
|
+
const unregisterPlugin = _registry.unregisterPlugin.bind(_registry);
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Return the plugin object with `name`, or undefined if not found.
|
|
512
|
+
*
|
|
513
|
+
* @param {string} name The key used to identify the plugin.
|
|
514
|
+
* @returns {object|undefined}
|
|
515
|
+
*/
|
|
516
|
+
const getPlugin = _registry.getPlugin.bind(_registry);
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Return an array of registered plugins that match `type`, all if not specified.
|
|
520
|
+
* Each entry contains {name, type, filename}
|
|
521
|
+
*
|
|
522
|
+
* @param {string} [type] The plugin type to filter by; all plugins are returned if omitted.
|
|
523
|
+
* @returns {Array<{name: string, type: string, filename: string}>}
|
|
524
|
+
*/
|
|
525
|
+
const getPlugins = _registry.getPlugins.bind(_registry);
|
|
395
526
|
|
|
396
527
|
/**
|
|
397
528
|
* MUError captures internal errors and makes it easy to communicate them externally.
|
|
@@ -12417,6 +12548,15 @@ function isSurrogatePair(str) {
|
|
|
12417
12548
|
let a = str.charCodeAt(0), b = str.charCodeAt(1);
|
|
12418
12549
|
return a >= 0xDC00 && a <= 0xDFFF && b >= 0xD800 && b <= 0xDBFF;
|
|
12419
12550
|
}
|
|
12551
|
+
|
|
12552
|
+
/**
|
|
12553
|
+
@internal
|
|
12554
|
+
*/
|
|
12555
|
+
const __parseFromClipboard = parseFromClipboard;
|
|
12556
|
+
/**
|
|
12557
|
+
@internal
|
|
12558
|
+
*/
|
|
12559
|
+
const __endComposition = endComposition;
|
|
12420
12560
|
/**
|
|
12421
12561
|
An editor view manages the DOM structure that represents an
|
|
12422
12562
|
editable document. Its state and behavior are determined by its
|
|
@@ -32597,11 +32737,18 @@ var HighlightJS = /*@__PURE__*/getDefaultExportFromCjs(commonExports);
|
|
|
32597
32737
|
* Delegates to hljs.getLanguage, which lowercases internally and checks
|
|
32598
32738
|
* both registered names and aliases, so this is case-insensitive and
|
|
32599
32739
|
* alias-aware.
|
|
32740
|
+
*
|
|
32741
|
+
* Plugins of type 'codeview' are also recognized languages in the sense
|
|
32742
|
+
* that UI that depends on the return value here needs to know they will
|
|
32743
|
+
* be rendered by the plugin.
|
|
32600
32744
|
*
|
|
32601
32745
|
* @param {string} name
|
|
32602
32746
|
* @returns {boolean}
|
|
32603
32747
|
*/
|
|
32604
32748
|
function isRecognizedLanguage(name) {
|
|
32749
|
+
let codeviews = getPlugins('codeview');
|
|
32750
|
+
let isCodeViewLanguage = codeviews.filter(plugin => plugin.name === name).length > 0;
|
|
32751
|
+
if (isCodeViewLanguage) { return true }
|
|
32605
32752
|
return !!HighlightJS.getLanguage((name ?? '').trim())
|
|
32606
32753
|
}
|
|
32607
32754
|
|
|
@@ -32629,6 +32776,61 @@ function presentCodeLanguages(doc) {
|
|
|
32629
32776
|
return Array.from(languages).sort()
|
|
32630
32777
|
}
|
|
32631
32778
|
|
|
32779
|
+
// hljs never has classPrefix reconfigured elsewhere in this codebase, so this
|
|
32780
|
+
// matches its own default. No public getter exists to read it back instead.
|
|
32781
|
+
const HLJS_CLASS_PREFIX = 'hljs-';
|
|
32782
|
+
|
|
32783
|
+
// Mirrors hljs's internal (unexported) scopeToCSSClass so class strings here
|
|
32784
|
+
// match what hljs's own HTML renderer would produce for the same scope name.
|
|
32785
|
+
function scopeToClass(name, prefix) {
|
|
32786
|
+
if (name.startsWith('language:')) return name.replace('language:', 'language-')
|
|
32787
|
+
if (name.includes('.')) {
|
|
32788
|
+
const pieces = name.split('.');
|
|
32789
|
+
return [`${prefix}${pieces.shift()}`, ...pieces.map((piece, i) => `${piece}${'_'.repeat(i + 1)}`)].join(' ')
|
|
32790
|
+
}
|
|
32791
|
+
return `${prefix}${name}`
|
|
32792
|
+
}
|
|
32793
|
+
|
|
32794
|
+
// Implements hljs's documented Renderer contract (addText/openNode/closeNode)
|
|
32795
|
+
// for TokenTree.walk, tracking span positions instead of building HTML.
|
|
32796
|
+
class SpanWalker {
|
|
32797
|
+
constructor(prefix) {
|
|
32798
|
+
this.prefix = prefix;
|
|
32799
|
+
this.position = 0;
|
|
32800
|
+
this.stack = [];
|
|
32801
|
+
this.spans = [];
|
|
32802
|
+
}
|
|
32803
|
+
|
|
32804
|
+
addText(text) {
|
|
32805
|
+
this.position += text.length;
|
|
32806
|
+
}
|
|
32807
|
+
|
|
32808
|
+
openNode(node) {
|
|
32809
|
+
if (node.scope) this.stack.push({ scope: node.scope, from: this.position });
|
|
32810
|
+
}
|
|
32811
|
+
|
|
32812
|
+
closeNode(node) {
|
|
32813
|
+
if (!node.scope) return
|
|
32814
|
+
const { scope, from } = this.stack.pop();
|
|
32815
|
+
this.spans.push({ from, to: this.position, class: scopeToClass(scope, this.prefix) });
|
|
32816
|
+
}
|
|
32817
|
+
}
|
|
32818
|
+
|
|
32819
|
+
/**
|
|
32820
|
+
* {from, to, class} spans for `code` highlighted as `language`, via hljs's
|
|
32821
|
+
* token tree (result._emitter.walk).
|
|
32822
|
+
*/
|
|
32823
|
+
function highlightSpans(code, language) {
|
|
32824
|
+
try {
|
|
32825
|
+
const result = HighlightJS.highlight(code, { language, ignoreIllegals: true });
|
|
32826
|
+
const walker = new SpanWalker(HLJS_CLASS_PREFIX);
|
|
32827
|
+
result._emitter.walk(walker);
|
|
32828
|
+
return walker.spans
|
|
32829
|
+
} catch {
|
|
32830
|
+
return []
|
|
32831
|
+
}
|
|
32832
|
+
}
|
|
32833
|
+
|
|
32632
32834
|
/**
|
|
32633
32835
|
* Define various arrays of tags used to represent MarkupEditor-specific concepts.
|
|
32634
32836
|
*
|
|
@@ -32962,21 +33164,61 @@ function pasteCode(text) {
|
|
|
32962
33164
|
|
|
32963
33165
|
/**
|
|
32964
33166
|
* Do a custom paste operation of "text only", which we will extract from the html
|
|
32965
|
-
* ourselves. First we get a node that conforms to the schema, which by definition
|
|
33167
|
+
* ourselves. First we get a node that conforms to the schema, which by definition
|
|
32966
33168
|
* only includes elements in a form we recognize, no spans, styles, etc.
|
|
32967
33169
|
* The trick here is that we want to use the same code to paste text as we do for
|
|
32968
33170
|
* HTML, but we want to paste something that is the MarkupEditor-equivalent of
|
|
32969
33171
|
* unformatted text.
|
|
32970
|
-
*
|
|
33172
|
+
*
|
|
32971
33173
|
* @param {string} html The HTML to be pasted
|
|
32972
33174
|
* @param {ClipboardEvent | null} event A mocked ClipboardEvent for testing
|
|
32973
33175
|
*/
|
|
32974
33176
|
function pasteText(html, event) {
|
|
32975
|
-
const node = _nodeFromHTML(html);
|
|
33177
|
+
const node = _nodeFromHTML(_preprocessPasteText(html));
|
|
32976
33178
|
const htmlFragment = fragmentFromNode(node);
|
|
32977
33179
|
const minimalHTML = _minimalHTML(htmlFragment); // Reduce to MarkupEditor-equivalent of "plain" text
|
|
32978
33180
|
pasteHTML(minimalHTML, event);
|
|
32979
33181
|
}
|
|
33182
|
+
/**
|
|
33183
|
+
* Preprocess raw text before it's parsed as HTML by _nodeFromHTML, converting
|
|
33184
|
+
* meaningful line structure into real markup so it survives.
|
|
33185
|
+
*
|
|
33186
|
+
* Only applied when there's an actual newline AND no real markup at all: that
|
|
33187
|
+
* combination means genuinely plain text (e.g., from a clipboard with no text/html
|
|
33188
|
+
* flavor available, or a plain-text-only source app) whose line structure is
|
|
33189
|
+
* meaningful — not HTML, where a run of whitespace (including newlines) collapses to
|
|
33190
|
+
* nothing meaningful once parsed. Gating on '\n' presence too, not just the absence
|
|
33191
|
+
* of '<', matters: some callers intentionally pass single-line text containing
|
|
33192
|
+
* literal HTML entities (e.g. "Hello <b>bold</b> world", meant to render
|
|
33193
|
+
* as that literal text, not a tag) with no '<' character anywhere in it either —
|
|
33194
|
+
* without the '\n' check, that would be misidentified as bare plain text needing
|
|
33195
|
+
* escaping and get double-escaped (< -> &lt;). No newline means there's
|
|
33196
|
+
* nothing this preprocessing could improve regardless, so it only ever applies to
|
|
33197
|
+
* input _nodeFromHTML couldn't already have handled correctly as HTML on its own.
|
|
33198
|
+
* @ignore
|
|
33199
|
+
*/
|
|
33200
|
+
function _preprocessPasteText(html) {
|
|
33201
|
+
return (html.includes('\n') && !html.includes('<')) ? _linebreaksToHTML(html) : html;
|
|
33202
|
+
}
|
|
33203
|
+
/**
|
|
33204
|
+
* Convert bare plain text (no HTML markup at all) into minimal paragraph/br-structured
|
|
33205
|
+
* HTML: a blank line (a run of two or more newlines) starts a new paragraph; a single
|
|
33206
|
+
* newline within a paragraph becomes a <br>. Text is HTML-escaped first via a detached
|
|
33207
|
+
* element's textContent/innerHTML round-trip, the standard safe way to escape arbitrary
|
|
33208
|
+
* text for insertion into an HTML string, so literal &, <, > in the source can't be
|
|
33209
|
+
* misinterpreted once this result is parsed as HTML by the caller.
|
|
33210
|
+
* @ignore
|
|
33211
|
+
*/
|
|
33212
|
+
function _linebreaksToHTML(text) {
|
|
33213
|
+
return text
|
|
33214
|
+
.split(/\n[ \t]*\n+/)
|
|
33215
|
+
.map(paragraph => {
|
|
33216
|
+
const div = document.createElement('div');
|
|
33217
|
+
div.textContent = paragraph;
|
|
33218
|
+
return `<p>${div.innerHTML.split('\n').join('<br>')}</p>`;
|
|
33219
|
+
})
|
|
33220
|
+
.join('');
|
|
33221
|
+
}
|
|
32980
33222
|
/**
|
|
32981
33223
|
* Return a minimal "unformatted equivalent" version of the HTML that is in fragment.
|
|
32982
33224
|
*
|
|
@@ -33792,22 +34034,21 @@ function codeBlockAtSelection(state) {
|
|
|
33792
34034
|
}
|
|
33793
34035
|
|
|
33794
34036
|
/**
|
|
33795
|
-
* Return `{pos, label}`
|
|
33796
|
-
*
|
|
33797
|
-
*
|
|
33798
|
-
*
|
|
33799
|
-
* <Capitalized name>" or "Language: None" — display-only, the underlying
|
|
34037
|
+
* Return `{pos, label}` for the language tab of the code_block at `state`'s
|
|
34038
|
+
* selection, or null if the selection isn't in a code_block. `pos` is the
|
|
34039
|
+
* position just inside the code_block's content. `label` is "Language:
|
|
34040
|
+
* <lowercase name>" or "Language: none" — display-only, the underlying
|
|
33800
34041
|
* `language` attribute value is untouched.
|
|
33801
34042
|
*
|
|
33802
34043
|
* @ignore
|
|
33803
34044
|
* @param {EditorState} state
|
|
33804
34045
|
* @returns {{pos: number, label: string} | null}
|
|
33805
34046
|
*/
|
|
33806
|
-
function
|
|
34047
|
+
function codeLanguageTabInfo(state) {
|
|
33807
34048
|
const found = codeBlockAtSelection(state);
|
|
33808
34049
|
if (!found) return null
|
|
33809
34050
|
const language = found.node.attrs.language;
|
|
33810
|
-
const label = language ? `Language: ${language.
|
|
34051
|
+
const label = language ? `Language: ${language.toLowerCase()}` : 'Language: none';
|
|
33811
34052
|
return { pos: found.pos + 1, label }
|
|
33812
34053
|
}
|
|
33813
34054
|
|
|
@@ -34833,7 +35074,7 @@ function _loadedUserFiles(target) {
|
|
|
34833
35074
|
* @param {MUError} error
|
|
34834
35075
|
*/
|
|
34835
35076
|
function _callbackError(error) {
|
|
34836
|
-
_callback(error.messageDict());
|
|
35077
|
+
_callback(JSON.stringify(error.messageDict()));
|
|
34837
35078
|
}
|
|
34838
35079
|
|
|
34839
35080
|
/**
|
|
@@ -35112,7 +35353,7 @@ function testPasteHTMLPreprocessing(html) {
|
|
|
35112
35353
|
* @param {string} html The HTML to paste
|
|
35113
35354
|
*/
|
|
35114
35355
|
function testPasteTextPreprocessing(html) {
|
|
35115
|
-
const node = _nodeFromHTML(html);
|
|
35356
|
+
const node = _nodeFromHTML(_preprocessPasteText(html));
|
|
35116
35357
|
const fragment = fragmentFromNode(node);
|
|
35117
35358
|
const minimalHTML = _minimalHTML(fragment);
|
|
35118
35359
|
return minimalHTML;
|
|
@@ -35133,12 +35374,12 @@ function testPresentCodeLanguages() {
|
|
|
35133
35374
|
return presentCodeLanguages(view.state.doc);
|
|
35134
35375
|
}
|
|
35135
35376
|
/**
|
|
35136
|
-
* For testing purposes, invoke
|
|
35377
|
+
* For testing purposes, invoke codeLanguageTabInfo on the active document's
|
|
35137
35378
|
* current selection.
|
|
35138
35379
|
*/
|
|
35139
|
-
function
|
|
35380
|
+
function testCodeLanguageTabInfo() {
|
|
35140
35381
|
const view = activeView();
|
|
35141
|
-
return
|
|
35382
|
+
return codeLanguageTabInfo(view.state);
|
|
35142
35383
|
}
|
|
35143
35384
|
/********************************************************************************
|
|
35144
35385
|
* Links
|
|
@@ -36085,6 +36326,20 @@ function consoleLog(string) {
|
|
|
36085
36326
|
};
|
|
36086
36327
|
_callback(JSON.stringify(messageDict));
|
|
36087
36328
|
}
|
|
36329
|
+
/**
|
|
36330
|
+
* Report an error to the delegate's markupError method, the same path internal
|
|
36331
|
+
* MUErrors use. For external plugins that have their own error conditions
|
|
36332
|
+
* (e.g., a failed render) but no access to the internal MUError/_callbackError
|
|
36333
|
+
* machinery directly.
|
|
36334
|
+
*
|
|
36335
|
+
* @param {string} code A short identifier for the error.
|
|
36336
|
+
* @param {string} message The error message.
|
|
36337
|
+
* @param {string} [info] Optional additional detail.
|
|
36338
|
+
* @param {boolean} [alert] Whether the host app should treat this as alert-worthy. Defaults to true.
|
|
36339
|
+
*/
|
|
36340
|
+
function reportError(code, message, info, alert=true) {
|
|
36341
|
+
_callbackError(new MUError(code, message, info, alert));
|
|
36342
|
+
}
|
|
36088
36343
|
|
|
36089
36344
|
var visibility = {
|
|
36090
36345
|
toolbar: true,
|
|
@@ -37683,7 +37938,7 @@ class Dropdown {
|
|
|
37683
37938
|
*/
|
|
37684
37939
|
render(view) {
|
|
37685
37940
|
let options = this.options;
|
|
37686
|
-
let content = renderDropdownItems(this.content, view);
|
|
37941
|
+
let content = renderDropdownItems([this.content], view);
|
|
37687
37942
|
let win = view.dom.ownerDocument.defaultView || window;
|
|
37688
37943
|
let indicator = crelt("span", "\u25BE");
|
|
37689
37944
|
setClass(indicator, this.prefix + "-dropdown-indicator", true);
|
|
@@ -37853,7 +38108,7 @@ class DropdownSubmenu {
|
|
|
37853
38108
|
*/
|
|
37854
38109
|
render(view) {
|
|
37855
38110
|
let options = this.options;
|
|
37856
|
-
let items = renderDropdownItems(this.content, view);
|
|
38111
|
+
let items = renderDropdownItems([this.content], view);
|
|
37857
38112
|
let win = view.dom.ownerDocument.defaultView || window;
|
|
37858
38113
|
let label = crelt("div", { class: this.prefix + "-submenu-label" }, translate(view, this.options.label || ""));
|
|
37859
38114
|
let submenu = crelt("div", { class: this.prefix + "-submenu" }, items.dom);
|
|
@@ -38491,15 +38746,11 @@ class LanguageDialogItem extends DialogItem {
|
|
|
38491
38746
|
this.languageArea.addEventListener('keydown', e => { // Use keydown because 'input' isn't triggered for Enter
|
|
38492
38747
|
if (e.key === 'Enter') {
|
|
38493
38748
|
e.preventDefault();
|
|
38494
|
-
//
|
|
38495
|
-
//
|
|
38496
|
-
//
|
|
38497
|
-
//
|
|
38498
|
-
|
|
38499
|
-
this.submit();
|
|
38500
|
-
} else {
|
|
38501
|
-
this.closeDialog();
|
|
38502
|
-
}
|
|
38749
|
+
// OK is always the default action, even for an empty or unrecognized
|
|
38750
|
+
// language — entering something not yet in the recognized set (e.g. a
|
|
38751
|
+
// new fenced-code language) must work on Enter alone, without first
|
|
38752
|
+
// having to click OK to override Cancel as the default.
|
|
38753
|
+
this.submit();
|
|
38503
38754
|
} else if (e.key === 'Tab') {
|
|
38504
38755
|
e.preventDefault();
|
|
38505
38756
|
} else if (e.key === 'Escape') {
|
|
@@ -38511,25 +38762,12 @@ class LanguageDialogItem extends DialogItem {
|
|
|
38511
38762
|
|
|
38512
38763
|
/**
|
|
38513
38764
|
* Whether the input currently has any language name entered, recognized or
|
|
38514
|
-
* not. An empty field means "no language" — never colored invalid
|
|
38515
|
-
* also not something recognized, so it alone doesn't make OK the default
|
|
38516
|
-
* button; see hasRecognizedLanguage().
|
|
38765
|
+
* not. An empty field means "no language" — never colored invalid.
|
|
38517
38766
|
*/
|
|
38518
38767
|
hasContent() {
|
|
38519
38768
|
return this.languageArea.value.trim().length > 0
|
|
38520
38769
|
}
|
|
38521
38770
|
|
|
38522
|
-
/**
|
|
38523
|
-
* Whether the input currently holds a recognized language name. Drives which
|
|
38524
|
-
* of OK/Cancel looks like the default button — Cancel is default when the
|
|
38525
|
-
* field is empty OR holds an unsupported name, OK only once a supported
|
|
38526
|
-
* language is entered. Submission itself is never blocked either way (OK
|
|
38527
|
-
* stays enabled always); this only changes which button LOOKS default.
|
|
38528
|
-
*/
|
|
38529
|
-
hasRecognizedLanguage() {
|
|
38530
|
-
return this.hasContent() && isRecognizedLanguage(this.languageArea.value)
|
|
38531
|
-
}
|
|
38532
|
-
|
|
38533
38771
|
/**
|
|
38534
38772
|
* Color the input text red (the same .invalid convention ImageItem's srcArea
|
|
38535
38773
|
* uses) when it has an unsupported language entered. Called on every keystroke;
|
|
@@ -38544,10 +38782,11 @@ class LanguageDialogItem extends DialogItem {
|
|
|
38544
38782
|
/**
|
|
38545
38783
|
* Create and append the OK/Cancel buttons, positioned and styled like LinkItem's:
|
|
38546
38784
|
* a left-side spacer so buttonsDiv's space-between pushes the OK/Cancel group to
|
|
38547
|
-
* the right, and
|
|
38548
|
-
*
|
|
38549
|
-
*
|
|
38550
|
-
* error to block on
|
|
38785
|
+
* the right, and OK always the default (active-styled) button, regardless of
|
|
38786
|
+
* whether the field is empty or holds an unrecognized language — an unrecognized
|
|
38787
|
+
* language is a valid, deliberate choice (e.g. a new fenced-code language), not
|
|
38788
|
+
* an error to block submission on. Both buttons stay enabled always; only OK's
|
|
38789
|
+
* active styling is a fixed constant, not state-dependent.
|
|
38551
38790
|
*
|
|
38552
38791
|
* @param {EditorView} view
|
|
38553
38792
|
*/
|
|
@@ -38562,7 +38801,7 @@ class LanguageDialogItem extends DialogItem {
|
|
|
38562
38801
|
let okItem = cmdItem(() => this.submit(), {
|
|
38563
38802
|
class: prefix + '-menuitem',
|
|
38564
38803
|
title: 'OK',
|
|
38565
|
-
active: () => { return
|
|
38804
|
+
active: () => { return true },
|
|
38566
38805
|
enable: () => { return true }
|
|
38567
38806
|
});
|
|
38568
38807
|
let {dom: okDom, update: okUpdate} = okItem.render(view);
|
|
@@ -38573,7 +38812,7 @@ class LanguageDialogItem extends DialogItem {
|
|
|
38573
38812
|
let cancelItem = cmdItem(() => this.closeDialog(), {
|
|
38574
38813
|
class: prefix + '-menuitem',
|
|
38575
38814
|
title: 'Cancel',
|
|
38576
|
-
active: () => { return
|
|
38815
|
+
active: () => { return false },
|
|
38577
38816
|
enable: () => { return true }
|
|
38578
38817
|
});
|
|
38579
38818
|
let {dom: cancelDom, update: cancelUpdate} = cancelItem.render(view);
|
|
@@ -38597,18 +38836,6 @@ class LanguageDialogItem extends DialogItem {
|
|
|
38597
38836
|
|
|
38598
38837
|
}
|
|
38599
38838
|
|
|
38600
|
-
/**
|
|
38601
|
-
* Capitalize just the first character of `text`, leaving the rest as-is
|
|
38602
|
-
* (e.g. "html" -> "Html", "weirdLanguage" -> "WeirdLanguage"). Display-only —
|
|
38603
|
-
* the underlying language value stays exactly as entered/stored.
|
|
38604
|
-
*
|
|
38605
|
-
* @param {string} text
|
|
38606
|
-
* @returns {string}
|
|
38607
|
-
*/
|
|
38608
|
-
function capitalized(text) {
|
|
38609
|
-
return text ? text.charAt(0).toUpperCase() + text.slice(1) : text
|
|
38610
|
-
}
|
|
38611
|
-
|
|
38612
38839
|
/**
|
|
38613
38840
|
* Find the code_block containing `state`'s selection and return its `language`
|
|
38614
38841
|
* attribute, or null if the selection isn't in a code_block or the block has
|
|
@@ -38657,15 +38884,24 @@ class CodeLanguageSubmenu {
|
|
|
38657
38884
|
}
|
|
38658
38885
|
|
|
38659
38886
|
buildItems(view) {
|
|
38660
|
-
let
|
|
38661
|
-
|
|
38662
|
-
label: 'Language: None',
|
|
38887
|
+
let noneItem = cmdItem(() => { this.setLanguage(view, null); return true }, {
|
|
38888
|
+
label: 'Language: none',
|
|
38663
38889
|
enable: () => true,
|
|
38664
38890
|
active: (state) => currentCodeLanguage(state) == null
|
|
38665
|
-
})
|
|
38891
|
+
});
|
|
38892
|
+
let dialogItem = cmdItem(() => {
|
|
38893
|
+
this.languageDialog.open(view, currentCodeLanguage(view.state) ?? '', (entered) => {
|
|
38894
|
+
this.setLanguage(view, entered ? entered : null);
|
|
38895
|
+
});
|
|
38896
|
+
return true
|
|
38897
|
+
}, {
|
|
38898
|
+
label: 'Language...',
|
|
38899
|
+
enable: () => true
|
|
38900
|
+
});
|
|
38901
|
+
let languageItems = [];
|
|
38666
38902
|
for (let language of presentCodeLanguages(view.state.doc)) {
|
|
38667
|
-
|
|
38668
|
-
label:
|
|
38903
|
+
languageItems.push(cmdItem(() => { this.setLanguage(view, language); return true }, {
|
|
38904
|
+
label: language.toLowerCase(),
|
|
38669
38905
|
enable: () => true,
|
|
38670
38906
|
// presentCodeLanguages lowercases its results for dedup, but the selected block's
|
|
38671
38907
|
// own attribute value keeps whatever case was originally typed — compare case-
|
|
@@ -38673,16 +38909,9 @@ class CodeLanguageSubmenu {
|
|
|
38673
38909
|
active: (state) => (currentCodeLanguage(state) ?? '').toLowerCase() === language
|
|
38674
38910
|
}));
|
|
38675
38911
|
}
|
|
38676
|
-
|
|
38677
|
-
|
|
38678
|
-
|
|
38679
|
-
});
|
|
38680
|
-
return true
|
|
38681
|
-
}, {
|
|
38682
|
-
label: 'Language...',
|
|
38683
|
-
enable: () => true
|
|
38684
|
-
}));
|
|
38685
|
-
return items
|
|
38912
|
+
// Grouped so renderDropdownItems provides separators. Safe to pass languageItems
|
|
38913
|
+
// even when empty — renderDropdownItems drops structurally empty groups.
|
|
38914
|
+
return [[noneItem, dialogItem], languageItems]
|
|
38686
38915
|
}
|
|
38687
38916
|
|
|
38688
38917
|
render(view) {
|
|
@@ -39143,7 +39372,7 @@ class TableCreateSubmenu {
|
|
|
39143
39372
|
render(view) {
|
|
39144
39373
|
let resetSize = this.resetSize.bind(this);
|
|
39145
39374
|
let options = this.options;
|
|
39146
|
-
let items = renderDropdownItems(this.content, view);
|
|
39375
|
+
let items = renderDropdownItems([this.content], view);
|
|
39147
39376
|
this.itemsUpdate = items.update; // Track the update method so we can update as the mouse is over items
|
|
39148
39377
|
let win = view.dom.ownerDocument.defaultView || window;
|
|
39149
39378
|
let label = crelt("div", { class: this.prefix + "-submenu-label" }, translate(view, this.options.label || ""));
|
|
@@ -39500,6 +39729,17 @@ function separator() {
|
|
|
39500
39729
|
return crelt("span", { class: prefix + "-menuseparator" });
|
|
39501
39730
|
}
|
|
39502
39731
|
|
|
39732
|
+
/**
|
|
39733
|
+
* Return a div for a separator between groups of MenuItems in a dropdown-item list
|
|
39734
|
+
* (vertical layout) — distinct from `separator()`, which is styled for the toolbar
|
|
39735
|
+
* bar's horizontal layout.
|
|
39736
|
+
*
|
|
39737
|
+
* @private
|
|
39738
|
+
*/
|
|
39739
|
+
function dropdownSeparator() {
|
|
39740
|
+
return crelt("div", { class: prefix + "-menu-dropdown-separator" });
|
|
39741
|
+
}
|
|
39742
|
+
|
|
39503
39743
|
/**
|
|
39504
39744
|
* Return whether the selection in state is within a mark of type `markType`.
|
|
39505
39745
|
* @private
|
|
@@ -39641,19 +39881,55 @@ function renderGroupedFit(view, content, wrapAtIndex) {
|
|
|
39641
39881
|
}
|
|
39642
39882
|
|
|
39643
39883
|
/**
|
|
39644
|
-
* Render the items in a Dropdown in the view.
|
|
39645
|
-
*
|
|
39646
|
-
*
|
|
39647
|
-
*
|
|
39648
|
-
*
|
|
39884
|
+
* Render the items in a Dropdown in the view, as a list of dropdown-item rows.
|
|
39885
|
+
*
|
|
39886
|
+
* `groups` is an array of arrays: each sub-array is a group of MenuItems, and a
|
|
39887
|
+
* separator row is inserted between adjacent groups that both have content for
|
|
39888
|
+
* the current state (auto-hidden otherwise). Mirrors `renderGrouped`'s toolbar-bar
|
|
39889
|
+
* behavior, but keeps the dropdown-item row wrapper/CSS instead of switching to
|
|
39890
|
+
* the toolbar's menuitem span.
|
|
39891
|
+
*
|
|
39892
|
+
* @param {Array<Array<MenuItem>>} groups The groups of MenuItems to be rendered in a Dropdown
|
|
39893
|
+
* @param {EditorView} view The EditorView to render the items in
|
|
39894
|
+
* @returns {object} An object containing the DOM (items and separators, in display order) for the Dropdown and an update function to display it
|
|
39649
39895
|
*/
|
|
39650
|
-
function renderDropdownItems(
|
|
39651
|
-
|
|
39652
|
-
|
|
39653
|
-
|
|
39654
|
-
|
|
39655
|
-
|
|
39656
|
-
|
|
39896
|
+
function renderDropdownItems(groups, view) {
|
|
39897
|
+
// Drop structurally empty groups (e.g. no present languages) up front, so a
|
|
39898
|
+
// separator is never placed next to a group that was never going to render
|
|
39899
|
+
// anything — update() below only knows how to auto-hide a separator when the
|
|
39900
|
+
// adjacent group rendered items that are merely inactive/disabled for the
|
|
39901
|
+
// current state, not when the group had zero items to begin with.
|
|
39902
|
+
let nonEmptyGroups = groups.filter(items => items.length > 0);
|
|
39903
|
+
let rendered = [], updates = [], separators = [];
|
|
39904
|
+
for (let i = 0; i < nonEmptyGroups.length; i++) {
|
|
39905
|
+
let items = nonEmptyGroups[i], localUpdates = [], localNodes = [];
|
|
39906
|
+
for (let j = 0; j < items.length; j++) {
|
|
39907
|
+
let { dom, update } = items[j].render(view);
|
|
39908
|
+
let div = crelt("div", { class: prefix + "-menu-dropdown-item" }, dom);
|
|
39909
|
+
rendered.push(div);
|
|
39910
|
+
localNodes.push(div);
|
|
39911
|
+
localUpdates.push(update);
|
|
39912
|
+
}
|
|
39913
|
+
updates.push(combineUpdates(localUpdates, localNodes));
|
|
39914
|
+
if (i < nonEmptyGroups.length - 1) {
|
|
39915
|
+
let sep = dropdownSeparator();
|
|
39916
|
+
rendered.push(sep);
|
|
39917
|
+
separators.push(sep);
|
|
39918
|
+
}
|
|
39919
|
+
}
|
|
39920
|
+
function update(state) {
|
|
39921
|
+
let something = false, needSep = false;
|
|
39922
|
+
for (let i = 0; i < updates.length; i++) {
|
|
39923
|
+
let hasContent = updates[i](state);
|
|
39924
|
+
if (i)
|
|
39925
|
+
separators[i - 1].style.display = needSep && hasContent ? "" : "none";
|
|
39926
|
+
needSep = hasContent;
|
|
39927
|
+
if (hasContent)
|
|
39928
|
+
something = true;
|
|
39929
|
+
}
|
|
39930
|
+
return something;
|
|
39931
|
+
}
|
|
39932
|
+
return { dom: rendered, update };
|
|
39657
39933
|
}
|
|
39658
39934
|
|
|
39659
39935
|
function combineUpdates(updates, nodes) {
|
|
@@ -40542,78 +40818,37 @@ const tablePlugin = new Plugin({
|
|
|
40542
40818
|
}
|
|
40543
40819
|
});
|
|
40544
40820
|
|
|
40545
|
-
|
|
40821
|
+
function decorationsForSpans(pos, spans) {
|
|
40822
|
+
return spans.map(({from, to, class: cls}) => Decoration.inline(pos + 1 + from, pos + 1 + to, {class: cls}))
|
|
40823
|
+
}
|
|
40546
40824
|
|
|
40547
|
-
|
|
40548
|
-
|
|
40549
|
-
|
|
40550
|
-
|
|
40551
|
-
|
|
40552
|
-
|
|
40553
|
-
*
|
|
40554
|
-
* @ignore
|
|
40555
|
-
*/
|
|
40556
|
-
function highlightSpecs(code, language) {
|
|
40557
|
-
let html;
|
|
40558
|
-
try {
|
|
40559
|
-
html = HighlightJS.highlight(code, {language, ignoreIllegals: true}).value;
|
|
40560
|
-
} catch {
|
|
40561
|
-
return []
|
|
40562
|
-
}
|
|
40563
|
-
const container = document.createElement('div');
|
|
40564
|
-
container.innerHTML = html;
|
|
40565
|
-
const specs = [];
|
|
40566
|
-
let offset = 0;
|
|
40567
|
-
function walk(parent) {
|
|
40568
|
-
parent.childNodes.forEach((child) => {
|
|
40569
|
-
if (child.nodeType === Node.TEXT_NODE) {
|
|
40570
|
-
offset += child.textContent.length;
|
|
40571
|
-
} else if (child.nodeType === Node.ELEMENT_NODE) {
|
|
40572
|
-
const start = offset;
|
|
40573
|
-
walk(child);
|
|
40574
|
-
const cls = child.getAttribute('class');
|
|
40575
|
-
if (cls) specs.push({from: start, to: offset, class: cls});
|
|
40576
|
-
}
|
|
40577
|
-
});
|
|
40578
|
-
}
|
|
40579
|
-
walk(container);
|
|
40580
|
-
return specs
|
|
40825
|
+
// null for anything that isn't a highlightable code_block, so callers can
|
|
40826
|
+
// skip it (no cache entry, no decorations) with a single check.
|
|
40827
|
+
function highlightEntryFor(node) {
|
|
40828
|
+
const language = node.attrs.language;
|
|
40829
|
+
if (!language || !isRecognizedLanguage(language)) return null
|
|
40830
|
+
return {node, spans: highlightSpans(node.textContent, language)}
|
|
40581
40831
|
}
|
|
40582
40832
|
|
|
40583
|
-
|
|
40584
|
-
|
|
40585
|
-
* DecorationSet highlighting each one, using a WeakMap cache keyed by node
|
|
40586
|
-
* identity so unchanged blocks (same node reference across transactions,
|
|
40587
|
-
* per ProseMirror's persistent-tree structural sharing) are never re-run
|
|
40588
|
-
* through hljs.highlight().
|
|
40589
|
-
*
|
|
40590
|
-
* @ignore
|
|
40591
|
-
*/
|
|
40592
|
-
function computeCodeHighlightDecorations(doc) {
|
|
40833
|
+
function computeAllHighlights(doc) {
|
|
40834
|
+
const cache = new Map();
|
|
40593
40835
|
const decorations = [];
|
|
40594
40836
|
doc.descendants((node, pos) => {
|
|
40595
40837
|
if (node.type.name !== 'code_block') return
|
|
40596
|
-
const
|
|
40597
|
-
if (
|
|
40598
|
-
|
|
40599
|
-
|
|
40600
|
-
|
|
40601
|
-
codeHighlightCache.set(node, specs);
|
|
40602
|
-
}
|
|
40603
|
-
specs.forEach(({from, to, class: cls}) => {
|
|
40604
|
-
decorations.push(Decoration.inline(pos + 1 + from, pos + 1 + to, {class: cls}));
|
|
40605
|
-
});
|
|
40838
|
+
const entry = highlightEntryFor(node);
|
|
40839
|
+
if (entry) {
|
|
40840
|
+
cache.set(pos, entry);
|
|
40841
|
+
decorations.push(...decorationsForSpans(pos, entry.spans));
|
|
40842
|
+
}
|
|
40606
40843
|
return false
|
|
40607
40844
|
});
|
|
40608
|
-
return DecorationSet.create(doc, decorations)
|
|
40845
|
+
return {cache, decorations: DecorationSet.create(doc, decorations)}
|
|
40609
40846
|
}
|
|
40610
40847
|
|
|
40611
40848
|
/**
|
|
40612
40849
|
* Walk cur's children, comparing against old's, skipping any subtree that's
|
|
40613
|
-
* the same node object as before
|
|
40614
|
-
*
|
|
40615
|
-
* nodes in the changed region, so cost is bounded by how much of the doc
|
|
40616
|
-
* actually changed rather than the whole document.
|
|
40850
|
+
* the same node object as before. Bounds cost to the changed region rather
|
|
40851
|
+
* than the whole document.
|
|
40617
40852
|
*
|
|
40618
40853
|
* @ignore
|
|
40619
40854
|
*/
|
|
@@ -40633,149 +40868,73 @@ function changedDescendants(old, cur, offset, f) {
|
|
|
40633
40868
|
}
|
|
40634
40869
|
}
|
|
40635
40870
|
|
|
40636
|
-
|
|
40637
|
-
|
|
40638
|
-
|
|
40639
|
-
|
|
40640
|
-
|
|
40641
|
-
|
|
40642
|
-
|
|
40643
|
-
|
|
40871
|
+
// Carries cache entries forward by position + Node.eq, not object identity;
|
|
40872
|
+
// changedDescendants then fills in anything new or actually changed.
|
|
40873
|
+
function updateHighlights(tr, {cache}) {
|
|
40874
|
+
const nextCache = new Map();
|
|
40875
|
+
const decorations = [];
|
|
40876
|
+
const carryForward = (pos, entry) => {
|
|
40877
|
+
nextCache.set(pos, entry);
|
|
40878
|
+
decorations.push(...decorationsForSpans(pos, entry.spans));
|
|
40879
|
+
};
|
|
40880
|
+
|
|
40881
|
+
cache.forEach(({node, spans}, pos) => {
|
|
40882
|
+
const mapped = tr.mapping.mapResult(pos);
|
|
40883
|
+
if (mapped.deleted) return
|
|
40884
|
+
const newNode = tr.doc.nodeAt(mapped.pos);
|
|
40885
|
+
if (newNode && newNode.eq(node)) carryForward(mapped.pos, {node: newNode, spans});
|
|
40886
|
+
});
|
|
40887
|
+
|
|
40888
|
+
changedDescendants(tr.before, tr.doc, 0, (node, pos) => {
|
|
40889
|
+
if (node.type.name !== 'code_block' || nextCache.has(pos)) return
|
|
40890
|
+
const entry = highlightEntryFor(node);
|
|
40891
|
+
if (entry) carryForward(pos, entry);
|
|
40892
|
+
});
|
|
40893
|
+
|
|
40894
|
+
return {cache: nextCache, decorations: DecorationSet.create(tr.doc, decorations)}
|
|
40895
|
+
}
|
|
40896
|
+
|
|
40897
|
+
// Syntax-highlighting decorations for code_block nodes with a registered
|
|
40898
|
+
// language. Only registered via markupSetup() when highlightCode is on.
|
|
40644
40899
|
const codeHighlightPlugin = new Plugin({
|
|
40645
40900
|
state: {
|
|
40646
40901
|
init(_, {doc}) {
|
|
40647
|
-
return
|
|
40902
|
+
return computeAllHighlights(doc)
|
|
40648
40903
|
},
|
|
40649
|
-
apply(tr,
|
|
40650
|
-
|
|
40651
|
-
let touchedCodeBlock = false;
|
|
40652
|
-
const checkCodeBlock = (node) => {
|
|
40653
|
-
if (node.type.name === 'code_block') touchedCodeBlock = true;
|
|
40654
|
-
};
|
|
40655
|
-
// Check both directions: a code_block added/changed in the new doc, and one
|
|
40656
|
-
// removed (e.g. by undoing a paragraph->code_block conversion) from the old
|
|
40657
|
-
// doc. changedDescendants(old, cur, ...) only ever visits cur's children, so
|
|
40658
|
-
// catching removal requires calling it again with old/new swapped.
|
|
40659
|
-
changedDescendants(tr.before, tr.doc, 0, checkCodeBlock);
|
|
40660
|
-
if (!touchedCodeBlock) changedDescendants(tr.doc, tr.before, 0, checkCodeBlock);
|
|
40661
|
-
if (!touchedCodeBlock) return set.map(tr.mapping, tr.doc)
|
|
40662
|
-
return computeCodeHighlightDecorations(tr.doc)
|
|
40904
|
+
apply(tr, value) {
|
|
40905
|
+
return tr.docChanged ? updateHighlights(tr, value) : value
|
|
40663
40906
|
}
|
|
40664
40907
|
},
|
|
40665
40908
|
props: {
|
|
40666
|
-
decorations(state) { return codeHighlightPlugin.getState(state) }
|
|
40909
|
+
decorations(state) { return codeHighlightPlugin.getState(state).decorations }
|
|
40667
40910
|
}
|
|
40668
40911
|
});
|
|
40669
40912
|
|
|
40670
40913
|
/**
|
|
40671
|
-
*
|
|
40672
|
-
*
|
|
40673
|
-
* doesn't need to be pixel-exact, just enough to decide which side to attach to.
|
|
40674
|
-
*
|
|
40675
|
-
* @ignore
|
|
40676
|
-
*/
|
|
40677
|
-
const CODE_LANGUAGE_OVERLAY_HEIGHT = 24;
|
|
40678
|
-
|
|
40679
|
-
/**
|
|
40680
|
-
* Whether there's room above `preDOM` (the code_block's own <pre> element) to show
|
|
40681
|
-
* the language overlay label there without it being pushed above the toolbar or
|
|
40682
|
-
* off-screen — if not, it should attach below instead. `view.nodeDOM` is used
|
|
40683
|
-
* read-only here (getBoundingClientRect only); mutating its result is what caused
|
|
40684
|
-
* the CPU-loop regression fixed earlier, so this must never write to preDOM.
|
|
40685
|
-
*
|
|
40686
|
-
* @ignore
|
|
40687
|
-
*/
|
|
40688
|
-
function hasRoomAboveOverlay(view, preDOM) {
|
|
40689
|
-
if (!preDOM) return true
|
|
40690
|
-
const preRect = preDOM.getBoundingClientRect();
|
|
40691
|
-
const toolbarRect = getToolbar(view)?.getBoundingClientRect();
|
|
40692
|
-
const minTop = (toolbarRect?.bottom ?? 0) + CODE_LANGUAGE_OVERLAY_HEIGHT;
|
|
40693
|
-
return preRect.top >= minTop
|
|
40694
|
-
}
|
|
40695
|
-
|
|
40696
|
-
/**
|
|
40697
|
-
* Compute the semi-transparent "Language: <name>" widget Decoration for the
|
|
40698
|
-
* selected code_block, if any. Recomputed on every transaction — this is only
|
|
40699
|
-
* a selection lookup plus a string, not a doc walk, so unlike
|
|
40700
|
-
* computeCodeHighlightDecorations there's no need to cache or gate on
|
|
40701
|
-
* tr.docChanged.
|
|
40702
|
-
*
|
|
40703
|
-
* @ignore
|
|
40704
|
-
*/
|
|
40705
|
-
function computeCodeLanguageOverlayDecorations(state, languageDialog) {
|
|
40706
|
-
const info = codeLanguageOverlayInfo(state);
|
|
40707
|
-
if (!info) return DecorationSet.empty
|
|
40708
|
-
// An empty code_block's only content would become this widget's contentEditable=false
|
|
40709
|
-
// button — the exact "non-editable content alone in a textblock" case prosemirror-view
|
|
40710
|
-
// itself flags as fragile in Safari/Chrome (addTextblockHacks, working around Safari
|
|
40711
|
-
// bug #1165 and Chrome bug #1152). Skip the overlay until there's actual code; there's
|
|
40712
|
-
// nothing useful to show a language badge over yet anyway.
|
|
40713
|
-
const found = codeBlockAtSelection(state);
|
|
40714
|
-
if (!found || found.node.content.size === 0) return DecorationSet.empty
|
|
40715
|
-
const widget = Decoration.widget(info.pos, (view) => {
|
|
40716
|
-
const button = document.createElement('button');
|
|
40717
|
-
button.type = 'button';
|
|
40718
|
-
button.className = prefix + '-code-language-overlay';
|
|
40719
|
-
if (!hasRoomAboveOverlay(view, view.nodeDOM(found.pos))) {
|
|
40720
|
-
button.classList.add(prefix + '-code-language-overlay-below');
|
|
40721
|
-
}
|
|
40722
|
-
button.textContent = info.label;
|
|
40723
|
-
// Without this, the button is ambiguous to the browser's native cursor placement
|
|
40724
|
-
// as part of the code_block's editable text flow.
|
|
40725
|
-
button.contentEditable = 'false';
|
|
40726
|
-
button.addEventListener('mousedown', (e) => {
|
|
40727
|
-
e.preventDefault();
|
|
40728
|
-
const found = codeBlockAtSelection(view.state);
|
|
40729
|
-
languageDialog.open(view, found?.node.attrs.language ?? '', (entered) => {
|
|
40730
|
-
setCodeLanguageCommand(entered ? entered : null)(view.state, view.dispatch, view);
|
|
40731
|
-
});
|
|
40732
|
-
});
|
|
40733
|
-
return button
|
|
40734
|
-
}, {
|
|
40735
|
-
side: 1,
|
|
40736
|
-
// By default the cursor at the widget's position is strictly kept on the
|
|
40737
|
-
// `side` indicated above, and per prosemirror-view's own docs "keyboard
|
|
40738
|
-
// cursor motion will not, without further custom handling, visit both
|
|
40739
|
-
// sides of the widget" — which is what broke placing/moving the cursor to
|
|
40740
|
-
// a code_block's start. relaxedSide lets the DOM selection land on either
|
|
40741
|
-
// side instead of being force-pinned to one.
|
|
40742
|
-
relaxedSide: true,
|
|
40743
|
-
// Keyed so ProseMirror reuses the existing button DOM node (and its click listener)
|
|
40744
|
-
// across transactions unrelated to this block, instead of destroying and rebuilding it
|
|
40745
|
-
// on every single transaction while a code block is selected (WidgetType.eq() only
|
|
40746
|
-
// short-circuits reuse on a spec.key match). The key includes pos and label — not just
|
|
40747
|
-
// a static string — so a genuinely different block or language change still gets a
|
|
40748
|
-
// fresh toDOM call rather than silently reusing stale button text.
|
|
40749
|
-
key: `code-language-overlay-${info.pos}-${info.label}`
|
|
40750
|
-
});
|
|
40751
|
-
return DecorationSet.create(state.doc, [widget])
|
|
40752
|
-
}
|
|
40753
|
-
|
|
40754
|
-
/**
|
|
40755
|
-
* Build the plugin that shows the selected code_block's language overlay.
|
|
40756
|
-
* A factory (not a module-level singleton like codeHighlightPlugin) because it
|
|
40757
|
-
* owns a LanguageDialogItem bound to this editor instance's `config` — sharing
|
|
40758
|
-
* one across multiple `<markup-editor>` instances on the same page would let
|
|
40759
|
-
* one instance's dialog state stomp on another's.
|
|
40914
|
+
* Return the plugin that shows the selected code_block's language tab (owned
|
|
40915
|
+
* by its CodeView NodeView, ../nodeview/codeview.js).
|
|
40760
40916
|
*
|
|
40761
40917
|
* @ignore
|
|
40762
40918
|
*/
|
|
40763
|
-
function
|
|
40764
|
-
|
|
40765
|
-
|
|
40766
|
-
|
|
40767
|
-
|
|
40768
|
-
|
|
40769
|
-
|
|
40770
|
-
|
|
40771
|
-
|
|
40772
|
-
|
|
40773
|
-
|
|
40774
|
-
|
|
40775
|
-
|
|
40919
|
+
function codeLanguageTabPlugin() {
|
|
40920
|
+
return new Plugin({
|
|
40921
|
+
view(editorView) {
|
|
40922
|
+
let activeCodeView = null;
|
|
40923
|
+
// A pure selection-only transaction never calls a NodeView's own
|
|
40924
|
+
// update(), so this reaches the CodeView directly via view.nodeDOM(pos)
|
|
40925
|
+
// instead.
|
|
40926
|
+
const sync = (view) => {
|
|
40927
|
+
const found = codeBlockAtSelection(view.state);
|
|
40928
|
+
const nextCodeView = found ? view.nodeDOM(found.pos)?.codeView : null;
|
|
40929
|
+
if (nextCodeView === activeCodeView) return
|
|
40930
|
+
activeCodeView?.setActive(false);
|
|
40931
|
+
nextCodeView?.setActive(true);
|
|
40932
|
+
activeCodeView = nextCodeView;
|
|
40933
|
+
};
|
|
40934
|
+
sync(editorView);
|
|
40935
|
+
return { update: sync }
|
|
40776
40936
|
}
|
|
40777
|
-
})
|
|
40778
|
-
return thePlugin
|
|
40937
|
+
})
|
|
40779
40938
|
}
|
|
40780
40939
|
|
|
40781
40940
|
const searchModePlugin = new Plugin({
|
|
@@ -40924,10 +41083,10 @@ function markupSetup(config, schema) {
|
|
|
40924
41083
|
plugins.push(tablePlugin);
|
|
40925
41084
|
|
|
40926
41085
|
// Add the plugins that highlight code blocks and show the selected block's
|
|
40927
|
-
// language
|
|
41086
|
+
// language tab, if enabled in behavior config
|
|
40928
41087
|
if (config.behavior.highlightCode) {
|
|
40929
41088
|
plugins.push(codeHighlightPlugin);
|
|
40930
|
-
plugins.push(
|
|
41089
|
+
plugins.push(codeLanguageTabPlugin());
|
|
40931
41090
|
}
|
|
40932
41091
|
|
|
40933
41092
|
// Add the plugin that handles placeholder display for an empty document, as passed in config
|
|
@@ -41562,6 +41721,102 @@ class DivView {
|
|
|
41562
41721
|
|
|
41563
41722
|
}
|
|
41564
41723
|
|
|
41724
|
+
// Approximate rendered height of the language tab (font-size 0.75rem +
|
|
41725
|
+
// padding 2px 6px, styles/markup.css) — only used for the room-above check below,
|
|
41726
|
+
// doesn't need to be pixel-exact.
|
|
41727
|
+
const CODE_LANGUAGE_TAB_HEIGHT = 24;
|
|
41728
|
+
|
|
41729
|
+
/**
|
|
41730
|
+
* NodeView for code_block. The language tab is a DOM sibling of contentDOM,
|
|
41731
|
+
* appended/removed by setActive. No selectNode/deselectNode — code_block
|
|
41732
|
+
* selection is a TextSelection inside its content, never a NodeSelection.
|
|
41733
|
+
*
|
|
41734
|
+
* Stores `view` directly rather than this codebase's activeView() lookup
|
|
41735
|
+
* (imageview.js): the tab's mousedown only ever fires from this view's own
|
|
41736
|
+
* DOM, so there's no cross-instance ambiguity to resolve.
|
|
41737
|
+
*
|
|
41738
|
+
* node.attrs.language only ever affects the tab's label and contentDOM's
|
|
41739
|
+
* class — never which code_block this applies to or how it's structured.
|
|
41740
|
+
*/
|
|
41741
|
+
class CodeView {
|
|
41742
|
+
constructor(node, view, getPos, languageDialog) {
|
|
41743
|
+
this.view = view;
|
|
41744
|
+
this.languageDialog = languageDialog;
|
|
41745
|
+
this.dom = document.createElement('pre');
|
|
41746
|
+
this.contentDOM = document.createElement('code');
|
|
41747
|
+
this.dom.appendChild(this.contentDOM);
|
|
41748
|
+
// codeLanguageTabPlugin (setup/index.js) reads this via
|
|
41749
|
+
// view.nodeDOM(pos) to call setActive on the right instance.
|
|
41750
|
+
this.dom.codeView = this;
|
|
41751
|
+
this.syncLanguageClass(node);
|
|
41752
|
+
this.tab = this.buildTab();
|
|
41753
|
+
this.setTabLabel(node);
|
|
41754
|
+
}
|
|
41755
|
+
|
|
41756
|
+
update(node) {
|
|
41757
|
+
if (node.type.name !== 'code_block') return false
|
|
41758
|
+
this.syncLanguageClass(node);
|
|
41759
|
+
this.setTabLabel(node);
|
|
41760
|
+
return true
|
|
41761
|
+
}
|
|
41762
|
+
|
|
41763
|
+
// ProseMirror's default ignoreMutation is `!contentDOM && mutation.type !=
|
|
41764
|
+
// "selection"`. Override here to avoid losing tab during mutation.
|
|
41765
|
+
ignoreMutation(mutation) {
|
|
41766
|
+
return mutation.type !== 'selection' && !this.contentDOM.contains(mutation.target)
|
|
41767
|
+
}
|
|
41768
|
+
|
|
41769
|
+
// Whether there's room above this.dom to show the language tab
|
|
41770
|
+
// without it being pushed above the toolbar or off-screen.
|
|
41771
|
+
hasRoomAbove() {
|
|
41772
|
+
if (!this.dom) return true
|
|
41773
|
+
const preRect = this.dom.getBoundingClientRect();
|
|
41774
|
+
const toolbarRect = getToolbar(this.view)?.getBoundingClientRect();
|
|
41775
|
+
const minTop = (toolbarRect?.bottom ?? 0) + CODE_LANGUAGE_TAB_HEIGHT;
|
|
41776
|
+
return preRect.top >= minTop
|
|
41777
|
+
}
|
|
41778
|
+
|
|
41779
|
+
// The tab sits outside contentDOM, similar to the way imageView works
|
|
41780
|
+
// for handles.
|
|
41781
|
+
setActive(isActive) {
|
|
41782
|
+
if (isActive && !this.tab.isConnected) {
|
|
41783
|
+
this.tab.classList.toggle(prefix + '-code-language-tab-below', !this.hasRoomAbove());
|
|
41784
|
+
this.dom.appendChild(this.tab);
|
|
41785
|
+
} else if (!isActive && this.tab.isConnected) {
|
|
41786
|
+
this.dom.removeChild(this.tab);
|
|
41787
|
+
}
|
|
41788
|
+
}
|
|
41789
|
+
|
|
41790
|
+
destroy() {
|
|
41791
|
+
this.dom.codeView = null;
|
|
41792
|
+
}
|
|
41793
|
+
|
|
41794
|
+
syncLanguageClass(node) {
|
|
41795
|
+
const language = node.attrs.language;
|
|
41796
|
+
this.contentDOM.className = language ? `language-${language}` : '';
|
|
41797
|
+
}
|
|
41798
|
+
|
|
41799
|
+
setTabLabel(node) {
|
|
41800
|
+
const language = node.attrs.language;
|
|
41801
|
+
this.tab.textContent = language ? `Language: ${language.toLowerCase()}` : 'Language: none';
|
|
41802
|
+
}
|
|
41803
|
+
|
|
41804
|
+
buildTab() {
|
|
41805
|
+
const button = document.createElement('button');
|
|
41806
|
+
button.type = 'button';
|
|
41807
|
+
button.className = prefix + '-code-language-tab';
|
|
41808
|
+
button.contentEditable = 'false';
|
|
41809
|
+
button.addEventListener('mousedown', (e) => {
|
|
41810
|
+
e.preventDefault();
|
|
41811
|
+
const found = codeBlockAtSelection(this.view.state);
|
|
41812
|
+
this.languageDialog.open(this.view, found?.node.attrs.language ?? '', (entered) => {
|
|
41813
|
+
setCodeLanguageCommand(entered ? entered : null)(this.view.state, this.view.dispatch, this.view);
|
|
41814
|
+
});
|
|
41815
|
+
});
|
|
41816
|
+
return button
|
|
41817
|
+
}
|
|
41818
|
+
}
|
|
41819
|
+
|
|
41565
41820
|
/**
|
|
41566
41821
|
* The MessageHandler receives `postMessage` from the MarkupEditor as the document state changes.
|
|
41567
41822
|
*
|
|
@@ -42004,10 +42259,19 @@ class MarkupEditor {
|
|
|
42004
42259
|
this.config.delegate = getDelegate(delegate);
|
|
42005
42260
|
}
|
|
42006
42261
|
|
|
42262
|
+
// One dialog per editor instance, not per code_block — sharing one across
|
|
42263
|
+
// multiple <markup-editor> instances on the same page would let one
|
|
42264
|
+
// instance's dialog state stomp on another's. In contrast to the other
|
|
42265
|
+
// dialogs, like LinkDialog, the languageDialog can be initiated from
|
|
42266
|
+
// the tab that shows when it is selected, not just the menu item. This
|
|
42267
|
+
// necessitates creating it here so we can pass it to the CodeView.
|
|
42268
|
+
const languageDialog = new LanguageDialogItem(this.config);
|
|
42269
|
+
MU.languageDialog = languageDialog;
|
|
42270
|
+
|
|
42007
42271
|
// Create the EditorView for this MarkupEditor
|
|
42008
42272
|
this.view = new EditorView(this.element, {
|
|
42009
42273
|
state: EditorState.create({
|
|
42010
|
-
// For the MarkupEditor, we can just use the editor element.
|
|
42274
|
+
// For the MarkupEditor, we can just use the editor element.
|
|
42011
42275
|
// There is no need to use a separate content element.
|
|
42012
42276
|
doc: DOMParser.fromSchema(schema).parse(this.element),
|
|
42013
42277
|
plugins: markupSetup(this.config, schema)
|
|
@@ -42016,6 +42280,7 @@ class MarkupEditor {
|
|
|
42016
42280
|
link(node, view, getPos) { return new LinkView(node, view, getPos) },
|
|
42017
42281
|
image(node, view, getPos) { return new ImageView(node, view, getPos) },
|
|
42018
42282
|
div(node, view, getPos) { return new DivView(node, view, getPos) },
|
|
42283
|
+
code_block(node, view, getPos) { return new CodeView(node, view, getPos, languageDialog) },
|
|
42019
42284
|
},
|
|
42020
42285
|
// All text input makes callbacks to indicate the document state has changed.
|
|
42021
42286
|
// For history, used handleTextInput, but that fires *before* input happens.
|
|
@@ -42157,6 +42422,7 @@ const MU = {
|
|
|
42157
42422
|
cancelSearch,
|
|
42158
42423
|
canUndo,
|
|
42159
42424
|
canRedo,
|
|
42425
|
+
CodeView,
|
|
42160
42426
|
consoleLog,
|
|
42161
42427
|
cutImage,
|
|
42162
42428
|
deactivateSearch,
|
|
@@ -42193,6 +42459,7 @@ const MU = {
|
|
|
42193
42459
|
removeAllDivs,
|
|
42194
42460
|
removeButton,
|
|
42195
42461
|
removeDiv,
|
|
42462
|
+
reportError,
|
|
42196
42463
|
resetSelection,
|
|
42197
42464
|
savedDataImage,
|
|
42198
42465
|
searchFor,
|
|
@@ -42203,7 +42470,7 @@ const MU = {
|
|
|
42203
42470
|
setToolbarVisible,
|
|
42204
42471
|
setTopLevelAttributes,
|
|
42205
42472
|
testBlockquoteEnter,
|
|
42206
|
-
|
|
42473
|
+
testCodeLanguageTabInfo,
|
|
42207
42474
|
testExtractContents,
|
|
42208
42475
|
testIsRecognizedLanguage,
|
|
42209
42476
|
testListEnter,
|
|
@@ -42236,8 +42503,9 @@ const MU = {
|
|
|
42236
42503
|
registerDelegate,
|
|
42237
42504
|
registerMessageHandler,
|
|
42238
42505
|
registerPlugin,
|
|
42239
|
-
|
|
42240
|
-
|
|
42506
|
+
unregisterPlugin,
|
|
42507
|
+
getPlugin,
|
|
42508
|
+
getPlugins,
|
|
42241
42509
|
};
|
|
42242
42510
|
|
|
42243
42511
|
sheet.media.mediaText = '(prefers-color-scheme: dark)';
|
|
@@ -42254,22 +42522,20 @@ sheet.media.mediaText = '(prefers-color-scheme: dark)';
|
|
|
42254
42522
|
*
|
|
42255
42523
|
* @param {string[]} pluginPaths Resolved paths to plugin modules.
|
|
42256
42524
|
* @param {object|null} delegate A MarkupDelegate instance (may be null/undefined).
|
|
42257
|
-
* @param {function} [importFn] Optional import function; defaults to the native dynamic
|
|
42258
|
-
* import. Provided for testing.
|
|
42259
42525
|
* @returns {Promise<void>}
|
|
42260
42526
|
*/
|
|
42261
|
-
async function loadPlugins(pluginPaths, delegate
|
|
42527
|
+
async function loadPlugins(pluginPaths, delegate) {
|
|
42262
42528
|
if (!pluginPaths || pluginPaths.length === 0) return
|
|
42263
|
-
const before = new Set(MU.
|
|
42529
|
+
const before = new Set(MU.getPlugins().map(m => m.name));
|
|
42264
42530
|
await Promise.all(
|
|
42265
42531
|
pluginPaths.map(path =>
|
|
42266
|
-
|
|
42532
|
+
import(path).catch(err => {
|
|
42267
42533
|
console.error('Plugin load failed:', path, err);
|
|
42268
42534
|
return null
|
|
42269
42535
|
})
|
|
42270
42536
|
)
|
|
42271
42537
|
);
|
|
42272
|
-
const after = MU.
|
|
42538
|
+
const after = MU.getPlugins();
|
|
42273
42539
|
const newManifests = after.filter(m => !before.has(m.name));
|
|
42274
42540
|
delegate?.markupPluginsDidLoad && delegate.markupPluginsDidLoad(newManifests);
|
|
42275
42541
|
}
|
|
@@ -42330,7 +42596,7 @@ class MarkupEditorElement extends HTMLElement {
|
|
|
42330
42596
|
const pluginPaths = pluginsAttr ? JSON.parse(pluginsAttr) : [];
|
|
42331
42597
|
const delegate = this.editor.config?.delegate;
|
|
42332
42598
|
await loadPlugins(pluginPaths, delegate);
|
|
42333
|
-
const manifests = MU.
|
|
42599
|
+
const manifests = MU.getPlugins();
|
|
42334
42600
|
if (manifests.length > 0) {
|
|
42335
42601
|
this.editor.messageHandler.postMessage(
|
|
42336
42602
|
JSON.stringify({ messageType: 'markupPluginsDidLoad', plugins: manifests })
|
|
@@ -42507,4 +42773,4 @@ class MarkupEditorElement extends HTMLElement {
|
|
|
42507
42773
|
// Let the browser know about the custom element
|
|
42508
42774
|
customElements.define('markup-editor', MarkupEditorElement);
|
|
42509
42775
|
|
|
42510
|
-
export { ContentMatch, DOMParser, DOMSerializer, Fragment, MU, Mark, MarkType, Node$1 as Node, NodeRange, NodeType$1 as NodeType, ReplaceError, ResolvedPos, Schema, Slice, loadPlugins };
|
|
42776
|
+
export { AllSelection, ContentMatch, DOMParser, DOMSerializer, Decoration, DecorationSet, EditorState, EditorView, Fragment, MU, Mark, MarkType, Node$1 as Node, NodeRange, NodeSelection, NodeType$1 as NodeType, Plugin, PluginKey, ReplaceError, ResolvedPos, Schema, Selection, SelectionRange, Slice, TextSelection, Transaction, __endComposition, __parseFromClipboard, loadPlugins };
|