recappi 0.1.79 → 0.1.81
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.js +95 -49
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1288,7 +1288,8 @@ function RecordingDetailView({
|
|
|
1288
1288
|
item,
|
|
1289
1289
|
nowMs,
|
|
1290
1290
|
transcript,
|
|
1291
|
-
audio
|
|
1291
|
+
audio,
|
|
1292
|
+
localDir
|
|
1292
1293
|
}) {
|
|
1293
1294
|
const size = useTerminalSize();
|
|
1294
1295
|
const [tab, setTab] = useState4("summary");
|
|
@@ -1355,6 +1356,10 @@ function RecordingDetailView({
|
|
|
1355
1356
|
] }),
|
|
1356
1357
|
meta3 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: meta3 }) : null,
|
|
1357
1358
|
/* @__PURE__ */ jsx14(AudioActionRow, { item, audio }),
|
|
1359
|
+
localDir ? /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
1360
|
+
/* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "\u2302 Local \xB7 " }),
|
|
1361
|
+
/* @__PURE__ */ jsx14(Text12, { dimColor: true, wrap: "truncate-middle", children: localDir })
|
|
1362
|
+
] }) : null,
|
|
1358
1363
|
!item.activeTranscriptId ? /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Transcript not available yet" }) }) : transcript === "loading" || transcript === void 0 ? /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Loading\u2026" }) }) : transcript === "error" ? /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "(transcript unavailable)" }) }) : /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
1359
1364
|
/* @__PURE__ */ jsx14(TabBar, { active: tab }),
|
|
1360
1365
|
/* @__PURE__ */ jsx14(Box12, { marginTop: 1, flexDirection: "column", children: tab === "summary" ? /* @__PURE__ */ jsx14(SummaryPane, { summary, budget: paneBudget }) : tab === "chapters" ? /* @__PURE__ */ jsx14(ChaptersPane, { chapters, win: chapWin, selectedIndex: chapterSel }) : /* @__PURE__ */ jsx14(TranscriptPane, { segments, win: segWin }) })
|
|
@@ -1365,6 +1370,7 @@ function RecordingDetailView({
|
|
|
1365
1370
|
scrollable ? " \xB7 \u2191\u2193 scroll" : "",
|
|
1366
1371
|
ready ? " \xB7 " : "",
|
|
1367
1372
|
`o open \xB7 d download \xB7 f finder`,
|
|
1373
|
+
localDir ? " \xB7 l folder" : "",
|
|
1368
1374
|
" \xB7 T re-transcribe \xB7 s re-summarize \xB7 a ask \xB7 e export",
|
|
1369
1375
|
item.activeTranscriptId ? " \xB7 t full" : "",
|
|
1370
1376
|
links.webUrl ? " \xB7 w web" : "",
|
|
@@ -1487,12 +1493,14 @@ function AskScreen({
|
|
|
1487
1493
|
onOpenTranscript,
|
|
1488
1494
|
spinnerFrame
|
|
1489
1495
|
}) {
|
|
1496
|
+
const size = useTerminalSize();
|
|
1490
1497
|
const [phase, setPhase] = useState5("input");
|
|
1491
1498
|
const [question, setQuestion] = useState5("");
|
|
1492
1499
|
const [asked, setAsked] = useState5("");
|
|
1493
1500
|
const [content, setContent] = useState5("");
|
|
1494
1501
|
const [citations, setCitations] = useState5([]);
|
|
1495
1502
|
const [error51, setError] = useState5(void 0);
|
|
1503
|
+
const [scroll, setScroll] = useState5(0);
|
|
1496
1504
|
const runIdRef = useRef2(0);
|
|
1497
1505
|
const submit = useCallback(
|
|
1498
1506
|
(raw) => {
|
|
@@ -1504,6 +1512,7 @@ function AskScreen({
|
|
|
1504
1512
|
setContent("");
|
|
1505
1513
|
setCitations([]);
|
|
1506
1514
|
setError(void 0);
|
|
1515
|
+
setScroll(0);
|
|
1507
1516
|
void (async () => {
|
|
1508
1517
|
try {
|
|
1509
1518
|
let text = "";
|
|
@@ -1546,7 +1555,16 @@ function AskScreen({
|
|
|
1546
1555
|
setContent("");
|
|
1547
1556
|
setCitations([]);
|
|
1548
1557
|
setError(void 0);
|
|
1558
|
+
setScroll(0);
|
|
1549
1559
|
};
|
|
1560
|
+
const answerText = phase === "done" ? formatAskAnswerPlain(content, citations) : content ? strippedAskAnswer(content) : "";
|
|
1561
|
+
const innerWidth = Math.max(10, size.columns - 2);
|
|
1562
|
+
const lines = answerText ? wrapToLines(answerText, innerWidth) : [];
|
|
1563
|
+
const paneBudget = Math.max(3, size.rows - 9);
|
|
1564
|
+
const maxScroll = Math.max(0, lines.length - paneBudget);
|
|
1565
|
+
const effectiveScroll = phase === "asking" ? maxScroll : Math.min(scroll, maxScroll);
|
|
1566
|
+
const windowLines = lines.slice(effectiveScroll, effectiveScroll + paneBudget);
|
|
1567
|
+
const page = Math.max(1, paneBudget - 1);
|
|
1550
1568
|
useInput5((input, key) => {
|
|
1551
1569
|
if (key.escape) {
|
|
1552
1570
|
runIdRef.current++;
|
|
@@ -1559,10 +1577,14 @@ function AskScreen({
|
|
|
1559
1577
|
if (input && !key.ctrl && !key.meta) setQuestion((q) => q + input);
|
|
1560
1578
|
return;
|
|
1561
1579
|
}
|
|
1562
|
-
if (
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1580
|
+
if (key.downArrow || input === "j") setScroll((s) => Math.min(maxScroll, s + 1));
|
|
1581
|
+
else if (key.upArrow || input === "k") setScroll((s) => Math.max(0, s - 1));
|
|
1582
|
+
else if (key.pageDown || input === " ") setScroll((s) => Math.min(maxScroll, s + page));
|
|
1583
|
+
else if (key.pageUp || input === "b") setScroll((s) => Math.max(0, s - page));
|
|
1584
|
+
else if (input === "g") setScroll(0);
|
|
1585
|
+
else if (input === "G") setScroll(maxScroll);
|
|
1586
|
+
else if (input === "a") reset();
|
|
1587
|
+
else if (input === "t" && onOpenTranscript) onOpenTranscript();
|
|
1566
1588
|
});
|
|
1567
1589
|
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 1, children: [
|
|
1568
1590
|
/* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
@@ -1578,63 +1600,56 @@ function AskScreen({
|
|
|
1578
1600
|
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Type a question \xB7 \u23CE ask \xB7 esc back" }) })
|
|
1579
1601
|
] }) : /* @__PURE__ */ jsxs12(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
1580
1602
|
/* @__PURE__ */ jsx15(Text13, { dimColor: true, wrap: "truncate-end", children: `? ${asked}` }),
|
|
1581
|
-
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx15(
|
|
1582
|
-
|
|
1603
|
+
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, flexDirection: "column", children: phase === "asking" && !content ? /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: `${SPINNER2[spinnerFrame % SPINNER2.length]} Thinking\u2026` }) : windowLines.map((line, i) => /* @__PURE__ */ jsx15(AnswerLine, { line }, effectiveScroll + i)) }),
|
|
1604
|
+
maxScroll > 0 ? /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: ` ${Math.min(effectiveScroll + paneBudget, lines.length)} / ${lines.length}${phase === "asking" ? " \xB7 streaming" : " \xB7 \u2191\u2193 scroll"}` }) : null,
|
|
1605
|
+
phase === "error" ? /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text13, { color: "red", children: error51 ? `Ask failed: ${error51}` : "Ask failed" }) }) : null,
|
|
1583
1606
|
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
1584
1607
|
phase === "asking" ? "esc cancel" : "a ask again",
|
|
1585
1608
|
(phase === "done" || phase === "error") && onOpenTranscript ? " \xB7 t transcript" : "",
|
|
1609
|
+
maxScroll > 0 ? " \xB7 \u2191\u2193 scroll" : "",
|
|
1586
1610
|
phase !== "asking" ? " \xB7 esc back" : ""
|
|
1587
1611
|
] }) })
|
|
1588
1612
|
] })
|
|
1589
1613
|
] });
|
|
1590
1614
|
}
|
|
1591
|
-
function
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
spinnerFrame
|
|
1597
|
-
}) {
|
|
1598
|
-
if (phase === "error") {
|
|
1599
|
-
return /* @__PURE__ */ jsx15(Text13, { color: "red", children: error51 ? `Ask failed: ${error51}` : "Ask failed" });
|
|
1600
|
-
}
|
|
1601
|
-
if (phase === "asking" && !content) {
|
|
1602
|
-
return /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: `${SPINNER2[spinnerFrame % SPINNER2.length]} Thinking\u2026` });
|
|
1603
|
-
}
|
|
1604
|
-
if (phase === "asking") {
|
|
1605
|
-
return /* @__PURE__ */ jsx15(Text13, { children: strippedAskAnswer(content) });
|
|
1606
|
-
}
|
|
1607
|
-
const segments = renderAskInline(content, citations);
|
|
1608
|
-
return /* @__PURE__ */ jsx15(Text13, { children: segments.map(
|
|
1609
|
-
(segment, i) => segment.kind === "text" ? /* @__PURE__ */ jsx15(Text13, { children: segment.text }, i) : /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: `\u27E8${segment.label}\u27E9` }, i)
|
|
1615
|
+
function AnswerLine({ line }) {
|
|
1616
|
+
if (line === "") return /* @__PURE__ */ jsx15(Text13, { children: " " });
|
|
1617
|
+
const parts = line.split(/(⟨[^⟩]*⟩)/);
|
|
1618
|
+
return /* @__PURE__ */ jsx15(Text13, { children: parts.map(
|
|
1619
|
+
(part, i) => part.startsWith("\u27E8") && part.endsWith("\u27E9") ? /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: part }, i) : /* @__PURE__ */ jsx15(Text13, { children: part }, i)
|
|
1610
1620
|
) });
|
|
1611
1621
|
}
|
|
1612
|
-
function
|
|
1613
|
-
const
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1622
|
+
function wrapToLines(text, width) {
|
|
1623
|
+
const out = [];
|
|
1624
|
+
for (const paragraph of text.split("\n")) {
|
|
1625
|
+
if (paragraph === "") {
|
|
1626
|
+
out.push("");
|
|
1627
|
+
continue;
|
|
1628
|
+
}
|
|
1629
|
+
let current = "";
|
|
1630
|
+
let currentWidth = 0;
|
|
1631
|
+
for (const ch of paragraph) {
|
|
1632
|
+
const w = displayWidth(ch);
|
|
1633
|
+
if (current !== "" && currentWidth + w > width) {
|
|
1634
|
+
out.push(current);
|
|
1635
|
+
current = ch;
|
|
1636
|
+
currentWidth = w;
|
|
1637
|
+
} else {
|
|
1638
|
+
current += ch;
|
|
1639
|
+
currentWidth += w;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
out.push(current);
|
|
1643
|
+
}
|
|
1644
|
+
return out;
|
|
1632
1645
|
}
|
|
1633
1646
|
var SPINNER2;
|
|
1634
1647
|
var init_AskScreen = __esm({
|
|
1635
1648
|
"src/tui/AskScreen.tsx"() {
|
|
1636
1649
|
"use strict";
|
|
1637
1650
|
init_askInline();
|
|
1651
|
+
init_format();
|
|
1652
|
+
init_terminal();
|
|
1638
1653
|
SPINNER2 = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F";
|
|
1639
1654
|
}
|
|
1640
1655
|
});
|
|
@@ -2769,6 +2784,13 @@ function AppShell({
|
|
|
2769
2784
|
},
|
|
2770
2785
|
[onResummarize, refresh, recordings, now, refetchTranscript]
|
|
2771
2786
|
);
|
|
2787
|
+
const [sessionDirByRecording, setSessionDirByRecording] = useState9(
|
|
2788
|
+
() => /* @__PURE__ */ new Map()
|
|
2789
|
+
);
|
|
2790
|
+
const rememberSessionDir = useCallback2((recordingId, dir) => {
|
|
2791
|
+
if (!dir) return;
|
|
2792
|
+
setSessionDirByRecording((m) => m.get(recordingId) === dir ? m : new Map(m).set(recordingId, dir));
|
|
2793
|
+
}, []);
|
|
2772
2794
|
const syncedTextRef = useRef4(/* @__PURE__ */ new Set());
|
|
2773
2795
|
const syncRecordingText2 = useCallback2(
|
|
2774
2796
|
async (recordingId, opts = {}) => {
|
|
@@ -2777,12 +2799,13 @@ function AppShell({
|
|
|
2777
2799
|
try {
|
|
2778
2800
|
const data = await onSyncRecordingText(recordingId);
|
|
2779
2801
|
syncedTextRef.current.add(recordingId);
|
|
2802
|
+
rememberSessionDir(recordingId, data.sessionDir);
|
|
2780
2803
|
if (opts.manual) setNotice(`Text synced \xB7 ${data.sessionDir}`);
|
|
2781
2804
|
} catch (error51) {
|
|
2782
2805
|
if (opts.manual) setNotice(transcribeHandoffErrorCopy(error51));
|
|
2783
2806
|
}
|
|
2784
2807
|
},
|
|
2785
|
-
[onSyncRecordingText]
|
|
2808
|
+
[onSyncRecordingText, rememberSessionDir]
|
|
2786
2809
|
);
|
|
2787
2810
|
const syncRecordingAudio2 = useCallback2(
|
|
2788
2811
|
async (recordingId) => {
|
|
@@ -2793,12 +2816,33 @@ function AppShell({
|
|
|
2793
2816
|
setNotice("Downloading audio\u2026");
|
|
2794
2817
|
try {
|
|
2795
2818
|
const data = await onSyncRecordingAudio(recordingId);
|
|
2819
|
+
rememberSessionDir(recordingId, data.sessionDir);
|
|
2796
2820
|
setNotice(`Audio saved \xB7 ${data.audioPath}`);
|
|
2797
2821
|
} catch (error51) {
|
|
2798
2822
|
setNotice(transcribeHandoffErrorCopy(error51));
|
|
2799
2823
|
}
|
|
2800
2824
|
},
|
|
2801
|
-
[onSyncRecordingAudio]
|
|
2825
|
+
[onSyncRecordingAudio, rememberSessionDir]
|
|
2826
|
+
);
|
|
2827
|
+
const openLocalFolder = useCallback2(
|
|
2828
|
+
async (recordingId) => {
|
|
2829
|
+
const dir = sessionDirByRecording.get(recordingId);
|
|
2830
|
+
if (!dir) {
|
|
2831
|
+
setNotice("No local copy yet \u2014 it syncs when you open the recording.");
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
if (!recordingAudio) {
|
|
2835
|
+
setNotice("Opening the local folder isn't available in this CLI session.");
|
|
2836
|
+
return;
|
|
2837
|
+
}
|
|
2838
|
+
try {
|
|
2839
|
+
await recordingAudio.openPath(dir);
|
|
2840
|
+
setNotice(`Opened ${dir}`);
|
|
2841
|
+
} catch (error51) {
|
|
2842
|
+
setNotice(transcribeHandoffErrorCopy(error51));
|
|
2843
|
+
}
|
|
2844
|
+
},
|
|
2845
|
+
[sessionDirByRecording, recordingAudio]
|
|
2802
2846
|
);
|
|
2803
2847
|
const exportRecordingForAgent = useCallback2(
|
|
2804
2848
|
async (recordingId) => {
|
|
@@ -3125,6 +3169,7 @@ function AppShell({
|
|
|
3125
3169
|
if (onSyncRecordingAudio) void syncRecordingAudio2(rec.recordingId);
|
|
3126
3170
|
else void runAudio(rec.recordingId, "download");
|
|
3127
3171
|
} else if (input === "f" && rec) void runAudio(rec.recordingId, "finder");
|
|
3172
|
+
else if (input === "l" && rec) void openLocalFolder(rec.recordingId);
|
|
3128
3173
|
else if (input === "w" && links.webUrl) openUrl2?.(links.webUrl);
|
|
3129
3174
|
else if (input === "c" && links.webUrl) {
|
|
3130
3175
|
copyText2?.(links.webUrl);
|
|
@@ -3166,7 +3211,8 @@ function AppShell({
|
|
|
3166
3211
|
item: rec,
|
|
3167
3212
|
nowMs: now(),
|
|
3168
3213
|
transcript: detailTranscript,
|
|
3169
|
-
audio: audioCache.get(rec.recordingId)
|
|
3214
|
+
audio: audioCache.get(rec.recordingId),
|
|
3215
|
+
localDir: sessionDirByRecording.get(rec.recordingId)
|
|
3170
3216
|
}
|
|
3171
3217
|
) });
|
|
3172
3218
|
}
|