@particle-academy/fancy-code 0.4.0 → 0.4.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.
package/README.md CHANGED
@@ -210,25 +210,19 @@ Add languages beyond the built-ins using `registerLanguage`:
210
210
 
211
211
  ```tsx
212
212
  import { registerLanguage } from "@particle-academy/fancy-code";
213
+ import type { Tokenizer } from "@particle-academy/fancy-code";
213
214
 
214
- registerLanguage({
215
- name: "Python",
216
- aliases: ["py", "python"],
217
- support: () => {
218
- // Return a LanguageSupport instance
219
- const { python } = require("@codemirror/lang-python");
220
- return python();
221
- },
222
- });
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
+ };
223
221
 
224
- // Lazy-loaded
225
222
  registerLanguage({
226
- name: "Rust",
227
- aliases: ["rs", "rust"],
228
- support: async () => {
229
- const { rust } = await import("@codemirror/lang-rust");
230
- return rust();
231
- },
223
+ name: "Ruby",
224
+ aliases: ["rb", "ruby"],
225
+ tokenize: tokenizeRuby,
232
226
  });
233
227
  ```
234
228
 
@@ -323,7 +317,7 @@ src/
323
317
  │ ├── CodeEditorStatusBar.tsx # Cursor/language/tab display
324
318
  │ └── index.ts
325
319
  ├── hooks/
326
- │ ├── use-codemirror.ts # Core editor lifecycle
320
+ │ ├── use-editor-engine.ts # Core editor lifecycle
327
321
  │ └── use-dark-mode.ts # Reactive prefers-color-scheme
328
322
  ├── languages/
329
323
  │ ├── registry.ts # Global language registry
package/dist/index.cjs CHANGED
@@ -85,7 +85,7 @@ function CodeEditorPanel({ className }) {
85
85
  children: lineNumberElements
86
86
  }
87
87
  ),
88
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-w-0 flex-1", children: [
88
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: reactFancy.cn("relative flex-1", wordWrap && "min-w-0"), children: [
89
89
  /* @__PURE__ */ jsxRuntime.jsx(
90
90
  "pre",
91
91
  {
@@ -110,10 +110,14 @@ function CodeEditorPanel({ className }) {
110
110
  "textarea",
111
111
  {
112
112
  ref: textareaRef,
113
- className: "relative m-0 block w-full resize-none overflow-hidden border-none bg-transparent p-2.5 text-[13px] leading-[1.5] text-transparent outline-none",
113
+ className: reactFancy.cn(
114
+ "relative m-0 block resize-none border-none bg-transparent p-2.5 text-[13px] leading-[1.5] text-transparent outline-none",
115
+ wordWrap ? "w-full" : "min-w-full"
116
+ ),
114
117
  style: {
115
118
  caretColor: themeColors.cursorColor,
116
119
  minHeight: _minHeight ? _minHeight - 40 : 80,
120
+ overflow: "hidden",
117
121
  whiteSpace: wordWrap ? "pre-wrap" : "pre",
118
122
  overflowWrap: wordWrap ? "break-word" : "normal"
119
123
  },
@@ -1294,6 +1298,7 @@ function useEditorEngine({
1294
1298
  language,
1295
1299
  theme,
1296
1300
  readOnly,
1301
+ wordWrap,
1297
1302
  tabSize,
1298
1303
  onCursorChange
1299
1304
  }) {
@@ -1327,14 +1332,29 @@ function useEditorEngine({
1327
1332
  }
1328
1333
  return count;
1329
1334
  }, [value]);
1335
+ const autoResize = react.useCallback(() => {
1336
+ const ta = textareaRef.current;
1337
+ if (!ta) return;
1338
+ ta.style.height = "auto";
1339
+ ta.style.height = ta.scrollHeight + "px";
1340
+ if (!wordWrap) {
1341
+ ta.style.width = "0";
1342
+ ta.style.width = ta.scrollWidth + "px";
1343
+ } else {
1344
+ ta.style.width = "";
1345
+ }
1346
+ }, [wordWrap]);
1330
1347
  react.useEffect(() => {
1331
1348
  const ta = textareaRef.current;
1332
- if (!ta || ta.value === value) return;
1333
- const { selectionStart, selectionEnd } = ta;
1334
- ta.value = value;
1335
- ta.selectionStart = selectionStart;
1336
- ta.selectionEnd = selectionEnd;
1337
- }, [value]);
1349
+ if (!ta) return;
1350
+ if (ta.value !== value) {
1351
+ const { selectionStart, selectionEnd } = ta;
1352
+ ta.value = value;
1353
+ ta.selectionStart = selectionStart;
1354
+ ta.selectionEnd = selectionEnd;
1355
+ }
1356
+ autoResize();
1357
+ }, [value, autoResize]);
1338
1358
  const updateCursorInfo = react.useCallback(() => {
1339
1359
  const ta = textareaRef.current;
1340
1360
  if (!ta) return;
@@ -1351,8 +1371,9 @@ function useEditorEngine({
1351
1371
  const ta = textareaRef.current;
1352
1372
  if (!ta) return;
1353
1373
  onChangeRef.current?.(ta.value);
1374
+ autoResize();
1354
1375
  updateCursorInfo();
1355
- }, [updateCursorInfo]);
1376
+ }, [autoResize, updateCursorInfo]);
1356
1377
  const handleSelect = react.useCallback(() => {
1357
1378
  updateCursorInfo();
1358
1379
  }, [updateCursorInfo]);
@@ -1412,6 +1433,7 @@ function useEditorEngine({
1412
1433
  ta.selectionStart = ta.selectionEnd = start + tabSize;
1413
1434
  onChangeRef.current?.(ta.value);
1414
1435
  }
1436
+ autoResize();
1415
1437
  updateCursorInfo();
1416
1438
  return;
1417
1439
  }
@@ -1430,11 +1452,12 @@ function useEditorEngine({
1430
1452
  ta.value = before + insertion + after;
1431
1453
  ta.selectionStart = ta.selectionEnd = start + insertion.length;
1432
1454
  onChangeRef.current?.(ta.value);
1455
+ autoResize();
1433
1456
  updateCursorInfo();
1434
1457
  return;
1435
1458
  }
1436
1459
  },
1437
- [readOnly, tabSize, updateCursorInfo]
1460
+ [readOnly, tabSize, autoResize, updateCursorInfo]
1438
1461
  );
1439
1462
  return {
1440
1463
  textareaRef,
@@ -1508,6 +1531,7 @@ function CodeEditorRoot({
1508
1531
  language: currentLanguage,
1509
1532
  theme: resolvedTheme,
1510
1533
  readOnly,
1534
+ wordWrap: isWordWrap,
1511
1535
  tabSize: tabSizeProp,
1512
1536
  onCursorChange: ({ line, col, selectionLength: sel }) => {
1513
1537
  setCursorPosition({ line, col });