* feat(settings): add editor font size setting for chat input and code editor Adds an 'Editor font size' control in Settings > Appearance that sets an absolute px font size for the chat input textarea and the in-app CodeMirror editor. Mirrors the existing terminalFontSize lifecycle. - New store field editorFontSize (default 13, clamp 9-32, step 1) in useUIStore with narrow selectors at each consumer. - Persistence wired through appearanceAutoSave, desktop + runtime API types, and persistence.ts read/normalize. - Settings UI row (NumberInput) with reset to 13, VisibleSetting union entry, OpenChamberPage registration, and search index entry appearance.editor-font-size. - Applied as a post-zoom absolute override on the chat input textarea and on the CodeMirror theme's content rule, leaving gutter/line-number chrome at its existing hardcoded sizes (matches terminal scope). - All 10 locales translated (en, es, fr, ja, ko, pl, pt-BR, uk, zh-CN, zh-TW); no English placeholders in non-English dictionaries. Refs #1325 * fix(codemirror): use unitless lineHeight so it scales with editor font size The & rule in the CodeMirror theme set lineHeight to 1.5rem (~24px), which does not scale when editorFontSize is increased (e.g., 28-32px). This causes overlapping lines at larger font sizes. Change to unitless 1.5, which scales proportionally with whatever fontSize resolves to (dynamic prop or --text-code fallback). Matches browser best practice for proportional leading. Review comment: https://github.com/openchamber/openchamber/pull/2065 --------- Co-authored-by: bashrusakh <bashrusakh@users.noreply.github.com> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
577 lines
20 KiB
TypeScript
577 lines
20 KiB
TypeScript
import type { Extension } from '@codemirror/state';
|
||
|
||
import { EditorView } from '@codemirror/view';
|
||
import { HighlightStyle, defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
||
import { classHighlighter, tags as t } from '@lezer/highlight';
|
||
|
||
import type { Theme } from '@/types/theme';
|
||
|
||
export function createFlexokiCodeMirrorTheme(
|
||
theme: Theme,
|
||
// When syntax colors are provided elsewhere (e.g. the Shiki decoration
|
||
// extension), pass `{ syntaxColors: false }` to keep only the editor UI theme
|
||
// (gutters, selection, cursor) and avoid a competing token color source.
|
||
options?: { syntaxColors?: boolean; fontSize?: number },
|
||
): Extension {
|
||
const isDark = theme.metadata.variant === 'dark';
|
||
|
||
const monoFont = theme.config?.fonts?.mono || 'monospace';
|
||
const highlights = theme.colors.syntax.highlights || {};
|
||
const tokens = theme.colors.syntax.tokens || {};
|
||
|
||
const contentFontSize = options?.fontSize ? `${options.fontSize}px` : 'var(--text-code)';
|
||
|
||
const ui = EditorView.theme({
|
||
'&': {
|
||
backgroundColor: 'var(--background)',
|
||
color: theme.colors.syntax.base.foreground,
|
||
fontSize: contentFontSize,
|
||
lineHeight: '1.5',
|
||
position: 'relative' as const,
|
||
},
|
||
'.cm-scroller': {
|
||
fontFamily: monoFont,
|
||
backgroundColor: 'var(--background)',
|
||
},
|
||
|
||
/* StreamLanguage/legacy-modes tokens (class-based) */
|
||
'.cm-comment': {
|
||
color: theme.colors.syntax.base.comment,
|
||
},
|
||
'.cm-keyword': {
|
||
color: theme.colors.syntax.base.keyword,
|
||
},
|
||
'.cm-string': {
|
||
color: theme.colors.syntax.base.string,
|
||
},
|
||
'.cm-string-2': {
|
||
color: tokens.stringEscape || theme.colors.syntax.base.string,
|
||
},
|
||
'.cm-number': {
|
||
color: theme.colors.syntax.base.number,
|
||
},
|
||
'.cm-operator': {
|
||
color: theme.colors.syntax.base.operator,
|
||
},
|
||
'.cm-punctuation': {
|
||
color: tokens.punctuation || theme.colors.syntax.base.comment,
|
||
},
|
||
'.cm-atom': {
|
||
color: tokens.boolean || theme.colors.syntax.base.number,
|
||
},
|
||
'.cm-builtin': {
|
||
color: tokens.functionCall || theme.colors.syntax.base.function,
|
||
},
|
||
'.cm-def': {
|
||
color: tokens.variableGlobal || theme.colors.syntax.base.variable,
|
||
},
|
||
// Legacy shell flags (--foo, -bar)
|
||
'.cm-attribute': {
|
||
color: tokens.variableOther || tokens.variableProperty || theme.colors.syntax.base.operator,
|
||
},
|
||
'.cm-meta': {
|
||
color: theme.colors.syntax.base.comment,
|
||
},
|
||
'.cm-property': {
|
||
color: tokens.variableProperty || theme.colors.syntax.base.keyword,
|
||
},
|
||
'.cm-variable': {
|
||
color: theme.colors.syntax.base.variable,
|
||
},
|
||
'.cm-variable-2': {
|
||
color: tokens.variableOther || theme.colors.syntax.base.function,
|
||
},
|
||
'.cm-variable-3': {
|
||
color: tokens.variableGlobal || theme.colors.syntax.base.type,
|
||
},
|
||
'.cm-tag': {
|
||
color: tokens.tag || theme.colors.syntax.base.keyword,
|
||
},
|
||
'.cm-link': {
|
||
color: tokens.url || theme.colors.syntax.base.keyword,
|
||
textDecoration: 'underline',
|
||
},
|
||
|
||
'.cm-content': {
|
||
caretColor: theme.colors.interactive.cursor,
|
||
},
|
||
'.cm-cursor, .cm-dropCursor': {
|
||
borderLeftColor: theme.colors.interactive.cursor,
|
||
},
|
||
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection': {
|
||
backgroundColor: theme.colors.interactive.selection,
|
||
},
|
||
'.cm-gutters': {
|
||
backgroundColor: 'var(--background)',
|
||
color: highlights.lineNumber || theme.colors.syntax.base.comment,
|
||
borderRight: `1px solid ${theme.colors.interactive.border}`,
|
||
position: 'sticky',
|
||
paddingRight: '8px',
|
||
left: 0,
|
||
zIndex: 2,
|
||
boxShadow: `0 0 0 var(--background)`,
|
||
},
|
||
'.cm-gutter': {
|
||
backgroundColor: 'var(--background)',
|
||
},
|
||
'.cm-gutterElement': {
|
||
backgroundColor: 'var(--background)',
|
||
},
|
||
'.cm-lineNumbers': {
|
||
backgroundColor: 'var(--background)',
|
||
},
|
||
'.cm-lineNumbers .cm-gutterElement': {
|
||
paddingLeft: '8px',
|
||
paddingRight: '8px',
|
||
minWidth: '42px',
|
||
},
|
||
'.cm-activeLineGutter': {
|
||
color: highlights.lineNumberActive || theme.colors.syntax.base.foreground,
|
||
},
|
||
'.cm-activeLine': {
|
||
backgroundColor: theme.colors.surface.overlay,
|
||
},
|
||
/* ── Floating search: panels container ── */
|
||
'.cm-panels': {
|
||
backgroundColor: 'transparent',
|
||
color: theme.colors.surface.foreground,
|
||
position: 'absolute' as const,
|
||
top: '0',
|
||
right: '0',
|
||
left: 'auto',
|
||
width: 'auto',
|
||
zIndex: 10,
|
||
pointerEvents: 'none',
|
||
},
|
||
'.cm-panels-top': {
|
||
borderBottom: 'none',
|
||
position: 'absolute' as const,
|
||
top: '6px',
|
||
right: '14px',
|
||
left: 'auto',
|
||
width: 'auto',
|
||
},
|
||
'.cm-panels-bottom': {
|
||
position: 'static' as const,
|
||
width: '100%',
|
||
pointerEvents: 'auto',
|
||
},
|
||
'.cm-panels-bottom .cm-vim-panel': {
|
||
borderTop: `1px solid ${theme.colors.interactive.border}`,
|
||
backgroundColor: theme.colors.surface.elevated,
|
||
color: theme.colors.surface.mutedForeground,
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: '12px',
|
||
minHeight: '22px',
|
||
padding: '2px 8px',
|
||
},
|
||
|
||
/* ── Floating search panel ── */
|
||
'.cm-panel.cm-search': {
|
||
pointerEvents: 'auto',
|
||
padding: '4px',
|
||
backgroundColor: `color-mix(in srgb, ${theme.colors.surface.elevated} 85%, transparent)`,
|
||
border: `1px solid ${theme.colors.interactive.border}`,
|
||
borderRadius: '10px',
|
||
boxShadow: `0 2px 8px color-mix(in srgb, ${theme.colors.surface.background} 60%, transparent)`,
|
||
width: 'auto',
|
||
minWidth: '360px',
|
||
maxWidth: '480px',
|
||
},
|
||
|
||
/* ── Search inner layout: flexbox rows ── */
|
||
'.cm-search': {
|
||
display: 'flex',
|
||
flexWrap: 'wrap',
|
||
alignItems: 'center',
|
||
gap: '0',
|
||
width: '100%',
|
||
color: theme.colors.surface.foreground,
|
||
fontFamily: 'inherit',
|
||
fontSize: '13px',
|
||
lineHeight: '1',
|
||
},
|
||
|
||
/* ── Hide <br>; we force row break via replace input's flex-basis ── */
|
||
'.cm-search br': {
|
||
display: 'none',
|
||
},
|
||
|
||
/* ──────────────────────────────────────────────────
|
||
ROW 1: [Find input] [Aa] [ab] [.*] [match count] [↑] [↓] [≡] [×]
|
||
────────────────────────────────────────────────── */
|
||
|
||
/* ── Text input base: border-radius matches action buttons (4px) ── */
|
||
'.cm-search .cm-textfield': {
|
||
appearance: 'none',
|
||
height: '26px',
|
||
margin: 0,
|
||
padding: '0 8px',
|
||
borderRadius: '6px !important',
|
||
border: `1px solid ${theme.colors.interactive.border}`,
|
||
backgroundColor: theme.colors.surface.background,
|
||
color: theme.colors.surface.foreground,
|
||
fontFamily: 'inherit',
|
||
fontSize: '12px',
|
||
lineHeight: '1',
|
||
outline: 'none',
|
||
transition: 'border-color 150ms ease',
|
||
},
|
||
'.cm-search .cm-textfield::placeholder': {
|
||
color: theme.colors.surface.mutedForeground,
|
||
opacity: 1,
|
||
},
|
||
'.cm-search .cm-textfield:focus': {
|
||
borderColor: theme.colors.primary.base,
|
||
},
|
||
|
||
/* Find input: row 1, max-width matches replace input for alignment */
|
||
'.cm-search .cm-textfield[name="search"]': {
|
||
order: 1,
|
||
flex: '1 1 120px',
|
||
maxWidth: 'calc(100% - 142px)',
|
||
minWidth: '100px',
|
||
},
|
||
|
||
/* Replace input is styled below in the row-2 section */
|
||
|
||
/* ────────────────────────────────────────────────────
|
||
ICON-ONLY TOGGLE BUTTONS (checkbox labels → icon toggles)
|
||
Labels contain: [checkbox] "match case" / "regexp" / "by word"
|
||
We hide text, hide the checkbox, and show icon via label::after
|
||
──────────────────────────────────────────────────── */
|
||
'.cm-search label': {
|
||
order: 2,
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
width: '26px',
|
||
height: '26px',
|
||
minWidth: '26px',
|
||
flexShrink: '0',
|
||
margin: 0,
|
||
marginLeft: '1px',
|
||
padding: 0,
|
||
borderRadius: '4px',
|
||
border: `1px solid transparent`,
|
||
backgroundColor: 'transparent',
|
||
color: 'transparent', /* hide label text */
|
||
cursor: 'pointer',
|
||
userSelect: 'none' as const,
|
||
fontSize: '1px', /* near-zero to collapse text nodes */
|
||
lineHeight: '0',
|
||
overflow: 'hidden',
|
||
position: 'relative' as const,
|
||
transition: 'background-color 120ms ease, border-color 120ms ease',
|
||
},
|
||
'.cm-search label:hover': {
|
||
backgroundColor: theme.colors.interactive.hover,
|
||
},
|
||
'.cm-search label:has(input[type="checkbox"]:checked)': {
|
||
backgroundColor: theme.colors.interactive.hover,
|
||
borderColor: theme.colors.interactive.border,
|
||
},
|
||
|
||
/* Hide the actual checkbox input inside labels */
|
||
'.cm-search [type="checkbox"]': {
|
||
appearance: 'none',
|
||
WebkitAppearance: 'none',
|
||
width: '0',
|
||
height: '0',
|
||
margin: '0',
|
||
padding: '0',
|
||
border: 'none',
|
||
position: 'absolute' as const,
|
||
opacity: '0',
|
||
pointerEvents: 'none' as const,
|
||
},
|
||
|
||
/* Icon via ::after on each label - absolutely positioned so text-hiding doesn't affect it */
|
||
'.cm-search label::after': {
|
||
position: 'absolute' as const,
|
||
top: '50%',
|
||
left: '50%',
|
||
transform: 'translate(-50%, -50%)',
|
||
fontSize: '13px',
|
||
fontWeight: '600',
|
||
lineHeight: '1',
|
||
fontFamily: 'inherit',
|
||
color: theme.colors.surface.mutedForeground,
|
||
pointerEvents: 'none' as const,
|
||
},
|
||
'.cm-search label:hover::after': {
|
||
color: theme.colors.surface.foreground,
|
||
},
|
||
'.cm-search label:has(input[type="checkbox"]:checked)::after': {
|
||
color: theme.colors.surface.foreground,
|
||
},
|
||
|
||
/* Case sensitive: "Aa" */
|
||
'.cm-search label:has(input[name="case"])::after': {
|
||
content: '"Aa"',
|
||
fontSize: '12px',
|
||
letterSpacing: '-0.5px',
|
||
},
|
||
/* Regex: ".*" */
|
||
'.cm-search label:has(input[name="re"])::after': {
|
||
content: '".*"',
|
||
fontSize: '13px',
|
||
fontFamily: 'monospace',
|
||
},
|
||
/* Whole word: "ab" with underline */
|
||
'.cm-search label:has(input[name="word"])::after': {
|
||
content: '"ab"',
|
||
fontSize: '12px',
|
||
textDecoration: 'underline',
|
||
textUnderlineOffset: '2px',
|
||
},
|
||
|
||
/* ────────────────────────────────────────────────────
|
||
MATCH COUNT (search message - "X of Y")
|
||
──────────────────────────────────────────────────── */
|
||
'.cm-search .cm-search-message': {
|
||
order: 3,
|
||
color: theme.colors.surface.mutedForeground,
|
||
backgroundColor: 'transparent',
|
||
border: 'none',
|
||
borderRadius: '0',
|
||
padding: '0 6px',
|
||
fontSize: '11px',
|
||
whiteSpace: 'nowrap' as const,
|
||
lineHeight: '26px',
|
||
},
|
||
|
||
/* ────────────────────────────────────────────────────
|
||
ICON-ONLY ACTION BUTTONS
|
||
Hide button text with font-size:0, show icon via ::after
|
||
──────────────────────────────────────────────────── */
|
||
'.cm-search .cm-button': {
|
||
appearance: 'none',
|
||
WebkitAppearance: 'none',
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
width: '26px',
|
||
height: '26px',
|
||
minWidth: '26px',
|
||
flexShrink: '0',
|
||
margin: 0,
|
||
marginLeft: '1px',
|
||
padding: 0,
|
||
borderRadius: '4px',
|
||
border: 'none',
|
||
backgroundColor: 'transparent',
|
||
backgroundImage: 'none',
|
||
color: 'transparent', /* hide button text */
|
||
cursor: 'pointer',
|
||
fontSize: '1px', /* near-zero to collapse text */
|
||
fontWeight: '400',
|
||
lineHeight: '0',
|
||
overflow: 'hidden',
|
||
position: 'relative' as const,
|
||
boxShadow: 'none',
|
||
transition: 'background-color 120ms ease',
|
||
},
|
||
'.cm-search .cm-button::after': {
|
||
position: 'absolute' as const,
|
||
top: '50%',
|
||
left: '50%',
|
||
transform: 'translate(-50%, -50%)',
|
||
fontSize: '15px',
|
||
lineHeight: '1',
|
||
color: theme.colors.surface.mutedForeground,
|
||
pointerEvents: 'none' as const,
|
||
},
|
||
'.cm-search .cm-button:hover': {
|
||
backgroundColor: theme.colors.interactive.hover,
|
||
},
|
||
'.cm-search .cm-button:hover::after': {
|
||
color: theme.colors.surface.foreground,
|
||
},
|
||
'.cm-search .cm-button:active': {
|
||
backgroundColor: theme.colors.interactive.active,
|
||
},
|
||
'.cm-search .cm-button:focus-visible': {
|
||
outline: 'none',
|
||
boxShadow: `0 0 0 1px ${theme.colors.interactive.focusRing}`,
|
||
},
|
||
'.cm-search .cm-button:disabled': {
|
||
opacity: 0.4,
|
||
cursor: 'default',
|
||
pointerEvents: 'none' as const,
|
||
},
|
||
|
||
/* prev → ↑ arrow (row 2) */
|
||
'.cm-search .cm-button[name="prev"]': {
|
||
order: 21,
|
||
marginTop: '2px',
|
||
},
|
||
'.cm-search .cm-button[name="prev"]::after': {
|
||
content: '"↑"',
|
||
},
|
||
/* next → ↓ arrow (row 2) */
|
||
'.cm-search .cm-button[name="next"]': {
|
||
order: 22,
|
||
marginTop: '2px',
|
||
},
|
||
'.cm-search .cm-button[name="next"]::after': {
|
||
content: '"↓"',
|
||
},
|
||
/* select all → ≡ (row 2) */
|
||
'.cm-search .cm-button[name="select"]': {
|
||
order: 23,
|
||
marginTop: '2px',
|
||
},
|
||
'.cm-search .cm-button[name="select"]::after': {
|
||
content: '"≡"',
|
||
fontSize: '17px',
|
||
},
|
||
|
||
/* ── Close button (×) - not a .cm-button, separate selector ── */
|
||
'.cm-panel.cm-search button[name="close"]': {
|
||
order: 5,
|
||
appearance: 'none',
|
||
WebkitAppearance: 'none',
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
width: '26px',
|
||
height: '26px',
|
||
minWidth: '26px',
|
||
flexShrink: '0',
|
||
margin: '0 0 0 auto',
|
||
padding: '0',
|
||
borderRadius: '4px',
|
||
border: 'none',
|
||
backgroundColor: 'transparent',
|
||
backgroundImage: 'none',
|
||
color: theme.colors.surface.mutedForeground,
|
||
cursor: 'pointer',
|
||
fontSize: '16px',
|
||
lineHeight: '1',
|
||
boxShadow: 'none',
|
||
position: 'relative' as const,
|
||
overflow: 'visible',
|
||
transition: 'background-color 120ms ease, color 120ms ease',
|
||
},
|
||
'.cm-panel.cm-search button[name="close"]:hover': {
|
||
backgroundColor: theme.colors.interactive.hover,
|
||
color: theme.colors.surface.foreground,
|
||
},
|
||
|
||
/* ──────────────────────────────────────────────────
|
||
ROW 2: [Replace input] [replace icon] [replace-all icon]
|
||
────────────────────────────────────────────────── */
|
||
|
||
/* replace → swap icon (row 2) */
|
||
'.cm-search .cm-button[name="replace"]': {
|
||
order: 24,
|
||
marginTop: '2px',
|
||
},
|
||
'.cm-search .cm-button[name="replace"]::after': {
|
||
content: '"⇄"',
|
||
},
|
||
/* replace all → double arrow icon (row 2) */
|
||
'.cm-search .cm-button[name="replaceAll"], .cm-search .cm-button[name="replace-all"]': {
|
||
order: 25,
|
||
marginTop: '2px',
|
||
},
|
||
'.cm-search .cm-button[name="replaceAll"]::after, .cm-search .cm-button[name="replace-all"]::after': {
|
||
content: '"⇉"',
|
||
},
|
||
|
||
/* ── Replace input: forces wrap to row 2, same width as find input ── */
|
||
'.cm-search .cm-textfield[name="replace"]': {
|
||
order: 20,
|
||
flex: '1 1 100%',
|
||
maxWidth: 'calc(100% - 142px)', /* room for ↑↓≡⇄⇉ (5 × 27px + gaps) */
|
||
minWidth: '80px',
|
||
marginTop: '2px',
|
||
},
|
||
'.cm-searchMatch': {
|
||
backgroundColor: theme.colors.status.infoBackground,
|
||
boxShadow: `inset 0 0 0 1px ${theme.colors.status.infoBorder}`,
|
||
borderRadius: '2px',
|
||
},
|
||
'.cm-searchMatch.cm-searchMatch-selected': {
|
||
backgroundColor: theme.colors.interactive.selection,
|
||
color: theme.colors.interactive.selectionForeground,
|
||
boxShadow: `inset 0 0 0 1px ${theme.colors.interactive.borderFocus}`,
|
||
},
|
||
'&.cm-focused': {
|
||
outline: 'none',
|
||
},
|
||
}, { dark: isDark });
|
||
|
||
const syntax = HighlightStyle.define([
|
||
{ tag: [t.comment, t.docComment, t.meta, t.documentMeta], class: 'cm-comment' },
|
||
{ tag: [t.keyword, t.controlKeyword, t.moduleKeyword, t.definitionKeyword, t.modifier], class: 'cm-keyword' },
|
||
{ tag: [t.operatorKeyword, t.operator, t.derefOperator, t.updateOperator, t.definitionOperator, t.typeOperator, t.controlOperator, t.logicOperator, t.bitwiseOperator, t.arithmeticOperator, t.compareOperator], class: 'cm-operator' },
|
||
{ tag: [t.punctuation, t.separator, t.bracket, t.paren, t.brace, t.squareBracket, t.angleBracket], class: 'cm-punctuation' },
|
||
|
||
{ tag: [t.string, t.regexp, t.attributeValue, t.special(t.string), t.monospace], class: 'cm-string' },
|
||
{ tag: t.escape, class: 'cm-string-2' },
|
||
{ tag: [t.number, t.bool, t.atom, t.null, t.self], class: 'cm-number' },
|
||
|
||
{ tag: [t.function(t.variableName), t.function(t.definition(t.variableName)), t.function(t.propertyName), t.standard(t.variableName), t.special(t.variableName)], class: 'cm-builtin' },
|
||
{ tag: t.definition(t.variableName), class: 'cm-def' },
|
||
{ tag: [t.variableName, t.local(t.variableName), t.constant(t.variableName), t.literal], class: 'cm-variable' },
|
||
{ tag: t.propertyName, class: 'cm-property' },
|
||
{ tag: t.attributeName, class: 'cm-attribute' },
|
||
|
||
{ tag: [t.className, t.typeName, t.namespace], class: 'cm-variable-3' },
|
||
{ tag: [t.tagName, t.labelName, t.annotation, t.macroName], class: 'cm-tag' },
|
||
{ tag: t.link, class: 'cm-link' },
|
||
|
||
{ tag: [t.heading, t.heading1, t.heading2, t.heading3, t.heading4, t.heading5, t.heading6], class: 'cm-keyword' },
|
||
]);
|
||
|
||
const directSyntax = HighlightStyle.define([
|
||
{ tag: [t.comment, t.docComment, t.meta, t.documentMeta], color: theme.colors.syntax.base.comment },
|
||
{ tag: [t.keyword, t.controlKeyword, t.moduleKeyword, t.definitionKeyword, t.modifier], color: theme.colors.syntax.base.keyword },
|
||
{
|
||
tag: [
|
||
t.operatorKeyword,
|
||
t.operator,
|
||
t.derefOperator,
|
||
t.updateOperator,
|
||
t.definitionOperator,
|
||
t.typeOperator,
|
||
t.controlOperator,
|
||
t.logicOperator,
|
||
t.bitwiseOperator,
|
||
t.arithmeticOperator,
|
||
t.compareOperator,
|
||
],
|
||
color: theme.colors.syntax.base.operator,
|
||
},
|
||
{ tag: [t.string, t.regexp, t.attributeValue, t.special(t.string), t.monospace], color: theme.colors.syntax.base.string },
|
||
{ tag: t.escape, color: tokens.stringEscape || theme.colors.syntax.base.string },
|
||
{ tag: [t.number, t.bool, t.atom, t.null, t.self], color: theme.colors.syntax.base.number },
|
||
{ tag: [t.function(t.variableName), t.function(t.definition(t.variableName)), t.function(t.propertyName), t.standard(t.variableName), t.special(t.variableName)], color: theme.colors.syntax.base.function },
|
||
{ tag: t.definition(t.variableName), color: tokens.variableGlobal || theme.colors.syntax.base.variable },
|
||
{ tag: [t.variableName, t.local(t.variableName), t.constant(t.variableName), t.literal], color: theme.colors.syntax.base.variable },
|
||
{ tag: t.propertyName, color: tokens.variableProperty || theme.colors.syntax.base.variable },
|
||
{ tag: t.attributeName, color: tokens.variableOther || theme.colors.syntax.base.variable },
|
||
{ tag: [t.className, t.typeName, t.namespace], color: theme.colors.syntax.base.type },
|
||
{ tag: [t.tagName, t.labelName, t.annotation, t.macroName], color: tokens.tag || theme.colors.syntax.base.keyword },
|
||
{ tag: t.link, color: tokens.url || theme.colors.syntax.base.function, textDecoration: 'underline' },
|
||
{
|
||
tag: [t.punctuation, t.separator, t.bracket, t.paren, t.brace, t.squareBracket, t.angleBracket],
|
||
color: tokens.punctuation || theme.colors.syntax.base.comment,
|
||
},
|
||
]);
|
||
|
||
if (options?.syntaxColors === false) {
|
||
return [ui];
|
||
}
|
||
|
||
return [
|
||
ui,
|
||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||
syntaxHighlighting(classHighlighter),
|
||
syntaxHighlighting(syntax),
|
||
syntaxHighlighting(directSyntax),
|
||
];
|
||
}
|