open-research 0.1.19 → 0.1.21

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 +118 -31
  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.19";
814
+ var PACKAGE_VERSION = "0.1.21";
815
815
  function getPackageVersion() {
816
816
  return PACKAGE_VERSION;
817
817
  }
@@ -877,13 +877,13 @@ import {
877
877
  useDeferredValue,
878
878
  useEffect as useEffect2,
879
879
  useMemo as useMemo3,
880
- useRef,
880
+ useRef as useRef2,
881
881
  useState as useState4
882
882
  } from "react";
883
883
  import { Box as Box5, Text as Text5, useApp, useInput as useInput4 } from "ink";
884
884
 
885
885
  // src/tui/text-input.tsx
886
- import { useState, useEffect } from "react";
886
+ import { useState, useEffect, useRef } from "react";
887
887
  import { Box, Text, useInput } from "ink";
888
888
 
889
889
  // node_modules/chalk/source/vendor/ansi-styles/index.js
@@ -1383,6 +1383,22 @@ var source_default = chalk;
1383
1383
 
1384
1384
  // src/tui/text-input.tsx
1385
1385
  import { jsx } from "react/jsx-runtime";
1386
+ var PASTE_MARKER = "\uFFFC";
1387
+ function expandPasteMarkers(value, pasteMap) {
1388
+ let result = "";
1389
+ let pasteIdx = 0;
1390
+ const ids = [...pasteMap.keys()].sort((a, b) => a - b);
1391
+ for (const char of value) {
1392
+ if (char === PASTE_MARKER && pasteIdx < ids.length) {
1393
+ const entry = pasteMap.get(ids[pasteIdx]);
1394
+ result += entry?.text ?? "";
1395
+ pasteIdx++;
1396
+ } else {
1397
+ result += char;
1398
+ }
1399
+ }
1400
+ return result;
1401
+ }
1386
1402
  function prevWordBoundary(value, cursor) {
1387
1403
  let i = cursor;
1388
1404
  while (i > 0 && /\s/.test(value[i - 1])) i--;
@@ -1408,40 +1424,84 @@ function TextInput({
1408
1424
  cursorToEnd = 0
1409
1425
  }) {
1410
1426
  const [cursorOffset, setCursorOffset] = useState(originalValue.length);
1427
+ const valueRef = useRef(originalValue);
1428
+ const cursorOffsetRef = useRef(originalValue.length);
1429
+ const pasteMapRef = useRef(/* @__PURE__ */ new Map());
1430
+ const pasteCounterRef = useRef(0);
1431
+ useEffect(() => {
1432
+ valueRef.current = originalValue;
1433
+ }, [originalValue]);
1434
+ useEffect(() => {
1435
+ cursorOffsetRef.current = cursorOffset;
1436
+ }, [cursorOffset]);
1411
1437
  useEffect(() => {
1412
1438
  if (!focus || !showCursor) return;
1413
1439
  if (cursorOffset > originalValue.length) {
1440
+ cursorOffsetRef.current = originalValue.length;
1414
1441
  setCursorOffset(originalValue.length);
1415
1442
  }
1416
1443
  }, [originalValue, focus, showCursor]);
1417
1444
  useEffect(() => {
1418
1445
  if (cursorToEnd > 0) {
1446
+ cursorOffsetRef.current = originalValue.length;
1419
1447
  setCursorOffset(originalValue.length);
1420
1448
  }
1421
1449
  }, [cursorToEnd]);
1450
+ useEffect(() => {
1451
+ if (originalValue === "") {
1452
+ pasteMapRef.current.clear();
1453
+ }
1454
+ }, [originalValue]);
1455
+ function pasteBadge(entry) {
1456
+ return source_default.dim.cyan(`[Pasted text #${entry.id} +${entry.lineCount} lines]`);
1457
+ }
1422
1458
  function buildRendered() {
1423
1459
  if (showCursor && focus) {
1424
1460
  if (originalValue.length === 0) return source_default.inverse(" ");
1425
- let result = "";
1461
+ const pasteIds2 = [...pasteMapRef.current.keys()].sort((a, b) => a - b);
1462
+ let pasteIdx2 = 0;
1463
+ let result2 = "";
1426
1464
  let i = 0;
1427
1465
  for (const char of originalValue) {
1428
- if (i === cursorOffset) {
1429
- result += char === "\n" ? source_default.inverse(" ") + "\n" : source_default.inverse(char);
1466
+ if (char === PASTE_MARKER) {
1467
+ const entry = pasteIdx2 < pasteIds2.length ? pasteMapRef.current.get(pasteIds2[pasteIdx2]) : void 0;
1468
+ pasteIdx2++;
1469
+ if (i === cursorOffset) {
1470
+ result2 += entry ? pasteBadge(entry) + source_default.inverse(" ") : source_default.inverse(" ");
1471
+ } else {
1472
+ result2 += entry ? pasteBadge(entry) : "";
1473
+ }
1474
+ } else if (i === cursorOffset) {
1475
+ result2 += char === "\n" ? source_default.inverse(" ") + "\n" : source_default.inverse(char);
1430
1476
  } else {
1431
- result += char;
1477
+ result2 += char;
1432
1478
  }
1433
1479
  i++;
1434
1480
  }
1435
1481
  if (cursorOffset === originalValue.length) {
1436
- result += source_default.inverse(" ");
1482
+ result2 += source_default.inverse(" ");
1483
+ }
1484
+ return result2;
1485
+ }
1486
+ let result = "";
1487
+ const pasteIds = [...pasteMapRef.current.keys()].sort((a, b) => a - b);
1488
+ let pasteIdx = 0;
1489
+ for (const char of originalValue) {
1490
+ if (char === PASTE_MARKER) {
1491
+ const entry = pasteIdx < pasteIds.length ? pasteMapRef.current.get(pasteIds[pasteIdx]) : void 0;
1492
+ pasteIdx++;
1493
+ result += entry ? pasteBadge(entry) : "";
1494
+ } else {
1495
+ result += char;
1437
1496
  }
1438
- return result;
1439
1497
  }
1440
- return originalValue;
1498
+ return result;
1441
1499
  }
1442
1500
  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;
1443
1501
  useInput(
1444
1502
  (input2, key) => {
1503
+ const currentValue = valueRef.current;
1504
+ const currentCursor = cursorOffsetRef.current;
1445
1505
  if (key.ctrl && input2 === "c" || key.shift && key.tab) {
1446
1506
  return;
1447
1507
  }
@@ -1458,42 +1518,56 @@ function TextInput({
1458
1518
  return;
1459
1519
  }
1460
1520
  if (key.return && key.shift || key.return && key.meta) {
1461
- const inserted = originalValue.slice(0, cursorOffset) + "\n" + originalValue.slice(cursorOffset);
1462
- setCursorOffset(cursorOffset + 1);
1521
+ const inserted = currentValue.slice(0, currentCursor) + "\n" + currentValue.slice(currentCursor);
1522
+ cursorOffsetRef.current = currentCursor + 1;
1523
+ valueRef.current = inserted;
1524
+ setCursorOffset(currentCursor + 1);
1463
1525
  onChange(inserted);
1464
1526
  return;
1465
1527
  }
1466
1528
  if (key.return) {
1467
- onSubmit?.(originalValue);
1529
+ const expanded = expandPasteMarkers(currentValue, pasteMapRef.current);
1530
+ onSubmit?.(expanded);
1468
1531
  return;
1469
1532
  }
1470
- let nextValue = originalValue;
1471
- let nextCursor = cursorOffset;
1533
+ let nextValue = currentValue;
1534
+ let nextCursor = currentCursor;
1472
1535
  if (key.meta && key.backspace || key.ctrl && input2 === "w") {
1473
- const boundary = prevWordBoundary(originalValue, cursorOffset);
1474
- nextValue = originalValue.slice(0, boundary) + originalValue.slice(cursorOffset);
1536
+ const boundary = prevWordBoundary(currentValue, currentCursor);
1537
+ nextValue = currentValue.slice(0, boundary) + currentValue.slice(currentCursor);
1475
1538
  nextCursor = boundary;
1476
1539
  } else if (key.ctrl && input2 === "u") {
1477
- nextValue = originalValue.slice(cursorOffset);
1540
+ nextValue = currentValue.slice(currentCursor);
1478
1541
  nextCursor = 0;
1479
1542
  } else if (key.ctrl && input2 === "k") {
1480
- nextValue = originalValue.slice(0, cursorOffset);
1543
+ nextValue = currentValue.slice(0, currentCursor);
1481
1544
  } else if (key.meta && key.delete) {
1482
- const boundary = nextWordBoundary(originalValue, cursorOffset);
1483
- nextValue = originalValue.slice(0, cursorOffset) + originalValue.slice(boundary);
1545
+ const boundary = nextWordBoundary(currentValue, currentCursor);
1546
+ nextValue = currentValue.slice(0, currentCursor) + currentValue.slice(boundary);
1484
1547
  } else if (key.backspace || key.delete) {
1485
- if (cursorOffset > 0) {
1486
- nextValue = originalValue.slice(0, cursorOffset - 1) + originalValue.slice(cursorOffset);
1548
+ if (currentCursor > 0) {
1549
+ const deletedChar = currentValue[currentCursor - 1];
1550
+ if (deletedChar === PASTE_MARKER) {
1551
+ let markerIndex = 0;
1552
+ for (let ci = 0; ci < currentCursor - 1; ci++) {
1553
+ if (currentValue[ci] === PASTE_MARKER) markerIndex++;
1554
+ }
1555
+ const ids = [...pasteMapRef.current.keys()].sort((a, b) => a - b);
1556
+ if (markerIndex < ids.length) {
1557
+ pasteMapRef.current.delete(ids[markerIndex]);
1558
+ }
1559
+ }
1560
+ nextValue = currentValue.slice(0, currentCursor - 1) + currentValue.slice(currentCursor);
1487
1561
  nextCursor--;
1488
1562
  }
1489
1563
  } else if (key.meta && key.leftArrow || key.meta && input2 === "b" || key.ctrl && key.leftArrow) {
1490
- nextCursor = prevWordBoundary(originalValue, cursorOffset);
1564
+ nextCursor = prevWordBoundary(currentValue, currentCursor);
1491
1565
  } else if (key.meta && key.rightArrow || key.meta && input2 === "f" || key.ctrl && key.rightArrow) {
1492
- nextCursor = nextWordBoundary(originalValue, cursorOffset);
1566
+ nextCursor = nextWordBoundary(currentValue, currentCursor);
1493
1567
  } else if (key.ctrl && input2 === "a" || key.home) {
1494
1568
  nextCursor = 0;
1495
1569
  } else if (key.ctrl && input2 === "e" || key.end) {
1496
- nextCursor = originalValue.length;
1570
+ nextCursor = currentValue.length;
1497
1571
  } else if (key.leftArrow) {
1498
1572
  if (showCursor) nextCursor--;
1499
1573
  } else if (key.rightArrow) {
@@ -1501,13 +1575,26 @@ function TextInput({
1501
1575
  } else if (!key.ctrl && !key.meta) {
1502
1576
  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, "");
1503
1577
  if (clean) {
1504
- nextValue = originalValue.slice(0, cursorOffset) + clean + originalValue.slice(cursorOffset);
1505
- nextCursor += clean.length;
1578
+ const lineCount = (clean.match(/\n/g) || []).length;
1579
+ if (lineCount >= 2) {
1580
+ const id = ++pasteCounterRef.current;
1581
+ pasteMapRef.current.set(id, { text: clean, lineCount, id });
1582
+ nextValue = currentValue.slice(0, currentCursor) + PASTE_MARKER + currentValue.slice(currentCursor);
1583
+ nextCursor += 1;
1584
+ } else {
1585
+ nextValue = currentValue.slice(0, currentCursor) + clean + currentValue.slice(currentCursor);
1586
+ nextCursor += clean.length;
1587
+ }
1506
1588
  }
1507
1589
  }
1508
1590
  nextCursor = Math.max(0, Math.min(nextCursor, nextValue.length));
1591
+ if (nextValue === "") {
1592
+ pasteMapRef.current.clear();
1593
+ }
1594
+ cursorOffsetRef.current = nextCursor;
1509
1595
  setCursorOffset(nextCursor);
1510
- if (nextValue !== originalValue) {
1596
+ if (nextValue !== currentValue) {
1597
+ valueRef.current = nextValue;
1511
1598
  onChange(nextValue);
1512
1599
  }
1513
1600
  },
@@ -6901,7 +6988,7 @@ function App({
6901
6988
  homeDir
6902
6989
  }) {
6903
6990
  const app = useApp();
6904
- const abortRef = useRef(null);
6991
+ const abortRef = useRef2(null);
6905
6992
  const [input2, setInput] = useState4("");
6906
6993
  const [composerFocused, setComposerFocused] = useState4(true);
6907
6994
  const [busy, setBusy] = useState4(false);
@@ -6933,7 +7020,7 @@ function App({
6933
7020
  const deferredPendingUpdates = useDeferredValue(pendingUpdates);
6934
7021
  const activityFrame = useAnimatedFrame(busy);
6935
7022
  const [agentQuestion, setAgentQuestion] = useState4(null);
6936
- const previewRef = useRef(null);
7023
+ const previewRef = useRef2(null);
6937
7024
  const isHome = deferredMessages.length === 0 && !busy;
6938
7025
  const hasWorkspace = workspacePath !== null;
6939
7026
  const hasAuth = authStatus === "connected";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-research",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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",