@inferencesh/ui 0.0.0-stage → 0.1.2

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 (165) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +68 -2
  3. package/components.json +22 -0
  4. package/package.json +231 -4
  5. package/src/components/audio-player.tsx +549 -0
  6. package/src/components/button.tsx +13 -0
  7. package/src/components/chart.tsx +403 -0
  8. package/src/components/circuit.tsx +276 -0
  9. package/src/components/code-block/code-block.tsx +260 -0
  10. package/src/components/code-block/index.ts +18 -0
  11. package/src/components/code-block/languages.ts +311 -0
  12. package/src/components/code-block/styles.ts +39 -0
  13. package/src/components/code-block/tokenizer.ts +290 -0
  14. package/src/components/code-block/types.ts +38 -0
  15. package/src/components/code-block/utils.ts +39 -0
  16. package/src/components/code.tsx +48 -0
  17. package/src/components/cookie-consent.tsx +137 -0
  18. package/src/components/copy-button.tsx +44 -0
  19. package/src/components/country-dropdown.tsx +188 -0
  20. package/src/components/glow-loader-fast.tsx +32 -0
  21. package/src/components/glow-loader-mono.tsx +33 -0
  22. package/src/components/glow-loader-sm-fast.tsx +32 -0
  23. package/src/components/glow-loader-sm-mono.tsx +34 -0
  24. package/src/components/glow-loader-sm.tsx +32 -0
  25. package/src/components/glow-loader.tsx +32 -0
  26. package/src/components/glow-logo.tsx +32 -0
  27. package/src/components/gradient.tsx +80 -0
  28. package/src/components/index.ts +45 -0
  29. package/src/components/inline-sidebar.tsx +351 -0
  30. package/src/components/input-color.tsx +349 -0
  31. package/src/components/logo-base.tsx +213 -0
  32. package/src/components/logo-color.tsx +23 -0
  33. package/src/components/logo-full.tsx +71 -0
  34. package/src/components/logo-svg.tsx +23 -0
  35. package/src/components/menu-navigation.tsx +359 -0
  36. package/src/components/multi-select.tsx +721 -0
  37. package/src/components/pastel-gradient.tsx +25 -0
  38. package/src/components/pixel-symmetry.tsx +151 -0
  39. package/src/components/progress.tsx +28 -0
  40. package/src/components/scroll-more.tsx +131 -0
  41. package/src/components/select.tsx +85 -0
  42. package/src/components/shimmering-text.tsx +108 -0
  43. package/src/components/sidebar-light.tsx +102 -0
  44. package/src/components/sonner.tsx +38 -0
  45. package/src/components/theme-switcher-sidebar.tsx +56 -0
  46. package/src/components/theme-switcher-simple.tsx +110 -0
  47. package/src/components/theme-switcher.tsx +47 -0
  48. package/src/components/toggle-group.tsx +55 -0
  49. package/src/components/tooltip-button.tsx +91 -0
  50. package/src/components/tooltip.tsx +71 -0
  51. package/src/components/tree-view.tsx +499 -0
  52. package/src/components/unglow-loader.tsx +33 -0
  53. package/src/components/waveform.tsx +1632 -0
  54. package/src/components/zoomable-image.tsx +76 -0
  55. package/src/docs/api-docs.tsx +763 -0
  56. package/src/docs/callout.tsx +160 -0
  57. package/src/docs/code-group.tsx +215 -0
  58. package/src/docs/copy-menu.tsx +143 -0
  59. package/src/docs/doc-tabs.tsx +84 -0
  60. package/src/docs/index.ts +16 -0
  61. package/src/docs/steps.tsx +105 -0
  62. package/src/docs/table-of-contents.tsx +174 -0
  63. package/src/examples/guide-markdown.ts +129 -0
  64. package/src/examples/index.ts +1153 -0
  65. package/src/global.d.ts +1 -0
  66. package/src/hooks/use-copy-to-clipboard.ts +36 -0
  67. package/src/hooks/use-mobile.ts +19 -0
  68. package/src/lib/display-name.ts +15 -0
  69. package/src/lib/example-input.ts +130 -0
  70. package/src/lib/plans.ts +44 -0
  71. package/src/lib/safe-content.ts +137 -0
  72. package/src/lib/schema.ts +320 -0
  73. package/src/lib/storage.ts +108 -0
  74. package/src/markdown/docs-markdown-renderer.tsx +518 -0
  75. package/src/markdown/helpers.ts +95 -0
  76. package/src/markdown/index.ts +24 -0
  77. package/src/markdown/markdown-renderer.tsx +232 -0
  78. package/src/markdown/youtube-embed.tsx +43 -0
  79. package/src/og/image.ts +312 -0
  80. package/src/og/index.tsx +430 -0
  81. package/src/og/query.ts +26 -0
  82. package/src/shadcn/accordion.tsx +71 -0
  83. package/src/shadcn/alert-dialog.tsx +187 -0
  84. package/src/shadcn/alert.tsx +75 -0
  85. package/src/shadcn/aspect-ratio.tsx +22 -0
  86. package/src/shadcn/attachment.tsx +207 -0
  87. package/src/shadcn/avatar.tsx +106 -0
  88. package/src/shadcn/badge.tsx +51 -0
  89. package/src/shadcn/breadcrumb.tsx +124 -0
  90. package/src/shadcn/bubble.tsx +127 -0
  91. package/src/shadcn/button-group.tsx +87 -0
  92. package/src/shadcn/button.tsx +57 -0
  93. package/src/shadcn/calendar.tsx +219 -0
  94. package/src/shadcn/card.tsx +102 -0
  95. package/src/shadcn/carousel.tsx +242 -0
  96. package/src/shadcn/chart.tsx +372 -0
  97. package/src/shadcn/checkbox.tsx +28 -0
  98. package/src/shadcn/collapsible.tsx +19 -0
  99. package/src/shadcn/combobox.tsx +297 -0
  100. package/src/shadcn/command.tsx +194 -0
  101. package/src/shadcn/context-menu.tsx +270 -0
  102. package/src/shadcn/dialog.tsx +158 -0
  103. package/src/shadcn/direction.tsx +4 -0
  104. package/src/shadcn/drawer.tsx +227 -0
  105. package/src/shadcn/dropdown-menu.tsx +265 -0
  106. package/src/shadcn/empty.tsx +103 -0
  107. package/src/shadcn/field.tsx +238 -0
  108. package/src/shadcn/form.tsx +173 -0
  109. package/src/shadcn/hover-card.tsx +50 -0
  110. package/src/shadcn/index.ts +53 -0
  111. package/src/shadcn/input-group.tsx +156 -0
  112. package/src/shadcn/input-otp.tsx +84 -0
  113. package/src/shadcn/input.tsx +19 -0
  114. package/src/shadcn/item.tsx +201 -0
  115. package/src/shadcn/kbd.tsx +26 -0
  116. package/src/shadcn/label.tsx +19 -0
  117. package/src/shadcn/marker.tsx +70 -0
  118. package/src/shadcn/menubar.tsx +278 -0
  119. package/src/shadcn/message-scroller.tsx +131 -0
  120. package/src/shadcn/message.tsx +91 -0
  121. package/src/shadcn/native-select.tsx +60 -0
  122. package/src/shadcn/navigation-menu.tsx +167 -0
  123. package/src/shadcn/pagination.tsx +130 -0
  124. package/src/shadcn/popover.tsx +87 -0
  125. package/src/shadcn/progress.tsx +82 -0
  126. package/src/shadcn/radio-group.tsx +35 -0
  127. package/src/shadcn/resizable.tsx +49 -0
  128. package/src/shadcn/scroll-area.tsx +52 -0
  129. package/src/shadcn/select.tsx +200 -0
  130. package/src/shadcn/separator.tsx +22 -0
  131. package/src/shadcn/sheet.tsx +136 -0
  132. package/src/shadcn/sidebar.tsx +721 -0
  133. package/src/shadcn/skeleton.tsx +13 -0
  134. package/src/shadcn/slider.tsx +51 -0
  135. package/src/shadcn/sonner.tsx +49 -0
  136. package/src/shadcn/spinner.tsx +10 -0
  137. package/src/shadcn/switch.tsx +29 -0
  138. package/src/shadcn/table.tsx +115 -0
  139. package/src/shadcn/tabs.tsx +79 -0
  140. package/src/shadcn/textarea.tsx +17 -0
  141. package/src/shadcn/toggle-group.tsx +89 -0
  142. package/src/shadcn/toggle.tsx +44 -0
  143. package/src/shadcn/tooltip.tsx +63 -0
  144. package/src/theme/config.ts +48 -0
  145. package/src/theme/css/base.css +92 -0
  146. package/src/theme/css/bitboy.css +75 -0
  147. package/src/theme/css/dark.css +47 -0
  148. package/src/theme/css/designer.css +122 -0
  149. package/src/theme/css/fonts.css +33 -0
  150. package/src/theme/css/globals.css +14 -0
  151. package/src/theme/css/hackintosh.css +57 -0
  152. package/src/theme/css/llama.css +103 -0
  153. package/src/theme/css/safelist.css +39 -0
  154. package/src/theme/css/shadcn.css +77 -0
  155. package/src/theme/css/theme.css +216 -0
  156. package/src/theme/embed-theme.ts +146 -0
  157. package/src/theme/fonts/FiraCode-VF.woff2 +0 -0
  158. package/src/theme/fonts/Hack-Regular.woff2 +0 -0
  159. package/src/theme/fonts/LICENSE-FiraCode.txt +93 -0
  160. package/src/theme/fonts/LICENSE-Hack.txt +45 -0
  161. package/src/theme/fonts/LICENSE-MonaSans.txt +93 -0
  162. package/src/theme/fonts/Mona-Sans.woff2 +0 -0
  163. package/src/theme/index.ts +15 -0
  164. package/src/utils/cn.ts +3 -0
  165. package/src/utils/index.ts +2 -0
@@ -0,0 +1,260 @@
1
+ 'use client'
2
+
3
+ import { cn } from 'cn'
4
+ import { Check, Copy } from 'lucide-react'
5
+ import React, { memo, useMemo, useState } from 'react'
6
+
7
+ import { tokenize, type TokenizeContext } from './tokenizer'
8
+ import { tokenStyles } from './styles'
9
+ import { normalizeLanguage } from './languages'
10
+ import { getTextContent, splitLines, copyToClipboard } from './utils'
11
+
12
+ // Re-export types for consumers
13
+ export type { Token, TokenType, LanguageDefinition } from './types'
14
+ export { getLanguage, normalizeLanguage } from './languages'
15
+
16
+ /**
17
+ * Tokenize all lines with context passing between lines
18
+ * This handles multiline constructs like template literals
19
+ */
20
+ function tokenizeLines(lines: string[], language: string) {
21
+ const tokenizedLines: { tokens: ReturnType<typeof tokenize>['tokens'] }[] = []
22
+ let context: TokenizeContext = {}
23
+
24
+ for (const line of lines) {
25
+ const result = tokenize(line, language, context)
26
+ tokenizedLines.push({ tokens: result.tokens })
27
+ context = result.context
28
+ }
29
+
30
+ return tokenizedLines
31
+ }
32
+
33
+ /**
34
+ * Renders a single line with pre-computed tokens
35
+ */
36
+ const HighlightedLine = memo(function HighlightedLine({
37
+ tokens,
38
+ }: {
39
+ tokens: ReturnType<typeof tokenize>['tokens']
40
+ }) {
41
+ return (
42
+ <>
43
+ {tokens.map((token, i) =>
44
+ token.type ? (
45
+ <span key={i} className={tokenStyles[token.type]}>
46
+ {token.content}
47
+ </span>
48
+ ) : (
49
+ <span key={i}>{token.content}</span>
50
+ )
51
+ )}
52
+ </>
53
+ )
54
+ })
55
+
56
+ export interface CodeBlockProps {
57
+ children: React.ReactNode
58
+ language?: string
59
+ className?: string
60
+ /** Show line numbers (default: true) */
61
+ showLineNumbers?: boolean
62
+ /** Show copy button (default: true) */
63
+ showCopyButton?: boolean
64
+ /** Show language header (default: true) */
65
+ showHeader?: boolean
66
+ /** Enable syntax highlighting (default: true) */
67
+ enableHighlighting?: boolean
68
+ /** Custom text size class */
69
+ textSize?: string
70
+ }
71
+
72
+ /**
73
+ * Full-featured code block with syntax highlighting, line numbers, and copy button
74
+ */
75
+ export const CodeBlock = memo(function CodeBlock({
76
+ children,
77
+ language,
78
+ className,
79
+ showLineNumbers = true,
80
+ showCopyButton = true,
81
+ showHeader = true,
82
+ enableHighlighting = true,
83
+ textSize,
84
+ }: CodeBlockProps) {
85
+ const [copied, setCopied] = useState(false)
86
+
87
+ const text = getTextContent(children)
88
+ const lines = splitLines(text)
89
+ const normalizedLang = normalizeLanguage(language)
90
+
91
+ // Pre-tokenize all lines with context passing for multiline support
92
+ const tokenizedLines = useMemo(
93
+ () => enableHighlighting ? tokenizeLines(lines, normalizedLang) : null,
94
+ [lines, normalizedLang, enableHighlighting]
95
+ )
96
+
97
+ const handleCopy = async () => {
98
+ const success = await copyToClipboard(text)
99
+ if (success) {
100
+ setCopied(true)
101
+ setTimeout(() => setCopied(false), 1500)
102
+ }
103
+ }
104
+
105
+ return (
106
+ <div
107
+ className={cn(
108
+ 'relative group/codeblock my-6 rounded-lg border border-border overflow-hidden bg-muted/40 dark:bg-muted/20 min-h-0 flex flex-col',
109
+ className
110
+ )}
111
+ >
112
+ {/* Header bar */}
113
+ {showHeader && (
114
+ <div className="flex-none flex items-center justify-between px-2 py-2 border-b border-border/30 bg-muted">
115
+ <span className="text-xs text-zinc-500 font-mono">
116
+ {language || 'code'}
117
+ </span>
118
+ {showCopyButton && (
119
+ <button
120
+ onClick={handleCopy}
121
+ className="cursor-pointer flex items-center gap-1.5 rounded text-xs text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
122
+ aria-label="Copy code"
123
+ >
124
+ {copied ? (
125
+ <>
126
+ <Check className="h-3.5 w-3.5 text-emerald-400" />
127
+ <span>copied!</span>
128
+ </>
129
+ ) : (
130
+ <>
131
+ <Copy className="h-3.5 w-3.5" />
132
+ <span>copy</span>
133
+ </>
134
+ )}
135
+ </button>
136
+ )}
137
+ </div>
138
+ )}
139
+
140
+ {/* Code content */}
141
+ <pre className="flex-1 min-h-0 overflow-auto p-4">
142
+ <code className={cn('grid font-mono', textSize || 'text-xs')}>
143
+ {lines.map((line, i) => (
144
+ <span
145
+ key={i}
146
+ className={cn(
147
+ 'grid gap-6',
148
+ showLineNumbers ? 'grid-cols-[auto_1fr]' : 'grid-cols-1'
149
+ )}
150
+ >
151
+ {showLineNumbers && (
152
+ <span className="text-zinc-600 select-none text-right min-w-[2ch] text-xs leading-6">
153
+ {i + 1}
154
+ </span>
155
+ )}
156
+ <span className="whitespace-pre leading-6">
157
+ {tokenizedLines ? (
158
+ <HighlightedLine tokens={tokenizedLines[i]?.tokens ?? [{ type: null, content: line || ' ' }]} />
159
+ ) : (
160
+ line || ' '
161
+ )}
162
+ </span>
163
+ </span>
164
+ ))}
165
+ </code>
166
+ </pre>
167
+ </div>
168
+ )
169
+ })
170
+
171
+ export interface CompactCodeBlockProps {
172
+ children: React.ReactNode
173
+ className?: string
174
+ textSize?: string
175
+ /** Optional language for syntax highlighting */
176
+ language?: string
177
+ }
178
+
179
+ /**
180
+ * Simpler code block for compact/chat contexts
181
+ * Optional syntax highlighting, minimal styling
182
+ */
183
+ export const CompactCodeBlock = memo(function CompactCodeBlock({
184
+ children,
185
+ className,
186
+ textSize = 'text-xs',
187
+ language,
188
+ }: CompactCodeBlockProps) {
189
+ const [copied, setCopied] = useState(false)
190
+
191
+ const text = getTextContent(children)
192
+ const lines = splitLines(text)
193
+ const normalizedLang = language ? normalizeLanguage(language) : null
194
+
195
+ // Pre-tokenize if language is provided
196
+ const tokenizedLines = useMemo(
197
+ () => normalizedLang ? tokenizeLines(lines, normalizedLang) : null,
198
+ [lines, normalizedLang]
199
+ )
200
+
201
+ const handleCopy = async () => {
202
+ const success = await copyToClipboard(text)
203
+ if (success) {
204
+ setCopied(true)
205
+ setTimeout(() => setCopied(false), 1500)
206
+ }
207
+ }
208
+
209
+ return (
210
+ <div
211
+ className={cn(
212
+ 'relative group/codeblock block bg-muted/30 rounded-lg max-w-full min-w-0',
213
+ className
214
+ )}
215
+ >
216
+ <button
217
+ onClick={handleCopy}
218
+ className="cursor-pointer absolute top-1 right-1 p-1.5 rounded bg-background/80 hover:bg-background border border-border opacity-0 group-hover/codeblock:opacity-100 transition-opacity z-10"
219
+ aria-label="Copy code"
220
+ >
221
+ {copied ? (
222
+ <Check className="h-3.5 w-3.5 text-pink-400" />
223
+ ) : (
224
+ <Copy className="h-3.5 w-3.5 text-muted-foreground" />
225
+ )}
226
+ </button>
227
+ <div className="overflow-y-auto max-h-[inherit] p-2">
228
+ <pre className="overflow-x-auto">
229
+ <code className="grid">
230
+ {lines.map((line, i) => (
231
+ <span key={i} className="grid grid-cols-[auto_1fr] gap-4">
232
+ <span
233
+ className={cn(
234
+ textSize,
235
+ 'text-muted-foreground/30 select-none text-right min-w-[2ch]'
236
+ )}
237
+ >
238
+ {i + 1}
239
+ </span>
240
+ <span
241
+ className={cn(
242
+ textSize,
243
+ 'whitespace-pre-wrap break-all',
244
+ !tokenizedLines && 'text-muted-foreground'
245
+ )}
246
+ >
247
+ {tokenizedLines ? (
248
+ <HighlightedLine tokens={tokenizedLines[i]?.tokens ?? [{ type: null, content: line || ' ' }]} />
249
+ ) : (
250
+ line || ' '
251
+ )}
252
+ </span>
253
+ </span>
254
+ ))}
255
+ </code>
256
+ </pre>
257
+ </div>
258
+ </div>
259
+ )
260
+ })
@@ -0,0 +1,18 @@
1
+ export {
2
+ CodeBlock,
3
+ CompactCodeBlock,
4
+ type CodeBlockProps,
5
+ type CompactCodeBlockProps,
6
+ } from './code-block'
7
+
8
+ // Types
9
+ export type { Token, TokenType, LanguageDefinition } from './types'
10
+
11
+ // Language utilities
12
+ export { getLanguage, normalizeLanguage, languages } from './languages'
13
+
14
+ // Tokenizer (for advanced usage)
15
+ export { tokenize } from './tokenizer'
16
+
17
+ // Styles (for customization)
18
+ export { tokenStyles, getTokenStyle } from './styles'
@@ -0,0 +1,311 @@
1
+ import type { LanguageDefinition } from './types'
2
+
3
+ /**
4
+ * JavaScript/TypeScript/JSX/TSX language definition
5
+ * These all share the same tokenization rules
6
+ */
7
+ export const javascript: LanguageDefinition = {
8
+ name: 'javascript',
9
+ aliases: ['js', 'jsx', 'typescript', 'ts', 'tsx'],
10
+ keywords: {
11
+ import: new Set(['import', 'export', 'from', 'as', 'default']),
12
+ declaration: new Set([
13
+ 'const', 'let', 'var', 'function', 'class', 'extends',
14
+ 'static', 'get', 'set', 'async',
15
+ // TypeScript-specific
16
+ 'type', 'interface', 'enum', 'namespace', 'module',
17
+ 'declare', 'abstract', 'implements', 'readonly', 'private',
18
+ 'protected', 'public', 'override',
19
+ ]),
20
+ control: new Set([
21
+ 'if', 'else', 'for', 'while', 'do', 'switch', 'case',
22
+ 'break', 'continue', 'return', 'try', 'catch', 'finally',
23
+ 'throw', 'await', 'yield', 'of', 'in',
24
+ ]),
25
+ value: new Set(['true', 'false', 'null', 'undefined']),
26
+ other: new Set([
27
+ 'new', 'delete', 'typeof', 'instanceof', 'void', 'this',
28
+ 'super', 'satisfies', 'keyof', 'infer',
29
+ ]),
30
+ type: new Set([
31
+ 'string', 'number', 'boolean', 'object', 'symbol', 'bigint',
32
+ 'any', 'unknown', 'never', 'void',
33
+ ]),
34
+ },
35
+ patterns: {
36
+ comment: [
37
+ /^\/\/[^\n]*/, // single line
38
+ /^\/\*[\s\S]*?\*\//, // multi line
39
+ ],
40
+ string: [
41
+ /^"(?:[^"\\]|\\.)*"/, // double quotes
42
+ /^'(?:[^'\\]|\\.)*'/, // single quotes
43
+ /^`(?:[^`\\]|\\.)*`/, // template literals
44
+ ],
45
+ },
46
+ }
47
+
48
+ /**
49
+ * Python language definition
50
+ */
51
+ export const python: LanguageDefinition = {
52
+ name: 'python',
53
+ aliases: ['py'],
54
+ keywords: {
55
+ import: new Set(['import', 'from', 'as']),
56
+ declaration: new Set(['def', 'class', 'lambda', 'global', 'nonlocal']),
57
+ control: new Set([
58
+ 'if', 'elif', 'else', 'for', 'while', 'break', 'continue',
59
+ 'return', 'try', 'except', 'finally', 'raise', 'with',
60
+ 'yield', 'pass', 'assert', 'match', 'case',
61
+ ]),
62
+ value: new Set(['True', 'False', 'None']),
63
+ other: new Set(['and', 'or', 'not', 'in', 'is', 'del', 'print', 'self', 'cls']),
64
+ },
65
+ patterns: {
66
+ comment: [
67
+ /^#[^\n]*/,
68
+ ],
69
+ string: [
70
+ /^(?:f|r|b|fr|rf|br|rb)?"""[\s\S]*?"""/, // triple double
71
+ /^(?:f|r|b|fr|rf|br|rb)?'''[\s\S]*?'''/, // triple single
72
+ /^(?:f|r|b)?"(?:[^"\\]|\\.)*"/, // double
73
+ /^(?:f|r|b)?'(?:[^'\\]|\\.)*'/, // single
74
+ ],
75
+ },
76
+ }
77
+
78
+ /**
79
+ * Bash/Shell language definition
80
+ */
81
+ export const bash: LanguageDefinition = {
82
+ name: 'bash',
83
+ aliases: ['sh', 'shell', 'zsh'],
84
+ keywords: {
85
+ control: new Set([
86
+ 'if', 'then', 'else', 'elif', 'fi', 'for', 'while', 'do',
87
+ 'done', 'case', 'esac', 'function', 'return', 'exit',
88
+ 'select', 'until', 'in',
89
+ ]),
90
+ other: new Set([
91
+ 'echo', 'export', 'source', 'alias', 'unalias', 'cd', 'pwd',
92
+ 'ls', 'cat', 'grep', 'sed', 'awk', 'curl', 'wget', 'chmod',
93
+ 'chown', 'mkdir', 'rm', 'cp', 'mv', 'touch', 'find', 'xargs',
94
+ 'npm', 'npx', 'yarn', 'pnpm', 'node', 'python', 'pip', 'git',
95
+ 'docker', 'sudo', 'apt', 'brew', 'make', 'cargo', 'go',
96
+ ]),
97
+ },
98
+ patterns: {
99
+ comment: [
100
+ /^#[^\n]*/,
101
+ ],
102
+ string: [
103
+ /^"(?:[^"\\]|\\.)*"/,
104
+ /^'(?:[^'\\]|\\.)*'/,
105
+ ],
106
+ },
107
+ }
108
+
109
+ /**
110
+ * JSON language definition
111
+ */
112
+ export const json: LanguageDefinition = {
113
+ name: 'json',
114
+ aliases: ['jsonc'],
115
+ keywords: {
116
+ value: new Set(['true', 'false', 'null']),
117
+ },
118
+ patterns: {
119
+ string: [
120
+ /^"(?:[^"\\]|\\.)*"/,
121
+ ],
122
+ },
123
+ }
124
+
125
+ /**
126
+ * CSS language definition
127
+ */
128
+ export const css: LanguageDefinition = {
129
+ name: 'css',
130
+ aliases: ['scss', 'sass', 'less'],
131
+ keywords: {
132
+ other: new Set([
133
+ 'important', 'inherit', 'initial', 'unset', 'none', 'auto',
134
+ ]),
135
+ },
136
+ patterns: {
137
+ comment: [
138
+ /^\/\*[\s\S]*?\*\//,
139
+ ],
140
+ string: [
141
+ /^"(?:[^"\\]|\\.)*"/,
142
+ /^'(?:[^'\\]|\\.)*'/,
143
+ ],
144
+ },
145
+ }
146
+
147
+ /**
148
+ * HTML/XML language definition
149
+ */
150
+ export const html: LanguageDefinition = {
151
+ name: 'html',
152
+ aliases: ['xml', 'svg', 'htm'],
153
+ keywords: {},
154
+ patterns: {
155
+ comment: [
156
+ /^<!--[\s\S]*?-->/,
157
+ ],
158
+ string: [
159
+ /^"(?:[^"\\]|\\.)*"/,
160
+ /^'(?:[^'\\]|\\.)*'/,
161
+ ],
162
+ },
163
+ }
164
+
165
+ /**
166
+ * Go language definition
167
+ */
168
+ export const go: LanguageDefinition = {
169
+ name: 'go',
170
+ aliases: ['golang'],
171
+ keywords: {
172
+ import: new Set(['import', 'package']),
173
+ declaration: new Set(['func', 'var', 'const', 'type', 'struct', 'interface', 'map', 'chan']),
174
+ control: new Set([
175
+ 'if', 'else', 'for', 'range', 'switch', 'case', 'default',
176
+ 'break', 'continue', 'return', 'goto', 'fallthrough', 'defer',
177
+ 'go', 'select',
178
+ ]),
179
+ value: new Set(['true', 'false', 'nil', 'iota']),
180
+ other: new Set(['make', 'new', 'len', 'cap', 'append', 'copy', 'delete', 'panic', 'recover']),
181
+ },
182
+ patterns: {
183
+ comment: [
184
+ /^\/\/[^\n]*/,
185
+ /^\/\*[\s\S]*?\*\//,
186
+ ],
187
+ string: [
188
+ /^"(?:[^"\\]|\\.)*"/,
189
+ /^'(?:[^'\\]|\\.)*'/,
190
+ /^`[^`]*`/, // raw strings
191
+ ],
192
+ },
193
+ }
194
+
195
+ /**
196
+ * Rust language definition
197
+ */
198
+ export const rust: LanguageDefinition = {
199
+ name: 'rust',
200
+ aliases: ['rs'],
201
+ keywords: {
202
+ import: new Set(['use', 'mod', 'crate', 'extern', 'as', 'pub']),
203
+ declaration: new Set([
204
+ 'fn', 'let', 'mut', 'const', 'static', 'struct', 'enum',
205
+ 'trait', 'impl', 'type', 'where',
206
+ ]),
207
+ control: new Set([
208
+ 'if', 'else', 'for', 'while', 'loop', 'match', 'break',
209
+ 'continue', 'return', 'async', 'await', 'move',
210
+ ]),
211
+ value: new Set(['true', 'false', 'self', 'Self', 'None', 'Some', 'Ok', 'Err']),
212
+ other: new Set(['unsafe', 'ref', 'dyn', 'macro_rules']),
213
+ },
214
+ patterns: {
215
+ comment: [
216
+ /^\/\/[^\n]*/,
217
+ /^\/\*[\s\S]*?\*\//,
218
+ ],
219
+ string: [
220
+ /^"(?:[^"\\]|\\.)*"/,
221
+ /^r#*"[\s\S]*?"#*/, // raw strings
222
+ /^'(?:[^'\\]|\\.)*'/,
223
+ ],
224
+ },
225
+ }
226
+
227
+ /**
228
+ * Swift language definition
229
+ */
230
+ export const swift: LanguageDefinition = {
231
+ name: 'swift',
232
+ aliases: [],
233
+ keywords: {
234
+ import: new Set(['import']),
235
+ declaration: new Set([
236
+ 'let', 'var', 'func', 'class', 'struct', 'enum', 'protocol', 'extension',
237
+ 'actor', 'init', 'deinit', 'typealias', 'associatedtype', 'static',
238
+ 'final', 'private', 'fileprivate', 'internal', 'public', 'open',
239
+ 'mutating', 'nonmutating', 'override', 'lazy', 'weak', 'unowned',
240
+ 'async', 'throws', 'rethrows', 'some', 'any', 'inout', 'nonisolated',
241
+ ]),
242
+ control: new Set([
243
+ 'if', 'else', 'guard', 'for', 'in', 'while', 'repeat', 'switch', 'case',
244
+ 'default', 'break', 'continue', 'return', 'do', 'try', 'catch', 'throw',
245
+ 'await', 'defer', 'where', 'fallthrough',
246
+ ]),
247
+ value: new Set(['true', 'false', 'nil', 'self', 'Self', 'super']),
248
+ other: new Set(['as', 'is']),
249
+ type: new Set([
250
+ 'String', 'Int', 'Double', 'Float', 'Bool', 'Data', 'URL', 'Void',
251
+ 'Any', 'Error', 'Task',
252
+ ]),
253
+ },
254
+ patterns: {
255
+ comment: [
256
+ /^\/\/[^\n]*/,
257
+ /^\/\*[\s\S]*?\*\//,
258
+ ],
259
+ string: [
260
+ /^"""[\s\S]*?"""/, // multi-line
261
+ /^#+"[\s\S]*?"#+/, // raw strings
262
+ /^"(?:[^"\\\n]|\\.)*"/, // double quotes
263
+ ],
264
+ },
265
+ }
266
+
267
+ /**
268
+ * All supported languages
269
+ */
270
+ export const languages: Record<string, LanguageDefinition> = {
271
+ javascript,
272
+ python,
273
+ bash,
274
+ json,
275
+ css,
276
+ html,
277
+ go,
278
+ rust,
279
+ swift,
280
+ }
281
+
282
+ /**
283
+ * Get language definition by name or alias
284
+ */
285
+ export function getLanguage(name: string): LanguageDefinition | null {
286
+ const normalized = name.toLowerCase()
287
+
288
+ // Direct match
289
+ if (languages[normalized]) {
290
+ return languages[normalized]
291
+ }
292
+
293
+ // Check aliases
294
+ for (const lang of Object.values(languages)) {
295
+ if (lang.aliases.includes(normalized)) {
296
+ return lang
297
+ }
298
+ }
299
+
300
+ return null
301
+ }
302
+
303
+ /**
304
+ * Normalize language name to canonical form
305
+ */
306
+ export function normalizeLanguage(language?: string): string {
307
+ if (!language) return 'text'
308
+
309
+ const lang = getLanguage(language)
310
+ return lang?.name ?? 'text'
311
+ }
@@ -0,0 +1,39 @@
1
+ import type { TokenType } from './types'
2
+
3
+ /**
4
+ * Tailwind classes for each token type
5
+ * Light mode uses *-600/*-700 for contrast, dark mode uses *-400
6
+ */
7
+ export const tokenStyles: Record<TokenType, string> = {
8
+ comment: 'text-zinc-400 dark:text-zinc-500 italic',
9
+
10
+ string: 'text-stone-500 dark:text-stone-400',
11
+
12
+ 'keyword-import': 'text-pink-600 dark:text-pink-400 font-medium',
13
+ 'keyword-declaration': 'text-violet-600 dark:text-violet-400 font-medium',
14
+ 'keyword-control': 'text-blue-600 dark:text-blue-400 font-medium',
15
+ 'keyword-value': 'text-cyan-600 dark:text-cyan-400',
16
+ 'keyword-other': 'text-violet-600 dark:text-violet-400',
17
+
18
+ type: 'text-cyan-600 dark:text-cyan-400',
19
+
20
+ number: 'text-emerald-600 dark:text-emerald-400',
21
+
22
+ function: 'text-green-600 dark:text-green-400',
23
+
24
+ property: 'text-blue-600 dark:text-blue-400',
25
+
26
+ operator: 'text-zinc-500 dark:text-zinc-400',
27
+ punctuation: 'text-zinc-500 dark:text-zinc-400',
28
+
29
+ tag: 'text-red-600 dark:text-red-400',
30
+ attribute: 'text-orange-600 dark:text-orange-300',
31
+ }
32
+
33
+ /**
34
+ * Get the CSS class for a token type
35
+ */
36
+ export function getTokenStyle(type: TokenType | null): string | null {
37
+ if (!type) return null
38
+ return tokenStyles[type] ?? null
39
+ }