@inferencesh/ui 0.0.0-stage → 0.1.1
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.
- package/LICENSE +22 -0
- package/README.md +68 -2
- package/components.json +22 -0
- package/package.json +231 -4
- package/src/components/audio-player.tsx +549 -0
- package/src/components/button.tsx +13 -0
- package/src/components/chart.tsx +403 -0
- package/src/components/circuit.tsx +276 -0
- package/src/components/code-block/code-block.tsx +260 -0
- package/src/components/code-block/index.ts +18 -0
- package/src/components/code-block/languages.ts +311 -0
- package/src/components/code-block/styles.ts +39 -0
- package/src/components/code-block/tokenizer.ts +290 -0
- package/src/components/code-block/types.ts +38 -0
- package/src/components/code-block/utils.ts +39 -0
- package/src/components/code.tsx +48 -0
- package/src/components/cookie-consent.tsx +137 -0
- package/src/components/copy-button.tsx +44 -0
- package/src/components/country-dropdown.tsx +188 -0
- package/src/components/glow-loader-fast.tsx +32 -0
- package/src/components/glow-loader-mono.tsx +33 -0
- package/src/components/glow-loader-sm-fast.tsx +32 -0
- package/src/components/glow-loader-sm-mono.tsx +34 -0
- package/src/components/glow-loader-sm.tsx +32 -0
- package/src/components/glow-loader.tsx +32 -0
- package/src/components/glow-logo.tsx +32 -0
- package/src/components/gradient.tsx +80 -0
- package/src/components/index.ts +45 -0
- package/src/components/inline-sidebar.tsx +351 -0
- package/src/components/input-color.tsx +349 -0
- package/src/components/logo-base.tsx +213 -0
- package/src/components/logo-color.tsx +23 -0
- package/src/components/logo-full.tsx +71 -0
- package/src/components/logo-svg.tsx +23 -0
- package/src/components/menu-navigation.tsx +359 -0
- package/src/components/multi-select.tsx +721 -0
- package/src/components/pastel-gradient.tsx +25 -0
- package/src/components/pixel-symmetry.tsx +151 -0
- package/src/components/progress.tsx +28 -0
- package/src/components/scroll-more.tsx +131 -0
- package/src/components/select.tsx +85 -0
- package/src/components/shimmering-text.tsx +108 -0
- package/src/components/sidebar-light.tsx +102 -0
- package/src/components/sonner.tsx +38 -0
- package/src/components/theme-switcher-sidebar.tsx +56 -0
- package/src/components/theme-switcher-simple.tsx +110 -0
- package/src/components/theme-switcher.tsx +47 -0
- package/src/components/toggle-group.tsx +55 -0
- package/src/components/tooltip-button.tsx +91 -0
- package/src/components/tooltip.tsx +71 -0
- package/src/components/tree-view.tsx +499 -0
- package/src/components/unglow-loader.tsx +33 -0
- package/src/components/waveform.tsx +1632 -0
- package/src/components/zoomable-image.tsx +76 -0
- package/src/docs/api-docs.tsx +763 -0
- package/src/docs/callout.tsx +160 -0
- package/src/docs/code-group.tsx +215 -0
- package/src/docs/copy-menu.tsx +143 -0
- package/src/docs/doc-tabs.tsx +84 -0
- package/src/docs/index.ts +16 -0
- package/src/docs/steps.tsx +105 -0
- package/src/docs/table-of-contents.tsx +174 -0
- package/src/examples/guide-markdown.ts +129 -0
- package/src/examples/index.ts +1153 -0
- package/src/global.d.ts +1 -0
- package/src/hooks/use-copy-to-clipboard.ts +36 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/display-name.ts +15 -0
- package/src/lib/example-input.ts +130 -0
- package/src/lib/plans.ts +44 -0
- package/src/lib/safe-content.ts +137 -0
- package/src/lib/schema.ts +320 -0
- package/src/lib/storage.ts +108 -0
- package/src/markdown/docs-markdown-renderer.tsx +518 -0
- package/src/markdown/helpers.ts +95 -0
- package/src/markdown/index.ts +24 -0
- package/src/markdown/markdown-renderer.tsx +232 -0
- package/src/markdown/youtube-embed.tsx +43 -0
- package/src/og/image.ts +312 -0
- package/src/og/index.tsx +430 -0
- package/src/og/query.ts +26 -0
- package/src/shadcn/accordion.tsx +71 -0
- package/src/shadcn/alert-dialog.tsx +187 -0
- package/src/shadcn/alert.tsx +75 -0
- package/src/shadcn/aspect-ratio.tsx +22 -0
- package/src/shadcn/attachment.tsx +207 -0
- package/src/shadcn/avatar.tsx +106 -0
- package/src/shadcn/badge.tsx +51 -0
- package/src/shadcn/breadcrumb.tsx +124 -0
- package/src/shadcn/bubble.tsx +127 -0
- package/src/shadcn/button-group.tsx +87 -0
- package/src/shadcn/button.tsx +57 -0
- package/src/shadcn/calendar.tsx +219 -0
- package/src/shadcn/card.tsx +102 -0
- package/src/shadcn/carousel.tsx +242 -0
- package/src/shadcn/chart.tsx +372 -0
- package/src/shadcn/checkbox.tsx +28 -0
- package/src/shadcn/collapsible.tsx +19 -0
- package/src/shadcn/combobox.tsx +297 -0
- package/src/shadcn/command.tsx +194 -0
- package/src/shadcn/context-menu.tsx +270 -0
- package/src/shadcn/dialog.tsx +158 -0
- package/src/shadcn/direction.tsx +4 -0
- package/src/shadcn/drawer.tsx +227 -0
- package/src/shadcn/dropdown-menu.tsx +265 -0
- package/src/shadcn/empty.tsx +103 -0
- package/src/shadcn/field.tsx +238 -0
- package/src/shadcn/form.tsx +173 -0
- package/src/shadcn/hover-card.tsx +50 -0
- package/src/shadcn/index.ts +53 -0
- package/src/shadcn/input-group.tsx +156 -0
- package/src/shadcn/input-otp.tsx +84 -0
- package/src/shadcn/input.tsx +19 -0
- package/src/shadcn/item.tsx +201 -0
- package/src/shadcn/kbd.tsx +26 -0
- package/src/shadcn/label.tsx +19 -0
- package/src/shadcn/marker.tsx +70 -0
- package/src/shadcn/menubar.tsx +278 -0
- package/src/shadcn/message-scroller.tsx +131 -0
- package/src/shadcn/message.tsx +91 -0
- package/src/shadcn/native-select.tsx +60 -0
- package/src/shadcn/navigation-menu.tsx +167 -0
- package/src/shadcn/pagination.tsx +130 -0
- package/src/shadcn/popover.tsx +87 -0
- package/src/shadcn/progress.tsx +82 -0
- package/src/shadcn/radio-group.tsx +35 -0
- package/src/shadcn/resizable.tsx +49 -0
- package/src/shadcn/scroll-area.tsx +52 -0
- package/src/shadcn/select.tsx +200 -0
- package/src/shadcn/separator.tsx +22 -0
- package/src/shadcn/sheet.tsx +136 -0
- package/src/shadcn/sidebar.tsx +721 -0
- package/src/shadcn/skeleton.tsx +13 -0
- package/src/shadcn/slider.tsx +51 -0
- package/src/shadcn/sonner.tsx +49 -0
- package/src/shadcn/spinner.tsx +10 -0
- package/src/shadcn/switch.tsx +29 -0
- package/src/shadcn/table.tsx +115 -0
- package/src/shadcn/tabs.tsx +79 -0
- package/src/shadcn/textarea.tsx +17 -0
- package/src/shadcn/toggle-group.tsx +89 -0
- package/src/shadcn/toggle.tsx +44 -0
- package/src/shadcn/tooltip.tsx +63 -0
- package/src/theme/config.ts +48 -0
- package/src/theme/css/base.css +92 -0
- package/src/theme/css/bitboy.css +75 -0
- package/src/theme/css/dark.css +47 -0
- package/src/theme/css/designer.css +122 -0
- package/src/theme/css/fonts.css +33 -0
- package/src/theme/css/globals.css +14 -0
- package/src/theme/css/hackintosh.css +57 -0
- package/src/theme/css/llama.css +103 -0
- package/src/theme/css/safelist.css +39 -0
- package/src/theme/css/shadcn.css +77 -0
- package/src/theme/css/theme.css +216 -0
- package/src/theme/embed-theme.ts +146 -0
- package/src/theme/fonts/FiraCode-VF.woff2 +0 -0
- package/src/theme/fonts/Hack-Regular.woff2 +0 -0
- package/src/theme/fonts/LICENSE-FiraCode.txt +93 -0
- package/src/theme/fonts/LICENSE-Hack.txt +45 -0
- package/src/theme/fonts/LICENSE-MonaSans.txt +93 -0
- package/src/theme/fonts/Mona-Sans.woff2 +0 -0
- package/src/theme/index.ts +15 -0
- package/src/utils/cn.ts +3 -0
- 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
|
+
}
|