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