@particle-academy/fancy-code 0.8.0 → 0.9.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/dist/index.d.cts CHANGED
@@ -1,34 +1,9 @@
1
1
  import * as react from 'react';
2
2
  import { RefObject, ReactNode, CSSProperties } from 'react';
3
- import { DiffAnnotations, LineChangeType } from '@particle-academy/fancy-file-commons';
4
- export { languageFromFilename } from '@particle-academy/fancy-file-commons';
3
+ import { ThemeColors, DiffAnnotations, LineChangeType, Token, Tokenizer } from '@particle-academy/fancy-file-commons';
4
+ export { ThemeColors, Token, TokenType, Tokenizer, languageFromFilename } from '@particle-academy/fancy-file-commons';
5
5
  import { MediaKind, MediaViewerProps, InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
6
6
 
7
- interface ThemeColors {
8
- background: string;
9
- foreground: string;
10
- gutterBackground: string;
11
- gutterForeground: string;
12
- gutterBorder: string;
13
- activeLineBackground: string;
14
- selectionBackground: string;
15
- cursorColor: string;
16
- diffAdded?: string;
17
- diffModified?: string;
18
- diffRemoved?: string;
19
- keyword: string;
20
- string: string;
21
- comment: string;
22
- number: string;
23
- operator: string;
24
- function: string;
25
- type: string;
26
- tag: string;
27
- attribute: string;
28
- attributeValue: string;
29
- punctuation: string;
30
- variable: string;
31
- }
32
7
  interface ThemeDefinition {
33
8
  /** Unique theme name */
34
9
  name: string;
@@ -407,14 +382,6 @@ declare function diffRemovedColor(colors: ThemeColors): string;
407
382
  */
408
383
  declare function renderMarkdown(markdown: string): string;
409
384
 
410
- type TokenType = "keyword" | "string" | "comment" | "number" | "operator" | "tag" | "attribute" | "attributeValue" | "punctuation" | "function" | "type" | "variable" | "plain";
411
- interface Token {
412
- type: TokenType;
413
- start: number;
414
- end: number;
415
- }
416
- type Tokenizer = (source: string) => Token[];
417
-
418
385
  /**
419
386
  * Minimal, dependency-free Markdown tokenizer for editor highlighting. Line-based
420
387
  * for block constructs (fences, headings, blockquotes, list markers) plus a few
@@ -481,4 +448,4 @@ interface CodeEditorInputTagProps extends Omit<InputTagProps, "adapter"> {
481
448
  */
482
449
  declare function CodeEditorInputTag(props: CodeEditorInputTagProps): react.JSX.Element;
483
450
 
484
- export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ResolveFileKindInput, type ThemeColors, type ThemeDefinition, type Token, type TokenType, type Tokenizer, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
451
+ export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ResolveFileKindInput, type ThemeDefinition, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
package/dist/index.d.ts CHANGED
@@ -1,34 +1,9 @@
1
1
  import * as react from 'react';
2
2
  import { RefObject, ReactNode, CSSProperties } from 'react';
3
- import { DiffAnnotations, LineChangeType } from '@particle-academy/fancy-file-commons';
4
- export { languageFromFilename } from '@particle-academy/fancy-file-commons';
3
+ import { ThemeColors, DiffAnnotations, LineChangeType, Token, Tokenizer } from '@particle-academy/fancy-file-commons';
4
+ export { ThemeColors, Token, TokenType, Tokenizer, languageFromFilename } from '@particle-academy/fancy-file-commons';
5
5
  import { MediaKind, MediaViewerProps, InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
6
6
 
7
- interface ThemeColors {
8
- background: string;
9
- foreground: string;
10
- gutterBackground: string;
11
- gutterForeground: string;
12
- gutterBorder: string;
13
- activeLineBackground: string;
14
- selectionBackground: string;
15
- cursorColor: string;
16
- diffAdded?: string;
17
- diffModified?: string;
18
- diffRemoved?: string;
19
- keyword: string;
20
- string: string;
21
- comment: string;
22
- number: string;
23
- operator: string;
24
- function: string;
25
- type: string;
26
- tag: string;
27
- attribute: string;
28
- attributeValue: string;
29
- punctuation: string;
30
- variable: string;
31
- }
32
7
  interface ThemeDefinition {
33
8
  /** Unique theme name */
34
9
  name: string;
@@ -407,14 +382,6 @@ declare function diffRemovedColor(colors: ThemeColors): string;
407
382
  */
408
383
  declare function renderMarkdown(markdown: string): string;
409
384
 
410
- type TokenType = "keyword" | "string" | "comment" | "number" | "operator" | "tag" | "attribute" | "attributeValue" | "punctuation" | "function" | "type" | "variable" | "plain";
411
- interface Token {
412
- type: TokenType;
413
- start: number;
414
- end: number;
415
- }
416
- type Tokenizer = (source: string) => Token[];
417
-
418
385
  /**
419
386
  * Minimal, dependency-free Markdown tokenizer for editor highlighting. Line-based
420
387
  * for block constructs (fences, headings, blockquotes, list markers) plus a few
@@ -481,4 +448,4 @@ interface CodeEditorInputTagProps extends Omit<InputTagProps, "adapter"> {
481
448
  */
482
449
  declare function CodeEditorInputTag(props: CodeEditorInputTagProps): react.JSX.Element;
483
450
 
484
- export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ResolveFileKindInput, type ThemeColors, type ThemeDefinition, type Token, type TokenType, type Tokenizer, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
451
+ export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ResolveFileKindInput, type ThemeDefinition, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createContext, useMemo, useState, useCallback, useRef, useEffect, useSyncExternalStore, useContext } from 'react';
2
2
  import { cn, useControllableState, resolveMediaType, MediaViewer, textareaAdapter, InputTag } from '@particle-academy/react-fancy';
3
- import { annotateLines, languageFromFilename } from '@particle-academy/fancy-file-commons';
3
+ import { annotateLines, highlightCode, tok, tokenizeHtml, LIGHT_COLORS, DARK_COLORS, languageFromFilename } from '@particle-academy/fancy-file-commons';
4
4
  export { languageFromFilename } from '@particle-academy/fancy-file-commons';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
@@ -42,26 +42,12 @@ function CodeEditorPanel({ className }) {
42
42
  _maxHeight,
43
43
  _minHeight
44
44
  } = useCodeEditor();
45
- if (!engine) return null;
46
- const {
47
- textareaRef,
48
- highlightedHtml,
49
- lineCount,
50
- activeLine,
51
- themeColors,
52
- handleKeyDown,
53
- handleInput,
54
- handleScroll,
55
- handleSelect,
56
- scrollTop,
57
- scrollLeft
58
- } = engine;
59
- const gutterWidth = lineNumbers ? `${Math.max(String(lineCount).length, 2) * 0.75 + 1.5}em` : "0";
60
45
  const lineNumberElements = useMemo(() => {
61
- if (!lineNumbers) return null;
62
- const removed = diffRemovedColor(themeColors);
46
+ if (!engine || !lineNumbers) return null;
47
+ const { themeColors: themeColors2, lineCount: lineCount2, activeLine: activeLine2 } = engine;
48
+ const removed = diffRemovedColor(themeColors2);
63
49
  const lines = [];
64
- for (let i = 1; i <= lineCount; i++) {
50
+ for (let i = 1; i <= lineCount2; i++) {
65
51
  const mark = gutterDiffMark(diffAnnotations, i);
66
52
  lines.push(
67
53
  /* @__PURE__ */ jsxs(
@@ -71,8 +57,8 @@ function CodeEditorPanel({ className }) {
71
57
  "data-diff-deleted-above": mark?.deletedAbove ? "" : void 0,
72
58
  style: {
73
59
  position: "relative",
74
- color: i === activeLine ? themeColors.foreground : themeColors.gutterForeground,
75
- backgroundColor: i === activeLine ? themeColors.activeLineBackground : void 0
60
+ color: i === activeLine2 ? themeColors2.foreground : themeColors2.gutterForeground,
61
+ backgroundColor: i === activeLine2 ? themeColors2.activeLineBackground : void 0
76
62
  },
77
63
  className: "pr-3 text-right text-[13px] leading-[1.5] select-none",
78
64
  children: [
@@ -87,7 +73,7 @@ function CodeEditorPanel({ className }) {
87
73
  bottom: 0,
88
74
  width: 3,
89
75
  borderRadius: 2,
90
- backgroundColor: diffMarkColor(mark.type, themeColors)
76
+ backgroundColor: diffMarkColor(mark.type, themeColors2)
91
77
  }
92
78
  }
93
79
  ),
@@ -108,7 +94,7 @@ function CodeEditorPanel({ className }) {
108
94
  }
109
95
  ),
110
96
  i,
111
- i === lineCount && hasDeletedAtEnd(diffAnnotations) && /* @__PURE__ */ jsx(
97
+ i === lineCount2 && hasDeletedAtEnd(diffAnnotations) && /* @__PURE__ */ jsx(
112
98
  "span",
113
99
  {
114
100
  "aria-hidden": "true",
@@ -132,7 +118,22 @@ function CodeEditorPanel({ className }) {
132
118
  );
133
119
  }
134
120
  return lines;
135
- }, [lineNumbers, lineCount, activeLine, themeColors, diffAnnotations]);
121
+ }, [engine, lineNumbers, diffAnnotations]);
122
+ if (!engine) return null;
123
+ const {
124
+ textareaRef,
125
+ highlightedHtml,
126
+ lineCount,
127
+ activeLine,
128
+ themeColors,
129
+ handleKeyDown,
130
+ handleInput,
131
+ handleScroll,
132
+ handleSelect,
133
+ scrollTop,
134
+ scrollLeft
135
+ } = engine;
136
+ const gutterWidth = lineNumbers ? `${Math.max(String(lineCount).length, 2) * 0.75 + 1.5}em` : "0";
136
137
  const containerStyle = {
137
138
  backgroundColor: themeColors.background,
138
139
  color: themeColors.foreground,
@@ -445,89 +446,6 @@ var tokenizeJavaScript = (source) => {
445
446
  return tokens;
446
447
  };
447
448
 
448
- // src/engine/tokenizers/html.ts
449
- var tokenizeHtml = (source) => {
450
- const tokens = [];
451
- const len = source.length;
452
- let i = 0;
453
- while (i < len) {
454
- if (source[i] === "<" && source.slice(i, i + 4) === "<!--") {
455
- const pos2 = i;
456
- i += 4;
457
- while (i < len && source.slice(i, i + 3) !== "-->") i++;
458
- i += 3;
459
- tokens.push({ type: "comment", start: pos2, end: i });
460
- continue;
461
- }
462
- if (source[i] === "<" && source.slice(i, i + 9).toUpperCase() === "<!DOCTYPE") {
463
- const pos2 = i;
464
- while (i < len && source[i] !== ">") i++;
465
- i++;
466
- tokens.push({ type: "keyword", start: pos2, end: i });
467
- continue;
468
- }
469
- if (source[i] === "<") {
470
- tokens.push({ type: "punctuation", start: i, end: i + 1 });
471
- i++;
472
- if (i < len && source[i] === "/") {
473
- tokens.push({ type: "punctuation", start: i, end: i + 1 });
474
- i++;
475
- }
476
- if (i < len && /[a-zA-Z]/.test(source[i])) {
477
- const pos2 = i;
478
- while (i < len && /[a-zA-Z0-9-]/.test(source[i])) i++;
479
- tokens.push({ type: "tag", start: pos2, end: i });
480
- }
481
- while (i < len && source[i] !== ">" && !(source[i] === "/" && source[i + 1] === ">")) {
482
- if (source[i] === " " || source[i] === " " || source[i] === "\n" || source[i] === "\r") {
483
- i++;
484
- continue;
485
- }
486
- if (/[a-zA-Z_@:]/.test(source[i])) {
487
- const pos2 = i;
488
- while (i < len && /[a-zA-Z0-9_\-:.]/.test(source[i])) i++;
489
- tokens.push({ type: "attribute", start: pos2, end: i });
490
- while (i < len && (source[i] === " " || source[i] === " ")) i++;
491
- if (i < len && source[i] === "=") {
492
- tokens.push({ type: "operator", start: i, end: i + 1 });
493
- i++;
494
- while (i < len && (source[i] === " " || source[i] === " ")) i++;
495
- if (i < len && (source[i] === '"' || source[i] === "'")) {
496
- const quote = source[i];
497
- const pos3 = i;
498
- i++;
499
- while (i < len && source[i] !== quote) i++;
500
- i++;
501
- tokens.push({ type: "attributeValue", start: pos3, end: i });
502
- } else if (i < len && /[^\s>]/.test(source[i])) {
503
- const pos3 = i;
504
- while (i < len && /[^\s>]/.test(source[i])) i++;
505
- tokens.push({ type: "attributeValue", start: pos3, end: i });
506
- }
507
- }
508
- continue;
509
- }
510
- i++;
511
- }
512
- if (i < len && source[i] === "/") {
513
- tokens.push({ type: "punctuation", start: i, end: i + 1 });
514
- i++;
515
- }
516
- if (i < len && source[i] === ">") {
517
- tokens.push({ type: "punctuation", start: i, end: i + 1 });
518
- i++;
519
- }
520
- continue;
521
- }
522
- const pos = i;
523
- while (i < len && source[i] !== "<") i++;
524
- if (i > pos) {
525
- tokens.push({ type: "plain", start: pos, end: i });
526
- }
527
- }
528
- return tokens;
529
- };
530
-
531
449
  // src/engine/tokenizers/php.ts
532
450
  var PHP_KEYWORDS = /* @__PURE__ */ new Set([
533
451
  "abstract",
@@ -1111,11 +1029,6 @@ var tokenizeGo = (source) => {
1111
1029
  return tokens;
1112
1030
  };
1113
1031
 
1114
- // src/engine/tokenizer.ts
1115
- function tok(type, s, e) {
1116
- return { type, start: s, end: e };
1117
- }
1118
-
1119
1032
  // src/engine/tokenizers/markdown.ts
1120
1033
  function tokenizeMarkdown(source) {
1121
1034
  const tokens = [];
@@ -1320,112 +1233,17 @@ function getTheme(name) {
1320
1233
  function getRegisteredThemes() {
1321
1234
  return Array.from(themeRegistry.keys());
1322
1235
  }
1323
-
1324
- // src/themes/light.ts
1325
1236
  registerTheme({
1326
1237
  name: "light",
1327
1238
  variant: "light",
1328
- colors: {
1329
- background: "#ffffff",
1330
- foreground: "#1e1e2e",
1331
- gutterBackground: "#f8fafc",
1332
- gutterForeground: "#94a3b8",
1333
- gutterBorder: "#e2e8f0",
1334
- activeLineBackground: "#f8fafc",
1335
- selectionBackground: "#dbeafe",
1336
- cursorColor: "#3b82f6",
1337
- diffAdded: "#1a7f37",
1338
- diffModified: "#0969da",
1339
- diffRemoved: "#cf222e",
1340
- keyword: "#8b5cf6",
1341
- string: "#059669",
1342
- comment: "#94a3b8",
1343
- number: "#d97706",
1344
- operator: "#0891b2",
1345
- function: "#2563eb",
1346
- type: "#d97706",
1347
- tag: "#dc2626",
1348
- attribute: "#d97706",
1349
- attributeValue: "#059669",
1350
- punctuation: "#64748b",
1351
- variable: "#1e1e2e"
1352
- }
1239
+ colors: LIGHT_COLORS
1353
1240
  });
1354
-
1355
- // src/themes/dark.ts
1356
1241
  registerTheme({
1357
1242
  name: "dark",
1358
1243
  variant: "dark",
1359
- colors: {
1360
- background: "#18181b",
1361
- foreground: "#e4e4e7",
1362
- gutterBackground: "#18181b",
1363
- gutterForeground: "#52525b",
1364
- gutterBorder: "#27272a",
1365
- activeLineBackground: "#27272a",
1366
- selectionBackground: "#1e3a5f",
1367
- cursorColor: "#60a5fa",
1368
- diffAdded: "#3fb950",
1369
- diffModified: "#4184e4",
1370
- diffRemoved: "#f85149",
1371
- keyword: "#c084fc",
1372
- string: "#34d399",
1373
- comment: "#71717a",
1374
- number: "#fbbf24",
1375
- operator: "#22d3ee",
1376
- function: "#60a5fa",
1377
- type: "#fbbf24",
1378
- tag: "#f87171",
1379
- attribute: "#fbbf24",
1380
- attributeValue: "#34d399",
1381
- punctuation: "#a1a1aa",
1382
- variable: "#e4e4e7"
1383
- }
1244
+ colors: DARK_COLORS
1384
1245
  });
1385
1246
 
1386
- // src/engine/highlight.ts
1387
- function escapeHtml(text) {
1388
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1389
- }
1390
- var COLOR_RE = /^(#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla)\([\d\s,.%/-]+\)|[a-zA-Z]+)$/;
1391
- function safeColor(value) {
1392
- return typeof value === "string" && COLOR_RE.test(value.trim()) ? value.trim() : "inherit";
1393
- }
1394
- function highlightCode(source, tokens, colors) {
1395
- if (tokens.length === 0) return escapeHtml(source);
1396
- const colorMap = {
1397
- keyword: colors.keyword,
1398
- string: colors.string,
1399
- comment: colors.comment,
1400
- number: colors.number,
1401
- operator: colors.operator,
1402
- function: colors.function,
1403
- type: colors.type,
1404
- tag: colors.tag,
1405
- attribute: colors.attribute,
1406
- attributeValue: colors.attributeValue,
1407
- punctuation: colors.punctuation,
1408
- variable: colors.variable,
1409
- plain: colors.foreground
1410
- };
1411
- const parts = [];
1412
- let pos = 0;
1413
- for (const token of tokens) {
1414
- if (token.start > pos) {
1415
- parts.push(escapeHtml(source.slice(pos, token.start)));
1416
- }
1417
- const text = escapeHtml(source.slice(token.start, token.end));
1418
- const color = safeColor(colorMap[token.type]);
1419
- const style = token.type === "comment" ? `color:${color};font-style:italic` : `color:${color}`;
1420
- parts.push(`<span style="${style}">${text}</span>`);
1421
- pos = token.end;
1422
- }
1423
- if (pos < source.length) {
1424
- parts.push(escapeHtml(source.slice(pos)));
1425
- }
1426
- return parts.join("");
1427
- }
1428
-
1429
1247
  // src/engine/position.ts
1430
1248
  function lineColumnToOffset(text, line, column = 1) {
1431
1249
  const lines = text.split("\n");
@@ -1818,7 +1636,7 @@ var CodeEditor = Object.assign(CodeEditorRoot, {
1818
1636
  });
1819
1637
 
1820
1638
  // src/engine/markdown-render.ts
1821
- function escapeHtml2(s) {
1639
+ function escapeHtml(s) {
1822
1640
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1823
1641
  }
1824
1642
  function escapeAttr(s) {
@@ -1859,10 +1677,10 @@ function renderMarkdown(markdown) {
1859
1677
  i++;
1860
1678
  }
1861
1679
  i++;
1862
- out.push(`<pre><code${lang ? ` class="language-${escapeAttr(escapeHtml2(lang))}"` : ""}>${escapeHtml2(body.join("\n"))}</code></pre>`);
1680
+ out.push(`<pre><code${lang ? ` class="language-${escapeAttr(escapeHtml(lang))}"` : ""}>${escapeHtml(body.join("\n"))}</code></pre>`);
1863
1681
  continue;
1864
1682
  }
1865
- const esc = escapeHtml2(line);
1683
+ const esc = escapeHtml(line);
1866
1684
  if (/^\s*$/.test(line)) {
1867
1685
  flushPara();
1868
1686
  closeList();