├── .eslintrc ├── .gitignore ├── .nvmrc ├── .yarn └── install-state.gz ├── .yarnrc.yml ├── README.md ├── components ├── CodeEditor │ ├── CodeEditor.js │ └── index.js ├── CodeEditorOld │ ├── CodeEditor.js │ ├── CodeEditor.module.scss │ ├── CodeEditorSetup.js │ ├── CodeEditorUtils.js │ └── index.js ├── CodeMirror6Instance │ ├── CodeMirror6Instance.js │ ├── CodeMirror6InstanceHooks.js │ ├── extensions │ │ ├── autocomplete.js │ │ ├── codeFolding.js │ │ ├── defaultExtensions.js │ │ ├── emmet.js │ │ ├── extensions.js │ │ ├── fonts.js │ │ ├── indentation.js │ │ ├── index.js │ │ ├── languages.js │ │ ├── lineNumbers.js │ │ ├── lineWrapping.js │ │ ├── matchBrackets.js │ │ ├── onChange.js │ │ ├── readOnly.js │ │ ├── themes.js │ │ ├── themes │ │ │ ├── OLD │ │ │ │ ├── classic.module.scss │ │ │ │ ├── duotone-dark.module.scss │ │ │ │ ├── duotone-light.module.scss │ │ │ │ ├── highcontrast-dark.module.scss │ │ │ │ ├── highcontrast-light.module.scss │ │ │ │ ├── mdn-like.module.scss │ │ │ │ ├── oceanic-dark.module.scss │ │ │ │ ├── oceanic-light.module.scss │ │ │ │ ├── panda.module.scss │ │ │ │ ├── solarized-dark.module.scss │ │ │ │ ├── solarized-light.module.scss │ │ │ │ ├── synthwave.module.scss │ │ │ │ ├── tomorrow-night.module.scss │ │ │ │ ├── twilight.module.scss │ │ │ │ └── xq-light.module.scss │ │ │ ├── debug.js │ │ │ ├── highContrastDark.js │ │ │ └── twilight.js │ │ ├── useExtensionCompartment.js │ │ └── useExtensions.js │ └── index.js ├── CodeSamples │ ├── CodeSamples.js │ ├── CodeSamples.module.scss │ └── index.js └── EditorSettings │ ├── EditorSettings.js │ ├── EditorSettings.module.scss │ └── index.js ├── data ├── code.js ├── editorSettings.js ├── languages.js └── supportLevels.js ├── netlify.toml ├── next.config.js ├── package.json ├── pages ├── _app.js ├── _document.js ├── console.js ├── index.js ├── shared.js ├── sync.js └── yjs.js ├── styles ├── Home.module.scss ├── OLD-THEMES │ ├── _theme-bits.scss │ ├── classic.scss │ ├── duotone-dark.scss │ ├── duotone-light.scss │ ├── highcontrast-dark.scss │ ├── highcontrast-light.scss │ ├── mdn-like.scss │ ├── oceanic-dark.scss │ ├── oceanic-light.scss │ ├── panda.scss │ ├── solarized-dark.scss │ ├── solarized-light.scss │ ├── tomorrow-night.scss │ ├── twilight.scss │ └── xq-light.scss └── globals.css └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next", "next/core-web-vitals"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16.15.0 -------------------------------------------------------------------------------- /.yarn/install-state.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codepen/CodeMirror-6-Needs/e0a3964977ae107bf67956a0ae32e87eb261483b/.yarn/install-state.gz -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Playground to Figure Out Everything We Need Out of CodeMirror 6 2 | 3 | It's a [Next.js](https://nextjs.org/docs) app as that is the context we hope to be using [CodeMirror 6](https://codemirror.net/6/) in. 4 | 5 | Here's [a quick Netlify deployment](https://objective-blackwell-d4efc9.netlify.app/) to see what we mean. 6 | 7 | # TODOs 8 | 9 | - [ ] Port all our [existing syntax highlighting](https://github.com/codepen/CodeMirror-6-Needs/tree/main/styles/OLD-THEMES) themes to the [new format](https://github.com/codepen/CodeMirror-6-Needs/blob/main/themes/twilight.js). 10 | 11 | - [ ] Make [language packages](https://codemirror.net/6/examples/lang-package/) for... 12 | 13 | - [ ] Haml 14 | - [ ] Slim 15 | - [ ] Nunjucks 16 | - [ ] Sass (.scss format) 17 | - [ ] Sass (.sass format) 18 | - [ ] Less 19 | - [ ] Stylus 20 | - [ ] JSX (supported with JS package?) 21 | - [ ] CoffeeScript 22 | - [ ] TypeScript 23 | - [ ] LiveScript 24 | 25 | - [ ] Make [Emmet Work](https://github.com/emmetio/codemirror-plugin/issues/13) 26 | 27 | - [ ] [Search functionality](https://codemirror.net/6/docs/ref/#search) 28 | 29 | - [ ] [Commenting Functionality](https://codemirror.net/6/docs/ref/#comment) 30 | 31 | - [ ] Decide on supporting [Rectangular Selection](https://codemirror.net/6/docs/ref/#rectangular-selection) 32 | 33 | # Concerns 34 | 35 | - **Bundle Size**: With all the various packages needed for CM6, will the final bundle come down to an acceptable size? This repo is odd in that it includes Prettier at the moment to replicate our cody tidying features and for changing indentation, and that represents most of the JavaScript in the bundle. How can we see _only_ what CodeMirror contributes? `yarn run analyze` makes a visualization, so if we could remove Prettier from that it would be more accurate. 36 | 37 | - **Key Bindings**: CodeMirror 5 support Sublime Text and Vim key bindings. It's not clear if CodeMirror 6 does. Do we absolutely need them or not? 38 | 39 | # Notes from Marijn Haverbeke we need to look at 40 | 41 | - "Indent Width" is controlled with the `indentUnit` [1] facet. There's autoindentation logic and an `indentSelection` command, but I don't think there currently is a convenient way to reindent the entire document. Not sure if doing that implicitly would be a good idea—it might mess up manual formatting or even break things in whitespace-sensitive languages. 42 | 43 | - "Tabs or Spaces" whether autoindentation uses tabs or spaces is also controlled by `indentUnit` [1]. The column width of tabs is controlled by `EditorState.tabSize` [2] 44 | 45 | - "Line Wrapping" is enabled with the `EditorView.lineWrapping` extension [3]. 46 | 47 | - "Autocomplete" -- there's a rather solid module for handling autocompletion, but only robust completion sources for a few languages (HTML, XML, SQL). I have some unconcrete plans for adding some form of scope tracking to the system, which could help a lot with completion in languages like JS, but I don't know if/when that will materialize. 48 | 49 | - "Font Size" you can just change with CSS, and then follow up with a call to `requestMeasure` [4] to make sure the editor adjusts its internal info about the layout. 50 | 51 | - "Do we have to re-initialize the editor to change the theme or can we dispatch a change for a dynamically imported theme?" if you reconfigure [5] your theme extension, the highlighting will update immediately. 52 | 53 | - "Key bindings": There's some people working on vim bindings, but I don't have plans to port over the Sublime and Emacs bindings in the near future. The default bindings are based on VS Code (though they don't cover everything VS Code does). 54 | 55 | [1]: https://codemirror.net/6/docs/ref/#language.indentUnit 56 | [2]: https://codemirror.net/6/docs/ref/#state.EditorState%5EtabSize 57 | [3]: https://codemirror.net/6/docs/ref/#view.EditorView^lineWrapping 58 | [4]: https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure 59 | [5]: https://codemirror.net/6/examples/config/#dynamic-configuration 60 | -------------------------------------------------------------------------------- /components/CodeEditor/CodeEditor.js: -------------------------------------------------------------------------------- 1 | import CodeMirror6Instance from "../CodeMirror6Instance"; 2 | 3 | export default function CodeEditor({ 4 | title, 5 | language, 6 | value, 7 | editorSettings, 8 | onInit, 9 | ...props 10 | }) { 11 | return ( 12 |
13 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /components/CodeEditor/index.js: -------------------------------------------------------------------------------- 1 | import CodeEditor from "./CodeEditor"; 2 | 3 | export default CodeEditor; 4 | -------------------------------------------------------------------------------- /components/CodeEditorOld/CodeEditor.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from "react"; 2 | // import dynamic from "next/dynamic"; 3 | import classNames from "classnames"; 4 | import { editorSetup } from "./CodeEditorSetup"; 5 | import { EditorState, Compartment } from "@codemirror/state"; 6 | import { EditorView, keymap } from "@codemirror/view"; 7 | import { defaultTabBinding } from "@codemirror/commands"; 8 | import { adjustIndentWidth, CodeMirrorLanguageByType } from "./CodeEditorUtils"; 9 | import styles from "./CodeEditor.module.scss"; 10 | import { LANGUAGES } from "../../data/languages"; 11 | 12 | // TODO: Attempt dynamic imports 13 | // https://nextjs.org/docs/advanced-features/dynamic-import#with-named-exports 14 | // const DynamicComponent = dynamic( 15 | // () => 16 | // import("../../themes/twilight").then((mod) => { 17 | // console.log("mod", { mod }); 18 | // return mod.twilight; 19 | // }), 20 | // { 21 | // ssr: false, 22 | // } 23 | // ); 24 | // console.log(DynamicComponent); 25 | 26 | // import { oneDark } from "@codemirror/theme-one-dark"; 27 | 28 | // TODO: Convert more themes, dynamically load them when requested. 29 | import { twilight } from "../../themes/twilight"; 30 | // TODO: In reality we won't load multiple themes at once. 31 | import { oneDark } from "../../themes/oneDark"; 32 | 33 | // TODO: EditorSettings - rebuild with new settings or try to update compartments? 34 | 35 | export default function CodeEditor({ 36 | className, 37 | title, 38 | language, 39 | value, 40 | editorSettings, 41 | working, 42 | workingNotes, 43 | style, 44 | ...props 45 | }) { 46 | const indentWidth = Number(editorSettings.indentWidth); 47 | const container = useRef(); 48 | const view = useRef(); 49 | const compartments = { 50 | language: new Compartment(), 51 | tabSize: new Compartment(), 52 | // indentUnit: new Compartment(), 53 | }; 54 | 55 | // TODO: Dynamic language switching without destroying the whole instance. Do we need lifecycle Component methods? 56 | useEffect(() => { 57 | // To prevent Next.js fast refresh from adding additional editors 58 | if (container.current.children[0]) container.current.children[0].remove(); 59 | 60 | const lang = CodeMirrorLanguageByType(language); 61 | let langOptions = {}; 62 | if (language === LANGUAGES.JSX) { 63 | langOptions.jsx = true; 64 | } 65 | 66 | let theme = twilight; 67 | if (editorSettings.theme === "oneDark") { 68 | theme = oneDark; 69 | } 70 | 71 | let startState = EditorState.create({ 72 | doc: value, 73 | extensions: [ 74 | editorSetup(editorSettings), 75 | keymap.of(defaultTabBinding), 76 | compartments.tabSize.of(EditorState.tabSize.of(indentWidth)), 77 | // compartments.indentUnit.of(EditorState.indentUnit.of(indentUnit)), 78 | compartments.language.of(lang && lang(langOptions)), 79 | theme, 80 | ], 81 | }); 82 | 83 | let editorView = new EditorView({ 84 | state: startState, 85 | parent: container.current, 86 | lineWrapping: editorSettings.lineWrapping, 87 | }); 88 | 89 | view.current = editorView; 90 | 91 | return () => { 92 | editorView.destroy(); 93 | }; 94 | }, [ 95 | indentWidth, 96 | editorSettings.indentUnit, 97 | editorSettings.lineWrapping, 98 | editorSettings.lineNumbers, 99 | editorSettings.codeFolding, 100 | editorSettings.autocomplete, 101 | editorSettings.matchBrackets, 102 | editorSettings.theme, 103 | ]); 104 | 105 | useEffect(() => { 106 | if (view.current) { 107 | // TODO: Try to dispatch rather than rebuild. 108 | // view.current.dispatch({ 109 | // effects: compartments.tabSize.reconfigure( 110 | // EditorState.tabSize.of(indentWidth) 111 | // ), 112 | // }); 113 | 114 | const formattedValue = adjustIndentWidth({ 115 | indentWidth, 116 | language, 117 | value, 118 | }); 119 | view.current.dispatch( 120 | view.current.state.update({ 121 | changes: { 122 | from: 0, 123 | to: view.current.state.doc.length, 124 | insert: formattedValue, 125 | }, 126 | }) 127 | ); 128 | } 129 | }, [indentWidth, language, value]); 130 | 131 | const { fontSize, fontFamily } = editorSettings; 132 | useEffect(() => { 133 | // Is there a better way to "refresh" the view if font-size and such change? 134 | view.current.requestMeasure(); 135 | }, [fontSize, fontFamily]); 136 | 137 | // TODO: This doesn't work. But it feels like it would be better to dispatch a change to the theme. 138 | const { theme } = editorSettings; 139 | // useEffect(() => { 140 | // if (theme === "twilight") { 141 | // view.current.dispatch({ 142 | // effects: compartments.theme.reconfigure(twilight), 143 | // }); 144 | // } 145 | // if (theme === "oneDark") { 146 | // view.current.dispatch({ 147 | // effects: compartments.theme.reconfigure(oneDark), 148 | // }); 149 | // } 150 | // }, [theme]); 151 | 152 | return ( 153 |
163 | ); 164 | } 165 | -------------------------------------------------------------------------------- /components/CodeEditorOld/CodeEditor.module.scss: -------------------------------------------------------------------------------- 1 | .editor { 2 | :global(.cm-scroller) { 3 | font-size: var(--font-size); 4 | /* For some reason, the default .cm-scroller styles have an !important font family declaration. */ 5 | font-family: var(--font-family) !important; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /components/CodeEditorOld/CodeEditorSetup.js: -------------------------------------------------------------------------------- 1 | import { 2 | highlightSpecialChars, 3 | drawSelection, 4 | highlightActiveLine, 5 | keymap, 6 | } from "@codemirror/view"; 7 | export { EditorView } from "@codemirror/view"; 8 | import { EditorState } from "@codemirror/state"; 9 | export { EditorState } from "@codemirror/state"; 10 | import { history, historyKeymap } from "@codemirror/history"; 11 | import { foldGutter, foldKeymap } from "@codemirror/fold"; 12 | import { indentOnInput } from "@codemirror/language"; 13 | import { lineNumbers, highlightActiveLineGutter } from "@codemirror/gutter"; 14 | import { defaultKeymap } from "@codemirror/commands"; 15 | import { bracketMatching } from "@codemirror/matchbrackets"; 16 | import { closeBrackets, closeBracketsKeymap } from "@codemirror/closebrackets"; 17 | import { highlightSelectionMatches, searchKeymap } from "@codemirror/search"; 18 | import { autocompletion, completionKeymap } from "@codemirror/autocomplete"; 19 | import { commentKeymap } from "@codemirror/comment"; 20 | import { rectangularSelection } from "@codemirror/rectangular-selection"; 21 | import { defaultHighlightStyle } from "@codemirror/highlight"; 22 | import { lintKeymap } from "@codemirror/lint"; 23 | 24 | /* 25 | - [the default command bindings](https://codemirror.net/6/docs/ref/#commands.defaultKeymap) 26 | - [line numbers](https://codemirror.net/6/docs/ref/#gutter.lineNumbers) 27 | - [special character highlighting](https://codemirror.net/6/docs/ref/#view.highlightSpecialChars) 28 | - [the undo history](https://codemirror.net/6/docs/ref/#history.history) 29 | - [a fold gutter](https://codemirror.net/6/docs/ref/#fold.foldGutter) 30 | - [custom selection drawing](https://codemirror.net/6/docs/ref/#view.drawSelection) 31 | - [multiple selections](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections) 32 | - [reindentation on input](https://codemirror.net/6/docs/ref/#language.indentOnInput) 33 | - [the default highlight style](https://codemirror.net/6/docs/ref/#highlight.defaultHighlightStyle) (as fallback) 34 | - [bracket matching](https://codemirror.net/6/docs/ref/#matchbrackets.bracketMatching) 35 | - [bracket closing](https://codemirror.net/6/docs/ref/#closebrackets.closeBrackets) 36 | - [autocompletion](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion) 37 | - [rectangular selection](https://codemirror.net/6/docs/ref/#rectangular-selection.rectangularSelection) 38 | - [active line highlighting](https://codemirror.net/6/docs/ref/#view.highlightActiveLine) 39 | - [active line gutter highlighting](https://codemirror.net/6/docs/ref/#gutter.highlightActiveLineGutter) 40 | - [selection match highlighting](https://codemirror.net/6/docs/ref/#search.highlightSelectionMatches) 41 | - [search](https://codemirror.net/6/docs/ref/#search.searchKeymap) 42 | - [commenting](https://codemirror.net/6/docs/ref/#comment.commentKeymap) 43 | - [linting](https://codemirror.net/6/docs/ref/#lint.lintKeymap) 44 | */ 45 | 46 | const alwaysOn = [ 47 | highlightSpecialChars(), 48 | history(), 49 | drawSelection(), 50 | EditorState.allowMultipleSelections.of(true), 51 | indentOnInput(), 52 | defaultHighlightStyle.fallback, 53 | rectangularSelection(), 54 | highlightActiveLine(), 55 | highlightSelectionMatches(), 56 | keymap.of([ 57 | ...closeBracketsKeymap, 58 | ...defaultKeymap, 59 | ...searchKeymap, 60 | ...historyKeymap, 61 | ...foldKeymap, 62 | ...commentKeymap, 63 | ...completionKeymap, 64 | ...lintKeymap, 65 | ]), 66 | ]; 67 | 68 | function editorSetup(editorSettings) { 69 | let completeSetup = [...alwaysOn]; 70 | 71 | // NOTE: Order matters here! 72 | // If foldGutter went first, it will render to the left of the line numbers in the gutter. 73 | if (editorSettings.lineNumbers) { 74 | completeSetup.push(lineNumbers()); 75 | completeSetup.push(highlightActiveLineGutter()); 76 | } 77 | 78 | if (editorSettings.codeFolding) { 79 | completeSetup.push(foldGutter()); 80 | } 81 | if (editorSettings.autocomplete) { 82 | completeSetup.push(autocompletion()); 83 | } 84 | 85 | if (editorSettings.matchBrackets) { 86 | completeSetup.push(bracketMatching()); 87 | completeSetup.push(closeBrackets()); 88 | } 89 | 90 | return completeSetup; 91 | } 92 | 93 | export { editorSetup }; 94 | -------------------------------------------------------------------------------- /components/CodeEditorOld/CodeEditorUtils.js: -------------------------------------------------------------------------------- 1 | import { LANGUAGES } from "../../data/languages"; 2 | 3 | // TODO: Dynamic imports 4 | import { css } from "@codemirror/lang-css"; 5 | import { html } from "@codemirror/lang-html"; 6 | import { javascript } from "@codemirror/lang-javascript"; 7 | import { markdown } from "@codemirror/lang-markdown"; 8 | 9 | import prettier from "prettier/standalone"; 10 | import parserBabel from "prettier/parser-babel"; 11 | import parserHtml from "prettier/parser-html"; 12 | import parserCss from "prettier/parser-postcss"; 13 | 14 | export function CodeMirrorLanguageByType(type) { 15 | switch (type) { 16 | // These seem fine 17 | case LANGUAGES.HTML: 18 | return html; 19 | case LANGUAGES.CSS: 20 | return css; 21 | case LANGUAGES.MARKDOWN: 22 | return markdown; 23 | 24 | case LANGUAGES.JAVASCRIPT: 25 | case LANGUAGES.JSX: 26 | return javascript; 27 | 28 | // TODO: Not quite right. 29 | case LANGUAGES.SCSS: 30 | case LANGUAGES.SASS: 31 | case LANGUAGES.LESS: 32 | case LANGUAGES.STYLUS: 33 | return css; 34 | 35 | // TODO entirely 36 | case LANGUAGES.HAML: 37 | case LANGUAGES.PUG: 38 | case LANGUAGES.SLIM: 39 | case LANGUAGES.NUNJUCKS: 40 | return html; 41 | 42 | case LANGUAGES.COFFEESCRIPT: 43 | case LANGUAGES.TYPESCRIPT: 44 | case LANGUAGES.LIVESCRIPT: 45 | return javascript; 46 | } 47 | } 48 | 49 | export function adjustIndentWidth({ language, value, indentWidth }) { 50 | // TODO: Only do Prettier on an Indent Width change. 51 | // TODO: See if CodeMirror has an official way of doing indentation changes. 52 | // Do Prettier! 53 | // https://prettier.io/docs/en/browser.html 54 | let parser = "babel"; 55 | if (language === "html") parser = "html"; 56 | if (language === "scss" || language === "css" || parser === "less") 57 | parser = "css"; 58 | // TODO: Do Prettier on the other supported languages 59 | if ( 60 | language === "js" || 61 | language === "html" || 62 | language === "css" || 63 | language === "scss" || 64 | language === "less" 65 | ) { 66 | // prettier can throw hard errors if the parser fails. 67 | try { 68 | // replace entire document with prettified version 69 | const formattedValue = prettier.format(value, { 70 | parser: parser, 71 | plugins: [parserBabel, parserHtml, parserCss], 72 | tabWidth: indentWidth, 73 | // semi: true, 74 | trailingComma: "none", 75 | // useTabs: indentWith === "tabs", 76 | bracketSpacing: true, 77 | jsxBracketSameLine: false, 78 | }); 79 | return formattedValue; 80 | } catch (err) { 81 | console.error(err); 82 | } 83 | } 84 | return value; 85 | } 86 | -------------------------------------------------------------------------------- /components/CodeEditorOld/index.js: -------------------------------------------------------------------------------- 1 | import CodeEditor from "./CodeEditor"; 2 | 3 | export default CodeEditor; 4 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/CodeMirror6Instance.js: -------------------------------------------------------------------------------- 1 | import { useCodeMirror6Instance } from "./CodeMirror6InstanceHooks"; 2 | import PropTypes from "prop-types"; 3 | import { INDENT_VALUES } from "../../data/editorSettings"; 4 | 5 | function CodeMirror6Instance(props) { 6 | const cm6props = useCodeMirror6Instance(props); 7 | return
; 8 | } 9 | 10 | CodeMirror6Instance.propTypes = { 11 | value: PropTypes.string, 12 | state: PropTypes.object, 13 | extensions: PropTypes.arrayOf( 14 | PropTypes.oneOfType([PropTypes.object, PropTypes.func]) 15 | ), 16 | onInit: PropTypes.func, 17 | onChange: PropTypes.func, 18 | readOnly: PropTypes.bool, 19 | editorSettings: PropTypes.shape({ 20 | theme: PropTypes.string, 21 | fontSize: PropTypes.number, 22 | lineHeight: PropTypes.number, 23 | fontFamily: PropTypes.string, 24 | indentWidth: PropTypes.number, 25 | indentUnit: PropTypes.oneOf(Object.values(INDENT_VALUES)), 26 | lineNumbers: PropTypes.bool, 27 | lineWrapping: PropTypes.bool, 28 | codeFolding: PropTypes.bool, 29 | matchBrackets: PropTypes.bool, 30 | autocomplete: PropTypes.bool, 31 | emmet: PropTypes.bool, 32 | }), 33 | }; 34 | 35 | export default CodeMirror6Instance; 36 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/CodeMirror6InstanceHooks.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | import { EditorState } from "@codemirror/state"; 3 | import { EditorView } from "codemirror"; 4 | import { useExtensions } from "./extensions/useExtensions"; 5 | 6 | export function useCodeMirror6Instance(props) { 7 | const ref = useRef(); 8 | const [editorView, setEditorView] = useState(null); 9 | 10 | const extensions = useExtensions(props, editorView); 11 | 12 | // Initialize CodeMirror6 View 13 | useEffect(() => { 14 | const editorState = 15 | props.state || 16 | EditorState.create({ 17 | doc: props.value, 18 | extensions, 19 | }); 20 | 21 | const editorView = new EditorView({ 22 | state: editorState, 23 | parent: ref.current, 24 | }); 25 | 26 | // NOTE: State is available as `editorView.state` 27 | setEditorView(editorView); 28 | 29 | props.onInit && props.onInit(editorView); 30 | 31 | // Destroy when unmounted. 32 | return () => editorView.destroy(); 33 | // eslint-disable-next-line react-hooks/exhaustive-deps 34 | }, []); 35 | 36 | const { value } = props; 37 | useEffect(() => { 38 | const validValue = value || value === ""; 39 | if (!editorView || !validValue) return; 40 | 41 | const currentValue = editorView.state.doc.toString(); 42 | if (value !== currentValue) { 43 | // https://codemirror.net/docs/migration/#making-changes 44 | // NOTE: "To completely reset a state—for example to load a new document—it is recommended to create a new state instead of a transaction. That will make sure no unwanted state (such as undo history events) sticks around." 45 | // editorView.setState(EditorState.create()) 46 | editorView.dispatch({ 47 | changes: { from: 0, to: editorView.state.doc.length, insert: value }, 48 | }); 49 | } 50 | }, [value, editorView]); 51 | 52 | // Swap out state 53 | useEffect(() => { 54 | if (props.state && props.state !== editorView.state) { 55 | editorView.setState(props.state); 56 | } 57 | }, [props.state, editorView]); 58 | 59 | return { ref }; 60 | } 61 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/autocomplete.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useExtensionCompartment } from "./useExtensionCompartment"; 3 | import { autocompletion } from "@codemirror/autocomplete"; 4 | 5 | export function useAutocomplete(editorSettings, editorView) { 6 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 7 | 8 | useEffect(() => { 9 | updateCompartment(editorSettings.autocomplete ? autocompletion() : []); 10 | }, [editorSettings.autocomplete, updateCompartment]); 11 | 12 | return compartment; 13 | } 14 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/codeFolding.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useExtensionCompartment } from "./useExtensionCompartment"; 3 | import { codeFolding, foldGutter, foldKeymap } from "@codemirror/language"; 4 | import { keymap } from "@codemirror/view"; 5 | 6 | export function useCodeFolding(editorSettings, editorView) { 7 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 8 | 9 | useEffect(() => { 10 | updateCompartment( 11 | editorSettings.codeFolding 12 | ? [ 13 | // https://codemirror.net/docs/ref/#language.codeFolding 14 | codeFolding(), 15 | keymap.of(foldKeymap), 16 | // https://codemirror.net/docs/ref/#language.foldGutter 17 | foldGutter(), 18 | ] 19 | : [] 20 | ); 21 | }, [editorSettings.codeFolding, updateCompartment]); 22 | 23 | return compartment; 24 | } 25 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/defaultExtensions.js: -------------------------------------------------------------------------------- 1 | import { 2 | crosshairCursor, 3 | drawSelection, 4 | dropCursor, 5 | highlightSpecialChars, 6 | keymap, 7 | rectangularSelection, 8 | } from "@codemirror/view"; 9 | import { 10 | defaultKeymap, 11 | history, 12 | historyKeymap, 13 | indentWithTab, 14 | } from "@codemirror/commands"; 15 | import { EditorState } from "@codemirror/state"; 16 | import { 17 | defaultHighlightStyle, 18 | indentOnInput, 19 | syntaxHighlighting, 20 | } from "@codemirror/language"; 21 | import { searchKeymap } from "@codemirror/search"; 22 | import { lintKeymap } from "@codemirror/lint"; 23 | 24 | // Adapted from basicSetup. Using this custom setup instead to better support our Editor Settings and desired CodePen experience. 25 | // https://codemirror.net/docs/ref/#codemirror.basicSetup 26 | // https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts 27 | // import { basicSetup } from "codemirror"; 28 | 29 | export const defaultKeymaps = keymap.of([ 30 | ...defaultKeymap, 31 | ...searchKeymap, 32 | ...historyKeymap, 33 | ...lintKeymap, 34 | 35 | // NOTE: This keymap refers to the `tab` key, NOT tabs vs spaces. 36 | // NOTE: `indentWithTab` should be loaded after Emmet to ensure Emmet completions can take precedence 37 | // NOTE: Warn users about ESC + Tab https://codemirror.net/examples/tab/ 38 | indentWithTab, 39 | ]); 40 | 41 | export const defaultExtensions = [ 42 | syntaxHighlighting(defaultHighlightStyle, { fallback: true }), 43 | highlightSpecialChars(), 44 | history(), 45 | 46 | drawSelection(), 47 | // Multi cursor/select 48 | [ 49 | EditorState.allowMultipleSelections.of(true), 50 | rectangularSelection(), 51 | crosshairCursor(), 52 | ], 53 | 54 | dropCursor(), 55 | indentOnInput(), 56 | defaultKeymaps, 57 | ]; 58 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/emmet.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { keymap } from "@codemirror/view"; 3 | 4 | // Import Expand Abbreviation command 5 | import { 6 | abbreviationTracker, 7 | emmetConfig, 8 | expandAbbreviation, 9 | } from "@emmetio/codemirror6-plugin"; 10 | import { useExtensionCompartment } from "./useExtensionCompartment"; 11 | import { LANGUAGES } from "../../../data/languages"; 12 | import { Prec } from "@codemirror/state"; 13 | 14 | const emmetSupportedModes = [ 15 | LANGUAGES.HTML, 16 | LANGUAGES.PUG, 17 | LANGUAGES.CSS, 18 | LANGUAGES.SCSS, 19 | LANGUAGES.STYLUS, 20 | LANGUAGES.LESS, 21 | LANGUAGES.JSX, 22 | // "sass", 23 | // "vue", // Is the mode in the Vue custom editor actually "js"? 24 | // May need JS here? for JSX? 25 | ]; 26 | 27 | const validEmmetEditorMode = (mode) => { 28 | return emmetSupportedModes.includes(mode); 29 | }; 30 | 31 | import { EmmetKnownSyntax } from "@emmetio/codemirror6-plugin"; 32 | 33 | export const LANGUAGES_TO_EMMET_SYNTAX = { 34 | [LANGUAGES.CSS]: EmmetKnownSyntax.css, 35 | [LANGUAGES.HAML]: EmmetKnownSyntax.haml, 36 | [LANGUAGES.HTML]: EmmetKnownSyntax.html, 37 | [LANGUAGES.JSX]: EmmetKnownSyntax.jsx, 38 | [LANGUAGES.LESS]: EmmetKnownSyntax.css, 39 | [LANGUAGES.MARKDOWN]: EmmetKnownSyntax.html, 40 | [LANGUAGES.NUNJUCKS]: EmmetKnownSyntax.html, 41 | [LANGUAGES.VUE]: EmmetKnownSyntax.vue, 42 | [LANGUAGES.PUG]: EmmetKnownSyntax.pug, 43 | [LANGUAGES.SASS]: EmmetKnownSyntax.sass, 44 | [LANGUAGES.SCSS]: EmmetKnownSyntax.scss, 45 | [LANGUAGES.SLIM]: EmmetKnownSyntax.slim, 46 | [LANGUAGES.STYLUS]: EmmetKnownSyntax.stylus, 47 | }; 48 | 49 | export function getEmmetSyntax(language) { 50 | const lang = language; // Gotta tell TypeScript to knock it off :rolling_eyes: 51 | if (lang && lang in LANGUAGES_TO_EMMET_SYNTAX) { 52 | return LANGUAGES_TO_EMMET_SYNTAX[lang]; 53 | } 54 | 55 | return false; 56 | } 57 | 58 | export function useEmmetExtension(language, editorSettings, editorView) { 59 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 60 | 61 | const enabled = editorSettings.emmet; 62 | 63 | useEffect(() => { 64 | // Emmet only works properly on certain languages 65 | const canUseEmmet = enabled && validEmmetEditorMode(language); 66 | 67 | updateCompartment( 68 | canUseEmmet 69 | ? [ 70 | emmetConfig.of({ 71 | syntax: getEmmetSyntax(language), 72 | preview: true, 73 | config: { 74 | markup: { 75 | snippets: { 76 | foo: "invalid snippet", 77 | // The above will break this VALID snippet below: 78 | foo2: "button", 79 | }, 80 | }, 81 | stylesheet: { 82 | snippets: {}, 83 | }, 84 | }, 85 | }), 86 | 87 | Prec.high(abbreviationTracker()), 88 | ] 89 | : [] 90 | ); 91 | }, [language, enabled, updateCompartment]); 92 | 93 | return compartment; 94 | } 95 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/extensions.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useExtensionCompartment } from "./useExtensionCompartment"; 3 | 4 | export function useExtraExtensions({ extensions }, editorView) { 5 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 6 | 7 | useEffect(() => { 8 | updateCompartment(extensions || []); 9 | }, [extensions, updateCompartment]); 10 | 11 | return compartment; 12 | } 13 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/fonts.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useMemo } from "react"; 2 | import { EditorView } from "codemirror"; 3 | import { useExtensionCompartment } from "./useExtensionCompartment"; 4 | 5 | export function useFonts({ fontSize, fontFamily, lineHeight }, editorView) { 6 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 7 | 8 | // const Theme = useMemo( 9 | // () => 10 | // [] 11 | // ); 12 | 13 | useEffect(() => { 14 | const Theme = EditorView.theme({ 15 | "&": { 16 | fontSize: `${fontSize}px`, 17 | }, 18 | ".cm-scroller": { 19 | fontFamily: fontFamily, 20 | lineHeight: lineHeight, 21 | }, 22 | }); 23 | updateCompartment([Theme]); 24 | }, [lineHeight, fontSize, fontFamily, updateCompartment]); 25 | 26 | return compartment; 27 | } 28 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/indentation.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useExtensionCompartment } from "./useExtensionCompartment"; 3 | 4 | import { indentationMarkers } from "@replit/codemirror-indentation-markers"; 5 | import { INDENT_VALUES } from "../../../data/editorSettings"; 6 | import { indentUnit } from "@codemirror/language"; 7 | import { EditorState } from "@codemirror/state"; 8 | 9 | export function useIndentation(editorSettings, editorView) { 10 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 11 | 12 | const shouldIndentWithTab = editorSettings.indentUnit === INDENT_VALUES.TABS; 13 | const indentSize = editorSettings.indentWidth; 14 | 15 | useEffect(() => { 16 | const indentUnitValue = shouldIndentWithTab ? "\t" : " ".repeat(indentSize); 17 | 18 | updateCompartment([ 19 | indentUnit.of(indentUnitValue), 20 | EditorState.tabSize.of(indentSize), 21 | indentationMarkers(), 22 | ]); 23 | }, [shouldIndentWithTab, indentSize, updateCompartment]); 24 | 25 | // TODO: convert tabs & spaces 26 | /* 27 | From: https://codemirror.net/examples/change/ 28 | When dispatching a transaction, you can also pass an array of changes. The from/to in each of these changes refer to positions in the start document, not to the document created by previously listed changes. 29 | 30 | For example, to replace all tabs in a document with two spaces, you could do something like this: 31 | 32 | ``` 33 | let text = view.state.doc.toString(), pos = 0 34 | let changes = [] 35 | for (let next; (next = text.indexOf("\t", pos)) > -1;) { 36 | changes.push({from: next, to: next + 1, insert: " "}) 37 | pos = next + 1 38 | } 39 | view.dispatch({changes}) 40 | ``` 41 | */ 42 | 43 | return compartment; 44 | } 45 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/index.js: -------------------------------------------------------------------------------- 1 | export * from "./useExtensions"; 2 | export * from "./useExtensionCompartment"; 3 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/languages.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { languages } from "@codemirror/language-data"; 3 | import { useExtensionCompartment } from "./useExtensionCompartment"; 4 | import { LANGUAGES } from "../../../data/languages"; 5 | 6 | // TODO: Detect language by file extension 7 | // NOTE: May be able to use @codemirror/language-data for file extensions and lazy loading certain languages https://codemirror.net/docs/ref/#language.LanguageDescription 8 | 9 | function getLanguageFallback(language) { 10 | switch (language) { 11 | case LANGUAGES.NUNJUCKS: 12 | case LANGUAGES.PUG: 13 | case LANGUAGES.HAML: 14 | case LANGUAGES.SLIM: 15 | return LANGUAGES.HTML; 16 | } 17 | 18 | return language; 19 | } 20 | 21 | const htmlLanguage = languages.find((lang) => lang.name === "HTML"); 22 | 23 | function getCodeMirrorLanguageData(language, matchBrackets) { 24 | language = getLanguageFallback(language); 25 | 26 | let languageData = languages.find( 27 | (lang) => lang.name === language || lang.alias.includes(language) 28 | ); 29 | 30 | if (languageData === htmlLanguage) { 31 | // For HTML, we need to manually override the loader here to turn off `matchClosingTags` and `autoCloseTags` so that we can enable them. 32 | // TODO: Should this be a distinct option, or paired with `matchBrackets` for simplicity? 33 | languageData.load = function () { 34 | return import("@codemirror/lang-html").then((m) => 35 | m.html({ 36 | // `matchClosingTags` is part of the language configuration, so we can't easily split that off into a standalone extension. 37 | matchClosingTags: matchBrackets, 38 | // `autoCloseTags` is a separate extension exported from `@codemirror/lang-html` so we could split that out, but it kinda makes sense to just leave it here. 39 | autoCloseTags: matchBrackets, 40 | }) 41 | ); 42 | }; 43 | } 44 | 45 | return languageData; 46 | } 47 | 48 | export function useLanguageExtension({ language, editorSettings }, editorView) { 49 | const [languageCompartment, updateCompartment] = 50 | useExtensionCompartment(editorView); 51 | 52 | const matchBrackets = editorSettings?.matchBrackets ? true : false; 53 | 54 | useEffect(() => { 55 | async function loadLanguage() { 56 | const lang = getCodeMirrorLanguageData(language, matchBrackets); 57 | 58 | if (lang) { 59 | const languageExtension = await lang.load(); 60 | updateCompartment(languageExtension); 61 | } 62 | } 63 | 64 | loadLanguage(); 65 | }, [language, matchBrackets, updateCompartment]); 66 | 67 | return languageCompartment; 68 | } 69 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/lineNumbers.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useExtensionCompartment } from "./useExtensionCompartment"; 3 | import { lineNumbers } from "@codemirror/view"; 4 | 5 | export function useLineNumbers(editorSettings, editorView) { 6 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 7 | 8 | useEffect(() => { 9 | updateCompartment( 10 | editorSettings.lineNumbers 11 | ? // https://codemirror.net/docs/ref/#view.lineNumbers 12 | lineNumbers() 13 | : [] 14 | ); 15 | }, [editorSettings.lineNumbers, updateCompartment]); 16 | 17 | return compartment; 18 | } 19 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/lineWrapping.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { EditorView } from "codemirror"; 3 | import { useExtensionCompartment } from "./useExtensionCompartment"; 4 | 5 | export function useLineWrapping({ lineWrapping }, editorView) { 6 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 7 | 8 | useEffect(() => { 9 | updateCompartment(lineWrapping ? EditorView.lineWrapping : null); 10 | }, [lineWrapping, updateCompartment]); 11 | 12 | return compartment; 13 | } 14 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/matchBrackets.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useExtensionCompartment } from "./useExtensionCompartment"; 3 | import { bracketMatching } from "@codemirror/language"; 4 | import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete"; 5 | import { keymap } from "@codemirror/view"; 6 | 7 | export function useMatchBrackets({ matchBrackets }, editorView) { 8 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 9 | 10 | useEffect(() => { 11 | updateCompartment( 12 | matchBrackets 13 | ? [bracketMatching(), closeBrackets(), keymap.of(closeBracketsKeymap)] 14 | : [] 15 | ); 16 | }, [matchBrackets, updateCompartment]); 17 | 18 | return compartment; 19 | } 20 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/onChange.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useExtensionCompartment } from "./useExtensionCompartment"; 3 | import { EditorView } from "codemirror"; 4 | 5 | export function useOnChange({ onChange }, editorView) { 6 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 7 | 8 | useEffect(() => { 9 | updateCompartment(onChange ? EditorView.updateListener.of(onChange) : []); 10 | }, [onChange, updateCompartment]); 11 | 12 | return compartment; 13 | } 14 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/readOnly.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { EditorState } from "@codemirror/state"; 3 | import { useExtensionCompartment } from "./useExtensionCompartment"; 4 | import { 5 | EditorView, 6 | highlightActiveLine, 7 | highlightActiveLineGutter, 8 | } from "@codemirror/view"; 9 | 10 | const hideCursorTheme = EditorView.theme({ 11 | ".cm-cursor-primary": { 12 | display: "none", 13 | opacity: 0, 14 | }, 15 | }); 16 | 17 | export function useReadOnly({ readOnly }, editorView) { 18 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 19 | 20 | useEffect(() => { 21 | const value = [EditorState.readOnly.of(readOnly)]; 22 | if (readOnly) { 23 | // Hide cursor if read only 24 | value.push(hideCursorTheme); 25 | } else { 26 | // Highlight active lines if not read only 27 | value.push(highlightActiveLine(), highlightActiveLineGutter()); 28 | } 29 | updateCompartment(value); 30 | }, [readOnly, updateCompartment]); 31 | 32 | return compartment; 33 | } 34 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | // import { oneDark } from "@codemirror/theme-one-dark"; 3 | 4 | import { useExtensionCompartment } from "./useExtensionCompartment"; 5 | 6 | import * as themeMirror from "thememirror"; 7 | import { debug } from "./themes/debug"; 8 | 9 | // TODO: Port themes 10 | // https://codemirror.net/examples/styling/ 11 | // https://codemirror.net/docs/migration/#dom-structure 12 | 13 | export const THEMES = { 14 | HIGH_CONTRAST_DARK: "High Contrast Dark", 15 | TWILIGHT: "Twilight", 16 | ONE_DARK: "One Dark", 17 | // https://github.com/craftzdog/cm6-themes/ 18 | SOLARIZED_DARK: "Solarized Dark", 19 | SOLARIZED_LIGHT: "Solarized Light", 20 | MATERIAL_DARK: "Material Dark", 21 | }; 22 | 23 | export const THEME_LOADERS = { 24 | [THEMES.ONE_DARK]: () => 25 | import("@codemirror/theme-one-dark").then(({ oneDark }) => oneDark), 26 | [THEMES.TWILIGHT]: () => 27 | import("./themes/twilight").then(({ twilight }) => twilight), 28 | [THEMES.HIGH_CONTRAST_DARK]: () => 29 | import("./themes/highContrastDark").then( 30 | ({ highContrastDark }) => highContrastDark 31 | ), 32 | [THEMES.SOLARIZED_DARK]: () => 33 | import("cm6-theme-solarized-dark").then( 34 | ({ solarizedDark }) => solarizedDark 35 | ), 36 | [THEMES.SOLARIZED_LIGHT]: () => 37 | import("cm6-theme-solarized-light").then( 38 | ({ solarizedLight }) => solarizedLight 39 | ), 40 | [THEMES.MATERIAL_DARK]: () => 41 | import("cm6-theme-material-dark").then(({ materialDark }) => materialDark), 42 | 43 | // [THEMES.DRACULA]: () => import("thememirror").then(({ dracula }) => dracula), 44 | }; 45 | 46 | // Automatically adding the ThemeMirror instead of manually setting up the lazy loading for each of these themes. 47 | Object.entries(themeMirror).forEach(([key, theme]) => { 48 | if (key !== "createTheme") { 49 | THEMES[key] = key; 50 | THEME_LOADERS[key] = () => theme; 51 | } 52 | }); 53 | 54 | export function useThemeExtension({ theme }, editorView) { 55 | const [compartment, updateCompartment] = useExtensionCompartment(editorView); 56 | 57 | useEffect(() => { 58 | async function loadTheme() { 59 | const themeLoader = THEME_LOADERS[theme]; 60 | if (themeLoader) { 61 | const loadedTheme = await themeLoader(); 62 | updateCompartment([debug, loadedTheme]); 63 | } 64 | } 65 | 66 | loadTheme(); 67 | }, [theme, updateCompartment]); 68 | 69 | return compartment; 70 | } 71 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/classic.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --black: #000; 3 | --blue: #219; 4 | --red: #a11; 5 | --green: #164; 6 | --lightGray: #ccc; 7 | --midGray: #555; 8 | --purple: #708; 9 | 10 | background: white; 11 | color: #222; 12 | 13 | :global { 14 | .cm-keyword { 15 | // const, var, let 16 | color: var(--blue); 17 | } 18 | .cm-atom { 19 | // tag part of 20 | color: var(--blue); 21 | } 22 | .cm-def { 23 | // params part of function(params) { 24 | color: var(--purple); 25 | } 26 | .cm-variable { 27 | // coffeescript var 28 | color: var(--blue); 29 | } 30 | .cm-variable-2 { 31 | // for (key in [this]), markdown li, decimals without 0 in front 32 | color: var(--black); 33 | } 34 | .cm-variable-3 { 35 | color: var(--black); 36 | } 37 | .cm-header { 38 | color: var(--red); 39 | } 40 | .cm-number { 41 | color: var(--green); 42 | } 43 | .cm-property { 44 | // margin part of margin: 10px;, getElementById 45 | color: var(--blue); 46 | } 47 | .cm-attribute { 48 | color: var(--midGray); 49 | } 50 | .cm-builtin { 51 | color: var(--blue); 52 | } 53 | .cm-qualifier { 54 | color: var(--blue); 55 | } 56 | .cm-operator { 57 | // CoffeeScript -> 58 | color: var(--black); 59 | } 60 | .cm-meta { 61 | // @font-face 62 | color: var(--blue); 63 | } 64 | .cm-string { 65 | // href=["val"] 66 | color: var(--red); 67 | } 68 | .cm-string-2 { 69 | // match([this]) 70 | color: var(--red); 71 | } 72 | .cm-tag { 73 | color: var(--blue); 74 | } 75 | .cm-bracket { 76 | color: var(--black); 77 | } 78 | .cm-comment { 79 | // /* this comment */ 80 | color: var(--lightGray); 81 | } 82 | } 83 | 84 | // Mode Overrides 85 | &[data-mode='htmlmixed'] { 86 | :global { 87 | .cm-atom { 88 | color: var(--red); 89 | } 90 | } 91 | } 92 | &[data-mode='text/css'] { 93 | :global { 94 | .cm-tag { 95 | color: var(--blue); 96 | } 97 | } 98 | } 99 | 100 | :global { 101 | .CodeMirror-cursor { 102 | border-left-color: black; 103 | } 104 | .CodeMirror-selected { 105 | background: rgba(black, 0.05); 106 | } 107 | .CodeMirror-focused .CodeMirror-selected { 108 | background: rgba(black, 0.1); 109 | } 110 | .CodeMirror-matchingbracket, 111 | .CodeMirror-matchingtag { 112 | text-decoration: underline; 113 | text-decoration-color: rgba(black, 0.5); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/duotone-dark.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --purple: #9b87fd; 3 | --orange: #ffcc99; 4 | --lightGray: #eeebff; 5 | --darkGray: #6c6783; 6 | 7 | background: #2a2734; 8 | color: var(--lightGray); 9 | 10 | :global { 11 | .cm-keyword { 12 | // const, var, let 13 | color: var(--orange); 14 | } 15 | .cm-atom { 16 | // tag part of 17 | color: var(--orange); 18 | } 19 | .cm-def { 20 | // params part of function(params) { 21 | color: var(--lightGray); 22 | } 23 | .cm-variable { 24 | // coffeescript var 25 | color: var(--orange); 26 | } 27 | .cm-variable-2 { 28 | // for (key in [this]), markdown li, decimals without 0 in front 29 | color: var(--purple); 30 | } 31 | .cm-variable-3 { 32 | color: var(--purple); 33 | } 34 | .cm-header { 35 | color: var(--lightGray); 36 | } 37 | .cm-number { 38 | color: var(--orange); 39 | } 40 | .cm-property { 41 | // margin part of margin: 10px;, getElementById 42 | color: var(--purple); 43 | } 44 | .cm-attribute { 45 | color: var(--orange); 46 | } 47 | .cm-builtin { 48 | color: var(--lightGray); 49 | } 50 | .cm-qualifier { 51 | color: var(--lightGray); 52 | } 53 | .cm-operator { 54 | // CoffeeScript -> 55 | color: var(--orange); 56 | } 57 | .cm-meta { 58 | // @font-face 59 | color: var(--orange); 60 | } 61 | .cm-string { 62 | // href=["val"] 63 | color: var(--orange); 64 | } 65 | .cm-string-2 { 66 | // match([this]) 67 | color: var(--purple); 68 | } 69 | .cm-tag { 70 | color: var(--lightGray); 71 | } 72 | .cm-bracket { 73 | color: var(--darkGray); 74 | } 75 | .cm-comment { 76 | // /* this comment */ 77 | color: var(--darkGray); 78 | } 79 | } 80 | 81 | :global { 82 | .CodeMirror-cursor { 83 | border-left-color: var(--lightGray); 84 | } 85 | .CodeMirror-selected { 86 | background: #221f2a; 87 | } 88 | .CodeMirror-focused .CodeMirror-selected { 89 | background: #3e3b51; 90 | } 91 | .CodeMirror-matchingbracket, 92 | .CodeMirror-matchingtag { 93 | text-decoration: underline; 94 | text-decoration-color: rgba(black, 0.5); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/duotone-light.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --lightTan: #b6ad9a; 3 | --tan: #b29762; 4 | --medTan: #896724; 5 | --darkTan: #2d2006; 6 | --blue: #1657da; 7 | --medBlue: #0e4ecd; 8 | --darkBlue: #063289; 9 | 10 | background: #faf8f5; 11 | color: var(--tan); 12 | 13 | :global { 14 | .cm-keyword { 15 | // const, var, let 16 | color: var(--tan); 17 | } 18 | .cm-atom { 19 | // tag part of 20 | color: var(--tan); 21 | } 22 | .cm-def { 23 | // params part of function(params) { 24 | color: var(--darkTan); 25 | } 26 | .cm-variable { 27 | // coffeescript var 28 | color: var(--tan); 29 | } 30 | .cm-variable-2 { 31 | // for (key in [this]), markdown li, decimals without 0 in front 32 | color: var(--tan); 33 | } 34 | .cm-variable-3 { 35 | color: var(--tan); 36 | } 37 | .cm-header { 38 | color: var(--darkTan); 39 | } 40 | .cm-number { 41 | color: var(--tan); 42 | } 43 | .cm-property { 44 | // margin part of margin: 10px;, getElementById 45 | color: var(--blue); 46 | } 47 | .cm-attribute { 48 | color: var(--tan); 49 | } 50 | .cm-builtin { 51 | color: var(--darkTan); 52 | } 53 | .cm-qualifier { 54 | color: var(--darkTan); 55 | } 56 | .cm-operator { 57 | // CoffeeScript -> 58 | color: var(--medBlue); 59 | } 60 | .cm-meta { 61 | // @font-face 62 | color: var(--tan); 63 | } 64 | .cm-string { 65 | // href=["val"] 66 | color: var(--darkBlue); 67 | } 68 | .cm-string-2 { 69 | // match([this]) 70 | color: var(--tan); 71 | } 72 | .cm-tag { 73 | color: var(--darkTan); 74 | } 75 | .cm-bracket { 76 | color: var(--lightTan); 77 | } 78 | .cm-comment { 79 | // /* this comment */ 80 | color: var(--lightTan); 81 | } 82 | } 83 | 84 | :global { 85 | .CodeMirror-cursor { 86 | border-left-color: black; 87 | } 88 | .CodeMirror-selected { 89 | background: #e9e7e0; 90 | } 91 | .CodeMirror-focused .CodeMirror-selected { 92 | background: #e7e0d0; 93 | } 94 | .CodeMirror-matchingbracket, 95 | .CodeMirror-matchingtag { 96 | text-decoration: underline; 97 | text-decoration-color: rgba(black, 0.5); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/highcontrast-dark.module.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Note this is the CodePen Brand theme. It uses many colors from the CodePen global scope. 3 | */ 4 | 5 | .root { 6 | --white: #fff; 7 | --yellow: #ffdd40; 8 | --green: #a3d65a; 9 | --teal: #2bc7b9; 10 | --blue: #5e91f2; 11 | --purple: #ae63e4; 12 | --blueSlate: #88afbf; 13 | --red: #ff3c41; 14 | --orange: #ff8d41; 15 | --pink: #d75093; 16 | --gray: #c7c9d3; 17 | --darkGray: #9b9dad; 18 | 19 | background: #131417; 20 | color: #d5d7de; 21 | 22 | :global { 23 | .cm-keyword { 24 | color: var(--yellow); 25 | } 26 | .cm-atom { 27 | color: var(--red); 28 | } 29 | .cm-def { 30 | color: var(--blue); 31 | } 32 | .cm-variable { 33 | color: var(--gray); 34 | } 35 | .cm-variable-2 { 36 | color: var(--green); 37 | } 38 | .cm-variable-3 { 39 | color: var(--white); 40 | } 41 | .cm-header { 42 | color: var(--red); 43 | } 44 | .cm-number { 45 | color: var(--teal); 46 | } 47 | .cm-property { 48 | color: var(--blue); 49 | } 50 | .cm-attribute { 51 | color: var(--gray); 52 | } 53 | .cm-builtin { 54 | color: var(--purple); 55 | } 56 | .cm-qualifier { 57 | color: var(--yellow); 58 | } 59 | .cm-operator { 60 | color: var(--green); 61 | } 62 | .cm-meta { 63 | color: var(--blue); 64 | } 65 | .cm-string { 66 | color: var(--teal); 67 | } 68 | .cm-string-2 { 69 | color: var(--pink); 70 | } 71 | .cm-tag { 72 | color: var(--yellow); 73 | } 74 | .cm-bracket { 75 | color: var(--darkGray); 76 | } 77 | .cm-comment { 78 | color: var(--blueSlate); 79 | } 80 | } 81 | 82 | // Mode Overrides 83 | &[data-mode='htmlmixed'] { 84 | :global { 85 | .cm-atom { 86 | color: var(--red); 87 | } 88 | } 89 | } 90 | &[data-mode='text/css'] { 91 | :global { 92 | .cm-tag { 93 | color: var(--orange); 94 | } 95 | } 96 | } 97 | 98 | :global { 99 | .CodeMirror-cursor { 100 | border-left-color: var(--white); 101 | } 102 | .CodeMirror-selected { 103 | background: #1e2129; 104 | } 105 | .CodeMirror-focused .CodeMirror-selected { 106 | background: #272c3b; 107 | } 108 | .CodeMirror-matchingbracket, 109 | .CodeMirror-matchingtag { 110 | text-decoration: underline; 111 | text-decoration-color: rgba(white, 0.5); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/highcontrast-light.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --red: #ca2825; 3 | --teal: #0b6d6c; 4 | --orange: #d26d13; 5 | --purple: #7c47b2; 6 | --bluePurple: #575db7; 7 | --blue: #3172bc; 8 | --green: #048500; 9 | --gray: #5a5f73; 10 | --pink: #a3386c; 11 | 12 | background: #fffefb; 13 | color: #886969; 14 | 15 | :global { 16 | .cm-keyword { 17 | // const, var, let 18 | color: var(--red); 19 | } 20 | .cm-atom { 21 | // tag part of 22 | color: var(--teal); 23 | } 24 | .cm-def { 25 | // params part of function(params) { 26 | color: var(--purple); 27 | } 28 | .cm-variable { 29 | // coffeescript var 30 | color: var(--bluePurple); 31 | } 32 | .cm-variable-2 { 33 | // for (key in [this]), markdown li, decimals without 0 in front 34 | color: var(--purple); 35 | } 36 | .cm-variable-3 { 37 | color: black; 38 | } 39 | .cm-header { 40 | color: var(--red); 41 | } 42 | .cm-number { 43 | color: var(--green); 44 | } 45 | .cm-property { 46 | // margin part of margin: 10px;, getElementById 47 | color: var(--teal); 48 | } 49 | .cm-attribute { 50 | color: var(--gray); 51 | } 52 | .cm-builtin { 53 | color: var(--purple); 54 | } 55 | .cm-qualifier { 56 | color: var(--red); 57 | } 58 | .cm-operator { 59 | // CoffeeScript -> 60 | color: var(--teal); 61 | } 62 | .cm-meta { 63 | // @font-face 64 | color: var(--blue); 65 | } 66 | .cm-string { 67 | // href=["val"] 68 | color: var(--teal); 69 | } 70 | .cm-string-2 { 71 | // match([this]) 72 | color: var(--pink); 73 | } 74 | .cm-tag { 75 | color: var(--pink); 76 | } 77 | .cm-bracket { 78 | color: var(--gray); 79 | } 80 | .cm-tag.cm-bracket { 81 | color: var(--gray); 82 | } 83 | .cm-comment { 84 | // /* this comment */ 85 | color: var(--gray); 86 | } 87 | } 88 | 89 | // Mode Overrides 90 | &[data-mode='htmlmixed'] { 91 | :global { 92 | .cm-atom { 93 | color: var(--teal); 94 | } 95 | } 96 | } 97 | &[data-mode='text/css'] { 98 | :global { 99 | .cm-tag { 100 | color: var(--orange); 101 | } 102 | } 103 | } 104 | 105 | :global { 106 | .CodeMirror-cursor { 107 | border-left-color: black; 108 | } 109 | .CodeMirror-selected { 110 | background: #ecf2f9; 111 | } 112 | .CodeMirror-focused .CodeMirror-selected { 113 | background: #e4e8ed; 114 | } 115 | .CodeMirror-matchingbracket, 116 | .CodeMirror-matchingtag { 117 | text-decoration: underline; 118 | text-decoration-color: rgba(black, 0.5); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/mdn-like.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --lightGray: #6d6d6d; 3 | --darkGray: #1b1b1b; 4 | --red: #a30008; 5 | --green: #005a38; 6 | --blue: #005282; 7 | 8 | background: #f4f4f4; 9 | color: var(--darkGray); 10 | 11 | :global { 12 | .cm-keyword { 13 | // const, var, let 14 | color: var(--blue); 15 | } 16 | .cm-atom { 17 | // tag part of 18 | color: var(--red); 19 | } 20 | .cm-def { 21 | // params part of function(params) { 22 | color: var(--green); 23 | } 24 | .cm-variable { 25 | // coffeescript var 26 | color: var(--blue); 27 | } 28 | .cm-variable-2 { 29 | // for (key in [this]), markdown li, decimals without 0 in front 30 | color: var(--green); 31 | } 32 | .cm-variable-3 { 33 | color: var(--green); 34 | } 35 | .cm-header { 36 | color: var(--red); 37 | } 38 | .cm-number { 39 | color: var(--darkGray); 40 | } 41 | .cm-property { 42 | // margin part of margin: 10px;, getElementById 43 | color: var(--red); 44 | } 45 | .cm-attribute { 46 | color: var(--green); 47 | } 48 | .cm-builtin { 49 | color: var(--darkGray); 50 | } 51 | .cm-qualifier { 52 | color: var(--green); 53 | } 54 | .cm-operator { 55 | // CoffeeScript -> 56 | color: var(--green); 57 | } 58 | .cm-meta { 59 | // @font-face 60 | color: var(--red); 61 | } 62 | .cm-string { 63 | // href=["val"] 64 | color: var(--blue); 65 | } 66 | .cm-string-2 { 67 | // match([this]) 68 | color: var(--green); 69 | } 70 | .cm-tag { 71 | color: var(--red); 72 | } 73 | .cm-bracket { 74 | color: var(--darkGray); 75 | } 76 | .cm-comment { 77 | // /* this comment */ 78 | color: var(--lightGray); 79 | } 80 | } 81 | 82 | :global { 83 | .CodeMirror-cursor { 84 | border-left-color: black; 85 | } 86 | .CodeMirror-selected { 87 | background: #e3edf7; 88 | } 89 | .CodeMirror-focused .CodeMirror-selected { 90 | background: #cae3ff; 91 | } 92 | .CodeMirror-matchingbracket, 93 | .CodeMirror-matchingtag { 94 | text-decoration: underline; 95 | text-decoration-color: rgba(black, 0.5); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/oceanic-dark.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --gray: #65737e; 3 | --pink: #c594c5; 4 | --green: #99c794; 5 | --red: #ec5f67; 6 | --yellow: #fac863; 7 | --blue: #6699cc; 8 | --lightBlue: #cdd3de; 9 | --orange: #f99157; 10 | 11 | background: #1b2b34; 12 | color: var(--lightBlue); 13 | 14 | :global { 15 | .cm-keyword { 16 | // const, var, let 17 | color: var(--pink); 18 | } 19 | .cm-atom { 20 | // tag part of 21 | color: var(--pink); 22 | } 23 | .cm-def { 24 | // params part of function(params) { 25 | color: var(--orange); 26 | } 27 | .cm-variable { 28 | // coffeescript var 29 | color: var(--green); 30 | } 31 | .cm-variable-2 { 32 | // for (key in [this]), markdown li, decimals without 0 in front 33 | color: var(--blue); 34 | } 35 | .cm-variable-3 { 36 | color: var(--blue); 37 | } 38 | .cm-header { 39 | color: var(--lightBlue); 40 | } 41 | .cm-number { 42 | color: var(--pink); 43 | } 44 | .cm-property { 45 | // margin part of margin: 10px;, getElementById 46 | color: var(--green); 47 | } 48 | .cm-attribute { 49 | color: var(--green); 50 | } 51 | .cm-builtin { 52 | color: var(--lightBlue); 53 | } 54 | .cm-qualifier { 55 | color: var(--lightBlue); 56 | } 57 | .cm-operator { 58 | // CoffeeScript -> 59 | color: var(--lightBlue); 60 | } 61 | .cm-meta { 62 | // @font-face 63 | color: var(--lightBlue); 64 | } 65 | .cm-string { 66 | // href=["val"] 67 | color: var(--yellow); 68 | } 69 | .cm-string-2 { 70 | // match([this]) 71 | color: var(--yellow); 72 | } 73 | .cm-tag { 74 | color: var(--red); 75 | } 76 | .cm-bracket { 77 | color: var(--lightBlue); 78 | } 79 | .cm-comment { 80 | // /* this comment */ 81 | color: var(--gray); 82 | } 83 | } 84 | 85 | :global { 86 | .CodeMirror-cursor { 87 | border-left-color: white; 88 | } 89 | .CodeMirror-selected { 90 | background: #161e23; 91 | } 92 | .CodeMirror-focused .CodeMirror-selected { 93 | background: #04151f; 94 | } 95 | .CodeMirror-matchingbracket, 96 | .CodeMirror-matchingtag { 97 | text-decoration: underline; 98 | text-decoration-color: rgba(white, 0.5); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/oceanic-light.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --brown: #ab7967; 3 | --pink: #b48ead; 4 | --red: #bf616a; 5 | --green: #72a943; 6 | --yellow: #e5ab00; 7 | --blue: #8fa1b3; 8 | --orange: #d08770; 9 | --darkGray: #343d46; 10 | --lightGray: #65737e; 11 | 12 | background: #eff1f5; 13 | color: var(--darkGray); 14 | 15 | :global { 16 | .cm-keyword { 17 | // const, var, let 18 | color: var(--red); 19 | } 20 | .cm-atom { 21 | // tag part of 22 | color: var(--pink); 23 | } 24 | .cm-def { 25 | // params part of function(params) { 26 | color: var(--orange); 27 | } 28 | .cm-variable { 29 | // coffeescript var 30 | color: var(--green); 31 | } 32 | .cm-variable-2 { 33 | // for (key in [this]), markdown li, decimals without 0 in front 34 | color: var(--blue); 35 | } 36 | .cm-variable-3 { 37 | color: var(--blue); 38 | } 39 | .cm-header { 40 | color: var(--darkGray); 41 | } 42 | .cm-number { 43 | color: var(--pink); 44 | } 45 | .cm-property { 46 | // margin part of margin: 10px;, getElementById 47 | color: var(--green); 48 | } 49 | .cm-attribute { 50 | color: var(--green); 51 | } 52 | .cm-builtin { 53 | color: var(--darkGray); 54 | } 55 | .cm-qualifier { 56 | color: var(--darkGray); 57 | } 58 | .cm-operator { 59 | // CoffeeScript -> 60 | color: var(--darkGray); 61 | } 62 | .cm-meta { 63 | // @font-face 64 | color: var(--darkGray); 65 | } 66 | .cm-string { 67 | // href=["val"] 68 | color: var(--orange); 69 | } 70 | .cm-string-2 { 71 | // match([this]) 72 | color: var(--darkGray); 73 | } 74 | .cm-tag { 75 | color: var(--red); 76 | } 77 | .cm-tag.cm-bracket { 78 | color: var(--red); 79 | } 80 | .cm-bracket { 81 | color: var(--darkGray); 82 | } 83 | .cm-comment { 84 | // /* this comment */ 85 | color: var(--lightGray); 86 | } 87 | } 88 | 89 | :global { 90 | .CodeMirror-cursor { 91 | border-left-color: black; 92 | } 93 | .CodeMirror-selected { 94 | background: #e7ebf1; 95 | } 96 | .CodeMirror-focused .CodeMirror-selected { 97 | background: #dde5f1; 98 | } 99 | .CodeMirror-matchingbracket, 100 | .CodeMirror-matchingtag { 101 | text-decoration: underline; 102 | text-decoration-color: rgba(black, 0.5); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/panda.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --white: #f3f3f3; 3 | --lightGray: #e6e6e6; 4 | --midGray: #676b79; 5 | --green: #19f9d8; 6 | --orange: #ffb86c; 7 | --red: #ff2c6d; 8 | --purple: #b084eb; 9 | --lightPink: #ff9ac1; 10 | --pink: #ff75b5; 11 | 12 | background: #292a2b; 13 | color: var(--lightGray); 14 | 15 | :global { 16 | .cm-keyword { 17 | // const, var, let 18 | color: var(--pink); 19 | } 20 | .cm-atom { 21 | // tag part of 22 | color: var(--red); 23 | } 24 | .cm-def { 25 | // params part of function(params) { 26 | color: var(--lightGray); 27 | } 28 | .cm-variable { 29 | // coffeescript var 30 | color: var(--orange); 31 | } 32 | .cm-variable-2 { 33 | // for (key in [this]), markdown li, decimals without 0 in front 34 | color: var(--lightPink); 35 | } 36 | .cm-variable-3 { 37 | color: var(--lightPink); 38 | } 39 | .cm-header { 40 | color: var(--darkGray); 41 | } 42 | .cm-number { 43 | color: var(--orange); 44 | } 45 | .cm-property { 46 | // margin part of margin: 10px;, getElementById 47 | color: var(--white); 48 | } 49 | .cm-attribute { 50 | color: var(--orange); 51 | } 52 | .cm-builtin { 53 | color: var(--lightGray); 54 | } 55 | .cm-qualifier { 56 | color: var(--lightGray); 57 | } 58 | .cm-operator { 59 | // CoffeeScript -> 60 | color: var(--lightGray); 61 | } 62 | .cm-meta { 63 | // @font-face 64 | color: var(--purple); 65 | } 66 | .cm-string { 67 | // href=["val"] 68 | color: var(--green); 69 | } 70 | .cm-string-2 { 71 | // match([this]) 72 | color: var(--orange); 73 | } 74 | .cm-tag { 75 | color: var(--red); 76 | } 77 | .cm-tag.cm-bracket { 78 | color: var(--red); 79 | } 80 | .cm-bracket { 81 | color: var(--lightGray); 82 | } 83 | .cm-comment { 84 | // /* this comment */ 85 | color: var(--midGray); 86 | } 87 | } 88 | 89 | :global { 90 | .CodeMirror-cursor { 91 | border-left-color: white; 92 | } 93 | .CodeMirror-selected { 94 | background: rgba(black, 0.05); 95 | } 96 | .CodeMirror-focused .CodeMirror-selected { 97 | background: rgba(black, 0.1); 98 | } 99 | .CodeMirror-matchingbracket, 100 | .CodeMirror-matchingtag { 101 | text-decoration: underline; 102 | text-decoration-color: rgba(white, 0.5); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/solarized-dark.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --blue: #268bd2; 3 | --green: #859900; 4 | --red: #cb4b16; 5 | --pink: #d33682; 6 | --brown: #b58900; 7 | --slate: #839496; 8 | --lightSlate: #93a1a1; 9 | --darkSlate: #586e75; 10 | --teal: #2aa198; 11 | --purple: #6c71c4; 12 | 13 | background: #002b36; 14 | color: #77897e; 15 | 16 | :global { 17 | .cm-keyword { 18 | // const, var, let 19 | color: var(--blue); 20 | } 21 | .cm-atom { 22 | // tag part of 23 | color: var(--green); 24 | } 25 | .cm-def { 26 | // params part of function(params) { 27 | color: var(--brown); 28 | } 29 | .cm-variable { 30 | // coffeescript var 31 | color: var(--slate); 32 | } 33 | .cm-variable-2 { 34 | // for (key in [this]), markdown li, decimals without 0 in front 35 | color: var(--green); 36 | } 37 | .cm-variable-3 { 38 | color: var(--lightSlate); 39 | } 40 | .cm-header { 41 | color: var(--red); 42 | } 43 | .cm-number { 44 | color: var(--teal); 45 | } 46 | .cm-property { 47 | // margin part of margin: 10px;, getElementById 48 | color: var(--brown); 49 | } 50 | .cm-attribute { 51 | color: var(--slate); 52 | } 53 | .cm-builtin { 54 | color: var(--purple); 55 | } 56 | .cm-qualifier { 57 | color: var(--blue); 58 | } 59 | .cm-operator { 60 | // CoffeeScript -> 61 | color: var(--green); 62 | } 63 | .cm-meta { 64 | // @font-face 65 | color: var(--blue); 66 | } 67 | .cm-string { 68 | // href=["val"] 69 | color: var(--teal); 70 | } 71 | .cm-string-2 { 72 | // match([this]) 73 | color: var(--pink); 74 | } 75 | .cm-tag { 76 | color: var(--blue); 77 | } 78 | .cm-tag.cm-bracket { 79 | color: var(--slate); 80 | } 81 | .cm-bracket { 82 | color: var(--slate); 83 | } 84 | .cm-comment { 85 | // /* this comment */ 86 | color: var(--darkSlate); 87 | } 88 | } 89 | 90 | // Mode Overrides 91 | &[data-mode='htmlmixed'] { 92 | :global { 93 | .cm-atom { 94 | color: var(--red); 95 | } 96 | } 97 | } 98 | &[data-mode='text/css'] { 99 | :global { 100 | .cm-tag { 101 | color: var(--green); 102 | } 103 | } 104 | } 105 | 106 | :global { 107 | .CodeMirror-cursor { 108 | border-left-color: white; 109 | } 110 | .CodeMirror-selected { 111 | background: #04252f; 112 | } 113 | .CodeMirror-focused .CodeMirror-selected { 114 | background: #00181f; 115 | } 116 | .CodeMirror-matchingbracket, 117 | .CodeMirror-matchingtag { 118 | text-decoration: underline; 119 | text-decoration-color: rgba(white, 0.5); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/solarized-light.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --blue: #268bd2; 3 | --green: #859900; 4 | --red: #cb4b16; 5 | --brown: #b58900; 6 | --slate: #657b83; 7 | --darkSlate: #586e75; 8 | --teal: #2aa198; 9 | --pink: #d33682; 10 | --lightGray: #93a1a1; 11 | 12 | background: #fdf6e3; 13 | color: #657b83; 14 | 15 | :global { 16 | .cm-keyword { 17 | // const, var, let 18 | color: var(--blue); 19 | } 20 | .cm-atom { 21 | // tag part of 22 | color: var(--green); 23 | } 24 | .cm-def { 25 | // params part of function(params) { 26 | color: var(--brown); 27 | } 28 | .cm-variable { 29 | // coffeescript var 30 | color: var(--slate); 31 | } 32 | .cm-variable-2 { 33 | // for (key in [this]), markdown li, decimals without 0 in front 34 | color: var(--green); 35 | } 36 | .cm-variable-3 { 37 | color: var(--darkSlate); 38 | } 39 | .cm-header { 40 | color: var(--red); 41 | } 42 | .cm-number { 43 | color: var(--teal); 44 | } 45 | .cm-property { 46 | // margin part of margin: 10px;, getElementById 47 | color: var(--brown); 48 | } 49 | .cm-attribute { 50 | color: var(--slate); 51 | } 52 | .cm-builtin { 53 | color: var(--purple); 54 | } 55 | .cm-qualifier { 56 | color: var(--blue); 57 | } 58 | .cm-operator { 59 | // CoffeeScript -> 60 | color: var(--green); 61 | } 62 | .cm-meta { 63 | // @font-face 64 | color: var(--blue); 65 | } 66 | .cm-string { 67 | // href=["val"] 68 | color: var(--teal); 69 | } 70 | .cm-string-2 { 71 | // match([this]) 72 | color: var(--pink); 73 | } 74 | .cm-tag { 75 | color: var(--blue); 76 | } 77 | .cm-tag.cm-bracket { 78 | color: var(--slate); 79 | } 80 | .cm-bracket { 81 | color: var(--slate); 82 | } 83 | .cm-comment { 84 | // /* this comment */ 85 | color: var(--lightGray); 86 | } 87 | } 88 | 89 | :global { 90 | .CodeMirror-cursor { 91 | border-left-color: black; 92 | } 93 | .CodeMirror-selected { 94 | background: #efecd7; 95 | } 96 | .CodeMirror-focused .CodeMirror-selected { 97 | background: #e9e3c2; 98 | } 99 | .CodeMirror-matchingbracket, 100 | .CodeMirror-matchingtag { 101 | text-decoration: underline; 102 | text-decoration-color: rgba(black, 0.5); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/synthwave.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --white: #ffffff; 3 | --yellow: #ffdd40; 4 | --lightGreen: #a3d65a; 5 | --darkGreen: #47cf73; 6 | --teal: #2bc7b9; 7 | --red: #ff3c41; 8 | --blue: #0ebeff; 9 | --darkBlue: #5e91f2; 10 | --purple: #ae63e4; 11 | --lightGray: #c7c9d3; 12 | --darkGray: #9b9dad; 13 | --pink: #d75093; 14 | --slate: #88afbf; 15 | --black: #131417; 16 | 17 | background: repeating-linear-gradient( 18 | to top, 19 | rgba(255, 255, 255, 0.03) 0 2px, 20 | transparent 2px 4px 21 | ), 22 | linear-gradient(to bottom, #200933 75%, #3d0b43); 23 | color: #f1f1f3; 24 | 25 | &::after { 26 | content: ''; 27 | height: 50%; 28 | width: 100%; 29 | display: block; 30 | background-image: linear-gradient( 31 | 90deg, 32 | rgba(252, 25, 154, 0.1) 1px, 33 | rgba(0, 0, 0, 0) 1px 34 | ), 35 | linear-gradient(0deg, rgba(252, 25, 154, 0.1) 1px, rgba(0, 0, 0, 0) 1px); 36 | background-position: bottom; 37 | background-repeat: repeat; 38 | background-size: 20px 20px; 39 | left: -25px; 40 | position: absolute; 41 | pointer-events: none; 42 | bottom: 0; 43 | transform: perspective(100px) rotateX(60deg); 44 | z-index: 0; 45 | } 46 | 47 | :global { 48 | .cm-keyword { 49 | // const, var, let 50 | color: var(--yellow); 51 | } 52 | .cm-atom { 53 | // tag part of 54 | color: var(--lightGreen); 55 | } 56 | .cm-def { 57 | // params part of function(params) { 58 | color: var(--blue); 59 | } 60 | .cm-variable { 61 | // coffeescript var 62 | color: var(--lightGray); 63 | } 64 | .cm-variable-2 { 65 | // for (key in [this]), markdown li, decimals without 0 in front 66 | color: var(--darkGreen); 67 | } 68 | .cm-variable-3 { 69 | color: var(--white); 70 | } 71 | .cm-header { 72 | color: var(--red); 73 | } 74 | .cm-number { 75 | color: var(--teal); 76 | } 77 | .cm-property { 78 | // margin part of margin: 10px;, getElementById 79 | color: var(--darkBlue); 80 | } 81 | .cm-attribute { 82 | color: var(--lightGray); 83 | } 84 | .cm-builtin { 85 | color: var(--purple); 86 | } 87 | .cm-qualifier { 88 | color: var(--yellow); 89 | } 90 | .cm-operator { 91 | // CoffeeScript -> 92 | color: var(--darkGreen); 93 | } 94 | .cm-meta { 95 | // @font-face 96 | color: var(--blue); 97 | } 98 | .cm-string { 99 | // href=["val"] 100 | color: var(--real); 101 | } 102 | .cm-string-2 { 103 | // match([this]) 104 | color: var(--pink); 105 | } 106 | .cm-tag { 107 | color: var(--yellow); 108 | } 109 | .cm-bracket { 110 | color: var(--darkGray); 111 | } 112 | .cm-tag.cm-bracket { 113 | color: var(--darkGray); 114 | } 115 | .cm-comment { 116 | // /* this comment */ 117 | color: var(--slate); 118 | } 119 | 120 | // Theme Extras 121 | .cm-operator, 122 | .cm-variable-3, 123 | .cm-attribute, 124 | .cm-property, 125 | .cm-keyword, 126 | .presentation > .cm-def, 127 | .cm-qualifier { 128 | color: #fc199a; 129 | text-shadow: 0 0 2px #393a33, 0 0 35px #ffffff44, 0 0 10px #fc199a, 130 | 0 0 2px #fc199a; 131 | } 132 | 133 | .cm-def, 134 | .cm-variable-2, 135 | .cm-variable { 136 | color: #61e2ff; 137 | text-shadow: 0 0 2px #001716, 0 0 5px #03edf933, 0 0 10px #ffff6633; 138 | } 139 | 140 | .cm-meta, 141 | .cm-meta + .cm-property, 142 | .cm-string, 143 | .cm-string-2 { 144 | color: #9963ff; 145 | text-shadow: none; 146 | } 147 | .cm-tag, 148 | .cm-callee, 149 | .cm-tag.cm-bracket { 150 | color: #fc0; 151 | text-shadow: 0 0 2px #100c0f, 0 0 3px #ffaa0099, 0 0 5px #ffaa0099, 152 | 0 0 10px #ffaa0099; 153 | } 154 | .cm-comment { 155 | color: #9963ff99; 156 | text-shadow: 0 0 2px #001716, 0 0 5px #03edf933, 0 0 10px #ffff6633; 157 | } 158 | } 159 | 160 | // Mode Overrides 161 | &[data-mode='htmlmixed'] { 162 | :global { 163 | .cm-atom { 164 | color: var(--red); 165 | } 166 | } 167 | } 168 | &[data-mode='text/css'] { 169 | :global { 170 | .cm-tag { 171 | color: var(--orange); 172 | text-shadow: 0 0 2px #100c0f, 0 0 3px #ffaa0099, 0 0 5px #ffaa0099, 173 | 0 0 10px #ffaa0099; 174 | } 175 | } 176 | } 177 | &[data-mode='text/javascript'], 178 | &[data-mode='text/typescript'] { 179 | :global { 180 | .cm-variable + .cm-property { 181 | color: #61e2ff; 182 | text-shadow: 0 0 2px #001716, 0 0 5px #03edf933, 0 0 10px #ffff6633; 183 | } 184 | .cm-property { 185 | color: #fc0; 186 | text-shadow: 0 0 2px #100c0f, 0 0 3px #ffaa0099, 0 0 5px #ffaa0099, 187 | 0 0 10px #ffaa0099; 188 | } 189 | } 190 | } 191 | 192 | :global { 193 | .CodeMirror-cursor { 194 | border-left-color: white; 195 | } 196 | .CodeMirror-selected { 197 | background: rgba(255, 255, 255, 0.05); 198 | } 199 | .CodeMirror-focused .CodeMirror-selected { 200 | background: rgba(255, 255, 255, 0.1); 201 | } 202 | .CodeMirror-matchingbracket, 203 | .CodeMirror-matchingtag { 204 | text-decoration: underline; 205 | text-decoration-color: rgba(255, 255, 255, 0.5); 206 | } 207 | 208 | .CodeMirror-simplescroll-horizontal div, 209 | .CodeMirror-simplescroll-vertical div { 210 | background: rgba(153, 99, 255, 0.5); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/tomorrow-night.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --white: white; 3 | --midGray: #c3c6c4; 4 | --darkGray: #b7bbc8; 5 | --orange: #dd925f; 6 | --purple: #ae94c0; 7 | --yellow: #efc371; 8 | --green: #b5bc67; 9 | 10 | background: #1d1f20; 11 | color: var(--midGray); 12 | 13 | :global { 14 | .cm-keyword { 15 | // const, var, let 16 | color: var(--yellow); 17 | } 18 | .cm-atom { 19 | // tag part of 20 | color: var(--yellow); 21 | } 22 | .cm-def { 23 | // params part of function(params) { 24 | color: var(--yellow); 25 | } 26 | .cm-variable { 27 | // coffeescript var 28 | color: var(--yellow); 29 | } 30 | .cm-variable-2 { 31 | // for (key in [this]), markdown li, decimals without 0 in front 32 | color: var(--purple); 33 | } 34 | .cm-variable-3 { 35 | color: var(--purple); 36 | } 37 | .cm-header { 38 | color: var(--orange); 39 | } 40 | .cm-number { 41 | color: var(--orange); 42 | } 43 | .cm-property { 44 | // margin part of margin: 10px;, getElementById 45 | color: var(--purple); 46 | } 47 | .cm-attribute { 48 | color: var(--midGray); 49 | } 50 | .cm-builtin { 51 | color: var(--yellow); 52 | } 53 | .cm-qualifier { 54 | color: var(--yellow); 55 | } 56 | .cm-operator { 57 | // CoffeeScript -> 58 | color: var(--midGray); 59 | } 60 | .cm-meta { 61 | // @font-face 62 | color: var(--green); 63 | } 64 | .cm-string { 65 | // href=["val"] 66 | color: var(--green); 67 | } 68 | .cm-string-2 { 69 | // match([this]) 70 | color: var(--white); 71 | } 72 | .cm-tag { 73 | color: var(--yellow); 74 | } 75 | .cm-bracket { 76 | color: var(--midGray); 77 | } 78 | .cm-comment { 79 | // /* this comment */ 80 | color: var(--darkGray); 81 | } 82 | } 83 | 84 | // Mode Overrides 85 | &[data-mode='htmlmixed'] { 86 | :global { 87 | .cm-atom { 88 | color: var(--green); 89 | } 90 | } 91 | } 92 | &[data-mode='text/css'] { 93 | :global { 94 | .cm-tag { 95 | color: var(--yellow); 96 | } 97 | } 98 | } 99 | 100 | :global { 101 | .CodeMirror-cursor { 102 | border-left-color: white; 103 | } 104 | .CodeMirror-selected { 105 | background: #171813; 106 | } 107 | .CodeMirror-focused .CodeMirror-selected { 108 | background: #0c0c04; 109 | } 110 | .CodeMirror-matchingbracket, 111 | .CodeMirror-matchingtag { 112 | text-decoration: underline; 113 | text-decoration-color: rgba(white, 0.5); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/twilight.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | background: #1d1e22; 3 | color: var(--cp-color-1); 4 | 5 | --yellow: #ddca7e; 6 | --blue: #809bbd; 7 | --purple: #9a8297; 8 | --brown: #a7925a; 9 | --orange: #ff6400; 10 | --burnt: #d0782a; 11 | --green: #96b38a; 12 | 13 | :global { 14 | .cm-keyword { 15 | color: var(--yellow); 16 | } 17 | .cm-atom { 18 | color: var(--yellow); 19 | } 20 | .cm-def { 21 | color: var(--blue); 22 | } 23 | .cm-variable { 24 | color: var(--yellow); 25 | } 26 | .cm-variable-2 { 27 | color: var(--blue); 28 | } 29 | .cm-variable-3 { 30 | color: var(--blue); 31 | } 32 | .cm-header { 33 | color: var(--orange); 34 | } 35 | .cm-number { 36 | color: var(--burnt); 37 | } 38 | .cm-property { 39 | color: var(--purple); 40 | } 41 | .cm-attribute { 42 | color: var(--yellow); 43 | } 44 | .cm-builtin { 45 | color: var(--yellow); 46 | } 47 | .cm-qualifier { 48 | color: var(--yellow); 49 | } 50 | .cm-operator { 51 | color: var(--cp-color-5); 52 | } 53 | .cm-meta { 54 | color: var(--purple); 55 | } 56 | .cm-string { 57 | color: var(--green); 58 | } 59 | .cm-string-2 { 60 | color: var(--green); 61 | } 62 | .cm-tag { 63 | color: var(--brown); 64 | } 65 | .cm-tag.cm-bracket { 66 | // so that the entire HTML is the same color. 67 | color: var(--brown); 68 | } 69 | .cm-bracket { 70 | color: inherit; 71 | } 72 | .cm-comment { 73 | color: var(--cp-color-12); 74 | } 75 | } 76 | 77 | // Mode Overrides 78 | &[data-mode='text/css'] { 79 | :global { 80 | .cm-tag { 81 | color: var(--yellow); 82 | } 83 | } 84 | } 85 | &[data-mode='htmlmixed'] { 86 | :global { 87 | .cm-atom { 88 | color: var(--green); 89 | } 90 | } 91 | } 92 | 93 | :global { 94 | .CodeMirror-cursor { 95 | border-left-color: white; 96 | } 97 | .CodeMirror-selected { 98 | background: #222427; 99 | } 100 | .CodeMirror-focused .CodeMirror-selected { 101 | background: #2a2e33; 102 | } 103 | .CodeMirror-matchingbracket, 104 | .CodeMirror-matchingtag { 105 | text-decoration: underline; 106 | text-decoration-color: rgba(white, 0.5); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/OLD/xq-light.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | --white: white; 3 | --black: black; 4 | --purple: #5a5cad; 5 | --darkPurple: #7f007f; 6 | --lightBlue: #6c8cd5; 7 | --blue: #0080ff; 8 | --darkGreen: #164; 9 | --green: #7ea656; 10 | --lightGreen: #cc7; 11 | --teal: #3f7f7f; 12 | --gray: #999; 13 | --red: #f00; 14 | 15 | background: var(--white); 16 | color: var(--black); 17 | 18 | :global { 19 | .cm-keyword { 20 | // const, var, let 21 | color: var(--purple); 22 | } 23 | .cm-atom { 24 | // tag part of 25 | color: var(--lightBlue); 26 | } 27 | .cm-def { 28 | // params part of function(params) { 29 | color: var(--black); 30 | } 31 | .cm-variable { 32 | // coffeescript var 33 | color: var(--black); 34 | } 35 | .cm-variable-2 { 36 | // for (key in [this]), markdown li, decimals without 0 in front 37 | color: var(--black); 38 | } 39 | .cm-variable-3 { 40 | color: var(--black); 41 | } 42 | .cm-header { 43 | color: var(--black); 44 | } 45 | .cm-number { 46 | color: var(--darkGreen); 47 | } 48 | .cm-property { 49 | // margin part of margin: 10px;, getElementById 50 | color: var(--gray); 51 | } 52 | .cm-attribute { 53 | color: var(--darkPurple); 54 | } 55 | .cm-builtin { 56 | color: var(--green); 57 | } 58 | .cm-qualifier { 59 | color: var(--gray); 60 | } 61 | .cm-operator { 62 | // CoffeeScript -> 63 | color: var(--purple); 64 | } 65 | .cm-meta { 66 | // @font-face 67 | color: var(--blue); 68 | } 69 | .cm-string { 70 | // href=["val"] 71 | color: var(--red); 72 | } 73 | .cm-string-2 { 74 | // match([this]) 75 | color: var(--black); 76 | } 77 | .cm-tag { 78 | color: var(--teal); 79 | } 80 | .cm-bracket { 81 | color: var(--teal); 82 | } 83 | .cm-comment { 84 | // /* this comment */ 85 | color: var(--blue); 86 | } 87 | } 88 | 89 | :global { 90 | .CodeMirror-cursor { 91 | border-left-color: black; 92 | } 93 | .CodeMirror-selected { 94 | background: rgba(black, 0.05); 95 | } 96 | .CodeMirror-focused .CodeMirror-selected { 97 | background: rgba(black, 0.1); 98 | } 99 | .CodeMirror-matchingbracket, 100 | .CodeMirror-matchingtag { 101 | text-decoration: underline; 102 | text-decoration-color: rgba(black, 0.5); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/debug.js: -------------------------------------------------------------------------------- 1 | import { EditorView } from "codemirror"; 2 | import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; 3 | import { tags } from "@lezer/highlight"; 4 | 5 | // Helpful "theme" to debug what Lezer tag properly matches each piece of the syntax. 6 | // https://lezer.codemirror.net/docs/ref/#highlight.tags 7 | const debugTheme = EditorView.theme({ 8 | ".cm-line span": { 9 | position: "relative", 10 | }, 11 | ".cm-line span:hover::after": { 12 | position: "absolute", 13 | bottom: "100%", 14 | left: 0, 15 | background: "black", 16 | color: "white", 17 | border: "solid 2px", 18 | borderRadius: "5px", 19 | content: "var(--tags)", 20 | width: `max-content`, 21 | padding: "1px 4px", 22 | zIndex: 10, 23 | pointerEvents: "none", 24 | }, 25 | }); 26 | const debugHighlightStyle = HighlightStyle.define( 27 | Object.entries(tags).map(([key, value]) => { 28 | return { tag: value, "--tags": `"tag.${key}"` }; 29 | }) 30 | ); 31 | 32 | const debug = [debugTheme, syntaxHighlighting(debugHighlightStyle)]; 33 | 34 | export { debug, debugTheme, debugHighlightStyle }; 35 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/highContrastDark.js: -------------------------------------------------------------------------------- 1 | import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; 2 | import { EditorView } from "@codemirror/view"; 3 | import { tags } from "@lezer/highlight"; 4 | 5 | // TODO: Confirm a match between old & new 6 | const COLORS = { 7 | black: "#131417", 8 | darkestGray: "#1e2129", 9 | darkerGray: "#272c3b", 10 | white: "#fff", 11 | yellow: "#ffdd40", 12 | green: "#47cf73", 13 | teal: "#2bc7b9", 14 | blue: "#5e91f2", 15 | lightBlue: "#0ebeff", 16 | purple: "#ae63e4", 17 | blueSlate: "#88afbf", 18 | red: "#ff3c41", 19 | orange: "#ff8d41", 20 | pink: "#d75093", 21 | gray: "#c7c9d3", 22 | darkGray: "#9b9dad", 23 | }; 24 | 25 | const highContrastDarkTheme = EditorView.theme( 26 | { 27 | "&": { 28 | color: COLORS.white, 29 | backgroundColor: COLORS.black, 30 | }, 31 | ".cm-content": { 32 | caretColor: COLORS.white, 33 | }, 34 | "&.cm-focused .cm-cursor": { borderLeftColor: COLORS.white }, 35 | "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection": 36 | { backgroundColor: COLORS.darkerGray }, 37 | ".cm-panels": { backgroundColor: COLORS.black, color: COLORS.white }, 38 | ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, 39 | ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" }, 40 | ".cm-searchMatch": { 41 | backgroundColor: COLORS.blue, 42 | color: COLORS.white, 43 | outline: `1px solid ${COLORS.blue}`, 44 | }, 45 | ".cm-searchMatch.cm-searchMatch-selected": { 46 | backgroundColor: "#6199ff2f", 47 | }, 48 | ".cm-selectionMatch": { backgroundColor: "#aafe661a" }, 49 | ".cm-matchingBracket, .cm-nonmatchingBracket": { 50 | backgroundColor: COLORS.darkGray, 51 | outline: `1px solid ${COLORS.darkGray}`, 52 | }, 53 | ".cm-gutters": { 54 | backgroundColor: COLORS.black, 55 | color: COLORS.darkGray, 56 | border: "none", 57 | }, 58 | ".cm-activeLineGutter": { 59 | color: COLORS.gray, 60 | }, 61 | ".cm-activeLine, .cm-activeLineGutter": { 62 | backgroundColor: COLORS.darkestGray, 63 | }, 64 | ".cm-foldPlaceholder": { 65 | backgroundColor: "transparent", 66 | border: "none", 67 | color: "#ddd", 68 | }, 69 | ".cm-tooltip": { 70 | border: `1px solid ${COLORS.darkGray}`, 71 | backgroundColor: COLORS.black, 72 | }, 73 | // ".cm-tooltip-autocomplete": { 74 | // "& > ul > li[aria-selected]": { 75 | // backgroundColor: "#1d1e22", 76 | // color: COLORS.white, 77 | // }, 78 | // }, 79 | }, 80 | { dark: true } 81 | ); 82 | 83 | // https://lezer.codemirror.net/docs/ref/#highlight.tags 84 | const highContrastDarkHighlightStyle = HighlightStyle.define( 85 | [ 86 | // @mixin cool, rem, function, return, let, const 87 | { tag: tags.keyword, color: COLORS.yellow }, 88 | 89 | // property name, attribute name, variable name 90 | { 91 | tag: tags.name, 92 | color: COLORS.blue, 93 | }, 94 | 95 | { 96 | tag: tags.attributeName, 97 | color: COLORS.white, 98 | }, 99 | 100 | // JavaScript property name 101 | { 102 | tag: tags.propertyName, 103 | color: COLORS.blue, 104 | }, 105 | 106 | // ??? 107 | { 108 | tag: [tags.character, tags.macroName, tags.meta], 109 | color: COLORS.blue, 110 | }, 111 | 112 | // JavaScript function name 113 | { 114 | tag: [tags.function(tags.variableName)], 115 | color: COLORS.teal, 116 | }, 117 | 118 | { 119 | tag: tags.angleBracket, 120 | color: COLORS.gray, 121 | }, 122 | 123 | // ??? 124 | { 125 | tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], 126 | color: COLORS.yellow, 127 | }, 128 | 129 | // { 130 | // tag: tags.tagName, 131 | // color: COLORS.orange, 132 | // }, 133 | 134 | // variable name 135 | { 136 | tag: [tags.definition(tags.name)], 137 | color: COLORS.lightBlue, 138 | }, 139 | { 140 | tag: tags.operator, 141 | color: COLORS.green, 142 | }, 143 | 144 | // Semicolon 145 | { 146 | tag: [tags.separator], 147 | color: COLORS.white, 148 | }, 149 | 150 | // HTML tag names, selectors 151 | { 152 | tag: [ 153 | tags.typeName, 154 | tags.className, 155 | tags.changed, 156 | tags.annotation, 157 | tags.modifier, 158 | tags.self, 159 | tags.namespace, 160 | ], 161 | color: COLORS.yellow, 162 | }, 163 | 164 | // 0 165 | { tag: [tags.number], color: COLORS.teal }, 166 | 167 | // + 168 | { 169 | tag: [ 170 | // tags.operator, 171 | tags.operatorKeyword, 172 | tags.url, 173 | tags.escape, 174 | tags.regexp, 175 | tags.link, 176 | tags.special(tags.string), 177 | ], 178 | color: COLORS.pink, 179 | }, 180 | 181 | // Comments 182 | { tag: [tags.comment], color: COLORS.blueSlate }, 183 | 184 | { tag: tags.strong, fontWeight: "bold" }, 185 | { tag: tags.emphasis, fontStyle: "italic" }, 186 | { tag: tags.link, color: "#eee", textDecoration: "underline" }, 187 | { 188 | tag: tags.heading, 189 | fontWeight: "bold", 190 | color: "#eee", 191 | }, 192 | { 193 | tag: [tags.atom, tags.bool, tags.special(tags.variableName)], 194 | color: COLORS.yellow, 195 | }, 196 | 197 | // "bar", quoted HTML attribute value 198 | { 199 | tag: [tags.processingInstruction, tags.string, tags.inserted], 200 | color: COLORS.teal, 201 | }, 202 | 203 | // Errors 204 | { tag: [tags.invalid, tags.deleted], color: COLORS.red }, 205 | ], 206 | { dark: true } 207 | ); 208 | 209 | const highContrastDark = [ 210 | highContrastDarkTheme, 211 | syntaxHighlighting(highContrastDarkHighlightStyle), 212 | ]; 213 | 214 | export { 215 | highContrastDark, 216 | highContrastDarkHighlightStyle, 217 | highContrastDarkTheme, 218 | }; 219 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/themes/twilight.js: -------------------------------------------------------------------------------- 1 | import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; 2 | import { EditorView } from "@codemirror/view"; 3 | import { tags } from "@lezer/highlight"; 4 | 5 | const twilightTheme = EditorView.theme( 6 | { 7 | "&": { 8 | color: "white", 9 | backgroundColor: "#1d1e22", 10 | }, 11 | ".cm-content": { 12 | caretColor: "white", 13 | }, 14 | "&.cm-focused .cm-cursor": { borderLeftColor: "white" }, 15 | "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection": 16 | { backgroundColor: "#343539" }, 17 | ".cm-panels": { backgroundColor: "#1d1e22", color: "white" }, 18 | ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, 19 | ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" }, 20 | ".cm-searchMatch": { 21 | backgroundColor: "#72a1ff59", 22 | outline: "1px solid #457dff", 23 | }, 24 | ".cm-searchMatch.cm-searchMatch-selected": { 25 | backgroundColor: "#6199ff2f", 26 | }, 27 | ".cm-activeLine": { backgroundColor: "#333438" }, 28 | ".cm-selectionMatch": { backgroundColor: "#aafe661a" }, 29 | ".cm-matchingBracket, .cm-nonmatchingBracket": { 30 | backgroundColor: "#bad0f847", 31 | outline: "1px solid #515a6b", 32 | }, 33 | ".cm-gutters": { 34 | backgroundColor: "#1d1e22", 35 | color: "#44464e", 36 | border: "none", 37 | }, 38 | ".cm-activeLineGutter": { 39 | backgroundColor: "#333438", 40 | color: "#54565e", 41 | }, 42 | ".cm-foldPlaceholder": { 43 | backgroundColor: "transparent", 44 | border: "none", 45 | color: "#ddd", 46 | }, 47 | ".cm-tooltip": { 48 | border: "1px solid #181a1f", 49 | backgroundColor: "#1d1e22", 50 | }, 51 | ".cm-tooltip-autocomplete": { 52 | "& > ul > li[aria-selected]": { 53 | backgroundColor: "#1d1e22", 54 | color: "white", 55 | }, 56 | }, 57 | }, 58 | { dark: true } 59 | ); 60 | 61 | const twilightHighlightStyle = HighlightStyle.define([ 62 | // @mixin cool, rem, function, return, let, const 63 | { tag: tags.keyword, color: "#ddca7e" }, 64 | 65 | // property name, attribute name, variable name 66 | { 67 | tag: tags.name, 68 | color: "#9a8297", 69 | }, 70 | 71 | // JavaScript property name 72 | { 73 | tag: tags.propertyName, 74 | color: "#9a8297", 75 | }, 76 | 77 | // ??? 78 | { 79 | tag: [tags.character, tags.macroName], 80 | color: "blue", 81 | }, 82 | 83 | // JavaScript function name 84 | { 85 | tag: [tags.function(tags.variableName), tags.labelName], 86 | color: "#809bbd", 87 | }, 88 | 89 | // ??? 90 | { 91 | tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], 92 | color: "#a7925a", 93 | }, 94 | 95 | // variable name 96 | { 97 | tag: [tags.definition(tags.name)], 98 | color: "#809bbd", 99 | }, 100 | 101 | // Semicolon 102 | { 103 | tag: [tags.separator], 104 | color: "white", 105 | }, 106 | 107 | // HTML tag names, selectors 108 | { 109 | tag: [ 110 | tags.typeName, 111 | tags.className, 112 | tags.changed, 113 | tags.annotation, 114 | tags.modifier, 115 | tags.self, 116 | tags.namespace, 117 | ], 118 | color: "#ddca7e", 119 | }, 120 | 121 | // 0 122 | { tag: [tags.number], color: "#d0782a" }, 123 | 124 | // + 125 | { 126 | tag: [ 127 | tags.operator, 128 | tags.operatorKeyword, 129 | tags.url, 130 | tags.escape, 131 | tags.regexp, 132 | tags.link, 133 | tags.special(tags.string), 134 | ], 135 | color: "#809bbd", 136 | }, 137 | 138 | // Comments 139 | { tag: [tags.meta, tags.comment], color: "#717790" }, 140 | 141 | { tag: tags.strong, fontWeight: "bold" }, 142 | { tag: tags.emphasis, fontStyle: "italic" }, 143 | { tag: tags.link, color: "#eee", textDecoration: "underline" }, 144 | { 145 | tag: tags.heading, 146 | fontWeight: "bold", 147 | color: "#eee", 148 | }, 149 | { 150 | tag: [tags.atom, tags.bool, tags.special(tags.variableName)], 151 | color: "#ddca7e", 152 | }, 153 | 154 | // "bar", quoted HTML attribute value 155 | { 156 | tag: [tags.processingInstruction, tags.string, tags.inserted], 157 | color: "#96b38a", 158 | }, 159 | 160 | // Errors 161 | { tag: [tags.invalid, tags.deleted], color: "red" }, 162 | ]); 163 | 164 | const twilight = [twilightTheme, syntaxHighlighting(twilightHighlightStyle)]; 165 | 166 | export { twilight, twilightHighlightStyle, twilightTheme }; 167 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/useExtensionCompartment.js: -------------------------------------------------------------------------------- 1 | import { useMemo, useCallback } from "react"; 2 | import { Compartment } from "@codemirror/state"; 3 | 4 | export function useExtensionCompartment(editorView) { 5 | const compartment = useMemo(() => new Compartment(), []); 6 | 7 | const dispatch = editorView?.dispatch; 8 | const updateCompartment = useCallback( 9 | function updateCompartment(extension) { 10 | if (dispatch) 11 | dispatch({ 12 | effects: compartment.reconfigure(extension), 13 | }); 14 | }, 15 | [compartment, dispatch] 16 | ); 17 | 18 | return [ 19 | // Initial value of [] to prevent extension errors 20 | compartment.of([]), 21 | updateCompartment, 22 | ]; 23 | } 24 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/extensions/useExtensions.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { defaultExtensions } from "./defaultExtensions"; 3 | import { useEmmetExtension } from "./emmet"; 4 | import { useLanguageExtension } from "./languages"; 5 | import { useThemeExtension } from "./themes"; 6 | import { useReadOnly } from "./readOnly"; 7 | import { useLineWrapping } from "./lineWrapping"; 8 | import { useIndentation } from "./indentation"; 9 | import { useFonts } from "./fonts"; 10 | import { useLineNumbers } from "./lineNumbers"; 11 | import { useCodeFolding } from "./codeFolding"; 12 | import { useMatchBrackets } from "./matchBrackets"; 13 | import { useAutocomplete } from "./autocomplete"; 14 | import { useOnChange } from "./onChange"; 15 | import { useExtraExtensions } from "./extensions"; 16 | 17 | export function useExtensions(props, editorView) { 18 | const languageExtension = useLanguageExtension(props, editorView); 19 | 20 | const editorSettings = props.editorSettings || {}; 21 | // console.log("useExtensions", editorSettings); 22 | 23 | const lineNumbersExtension = useLineNumbers(editorSettings, editorView); 24 | const codeFoldingExtension = useCodeFolding(editorSettings, editorView); 25 | const themeExtension = useThemeExtension(editorSettings, editorView); 26 | const fontsExtension = useFonts(editorSettings, editorView); 27 | const lineWrappingExtension = useLineWrapping(editorSettings, editorView); 28 | const indentationExtension = useIndentation(editorSettings, editorView); 29 | const matchBracketsExtension = useMatchBrackets(editorSettings, editorView); 30 | const autocompleteExtension = useAutocomplete(editorSettings, editorView); 31 | const emmetExtension = useEmmetExtension( 32 | props.language, 33 | editorSettings, 34 | editorView 35 | ); 36 | const readOnlyExtension = useReadOnly(props, editorView); 37 | const onChangeExtension = useOnChange(props, editorView); 38 | const extraExtensions = useExtraExtensions(props, editorView); 39 | 40 | // Store as a ref because the extensions themselves are stored in compartments that won't change. We don't need to rebuild this array every time it re-renders. 41 | const extensionsRef = useRef([ 42 | // Order for Emmet & default is important to allow `tab` key indentation to work. 43 | emmetExtension, 44 | defaultExtensions, 45 | 46 | // Order can affect gutter layout and cascade precedence. 47 | lineNumbersExtension, 48 | codeFoldingExtension, 49 | languageExtension, 50 | 51 | matchBracketsExtension, 52 | autocompleteExtension, 53 | readOnlyExtension, 54 | themeExtension, 55 | fontsExtension, 56 | lineWrappingExtension, 57 | indentationExtension, 58 | onChangeExtension, 59 | extraExtensions, 60 | ]); 61 | 62 | return extensionsRef.current; 63 | } 64 | -------------------------------------------------------------------------------- /components/CodeMirror6Instance/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CodeMirror6Instance"; 2 | -------------------------------------------------------------------------------- /components/CodeSamples/CodeSamples.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | import CodeEditor from "../CodeEditor"; 4 | import { CODE_SAMPLES } from "../../data/code"; 5 | import { SUPPORT_LEVELS } from "../../data/supportLevels"; 6 | 7 | import styles from "./CodeSamples.module.scss"; 8 | 9 | export default function CodeSamples({ editorSettings }) { 10 | const [supportedFilter, setSupportedFilter] = useState("ALL"); 11 | const [nameFilter, setNameFilter] = useState(""); 12 | 13 | let filteredSamples = [...CODE_SAMPLES]; 14 | 15 | if (supportedFilter && supportedFilter !== "ALL") { 16 | filteredSamples = filteredSamples.filter( 17 | (sample) => sample.supported === supportedFilter 18 | ); 19 | } 20 | 21 | if (nameFilter) { 22 | filteredSamples = filteredSamples.filter((sample) => 23 | sample.language.toLowerCase().includes(nameFilter.toLowerCase()) 24 | ); 25 | } 26 | 27 | return ( 28 |
29 |
30 | 41 | 48 |
49 | {filteredSamples.map(({ label, code, language, notes, supported }, i) => { 50 | return ( 51 |
56 |

{label}

57 | {notes &&

{notes}

} 58 | 64 |
65 | ); 66 | })} 67 |
68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /components/CodeSamples/CodeSamples.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | display: grid; 3 | grid-template-columns: repeat(auto-fill, minmax(450px, 1fr)); 4 | gap: 1rem; 5 | } 6 | 7 | .filters { 8 | display: flex; 9 | gap: 2rem; 10 | justify-content: space-between; 11 | padding: 1rem; 12 | background: #222; 13 | border-radius: 0.5rem; 14 | grid-column: 1 / -1; 15 | } 16 | 17 | .sample { 18 | &, 19 | a { 20 | color: white; 21 | } 22 | h2 { 23 | margin: 0 0 0.5rem 0; 24 | } 25 | } 26 | 27 | .notes { 28 | font-size: 0.9rem; 29 | opacity: 0.66; 30 | } 31 | 32 | .codeEditor { 33 | border: solid 2px #555; 34 | } 35 | -------------------------------------------------------------------------------- /components/CodeSamples/index.js: -------------------------------------------------------------------------------- 1 | import CodeSamples from "./CodeSamples"; 2 | 3 | export default CodeSamples; 4 | -------------------------------------------------------------------------------- /components/EditorSettings/EditorSettings.js: -------------------------------------------------------------------------------- 1 | import { EDITOR_SETTINGS } from "../../data/editorSettings"; 2 | import styles from "./EditorSettings.module.scss"; 3 | 4 | export default function EditorSettings({ editorSettings, setEditorSettings }) { 5 | function changeEditorSetting(newSettings) { 6 | return setEditorSettings((editorSettings) => { 7 | return { 8 | ...editorSettings, 9 | ...newSettings, 10 | }; 11 | }); 12 | } 13 | 14 | return ( 15 |
16 | {Object.entries(EDITOR_SETTINGS).map(([key, value]) => { 17 | const { label, options, supported, notes, implemented } = value; 18 | return ( 19 |
25 | 26 | 48 | 49 | {notes &&
{notes}
} 50 |
51 | ); 52 | })} 53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /components/EditorSettings/EditorSettings.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | display: grid; 3 | grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); 4 | gap: 1rem; 5 | > div { 6 | flex: 1; 7 | } 8 | label { 9 | display: block; 10 | } 11 | select { 12 | width: 100%; 13 | } 14 | } 15 | 16 | .option { 17 | &[data-implemented="false"] { 18 | opacity: 0.5; 19 | &:hover { 20 | opacity: 0.8; 21 | } 22 | } 23 | } 24 | 25 | .notes { 26 | font-size: 0.7rem; 27 | margin: 0.5rem 0; 28 | opacity: 0.75; 29 | } 30 | -------------------------------------------------------------------------------- /components/EditorSettings/index.js: -------------------------------------------------------------------------------- 1 | import EditorSettings from "./EditorSettings"; 2 | export default EditorSettings; 3 | -------------------------------------------------------------------------------- /data/code.js: -------------------------------------------------------------------------------- 1 | import { LANGUAGES } from "./languages"; 2 | import { SUPPORT_LEVELS } from "./supportLevels"; 3 | 4 | /* Languages! https://codemirror.net/6/examples/lang-package/ */ 5 | export const CODE_SAMPLES = [ 6 | { 7 | language: LANGUAGES.HTML, 8 | label: "HTML", 9 | supported: SUPPORT_LEVELS.SUPPORTED, 10 | notes: null, 11 | code: ` 12 | 13 | 14 | 15 | 16 | A Great Demo on CodePen 17 | 18 | 19 | 20 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent bibendum, lorem vel tincidunt imperdiet, nibh elit laoreet felis, a bibendum nisl tortor non orci.

21 | 22 |

Vestibulum nunc massa, gravida quis porta nec, feugiat id metus. Nunc ac arcu dolor, quis vestibulum leo. Cras viverra mollis ipsum, non rhoncus lectus aliquam et. Morbi faucibus purus sit amet lacus aliquet elementum. Donec sit amet posuere enim.

23 | 24 | 25 | `, 26 | }, 27 | { 28 | language: LANGUAGES.CSS, 29 | label: "CSS", 30 | supported: SUPPORT_LEVELS.SUPPORTED, 31 | notes: null, 32 | code: `body { 33 | background: red; 34 | margin: 0; 35 | }`, 36 | }, 37 | { 38 | language: LANGUAGES.JAVASCRIPT, 39 | label: "JavaScript", 40 | supported: SUPPORT_LEVELS.SUPPORTED, 41 | notes: null, 42 | code: `import gsap from 'gsap'; 43 | 44 | let foo = "bar"; 45 | 46 | const data = { 47 | age: 12 48 | }; 49 | 50 | function hello(){ 51 | console.log(data.age); 52 | } 53 | `, 54 | }, 55 | { 56 | language: LANGUAGES.JSX, 57 | label: "JSX", 58 | supported: SUPPORT_LEVELS.SUPPORTED, 59 | notes: null, 60 | code: `import React from 'react'; 61 | 62 | function App() { 63 | return
text
; 64 | } 65 | 66 | export default App; 67 | `, 68 | }, 69 | { 70 | language: LANGUAGES.MARKDOWN, 71 | label: "Markdown", 72 | supported: SUPPORT_LEVELS.SUPPORTED, 73 | notes: null, 74 | code: `# This is markdown 75 | 76 | Just some *copy* here with a [link](https://codepen.io) in it. 77 | 78 | > blockquote 79 | 80 | Ordered List 81 | 1. First item 82 | 2. Second item 83 | 3. Third item 84 | 85 | Unordered List 86 | - First item 87 | - Second item 88 | - Third item 89 | `, 90 | }, 91 | { 92 | language: LANGUAGES.HAML, 93 | label: Haml, 94 | supported: SUPPORT_LEVELS.NOT_SUPPORTED, 95 | notes: null, 96 | code: `- (1..16).each do |i| 97 | %div #{i} 98 | 99 | %blockquote Hello, World!`, 100 | }, 101 | { 102 | language: LANGUAGES.PUG, 103 | label: Pug, 104 | supported: SUPPORT_LEVELS.NOT_SUPPORTED, 105 | code: `- 106 | var list = ["Uno", "Dos", "Tres", 107 | "Cuatro", "Cinco", "Seis"] 108 | each item in list 109 | li= item 110 | 111 | p 112 | | The pipe always goes at the beginning of its own line, 113 | | not counting indentation.`, 114 | }, 115 | { 116 | language: LANGUAGES.SLIM, 117 | label: Slim, 118 | supported: SUPPORT_LEVELS.NOT_SUPPORTED, 119 | notes: null, 120 | code: `doctype html 121 | html 122 | head 123 | title Slim Examples 124 | meta name="keywords" content="template language" 125 | meta name="author" content=author 126 | javascript: 127 | alert('Slim supports embedded javascript!') 128 | 129 | body 130 | h1 Markup examples 131 | 132 | #content 133 | p This example shows you what a basic Slim file looks like. 134 | 135 | == yield 136 | 137 | - unless items.empty? 138 | table 139 | - items.each do |item| 140 | tr 141 | td.name = item.name 142 | td.price = item.price 143 | - else 144 | p 145 | | No items found. Please add some inventory. 146 | Thank you! 147 | 148 | div id="footer" 149 | = render 'footer' 150 | | Copyright © #{year} #{author}`, 151 | }, 152 | { 153 | language: LANGUAGES.SCSS, 154 | label: SCSS, 155 | supported: SUPPORT_LEVELS.SUPPORTED, 156 | notes: null, 157 | code: `@mixin cool { 158 | padding: 1rem; 159 | } 160 | 161 | .options { 162 | display: grid; 163 | grid-template-columns: repeat(5, 1fr); 164 | gap: 1rem; 165 | margin: 0 0 1rem 0; 166 | > div { 167 | flex: 1; 168 | } 169 | label { 170 | display: block; 171 | } 172 | select { 173 | width: 100%; 174 | } 175 | } 176 | `, 177 | }, 178 | { 179 | language: LANGUAGES.SASS, 180 | label: Sass, 181 | supported: SUPPORT_LEVELS.SUPPORTED, 182 | code: `// SASS SYNTAX 183 | $font-stack: Helvetica, sans-serif 184 | $primary-color: #333 185 | 186 | body 187 | font: 100% $font-stack 188 | color: $primary-color`, 189 | }, 190 | { 191 | language: LANGUAGES.SCSS, 192 | label: Less, 193 | supported: SUPPORT_LEVELS.SUPPORTED, 194 | code: `// Variables 195 | @link-color: #428bca; // sea blue 196 | @link-color-hover: darken(@link-color, 10%); 197 | 198 | // Usage 199 | a, 200 | .link { 201 | color: @link-color; 202 | } 203 | a:hover { 204 | color: @link-color-hover; 205 | } 206 | .widget { 207 | color: #fff; 208 | background: @link-color; 209 | }`, 210 | }, 211 | { 212 | language: LANGUAGES.STYLUS, 213 | label: Stylus, 214 | supported: SUPPORT_LEVELS.SUPPORTED, 215 | notes: "Legacy mode", 216 | code: `border-radius() 217 | -webkit-border-radius: arguments 218 | -moz-border-radius: arguments 219 | border-radius: arguments 220 | 221 | body 222 | font: 12px Helvetica, Arial, sans-serif 223 | 224 | a.button 225 | border-radius(5px)`, 226 | }, 227 | { 228 | language: LANGUAGES.COFFEESCRIPT, 229 | label: CoffeeScript, 230 | supported: SUPPORT_LEVELS.SUPPORTED, 231 | notes: "Legacy mode", 232 | code: `# Assignment: 233 | number = 42 234 | opposite = true 235 | 236 | # Conditions: 237 | number = -42 if opposite 238 | 239 | # Functions: 240 | square = (x) -> x * x 241 | 242 | # Arrays: 243 | list = [1, 2, 3, 4, 5] 244 | 245 | # Objects: 246 | math = 247 | root: Math.sqrt 248 | square: square 249 | cube: (x) -> x * square x 250 | 251 | # Splats: 252 | race = (winner, runners...) -> 253 | print winner, runners 254 | 255 | # Existence: 256 | alert "I knew it!" if elvis? 257 | 258 | # Array comprehensions: 259 | cubes = (math.cube num for num in list)`, 260 | }, 261 | { 262 | language: LANGUAGES.TYPESCRIPT, 263 | label: TypeScript, 264 | supported: SUPPORT_LEVELS.SUPPORTED, 265 | notes: null, 266 | code: `type Cat = { meows: true }; 267 | type Dog = { barks: true }; 268 | type Cheetah = { meows: true; fast: true }; 269 | type Wolf = { barks: true; howls: true }; 270 | 271 | // We can create a conditional type which lets extract 272 | // types which only conform to something which barks. 273 | 274 | type ExtractDogish = A extends { barks: true } ? A : never; 275 | 276 | // Then we can create types which ExtractDogish wraps: 277 | 278 | // A cat doesn't bark, so it will return never 279 | type NeverCat = ExtractDogish; 280 | // A wolf will bark, so it returns the wolf shape 281 | type Wolfish = ExtractDogish; 282 | `, 283 | }, 284 | { 285 | language: LANGUAGES.LIVESCRIPT, 286 | label: LiveScript, 287 | supported: SUPPORT_LEVELS.SUPPORTED, 288 | notes: "Legacy mode", 289 | code: `# Easy listing of implicit objects 290 | table1 = 291 | * id: 1 292 | name: 'george' 293 | * id: 2 294 | name: 'mike' 295 | * id: 3 296 | name: 'donald' 297 | 298 | table2 = 299 | * id: 2 300 | age: 21 301 | * id: 1 302 | age: 20 303 | * id: 3 304 | age: 26`, 305 | }, 306 | { 307 | language: LANGUAGES.NUNJUCKS, 308 | label: Nunjucks, 309 | supported: SUPPORT_LEVELS.NOT_SUPPORTED, 310 | notes: null, 311 | code: `{% extends "base.html" %} 312 | 313 | {% block header %} 314 |

{{ title }}

315 | {% endblock %} 316 | 317 | {% block content %} 318 |
    319 | {% for name, item in items %} 320 |
  • {{ name }}: {{ item }}
  • 321 | {% endfor %} 322 |
323 | {% endblock %}`, 324 | }, 325 | ]; 326 | -------------------------------------------------------------------------------- /data/editorSettings.js: -------------------------------------------------------------------------------- 1 | import { THEMES } from "../components/CodeMirror6Instance/extensions/themes"; 2 | import { SUPPORT_LEVELS } from "./supportLevels"; 3 | 4 | export const INDENT_VALUES = { 5 | TABS: "Tabs", 6 | SPACES: "Spaces", 7 | }; 8 | 9 | export const EDITOR_SETTINGS = { 10 | theme: { 11 | label: "Syntax Highlighting", 12 | default: THEMES.TWILIGHT, 13 | options: Object.values(THEMES), // [THEMES.ONE_DARK, THEMES.TWILIGHT], //Object.values(THEMES), 14 | // "Solarized Dark", 15 | // "Tomorrow Night", 16 | // "Oceanic Dark", 17 | // "Panda", 18 | // "DuoTone Dark", 19 | // "High Contrast Dark", 20 | // "Classic", 21 | // "Solarized Light", 22 | // "XQ Light", 23 | // "Oceanic Light", 24 | // "MDN Like", 25 | // "DuoTone Light", 26 | // "High Contrast Light", 27 | 28 | supported: SUPPORT_LEVELS.SUPPORTED, 29 | implemented: true, 30 | notes: "We have lots of themes to port over; may want to drop ", 31 | }, 32 | 33 | fontSize: { 34 | label: "Font Size", 35 | default: 14, 36 | options: [10, 12, 14, 16, 18, 20, 22, 24], 37 | supported: SUPPORT_LEVELS.SUPPORTED, 38 | implemented: true, 39 | notes: ( 40 | <> 41 | 42 | Implemented via Theme 43 | 44 | 45 | ), 46 | }, 47 | 48 | lineHeight: { 49 | label: "Line Height", 50 | default: 1.4, 51 | options: [1, 1.2, 1.4, 1.6, 1.8, 2], 52 | supported: SUPPORT_LEVELS.SUPPORTED, 53 | implemented: true, 54 | notes: null, 55 | }, 56 | 57 | fontFamily: { 58 | label: "Font", 59 | default: "Source Code Pro", 60 | options: [ 61 | "Monaco", 62 | "Hack", 63 | "Inconsolata", 64 | "Source Code Pro", 65 | "Monoid", 66 | "Fantasque Sans Mono", 67 | "Input Mono", 68 | "DejaVu Sans Mono", 69 | "FireCode Medium", 70 | "Operator Mono", 71 | "Dank Mono", 72 | "Gintronic", 73 | "Courier Prime", 74 | "JetBrains Mono", 75 | "Recursive", 76 | "MonoLisa", 77 | "Codelia", 78 | "Comic Code", 79 | ], 80 | supported: SUPPORT_LEVELS.SUPPORTED, 81 | implemented: true, 82 | notes: null, 83 | }, 84 | 85 | indentWidth: { 86 | label: "Indent Width", 87 | default: 2, 88 | options: [2, 4, 6, 8], 89 | supported: SUPPORT_LEVELS.SUPPORTED, 90 | implemented: true, 91 | notes: ( 92 | <> 93 | NOTE: Does not convert previous indentations to a new width. There are 94 | some ways to do that through{" "} 95 | Prettier. Is there an 96 | official CodeMirror way of altering indent width of pre-authored code 97 | for spaces? 98 | 99 | ), 100 | }, 101 | 102 | indentUnit: { 103 | label: "Tabs or Spaces", 104 | default: INDENT_VALUES.SPACES, 105 | options: [INDENT_VALUES.SPACES, INDENT_VALUES.TABS], 106 | supported: SUPPORT_LEVELS.SUPPORTED, 107 | implemented: true, 108 | notes: ( 109 | <>NOTE: Does not convert previous indentations to the new indent unit. 110 | ), 111 | }, 112 | 113 | lineNumbers: { 114 | label: "Line Numbers", 115 | default: true, 116 | options: [true, false], 117 | supported: SUPPORT_LEVELS.SUPPORTED, 118 | implemented: true, 119 | notes: ( 120 | <> 121 | 122 | Officially supported 123 | 124 | 125 | ), 126 | }, 127 | 128 | lineWrapping: { 129 | label: "Line Wrapping", 130 | default: true, 131 | options: [true, false], 132 | supported: SUPPORT_LEVELS.SUPPORTED, 133 | implemented: true, 134 | notes: ( 135 | <> 136 | 137 | Officially supported 138 | 139 | 140 | ), 141 | }, 142 | 143 | codeFolding: { 144 | label: "Code Folding", 145 | default: true, 146 | options: [true, false], 147 | supported: SUPPORT_LEVELS.SUPPORTED, 148 | implemented: true, 149 | notes: ( 150 | <> 151 | 152 | Officially supported. 153 | 154 | 155 | ), 156 | }, 157 | 158 | matchBrackets: { 159 | label: "Match & Close Brackets / Tags", 160 | default: true, 161 | options: [true, false], 162 | supported: SUPPORT_LEVELS.SUPPORTED, 163 | implemented: true, 164 | notes: ( 165 | <> 166 |

167 | 168 | Officially supported. 169 | 170 |

171 |

172 | TODO: CodePen has traditionally paired this concept with{" "} 173 | 174 | Close Brackets 175 | 176 | , but they are different plugins in CodeMirror. Should we separate or 177 | combine? 178 |

179 | 180 |

181 | TODO: There's also the concept of{" "} 182 | 183 | `matchClosingTags` and `autoCloseTags` for HTML 184 | {" "} 185 | (possibly JSX as well?). Do we want this all linked to one option? In 186 | the interest of simplicity, they do all seem related. 187 |

188 | 189 | ), 190 | }, 191 | 192 | /* https://codemirror.net/6/docs/ref/#autocomplete */ 193 | autocomplete: { 194 | label: "Autocomplete", 195 | default: true, 196 | options: [true, false], 197 | supported: SUPPORT_LEVELS.PARTIAL_SUPPORT, 198 | implemented: true, 199 | notes: ( 200 | <> 201 | 202 | Officially supported 203 | 204 | . Need to figure out which languages it works on. Doesn't seem to 205 | do simple stuff in JavaScript like `document`, or `querySelector`. Also 206 | we need to pipe in authored JavaScript, so autocomplete works on 207 | user-authored code. 208 | 209 | ), 210 | }, 211 | 212 | emmet: { 213 | label: "Emmet", 214 | default: true, 215 | options: [true, false], 216 | supported: SUPPORT_LEVELS.SUPPORTED, 217 | implemented: true, 218 | notes: ( 219 | <> 220 | 221 | Implemented as a plugin 222 | {" "} 223 | by Sergey. 224 | 225 | ), 226 | }, 227 | }; 228 | 229 | export const EDITOR_SETTINGS_DEFAULTS = Object.entries(EDITOR_SETTINGS).reduce( 230 | (obj, [key, value]) => { 231 | obj[key] = value.default; 232 | return obj; 233 | }, 234 | {} 235 | ); 236 | -------------------------------------------------------------------------------- /data/languages.js: -------------------------------------------------------------------------------- 1 | export const LANGUAGES = { 2 | HTML: "html", 3 | CSS: "css", 4 | MARKDOWN: "markdown", 5 | JAVASCRIPT: "js", 6 | JSX: "jsx", 7 | SCSS: "scss", 8 | SASS: "sass", 9 | LESS: "less", 10 | STYLUS: "stylus", 11 | HAML: "haml", 12 | PUG: "pug", 13 | SLIM: "slim", 14 | NUNJUCKS: "nunjucks", 15 | COFFEESCRIPT: "coffeescript", 16 | TYPESCRIPT: "typescript", 17 | LIVESCRIPT: "livescript", 18 | }; 19 | -------------------------------------------------------------------------------- /data/supportLevels.js: -------------------------------------------------------------------------------- 1 | export const SUPPORT_LEVELS = { 2 | SUPPORTED: "SUPPORTED", 3 | NOT_SUPPORTED: "NOT_SUPPORTED", 4 | PARTIAL_SUPPORT: "PARTIAL_SUPPORT", 5 | }; 6 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[plugins]] 2 | package = "@netlify/plugin-nextjs" 3 | 4 | [build] 5 | publish = ".next" -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | import withBundleAnalyzer from "@next/bundle-analyzer"; 2 | 3 | const bundleAnalyzer = withBundleAnalyzer({ 4 | enabled: process.env.ANALYZE === "true", 5 | }); 6 | 7 | export default bundleAnalyzer({ 8 | reactStrictMode: true, 9 | target: "serverless", 10 | }); 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codemirror-6-needs", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "next dev -p 3009", 8 | "build": "next build", 9 | "export": "next export", 10 | "start": "next start", 11 | "lint": "next lint", 12 | "analyze": "ANALYZE=true yarn build" 13 | }, 14 | "dependencies": { 15 | "@codemirror/autocomplete": "^6.18.0", 16 | "@codemirror/commands": "^6.6.0", 17 | "@codemirror/language": "^6.10.2", 18 | "@codemirror/language-data": "6.5.1", 19 | "@codemirror/legacy-modes": "^6.0.0", 20 | "@codemirror/lint": "^6.0.0", 21 | "@codemirror/search": "^6.5.6", 22 | "@codemirror/state": "^6.4.1", 23 | "@codemirror/theme-one-dark": "^6.0.0", 24 | "@codemirror/view": "^6.32.0", 25 | "@emmetio/codemirror6-plugin": "^0.3.2", 26 | "@netlify/plugin-nextjs": "^4.9.1", 27 | "@next/bundle-analyzer": "^11.0.1", 28 | "@replit/codemirror-indentation-markers": "^0.20.0", 29 | "classnames": "^2.3.1", 30 | "cm6-theme-material-dark": "^0.2.0", 31 | "cm6-theme-solarized-dark": "^0.2.0", 32 | "cm6-theme-solarized-light": "^0.2.0", 33 | "codemirror": "^6.0.1", 34 | "next": "^12.2", 35 | "prettier": "^2.3.1", 36 | "react": "18.3.1", 37 | "react-dom": "18.3.1", 38 | "sass": "^1.70.0", 39 | "thememirror": "^2.0.0", 40 | "y-codemirror.next": "^0.3.0", 41 | "y-webrtc": "^10.2.3", 42 | "yjs": "^13.6.14" 43 | }, 44 | "devDependencies": { 45 | "eslint": "7.29.0", 46 | "eslint-config-next": "11.0.1" 47 | }, 48 | "packageManager": "yarn@4.1.1+sha256.f3cc0eda8e5560e529c7147565b30faa43b4e472d90e8634d7134a37c7f59781" 49 | } 50 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import "../styles/globals.css"; 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return ; 5 | } 6 | 7 | export default MyApp; 8 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 | 13 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /pages/console.js: -------------------------------------------------------------------------------- 1 | import { useState, Component } from "react"; 2 | import Head from "next/head"; 3 | import EditorSettings from "../components/EditorSettings"; 4 | import { EDITOR_SETTINGS_DEFAULTS } from "../data/editorSettings"; 5 | import { LANGUAGES } from "../data/languages"; 6 | import styles from "../styles/Home.module.scss"; 7 | 8 | import CodeMirror6Instance from "../components/CodeMirror6Instance"; 9 | 10 | import { EditorView, Decoration } from "@codemirror/view"; 11 | import { StateField, StateEffect, Range, RangeSet } from "@codemirror/state"; 12 | import { 13 | ensureSyntaxTree, 14 | foldable, 15 | foldAll, 16 | foldCode, 17 | foldEffect, 18 | foldService, 19 | language, 20 | syntaxTreeAvailable, 21 | } from "@codemirror/language"; 22 | 23 | const consoleLineClasses = { 24 | clear: "cm-console-clear", 25 | error: "cm-console-error", 26 | warn: "cm-console-warn", 27 | info: "cm-console-info", 28 | }; 29 | 30 | const consoleEntriesTheme = EditorView.baseTheme({ 31 | [`.${consoleLineClasses.clear}`]: { 32 | fontStyle: "italic", 33 | opacity: 0.5, 34 | }, 35 | [`.${consoleLineClasses.error}`]: { 36 | background: "hsl(358.462deg 100% 61.7647% / 35%)", 37 | }, 38 | [`.${consoleLineClasses.warn}`]: { 39 | background: "hsl(49.3194deg 100% 62.549% / 35%)", 40 | }, 41 | [`.${consoleLineClasses.info}`]: { 42 | background: "hsl(206.418deg 52.7559% 50.1961% / 50%)", 43 | }, 44 | }); 45 | 46 | const addConsoleEntryEffect = StateEffect.define(); 47 | const removeConsoleEntryEffect = StateEffect.define(); 48 | 49 | const consoleEntriesField = StateField.define({ 50 | create() { 51 | return Decoration.none; //RangeSet.empty; 52 | }, 53 | update(consoleEntries, tr) { 54 | consoleEntries = consoleEntries.map(tr.changes); 55 | for (let e of tr.effects) { 56 | if (e.is(addConsoleEntryEffect)) { 57 | let { log, from, to } = e.value; 58 | console.log("adding console decoration", log); 59 | 60 | const toAdd = [Decoration.mark({ logId: log.id }).range(from, to)]; 61 | const type = log.function; 62 | if (consoleLineClasses[type]) { 63 | const lines = getLines(tr.state, from, to); 64 | toAdd.push( 65 | ...lines.map((line) => 66 | Decoration.line({ 67 | class: consoleLineClasses[type], 68 | logId: log.id, 69 | }).range(line.from) 70 | ) 71 | ); 72 | } 73 | consoleEntries = consoleEntries.update({ 74 | add: toAdd, 75 | sort: true, 76 | }); 77 | } else if (e.is(removeConsoleEntryEffect)) { 78 | let { log } = e.value; 79 | consoleEntries = consoleEntries.update({ 80 | filter(from, to, value) { 81 | return value.spec.logId !== log.id; 82 | }, 83 | }); 84 | } 85 | } 86 | return consoleEntries; 87 | }, 88 | provide: (f) => EditorView.decorations.from(f), 89 | }); 90 | 91 | function getLines(state, from, to) { 92 | const lines = []; 93 | // Loop through lines 94 | for (let pos = from; pos <= to; ) { 95 | let line = state.doc.lineAt(pos); 96 | lines.push(line); 97 | // Next line 98 | pos = line.to + 1; 99 | } 100 | 101 | return lines; 102 | } 103 | 104 | function findConsoleEntryRange(view, log) { 105 | const range = { from: 0, to: 0 }; 106 | 107 | const field = view.state.field(consoleEntriesField); 108 | field.between(0, view.state.doc.length, function (from, to, value) { 109 | if (value.spec.logId === log.id) { 110 | range.from = Math.min(from, range.from); 111 | range.to = Math.max(to, range.to); 112 | // console.log({ from, to, range }); 113 | } 114 | }); 115 | 116 | return range; 117 | } 118 | 119 | async function foldLog(view, log) { 120 | // const range = findConsoleEntryRange(view, log); 121 | // const { from, to } = range; 122 | // setTimeout(() => { 123 | // const langField = view.state.field(language, false); 124 | // console.log("language", langField); 125 | // let f = foldable(view.state, from + 1, to - 1); 126 | // console.log("foldable", f); 127 | // // foldAll(view); 128 | // }, 100); 129 | // view.dispatch({}); 130 | // let available = syntaxTreeAvailable(view.state, to); 131 | // console.log("before", { available }); 132 | // let tree = await ensureSyntaxTree(view.state, to + 1, 1000); 133 | // available = syntaxTreeAvailable(view.state, to); 134 | // console.log("after", { available, tree }); 135 | // setTimeout(() => { 136 | // let available = syntaxTreeAvailable(view.state, to); 137 | // let tree = ensureSyntaxTree(view.state, to + 1, 1000); 138 | // console.log("setTimeout", { available, tree }); 139 | // // let f = foldable(view.state, from, to + 1); 140 | // // console.log("foldable", f); 141 | // // foldAll(view); 142 | // // view.dispatch({ effects: [foldEffect.of({ from, to: to + 1 })] }); 143 | // }); 144 | } 145 | 146 | async function addConsoleEntry(view, log) { 147 | const value = log.arguments.join(" "); 148 | let from = view.state.doc.length; 149 | let to = from; 150 | 151 | // const type = log.function; 152 | // if (type === "clear") { 153 | // from = 0; 154 | // } 155 | 156 | const insertLineBreak = from !== 0; 157 | 158 | const lineDecorationsData = { 159 | log, 160 | from: insertLineBreak ? from + 1 : 0, 161 | }; 162 | lineDecorationsData.to = lineDecorationsData.from + value.length; 163 | 164 | let effects = [addConsoleEntryEffect.of(lineDecorationsData)]; 165 | 166 | // Ensure that the necessary extensions are added. 167 | if (!view.state.field(consoleEntriesField, false)) { 168 | effects.push( 169 | StateEffect.appendConfig.of([consoleEntriesField, consoleEntriesTheme]) 170 | ); 171 | } 172 | 173 | view.dispatch({ 174 | changes: { 175 | from, 176 | to, 177 | insert: (insertLineBreak ? view.state.lineBreak : "") + value, 178 | }, 179 | effects, 180 | }); 181 | 182 | await foldLog(view, log); 183 | } 184 | 185 | function removeConsoleEntry(view, log) { 186 | const range = findConsoleEntryRange(view, log); 187 | 188 | // if (insertLineBreak) { 189 | // console.log("insertLineBreak!", range.from); 190 | // range.from = Math.max(0, from - 1); 191 | // } 192 | 193 | console.log("field before", view.state.field(consoleEntriesField)); 194 | 195 | console.log("removing", log.id, range); 196 | 197 | view.dispatch({ 198 | changes: { 199 | from: range.from, 200 | to: Math.min(view.state.doc.length, range.to + 1), 201 | }, 202 | effects: [removeConsoleEntryEffect.of({ log })], 203 | }); 204 | 205 | // const firstLine = view.state.doc.line(1); 206 | // if (firstLine.text === "") { 207 | // console.log(firstLine); 208 | 209 | // view.dispatch({ 210 | // changes: { 211 | // from: firstLine.from, 212 | // to: firstLine.to + 1, 213 | // }, 214 | // }); 215 | // } 216 | // console.log("field after", view.state.field(consoleEntriesField)); 217 | } 218 | 219 | class ConsoleLog extends Component { 220 | componentDidMount() { 221 | if (this.props.view) { 222 | this.remove = addConsoleEntry(this.props.view, this.props.log); 223 | } 224 | } 225 | 226 | componentWillUnmount() { 227 | removeConsoleEntry(this.props.view, this.props.log); 228 | // Remove lines. We should get some kind of Range back from the addConsoleLog function that can then be removed. 229 | } 230 | 231 | render() { 232 | return ( 233 |
  • 234 | {this.props.log.id}: {this.props.log.arguments.join(" ")} 235 |
  • 236 | ); 237 | } 238 | } 239 | 240 | export default function Console() { 241 | const [editorSettings, setEditorSettings] = useState({ 242 | ...EDITOR_SETTINGS_DEFAULTS, 243 | lineNumbers: false, 244 | }); 245 | 246 | const [view, setView] = useState(); 247 | const [lastLog, setLastLog] = useState(8); 248 | const [logs, setLogs] = useState(LOGS.slice(0, lastLog)); 249 | 250 | function addLogs() { 251 | setLogs((logs) => { 252 | return [...logs, LOGS[lastLog]]; 253 | }); 254 | setLastLog((lastLog + 1) % LOGS.length); 255 | } 256 | 257 | function removeLogs() { 258 | setLogs((logs) => { 259 | let logs2 = [...logs]; 260 | logs2.shift(); 261 | return logs2; //.slice(1 - logs.length); 262 | }); 263 | } 264 | 265 | function onInit(view) { 266 | setView(view); 267 | } 268 | 269 | return ( 270 |
    271 | 272 | CodeMirror 6 Console 273 | 274 | 275 |
    276 |
    277 |

    CodeMirror 6 Console

    278 |
    279 | 280 |
    281 | 286 |
    287 |
    288 | 289 | 290 | 296 | {view && 297 | logs.map((log) => ( 298 | 299 | ))} 300 |
    301 |
    302 |
    303 | ); 304 | } 305 | 306 | const LOGS = [ 307 | { 308 | function: "log", 309 | arguments: ["
    \n\txyz\n\t123\n
    "], 310 | id: "1655911665381322333141", 311 | }, 312 | { 313 | function: "log", 314 | arguments: ['"console.log"'], 315 | id: "16559116653812341", 316 | }, 317 | { 318 | function: "error", 319 | arguments: ['"console.error"'], 320 | id: "1655911665381", 321 | }, 322 | { 323 | function: "warn", 324 | arguments: ['"console.warn"'], 325 | id: "1655911665385", 326 | }, 327 | { 328 | function: "info", 329 | arguments: ['"console.info"'], 330 | id: "1655911665382", 331 | }, 332 | { 333 | function: "log", 334 | arguments: ['"regular console.log log\nwith multiple lines!"'], 335 | id: "1655911665381322341", 336 | }, 337 | 338 | { 339 | arguments: ["Console was cleared"], 340 | complexity: 1, 341 | function: "clear", 342 | id: "1655911665379", 343 | }, 344 | { 345 | function: "log", 346 | arguments: ["1"], 347 | id: "165591166538333", 348 | }, 349 | { 350 | function: "log", 351 | arguments: ["2"], 352 | id: "165591165653833334", 353 | }, 354 | { 355 | function: "log", 356 | arguments: ["3"], 357 | id: "1655911665383537223", 358 | }, 359 | { 360 | function: "log", 361 | arguments: ["4"], 362 | id: "165591163653833673", 363 | }, 364 | { 365 | function: "log", 366 | arguments: ["5"], 367 | id: "165591166538353343", 368 | }, 369 | { 370 | function: "log", 371 | arguments: ["6"], 372 | id: "1655911665383253634399", 373 | }, 374 | { 375 | function: "log", 376 | arguments: ["7"], 377 | id: "1655911665383363311341", 378 | }, 379 | { 380 | function: "log", 381 | arguments: ["8"], 382 | id: "165591166538303634463234", 383 | }, 384 | { 385 | function: "log", 386 | arguments: ["9"], 387 | id: "1655911665383392634323", 388 | }, 389 | { 390 | function: "debug", 391 | arguments: ['"debug"'], 392 | id: "1655911665380", 393 | }, 394 | { 395 | function: "log", 396 | arguments: ['"multiple\nlines\nin\none\nlog"'], 397 | id: "1655911665383", 398 | }, 399 | { 400 | function: "table", 401 | arguments: ['"table"'], 402 | id: "1655911665384", 403 | }, 404 | { 405 | function: "info", 406 | arguments: ['"[WDS] Hot Module Replacement enabled."'], 407 | id: "1655911665392", 408 | }, 409 | { 410 | function: "info", 411 | arguments: ['"[WDS] Live Reloading enabled."'], 412 | id: "1655911665393", 413 | }, 414 | ]; 415 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import Head from "next/head"; 3 | import CodeSamples from "../components/CodeSamples"; 4 | import EditorSettings from "../components/EditorSettings"; 5 | import { EDITOR_SETTINGS_DEFAULTS } from "../data/editorSettings"; 6 | import styles from "../styles/Home.module.scss"; 7 | 8 | export default function Home() { 9 | // TODO: Get controls working via editorSettings object. 10 | const [editorSettings, setEditorSettings] = useState( 11 | EDITOR_SETTINGS_DEFAULTS 12 | ); 13 | 14 | console.log(editorSettings); 15 | 16 | return ( 17 |
    18 | 19 | CodeMirror 6 Needs 20 | 21 | 22 |
    23 |
    24 |

    CodeMirror 6 Needs on CodePen

    25 |

    26 | This page shows live{" "} 27 | CodeMirror 6 editors in all 28 | the modes that we need (so far) on{" "} 29 | CodePen. It also has dropdowns for 30 | settings that we need to support.{" "} 31 |

    32 | Synced State ViewPlugin Demo 33 |
    34 | 35 |
    36 |

    Settings

    37 | 38 | 42 |
    43 | 44 |
    45 |

    Samples

    46 | 47 | 48 |
    49 |
    50 |
    51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /pages/shared.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from "react"; 2 | import Head from "next/head"; 3 | import EditorSettings from "../components/EditorSettings"; 4 | import CodeEditor from "../components/CodeEditor"; 5 | import { EDITOR_SETTINGS_DEFAULTS } from "../data/editorSettings"; 6 | import { LANGUAGES } from "../data/languages"; 7 | import styles from "../styles/Home.module.scss"; 8 | 9 | import { AnnotationType, EditorState, StateEffect } from "@codemirror/state"; 10 | import { Annotation } from "@codemirror/state"; 11 | import { useExtensions } from "../components/CodeMirror6Instance/extensions/useExtensions"; 12 | import CodeMirror6Instance from "../components/CodeMirror6Instance"; 13 | 14 | const syncAnnotation = new AnnotationType(Boolean); 15 | 16 | class SyncedState { 17 | constructor() { 18 | this.addView = this.addView.bind(this); 19 | this.syncDispatch = this.syncDispatch.bind(this); 20 | } 21 | 22 | views = []; 23 | 24 | addView(view) { 25 | this.views.push(view); 26 | 27 | // Get them all on the same state. 28 | if (this.views.length > 1) { 29 | view.setState(this.views[0].state); 30 | } 31 | 32 | // Have to override _dispatch as that's what the config option goes to. 33 | view._dispatch = this.syncDispatch({ 34 | views: this.views, 35 | view, 36 | }); 37 | } 38 | 39 | syncDispatch({ views, view }) { 40 | return (tr) => { 41 | view.update([tr]); 42 | // If not an empty change and not a sync change, then apply the change to all other views. 43 | if (!tr.annotation(syncAnnotation)) { 44 | console.log("sync dispatch", views.indexOf(view), tr); 45 | 46 | // Mark this as a sync transaction. 47 | const annotations = syncAnnotation.of(true); 48 | let transaction; 49 | 50 | if (!tr.changes.empty) { 51 | transaction = { 52 | // Mark this as a sync transaction. 53 | annotations, 54 | changes: tr.changes, 55 | }; 56 | } else if (tr.effects.length) { 57 | transaction = { 58 | annotations, 59 | effects: tr.effects, 60 | }; 61 | } 62 | 63 | if (transaction) { 64 | views.forEach((v) => { 65 | if (v === view) return; 66 | v.dispatch(transaction); 67 | }); 68 | } 69 | } 70 | }; 71 | } 72 | } 73 | 74 | export default function Shared() { 75 | const [editorSettings, setEditorSettings] = useState( 76 | EDITOR_SETTINGS_DEFAULTS 77 | ); 78 | 79 | const syncedStateRef = useRef(new SyncedState()); 80 | const syncedState = syncedStateRef.current; 81 | 82 | function onInit(editorView) { 83 | syncedState.addView(editorView); 84 | } 85 | 86 | const [fileValue, setFileValue] = useState( 87 | `\n \n Hello World\n \n` 88 | ); 89 | const [submittedValue, setSubmittedValue] = useState(fileValue); 90 | 91 | function onSubmit() { 92 | console.log("onSubmit", fileValue); 93 | setSubmittedValue(fileValue); 94 | } 95 | 96 | function onChange(update) { 97 | if (update.docChanged) { 98 | console.log("onChange", update); 99 | let value = update.state.doc.toString(); 100 | setFileValue(value); 101 | } 102 | } 103 | 104 | return ( 105 |
    106 | 107 | CodeMirror 6 Shared State 108 | 109 | 110 |
    111 |
    112 |

    CodeMirror 6 Shared State

    113 |
    114 | 115 |
    116 |
    117 |

    setFileValue("hello")}>File Contents

    118 |