Skip to content

Instantly share code, notes, and snippets.

@benatkin
Created May 22, 2024 02:51
Show Gist options
  • Select an option

  • Save benatkin/b71f7243090b36975f4103cfe7d28d24 to your computer and use it in GitHub Desktop.

Select an option

Save benatkin/b71f7243090b36975f4103cfe7d28d24 to your computer and use it in GitHub Desktop.

Revisions

  1. benatkin created this gist May 22, 2024.
    328 changes: 328 additions & 0 deletions chatgpt-codemirror-theme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,328 @@
    # Code Edit

    `notebook.json`

    ```json
    {
    "bundleFiles": [
    ["codemirror-bundle.md", "codemirror-bundle.js"]
    ]
    }
    ```

    This sets up CodeMirror. It supports getting and setting the text, and sends the `code-input` event when the text is edited. The filetype can be changed dynamically.

    `code-edit.js`

    ```js
    const {tags} = window.CodeMirrorModules['@lezer/highlight'];
    const {HighlightStyle} = window.CodeMirrorModules['@codemirror/language'];
    const {EditorView} = window.CodeMirrorModules['@codemirror/view'];

    const myTheme = EditorView.theme({
    "&": {
    color: "#FFFF33", // Bright yellow text
    backgroundColor: "#FF6600", // Bright orange background
    },
    ".cm-content": {
    caretColor: "#FFFF33", // Bright yellow caret
    },
    "&.cm-focused .cm-cursor": {
    borderLeftColor: "#FFFF33", // Bright yellow cursor
    },
    "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection": {
    backgroundColor: "#FF9933", // Light orange selection background
    },
    ".cm-activeLine": {
    backgroundColor: "#FF8533", // Slightly darker orange for active line
    },
    ".cm-gutters": {
    backgroundColor: "#FF6600", // Bright orange background for gutters
    color: "#FFFF66", // Bright yellow for line numbers
    border: "none",
    },
    ".cm-tooltip": {
    border: "1px solid #FFFF66",
    backgroundColor: "#FF6600",
    color: "#FFFF33",
    },
    ".cm-tooltip.cm-tooltip-autocomplete": {
    "& > ul > li[aria-selected]": {
    backgroundColor: "#FF8533",
    color: "#FFFF33",
    },
    },
    }, {dark: true});

    const myHighlightStyle = HighlightStyle.define([
    { tag: tags.keyword, color: "#FF4500" }, // Red-orange keywords
    { tag: tags.string, color: "#FFFF66" }, // Light yellow strings
    { tag: tags.comment, color: "#66FF66", fontStyle: "italic" }, // Green comments
    { tag: tags.function(tags.variableName), color: "#66CCFF" }, // Light blue functions
    { tag: tags.number, color: "#FF66CC" }, // Pink numbers
    { tag: tags.variableName, color: "#FFFF33" }, // Brighter yellow variable names
    { tag: tags.operator, color: "#FF66FF" }, // Magenta operators
    { tag: tags.meta, color: "#66FFFF" }, // Cyan meta
    { tag: tags.attributeName, color: "#FFD700" }, // Gold attribute names
    { tag: tags.className, color: "#FF6600" }, // Orange class names
    { tag: tags.typeName, color: "#66FFCC" }, // Mint green type names
    { tag: tags.tagName, color: "#FF4500" }, // Red-orange tag names
    { tag: tags.bracket, color: "#FFFF66" }, // Light yellow brackets
    { tag: tags.angleBracket, color: "#FFCC66" }, // Peach angle brackets
    { tag: tags.link, color: "#66CCFF" }, // Light blue links
    { tag: tags.heading, color: "#FFCC00", fontWeight: "bold" }, // Dark yellow headings
    { tag: tags.emphasis, color: "#FFFF33", fontStyle: "italic" }, // Brighter yellow italic
    { tag: tags.strong, color: "#FFFF33", fontWeight: "bold" }, // Brighter yellow bold
    { tag: tags.strikethrough, color: "#FFFF33", textDecoration: "line-through" }, // Brighter yellow strikethrough
    { tag: tags.attributeValue, color: "#FFD700" }, // Gold attribute values
    { tag: tags.definition(tags.typeName), color: "#FFD700" }, // Gold type definitions
    { tag: tags.definition(tags.className), color: "#FF6600" }, // Orange class definitions
    ]);

    console.log(myHighlightStyle)

    export class CodeEdit extends HTMLElement {
    constructor() {
    super()
    this.attachShadow({mode: 'open'})
    }

    connectedCallback() {
    this.shadowRoot.adoptedStyleSheets = [this.constructor.styleSheet]
    this.initEditor()
    }

    static css = `
    :host {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-grow: 1;
    background-color: #fff;
    height: 100%;
    }
    `

    static get styleSheet() {
    if (this._styleSheet === undefined) {
    this._styleSheet = new CSSStyleSheet()
    this._styleSheet.replaceSync(this.css)
    }
    return this._styleSheet
    }

    set value(value) {
    if (this.view) {
    this.view.dispatch({changes: {
    from: 0,
    to: this.view.state.doc.length,
    insert: value
    }})
    } else {
    this._value = value
    }
    }

    get value() {
    if (this.view) {
    return this.view.state.doc.toString()
    } else {
    return this._value ?? ''
    }
    }

    set fileType(value) {
    this._fileType = value
    if (this.view) {
    const langPlugins = this.langPlugins
    this.view.dispatch({
    effects:
    this.languageCompartment.reconfigure(langPlugins)
    })
    }
    }

    get fileType() {
    return this._fileType
    }

    get langPlugins() {
    const cmView = window.CodeMirrorModules['@codemirror/view']
    const cmState = window.CodeMirrorModules['@codemirror/state']
    const cmLanguage = window.CodeMirrorModules['@codemirror/language']
    const cmJavaScript = window.CodeMirrorModules['@codemirror/lang-javascript']
    const cmCss = window.CodeMirrorModules['@codemirror/lang-css']
    const cmHtml = window.CodeMirrorModules['@codemirror/lang-html']
    const cmJson = window.CodeMirrorModules['@codemirror/lang-json']
    const cmMarkdown = window.CodeMirrorModules['@codemirror/lang-markdown']
    const langPlugins = []
    if (['js', 'javascript'].includes(this.fileType)) {
    langPlugins.push(cmJavaScript.javascriptLanguage)
    } else if (this.fileType === 'css') {
    langPlugins.push(cmCss.cssLanguage)
    } else if (this.fileType === 'html') {
    langPlugins.push(cmHtml.htmlLanguage)
    } else if (this.fileType === 'json') {
    langPlugins.push(cmJson.jsonLanguage)
    } else if (this.fileType === 'md') {
    const codeLanguages = [
    cmLanguage.LanguageDescription.of({
    name: 'javascript',
    alias: ['js'],
    async load() {
    return new cmLanguage.LanguageSupport(cmJavaScript.javascriptLanguage)
    },
    }),
    cmLanguage.LanguageDescription.of({
    name: 'css',
    async load() {
    return new cmLanguage.LanguageSupport(cmCss.cssLanguage)
    },
    }),
    cmLanguage.LanguageDescription.of({
    name: 'json',
    async load() {
    return new cmLanguage.LanguageSupport(cmJson.jsonLanguage)
    },
    }),
    cmLanguage.LanguageDescription.of({
    name: 'html',
    async load() {
    const javascript = new cmLanguage.LanguageSupport(cmJavaScript.javascriptLanguage)
    const css = new cmLanguage.LanguageSupport(cmCss.cssLanguage)
    return new cmLanguage.LanguageSupport(cmHtml.htmlLanguage, [css, javascript])
    },
    }),
    ]
    const { language, support } = cmMarkdown.markdown({codeLanguages, addKeymap: false})
    const markdownSupport = new cmLanguage.LanguageSupport(
    language, [...support, cmState.Prec.high(cmView.keymap.of(cmMarkdown.markdownKeymap))]
    )
    langPlugins.push(
    markdownSupport
    )
    }
    return langPlugins
    }

    initEditor() {
    const cmView = window.CodeMirrorModules['@codemirror/view']
    const cmState = window.CodeMirrorModules['@codemirror/state']
    const cmLanguage = window.CodeMirrorModules['@codemirror/language']
    const cmCommands = window.CodeMirrorModules['@codemirror/commands']
    const cmAutocomplete = window.CodeMirrorModules['@codemirror/autocomplete']
    const cmSearch = window.CodeMirrorModules['@codemirror/search']
    const cmLint = window.CodeMirrorModules['@codemirror/lint']
    this.languageCompartment = new cmState.Compartment()
    const langPlugins = this.langPlugins
    const basicSetup = [
    cmView.lineNumbers(),
    cmView.highlightActiveLineGutter(),
    cmView.highlightSpecialChars(),
    cmCommands.history(),
    cmLanguage.foldGutter(),
    cmView.drawSelection(),
    cmView.dropCursor(),
    cmState.EditorState.allowMultipleSelections.of(true),
    cmLanguage.indentOnInput(),
    cmLanguage.bracketMatching(),
    cmAutocomplete.closeBrackets(),
    cmAutocomplete.autocompletion(),
    cmView.rectangularSelection(),
    cmView.crosshairCursor(),
    cmView.highlightActiveLine(),
    cmSearch.highlightSelectionMatches(),
    cmLanguage.syntaxHighlighting(myHighlightStyle),
    cmView.keymap.of([
    ...cmAutocomplete.closeBracketsKeymap,
    ...cmCommands.defaultKeymap,
    ...cmSearch.searchKeymap,
    ...cmCommands.historyKeymap,
    ...cmLanguage.foldKeymap,
    ...cmAutocomplete.completionKeymap,
    ...cmLint.lintKeymap,
    ]),
    ]
    const viewTheme = cmView.EditorView.theme({
    '&': {flexGrow: '1', height: '100%'},
    '.cm-scroller': {overflow: 'auto'}
    })
    this.view = new cmView.EditorView({
    doc: this._value ?? '',
    extensions: [
    ...basicSetup,
    this.languageCompartment.of(langPlugins),
    myTheme,
    cmView.EditorView.updateListener.of(e => {
    if (e.docChanged) {
    this.dispatchEvent(new CustomEvent(
    'code-input', {bubbles: true, composed: true}
    ))
    }
    }),
    ],
    root: this.shadowRoot,
    })
    this.shadowRoot.append(this.view.dom)
    }

    focus() {
    this.view.focus()
    }
    }
    ```
    `app-view.js`
    ```js
    export class AppView extends HTMLElement {
    constructor() {
    super()
    this.attachShadow({mode: 'open'})
    }

    connectedCallback() {
    const globalStyle = document.createElement('style')
    globalStyle.textContent = `
    body {
    margin: 0;
    padding: 0;
    }
    html {
    box-sizing: border-box;
    }
    *, *:before, *:after {
    box-sizing: inherit;
    }
    `
    document.head.append(globalStyle)

    const style = document.createElement('style')
    style.textContent = `
    `
    this.shadowRoot.append(style)

    const codeEdit = document.createElement('code-edit')
    codeEdit.fileType = 'js'
    codeEdit.value = `const x = 9`
    this.shadowRoot.append(codeEdit)
    }
    }
    ```
    `app.js`
    ```js
    import {CodeEdit} from '/code-edit.js'
    import {AppView} from '/app-view.js'

    customElements.define('code-edit', CodeEdit)
    customElements.define('app-view', AppView)

    async function setup() {
    const appView = document.createElement('app-view')
    document.body.append(appView)
    }

    await setup()
    ```