LCOV - code coverage report
Current view: top level - dashql-app/src/session/editor/themes - theme.ts Coverage Total Hit
Test: DashQL 915828b Lines: 100.0 % 84 84
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import { EditorView } from '@codemirror/view';
       2              : import { Extension } from '@codemirror/state';
       3              : import { HighlightStyle, TagStyle, syntaxHighlighting } from '@codemirror/language';
       4              : import { StyleSpec } from 'style-mod';
       5              : 
       6              : export interface CreateThemeOptions {
       7              :     /**
       8              :      * Settings to customize the look of the editor, like background, gutter, selection and others.
       9              :      */
      10              :     settings: Settings;
      11              :     /** Syntax highlighting styles. */
      12              :     styles: TagStyle[];
      13              : }
      14              : 
      15              : export interface Settings {
      16              :     /** Editor background color. */
      17              :     background?: string;
      18              :     /** Editor background image. */
      19              :     backgroundImage?: string;
      20              :     /** Default text color. */
      21              :     foreground?: string;
      22              :     /** Caret color. */
      23              :     caret?: string;
      24              :     /** Selection background. */
      25              :     selection?: string;
      26              :     /** Selection match background. */
      27              :     selectionMatch?: string;
      28              :     /** Background of highlighted lines. */
      29              :     lineHighlight?: string;
      30              :     /** Gutter background. */
      31              :     gutterBackground?: string;
      32              :     /** Text color inside gutter. */
      33              :     gutterForeground?: string;
      34              :     /** Text active color inside gutter. */
      35              :     gutterActiveForeground?: string;
      36              :     /** Gutter right border color. */
      37              :     gutterBorder?: string;
      38              :     /** set editor font */
      39              :     fontFamily?: string;
      40              : }
      41              : 
      42              : /// Create a theme of the editors.
      43              : /// The colors are CSS colors that may refer to the tokens of the theme of the app. CodeMirror's own base theme
      44              : /// picks its light variant, the theme covers the parts it colors: panels, tooltips, matches and brackets.
      45            1 : export const createTheme = ({ settings = {}, styles = [] }: CreateThemeOptions): Extension => {
      46            1 :     const themeOptions: Record<string, StyleSpec> = {
      47            1 :         '.cm-gutters': {},
      48            1 :     };
      49            1 :     const baseStyle: StyleSpec = {};
      50            1 :     if (settings.background) {
      51            1 :         baseStyle.backgroundColor = settings.background;
      52              :     }
      53            1 :     if (settings.backgroundImage) {
      54            1 :         baseStyle.backgroundImage = settings.backgroundImage;
      55              :     }
      56            1 :     if (settings.foreground) {
      57            1 :         baseStyle.color = settings.foreground;
      58              :     }
      59            1 :     if (settings.background || settings.foreground) {
      60            1 :         themeOptions['&'] = baseStyle;
      61              :     }
      62              : 
      63            1 :     if (settings.fontFamily) {
      64            1 :         themeOptions['&.cm-editor .cm-scroller'] = {
      65            1 :             fontFamily: settings.fontFamily,
      66            1 :         };
      67              :     }
      68            1 :     if (settings.gutterBackground) {
      69            1 :         themeOptions['.cm-gutters'].backgroundColor = settings.gutterBackground;
      70              :     }
      71            1 :     if (settings.gutterForeground) {
      72            1 :         themeOptions['.cm-gutters'].color = settings.gutterForeground;
      73              :     }
      74            1 :     if (settings.gutterBorder) {
      75            1 :         themeOptions['.cm-gutters'].borderRightColor = settings.gutterBorder;
      76              :     }
      77              : 
      78            1 :     if (settings.caret) {
      79            1 :         themeOptions['.cm-content'] = {
      80            1 :             caretColor: settings.caret,
      81            1 :         };
      82            1 :         themeOptions['.cm-cursor, .cm-dropCursor'] = {
      83            1 :             borderLeftColor: settings.caret,
      84            1 :         };
      85            1 :     }
      86            1 :     const activeLineGutterStyle: StyleSpec = {};
      87            1 :     if (settings.gutterActiveForeground) {
      88            1 :         activeLineGutterStyle.color = settings.gutterActiveForeground;
      89              :     }
      90            1 :     if (settings.lineHighlight) {
      91            1 :         themeOptions['.cm-activeLine'] = {
      92            1 :             backgroundColor: settings.lineHighlight,
      93            1 :         };
      94            1 :         activeLineGutterStyle.backgroundColor = settings.lineHighlight;
      95            1 :     }
      96            1 :     themeOptions['.cm-activeLineGutter'] = activeLineGutterStyle;
      97              : 
      98            1 :     if (settings.selection) {
      99            1 :         themeOptions[
     100            1 :             '&.cm-focused .cm-selectionBackground, & .cm-selectionLayer .cm-selectionBackground'
     101            1 :         ] = {
     102            1 :             background: settings.selection + ' !important',
     103            1 :         };
     104              :     }
     105            1 :     if (settings.selectionMatch) {
     106            1 :         themeOptions['& .cm-selectionMatch'] = {
     107            1 :             backgroundColor: settings.selectionMatch,
     108            1 :         };
     109              :     }
     110            1 :     themeOptions['.cm-panels'] = {
     111            1 :         backgroundColor: 'var(--color_bg_subtle)',
     112            1 :         color: 'var(--color_fg)',
     113            1 :     };
     114            1 :     themeOptions['.cm-panels.cm-panels-top'] = { borderBottom: '1px solid var(--color_border)' };
     115            1 :     themeOptions['.cm-panels.cm-panels-bottom'] = { borderTop: '1px solid var(--color_border)' };
     116            1 :     themeOptions['.cm-tooltip'] = {
     117            1 :         backgroundColor: 'var(--color_bg_card)',
     118            1 :         color: 'var(--color_fg)',
     119            1 :         border: '1px solid var(--color_border)',
     120            1 :     };
     121            1 :     themeOptions['.cm-tooltip-autocomplete ul li[aria-selected]'] = {
     122            1 :         backgroundColor: 'var(--color_accent_muted)',
     123            1 :         color: 'var(--color_fg)',
     124            1 :     };
     125            1 :     themeOptions['.cm-searchMatch'] = {
     126            1 :         backgroundColor: 'var(--color_attention_muted)',
     127            1 :         outline: '1px solid var(--color_attention_fg)',
     128            1 :     };
     129            1 :     themeOptions['.cm-searchMatch.cm-searchMatch-selected'] = {
     130            1 :         backgroundColor: 'var(--color_accent_muted)',
     131            1 :     };
     132            1 :     themeOptions['.cm-matchingBracket, &.cm-focused .cm-matchingBracket'] = {
     133            1 :         backgroundColor: 'var(--color_bg_selected)',
     134            1 :     };
     135            1 :     themeOptions['.cm-nonmatchingBracket, &.cm-focused .cm-nonmatchingBracket'] = {
     136            1 :         backgroundColor: 'var(--color_danger_muted)',
     137            1 :     };
     138            1 :     const themeExtension = EditorView.theme(themeOptions);
     139              : 
     140            1 :     const highlightStyle = HighlightStyle.define(styles);
     141            1 :     const extension = [themeExtension, syntaxHighlighting(highlightStyle)];
     142              : 
     143            1 :     return extension;
     144            1 : };
     145              : 
     146              : export default createTheme;
        

Generated by: LCOV version 2.0-1