SPB Git

Repository Public

monaco-setup.ts 3.6 KB 71 lines typescript

1/**2 * Monaco is bundled and self-hosted: no CDN loader, workers are emitted by the bundler3 * (`new Worker(new URL(...), import.meta.url)`), which the CSP allows via `worker-src 'self'`.4 */5import * as monaco from 'monaco-editor';6import { loader } from '@monaco-editor/react';7import { getBrand } from '@/stores/branding';89declare global {10  interface Window {11    MonacoEnvironment?: { getWorker(workerId: string, label: string): Worker };12  }13}1415let configured = false;16export function setupMonaco(): typeof monaco {17  if (configured) return monaco;18  configured = true;19  self.MonacoEnvironment = {20    getWorker(_id, label) {21      if (label === 'json')22        return new Worker(new URL('monaco-editor/language/json/json.worker.js', import.meta.url), { type: 'module' });23      if (label === 'css' || label === 'scss' || label === 'less')24        return new Worker(new URL('monaco-editor/language/css/css.worker.js', import.meta.url), { type: 'module' });25      if (label === 'html' || label === 'handlebars' || label === 'razor')26        return new Worker(new URL('monaco-editor/language/html/html.worker.js', import.meta.url), { type: 'module' });27      if (label === 'typescript' || label === 'javascript')28        return new Worker(new URL('monaco-editor/language/typescript/ts.worker.js', import.meta.url), { type: 'module' });29      return new Worker(new URL('monaco-editor/editor/editor.worker.js', import.meta.url), { type: 'module' });30    },31  };32  loader.config({ monaco });33  // Accent of the instance's agent (globals.css tokens). Monaco loads after sign-in: the branding is known.34  const accent = getBrand().agent === 'codex' ? { dark: '#2DD4BF', light: '#0B7268' } : { dark: '#8F8FFF', light: '#5151E8' };35  monaco.editor.defineTheme('cr-dark', {36    base: 'vs-dark',37    inherit: true,38    rules: [39      { token: 'comment', foreground: '7F8CA0' },40      { token: 'string', foreground: '42C995' },41      { token: 'number', foreground: 'E7B86B' },42      { token: 'keyword', foreground: '9DBCFF' },43    ],44    colors: {45      'editor.background': '#080B10', 'editor.foreground': '#F4F7FB', 'editorLineNumber.foreground': '#5D6A7D', 'editorLineNumber.activeForeground': '#A1ADBD',46      'editor.lineHighlightBackground': '#10151D', 'editor.selectionBackground': `${accent.dark}44`, 'editorCursor.foreground': accent.dark,47      'editorWidget.background': '#161D27', 'editorWidget.border': '#ffffff14', 'editorGutter.background': '#080B10',48      'diffEditor.insertedTextBackground': '#42C99522', 'diffEditor.removedTextBackground': '#ED777722',49      'scrollbarSlider.background': '#ffffff14', 'scrollbarSlider.hoverBackground': '#ffffff24',50    },51  });52  monaco.editor.defineTheme('cr-light', {53    base: 'vs',54    inherit: true,55    rules: [56      { token: 'comment', foreground: '6B7280' },57      { token: 'string', foreground: '116329' },58      { token: 'number', foreground: '8F5F00' },59      { token: 'keyword', foreground: accent.light.slice(1) },60    ],61    colors: {62      'editor.background': '#FFFFFF', 'editor.foreground': '#17181C', 'editorLineNumber.foreground': '#9AA0AD', 'editorLineNumber.activeForeground': '#555B69',63      'editor.lineHighlightBackground': '#F7F6F3', 'editor.selectionBackground': `${accent.light}30`, 'editorCursor.foreground': accent.light,64      'editorWidget.background': '#FFFFFF', 'editorWidget.border': '#00000017', 'editorGutter.background': '#FFFFFF',65      'diffEditor.insertedTextBackground': '#1FA97126', 'diffEditor.removedTextBackground': '#E5484D22',66      'scrollbarSlider.background': '#00000017', 'scrollbarSlider.hoverBackground': '#0000002e',67    },68  });69  return monaco;70}71
Files main
Menu

Theme