import { HighlightStyle, syntaxHighlighting } from '@codemirror/language' import type { Extension } from '@codemirror/state' import { EditorView } from '@codemirror/view' import { tags as t } from '@lezer/highlight' // GitHub "default" palettes, mirroring the read view's Shiki themes // (`github-light-default` / `github-dark-default`) so the spot editor matches // the preview it replaces. These are token *colors*; CodeMirror tokenizes with // Lezer rather than Shiki's TextMate grammars, so it's a palette match rather // than a byte-identical one — close enough that switching in/out of edit mode // doesn't recolor the file. interface GithubPalette { comment: string constant: string entity: string fg: string keyword: string number: string string: string tag: string type: string } const DARK: GithubPalette = { comment: '#8b949e', constant: '#79c0ff', entity: '#d2a8ff', fg: '#e6edf3', keyword: '#ff7b72', number: '#79c0ff', string: '#a5d6ff', tag: '#7ee787', type: '#ffa657' } const LIGHT: GithubPalette = { comment: '#6e7781', constant: '#0550ae', entity: '#8250df', fg: '#1f2328', keyword: '#cf222e', number: '#0550ae', string: '#0a3069', tag: '#116329', type: '#953800' } function makeHighlightStyle(p: GithubPalette): HighlightStyle { return HighlightStyle.define([ { color: p.keyword, tag: [t.keyword, t.modifier, t.controlKeyword, t.operatorKeyword, t.moduleKeyword] }, { color: p.string, tag: [t.string, t.special(t.string), t.attributeValue] }, { color: p.tag, tag: [t.regexp, t.escape] }, { color: p.comment, fontStyle: 'italic', tag: [t.comment, t.lineComment, t.blockComment, t.docComment] }, { color: p.entity, tag: [ t.function(t.variableName), t.function(t.propertyName), t.definition(t.function(t.variableName)), t.labelName ] }, { color: p.number, tag: [t.number, t.bool, t.atom] }, { color: p.constant, tag: [t.constant(t.variableName), t.standard(t.variableName)] }, { color: p.type, tag: [t.typeName, t.className, t.namespace] }, { color: p.tag, tag: [t.tagName] }, { color: p.constant, tag: [t.attributeName, t.propertyName] }, { color: p.fg, tag: [t.variableName] }, { color: p.fg, tag: [t.operator, t.punctuation, t.separator, t.bracket, t.angleBracket, t.derefOperator] }, { color: p.comment, tag: [t.meta, t.processingInstruction] }, { color: p.constant, tag: [t.link, t.url], textDecoration: 'underline' }, { color: p.constant, fontWeight: 'bold', tag: [t.heading] }, { fontWeight: 'bold', tag: [t.strong] }, { fontStyle: 'italic', tag: [t.emphasis] }, { color: p.keyword, tag: [t.deleted, t.invalid] } ]) } const DARK_STYLE = makeHighlightStyle(DARK) const LIGHT_STYLE = makeHighlightStyle(LIGHT) // Editor chrome (caret, selection, active line, gutters) on a transparent // background so the pane surface shows through, paired with the matching // GitHub highlight style. export function githubEditorTheme(dark: boolean): Extension { const p = dark ? DARK : LIGHT return [ EditorView.theme( { '&': { backgroundColor: 'transparent', color: p.fg }, '&.cm-focused .cm-selectionBackground, .cm-content ::selection, .cm-selectionBackground': { backgroundColor: dark ? 'rgba(56,139,253,0.25)' : 'rgba(84,174,255,0.28)' }, // Match the read view's gutter: dim, right-aligned line numbers. '.cm-content': { caretColor: p.fg }, '.cm-cursor, .cm-dropCursor': { borderLeftColor: p.fg }, '.cm-gutters': { backgroundColor: 'transparent', border: 'none' } }, { dark } ), syntaxHighlighting(dark ? DARK_STYLE : LIGHT_STYLE) ] }