@particle-academy/fancy-code 0.4.2 → 0.4.4
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/README.md +24 -371
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/docs/CodeEditor.md +208 -0
- package/docs/languages.md +65 -0
- package/docs/themes.md +88 -0
- package/package.json +68 -61
package/README.md
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
# @particle-academy/fancy-code
|
|
2
2
|
|
|
3
|
-
Lightweight embedded code editor with syntax highlighting, custom toolbar buttons, and extensible language
|
|
3
|
+
Lightweight embedded code editor with syntax highlighting, custom toolbar buttons, and extensible language and theme registries. Part of the `@particle-academy` component ecosystem.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
# npm
|
|
9
8
|
npm install @particle-academy/fancy-code
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
pnpm add @particle-academy/fancy-code
|
|
13
|
-
|
|
14
|
-
# yarn
|
|
15
|
-
yarn add @particle-academy/fancy-code
|
|
9
|
+
# or: pnpm add @particle-academy/fancy-code
|
|
10
|
+
# or: yarn add @particle-academy/fancy-code
|
|
16
11
|
```
|
|
17
12
|
|
|
18
13
|
**Peer dependencies:** `react >= 18`, `react-dom >= 18`, `@particle-academy/react-fancy >= 1.5`
|
|
19
14
|
|
|
20
|
-
##
|
|
15
|
+
## Setup
|
|
21
16
|
|
|
22
|
-
Add the
|
|
17
|
+
Add the package styles to your main CSS (Tailwind v4 scans via `@source`):
|
|
23
18
|
|
|
24
19
|
```css
|
|
25
20
|
@import "tailwindcss";
|
|
@@ -27,22 +22,16 @@ Add the `@source` directive to your main CSS file so Tailwind v4 scans the compo
|
|
|
27
22
|
@source "../node_modules/@particle-academy/fancy-code/dist/**/*.js";
|
|
28
23
|
```
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
## Quick Start
|
|
31
26
|
|
|
32
27
|
```tsx
|
|
33
28
|
import { CodeEditor } from "@particle-academy/fancy-code";
|
|
34
|
-
import "@particle-academy/fancy-code/styles.css";
|
|
35
29
|
|
|
36
30
|
function App() {
|
|
37
31
|
const [code, setCode] = useState('console.log("Hello");');
|
|
38
32
|
|
|
39
33
|
return (
|
|
40
|
-
<CodeEditor
|
|
41
|
-
value={code}
|
|
42
|
-
onChange={setCode}
|
|
43
|
-
language="javascript"
|
|
44
|
-
className="rounded-xl border border-zinc-200 dark:border-zinc-700"
|
|
45
|
-
>
|
|
34
|
+
<CodeEditor value={code} onChange={setCode} language="javascript">
|
|
46
35
|
<CodeEditor.Toolbar />
|
|
47
36
|
<CodeEditor.Panel />
|
|
48
37
|
<CodeEditor.StatusBar />
|
|
@@ -51,7 +40,15 @@ function App() {
|
|
|
51
40
|
}
|
|
52
41
|
```
|
|
53
42
|
|
|
54
|
-
The component
|
|
43
|
+
The component ships with no default border or rounding — add your own via `className` for standalone use, or embed directly into IDE layouts without visual conflicts.
|
|
44
|
+
|
|
45
|
+
## Documentation
|
|
46
|
+
|
|
47
|
+
| Topic | Doc |
|
|
48
|
+
|-------|-----|
|
|
49
|
+
| Full component API (props, sub-components, `useCodeEditor` hook) | [docs/CodeEditor.md](./docs/CodeEditor.md) |
|
|
50
|
+
| Built-in languages + registering custom ones | [docs/languages.md](./docs/languages.md) |
|
|
51
|
+
| Built-in themes + custom theme registration | [docs/themes.md](./docs/themes.md) |
|
|
55
52
|
|
|
56
53
|
## Commands
|
|
57
54
|
|
|
@@ -62,358 +59,14 @@ pnpm --filter @particle-academy/fancy-code lint # Type-check (tsc --noEmit)
|
|
|
62
59
|
pnpm --filter @particle-academy/fancy-code clean # Remove dist/
|
|
63
60
|
```
|
|
64
61
|
|
|
65
|
-
##
|
|
66
|
-
|
|
67
|
-
### Compound Components
|
|
68
|
-
|
|
69
|
-
| Component | Description |
|
|
70
|
-
|-----------|-------------|
|
|
71
|
-
| `CodeEditor` | Root wrapper — context provider, state management |
|
|
72
|
-
| `CodeEditor.Toolbar` | Action bar with default buttons or custom children |
|
|
73
|
-
| `CodeEditor.Toolbar.Separator` | Vertical divider between toolbar groups |
|
|
74
|
-
| `CodeEditor.Panel` | Code editing surface |
|
|
75
|
-
| `CodeEditor.StatusBar` | Cursor position, language, tab size display |
|
|
76
|
-
|
|
77
|
-
### CodeEditor Props
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
interface CodeEditorProps {
|
|
81
|
-
children: ReactNode;
|
|
82
|
-
className?: string;
|
|
83
|
-
value?: string; // Controlled value
|
|
84
|
-
defaultValue?: string; // Uncontrolled initial value
|
|
85
|
-
onChange?: (value: string) => void;
|
|
86
|
-
language?: string; // Language name or alias (default: "javascript")
|
|
87
|
-
onLanguageChange?: (lang: string) => void;
|
|
88
|
-
theme?: string; // "light" | "dark" | "auto" | custom name (default: "auto")
|
|
89
|
-
readOnly?: boolean; // Default: false
|
|
90
|
-
lineNumbers?: boolean; // Default: true
|
|
91
|
-
wordWrap?: boolean; // Default: false
|
|
92
|
-
tabSize?: number; // Default: 2
|
|
93
|
-
placeholder?: string;
|
|
94
|
-
minHeight?: number; // Minimum height in px
|
|
95
|
-
maxHeight?: number; // Max height before scrolling
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### useCodeEditor Hook
|
|
100
|
-
|
|
101
|
-
Access the editor context from custom toolbar buttons:
|
|
102
|
-
|
|
103
|
-
```tsx
|
|
104
|
-
import { useCodeEditor } from "@particle-academy/fancy-code";
|
|
105
|
-
import { Action } from "@particle-academy/react-fancy";
|
|
106
|
-
|
|
107
|
-
function RunButton() {
|
|
108
|
-
const { getValue } = useCodeEditor();
|
|
109
|
-
return <Action size="xs" onClick={() => run(getValue())}>Run</Action>;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
<CodeEditor value={code} onChange={setCode} language="javascript">
|
|
113
|
-
<CodeEditor.Toolbar>
|
|
114
|
-
<RunButton />
|
|
115
|
-
<CodeEditor.Toolbar.Separator />
|
|
116
|
-
</CodeEditor.Toolbar>
|
|
117
|
-
<CodeEditor.Panel />
|
|
118
|
-
</CodeEditor>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Context value:**
|
|
122
|
-
|
|
123
|
-
| Method / Property | Description |
|
|
124
|
-
|-------------------|-------------|
|
|
125
|
-
| `getValue()` | Get current document text |
|
|
126
|
-
| `getSelection()` | Get currently selected text |
|
|
127
|
-
| `setValue(text)` | Replace entire document |
|
|
128
|
-
| `replaceSelection(text)` | Replace current selection |
|
|
129
|
-
| `focus()` | Focus the editor |
|
|
130
|
-
| `language` | Current language name |
|
|
131
|
-
| `setLanguage(name)` | Switch active language |
|
|
132
|
-
| `theme` | Current theme name |
|
|
133
|
-
| `readOnly` | Whether the editor is read-only |
|
|
134
|
-
| `lineNumbers` | Whether line numbers are shown |
|
|
135
|
-
| `wordWrap` | Whether word wrap is enabled |
|
|
136
|
-
| `tabSize` | Current tab size |
|
|
137
|
-
| `toggleWordWrap()` | Toggle word wrap on/off |
|
|
138
|
-
| `toggleLineNumbers()` | Toggle line numbers on/off |
|
|
139
|
-
| `copyToClipboard()` | Copy entire document to clipboard |
|
|
140
|
-
| `cursorPosition` | `{ line, col }` — current cursor position |
|
|
141
|
-
| `selectionLength` | Length of current selection (0 if none) |
|
|
142
|
-
|
|
143
|
-
## Examples
|
|
144
|
-
|
|
145
|
-
### Basic Editor
|
|
146
|
-
|
|
147
|
-
```tsx
|
|
148
|
-
<CodeEditor value={code} onChange={setCode} language="typescript">
|
|
149
|
-
<CodeEditor.Toolbar />
|
|
150
|
-
<CodeEditor.Panel />
|
|
151
|
-
<CodeEditor.StatusBar />
|
|
152
|
-
</CodeEditor>
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Read-Only Code Display
|
|
156
|
-
|
|
157
|
-
```tsx
|
|
158
|
-
<CodeEditor value={snippet} language="php" readOnly>
|
|
159
|
-
<CodeEditor.Panel />
|
|
160
|
-
</CodeEditor>
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### Custom Toolbar
|
|
164
|
-
|
|
165
|
-
```tsx
|
|
166
|
-
<CodeEditor value={code} onChange={setCode} language="javascript">
|
|
167
|
-
<CodeEditor.Toolbar>
|
|
168
|
-
<RunButton />
|
|
169
|
-
<CodeEditor.Toolbar.Separator />
|
|
170
|
-
<FormatButton />
|
|
171
|
-
<CopyButton />
|
|
172
|
-
</CodeEditor.Toolbar>
|
|
173
|
-
<CodeEditor.Panel />
|
|
174
|
-
<CodeEditor.StatusBar />
|
|
175
|
-
</CodeEditor>
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Height Constraints
|
|
179
|
-
|
|
180
|
-
```tsx
|
|
181
|
-
<CodeEditor value={longCode} language="javascript" maxHeight={400} minHeight={200}>
|
|
182
|
-
<CodeEditor.Toolbar />
|
|
183
|
-
<CodeEditor.Panel />
|
|
184
|
-
<CodeEditor.StatusBar />
|
|
185
|
-
</CodeEditor>
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Minimal (No Toolbar, No StatusBar)
|
|
189
|
-
|
|
190
|
-
```tsx
|
|
191
|
-
<CodeEditor value={snippet} language="javascript" readOnly lineNumbers={false}>
|
|
192
|
-
<CodeEditor.Panel />
|
|
193
|
-
</CodeEditor>
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## Built-in Languages
|
|
197
|
-
|
|
198
|
-
| Language | Aliases |
|
|
199
|
-
|----------|---------|
|
|
200
|
-
| JavaScript | `js`, `javascript`, `jsx` |
|
|
201
|
-
| TypeScript | `ts`, `typescript`, `tsx` |
|
|
202
|
-
| HTML | `html`, `htm` |
|
|
203
|
-
| PHP | `php` |
|
|
204
|
-
| Python | `py`, `python` |
|
|
205
|
-
| Go | `go`, `golang` |
|
|
206
|
-
|
|
207
|
-
## Custom Language Registration
|
|
208
|
-
|
|
209
|
-
Add languages beyond the built-ins using `registerLanguage`:
|
|
210
|
-
|
|
211
|
-
```tsx
|
|
212
|
-
import { registerLanguage } from "@particle-academy/fancy-code";
|
|
213
|
-
import type { Tokenizer } from "@particle-academy/fancy-code";
|
|
214
|
-
|
|
215
|
-
const tokenizeRuby: Tokenizer = (source) => {
|
|
216
|
-
const tokens = [];
|
|
217
|
-
// Your regex-based tokenizer logic here
|
|
218
|
-
// See src/engine/tokenizers/javascript.ts for a full example
|
|
219
|
-
return tokens;
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
registerLanguage({
|
|
223
|
-
name: "Ruby",
|
|
224
|
-
aliases: ["rb", "ruby"],
|
|
225
|
-
tokenize: tokenizeRuby,
|
|
226
|
-
});
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
Then use it:
|
|
230
|
-
|
|
231
|
-
```tsx
|
|
232
|
-
<CodeEditor language="python" ... />
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## Built-in Color Schemes
|
|
236
|
-
|
|
237
|
-
| Theme | Description |
|
|
238
|
-
|-------|-------------|
|
|
239
|
-
| `light` | White background, blue/purple/green token colors |
|
|
240
|
-
| `dark` | Zinc-900 background, pastel token colors |
|
|
241
|
-
| `auto` | Automatically selects light/dark based on `prefers-color-scheme` (default) |
|
|
242
|
-
|
|
243
|
-
## Custom Theme Registration
|
|
244
|
-
|
|
245
|
-
Create custom syntax highlighting color schemes using `registerTheme`:
|
|
246
|
-
|
|
247
|
-
```tsx
|
|
248
|
-
import { registerTheme } from "@particle-academy/fancy-code";
|
|
249
|
-
|
|
250
|
-
registerTheme({
|
|
251
|
-
name: "monokai",
|
|
252
|
-
variant: "dark",
|
|
253
|
-
editorTheme: /* editor chrome styles (background, gutter, cursor, selection) */,
|
|
254
|
-
highlightStyle: /* token color definitions (keywords, strings, comments, etc.) */,
|
|
255
|
-
});
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
Then use it:
|
|
259
|
-
|
|
260
|
-
```tsx
|
|
261
|
-
<CodeEditor theme="monokai" ... />
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**Theme definition:**
|
|
265
|
-
|
|
266
|
-
```ts
|
|
267
|
-
interface ThemeDefinition {
|
|
268
|
-
name: string; // Unique theme name
|
|
269
|
-
variant: "light" | "dark"; // Light or dark base
|
|
270
|
-
editorTheme: Extension; // Editor chrome styles (gutter, cursor, selection)
|
|
271
|
-
highlightStyle: HighlightStyle; // Syntax token color definitions
|
|
272
|
-
}
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## Built-in Features
|
|
276
|
-
|
|
277
|
-
- Syntax highlighting with language-aware tokenization
|
|
278
|
-
- Line numbers with active line gutter highlight
|
|
279
|
-
- Active line highlighting
|
|
280
|
-
- Bracket matching and auto-close
|
|
281
|
-
- Code folding with fold gutter
|
|
282
|
-
- Search and replace (Ctrl+F / Cmd+F)
|
|
283
|
-
- Autocompletion
|
|
284
|
-
- History (undo/redo)
|
|
285
|
-
- Indent on input
|
|
286
|
-
- Selection highlighting
|
|
287
|
-
- Tab key indentation
|
|
288
|
-
|
|
289
|
-
All features are reconfigurable at runtime — switching languages, themes, line numbers, word wrap, tab size, and read-only mode happens without recreating the editor.
|
|
290
|
-
|
|
291
|
-
## Customization
|
|
292
|
-
|
|
293
|
-
All components render `data-fancy-code-*` attributes on their root elements for external CSS targeting:
|
|
294
|
-
|
|
295
|
-
| Attribute | Element |
|
|
296
|
-
|-----------|---------|
|
|
297
|
-
| `data-fancy-code-editor` | Root wrapper |
|
|
298
|
-
| `data-fancy-code-toolbar` | Toolbar bar |
|
|
299
|
-
| `data-fancy-code-toolbar-separator` | Toolbar separator |
|
|
300
|
-
| `data-fancy-code-panel` | Editing surface |
|
|
301
|
-
| `data-fancy-code-statusbar` | Status bar |
|
|
302
|
-
|
|
303
|
-
## Architecture
|
|
304
|
-
|
|
305
|
-
### Directory Layout
|
|
306
|
-
|
|
307
|
-
```
|
|
308
|
-
src/
|
|
309
|
-
├── components/
|
|
310
|
-
│ └── CodeEditor/
|
|
311
|
-
│ ├── CodeEditor.tsx # Root compound component + context
|
|
312
|
-
│ ├── CodeEditor.types.ts # All prop/context types
|
|
313
|
-
│ ├── CodeEditor.context.ts # React context + useCodeEditor hook
|
|
314
|
-
│ ├── CodeEditorPanel.tsx # Editing surface
|
|
315
|
-
│ ├── CodeEditorToolbar.tsx # Default toolbar + custom children
|
|
316
|
-
│ ├── CodeEditorToolbarSeparator.tsx
|
|
317
|
-
│ ├── CodeEditorStatusBar.tsx # Cursor/language/tab display
|
|
318
|
-
│ └── index.ts
|
|
319
|
-
├── hooks/
|
|
320
|
-
│ ├── use-editor-engine.ts # Core editor lifecycle
|
|
321
|
-
│ └── use-dark-mode.ts # Reactive prefers-color-scheme
|
|
322
|
-
├── languages/
|
|
323
|
-
│ ├── registry.ts # Global language registry
|
|
324
|
-
│ ├── builtin.ts # JS, TS, HTML, PHP registrations
|
|
325
|
-
│ ├── types.ts # LanguageDefinition type
|
|
326
|
-
│ └── index.ts
|
|
327
|
-
├── themes/
|
|
328
|
-
│ ├── registry.ts # Global theme registry
|
|
329
|
-
│ ├── light.ts # Built-in light color scheme
|
|
330
|
-
│ ├── dark.ts # Built-in dark color scheme
|
|
331
|
-
│ ├── types.ts # ThemeDefinition type
|
|
332
|
-
│ └── index.ts
|
|
333
|
-
├── styles.css # Base structural styles
|
|
334
|
-
└── index.ts # Public API
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### Public Exports
|
|
338
|
-
|
|
339
|
-
```ts
|
|
340
|
-
// Components
|
|
341
|
-
export { CodeEditor, useCodeEditor };
|
|
342
|
-
export type { CodeEditorProps, CodeEditorContextValue, CodeEditorToolbarProps, CodeEditorPanelProps, CodeEditorStatusBarProps };
|
|
343
|
-
|
|
344
|
-
// Language registration
|
|
345
|
-
export { registerLanguage, getLanguage, getRegisteredLanguages };
|
|
346
|
-
export type { LanguageDefinition };
|
|
347
|
-
|
|
348
|
-
// Theme registration
|
|
349
|
-
export { registerTheme, getTheme, getRegisteredThemes };
|
|
350
|
-
export type { ThemeDefinition };
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## Related: TreeNav from react-fancy
|
|
354
|
-
|
|
355
|
-
For IDE-style layouts, pair `CodeEditor` with the `TreeNav` component from `@particle-academy/react-fancy`. TreeNav provides hierarchical file/folder navigation with expand/collapse, selection, and extension-based file icons.
|
|
356
|
-
|
|
357
|
-
```tsx
|
|
358
|
-
import { TreeNav } from "@particle-academy/react-fancy";
|
|
359
|
-
import { CodeEditor } from "@particle-academy/fancy-code";
|
|
360
|
-
|
|
361
|
-
<div className="flex" style={{ height: 600 }}>
|
|
362
|
-
<TreeNav
|
|
363
|
-
nodes={fileTree}
|
|
364
|
-
selectedId={activeFile}
|
|
365
|
-
onSelect={(id, node) => openFile(id, node)}
|
|
366
|
-
/>
|
|
367
|
-
<CodeEditor value={code} onChange={setCode} language={lang}>
|
|
368
|
-
<CodeEditor.Toolbar />
|
|
369
|
-
<CodeEditor.Panel />
|
|
370
|
-
<CodeEditor.StatusBar />
|
|
371
|
-
</CodeEditor>
|
|
372
|
-
</div>
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
See the full IDE demo at `/react-demos/ide` in the monorepo.
|
|
376
|
-
|
|
377
|
-
## Demo Pages
|
|
378
|
-
|
|
379
|
-
The demo page lives in the monorepo at `resources/js/react-demos/pages/CodeEditorDemo.tsx` and is accessible at `/react-demos/code-editor`. It demonstrates:
|
|
380
|
-
|
|
381
|
-
1. Basic editor with default toolbar
|
|
382
|
-
2. Read-only code display
|
|
383
|
-
3. Custom toolbar buttons via `useCodeEditor()`
|
|
384
|
-
4. Language switching (JS, TS, HTML, PHP)
|
|
385
|
-
5. Light vs dark theme showcase
|
|
386
|
-
6. Word wrap and configuration
|
|
387
|
-
7. Minimal mode (no toolbar, no status bar)
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
## Agent Guidelines
|
|
392
|
-
|
|
393
|
-
Guidelines for AI agents (Claude Code, Copilot, etc.) working on this package.
|
|
394
|
-
|
|
395
|
-
### Component Pattern
|
|
396
|
-
|
|
397
|
-
- Uses the same compound component pattern as `@particle-academy/react-fancy`'s Editor: `Object.assign(Root, { Toolbar, Panel, StatusBar })`.
|
|
398
|
-
- Context is provided via `CodeEditorContext` and consumed with `useCodeEditor()`.
|
|
399
|
-
- All root elements have `data-fancy-code-*` attributes for external CSS targeting.
|
|
400
|
-
|
|
401
|
-
### Editor Engine
|
|
402
|
-
|
|
403
|
-
- The `useCodemirror` hook manages the editor lifecycle.
|
|
404
|
-
- Uses compartments for hot-swapping language, theme, line numbers, word wrap, tab size, read-only, placeholder, and height constraints.
|
|
405
|
-
- External value changes are synced via `isExternalUpdate` ref to prevent onChange loops.
|
|
406
|
-
- SSR-safe: the editor is only created inside `useEffect`.
|
|
407
|
-
|
|
408
|
-
### Extension System
|
|
62
|
+
## At a Glance
|
|
409
63
|
|
|
410
|
-
-
|
|
411
|
-
-
|
|
412
|
-
-
|
|
64
|
+
- **5 public exports** — `CodeEditor` (+ `Toolbar`, `Toolbar.Separator`, `Panel`, `StatusBar`) and the `useCodeEditor` hook
|
|
65
|
+
- **6 built-in languages** — JavaScript, TypeScript, HTML, PHP, Python, Go
|
|
66
|
+
- **3 built-in themes** — `light`, `dark`, `auto` (follows `prefers-color-scheme`)
|
|
67
|
+
- **Zero third-party dependencies** — custom engine, no Monaco / CodeMirror / Shiki
|
|
68
|
+
- **IDE-ready** — pairs with `TreeNav` from `@particle-academy/react-fancy` for full IDE layouts; see [docs/CodeEditor.md](./docs/CodeEditor.md) for an example.
|
|
413
69
|
|
|
414
|
-
|
|
70
|
+
## License
|
|
415
71
|
|
|
416
|
-
|
|
417
|
-
- `react`, `react-dom`, and `@particle-academy/react-fancy` are external dependencies.
|
|
418
|
-
- All editor engine packages are **bundled** (not external) so consumers don't need to install them.
|
|
419
|
-
- After any change, verify with `npm run build` from the monorepo root.
|
|
72
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1234,6 +1234,10 @@ registerTheme({
|
|
|
1234
1234
|
function escapeHtml(text) {
|
|
1235
1235
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1236
1236
|
}
|
|
1237
|
+
var COLOR_RE = /^(#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla)\([\d\s,.%/-]+\)|[a-zA-Z]+)$/;
|
|
1238
|
+
function safeColor(value) {
|
|
1239
|
+
return typeof value === "string" && COLOR_RE.test(value.trim()) ? value.trim() : "inherit";
|
|
1240
|
+
}
|
|
1237
1241
|
function highlightCode(source, tokens, colors) {
|
|
1238
1242
|
if (tokens.length === 0) return escapeHtml(source);
|
|
1239
1243
|
const colorMap = {
|
|
@@ -1258,7 +1262,7 @@ function highlightCode(source, tokens, colors) {
|
|
|
1258
1262
|
parts.push(escapeHtml(source.slice(pos, token.start)));
|
|
1259
1263
|
}
|
|
1260
1264
|
const text = escapeHtml(source.slice(token.start, token.end));
|
|
1261
|
-
const color = colorMap[token.type];
|
|
1265
|
+
const color = safeColor(colorMap[token.type]);
|
|
1262
1266
|
const style = token.type === "comment" ? `color:${color};font-style:italic` : `color:${color}`;
|
|
1263
1267
|
parts.push(`<span style="${style}">${text}</span>`);
|
|
1264
1268
|
pos = token.end;
|