@particle-academy/fancy-code 0.6.0 → 0.7.0

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.cjs CHANGED
@@ -1346,6 +1346,19 @@ function highlightCode(source, tokens, colors) {
1346
1346
  return parts.join("");
1347
1347
  }
1348
1348
 
1349
+ // src/engine/position.ts
1350
+ function lineColumnToOffset(text, line, column = 1) {
1351
+ const lines = text.split("\n");
1352
+ const targetLine = Math.max(1, Math.min(Math.trunc(line) || 1, lines.length));
1353
+ let offset = 0;
1354
+ for (let i = 0; i < targetLine - 1; i++) {
1355
+ offset += lines[i].length + 1;
1356
+ }
1357
+ const lineText = lines[targetLine - 1] ?? "";
1358
+ const col = Math.max(1, Math.min(Math.trunc(column) || 1, lineText.length + 1));
1359
+ return offset + (col - 1);
1360
+ }
1361
+
1349
1362
  // src/hooks/use-editor-engine.ts
1350
1363
  var DEFAULT_COLORS = {
1351
1364
  background: "#18181b",
@@ -1460,6 +1473,28 @@ function useEditorEngine({
1460
1473
  setScrollTop(ta.scrollTop);
1461
1474
  setScrollLeft(ta.scrollLeft);
1462
1475
  }, []);
1476
+ const revealLine = react.useCallback(
1477
+ (line, column = 1, opts) => {
1478
+ const ta = textareaRef.current;
1479
+ if (!ta) return;
1480
+ const offset = lineColumnToOffset(ta.value, line, column);
1481
+ if (opts?.focus) ta.focus();
1482
+ ta.selectionStart = ta.selectionEnd = offset;
1483
+ updateCursorInfo();
1484
+ const panel = ta.closest("[data-fancy-code-panel]");
1485
+ if (!panel) return;
1486
+ const cs = getComputedStyle(ta);
1487
+ const lineHeight = parseFloat(cs.lineHeight) || 19.5;
1488
+ const paddingTop = parseFloat(cs.paddingTop) || 0;
1489
+ const lineIndex = (ta.value.slice(0, offset).match(/\n/g) || []).length;
1490
+ const textareaTopInPanel = ta.getBoundingClientRect().top - panel.getBoundingClientRect().top + panel.scrollTop;
1491
+ const lineTop = textareaTopInPanel + paddingTop + lineIndex * lineHeight;
1492
+ const target = lineTop - panel.clientHeight / 2 + lineHeight / 2;
1493
+ const maxScroll = panel.scrollHeight - panel.clientHeight;
1494
+ panel.scrollTop = Math.max(0, Math.min(target, maxScroll));
1495
+ },
1496
+ [updateCursorInfo]
1497
+ );
1463
1498
  const handleKeyDown = react.useCallback(
1464
1499
  (e) => {
1465
1500
  if (readOnly) {
@@ -1546,6 +1581,7 @@ function useEditorEngine({
1546
1581
  handleInput,
1547
1582
  handleScroll,
1548
1583
  handleSelect,
1584
+ revealLine,
1549
1585
  scrollTop,
1550
1586
  scrollLeft
1551
1587
  };
@@ -1579,7 +1615,9 @@ function CodeEditorRoot({
1579
1615
  tabSize: tabSizeProp = 2,
1580
1616
  placeholder,
1581
1617
  minHeight,
1582
- maxHeight
1618
+ maxHeight,
1619
+ cursorLine,
1620
+ cursorColumn
1583
1621
  }) {
1584
1622
  const [currentValue, setCurrentValue] = reactFancy.useControllableState(valueProp, defaultValue, onChange);
1585
1623
  const isDark = useDarkMode();
@@ -1615,6 +1653,11 @@ function CodeEditorRoot({
1615
1653
  setSelectionLength(sel);
1616
1654
  }
1617
1655
  });
1656
+ const { revealLine: revealLineImpl } = engineReturn;
1657
+ react.useEffect(() => {
1658
+ if (cursorLine == null) return;
1659
+ revealLineImpl(cursorLine, cursorColumn ?? 1, { focus: false });
1660
+ }, [cursorLine, cursorColumn, revealLineImpl]);
1618
1661
  const contextValue = react.useMemo(
1619
1662
  () => ({
1620
1663
  getValue: () => engineReturn.textareaRef.current?.value ?? currentValue,
@@ -1636,6 +1679,8 @@ function CodeEditorRoot({
1636
1679
  setCurrentValue(ta.value);
1637
1680
  },
1638
1681
  focus: () => engineReturn.textareaRef.current?.focus(),
1682
+ revealLine: (line, column) => engineReturn.revealLine(line, column ?? 1, { focus: true }),
1683
+ setCursor: ({ line, column }) => engineReturn.revealLine(line, column ?? 1, { focus: true }),
1639
1684
  language: currentLanguage,
1640
1685
  setLanguage,
1641
1686
  theme: resolvedTheme,