theme.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. import { SyntaxStyle, RGBA, type TerminalColors } from "@opentui/core"
  2. import { createMemo } from "solid-js"
  3. import { useSync } from "@tui/context/sync"
  4. import { createSimpleContext } from "./helper"
  5. import aura from "./theme/aura.json" with { type: "json" }
  6. import ayu from "./theme/ayu.json" with { type: "json" }
  7. import catppuccin from "./theme/catppuccin.json" with { type: "json" }
  8. import cobalt2 from "./theme/cobalt2.json" with { type: "json" }
  9. import dracula from "./theme/dracula.json" with { type: "json" }
  10. import everforest from "./theme/everforest.json" with { type: "json" }
  11. import github from "./theme/github.json" with { type: "json" }
  12. import gruvbox from "./theme/gruvbox.json" with { type: "json" }
  13. import kanagawa from "./theme/kanagawa.json" with { type: "json" }
  14. import material from "./theme/material.json" with { type: "json" }
  15. import matrix from "./theme/matrix.json" with { type: "json" }
  16. import monokai from "./theme/monokai.json" with { type: "json" }
  17. import nightowl from "./theme/nightowl.json" with { type: "json" }
  18. import nord from "./theme/nord.json" with { type: "json" }
  19. import onedark from "./theme/one-dark.json" with { type: "json" }
  20. import opencode from "./theme/opencode.json" with { type: "json" }
  21. import palenight from "./theme/palenight.json" with { type: "json" }
  22. import rosepine from "./theme/rosepine.json" with { type: "json" }
  23. import solarized from "./theme/solarized.json" with { type: "json" }
  24. import synthwave84 from "./theme/synthwave84.json" with { type: "json" }
  25. import tokyonight from "./theme/tokyonight.json" with { type: "json" }
  26. import vesper from "./theme/vesper.json" with { type: "json" }
  27. import zenburn from "./theme/zenburn.json" with { type: "json" }
  28. import { useKV } from "./kv"
  29. import { useRenderer } from "@opentui/solid"
  30. import { createStore } from "solid-js/store"
  31. type Theme = {
  32. primary: RGBA
  33. secondary: RGBA
  34. accent: RGBA
  35. error: RGBA
  36. warning: RGBA
  37. success: RGBA
  38. info: RGBA
  39. text: RGBA
  40. textMuted: RGBA
  41. background: RGBA
  42. backgroundPanel: RGBA
  43. backgroundElement: RGBA
  44. border: RGBA
  45. borderActive: RGBA
  46. borderSubtle: RGBA
  47. diffAdded: RGBA
  48. diffRemoved: RGBA
  49. diffContext: RGBA
  50. diffHunkHeader: RGBA
  51. diffHighlightAdded: RGBA
  52. diffHighlightRemoved: RGBA
  53. diffAddedBg: RGBA
  54. diffRemovedBg: RGBA
  55. diffContextBg: RGBA
  56. diffLineNumber: RGBA
  57. diffAddedLineNumberBg: RGBA
  58. diffRemovedLineNumberBg: RGBA
  59. markdownText: RGBA
  60. markdownHeading: RGBA
  61. markdownLink: RGBA
  62. markdownLinkText: RGBA
  63. markdownCode: RGBA
  64. markdownBlockQuote: RGBA
  65. markdownEmph: RGBA
  66. markdownStrong: RGBA
  67. markdownHorizontalRule: RGBA
  68. markdownListItem: RGBA
  69. markdownListEnumeration: RGBA
  70. markdownImage: RGBA
  71. markdownImageText: RGBA
  72. markdownCodeBlock: RGBA
  73. syntaxComment: RGBA
  74. syntaxKeyword: RGBA
  75. syntaxFunction: RGBA
  76. syntaxVariable: RGBA
  77. syntaxString: RGBA
  78. syntaxNumber: RGBA
  79. syntaxType: RGBA
  80. syntaxOperator: RGBA
  81. syntaxPunctuation: RGBA
  82. }
  83. type HexColor = `#${string}`
  84. type RefName = string
  85. type Variant = {
  86. dark: HexColor | RefName
  87. light: HexColor | RefName
  88. }
  89. type ColorValue = HexColor | RefName | Variant | RGBA
  90. type ThemeJson = {
  91. $schema?: string
  92. defs?: Record<string, HexColor | RefName>
  93. theme: Record<keyof Theme, ColorValue>
  94. }
  95. export const DEFAULT_THEMES: Record<string, ThemeJson> = {
  96. aura,
  97. ayu,
  98. catppuccin,
  99. cobalt2,
  100. dracula,
  101. everforest,
  102. github,
  103. gruvbox,
  104. kanagawa,
  105. material,
  106. matrix,
  107. monokai,
  108. nightowl,
  109. nord,
  110. ["one-dark"]: onedark,
  111. opencode,
  112. palenight,
  113. rosepine,
  114. solarized,
  115. synthwave84,
  116. tokyonight,
  117. vesper,
  118. zenburn,
  119. }
  120. function resolveTheme(theme: ThemeJson, mode: "dark" | "light") {
  121. const defs = theme.defs ?? {}
  122. function resolveColor(c: ColorValue): RGBA {
  123. if (c instanceof RGBA) return c
  124. if (typeof c === "string") return c.startsWith("#") ? RGBA.fromHex(c) : resolveColor(defs[c])
  125. return resolveColor(c[mode])
  126. }
  127. return Object.fromEntries(
  128. Object.entries(theme.theme).map(([key, value]) => {
  129. return [key, resolveColor(value)]
  130. }),
  131. ) as Theme
  132. }
  133. export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
  134. name: "Theme",
  135. init: (props: { mode: "dark" | "light" }) => {
  136. const sync = useSync()
  137. const kv = useKV()
  138. const [store, setStore] = createStore({
  139. themes: DEFAULT_THEMES,
  140. mode: props.mode,
  141. active: (sync.data.config.theme ?? kv.get("theme", "opencode")) as string,
  142. })
  143. const renderer = useRenderer()
  144. renderer
  145. .getPalette({
  146. size: 16,
  147. })
  148. .then((colors) => {
  149. if (!colors.palette[0]) return
  150. setStore("themes", "system", generateSystem(colors, store.mode))
  151. })
  152. const values = createMemo(() => {
  153. return resolveTheme(store.themes[store.active] ?? store.themes.opencode, store.mode)
  154. })
  155. const syntax = createMemo(() => generateSyntax(values()))
  156. return {
  157. theme: new Proxy(values(), {
  158. get(_target, prop) {
  159. // @ts-expect-error
  160. return values()[prop]
  161. },
  162. }),
  163. get selected() {
  164. return store.active
  165. },
  166. all() {
  167. return store.themes
  168. },
  169. syntax,
  170. mode() {
  171. return store.mode
  172. },
  173. setMode(mode: "dark" | "light") {
  174. setStore("mode", mode)
  175. },
  176. set(theme: string) {
  177. setStore("active", theme)
  178. kv.set("theme", theme)
  179. },
  180. get ready() {
  181. return sync.ready
  182. },
  183. }
  184. },
  185. })
  186. function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJson {
  187. const bg = RGBA.fromHex(colors.defaultBackground ?? colors.palette[0]!)
  188. const fg = RGBA.fromHex(colors.defaultForeground ?? colors.palette[7]!)
  189. const palette = colors.palette.map((x) => RGBA.fromHex(x!))
  190. const isDark = mode == "dark"
  191. // Generate gray scale based on terminal background
  192. const grays = generateGrayScale(bg, isDark)
  193. const textMuted = generateMutedTextColor(bg, isDark)
  194. // ANSI color references
  195. const ansiColors = {
  196. black: palette[0],
  197. red: palette[1],
  198. green: palette[2],
  199. yellow: palette[3],
  200. blue: palette[4],
  201. magenta: palette[5],
  202. cyan: palette[6],
  203. white: palette[7],
  204. }
  205. return {
  206. theme: {
  207. // Primary colors using ANSI
  208. primary: ansiColors.cyan,
  209. secondary: ansiColors.magenta,
  210. accent: ansiColors.cyan,
  211. // Status colors using ANSI
  212. error: ansiColors.red,
  213. warning: ansiColors.yellow,
  214. success: ansiColors.green,
  215. info: ansiColors.cyan,
  216. // Text colors
  217. text: fg,
  218. textMuted,
  219. // Background colors
  220. background: bg,
  221. backgroundPanel: grays[2],
  222. backgroundElement: grays[3],
  223. // Border colors
  224. borderSubtle: grays[6],
  225. border: grays[7],
  226. borderActive: grays[8],
  227. // Diff colors
  228. diffAdded: ansiColors.green,
  229. diffRemoved: ansiColors.red,
  230. diffContext: grays[7],
  231. diffHunkHeader: grays[7],
  232. diffHighlightAdded: ansiColors.green,
  233. diffHighlightRemoved: ansiColors.red,
  234. diffAddedBg: grays[2],
  235. diffRemovedBg: grays[2],
  236. diffContextBg: grays[1],
  237. diffLineNumber: grays[6],
  238. diffAddedLineNumberBg: grays[3],
  239. diffRemovedLineNumberBg: grays[3],
  240. // Markdown colors
  241. markdownText: fg,
  242. markdownHeading: fg,
  243. markdownLink: ansiColors.blue,
  244. markdownLinkText: ansiColors.cyan,
  245. markdownCode: ansiColors.green,
  246. markdownBlockQuote: ansiColors.yellow,
  247. markdownEmph: ansiColors.yellow,
  248. markdownStrong: fg,
  249. markdownHorizontalRule: grays[7],
  250. markdownListItem: ansiColors.blue,
  251. markdownListEnumeration: ansiColors.cyan,
  252. markdownImage: ansiColors.blue,
  253. markdownImageText: ansiColors.cyan,
  254. markdownCodeBlock: fg,
  255. // Syntax colors
  256. syntaxComment: textMuted,
  257. syntaxKeyword: ansiColors.magenta,
  258. syntaxFunction: ansiColors.blue,
  259. syntaxVariable: fg,
  260. syntaxString: ansiColors.green,
  261. syntaxNumber: ansiColors.yellow,
  262. syntaxType: ansiColors.cyan,
  263. syntaxOperator: ansiColors.cyan,
  264. syntaxPunctuation: fg,
  265. },
  266. }
  267. }
  268. function generateGrayScale(bg: RGBA, isDark: boolean): Record<number, RGBA> {
  269. const grays: Record<number, RGBA> = {}
  270. // RGBA stores floats in range 0-1, convert to 0-255
  271. const bgR = bg.r * 255
  272. const bgG = bg.g * 255
  273. const bgB = bg.b * 255
  274. const luminance = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB
  275. for (let i = 1; i <= 12; i++) {
  276. const factor = i / 12.0
  277. let grayValue: number
  278. let newR: number
  279. let newG: number
  280. let newB: number
  281. if (isDark) {
  282. if (luminance < 10) {
  283. grayValue = Math.floor(factor * 0.4 * 255)
  284. newR = grayValue
  285. newG = grayValue
  286. newB = grayValue
  287. } else {
  288. const newLum = luminance + (255 - luminance) * factor * 0.4
  289. const ratio = newLum / luminance
  290. newR = Math.min(bgR * ratio, 255)
  291. newG = Math.min(bgG * ratio, 255)
  292. newB = Math.min(bgB * ratio, 255)
  293. }
  294. } else {
  295. if (luminance > 245) {
  296. grayValue = Math.floor(255 - factor * 0.4 * 255)
  297. newR = grayValue
  298. newG = grayValue
  299. newB = grayValue
  300. } else {
  301. const newLum = luminance * (1 - factor * 0.4)
  302. const ratio = newLum / luminance
  303. newR = Math.max(bgR * ratio, 0)
  304. newG = Math.max(bgG * ratio, 0)
  305. newB = Math.max(bgB * ratio, 0)
  306. }
  307. }
  308. grays[i] = RGBA.fromInts(Math.floor(newR), Math.floor(newG), Math.floor(newB))
  309. }
  310. return grays
  311. }
  312. function generateMutedTextColor(bg: RGBA, isDark: boolean): RGBA {
  313. // RGBA stores floats in range 0-1, convert to 0-255
  314. const bgR = bg.r * 255
  315. const bgG = bg.g * 255
  316. const bgB = bg.b * 255
  317. const bgLum = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB
  318. let grayValue: number
  319. if (isDark) {
  320. if (bgLum < 10) {
  321. // Very dark/black background
  322. grayValue = 180 // #b4b4b4
  323. } else {
  324. // Scale up for lighter dark backgrounds
  325. grayValue = Math.min(Math.floor(160 + bgLum * 0.3), 200)
  326. }
  327. } else {
  328. if (bgLum > 245) {
  329. // Very light/white background
  330. grayValue = 75 // #4b4b4b
  331. } else {
  332. // Scale down for darker light backgrounds
  333. grayValue = Math.max(Math.floor(100 - (255 - bgLum) * 0.2), 60)
  334. }
  335. }
  336. return RGBA.fromInts(grayValue, grayValue, grayValue)
  337. }
  338. function generateSyntax(theme: Theme) {
  339. return SyntaxStyle.fromTheme([
  340. {
  341. scope: ["prompt"],
  342. style: {
  343. foreground: theme.accent,
  344. },
  345. },
  346. {
  347. scope: ["extmark.file"],
  348. style: {
  349. foreground: theme.warning,
  350. bold: true,
  351. },
  352. },
  353. {
  354. scope: ["extmark.agent"],
  355. style: {
  356. foreground: theme.secondary,
  357. bold: true,
  358. },
  359. },
  360. {
  361. scope: ["extmark.paste"],
  362. style: {
  363. foreground: theme.background,
  364. background: theme.warning,
  365. bold: true,
  366. },
  367. },
  368. {
  369. scope: ["comment"],
  370. style: {
  371. foreground: theme.syntaxComment,
  372. italic: true,
  373. },
  374. },
  375. {
  376. scope: ["comment.documentation"],
  377. style: {
  378. foreground: theme.syntaxComment,
  379. italic: true,
  380. },
  381. },
  382. {
  383. scope: ["string", "symbol"],
  384. style: {
  385. foreground: theme.syntaxString,
  386. },
  387. },
  388. {
  389. scope: ["number", "boolean"],
  390. style: {
  391. foreground: theme.syntaxNumber,
  392. },
  393. },
  394. {
  395. scope: ["character.special"],
  396. style: {
  397. foreground: theme.syntaxString,
  398. },
  399. },
  400. {
  401. scope: ["keyword.return", "keyword.conditional", "keyword.repeat", "keyword.coroutine"],
  402. style: {
  403. foreground: theme.syntaxKeyword,
  404. italic: true,
  405. },
  406. },
  407. {
  408. scope: ["keyword.type"],
  409. style: {
  410. foreground: theme.syntaxType,
  411. bold: true,
  412. italic: true,
  413. },
  414. },
  415. {
  416. scope: ["keyword.function", "function.method"],
  417. style: {
  418. foreground: theme.syntaxFunction,
  419. },
  420. },
  421. {
  422. scope: ["keyword"],
  423. style: {
  424. foreground: theme.syntaxKeyword,
  425. italic: true,
  426. },
  427. },
  428. {
  429. scope: ["keyword.import"],
  430. style: {
  431. foreground: theme.syntaxKeyword,
  432. },
  433. },
  434. {
  435. scope: ["operator", "keyword.operator", "punctuation.delimiter"],
  436. style: {
  437. foreground: theme.syntaxOperator,
  438. },
  439. },
  440. {
  441. scope: ["keyword.conditional.ternary"],
  442. style: {
  443. foreground: theme.syntaxOperator,
  444. },
  445. },
  446. {
  447. scope: ["variable", "variable.parameter", "function.method.call", "function.call"],
  448. style: {
  449. foreground: theme.syntaxVariable,
  450. },
  451. },
  452. {
  453. scope: ["variable.member", "function", "constructor"],
  454. style: {
  455. foreground: theme.syntaxFunction,
  456. },
  457. },
  458. {
  459. scope: ["type", "module"],
  460. style: {
  461. foreground: theme.syntaxType,
  462. },
  463. },
  464. {
  465. scope: ["constant"],
  466. style: {
  467. foreground: theme.syntaxNumber,
  468. },
  469. },
  470. {
  471. scope: ["property"],
  472. style: {
  473. foreground: theme.syntaxVariable,
  474. },
  475. },
  476. {
  477. scope: ["class"],
  478. style: {
  479. foreground: theme.syntaxType,
  480. },
  481. },
  482. {
  483. scope: ["parameter"],
  484. style: {
  485. foreground: theme.syntaxVariable,
  486. },
  487. },
  488. {
  489. scope: ["punctuation", "punctuation.bracket"],
  490. style: {
  491. foreground: theme.syntaxPunctuation,
  492. },
  493. },
  494. {
  495. scope: [
  496. "variable.builtin",
  497. "type.builtin",
  498. "function.builtin",
  499. "module.builtin",
  500. "constant.builtin",
  501. ],
  502. style: {
  503. foreground: theme.error,
  504. },
  505. },
  506. {
  507. scope: ["variable.super"],
  508. style: {
  509. foreground: theme.error,
  510. },
  511. },
  512. {
  513. scope: ["string.escape", "string.regexp"],
  514. style: {
  515. foreground: theme.syntaxKeyword,
  516. },
  517. },
  518. {
  519. scope: ["keyword.directive"],
  520. style: {
  521. foreground: theme.syntaxKeyword,
  522. italic: true,
  523. },
  524. },
  525. {
  526. scope: ["punctuation.special"],
  527. style: {
  528. foreground: theme.syntaxOperator,
  529. },
  530. },
  531. {
  532. scope: ["keyword.modifier"],
  533. style: {
  534. foreground: theme.syntaxKeyword,
  535. italic: true,
  536. },
  537. },
  538. {
  539. scope: ["keyword.exception"],
  540. style: {
  541. foreground: theme.syntaxKeyword,
  542. italic: true,
  543. },
  544. },
  545. // Markdown specific styles
  546. {
  547. scope: ["markup.heading"],
  548. style: {
  549. foreground: theme.markdownHeading,
  550. bold: true,
  551. },
  552. },
  553. {
  554. scope: ["markup.heading.1"],
  555. style: {
  556. foreground: theme.markdownHeading,
  557. bold: true,
  558. },
  559. },
  560. {
  561. scope: ["markup.heading.2"],
  562. style: {
  563. foreground: theme.markdownHeading,
  564. bold: true,
  565. },
  566. },
  567. {
  568. scope: ["markup.heading.3"],
  569. style: {
  570. foreground: theme.markdownHeading,
  571. bold: true,
  572. },
  573. },
  574. {
  575. scope: ["markup.heading.4"],
  576. style: {
  577. foreground: theme.markdownHeading,
  578. bold: true,
  579. },
  580. },
  581. {
  582. scope: ["markup.heading.5"],
  583. style: {
  584. foreground: theme.markdownHeading,
  585. bold: true,
  586. },
  587. },
  588. {
  589. scope: ["markup.heading.6"],
  590. style: {
  591. foreground: theme.markdownHeading,
  592. bold: true,
  593. },
  594. },
  595. {
  596. scope: ["markup.bold", "markup.strong"],
  597. style: {
  598. foreground: theme.markdownStrong,
  599. bold: true,
  600. },
  601. },
  602. {
  603. scope: ["markup.italic"],
  604. style: {
  605. foreground: theme.markdownEmph,
  606. italic: true,
  607. },
  608. },
  609. {
  610. scope: ["markup.list"],
  611. style: {
  612. foreground: theme.markdownListItem,
  613. },
  614. },
  615. {
  616. scope: ["markup.quote"],
  617. style: {
  618. foreground: theme.markdownBlockQuote,
  619. italic: true,
  620. },
  621. },
  622. {
  623. scope: ["markup.raw", "markup.raw.block"],
  624. style: {
  625. foreground: theme.markdownCode,
  626. },
  627. },
  628. {
  629. scope: ["markup.raw.inline"],
  630. style: {
  631. foreground: theme.markdownCode,
  632. background: theme.background,
  633. },
  634. },
  635. {
  636. scope: ["markup.link"],
  637. style: {
  638. foreground: theme.markdownLink,
  639. underline: true,
  640. },
  641. },
  642. {
  643. scope: ["markup.link.label"],
  644. style: {
  645. foreground: theme.markdownLinkText,
  646. underline: true,
  647. },
  648. },
  649. {
  650. scope: ["markup.link.url"],
  651. style: {
  652. foreground: theme.markdownLink,
  653. underline: true,
  654. },
  655. },
  656. {
  657. scope: ["label"],
  658. style: {
  659. foreground: theme.markdownLinkText,
  660. },
  661. },
  662. {
  663. scope: ["spell", "nospell"],
  664. style: {
  665. foreground: theme.text,
  666. },
  667. },
  668. {
  669. scope: ["conceal"],
  670. style: {
  671. foreground: theme.textMuted,
  672. },
  673. },
  674. // Additional common highlight groups
  675. {
  676. scope: ["string.special", "string.special.url"],
  677. style: {
  678. foreground: theme.markdownLink,
  679. underline: true,
  680. },
  681. },
  682. {
  683. scope: ["character"],
  684. style: {
  685. foreground: theme.syntaxString,
  686. },
  687. },
  688. {
  689. scope: ["float"],
  690. style: {
  691. foreground: theme.syntaxNumber,
  692. },
  693. },
  694. {
  695. scope: ["comment.error"],
  696. style: {
  697. foreground: theme.error,
  698. italic: true,
  699. bold: true,
  700. },
  701. },
  702. {
  703. scope: ["comment.warning"],
  704. style: {
  705. foreground: theme.warning,
  706. italic: true,
  707. bold: true,
  708. },
  709. },
  710. {
  711. scope: ["comment.todo", "comment.note"],
  712. style: {
  713. foreground: theme.info,
  714. italic: true,
  715. bold: true,
  716. },
  717. },
  718. {
  719. scope: ["namespace"],
  720. style: {
  721. foreground: theme.syntaxType,
  722. },
  723. },
  724. {
  725. scope: ["field"],
  726. style: {
  727. foreground: theme.syntaxVariable,
  728. },
  729. },
  730. {
  731. scope: ["type.definition"],
  732. style: {
  733. foreground: theme.syntaxType,
  734. bold: true,
  735. },
  736. },
  737. {
  738. scope: ["keyword.export"],
  739. style: {
  740. foreground: theme.syntaxKeyword,
  741. },
  742. },
  743. {
  744. scope: ["attribute", "annotation"],
  745. style: {
  746. foreground: theme.warning,
  747. },
  748. },
  749. {
  750. scope: ["tag"],
  751. style: {
  752. foreground: theme.error,
  753. },
  754. },
  755. {
  756. scope: ["tag.attribute"],
  757. style: {
  758. foreground: theme.syntaxKeyword,
  759. },
  760. },
  761. {
  762. scope: ["tag.delimiter"],
  763. style: {
  764. foreground: theme.syntaxOperator,
  765. },
  766. },
  767. {
  768. scope: ["markup.strikethrough"],
  769. style: {
  770. foreground: theme.textMuted,
  771. },
  772. },
  773. {
  774. scope: ["markup.underline"],
  775. style: {
  776. foreground: theme.text,
  777. underline: true,
  778. },
  779. },
  780. {
  781. scope: ["markup.list.checked"],
  782. style: {
  783. foreground: theme.success,
  784. },
  785. },
  786. {
  787. scope: ["markup.list.unchecked"],
  788. style: {
  789. foreground: theme.textMuted,
  790. },
  791. },
  792. {
  793. scope: ["diff.plus"],
  794. style: {
  795. foreground: theme.diffAdded,
  796. },
  797. },
  798. {
  799. scope: ["diff.minus"],
  800. style: {
  801. foreground: theme.diffRemoved,
  802. },
  803. },
  804. {
  805. scope: ["diff.delta"],
  806. style: {
  807. foreground: theme.diffContext,
  808. },
  809. },
  810. {
  811. scope: ["error"],
  812. style: {
  813. foreground: theme.error,
  814. bold: true,
  815. },
  816. },
  817. {
  818. scope: ["warning"],
  819. style: {
  820. foreground: theme.warning,
  821. bold: true,
  822. },
  823. },
  824. {
  825. scope: ["info"],
  826. style: {
  827. foreground: theme.info,
  828. },
  829. },
  830. {
  831. scope: ["debug"],
  832. style: {
  833. foreground: theme.textMuted,
  834. },
  835. },
  836. ])
  837. }