open-research 0.1.20 → 0.1.22

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.
Files changed (2) hide show
  1. package/dist/cli.js +115 -33
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -811,7 +811,7 @@ function formatDateTime(value) {
811
811
  }
812
812
 
813
813
  // src/lib/cli/version.ts
814
- var PACKAGE_VERSION = "0.1.20";
814
+ var PACKAGE_VERSION = "0.1.22";
815
815
  function getPackageVersion() {
816
816
  return PACKAGE_VERSION;
817
817
  }
@@ -1384,6 +1384,8 @@ var source_default = chalk;
1384
1384
  // src/tui/text-input.tsx
1385
1385
  import { jsx } from "react/jsx-runtime";
1386
1386
  var PASTE_MARKER = "\uFFFC";
1387
+ var BRACKETED_PASTE_START = "[200~";
1388
+ var BRACKETED_PASTE_END = "[201~";
1387
1389
  function expandPasteMarkers(value, pasteMap) {
1388
1390
  let result = "";
1389
1391
  let pasteIdx = 0;
@@ -1424,16 +1426,27 @@ function TextInput({
1424
1426
  cursorToEnd = 0
1425
1427
  }) {
1426
1428
  const [cursorOffset, setCursorOffset] = useState(originalValue.length);
1429
+ const valueRef = useRef(originalValue);
1430
+ const cursorOffsetRef = useRef(originalValue.length);
1427
1431
  const pasteMapRef = useRef(/* @__PURE__ */ new Map());
1428
1432
  const pasteCounterRef = useRef(0);
1433
+ const bracketedPasteBufferRef = useRef(null);
1434
+ useEffect(() => {
1435
+ valueRef.current = originalValue;
1436
+ }, [originalValue]);
1437
+ useEffect(() => {
1438
+ cursorOffsetRef.current = cursorOffset;
1439
+ }, [cursorOffset]);
1429
1440
  useEffect(() => {
1430
1441
  if (!focus || !showCursor) return;
1431
1442
  if (cursorOffset > originalValue.length) {
1443
+ cursorOffsetRef.current = originalValue.length;
1432
1444
  setCursorOffset(originalValue.length);
1433
1445
  }
1434
1446
  }, [originalValue, focus, showCursor]);
1435
1447
  useEffect(() => {
1436
1448
  if (cursorToEnd > 0) {
1449
+ cursorOffsetRef.current = originalValue.length;
1437
1450
  setCursorOffset(originalValue.length);
1438
1451
  }
1439
1452
  }, [cursorToEnd]);
@@ -1488,8 +1501,32 @@ function TextInput({
1488
1501
  return result;
1489
1502
  }
1490
1503
  const renderedPlaceholder = showCursor && focus && placeholder.length > 0 ? source_default.inverse(placeholder[0]) + source_default.grey(placeholder.slice(1)) : placeholder ? source_default.grey(placeholder) : void 0;
1504
+ function sanitizeInput(raw) {
1505
+ return raw.replace(/\x1b\[[?>=!]*[0-9;]*[a-zA-Z~]/g, "").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)?/g, "").replace(/\[20[01]~/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "");
1506
+ }
1507
+ function insertCleanText(raw, currentValue, currentCursor) {
1508
+ const clean = sanitizeInput(raw);
1509
+ if (!clean) {
1510
+ return { nextValue: currentValue, nextCursor: currentCursor };
1511
+ }
1512
+ const lineCount = (clean.match(/\n/g) || []).length;
1513
+ if (lineCount >= 2) {
1514
+ const id = ++pasteCounterRef.current;
1515
+ pasteMapRef.current.set(id, { text: clean, lineCount, id });
1516
+ return {
1517
+ nextValue: currentValue.slice(0, currentCursor) + PASTE_MARKER + currentValue.slice(currentCursor),
1518
+ nextCursor: currentCursor + 1
1519
+ };
1520
+ }
1521
+ return {
1522
+ nextValue: currentValue.slice(0, currentCursor) + clean + currentValue.slice(currentCursor),
1523
+ nextCursor: currentCursor + clean.length
1524
+ };
1525
+ }
1491
1526
  useInput(
1492
1527
  (input2, key) => {
1528
+ const currentValue = valueRef.current;
1529
+ const currentCursor = cursorOffsetRef.current;
1493
1530
  if (key.ctrl && input2 === "c" || key.shift && key.tab) {
1494
1531
  return;
1495
1532
  }
@@ -1506,76 +1543,121 @@ function TextInput({
1506
1543
  return;
1507
1544
  }
1508
1545
  if (key.return && key.shift || key.return && key.meta) {
1509
- const inserted = originalValue.slice(0, cursorOffset) + "\n" + originalValue.slice(cursorOffset);
1510
- setCursorOffset(cursorOffset + 1);
1546
+ const inserted = currentValue.slice(0, currentCursor) + "\n" + currentValue.slice(currentCursor);
1547
+ cursorOffsetRef.current = currentCursor + 1;
1548
+ valueRef.current = inserted;
1549
+ setCursorOffset(currentCursor + 1);
1511
1550
  onChange(inserted);
1512
1551
  return;
1513
1552
  }
1514
1553
  if (key.return) {
1515
- const expanded = expandPasteMarkers(originalValue, pasteMapRef.current);
1554
+ const expanded = expandPasteMarkers(currentValue, pasteMapRef.current);
1516
1555
  onSubmit?.(expanded);
1517
1556
  return;
1518
1557
  }
1519
- let nextValue = originalValue;
1520
- let nextCursor = cursorOffset;
1558
+ let nextValue = currentValue;
1559
+ let nextCursor = currentCursor;
1521
1560
  if (key.meta && key.backspace || key.ctrl && input2 === "w") {
1522
- const boundary = prevWordBoundary(originalValue, cursorOffset);
1523
- nextValue = originalValue.slice(0, boundary) + originalValue.slice(cursorOffset);
1561
+ const boundary = prevWordBoundary(currentValue, currentCursor);
1562
+ nextValue = currentValue.slice(0, boundary) + currentValue.slice(currentCursor);
1524
1563
  nextCursor = boundary;
1525
1564
  } else if (key.ctrl && input2 === "u") {
1526
- nextValue = originalValue.slice(cursorOffset);
1527
- nextCursor = 0;
1565
+ const lineStart = currentValue.lastIndexOf("\n", currentCursor - 1) + 1;
1566
+ nextValue = currentValue.slice(0, lineStart) + currentValue.slice(currentCursor);
1567
+ nextCursor = lineStart;
1528
1568
  } else if (key.ctrl && input2 === "k") {
1529
- nextValue = originalValue.slice(0, cursorOffset);
1569
+ const lineEnd = currentValue.indexOf("\n", currentCursor);
1570
+ nextValue = currentValue.slice(0, currentCursor) + (lineEnd === -1 ? "" : currentValue.slice(lineEnd));
1530
1571
  } else if (key.meta && key.delete) {
1531
- const boundary = nextWordBoundary(originalValue, cursorOffset);
1532
- nextValue = originalValue.slice(0, cursorOffset) + originalValue.slice(boundary);
1572
+ const boundary = nextWordBoundary(currentValue, currentCursor);
1573
+ nextValue = currentValue.slice(0, currentCursor) + currentValue.slice(boundary);
1533
1574
  } else if (key.backspace || key.delete) {
1534
- if (cursorOffset > 0) {
1535
- const deletedChar = originalValue[cursorOffset - 1];
1575
+ if (currentCursor > 0) {
1576
+ const deletedChar = currentValue[currentCursor - 1];
1536
1577
  if (deletedChar === PASTE_MARKER) {
1537
1578
  let markerIndex = 0;
1538
- for (let ci = 0; ci < cursorOffset - 1; ci++) {
1539
- if (originalValue[ci] === PASTE_MARKER) markerIndex++;
1579
+ for (let ci = 0; ci < currentCursor - 1; ci++) {
1580
+ if (currentValue[ci] === PASTE_MARKER) markerIndex++;
1540
1581
  }
1541
1582
  const ids = [...pasteMapRef.current.keys()].sort((a, b) => a - b);
1542
1583
  if (markerIndex < ids.length) {
1543
1584
  pasteMapRef.current.delete(ids[markerIndex]);
1544
1585
  }
1545
1586
  }
1546
- nextValue = originalValue.slice(0, cursorOffset - 1) + originalValue.slice(cursorOffset);
1587
+ nextValue = currentValue.slice(0, currentCursor - 1) + currentValue.slice(currentCursor);
1547
1588
  nextCursor--;
1548
1589
  }
1549
1590
  } else if (key.meta && key.leftArrow || key.meta && input2 === "b" || key.ctrl && key.leftArrow) {
1550
- nextCursor = prevWordBoundary(originalValue, cursorOffset);
1591
+ nextCursor = prevWordBoundary(currentValue, currentCursor);
1551
1592
  } else if (key.meta && key.rightArrow || key.meta && input2 === "f" || key.ctrl && key.rightArrow) {
1552
- nextCursor = nextWordBoundary(originalValue, cursorOffset);
1593
+ nextCursor = nextWordBoundary(currentValue, currentCursor);
1553
1594
  } else if (key.ctrl && input2 === "a" || key.home) {
1554
1595
  nextCursor = 0;
1555
1596
  } else if (key.ctrl && input2 === "e" || key.end) {
1556
- nextCursor = originalValue.length;
1597
+ nextCursor = currentValue.length;
1557
1598
  } else if (key.leftArrow) {
1558
1599
  if (showCursor) nextCursor--;
1559
1600
  } else if (key.rightArrow) {
1560
1601
  if (showCursor) nextCursor++;
1561
1602
  } else if (!key.ctrl && !key.meta) {
1562
- const clean = input2.replace(/\x1b\[[?>=!]*[0-9;]*[a-zA-Z~]/g, "").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)?/g, "").replace(/\[20[01]~/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "");
1563
- if (clean) {
1564
- const lineCount = (clean.match(/\n/g) || []).length;
1565
- if (lineCount >= 2) {
1566
- const id = ++pasteCounterRef.current;
1567
- pasteMapRef.current.set(id, { text: clean, lineCount, id });
1568
- nextValue = originalValue.slice(0, cursorOffset) + PASTE_MARKER + originalValue.slice(cursorOffset);
1569
- nextCursor += 1;
1570
- } else {
1571
- nextValue = originalValue.slice(0, cursorOffset) + clean + originalValue.slice(cursorOffset);
1572
- nextCursor += clean.length;
1603
+ const hasPasteMarkers = input2.includes(BRACKETED_PASTE_START) || input2.includes(BRACKETED_PASTE_END) || bracketedPasteBufferRef.current !== null;
1604
+ if (hasPasteMarkers) {
1605
+ let remaining = input2;
1606
+ let workingValue = currentValue;
1607
+ let workingCursor = currentCursor;
1608
+ while (remaining.length > 0) {
1609
+ if (bracketedPasteBufferRef.current === null) {
1610
+ const startIndex = remaining.indexOf(BRACKETED_PASTE_START);
1611
+ if (startIndex === -1) {
1612
+ const inserted2 = insertCleanText(remaining, workingValue, workingCursor);
1613
+ workingValue = inserted2.nextValue;
1614
+ workingCursor = inserted2.nextCursor;
1615
+ remaining = "";
1616
+ break;
1617
+ }
1618
+ const prefix = remaining.slice(0, startIndex);
1619
+ if (prefix) {
1620
+ const inserted2 = insertCleanText(prefix, workingValue, workingCursor);
1621
+ workingValue = inserted2.nextValue;
1622
+ workingCursor = inserted2.nextCursor;
1623
+ }
1624
+ bracketedPasteBufferRef.current = "";
1625
+ remaining = remaining.slice(startIndex + BRACKETED_PASTE_START.length);
1626
+ continue;
1627
+ }
1628
+ const endIndex = remaining.indexOf(BRACKETED_PASTE_END);
1629
+ if (endIndex === -1) {
1630
+ bracketedPasteBufferRef.current += remaining;
1631
+ remaining = "";
1632
+ break;
1633
+ }
1634
+ bracketedPasteBufferRef.current += remaining.slice(0, endIndex);
1635
+ const inserted = insertCleanText(
1636
+ bracketedPasteBufferRef.current,
1637
+ workingValue,
1638
+ workingCursor
1639
+ );
1640
+ workingValue = inserted.nextValue;
1641
+ workingCursor = inserted.nextCursor;
1642
+ bracketedPasteBufferRef.current = null;
1643
+ remaining = remaining.slice(endIndex + BRACKETED_PASTE_END.length);
1573
1644
  }
1645
+ nextValue = workingValue;
1646
+ nextCursor = workingCursor;
1647
+ } else {
1648
+ const inserted = insertCleanText(input2, currentValue, currentCursor);
1649
+ nextValue = inserted.nextValue;
1650
+ nextCursor = inserted.nextCursor;
1574
1651
  }
1575
1652
  }
1576
1653
  nextCursor = Math.max(0, Math.min(nextCursor, nextValue.length));
1654
+ if (nextValue === "") {
1655
+ pasteMapRef.current.clear();
1656
+ }
1657
+ cursorOffsetRef.current = nextCursor;
1577
1658
  setCursorOffset(nextCursor);
1578
- if (nextValue !== originalValue) {
1659
+ if (nextValue !== currentValue) {
1660
+ valueRef.current = nextValue;
1579
1661
  onChange(nextValue);
1580
1662
  }
1581
1663
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-research",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "Local-first research CLI agent — discover papers, synthesize notes, run analysis, and draft artifacts from your terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",