recappi 0.1.67 → 0.1.69

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 CHANGED
@@ -1235,6 +1235,7 @@ function RecordingDetailView({
1235
1235
  scrollable ? " \xB7 \u2191\u2193 scroll" : "",
1236
1236
  ready ? " \xB7 " : "",
1237
1237
  `o open \xB7 d download \xB7 f finder`,
1238
+ " \xB7 T re-transcribe",
1238
1239
  item.activeTranscriptId ? " \xB7 t full" : "",
1239
1240
  links.webUrl ? " \xB7 w web" : "",
1240
1241
  " \xB7 esc back"
@@ -2006,6 +2007,7 @@ function AppShell({
2006
2007
  const [selected, setSelected] = useState8(0);
2007
2008
  const [spinnerFrame, setSpinnerFrame] = useState8(0);
2008
2009
  const [loadingMoreRecordings, setLoadingMoreRecordings] = useState8(false);
2010
+ const [loaded, setLoaded] = useState8(false);
2009
2011
  const [loadError, setLoadError] = useState8(void 0);
2010
2012
  const [notice, setNotice] = useState8(void 0);
2011
2013
  const [summaryCache, setSummaryCache] = useState8(() => /* @__PURE__ */ new Map());
@@ -2245,6 +2247,7 @@ function AppShell({
2245
2247
  } else {
2246
2248
  setAccountStatus("error");
2247
2249
  }
2250
+ setLoaded(true);
2248
2251
  }, [fetchJobs, fetchRecordings, fetchDashboardStats, fetchAccountStatus]);
2249
2252
  const transcribeStoppedRecording = useCallback(async () => {
2250
2253
  const current = liveRecord;
@@ -2385,6 +2388,23 @@ function AppShell({
2385
2388
  setNotice("Re-transcription failed. Press T to retry.");
2386
2389
  }
2387
2390
  }, [liveRecord, onRetranscribe, refresh]);
2391
+ const retranscribeExistingRecording = useCallback(
2392
+ async (recordingId) => {
2393
+ if (!onRetranscribe) {
2394
+ setNotice("Re-transcribe is not available in this CLI session.");
2395
+ return;
2396
+ }
2397
+ setNotice("Re-transcribe started\u2026");
2398
+ try {
2399
+ await onRetranscribe(recordingId);
2400
+ setNotice("Re-transcribe started \u2014 track it in Jobs.");
2401
+ await refresh({ resetRecordings: true });
2402
+ } catch (error51) {
2403
+ setNotice(transcribeHandoffErrorCopy(error51));
2404
+ }
2405
+ },
2406
+ [onRetranscribe, refresh]
2407
+ );
2388
2408
  useEffect4(() => {
2389
2409
  if (liveRecord?.kind !== "stopped") return;
2390
2410
  const artifact = liveRecord.artifact;
@@ -2428,10 +2448,10 @@ function AppShell({
2428
2448
  }, [refresh, pollMs]);
2429
2449
  const hasRunning = jobs.some((item) => item.status === "running");
2430
2450
  useEffect4(() => {
2431
- if (!hasRunning) return;
2451
+ if (!hasRunning && loaded) return;
2432
2452
  const id = setInterval(() => setSpinnerFrame((f) => f + 1), spinnerMs);
2433
2453
  return () => clearInterval(id);
2434
- }, [hasRunning, spinnerMs]);
2454
+ }, [hasRunning, loaded, spinnerMs]);
2435
2455
  const jobRank = (s) => s === "running" ? 4 : s === "queued" ? 3 : s === "failed" ? 2 : s === "succeeded" ? 1 : 0;
2436
2456
  const jobStatusByRecording = /* @__PURE__ */ new Map();
2437
2457
  for (const job of jobs) {
@@ -2646,7 +2666,8 @@ function AppShell({
2646
2666
  if (screen.kind === "recordingDetail") {
2647
2667
  const rec = recordings.find((r) => r.recordingId === screen.recordingId);
2648
2668
  const links = rec ? resolveRecordingLinks(rec.recordingId, rec.origin) : {};
2649
- if (input === "t" && rec?.activeTranscriptId) void openTranscript(rec.activeTranscriptId);
2669
+ if (input === "T" && rec) void retranscribeExistingRecording(rec.recordingId);
2670
+ else if (input === "t" && rec?.activeTranscriptId) void openTranscript(rec.activeTranscriptId);
2650
2671
  else if (input === "o" && rec) void runAudio(rec.recordingId, "open");
2651
2672
  else if (input === "d" && rec) void runAudio(rec.recordingId, "download");
2652
2673
  else if (input === "f" && rec) void runAudio(rec.recordingId, "finder");
@@ -2663,12 +2684,12 @@ function AppShell({
2663
2684
  }
2664
2685
  if (screen.kind === "jobDetail") {
2665
2686
  const job = jobs.find((j) => j.jobId === screen.jobId);
2666
- if (!job) return /* @__PURE__ */ jsx19(Missing, { label: "Job" });
2687
+ if (!job) return !loaded ? /* @__PURE__ */ jsx19(Loading, { label: "job" }) : /* @__PURE__ */ jsx19(Missing, { label: "Job" });
2667
2688
  return /* @__PURE__ */ jsx19(Detail, { notice, children: /* @__PURE__ */ jsx19(JobDetailView, { item: job, origin, spinnerFrame, nowMs: now() }) });
2668
2689
  }
2669
2690
  if (screen.kind === "recordingDetail") {
2670
2691
  const rec = recordings.find((r) => r.recordingId === screen.recordingId);
2671
- if (!rec) return /* @__PURE__ */ jsx19(Missing, { label: "Recording" });
2692
+ if (!rec) return !loaded ? /* @__PURE__ */ jsx19(Loading, { label: "recording" }) : /* @__PURE__ */ jsx19(Missing, { label: "Recording" });
2672
2693
  const detailTranscript = rec.activeTranscriptId ? transcriptCache.get(rec.activeTranscriptId) : void 0;
2673
2694
  return /* @__PURE__ */ jsx19(Detail, { notice, children: /* @__PURE__ */ jsx19(
2674
2695
  RecordingDetailView,
@@ -2735,7 +2756,14 @@ function AppShell({
2735
2756
  const tab = screen.kind === "jobs" ? "jobs" : screen.kind === "account" ? "account" : "overview";
2736
2757
  let body;
2737
2758
  let position = "";
2738
- if (screen.kind === "overview") {
2759
+ if (!loaded) {
2760
+ position = "";
2761
+ const spin = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F"[spinnerFrame % 10];
2762
+ body = /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text17, { color: "cyan", children: [
2763
+ spin,
2764
+ " Loading\u2026"
2765
+ ] }) });
2766
+ } else if (screen.kind === "overview") {
2739
2767
  const listBudget = Math.max(3, size.rows - 6);
2740
2768
  const buckets = recordings.map((r) => dateBucket(r.createdAt, now()));
2741
2769
  const win = groupedListWindow(buckets, selected, listBudget);
@@ -2811,6 +2839,13 @@ function Missing({ label }) {
2811
2839
  /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "esc back \xB7 q quit" })
2812
2840
  ] });
2813
2841
  }
2842
+ function Loading({ label }) {
2843
+ return /* @__PURE__ */ jsx19(Box17, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text17, { color: "cyan", children: [
2844
+ "Loading ",
2845
+ label,
2846
+ "\u2026"
2847
+ ] }) });
2848
+ }
2814
2849
  var RECORDINGS_PAGE_SIZE, RECORDINGS_PREFETCH_REMAINING;
2815
2850
  var init_AppShell = __esm({
2816
2851
  "src/tui/AppShell.tsx"() {
@@ -17876,6 +17911,9 @@ var jobDataSchema = external_exports.object({
17876
17911
  model: external_exports.string().optional(),
17877
17912
  language: external_exports.string().nullable().optional(),
17878
17913
  progressPercent: external_exports.number().min(0).max(100).nullable().optional(),
17914
+ claimExpiresAt: external_exports.number().int().nullable().optional(),
17915
+ lastHeartbeatAt: external_exports.number().int().nullable().optional(),
17916
+ heartbeatPhase: external_exports.string().nullable().optional(),
17879
17917
  processedDurationMs: external_exports.number().int().nonnegative().nullable().optional(),
17880
17918
  recording: external_exports.object({
17881
17919
  title: external_exports.string().nullable().optional(),
@@ -17902,6 +17940,8 @@ var jobListItemSchema = external_exports.object({
17902
17940
  enqueuedAt: external_exports.number().int().nullable().optional(),
17903
17941
  startedAt: external_exports.number().int().nullable().optional(),
17904
17942
  finishedAt: external_exports.number().int().nullable().optional(),
17943
+ claimExpiresAt: external_exports.number().int().nullable().optional(),
17944
+ lastHeartbeatAt: external_exports.number().int().nullable().optional(),
17905
17945
  processedDurationMs: external_exports.number().int().nonnegative().nullable().optional(),
17906
17946
  heartbeatPhase: external_exports.string().nullable().optional(),
17907
17947
  recording: external_exports.object({
@@ -19541,6 +19581,9 @@ var RecappiApiClient = class {
19541
19581
  ...typeof parsed.model === "string" ? { model: parsed.model } : {},
19542
19582
  ...typeof parsed.language === "string" || parsed.language === null ? { language: parsed.language } : {},
19543
19583
  ...progressPercent !== void 0 ? { progressPercent } : {},
19584
+ ...typeof parsed.claimExpiresAt === "number" || parsed.claimExpiresAt === null ? { claimExpiresAt: parsed.claimExpiresAt } : {},
19585
+ ...typeof parsed.lastHeartbeatAt === "number" || parsed.lastHeartbeatAt === null ? { lastHeartbeatAt: parsed.lastHeartbeatAt } : {},
19586
+ ...typeof parsed.heartbeatPhase === "string" || parsed.heartbeatPhase === null ? { heartbeatPhase: parsed.heartbeatPhase } : {},
19544
19587
  ...processedDurationMs !== void 0 ? { processedDurationMs } : {},
19545
19588
  ...recording ? {
19546
19589
  recording: {
@@ -19805,6 +19848,8 @@ function mapJobListItem(row) {
19805
19848
  ...typeof row.enqueuedAt === "number" || row.enqueuedAt === null ? { enqueuedAt: row.enqueuedAt } : {},
19806
19849
  ...typeof row.startedAt === "number" || row.startedAt === null ? { startedAt: row.startedAt } : {},
19807
19850
  ...typeof row.finishedAt === "number" || row.finishedAt === null ? { finishedAt: row.finishedAt } : {},
19851
+ ...typeof row.claimExpiresAt === "number" || row.claimExpiresAt === null ? { claimExpiresAt: row.claimExpiresAt } : {},
19852
+ ...typeof row.lastHeartbeatAt === "number" || row.lastHeartbeatAt === null ? { lastHeartbeatAt: row.lastHeartbeatAt } : {},
19808
19853
  ...typeof row.processedDurationMs === "number" || row.processedDurationMs === null ? { processedDurationMs: row.processedDurationMs } : {},
19809
19854
  ...typeof row.heartbeatPhase === "string" || row.heartbeatPhase === null ? { heartbeatPhase: row.heartbeatPhase } : {},
19810
19855
  recording: {