From 74b4751a1c110b4c824b14369d3a5eea3ad5816a Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sun, 16 Apr 2023 16:05:16 +0100 Subject: [PATCH] CM6: Aligned styling with existing, improved theme handling --- package-lock.json | 29 +- package.json | 2 + resources/js/code/index.mjs | 72 +++-- resources/js/code/languages.js | 8 +- resources/js/code/legacy-modes.mjs | 1 - resources/js/code/setups.js | 39 ++- resources/js/code/themes.js | 50 ++- resources/js/code/views.js | 43 +-- resources/sass/_codemirror.scss | 469 ++--------------------------- resources/sass/_pages.scss | 4 + resources/sass/_text.scss | 9 +- 11 files changed, 186 insertions(+), 540 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1c75d341b..8e28c3057 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,10 +7,12 @@ "dependencies": { "@codemirror/commands": "^6.0.1", "@codemirror/lang-css": "^6.1.1", + "@codemirror/lang-html": "^6.4.3", "@codemirror/lang-javascript": "^6.1.6", "@codemirror/lang-json": "^6.0.1", "@codemirror/lang-markdown": "^6.0.1", "@codemirror/lang-php": "^6.0.0", + "@codemirror/lang-xml": "^6.0.2", "@codemirror/language": "^6.2.1", "@codemirror/legacy-modes": "^6.1.0", "@codemirror/state": "^6.1.0", @@ -75,9 +77,9 @@ } }, "node_modules/@codemirror/lang-html": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.2.tgz", - "integrity": "sha512-bqCBASkteKySwtIbiV/WCtGnn/khLRbbiV5TE+d9S9eQJD7BA4c5dTRm2b3bVmSpilff5EYxvB4PQaZzM/7cNw==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.3.tgz", + "integrity": "sha512-VKzQXEC8nL69Jg2hvAFPBwOdZNvL8tMFOrdFwWpU+wc6a6KEkndJ/19R5xSaglNX6v2bttm8uIEFYxdQDcIZVQ==", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/lang-css": "^6.0.0", @@ -138,6 +140,18 @@ "@lezer/php": "^1.0.0" } }, + "node_modules/@codemirror/lang-xml": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.0.2.tgz", + "integrity": "sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, "node_modules/@codemirror/language": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz", @@ -646,6 +660,15 @@ "@lezer/lr": "^1.1.0" } }, + "node_modules/@lezer/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==", + "dependencies": { + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@ssddanbrown/codemirror-lang-smarty": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@ssddanbrown/codemirror-lang-smarty/-/codemirror-lang-smarty-1.0.0.tgz", diff --git a/package.json b/package.json index 349f1ae8d..a7ef86fcc 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,12 @@ "dependencies": { "@codemirror/commands": "^6.0.1", "@codemirror/lang-css": "^6.1.1", + "@codemirror/lang-html": "^6.4.3", "@codemirror/lang-javascript": "^6.1.6", "@codemirror/lang-json": "^6.0.1", "@codemirror/lang-markdown": "^6.0.1", "@codemirror/lang-php": "^6.0.0", + "@codemirror/lang-xml": "^6.0.2", "@codemirror/language": "^6.2.1", "@codemirror/legacy-modes": "^6.1.0", "@codemirror/state": "^6.1.0", diff --git a/resources/js/code/index.mjs b/resources/js/code/index.mjs index 2b1aa9e74..07dcd53c2 100644 --- a/resources/js/code/index.mjs +++ b/resources/js/code/index.mjs @@ -1,7 +1,6 @@ import {EditorView, keymap} from "@codemirror/view"; -import {copyTextToClipboard} from "../services/clipboard.js" -// Modes +import {copyTextToClipboard} from "../services/clipboard.js" import {viewer, editor} from "./setups.js"; import {createView, updateViewLanguage} from "./views.js"; @@ -46,13 +45,11 @@ function highlightElem(elem) { const ev = createView({ parent: wrapper, doc: content, - extensions: viewer(), + extensions: viewer(wrapper), }); + setMode(ev, langName, content); - window.cm = ev; - elem.remove(); - addCopyIcon(ev); } @@ -61,19 +58,31 @@ function highlightElem(elem) { * @param {EditorView} editorView */ function addCopyIcon(editorView) { - const copyIcon = ``; + const copyIcon = ``; + const checkIcon = ``; const copyButton = document.createElement('button'); copyButton.setAttribute('type', 'button') copyButton.classList.add('cm-copy-button'); copyButton.innerHTML = copyIcon; editorView.dom.appendChild(copyButton); + const notifyTime = 620; + const transitionTime = 60; copyButton.addEventListener('click', event => { copyTextToClipboard(editorView.state.doc.toString()); copyButton.classList.add('success'); + + setTimeout(() => { + copyButton.innerHTML = checkIcon; + }, transitionTime / 2); + setTimeout(() => { copyButton.classList.remove('success'); - }, 240); + }, notifyTime); + + setTimeout(() => { + copyButton.innerHTML = copyIcon; + }, notifyTime + (transitionTime / 2)); }); } @@ -93,17 +102,18 @@ function getTheme() { * @param {HTMLElement} cmContainer * @param {String} content * @param {String} language - * @returns {{wrap: Element, editor: *}} + * @returns {EditorView} */ export function wysiwygView(cmContainer, content, language) { - return CodeMirror(cmContainer, { - value: content, - mode: getMode(language, content), - lineNumbers: true, - lineWrapping: false, - theme: getTheme(), - readOnly: true + const ev = createView({ + parent: cmContainer, + doc: content, + extensions: viewer(cmContainer), }); + + setMode(ev, language, content); + + return ev; } @@ -132,15 +142,29 @@ export function popupEditor(elem, modeSuggestion) { * Create an inline editor to replace the given textarea. * @param {HTMLTextAreaElement} textArea * @param {String} mode - * @returns {CodeMirror3} + * @returns {EditorView} */ export function inlineEditor(textArea, mode) { - return CodeMirror.fromTextArea(textArea, { - mode: getMode(mode, textArea.value), - lineNumbers: true, - lineWrapping: false, - theme: getTheme(), - }); + const content = textArea.value; + const config = { + parent: textArea.parentNode, + doc: content, + extensions: [ + ...editor(textArea.parentElement), + EditorView.updateListener.of((v) => { + if (v.docChanged) { + textArea.value = v.state.doc.toString(); + } + }), + ], + }; + + // Create editor view, hide original input + const ev = createView(config); + setMode(ev, mode, content); + textArea.style.display = 'none'; + + return ev; } /** @@ -188,7 +212,7 @@ export function markdownEditor(elem, onChange, domEventHandlers, keyBindings) { parent: elem.parentNode, doc: content, extensions: [ - ...editor('markdown'), + ...editor(elem.parentElement), EditorView.updateListener.of((v) => { onChange(v); }), diff --git a/resources/js/code/languages.js b/resources/js/code/languages.js index 21ce69e6d..e7bac2a18 100644 --- a/resources/js/code/languages.js +++ b/resources/js/code/languages.js @@ -3,9 +3,11 @@ import {StreamLanguage} from "@codemirror/language" import {css} from '@codemirror/lang-css'; import {json} from '@codemirror/lang-json'; import {javascript} from '@codemirror/lang-javascript'; +import {html} from "@codemirror/lang-html"; import {markdown} from '@codemirror/lang-markdown'; import {php} from '@codemirror/lang-php'; -export {twig} from "@ssddanbrown/codemirror-lang-twig"; +import {twig} from "@ssddanbrown/codemirror-lang-twig"; +import {xml} from "@codemirror/lang-xml"; const legacyLoad = async (mode) => { const modes = await window.importVersioned('legacy-modes'); @@ -32,7 +34,7 @@ const modeMap = { go: () => legacyLoad('go'), haskell: () => legacyLoad('haskell'), hs: () => legacyLoad('haskell'), - html: () => legacyLoad('html'), + html: async () => html(), ini: () => legacyLoad('properties'), java: () => legacyLoad('java'), javascript: async () => javascript(), @@ -89,7 +91,7 @@ const modeMap = { vbscript: () => legacyLoad('vbScript'), 'vb.net': () => legacyLoad('vb'), vbnet: () => legacyLoad('vb'), - xml: () => legacyLoad('xml'), + xml: async () => xml(), yaml: () => legacyLoad('yaml'), yml: () => legacyLoad('yaml'), }; diff --git a/resources/js/code/legacy-modes.mjs b/resources/js/code/legacy-modes.mjs index 857ea6787..c3a1a1132 100644 --- a/resources/js/code/legacy-modes.mjs +++ b/resources/js/code/legacy-modes.mjs @@ -23,6 +23,5 @@ export {swift} from "@codemirror/legacy-modes/mode/swift"; export {toml} from '@codemirror/legacy-modes/mode/toml'; export {vb} from '@codemirror/legacy-modes/mode/vb'; export {vbScript} from '@codemirror/legacy-modes/mode/vbscript'; -export {xml, html} from '@codemirror/legacy-modes/mode/xml'; export {yaml} from '@codemirror/legacy-modes/mode/yaml'; export {smarty} from "@ssddanbrown/codemirror-lang-smarty"; \ No newline at end of file diff --git a/resources/js/code/setups.js b/resources/js/code/setups.js index 19f8c6c2e..842917285 100644 --- a/resources/js/code/setups.js +++ b/resources/js/code/setups.js @@ -1,22 +1,34 @@ - import {EditorView, keymap, drawSelection, highlightActiveLine, dropCursor, rectangularSelection, lineNumbers, highlightActiveLineGutter} from "@codemirror/view" -import {syntaxHighlighting, bracketMatching} from "@codemirror/language" +import {bracketMatching} from "@codemirror/language" import {defaultKeymap, history, historyKeymap} from "@codemirror/commands" import {EditorState} from "@codemirror/state" +import {getTheme} from "./themes"; -import {defaultLight} from "./themes"; - -export function viewer() { +/** + * @param {Element} parentEl + * @return {(Extension[]|{extension: Extension}|readonly Extension[])[]} + */ +function common(parentEl) { return [ + getTheme(parentEl), lineNumbers(), highlightActiveLineGutter(), drawSelection(), dropCursor(), - // syntaxHighlighting(defaultLight, {fallback: false}), bracketMatching(), rectangularSelection(), highlightActiveLine(), + ]; +} + +/** + * @param {Element} parentEl + * @return {*[]} + */ +export function viewer(parentEl) { + return [ + ...common(parentEl), keymap.of([ ...defaultKeymap, ]), @@ -24,17 +36,14 @@ export function viewer() { ]; } -export function editor(language) { +/** + * @param {Element} parentEl + * @return {*[]} + */ +export function editor(parentEl) { return [ - lineNumbers(), - highlightActiveLineGutter(), + ...common(parentEl), history(), - drawSelection(), - dropCursor(), - syntaxHighlighting(defaultLight, {fallback: true}), - bracketMatching(), - rectangularSelection(), - highlightActiveLine(), keymap.of([ ...defaultKeymap, ...historyKeymap, diff --git a/resources/js/code/themes.js b/resources/js/code/themes.js index 43feb2d53..492ca4a99 100644 --- a/resources/js/code/themes.js +++ b/resources/js/code/themes.js @@ -1,7 +1,9 @@ import {tags} from "@lezer/highlight"; -import {HighlightStyle} from "@codemirror/language"; +import {HighlightStyle, syntaxHighlighting} from "@codemirror/language"; +import {EditorView} from "@codemirror/view"; +import {oneDarkHighlightStyle, oneDarkTheme} from "@codemirror/theme-one-dark"; -export const defaultLight = HighlightStyle.define([ +const defaultLightHighlightStyle = HighlightStyle.define([ { tag: tags.meta, color: "#388938" }, { tag: tags.link, @@ -43,4 +45,46 @@ export const defaultLight = HighlightStyle.define([ color: "#940" }, { tag: tags.invalid, color: "#f00" } -]); \ No newline at end of file +]); + +const defaultThemeSpec = { + "&": { + color: "#000", + }, + "&.cm-focused": { + outline: "none", + }, + ".cm-line": { + lineHeight: "1.6", + }, +}; + +/** + * Get the theme extension to use for editor view instance. + * @returns {Extension[]} + */ +export function getTheme(viewParentEl) { + const darkMode = document.documentElement.classList.contains('dark-mode'); + let viewTheme = darkMode ? oneDarkTheme : EditorView.theme(defaultThemeSpec); + let highlightStyle = darkMode ? oneDarkHighlightStyle : defaultLightHighlightStyle; + + const eventData = { + darkModeActive: darkMode, + registerViewTheme(builder) { + const spec = builder(); + if (spec) { + viewTheme = EditorView.theme(spec); + } + }, + registerHighlightStyle(builder) { + const tagStyles = builder(tags) || []; + if (tagStyles.length) { + highlightStyle = HighlightStyle.define(tagStyles); + } + } + }; + + window.$events.emitPublic(viewParentEl, 'library-cm6::configure-theme', eventData); + + return [viewTheme, syntaxHighlighting(highlightStyle)]; +} \ No newline at end of file diff --git a/resources/js/code/views.js b/resources/js/code/views.js index 54490bc29..603b3cf8d 100644 --- a/resources/js/code/views.js +++ b/resources/js/code/views.js @@ -1,9 +1,6 @@ -import {getLanguageExtension} from "./languages" -import {HighlightStyle, syntaxHighlighting} from "@codemirror/language"; -import {Compartment} from "@codemirror/state" -import {EditorView} from "@codemirror/view" -import {oneDarkTheme, oneDarkHighlightStyle} from "@codemirror/theme-one-dark" -import {tags} from "@lezer/highlight" +import {Compartment} from "@codemirror/state"; +import {EditorView} from "@codemirror/view"; +import {getLanguageExtension} from "./languages"; const viewLangCompartments = new WeakMap(); @@ -16,7 +13,6 @@ const viewLangCompartments = new WeakMap(); export function createView(config) { const langCompartment = new Compartment(); config.extensions.push(langCompartment.of([])); - config.extensions.push(getTheme(config.parent)); const ev = new EditorView(config); @@ -25,37 +21,6 @@ export function createView(config) { return ev; } -/** - * Get the theme extension to use for editor view instance. - * @returns {Extension[]} - */ -function getTheme(viewParentEl) { - const darkMode = document.documentElement.classList.contains('dark-mode'); - let viewTheme = darkMode ? oneDarkTheme : []; - let highlightStyle = darkMode ? oneDarkHighlightStyle : null; - - const eventData = { - darkModeActive: darkMode, - registerViewTheme(builder) { - const spec = builder(); - if (spec) { - viewTheme = EditorView.theme(spec); - } - }, - registerHighlightStyle(builder) { - const tagStyles = builder(tags) || []; - console.log('called', tagStyles); - if (tagStyles.length) { - highlightStyle = HighlightStyle.define(tagStyles); - } - } - }; - - window.$events.emitPublic(viewParentEl, 'library-cm6::configure-theme', eventData); - - return [viewTheme, highlightStyle ? syntaxHighlighting(highlightStyle) : []]; -} - /** * Set the language mode of an EditorView. * @@ -69,5 +34,5 @@ export async function updateViewLanguage(ev, modeSuggestion, content) { ev.dispatch({ effects: compartment.reconfigure(language ? language : []) - }) + }); } \ No newline at end of file diff --git a/resources/sass/_codemirror.scss b/resources/sass/_codemirror.scss index b26e236a8..910b216e4 100644 --- a/resources/sass/_codemirror.scss +++ b/resources/sass/_codemirror.scss @@ -1,446 +1,14 @@ -/* BASICS */ - -.CodeMirror { - /* Set height, width, borders, and global font properties here */ - font-family: monospace; - height: 300px; - color: black; - direction: ltr; -} - -/* PADDING */ - -.CodeMirror-lines { - padding: 4px 0; /* Vertical padding around content */ -} -.CodeMirror pre.CodeMirror-line, -.CodeMirror pre.CodeMirror-line-like { - padding: 0 4px; /* Horizontal padding of content */ -} - -.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - background-color: white; /* The little square between H and V scrollbars */ -} - -/* GUTTER */ - -.CodeMirror-gutters { - border-right: 1px solid #ddd; - background-color: #f7f7f7; - white-space: nowrap; -} -.CodeMirror-linenumbers {} -.CodeMirror-linenumber { - padding: 0 3px 0 5px; - min-width: 20px; - text-align: right; - color: #999; - white-space: nowrap; -} - -.CodeMirror-guttermarker { color: black; } -.CodeMirror-guttermarker-subtle { color: #999; } - -/* CURSOR */ - -.CodeMirror-cursor { - border-left: 1px solid black; - border-right: none; - width: 0; -} -/* Shown when moving in bi-directional text */ -.CodeMirror div.CodeMirror-secondarycursor { - border-left: 1px solid silver; -} -.cm-fat-cursor .CodeMirror-cursor { - width: auto; - border: 0 !important; - background: #7e7; -} -.cm-fat-cursor div.CodeMirror-cursors { - z-index: 1; -} -.cm-fat-cursor-mark { - background-color: rgba(20, 255, 20, 0.5); - -webkit-animation: blink 1.06s steps(1) infinite; - -moz-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; -} -.cm-animate-fat-cursor { - width: auto; - border: 0; - -webkit-animation: blink 1.06s steps(1) infinite; - -moz-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; - background-color: #7e7; -} -@-moz-keyframes blink { - 0% {} - 50% { background-color: transparent; } - 100% {} -} -@-webkit-keyframes blink { - 0% {} - 50% { background-color: transparent; } - 100% {} -} -@keyframes blink { - 0% {} - 50% { background-color: transparent; } - 100% {} -} - -/* Can style cursor different in overwrite (non-insert) mode */ -.CodeMirror-overwrite .CodeMirror-cursor {} - -.cm-tab { display: inline-block; text-decoration: inherit; } - -.CodeMirror-rulers { - position: absolute; - left: 0; right: 0; top: -50px; bottom: 0; - overflow: hidden; -} -.CodeMirror-ruler { - border-left: 1px solid #ccc; - top: 0; bottom: 0; - position: absolute; -} - -/* DEFAULT THEME */ - -.cm-s-default .cm-header {color: blue;} -.cm-s-default .cm-quote {color: #090;} -.cm-negative {color: #d44;} -.cm-positive {color: #292;} -.cm-header, .cm-strong {font-weight: bold;} -.cm-em {font-style: italic;} -.cm-link {text-decoration: underline;} -.cm-strikethrough {text-decoration: line-through;} - -.cm-s-default .cm-keyword {color: #708;} -.cm-s-default .cm-atom {color: #219;} -.cm-s-default .cm-number {color: #164;} -.cm-s-default .cm-def {color: #00f;} -.cm-s-default .cm-variable, -.cm-s-default .cm-punctuation, -.cm-s-default .cm-property, -.cm-s-default .cm-operator {} -.cm-s-default .cm-variable-2 {color: #05a;} -.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} -.cm-s-default .cm-comment {color: #a50;} -.cm-s-default .cm-string {color: #a11;} -.cm-s-default .cm-string-2 {color: #f50;} -.cm-s-default .cm-meta {color: #555;} -.cm-s-default .cm-qualifier {color: #555;} -.cm-s-default .cm-builtin {color: #30a;} -.cm-s-default .cm-bracket {color: #997;} -.cm-s-default .cm-tag {color: #170;} -.cm-s-default .cm-attribute {color: #00c;} -.cm-s-default .cm-hr {color: #999;} -.cm-s-default .cm-link {color: #00c;} - -.cm-s-default .cm-error {color: #f00;} -.cm-invalidchar {color: #f00;} - -.CodeMirror-composing { border-bottom: 2px solid; } - -/* Default styles for common addons */ - -div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} -.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } -.CodeMirror-activeline-background {background: #e8f2ff;} - -/* STOP */ - -/* The rest of this file contains styles related to the mechanics of - the editor. You probably shouldn't touch them. */ - -.CodeMirror { - position: relative; - overflow: hidden; - background: white; -} - -.CodeMirror-scroll { - overflow: scroll !important; /* Things will break if this is overridden */ - /* 50px is the magic margin used to hide the element's real scrollbars */ - /* See overflow: hidden in .CodeMirror */ - margin-bottom: -50px; margin-right: -50px; - padding-bottom: 50px; - height: 100%; - outline: none; /* Prevent dragging from highlighting the element */ - position: relative; -} -.CodeMirror-sizer { - position: relative; - border-right: 50px solid transparent; -} - -/* The fake, visible scrollbars. Used to force redraw during scrolling - before actual scrolling happens, thus preventing shaking and - flickering artifacts. */ -.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - position: absolute; - z-index: 6; - display: none; - outline: none; -} -.CodeMirror-vscrollbar { - right: 0; top: 0; - overflow-x: hidden; - overflow-y: scroll; -} -.CodeMirror-hscrollbar { - bottom: 0; left: 0; - overflow-y: hidden; - overflow-x: scroll; -} -.CodeMirror-scrollbar-filler { - right: 0; bottom: 0; -} -.CodeMirror-gutter-filler { - left: 0; bottom: 0; -} - -.CodeMirror-gutters { - position: absolute; left: 0; top: 0; - min-height: 100%; - z-index: 3; -} -.CodeMirror-gutter { - white-space: normal; - height: 100%; - display: inline-block; - vertical-align: top; - margin-bottom: -50px; -} -.CodeMirror-gutter-wrapper { - position: absolute; - z-index: 4; - background: none !important; - border: none !important; -} -.CodeMirror-gutter-background { - position: absolute; - top: 0; bottom: 0; - z-index: 4; -} -.CodeMirror-gutter-elt { - position: absolute; - cursor: default; - z-index: 4; -} -.CodeMirror-gutter-wrapper ::selection { background-color: transparent } -.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } - -.CodeMirror-lines { - cursor: text; - min-height: 1px; /* prevents collapsing before first draw */ -} -.CodeMirror pre.CodeMirror-line, -.CodeMirror pre.CodeMirror-line-like { - /* Reset some styles that the rest of the page might have set */ - -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; - border-width: 0; - background: transparent; - font-family: inherit; - font-size: inherit; - margin: 0; - white-space: pre; - word-wrap: normal; - line-height: inherit; - color: inherit; - z-index: 2; - position: relative; - overflow: visible; - -webkit-tap-highlight-color: transparent; - -webkit-font-variant-ligatures: contextual; - font-variant-ligatures: contextual; -} -.CodeMirror-wrap pre.CodeMirror-line, -.CodeMirror-wrap pre.CodeMirror-line-like { - word-wrap: break-word; - white-space: pre-wrap; - word-break: normal; -} - -.CodeMirror-linebackground { - position: absolute; - left: 0; right: 0; top: 0; bottom: 0; - z-index: 0; -} - -.CodeMirror-linewidget { - position: relative; - z-index: 2; - padding: 0.1px; /* Force widget margins to stay inside of the container */ -} - -.CodeMirror-widget {} - -.CodeMirror-rtl pre { direction: rtl; } - -.CodeMirror-code { - outline: none; -} - -/* Force content-box sizing for the elements where we expect it */ -.CodeMirror-scroll, -.CodeMirror-sizer, -.CodeMirror-gutter, -.CodeMirror-gutters, -.CodeMirror-linenumber { - -moz-box-sizing: content-box; - box-sizing: content-box; -} - -.CodeMirror-measure { - position: absolute; - width: 100%; - height: 0; - overflow: hidden; - visibility: hidden; -} - -.CodeMirror-cursor { - position: absolute; - pointer-events: none; -} -.CodeMirror-measure pre { position: static; } - -div.CodeMirror-cursors { - visibility: hidden; - position: relative; - z-index: 3; -} -div.CodeMirror-dragcursors { - visibility: visible; -} - -.CodeMirror-focused div.CodeMirror-cursors { - visibility: visible; -} - -.CodeMirror-selected { background: #d9d9d9; } -.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } -.CodeMirror-crosshair { cursor: crosshair; } -.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } -.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } - -.cm-searching { - background-color: #ffa; - background-color: rgba(255, 255, 0, .4); -} - -/* Used to force a border model for a node */ -.cm-force-border { padding-right: .1px; } - -@media print { - /* Hide the cursor when printing */ - .CodeMirror div.CodeMirror-cursors { - visibility: hidden; - } -} - -/* See issue #2901 */ -.cm-tab-wrap-hack:after { content: ''; } - -/* Help users use markselection to safely style text background */ -span.CodeMirror-selectedtext { background: none; } - -/* STOP */ - /** - * Codemirror Darcula theme + * Custom CodeMirror BookStack overrides */ -/** - Name: IntelliJ IDEA darcula theme - From IntelliJ IDEA by JetBrains - */ - -.cm-s-darcula { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;} -.cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; } - -.cm-s-darcula span.cm-meta { color: #BBB529; } -.cm-s-darcula span.cm-number { color: #6897BB; } -.cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; } -.cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; } -.cm-s-darcula span.cm-variable { color: #A9B7C6; } -.cm-s-darcula span.cm-variable-2 { color: #A9B7C6; } -.cm-s-darcula span.cm-variable-3 { color: #9876AA; } -.cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; } -.cm-s-darcula span.cm-property { color: #FFC66D; } -.cm-s-darcula span.cm-operator { color: #A9B7C6; } -.cm-s-darcula span.cm-string { color: #6A8759; } -.cm-s-darcula span.cm-string-2 { color: #6A8759; } -.cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; } -.cm-s-darcula span.cm-link { color: #CC7832; } -.cm-s-darcula span.cm-atom { color: #CC7832; } -.cm-s-darcula span.cm-error { color: #BC3F3C; } -.cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; } -.cm-s-darcula span.cm-attribute { color: #6897bb; } -.cm-s-darcula span.cm-qualifier { color: #6A8759; } -.cm-s-darcula span.cm-bracket { color: #A9B7C6; } -.cm-s-darcula span.cm-builtin { color: #FF9E59; } -.cm-s-darcula span.cm-special { color: #FF9E59; } -.cm-s-darcula span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;} -.cm-s-darcula span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;} - -.cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; } -.cm-s-darcula .CodeMirror-activeline-background { background: #323232; } -.cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; } -.cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; } -.cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; } -.cm-s-darcula .CodeMirrir-linenumber { color: #606366; } -.cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; } - -.cm-s-darcula div.CodeMirror-selected { background: #214283; } - -.CodeMirror-hints.darcula { - font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; - color: #9C9E9E; - background-color: #3B3E3F !important; -} - -.CodeMirror-hints.darcula .CodeMirror-hint-active { - background-color: #494D4E !important; - color: #9C9E9E !important; -} - -/** - * Custom BookStack overrides - */ -// TODO - All below are old -.CodeMirror, .CodeMirror pre { +.cm-editor { font-size: 12px; -} -.CodeMirror { - font-size: 12px; - height: auto; - margin-bottom: $-l; border: 1px solid; - @include lightDark(border-color, #DDD, #111); -} -.CodeMirror pre::after { - display: none; -} -html.dark-mode .CodeMirror pre { - background-color: transparent; -} - -.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; border-left: 0; color: #333; } - -.code-fill .CodeMirror { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - margin-bottom: 0; - border: 0; + @include lightDark(border-color, #ddd, #444); + margin-bottom: $-l; + line-height: 1.4; + border-radius: 4px; } /** @@ -448,34 +16,39 @@ html.dark-mode .CodeMirror pre { */ .cm-copy-button { position: absolute; + display: flex; + align-items: center; + justify-content: center; top: -1px; right: -1px; background-color: #EEE; border: 1px solid #DDD; + border-radius: 0 4px 0 0; @include lightDark(background-color, #eee, #333); @include lightDark(border-color, #ddd, #444); - @include lightDark(fill, #444, #888); - padding: $-xs; + @include lightDark(color, #444, #888); line-height: 0; cursor: pointer; z-index: 5; user-select: none; opacity: 0; pointer-events: none; + width: 32px; + height: 32px; + transition: background-color linear 60ms, color linear 60ms; svg { - transition: all ease-in 240ms; - transform: translateY(0); + fill: currentColor; } &.success { - background-color: lighten($positive, 10%); - svg { - fill: #FFF; - transform: translateY(-3px); - } + background: $positive; + color: #FFF; + } + &:focus { + outline: 0 !important; } } .cm-editor:hover .cm-copy-button { user-select: all; - opacity: 1; + opacity: .6; pointer-events: all; } \ No newline at end of file diff --git a/resources/sass/_pages.scss b/resources/sass/_pages.scss index 57718e647..d58be2fe3 100755 --- a/resources/sass/_pages.scss +++ b/resources/sass/_pages.scss @@ -178,6 +178,10 @@ body.tox-fullscreen, body.markdown-fullscreen { white-space: pre-wrap; } } + + .cm-editor { + margin-bottom: 1.375em; + } } // Page content pointers diff --git a/resources/sass/_text.scss b/resources/sass/_text.scss index edf8ce614..6745d2a54 100644 --- a/resources/sass/_text.scss +++ b/resources/sass/_text.scss @@ -178,18 +178,19 @@ sub, .subscript { pre { font-size: 12px; border: 1px solid #DDD; - @include lightDark(background-color, #f5f5f5, #2B2B2B); + @include lightDark(background-color, #FFF, #2B2B2B); @include lightDark(border-color, #DDD, #111); - padding-left: 31px; + border-radius: 4px; + padding-left: 26px; position: relative; padding-top: 3px; padding-bottom: 3px; - &:after { + &:before { content: ''; display: block; position: absolute; top: 0; - width: 29px; + width: 22.4px; left: 0; height: 100%; @include lightDark(background-color, #f5f5f5, #313335);