editor.ts

  1import { withOpacity } from "../theme/color"
  2import { ColorScheme, Layer, StyleSets } from "../theme/colorScheme"
  3import { background, border, borderColor, foreground, text } from "./components"
  4import hoverPopover from "./hoverPopover"
  5
  6import { buildSyntax } from "../theme/syntax"
  7import { interactive } from "./interactive"
  8import { toggleable } from "./toggle"
  9
 10export default function editor(colorScheme: ColorScheme) {
 11  const { isLight } = colorScheme
 12
 13  let layer = colorScheme.highest
 14
 15  const autocompleteItem = {
 16    cornerRadius: 6,
 17    padding: {
 18      bottom: 2,
 19      left: 6,
 20      right: 6,
 21      top: 2,
 22    },
 23  }
 24
 25  function diagnostic(layer: Layer, styleSet: StyleSets) {
 26    return {
 27      textScaleFactor: 0.857,
 28      header: {
 29        border: border(layer, {
 30          top: true,
 31        }),
 32      },
 33      message: {
 34        text: text(layer, "sans", styleSet, "default", { size: "sm" }),
 35        highlightText: text(layer, "sans", styleSet, "default", {
 36          size: "sm",
 37          weight: "bold",
 38        }),
 39      },
 40    }
 41  }
 42
 43  const syntax = buildSyntax(colorScheme)
 44
 45  return {
 46    textColor: syntax.primary.color,
 47    background: background(layer),
 48    activeLineBackground: withOpacity(background(layer, "on"), 0.75),
 49    highlightedLineBackground: background(layer, "on"),
 50    // Inline autocomplete suggestions, Co-pilot suggestions, etc.
 51    suggestion: syntax.predictive,
 52    codeActions: {
 53      indicator: toggleable(interactive({
 54        base: {
 55          color: foreground(layer, "variant"),
 56        }, state: {
 57          clicked: {
 58            color: foreground(layer, "base"),
 59          },
 60          hovered: {
 61            color: foreground(layer, "on"),
 62          },
 63        }
 64      }),
 65        {
 66          default: {
 67            color: foreground(layer, "on"),
 68          }
 69        }),
 70
 71      verticalScale: 0.55,
 72    },
 73    folds: {
 74      iconMarginScale: 2.5,
 75      foldedIcon: "icons/chevron_right_8.svg",
 76      foldableIcon: "icons/chevron_down_8.svg",
 77      indicator: toggleable(interactive({
 78        base: {
 79          color: foreground(layer, "variant"),
 80        }, state: {
 81          clicked: {
 82            color: foreground(layer, "base"),
 83          },
 84          hovered: {
 85            color: foreground(layer, "on"),
 86          },
 87        }
 88      }),
 89        {
 90          default: {
 91            color: foreground(layer, "on"),
 92          }
 93        }),
 94      ellipses: {
 95        textColor: colorScheme.ramps.neutral(0.71).hex(),
 96        cornerRadiusFactor: 0.15,
 97        background: {
 98          // Copied from hover_popover highlight
 99          default: { color: colorScheme.ramps.neutral(0.5).alpha(0.0).hex() },
100
101          hover: {
102            color: colorScheme.ramps.neutral(0.5).alpha(0.5).hex(),
103          },
104
105          clicked: {
106            color: colorScheme.ramps.neutral(0.5).alpha(0.7).hex(),
107          },
108        },
109      },
110      foldBackground: foreground(layer, "variant"),
111    },
112    diff: {
113      deleted: isLight
114        ? colorScheme.ramps.red(0.5).hex()
115        : colorScheme.ramps.red(0.4).hex(),
116      modified: isLight
117        ? colorScheme.ramps.yellow(0.5).hex()
118        : colorScheme.ramps.yellow(0.5).hex(),
119      inserted: isLight
120        ? colorScheme.ramps.green(0.4).hex()
121        : colorScheme.ramps.green(0.5).hex(),
122      removedWidthEm: 0.275,
123      widthEm: 0.15,
124      cornerRadius: 0.05,
125    },
126    /** Highlights matching occurrences of what is under the cursor
127     * as well as matched brackets
128     */
129    documentHighlightReadBackground: withOpacity(
130      foreground(layer, "accent"),
131      0.1
132    ),
133    documentHighlightWriteBackground: colorScheme.ramps
134      .neutral(0.5)
135      .alpha(0.4)
136      .hex(), // TODO: This was blend * 2
137    errorColor: background(layer, "negative"),
138    gutterBackground: background(layer),
139    gutterPaddingFactor: 3.5,
140    lineNumber: withOpacity(foreground(layer), 0.35),
141    lineNumberActive: foreground(layer),
142    renameFade: 0.6,
143    unnecessaryCodeFade: 0.5,
144    selection: colorScheme.players[0],
145    whitespace: colorScheme.ramps.neutral(0.5).hex(),
146    guestSelections: [
147      colorScheme.players[1],
148      colorScheme.players[2],
149      colorScheme.players[3],
150      colorScheme.players[4],
151      colorScheme.players[5],
152      colorScheme.players[6],
153      colorScheme.players[7],
154    ],
155    autocomplete: {
156      background: background(colorScheme.middle),
157      cornerRadius: 8,
158      padding: 4,
159      margin: {
160        left: -14,
161      },
162      border: border(colorScheme.middle),
163      shadow: colorScheme.popoverShadow,
164      matchHighlight: foreground(colorScheme.middle, "accent"),
165      item: autocompleteItem,
166      hoveredItem: {
167        ...autocompleteItem,
168        matchHighlight: foreground(
169          colorScheme.middle,
170          "accent",
171          "hovered"
172        ),
173        background: background(colorScheme.middle, "hovered"),
174      },
175      selectedItem: {
176        ...autocompleteItem,
177        matchHighlight: foreground(
178          colorScheme.middle,
179          "accent",
180          "active"
181        ),
182        background: background(colorScheme.middle, "active"),
183      },
184    },
185    diagnosticHeader: {
186      background: background(colorScheme.middle),
187      iconWidthFactor: 1.5,
188      textScaleFactor: 0.857,
189      border: border(colorScheme.middle, {
190        bottom: true,
191        top: true,
192      }),
193      code: {
194        ...text(colorScheme.middle, "mono", { size: "sm" }),
195        margin: {
196          left: 10,
197        },
198      },
199      source: {
200        text: text(colorScheme.middle, "sans", {
201          size: "sm",
202          weight: "bold",
203        }),
204      },
205      message: {
206        highlightText: text(colorScheme.middle, "sans", {
207          size: "sm",
208          weight: "bold",
209        }),
210        text: text(colorScheme.middle, "sans", { size: "sm" }),
211      },
212    },
213    diagnosticPathHeader: {
214      background: background(colorScheme.middle),
215      textScaleFactor: 0.857,
216      filename: text(colorScheme.middle, "mono", { size: "sm" }),
217      path: {
218        ...text(colorScheme.middle, "mono", { size: "sm" }),
219        margin: {
220          left: 12,
221        },
222      },
223    },
224    errorDiagnostic: diagnostic(colorScheme.middle, "negative"),
225    warningDiagnostic: diagnostic(colorScheme.middle, "warning"),
226    informationDiagnostic: diagnostic(colorScheme.middle, "accent"),
227    hintDiagnostic: diagnostic(colorScheme.middle, "warning"),
228    invalidErrorDiagnostic: diagnostic(colorScheme.middle, "base"),
229    invalidHintDiagnostic: diagnostic(colorScheme.middle, "base"),
230    invalidInformationDiagnostic: diagnostic(colorScheme.middle, "base"),
231    invalidWarningDiagnostic: diagnostic(colorScheme.middle, "base"),
232    hoverPopover: hoverPopover(colorScheme),
233    linkDefinition: {
234      color: syntax.linkUri.color,
235      underline: syntax.linkUri.underline,
236    },
237    jumpIcon: interactive({
238      base: {
239        color: foreground(layer, "on"),
240        iconWidth: 20,
241        buttonWidth: 20,
242        cornerRadius: 6,
243        padding: {
244          top: 6,
245          bottom: 6,
246          left: 6,
247          right: 6,
248        }
249      }, state: {
250        hovered: {
251          background: background(layer, "on", "hovered"),
252        }
253      }
254    }),
255
256    scrollbar: {
257      width: 12,
258      minHeightFactor: 1.0,
259      track: {
260        border: border(layer, "variant", { left: true }),
261      },
262      thumb: {
263        background: withOpacity(background(layer, "inverted"), 0.3),
264        border: {
265          width: 1,
266          color: borderColor(layer, "variant"),
267          top: false,
268          right: true,
269          left: true,
270          bottom: false,
271        },
272      },
273      git: {
274        deleted: isLight
275          ? withOpacity(colorScheme.ramps.red(0.5).hex(), 0.8)
276          : withOpacity(colorScheme.ramps.red(0.4).hex(), 0.8),
277        modified: isLight
278          ? withOpacity(colorScheme.ramps.yellow(0.5).hex(), 0.8)
279          : withOpacity(colorScheme.ramps.yellow(0.4).hex(), 0.8),
280        inserted: isLight
281          ? withOpacity(colorScheme.ramps.green(0.5).hex(), 0.8)
282          : withOpacity(colorScheme.ramps.green(0.4).hex(), 0.8),
283      },
284    },
285    compositionMark: {
286      underline: {
287        thickness: 1.0,
288        color: borderColor(layer),
289      },
290    },
291    syntax,
292  }
293}