termcast 1.3.35 → 1.3.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/components/detail.d.ts.map +1 -1
  2. package/dist/components/detail.js +12 -2
  3. package/dist/components/detail.js.map +1 -1
  4. package/dist/components/list.d.ts.map +1 -1
  5. package/dist/components/list.js +11 -2
  6. package/dist/components/list.js.map +1 -1
  7. package/dist/diagram-parser.d.ts +34 -0
  8. package/dist/diagram-parser.d.ts.map +1 -0
  9. package/dist/diagram-parser.js +114 -0
  10. package/dist/diagram-parser.js.map +1 -0
  11. package/dist/examples/simple-detail-markdown.d.ts +2 -0
  12. package/dist/examples/simple-detail-markdown.d.ts.map +1 -0
  13. package/dist/examples/simple-detail-markdown.js +94 -0
  14. package/dist/examples/simple-detail-markdown.js.map +1 -0
  15. package/dist/internal/scrollbox.d.ts.map +1 -1
  16. package/dist/internal/scrollbox.js +1 -2
  17. package/dist/internal/scrollbox.js.map +1 -1
  18. package/dist/markdown-utils.d.ts +14 -0
  19. package/dist/markdown-utils.d.ts.map +1 -0
  20. package/dist/markdown-utils.js +138 -0
  21. package/dist/markdown-utils.js.map +1 -0
  22. package/dist/theme.d.ts.map +1 -1
  23. package/dist/theme.js +5 -24
  24. package/dist/theme.js.map +1 -1
  25. package/dist/themes/termcast.json +4 -4
  26. package/dist/themes.d.ts +12 -0
  27. package/dist/themes.d.ts.map +1 -1
  28. package/dist/themes.js +79 -0
  29. package/dist/themes.js.map +1 -1
  30. package/package.json +7 -4
  31. package/src/components/detail.tsx +16 -2
  32. package/src/components/list.tsx +15 -2
  33. package/src/diagram-parser.tsx +141 -0
  34. package/src/examples/list-with-detail.vitest.tsx +34 -34
  35. package/src/examples/list-with-sections.vitest.tsx +1 -1
  36. package/src/examples/simple-detail-markdown.tsx +96 -0
  37. package/src/examples/simple-detail-markdown.vitest.tsx +156 -0
  38. package/src/examples/simple-grid.vitest.tsx +2 -2
  39. package/src/examples/swift-extension.vitest.tsx +1 -1
  40. package/src/internal/scrollbox.tsx +1 -3
  41. package/src/markdown-utils.tsx +182 -0
  42. package/src/theme.tsx +5 -24
  43. package/src/themes/termcast.json +4 -4
  44. package/src/themes.ts +98 -0
@@ -0,0 +1,156 @@
1
+ import { test, expect, afterEach, beforeEach } from 'vitest'
2
+ import { launchTerminal, Session } from 'tuistory/src'
3
+
4
+ let session: Session
5
+
6
+ beforeEach(async () => {
7
+ session = await launchTerminal({
8
+ command: 'bun',
9
+ args: ['src/examples/simple-detail-markdown.tsx'],
10
+ cols: 80,
11
+ rows: 70,
12
+ })
13
+ })
14
+
15
+ afterEach(() => {
16
+ session?.close()
17
+ })
18
+
19
+ test('detail renders markdown with headings, lists, links, tables, code and diagrams', async () => {
20
+ const text = await session.text({
21
+ waitFor: (text) => {
22
+ return text.includes('Architecture') && text.includes('Configuration Table') && text.includes('Process')
23
+ },
24
+ timeout: 10000,
25
+ })
26
+
27
+ expect(text).toMatchInlineSnapshot(`
28
+ "
29
+
30
+
31
+
32
+
33
+ Architecture Overview ▀
34
+
35
+ This document describes the system architecture.
36
+
37
+ Components
38
+
39
+ The system has three main components:
40
+
41
+ - Client - handles user interaction
42
+ - Server - processes requests
43
+ - Database - stores data
44
+
45
+ Links
46
+
47
+ Check out the GitHub repository for the source code.
48
+
49
+ See the API documentation for more details.
50
+
51
+ A paragraph with multiple links inline here.
52
+
53
+ Nested formatting: bold with link inside and italic with link.
54
+
55
+ Configuration Table
56
+
57
+ ┌───────────┬───────────┬───────────────────────┐
58
+ │Setting │Default │Description │
59
+ │───────────│───────────│───────────────────────│
60
+ │Host │localhost │Database host address │
61
+ │───────────│───────────│───────────────────────│
62
+ │Port │5432 │Database port number │
63
+ │───────────│───────────│───────────────────────│
64
+ │SSL │false │Enable TLS encryption │
65
+ │───────────│───────────│───────────────────────│
66
+ │Pool Size │10 │Max connections │
67
+ └───────────┴───────────┴───────────────────────┘
68
+
69
+ Flow Diagram
70
+
71
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
72
+ │ Client │────▶│ Server │────▶│ Database │
73
+ └─────────────┘ └─────────────┘ └─────────────┘
74
+
75
+ Vertical Flow
76
+
77
+ ┌─────────┐
78
+ │ Start │
79
+ └────┬────┘
80
+
81
+
82
+ ┌─────────┐
83
+ │ Process │
84
+ └────┬────┘
85
+
86
+
87
+ ┌─────────┐
88
+ │ End │
89
+ └─────────┘
90
+
91
+ Code Example
92
+
93
+ interface Config {
94
+
95
+
96
+ esc go back powered by termcast
97
+
98
+ "
99
+ `)
100
+ // Headings
101
+ expect(text).toContain('Architecture')
102
+ expect(text).toContain('Components')
103
+ expect(text).toContain('Configuration Table')
104
+ // List items
105
+ expect(text).toContain('Client')
106
+ expect(text).toContain('Server')
107
+ expect(text).toContain('Database')
108
+ // Links - title text visible, URLs hidden
109
+ expect(text).toContain('GitHub repository')
110
+ expect(text).toContain('API documentation')
111
+ expect(text).toContain('link inside')
112
+ // Table
113
+ expect(text).toContain('Setting')
114
+ expect(text).toContain('localhost')
115
+ expect(text).toContain('5432')
116
+ // Diagram box-drawing chars
117
+ expect(text).toContain('┌─')
118
+ expect(text).toContain('─┐')
119
+ expect(text).toContain('Process')
120
+ // Code block header visible (content may need scroll)
121
+ expect(text).toContain('Code Example')
122
+ expect(text).toContain('interface Config')
123
+ }, 30000)
124
+
125
+ test('links have distinct cyan color from bold/heading text', async () => {
126
+ await session.text({
127
+ waitFor: (text) => text.includes('GitHub repository'),
128
+ timeout: 10000,
129
+ })
130
+
131
+ // Links should be cyan (#56b6c2)
132
+ const linkText = await session.text({
133
+ only: { foreground: '#56b6c2' },
134
+ timeout: 5000,
135
+ })
136
+
137
+ // Bold/heading text should be primary yellow (#ffc000)
138
+ const boldText = await session.text({
139
+ only: { foreground: '#ffc000' },
140
+ timeout: 5000,
141
+ })
142
+
143
+ // Verify links are rendered in cyan
144
+ expect(linkText).toContain('GitHub repository')
145
+ expect(linkText).toContain('API documentation')
146
+ expect(linkText).toContain('multiple')
147
+ expect(linkText).toContain('inline')
148
+
149
+ // Verify headings are rendered in primary color (not cyan)
150
+ expect(boldText).toContain('Architecture Overview')
151
+ expect(boldText).toContain('Components')
152
+
153
+ // Links should NOT appear in bold color
154
+ expect(boldText).not.toContain('GitHub repository')
155
+ expect(boldText).not.toContain('API documentation')
156
+ }, 30000)
@@ -72,8 +72,8 @@ test('grid navigation and display', async () => {
72
72
  > Search items...
73
73
 
74
74
  Fruits
75
- ›🍎 Apple
76
- 🍌 Banana
75
+ 🍎 Apple
76
+ ›🍌 Banana
77
77
  🍒 Cherry
78
78
 
79
79
  Animals
@@ -96,7 +96,7 @@ test.skipIf(isLinux)('swift extension dev mode shows command list', async () =>
96
96
 
97
97
 
98
98
 
99
- ↵ run command ↑↓ navigate ^k actions
99
+ ↵ run command
100
100
 
101
101
 
102
102
 
@@ -43,9 +43,7 @@ export function ScrollBox({
43
43
  // visible: true,
44
44
  // showArrows: true,
45
45
  trackOptions: {
46
- foregroundColor: theme.textMuted,
47
-
48
- // backgroundColor: '#414868',
46
+ foregroundColor: theme.border,
49
47
  },
50
48
  ...(style?.scrollbarOptions || {}),
51
49
  },
@@ -0,0 +1,182 @@
1
+ // Markdown renderNode hook for terminal rendering.
2
+ // Overrides paragraph rendering to hide URLs from links,
3
+ // showing only the link title text with distinct cyan color and underline.
4
+ // Uses opentui's renderNode callback on the <markdown> element.
5
+ //
6
+ // Link text gets TextChunk.link = { url } which encodes as OSC 8 terminal
7
+ // hyperlinks when the terminal supports it. Supported terminals include:
8
+ // - Ghostty, kitty, WezTerm, Alacritty, iTerm2
9
+ // In these terminals, links are clickable natively (cmd+click or hover).
10
+ // In unsupported terminals, links still display with distinct color/underline
11
+ // but won't be clickable.
12
+
13
+ import {
14
+ TextRenderable,
15
+ StyledText,
16
+ type TextChunk,
17
+ parseColor,
18
+ createTextAttributes,
19
+ type Renderable,
20
+ type RenderContext,
21
+ } from '@opentui/core'
22
+ import { getResolvedTheme } from './themes'
23
+ import { useStore } from './state'
24
+
25
+ // Minimal token types from marked (dependency of opentui, not termcast directly)
26
+ interface Token {
27
+ type: string
28
+ text?: string
29
+ raw?: string
30
+ href?: string
31
+ tokens?: Token[]
32
+ }
33
+
34
+ // Matches RenderNodeContext from @opentui/core/renderables/Markdown
35
+ interface RenderNodeContext {
36
+ defaultRender: () => Renderable | null
37
+ }
38
+
39
+ interface LinkInfo {
40
+ text: string
41
+ href: string
42
+ }
43
+
44
+ // Recursively check if a token or any of its children contain link tokens
45
+ function hasLinks(token: Token): boolean {
46
+ if (!Array.isArray(token.tokens)) {
47
+ return false
48
+ }
49
+ return token.tokens.some((t) => {
50
+ return t.type === 'link' || hasLinks(t)
51
+ })
52
+ }
53
+
54
+ // Recursively flatten inline tokens into chunks, stripping link URLs.
55
+ // Handles nested structures like **[link](url)** or *[link](url)*.
56
+ function flattenInlineTokens({
57
+ tokens,
58
+ chunks,
59
+ links,
60
+ primaryColor,
61
+ linkColor,
62
+ textColor,
63
+ defaultAttr,
64
+ }: {
65
+ tokens: Token[]
66
+ chunks: TextChunk[]
67
+ links: LinkInfo[]
68
+ primaryColor: ReturnType<typeof parseColor>
69
+ linkColor: ReturnType<typeof parseColor>
70
+ textColor: ReturnType<typeof parseColor>
71
+ defaultAttr?: number
72
+ }): void {
73
+ for (const token of tokens) {
74
+ if (token.type === 'link') {
75
+ links.push({ text: token.text || '', href: token.href || '' })
76
+ // Render link title only with distinct link color, underline, and OSC 8 terminal hyperlink
77
+ const linkAttr = createTextAttributes({ underline: true })
78
+ chunks.push({
79
+ __isChunk: true,
80
+ text: token.text || '',
81
+ fg: linkColor,
82
+ attributes: linkAttr,
83
+ link: { url: token.href || '' },
84
+ })
85
+ } else if (token.type === 'strong') {
86
+ const boldAttr = createTextAttributes({ bold: true })
87
+ // Recurse into strong children to handle nested links like **[link](url)**
88
+ flattenInlineTokens({
89
+ tokens: token.tokens || [],
90
+ chunks,
91
+ links,
92
+ primaryColor,
93
+ linkColor,
94
+ textColor,
95
+ defaultAttr: boldAttr,
96
+ })
97
+ } else if (token.type === 'em') {
98
+ const italicAttr = createTextAttributes({ italic: true })
99
+ // Recurse into em children to handle nested links like *[link](url)*
100
+ flattenInlineTokens({
101
+ tokens: token.tokens || [],
102
+ chunks,
103
+ links,
104
+ primaryColor,
105
+ linkColor,
106
+ textColor,
107
+ defaultAttr: italicAttr,
108
+ })
109
+ } else if (token.type === 'del') {
110
+ const strikeAttr = createTextAttributes({ strikethrough: true })
111
+ flattenInlineTokens({
112
+ tokens: token.tokens || [],
113
+ chunks,
114
+ links,
115
+ primaryColor,
116
+ linkColor,
117
+ textColor,
118
+ defaultAttr: strikeAttr,
119
+ })
120
+ } else if (token.type === 'codespan') {
121
+ chunks.push({
122
+ __isChunk: true,
123
+ text: token.text || '',
124
+ fg: primaryColor,
125
+ attributes: defaultAttr,
126
+ })
127
+ } else if (token.type === 'br') {
128
+ chunks.push({
129
+ __isChunk: true,
130
+ text: '\n',
131
+ fg: textColor,
132
+ })
133
+ } else {
134
+ // text, escape, etc.
135
+ chunks.push({
136
+ __isChunk: true,
137
+ text: token.text ?? token.raw ?? '',
138
+ fg: textColor,
139
+ attributes: defaultAttr,
140
+ })
141
+ }
142
+ }
143
+ }
144
+
145
+ // Create a renderNode function that overrides paragraph rendering
146
+ // to hide link URLs and make link text bold + bright.
147
+ // Links get OSC 8 terminal hyperlinks via TextChunk.link so terminals
148
+ // can handle click-to-open natively (cmd+click in iTerm2, kitty, Ghostty).
149
+ export function createMarkdownRenderNode(renderer: RenderContext): (token: Token, context: RenderNodeContext) => Renderable | undefined {
150
+ let nodeCounter = 0
151
+
152
+ return (token: Token, context: RenderNodeContext) => {
153
+ // Only override paragraphs that contain links (including nested)
154
+ if (token.type !== 'paragraph' || !hasLinks(token)) {
155
+ return undefined // use default rendering
156
+ }
157
+
158
+ const themeName = useStore.getState().currentThemeName
159
+ const theme = getResolvedTheme(themeName)
160
+ const primaryColor = parseColor(theme.primary)
161
+ const linkColor = parseColor(theme.markdownLinkText)
162
+ const textColor = parseColor(theme.text)
163
+
164
+ const chunks: TextChunk[] = []
165
+ const links: LinkInfo[] = []
166
+ flattenInlineTokens({
167
+ tokens: token.tokens || [],
168
+ chunks,
169
+ links,
170
+ primaryColor,
171
+ linkColor,
172
+ textColor,
173
+ })
174
+
175
+ return new TextRenderable(renderer, {
176
+ id: `para-links-${nodeCounter++}`,
177
+ content: new StyledText(chunks),
178
+ width: '100%',
179
+ marginBottom: 1,
180
+ })
181
+ }
182
+ }
package/src/theme.tsx CHANGED
@@ -1,5 +1,5 @@
1
- import { SyntaxStyle, RGBA } from '@opentui/core'
2
- import { getResolvedTheme, type ResolvedTheme, defaultThemeName, themeNames } from './themes'
1
+ import { SyntaxStyle } from '@opentui/core'
2
+ import { getResolvedTheme, getSyntaxTheme, type ResolvedTheme, defaultThemeName, themeNames } from './themes'
3
3
  import { useStore } from './state'
4
4
  import { Cache } from './apis/cache'
5
5
 
@@ -51,30 +51,11 @@ export function useTheme(): ResolvedTheme {
51
51
  return getResolvedTheme(themeName)
52
52
  }
53
53
 
54
+ // Returns a full SyntaxStyle with all code + markdown scopes.
55
+ // Code blocks inside markdown get proper syntax highlighting (keywords, strings, etc.)
54
56
  export function getMarkdownSyntaxStyle(): SyntaxStyle {
55
57
  const themeName = useStore.getState().currentThemeName
56
- const t = getResolvedTheme(themeName)
57
- return SyntaxStyle.fromStyles({
58
- default: { fg: RGBA.fromHex(t.markdownText) },
59
- 'markup.heading.1': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
60
- 'markup.heading.2': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
61
- 'markup.heading.3': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
62
- 'markup.heading.4': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
63
- 'markup.heading.5': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
64
- 'markup.heading.6': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
65
- 'markup.heading': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
66
- 'markup.raw.block': { fg: RGBA.fromHex(t.markdownCode) },
67
- 'markup.link.url': { fg: RGBA.fromHex(t.markdownLink) },
68
- 'markup.link.label': { fg: RGBA.fromHex(t.markdownLinkText) },
69
- 'markup.list': { fg: RGBA.fromHex(t.markdownListItem) },
70
- 'markup.list.checked': { fg: RGBA.fromHex(t.success) },
71
- 'markup.list.unchecked': { fg: RGBA.fromHex(t.textMuted) },
72
- 'markup.quote': { fg: RGBA.fromHex(t.markdownBlockQuote), italic: true },
73
- 'punctuation.special': { fg: RGBA.fromHex(t.syntaxPunctuation) },
74
- 'punctuation.delimiter': { fg: RGBA.fromHex(t.syntaxPunctuation) },
75
- 'string.escape': { fg: RGBA.fromHex(t.syntaxString) },
76
- label: { fg: RGBA.fromHex(t.accent) },
77
- })
58
+ return SyntaxStyle.fromStyles(getSyntaxTheme(themeName))
78
59
  }
79
60
 
80
61
  // For backward compatibility - some code imports markdownSyntaxStyle directly
@@ -140,12 +140,12 @@
140
140
  "light": "darkStep9"
141
141
  },
142
142
  "markdownLink": {
143
- "dark": "darkStep9",
144
- "light": "darkStep9"
143
+ "dark": "darkCyan",
144
+ "light": "darkCyan"
145
145
  },
146
146
  "markdownLinkText": {
147
- "dark": "darkStep9",
148
- "light": "darkStep9"
147
+ "dark": "darkCyan",
148
+ "light": "darkCyan"
149
149
  },
150
150
  "markdownCode": {
151
151
  "dark": "darkStep9",
package/src/themes.ts CHANGED
@@ -55,6 +55,7 @@ export interface ResolvedTheme {
55
55
  // Text colors
56
56
  text: string
57
57
  textMuted: string
58
+ conceal: string
58
59
 
59
60
  // Background colors
60
61
  background: string
@@ -122,6 +123,17 @@ export interface ResolvedTheme {
122
123
  transparent: undefined
123
124
  }
124
125
 
126
+ export interface SyntaxThemeStyle {
127
+ fg: RGBA
128
+ bold?: boolean
129
+ italic?: boolean
130
+ underline?: boolean
131
+ }
132
+
133
+ export interface SyntaxTheme {
134
+ [key: string]: SyntaxThemeStyle
135
+ }
136
+
125
137
  // Note: lucent-orng excluded because it uses transparent backgrounds
126
138
  const DEFAULT_THEMES: Record<string, ThemeJson> = {
127
139
  aura,
@@ -210,6 +222,7 @@ function resolveTheme(
210
222
  // Text
211
223
  text: resolveColorToHex(t.text ?? fallbackText),
212
224
  textMuted: resolveColorToHex(t.textMuted ?? fallbackGray),
225
+ conceal: resolveColorToHex(t.conceal ?? t.textMuted ?? fallbackGray),
213
226
 
214
227
  // Background
215
228
  background: resolveColorToHex(t.background ?? fallbackBg),
@@ -286,6 +299,91 @@ export function getResolvedTheme(
286
299
  return resolveTheme(themeJson, mode)
287
300
  }
288
301
 
302
+ // Full syntax theme with tree-sitter scope names for both code and markdown.
303
+ // Ported from critique's getSyntaxTheme() for consistent rendering.
304
+ export function getSyntaxTheme(
305
+ name: string,
306
+ mode: 'dark' | 'light' = 'dark',
307
+ ): SyntaxTheme {
308
+ const resolved = getResolvedTheme(name, mode)
309
+
310
+ const h = (hex: string): RGBA => {
311
+ return parseColor(hex)
312
+ }
313
+
314
+ return {
315
+ // Default text style
316
+ default: { fg: h(resolved.text) },
317
+
318
+ // Code syntax styles
319
+ keyword: { fg: h(resolved.syntaxKeyword), italic: true },
320
+ 'keyword.import': { fg: h(resolved.syntaxKeyword) },
321
+ 'keyword.return': { fg: h(resolved.syntaxKeyword), italic: true },
322
+ 'keyword.conditional': { fg: h(resolved.syntaxKeyword), italic: true },
323
+ 'keyword.repeat': { fg: h(resolved.syntaxKeyword), italic: true },
324
+ 'keyword.type': { fg: h(resolved.syntaxType), bold: true, italic: true },
325
+ 'keyword.function': { fg: h(resolved.syntaxFunction) },
326
+ 'keyword.operator': { fg: h(resolved.syntaxOperator) },
327
+ 'keyword.modifier': { fg: h(resolved.syntaxKeyword), italic: true },
328
+ 'keyword.exception': { fg: h(resolved.syntaxKeyword), italic: true },
329
+ string: { fg: h(resolved.syntaxString) },
330
+ symbol: { fg: h(resolved.syntaxString) },
331
+ comment: { fg: h(resolved.syntaxComment), italic: true },
332
+ 'comment.documentation': { fg: h(resolved.syntaxComment), italic: true },
333
+ number: { fg: h(resolved.syntaxNumber) },
334
+ boolean: { fg: h(resolved.syntaxNumber) },
335
+ constant: { fg: h(resolved.syntaxNumber) },
336
+ function: { fg: h(resolved.syntaxFunction) },
337
+ 'function.call': { fg: h(resolved.syntaxFunction) },
338
+ 'function.method': { fg: h(resolved.syntaxFunction) },
339
+ 'function.method.call': { fg: h(resolved.syntaxVariable) },
340
+ constructor: { fg: h(resolved.syntaxFunction) },
341
+ type: { fg: h(resolved.syntaxType) },
342
+ module: { fg: h(resolved.syntaxType) },
343
+ class: { fg: h(resolved.syntaxType) },
344
+ operator: { fg: h(resolved.syntaxOperator) },
345
+ variable: { fg: h(resolved.syntaxVariable) },
346
+ 'variable.parameter': { fg: h(resolved.syntaxVariable) },
347
+ 'variable.member': { fg: h(resolved.syntaxFunction) },
348
+ property: { fg: h(resolved.syntaxVariable) },
349
+ parameter: { fg: h(resolved.syntaxVariable) },
350
+ bracket: { fg: h(resolved.syntaxPunctuation) },
351
+ punctuation: { fg: h(resolved.syntaxPunctuation) },
352
+ 'punctuation.bracket': { fg: h(resolved.syntaxPunctuation) },
353
+ 'punctuation.delimiter': { fg: h(resolved.syntaxOperator) },
354
+ 'punctuation.special': { fg: h(resolved.syntaxOperator) },
355
+
356
+ // Markdown styles - tree-sitter scope names for markdown
357
+ 'markup.heading': { fg: h(resolved.markdownHeading), bold: true },
358
+ 'markup.heading.1': { fg: h(resolved.markdownHeading), bold: true },
359
+ 'markup.heading.2': { fg: h(resolved.markdownHeading), bold: true },
360
+ 'markup.heading.3': { fg: h(resolved.markdownHeading), bold: true },
361
+ 'markup.heading.4': { fg: h(resolved.markdownHeading), bold: true },
362
+ 'markup.heading.5': { fg: h(resolved.markdownHeading), bold: true },
363
+ 'markup.heading.6': { fg: h(resolved.markdownHeading), bold: true },
364
+ 'markup.bold': { fg: h(resolved.markdownStrong), bold: true },
365
+ 'markup.strong': { fg: h(resolved.markdownStrong), bold: true },
366
+ 'markup.italic': { fg: h(resolved.markdownEmph), italic: true },
367
+ 'markup.list': { fg: h(resolved.markdownListItem) },
368
+ 'markup.list.checked': { fg: h(resolved.success) },
369
+ 'markup.list.unchecked': { fg: h(resolved.textMuted) },
370
+ 'markup.quote': { fg: h(resolved.markdownBlockQuote), italic: true },
371
+ 'markup.raw': { fg: h(resolved.markdownCode) },
372
+ 'markup.raw.block': { fg: h(resolved.markdownCode) },
373
+ 'markup.raw.inline': { fg: h(resolved.markdownCode) },
374
+ 'markup.link': { fg: h(resolved.markdownLink), underline: true },
375
+ 'markup.link.label': { fg: h(resolved.markdownLinkText), underline: true },
376
+ 'markup.link.url': { fg: h(resolved.markdownLink), underline: true },
377
+ label: { fg: h(resolved.markdownLinkText) },
378
+ spell: { fg: h(resolved.text) },
379
+ nospell: { fg: h(resolved.text) },
380
+ conceal: { fg: h(resolved.conceal) },
381
+ 'string.special': { fg: h(resolved.markdownLink), underline: true },
382
+ 'string.special.url': { fg: h(resolved.markdownLink), underline: true },
383
+ 'string.escape': { fg: h(resolved.syntaxString) },
384
+ }
385
+ }
386
+
289
387
  export const themeNames = Object.keys(DEFAULT_THEMES).sort()
290
388
 
291
389
  export const defaultThemeName = 'termcast'