@ncukondo/reference-manager 0.15.2 → 0.16.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/chunks/alternate-screen-DcxkOKfW.js +19 -0
- package/dist/chunks/alternate-screen-DcxkOKfW.js.map +1 -0
- package/dist/chunks/format-C6FA-7hE.js +397 -0
- package/dist/chunks/format-C6FA-7hE.js.map +1 -0
- package/dist/chunks/{index-BIFsFHVj.js → index-4SVOiraD.js} +120 -81
- package/dist/chunks/index-4SVOiraD.js.map +1 -0
- package/dist/chunks/{index-C30Ouuee.js → index-Bzl4_3Ki.js} +2 -2
- package/dist/chunks/index-Bzl4_3Ki.js.map +1 -0
- package/dist/chunks/index-CEYp8OSj.js +531 -0
- package/dist/chunks/index-CEYp8OSj.js.map +1 -0
- package/dist/chunks/jsx-runtime-Q5cUjSur.js +322 -0
- package/dist/chunks/jsx-runtime-Q5cUjSur.js.map +1 -0
- package/dist/chunks/{loader-DStZe-OB.js → loader-DuhmXjiI.js} +8 -1
- package/dist/chunks/loader-DuhmXjiI.js.map +1 -0
- package/dist/chunks/reference-select-CgM-RBIa.js +214 -0
- package/dist/chunks/reference-select-CgM-RBIa.js.map +1 -0
- package/dist/chunks/{style-select-BNQHC79W.js → style-select-tmKOHx_B.js} +6 -31
- package/dist/chunks/style-select-tmKOHx_B.js.map +1 -0
- package/dist/cli/commands/attach.d.ts +1 -1
- package/dist/cli/commands/attach.d.ts.map +1 -1
- package/dist/cli/commands/cite.d.ts.map +1 -1
- package/dist/cli/commands/edit.d.ts.map +1 -1
- package/dist/cli/commands/fulltext.d.ts.map +1 -1
- package/dist/cli/commands/remove.d.ts.map +1 -1
- package/dist/cli/commands/search.d.ts +1 -1
- package/dist/cli/commands/search.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/features/interactive/action-menu.d.ts +10 -12
- package/dist/features/interactive/action-menu.d.ts.map +1 -1
- package/dist/features/interactive/alternate-screen.d.ts +42 -0
- package/dist/features/interactive/alternate-screen.d.ts.map +1 -0
- package/dist/features/interactive/apps/CiteFlowApp.d.ts +52 -0
- package/dist/features/interactive/apps/CiteFlowApp.d.ts.map +1 -0
- package/dist/features/interactive/apps/SearchFlowApp.d.ts +34 -0
- package/dist/features/interactive/apps/SearchFlowApp.d.ts.map +1 -0
- package/dist/features/interactive/apps/index.d.ts +11 -0
- package/dist/features/interactive/apps/index.d.ts.map +1 -0
- package/dist/features/interactive/apps/runCiteFlow.d.ts +42 -0
- package/dist/features/interactive/apps/runCiteFlow.d.ts.map +1 -0
- package/dist/features/interactive/apps/runSearchFlow.d.ts +28 -0
- package/dist/features/interactive/apps/runSearchFlow.d.ts.map +1 -0
- package/dist/features/interactive/components/SearchableMultiSelect.d.ts +49 -0
- package/dist/features/interactive/components/SearchableMultiSelect.d.ts.map +1 -0
- package/dist/features/interactive/components/Select.d.ts +26 -0
- package/dist/features/interactive/components/Select.d.ts.map +1 -0
- package/dist/features/interactive/components/index.d.ts +8 -0
- package/dist/features/interactive/components/index.d.ts.map +1 -0
- package/dist/features/interactive/search-prompt.d.ts +15 -12
- package/dist/features/interactive/search-prompt.d.ts.map +1 -1
- package/dist/features/interactive/style-select.d.ts +3 -11
- package/dist/features/interactive/style-select.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/opener.d.ts.map +1 -1
- package/package.json +7 -4
- package/dist/chunks/action-menu-CLBXtpmg.js +0 -119
- package/dist/chunks/action-menu-CLBXtpmg.js.map +0 -1
- package/dist/chunks/index-BIFsFHVj.js.map +0 -1
- package/dist/chunks/index-C30Ouuee.js.map +0 -1
- package/dist/chunks/loader-DStZe-OB.js.map +0 -1
- package/dist/chunks/reference-select-B9w9CLa1.js +0 -52
- package/dist/chunks/reference-select-B9w9CLa1.js.map +0 -1
- package/dist/chunks/search-prompt-BrWpOcij.js +0 -265
- package/dist/chunks/search-prompt-BrWpOcij.js.map +0 -1
- package/dist/chunks/style-select-BNQHC79W.js.map +0 -1
- /package/bin/{reference-manager.js → cli.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a, d, g, l, o, s } from "./index-
|
|
1
|
+
import { a, d, g, l, o, s } from "./index-4SVOiraD.js";
|
|
2
2
|
export {
|
|
3
3
|
a as addAttachment,
|
|
4
4
|
d as detachAttachment,
|
|
@@ -7,4 +7,4 @@ export {
|
|
|
7
7
|
o as openAttachment,
|
|
8
8
|
s as syncAttachments
|
|
9
9
|
};
|
|
10
|
-
//# sourceMappingURL=index-
|
|
10
|
+
//# sourceMappingURL=index-Bzl4_3Ki.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Bzl4_3Ki.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import { useFocus, useInput, Box, Text, useApp, render } from "ink";
|
|
2
|
+
import { useState, useEffect, createElement } from "react";
|
|
3
|
+
import { f as formatBibtex } from "./index-4SVOiraD.js";
|
|
4
|
+
import { f as formatBibliographyCSL } from "./index-DHgeuWGP.js";
|
|
5
|
+
import { j as jsxRuntimeExports } from "./jsx-runtime-Q5cUjSur.js";
|
|
6
|
+
import { S as SearchableMultiSelect, f as formatAuthors } from "./format-C6FA-7hE.js";
|
|
7
|
+
import { restoreStdinAfterInk } from "./alternate-screen-DcxkOKfW.js";
|
|
8
|
+
function Select({
|
|
9
|
+
options,
|
|
10
|
+
message,
|
|
11
|
+
onSelect,
|
|
12
|
+
onCancel,
|
|
13
|
+
initialIndex = 0
|
|
14
|
+
}) {
|
|
15
|
+
const [focusIndex, setFocusIndex] = useState(initialIndex);
|
|
16
|
+
const { isFocused } = useFocus({ autoFocus: true });
|
|
17
|
+
useInput(
|
|
18
|
+
(input, key) => {
|
|
19
|
+
if (key.escape) {
|
|
20
|
+
onCancel();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (key.return) {
|
|
24
|
+
const selected = options[focusIndex];
|
|
25
|
+
if (selected) {
|
|
26
|
+
onSelect(selected.value);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (key.upArrow || input === "k") {
|
|
31
|
+
setFocusIndex((prev) => Math.max(0, prev - 1));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (key.downArrow || input === "j") {
|
|
35
|
+
setFocusIndex((prev) => Math.min(options.length - 1, prev + 1));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{ isActive: isFocused }
|
|
40
|
+
);
|
|
41
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { flexDirection: "column", children: [
|
|
42
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { bold: true, color: "cyan", children: message }) }),
|
|
43
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Box, { flexDirection: "column", children: options.map((option, index) => {
|
|
44
|
+
const isFocusedItem = index === focusIndex;
|
|
45
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { flexDirection: "row", children: [
|
|
46
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Box, { width: 2, children: isFocusedItem ? /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { color: "cyan", children: "❯" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { children: " " }) }),
|
|
47
|
+
isFocusedItem ? /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { color: "cyan", bold: true, children: option.label }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { children: option.label }),
|
|
48
|
+
option.hint && /* @__PURE__ */ jsxRuntimeExports.jsxs(Text, { dimColor: true, children: [
|
|
49
|
+
" (",
|
|
50
|
+
option.hint,
|
|
51
|
+
")"
|
|
52
|
+
] })
|
|
53
|
+
] }, `${option.label}-${index}`);
|
|
54
|
+
}) }),
|
|
55
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { dimColor: true, children: "(↑↓: navigate, Enter: select, Esc: cancel)" }) })
|
|
56
|
+
] });
|
|
57
|
+
}
|
|
58
|
+
const ACTION_CHOICES = [
|
|
59
|
+
{ label: "Output IDs (citation keys)", value: "output-ids" },
|
|
60
|
+
{ label: "Output as CSL-JSON", value: "output-csl-json" },
|
|
61
|
+
{ label: "Output as BibTeX", value: "output-bibtex" },
|
|
62
|
+
{ label: "Generate citation (APA)", value: "cite-apa" },
|
|
63
|
+
{ label: "Generate citation (choose style)", value: "cite-choose" },
|
|
64
|
+
{ label: "Cancel", value: "cancel" }
|
|
65
|
+
];
|
|
66
|
+
const STYLE_CHOICES = [
|
|
67
|
+
{ label: "APA", value: "apa" },
|
|
68
|
+
{ label: "Vancouver", value: "vancouver" },
|
|
69
|
+
{ label: "Harvard", value: "harvard" }
|
|
70
|
+
];
|
|
71
|
+
function generateOutput(action, items, style = "apa") {
|
|
72
|
+
switch (action) {
|
|
73
|
+
case "output-ids":
|
|
74
|
+
return items.map((item) => item.id).join("\n");
|
|
75
|
+
case "output-csl-json":
|
|
76
|
+
return JSON.stringify(items, null, 2);
|
|
77
|
+
case "output-bibtex":
|
|
78
|
+
return formatBibtex(items);
|
|
79
|
+
case "cite-apa":
|
|
80
|
+
return formatBibliographyCSL(items, { style: "apa" });
|
|
81
|
+
case "cite-choose":
|
|
82
|
+
return formatBibliographyCSL(items, { style });
|
|
83
|
+
case "cancel":
|
|
84
|
+
return "";
|
|
85
|
+
default:
|
|
86
|
+
return "";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function SearchFlowApp({
|
|
90
|
+
choices,
|
|
91
|
+
filterFn,
|
|
92
|
+
visibleCount,
|
|
93
|
+
defaultSort,
|
|
94
|
+
onComplete,
|
|
95
|
+
onCancel
|
|
96
|
+
}) {
|
|
97
|
+
const { exit } = useApp();
|
|
98
|
+
const [state, setState] = useState("search");
|
|
99
|
+
const [selectedItems, setSelectedItems] = useState([]);
|
|
100
|
+
const [pendingResult, setPendingResult] = useState(null);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (state === "exiting" && pendingResult) {
|
|
103
|
+
exit();
|
|
104
|
+
if (pendingResult.cancelled) {
|
|
105
|
+
onCancel();
|
|
106
|
+
} else {
|
|
107
|
+
onComplete(pendingResult);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}, [state, pendingResult, exit, onCancel, onComplete]);
|
|
111
|
+
const exitWith = (result) => {
|
|
112
|
+
setPendingResult(result);
|
|
113
|
+
setState("exiting");
|
|
114
|
+
};
|
|
115
|
+
const handleSearchSubmit = (selected) => {
|
|
116
|
+
if (selected.length === 0) {
|
|
117
|
+
exitWith({ action: "cancel", output: "", cancelled: true });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
setSelectedItems(selected.map((c) => c.value));
|
|
121
|
+
setState("action");
|
|
122
|
+
};
|
|
123
|
+
const handleSearchCancel = () => {
|
|
124
|
+
exitWith({ action: "cancel", output: "", cancelled: true });
|
|
125
|
+
};
|
|
126
|
+
const handleActionSelect = (action) => {
|
|
127
|
+
if (action === "cancel") {
|
|
128
|
+
exitWith({ action: "cancel", output: "", cancelled: true });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (action === "cite-choose") {
|
|
132
|
+
setState("style");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const output = generateOutput(action, selectedItems);
|
|
136
|
+
exitWith({ action, output, cancelled: false });
|
|
137
|
+
};
|
|
138
|
+
const handleActionCancel = () => {
|
|
139
|
+
setState("search");
|
|
140
|
+
};
|
|
141
|
+
const handleStyleSelect = (style) => {
|
|
142
|
+
const output = generateOutput("cite-choose", selectedItems, style);
|
|
143
|
+
exitWith({ action: "cite-choose", output, cancelled: false });
|
|
144
|
+
};
|
|
145
|
+
const handleStyleCancel = () => {
|
|
146
|
+
setState("action");
|
|
147
|
+
};
|
|
148
|
+
if (state === "exiting") {
|
|
149
|
+
return createElement(Box);
|
|
150
|
+
}
|
|
151
|
+
if (state === "search") {
|
|
152
|
+
return createElement(SearchableMultiSelect, {
|
|
153
|
+
choices,
|
|
154
|
+
filterFn,
|
|
155
|
+
visibleCount,
|
|
156
|
+
onSubmit: handleSearchSubmit,
|
|
157
|
+
onCancel: handleSearchCancel,
|
|
158
|
+
header: "Search references",
|
|
159
|
+
placeholder: "Type to search...",
|
|
160
|
+
defaultSort
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (state === "action") {
|
|
164
|
+
const count = selectedItems.length;
|
|
165
|
+
const refWord = count === 1 ? "reference" : "references";
|
|
166
|
+
return createElement(Select, {
|
|
167
|
+
options: ACTION_CHOICES,
|
|
168
|
+
message: `Action for ${count} selected ${refWord}:`,
|
|
169
|
+
onSelect: handleActionSelect,
|
|
170
|
+
onCancel: handleActionCancel
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return createElement(Select, {
|
|
174
|
+
options: STYLE_CHOICES,
|
|
175
|
+
message: "Select citation style:",
|
|
176
|
+
onSelect: handleStyleSelect,
|
|
177
|
+
onCancel: handleStyleCancel
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function getTerminalHeight$1() {
|
|
181
|
+
return process.stdout.rows ?? 24;
|
|
182
|
+
}
|
|
183
|
+
function calculateEffectiveLimit$1(configLimit) {
|
|
184
|
+
const terminalHeight = getTerminalHeight$1();
|
|
185
|
+
const reservedLines = 10;
|
|
186
|
+
const linesPerItem = 3;
|
|
187
|
+
const availableLines = terminalHeight - reservedLines;
|
|
188
|
+
const maxVisibleChoices = Math.max(1, Math.floor(availableLines / linesPerItem));
|
|
189
|
+
return configLimit > 0 ? Math.min(configLimit, maxVisibleChoices) : maxVisibleChoices;
|
|
190
|
+
}
|
|
191
|
+
function extractYear$1(item) {
|
|
192
|
+
const dateParts = item.issued?.["date-parts"];
|
|
193
|
+
if (!dateParts || dateParts.length === 0) return void 0;
|
|
194
|
+
const firstDatePart = dateParts[0];
|
|
195
|
+
if (!firstDatePart || firstDatePart.length === 0) return void 0;
|
|
196
|
+
return firstDatePart[0];
|
|
197
|
+
}
|
|
198
|
+
function extractPublishedDate$1(item) {
|
|
199
|
+
const dateParts = item.issued?.["date-parts"];
|
|
200
|
+
if (!dateParts || dateParts.length === 0) return void 0;
|
|
201
|
+
const firstDatePart = dateParts[0];
|
|
202
|
+
if (!firstDatePart || firstDatePart.length === 0) return void 0;
|
|
203
|
+
const [year, month = 1, day = 1] = firstDatePart;
|
|
204
|
+
if (year === void 0) return void 0;
|
|
205
|
+
return new Date(year, month - 1, day);
|
|
206
|
+
}
|
|
207
|
+
function extractUpdatedDate$1(item) {
|
|
208
|
+
const dateStr = item.custom?.timestamp;
|
|
209
|
+
if (!dateStr || typeof dateStr !== "string") return void 0;
|
|
210
|
+
const date = new Date(dateStr);
|
|
211
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
212
|
+
}
|
|
213
|
+
function extractCreatedDate$1(item) {
|
|
214
|
+
const dateStr = item.custom?.created_at;
|
|
215
|
+
if (!dateStr || typeof dateStr !== "string") return void 0;
|
|
216
|
+
const date = new Date(dateStr);
|
|
217
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
218
|
+
}
|
|
219
|
+
function formatIdentifiers$1(item) {
|
|
220
|
+
const parts = [];
|
|
221
|
+
if (item.DOI) parts.push(`DOI: ${item.DOI}`);
|
|
222
|
+
if (item.PMID) parts.push(`PMID: ${item.PMID}`);
|
|
223
|
+
if (item.PMCID) parts.push(`PMCID: ${item.PMCID}`);
|
|
224
|
+
if (item.ISBN) parts.push(`ISBN: ${item.ISBN}`);
|
|
225
|
+
return parts.join(" · ");
|
|
226
|
+
}
|
|
227
|
+
function formatType$1(type) {
|
|
228
|
+
const typeMap = {
|
|
229
|
+
"article-journal": "Journal article",
|
|
230
|
+
"article-magazine": "Magazine article",
|
|
231
|
+
"article-newspaper": "Newspaper article",
|
|
232
|
+
book: "Book",
|
|
233
|
+
chapter: "Book chapter",
|
|
234
|
+
"paper-conference": "Conference paper",
|
|
235
|
+
thesis: "Thesis",
|
|
236
|
+
report: "Report",
|
|
237
|
+
webpage: "Web page"
|
|
238
|
+
};
|
|
239
|
+
return typeMap[type] ?? type;
|
|
240
|
+
}
|
|
241
|
+
function toChoice$1(item) {
|
|
242
|
+
const authors = formatAuthors(item.author);
|
|
243
|
+
const year = extractYear$1(item);
|
|
244
|
+
const identifiers = formatIdentifiers$1(item);
|
|
245
|
+
const itemType = formatType$1(item.type);
|
|
246
|
+
const metaParts = [];
|
|
247
|
+
if (year) metaParts.push(String(year));
|
|
248
|
+
metaParts.push(itemType);
|
|
249
|
+
if (identifiers) metaParts.push(identifiers);
|
|
250
|
+
const updatedDate = extractUpdatedDate$1(item);
|
|
251
|
+
const createdDate = extractCreatedDate$1(item);
|
|
252
|
+
const publishedDate = extractPublishedDate$1(item);
|
|
253
|
+
return {
|
|
254
|
+
id: item.id,
|
|
255
|
+
title: item.title ?? "(No title)",
|
|
256
|
+
subtitle: authors || "(No authors)",
|
|
257
|
+
meta: metaParts.join(" · "),
|
|
258
|
+
value: item,
|
|
259
|
+
...updatedDate && { updatedDate },
|
|
260
|
+
...createdDate && { createdDate },
|
|
261
|
+
...publishedDate && { publishedDate }
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async function runSearchFlow(allReferences, searchFn, config) {
|
|
265
|
+
const choices = allReferences.map(toChoice$1);
|
|
266
|
+
const effectiveLimit = calculateEffectiveLimit$1(config.limit);
|
|
267
|
+
const filterFn = (query, choices2) => {
|
|
268
|
+
if (!query.trim()) return choices2;
|
|
269
|
+
const results = searchFn(query);
|
|
270
|
+
return results.map((r) => toChoice$1(r.reference));
|
|
271
|
+
};
|
|
272
|
+
const defaultSort = "updated-desc";
|
|
273
|
+
return new Promise((resolve) => {
|
|
274
|
+
let flowResult = {
|
|
275
|
+
action: "cancel",
|
|
276
|
+
output: "",
|
|
277
|
+
cancelled: true
|
|
278
|
+
};
|
|
279
|
+
const handleComplete = (result) => {
|
|
280
|
+
flowResult = result;
|
|
281
|
+
};
|
|
282
|
+
const handleCancel = () => {
|
|
283
|
+
flowResult = {
|
|
284
|
+
action: "cancel",
|
|
285
|
+
output: "",
|
|
286
|
+
cancelled: true
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
const { waitUntilExit } = render(
|
|
290
|
+
createElement(SearchFlowApp, {
|
|
291
|
+
choices,
|
|
292
|
+
filterFn,
|
|
293
|
+
visibleCount: effectiveLimit,
|
|
294
|
+
defaultSort,
|
|
295
|
+
onComplete: handleComplete,
|
|
296
|
+
onCancel: handleCancel
|
|
297
|
+
})
|
|
298
|
+
);
|
|
299
|
+
waitUntilExit().then(() => {
|
|
300
|
+
restoreStdinAfterInk();
|
|
301
|
+
resolve(flowResult);
|
|
302
|
+
}).catch(() => {
|
|
303
|
+
restoreStdinAfterInk();
|
|
304
|
+
resolve({
|
|
305
|
+
action: "cancel",
|
|
306
|
+
output: "",
|
|
307
|
+
cancelled: true
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
function CiteFlowApp({
|
|
313
|
+
choices,
|
|
314
|
+
filterFn,
|
|
315
|
+
visibleCount,
|
|
316
|
+
defaultSort,
|
|
317
|
+
styleOptions,
|
|
318
|
+
showStyleSelect,
|
|
319
|
+
onComplete,
|
|
320
|
+
onCancel
|
|
321
|
+
}) {
|
|
322
|
+
const { exit } = useApp();
|
|
323
|
+
const [state, setState] = useState("search");
|
|
324
|
+
const [selectedItems, setSelectedItems] = useState([]);
|
|
325
|
+
const [pendingResult, setPendingResult] = useState(null);
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
if (state === "exiting" && pendingResult) {
|
|
328
|
+
exit();
|
|
329
|
+
if (pendingResult.cancelled) {
|
|
330
|
+
onCancel();
|
|
331
|
+
} else {
|
|
332
|
+
onComplete(pendingResult);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}, [state, pendingResult, exit, onCancel, onComplete]);
|
|
336
|
+
const exitWith = (result) => {
|
|
337
|
+
setPendingResult(result);
|
|
338
|
+
setState("exiting");
|
|
339
|
+
};
|
|
340
|
+
const handleSearchSubmit = (selected) => {
|
|
341
|
+
if (selected.length === 0) {
|
|
342
|
+
exitWith({ identifiers: [], cancelled: true });
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const items = selected.map((c) => c.value);
|
|
346
|
+
setSelectedItems(items);
|
|
347
|
+
if (showStyleSelect) {
|
|
348
|
+
setState("style");
|
|
349
|
+
} else {
|
|
350
|
+
exitWith({
|
|
351
|
+
identifiers: items.map((item) => item.id),
|
|
352
|
+
cancelled: false
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
const handleSearchCancel = () => {
|
|
357
|
+
exitWith({ identifiers: [], cancelled: true });
|
|
358
|
+
};
|
|
359
|
+
const handleStyleSelect = (style) => {
|
|
360
|
+
exitWith({
|
|
361
|
+
identifiers: selectedItems.map((item) => item.id),
|
|
362
|
+
style,
|
|
363
|
+
cancelled: false
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
const handleStyleCancel = () => {
|
|
367
|
+
setState("search");
|
|
368
|
+
};
|
|
369
|
+
if (state === "exiting") {
|
|
370
|
+
return createElement(Box);
|
|
371
|
+
}
|
|
372
|
+
if (state === "search") {
|
|
373
|
+
return createElement(SearchableMultiSelect, {
|
|
374
|
+
choices,
|
|
375
|
+
filterFn,
|
|
376
|
+
visibleCount,
|
|
377
|
+
onSubmit: handleSearchSubmit,
|
|
378
|
+
onCancel: handleSearchCancel,
|
|
379
|
+
header: "Select references to cite",
|
|
380
|
+
placeholder: "Type to search...",
|
|
381
|
+
defaultSort
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
const count = selectedItems.length;
|
|
385
|
+
const refWord = count === 1 ? "reference" : "references";
|
|
386
|
+
return createElement(Select, {
|
|
387
|
+
options: styleOptions,
|
|
388
|
+
message: `Select citation style for ${count} ${refWord}:`,
|
|
389
|
+
onSelect: handleStyleSelect,
|
|
390
|
+
onCancel: handleStyleCancel
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
function getTerminalHeight() {
|
|
394
|
+
return process.stdout.rows ?? 24;
|
|
395
|
+
}
|
|
396
|
+
function calculateEffectiveLimit(configLimit) {
|
|
397
|
+
const terminalHeight = getTerminalHeight();
|
|
398
|
+
const reservedLines = 10;
|
|
399
|
+
const linesPerItem = 3;
|
|
400
|
+
const availableLines = terminalHeight - reservedLines;
|
|
401
|
+
const maxVisibleChoices = Math.max(1, Math.floor(availableLines / linesPerItem));
|
|
402
|
+
return configLimit > 0 ? Math.min(configLimit, maxVisibleChoices) : maxVisibleChoices;
|
|
403
|
+
}
|
|
404
|
+
function extractYear(item) {
|
|
405
|
+
const dateParts = item.issued?.["date-parts"];
|
|
406
|
+
if (!dateParts || dateParts.length === 0) return void 0;
|
|
407
|
+
const firstDatePart = dateParts[0];
|
|
408
|
+
if (!firstDatePart || firstDatePart.length === 0) return void 0;
|
|
409
|
+
return firstDatePart[0];
|
|
410
|
+
}
|
|
411
|
+
function extractPublishedDate(item) {
|
|
412
|
+
const dateParts = item.issued?.["date-parts"];
|
|
413
|
+
if (!dateParts || dateParts.length === 0) return void 0;
|
|
414
|
+
const firstDatePart = dateParts[0];
|
|
415
|
+
if (!firstDatePart || firstDatePart.length === 0) return void 0;
|
|
416
|
+
const [year, month = 1, day = 1] = firstDatePart;
|
|
417
|
+
if (year === void 0) return void 0;
|
|
418
|
+
return new Date(year, month - 1, day);
|
|
419
|
+
}
|
|
420
|
+
function extractUpdatedDate(item) {
|
|
421
|
+
const dateStr = item.custom?.timestamp;
|
|
422
|
+
if (!dateStr || typeof dateStr !== "string") return void 0;
|
|
423
|
+
const date = new Date(dateStr);
|
|
424
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
425
|
+
}
|
|
426
|
+
function extractCreatedDate(item) {
|
|
427
|
+
const dateStr = item.custom?.created_at;
|
|
428
|
+
if (!dateStr || typeof dateStr !== "string") return void 0;
|
|
429
|
+
const date = new Date(dateStr);
|
|
430
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
431
|
+
}
|
|
432
|
+
function formatIdentifiers(item) {
|
|
433
|
+
const parts = [];
|
|
434
|
+
if (item.DOI) parts.push(`DOI: ${item.DOI}`);
|
|
435
|
+
if (item.PMID) parts.push(`PMID: ${item.PMID}`);
|
|
436
|
+
if (item.PMCID) parts.push(`PMCID: ${item.PMCID}`);
|
|
437
|
+
if (item.ISBN) parts.push(`ISBN: ${item.ISBN}`);
|
|
438
|
+
return parts.join(" · ");
|
|
439
|
+
}
|
|
440
|
+
function formatType(type) {
|
|
441
|
+
const typeMap = {
|
|
442
|
+
"article-journal": "Journal article",
|
|
443
|
+
"article-magazine": "Magazine article",
|
|
444
|
+
"article-newspaper": "Newspaper article",
|
|
445
|
+
book: "Book",
|
|
446
|
+
chapter: "Book chapter",
|
|
447
|
+
"paper-conference": "Conference paper",
|
|
448
|
+
thesis: "Thesis",
|
|
449
|
+
report: "Report",
|
|
450
|
+
webpage: "Web page"
|
|
451
|
+
};
|
|
452
|
+
return typeMap[type] ?? type;
|
|
453
|
+
}
|
|
454
|
+
function toChoice(item) {
|
|
455
|
+
const authors = formatAuthors(item.author);
|
|
456
|
+
const year = extractYear(item);
|
|
457
|
+
const identifiers = formatIdentifiers(item);
|
|
458
|
+
const itemType = formatType(item.type);
|
|
459
|
+
const metaParts = [];
|
|
460
|
+
if (year) metaParts.push(String(year));
|
|
461
|
+
metaParts.push(itemType);
|
|
462
|
+
if (identifiers) metaParts.push(identifiers);
|
|
463
|
+
const updatedDate = extractUpdatedDate(item);
|
|
464
|
+
const createdDate = extractCreatedDate(item);
|
|
465
|
+
const publishedDate = extractPublishedDate(item);
|
|
466
|
+
return {
|
|
467
|
+
id: item.id,
|
|
468
|
+
title: item.title ?? "(No title)",
|
|
469
|
+
subtitle: authors || "(No authors)",
|
|
470
|
+
meta: metaParts.join(" · "),
|
|
471
|
+
value: item,
|
|
472
|
+
...updatedDate && { updatedDate },
|
|
473
|
+
...createdDate && { createdDate },
|
|
474
|
+
...publishedDate && { publishedDate }
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
async function runCiteFlow(options) {
|
|
478
|
+
const { allReferences, searchFn, config, styleOptions, showStyleSelect } = options;
|
|
479
|
+
const choices = allReferences.map(toChoice);
|
|
480
|
+
const effectiveLimit = calculateEffectiveLimit(config.limit);
|
|
481
|
+
const filterFn = (query, choices2) => {
|
|
482
|
+
if (!query.trim()) return choices2;
|
|
483
|
+
const results = searchFn(query);
|
|
484
|
+
return results.map((r) => toChoice(r.reference));
|
|
485
|
+
};
|
|
486
|
+
const defaultSort = "updated-desc";
|
|
487
|
+
return new Promise((resolve) => {
|
|
488
|
+
let flowResult = {
|
|
489
|
+
identifiers: [],
|
|
490
|
+
cancelled: true
|
|
491
|
+
};
|
|
492
|
+
const handleComplete = (result) => {
|
|
493
|
+
flowResult = result;
|
|
494
|
+
};
|
|
495
|
+
const handleCancel = () => {
|
|
496
|
+
flowResult = {
|
|
497
|
+
identifiers: [],
|
|
498
|
+
cancelled: true
|
|
499
|
+
};
|
|
500
|
+
};
|
|
501
|
+
const { waitUntilExit } = render(
|
|
502
|
+
createElement(CiteFlowApp, {
|
|
503
|
+
choices,
|
|
504
|
+
filterFn,
|
|
505
|
+
visibleCount: effectiveLimit,
|
|
506
|
+
defaultSort,
|
|
507
|
+
styleOptions,
|
|
508
|
+
showStyleSelect,
|
|
509
|
+
onComplete: handleComplete,
|
|
510
|
+
onCancel: handleCancel
|
|
511
|
+
})
|
|
512
|
+
);
|
|
513
|
+
waitUntilExit().then(() => {
|
|
514
|
+
restoreStdinAfterInk();
|
|
515
|
+
resolve(flowResult);
|
|
516
|
+
}).catch(() => {
|
|
517
|
+
restoreStdinAfterInk();
|
|
518
|
+
resolve({
|
|
519
|
+
identifiers: [],
|
|
520
|
+
cancelled: true
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
export {
|
|
526
|
+
CiteFlowApp,
|
|
527
|
+
SearchFlowApp,
|
|
528
|
+
runCiteFlow,
|
|
529
|
+
runSearchFlow
|
|
530
|
+
};
|
|
531
|
+
//# sourceMappingURL=index-CEYp8OSj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CEYp8OSj.js","sources":["../../src/features/interactive/components/Select.tsx","../../src/features/interactive/action-menu.ts","../../src/features/interactive/apps/SearchFlowApp.tsx","../../src/features/interactive/apps/runSearchFlow.ts","../../src/features/interactive/apps/CiteFlowApp.tsx","../../src/features/interactive/apps/runCiteFlow.ts"],"sourcesContent":["/**\n * Select - A simple single-select component for React Ink\n */\n\nimport { Box, Text, useFocus, useInput } from \"ink\";\nimport type React from \"react\";\nimport { useState } from \"react\";\n\nexport interface SelectOption<T = string> {\n /** Display label */\n label: string;\n /** Associated value */\n value: T;\n /** Optional hint text */\n hint?: string;\n}\n\nexport interface SelectProps<T = string> {\n /** Available options */\n options: SelectOption<T>[];\n /** Prompt message */\n message: string;\n /** Callback when selection is confirmed */\n onSelect: (value: T) => void;\n /** Callback when cancelled */\n onCancel: () => void;\n /** Initial selection index */\n initialIndex?: number;\n}\n\nexport function Select<T = string>({\n options,\n message,\n onSelect,\n onCancel,\n initialIndex = 0,\n}: SelectProps<T>): React.ReactElement {\n const [focusIndex, setFocusIndex] = useState(initialIndex);\n const { isFocused } = useFocus({ autoFocus: true });\n\n useInput(\n (input, key) => {\n // Cancel on Escape\n if (key.escape) {\n onCancel();\n return;\n }\n\n // Submit on Enter\n if (key.return) {\n const selected = options[focusIndex];\n if (selected) {\n onSelect(selected.value);\n }\n return;\n }\n\n // Navigate up\n if (key.upArrow || input === \"k\") {\n setFocusIndex((prev) => Math.max(0, prev - 1));\n return;\n }\n\n // Navigate down\n if (key.downArrow || input === \"j\") {\n setFocusIndex((prev) => Math.min(options.length - 1, prev + 1));\n return;\n }\n },\n { isActive: isFocused }\n );\n\n return (\n <Box flexDirection=\"column\">\n {/* Message */}\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n {message}\n </Text>\n </Box>\n\n {/* Options */}\n <Box flexDirection=\"column\">\n {options.map((option, index) => {\n const isFocusedItem = index === focusIndex;\n\n return (\n <Box key={`${option.label}-${index}`} flexDirection=\"row\">\n {/* Focus indicator - fixed width for layout stability */}\n <Box width={2}>{isFocusedItem ? <Text color=\"cyan\">❯</Text> : <Text> </Text>}</Box>\n\n {/* Label */}\n {isFocusedItem ? (\n <Text color=\"cyan\" bold>\n {option.label}\n </Text>\n ) : (\n <Text>{option.label}</Text>\n )}\n\n {/* Hint */}\n {option.hint && <Text dimColor> ({option.hint})</Text>}\n </Box>\n );\n })}\n </Box>\n\n {/* Footer */}\n <Box marginTop={1}>\n <Text dimColor>(↑↓: navigate, Enter: select, Esc: cancel)</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Action menu for interactive search mode.\n * Allows users to perform actions on selected references.\n */\n\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type React from \"react\";\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport { formatBibliographyCSL, formatBibtex } from \"../format/index.js\";\nimport { restoreStdinAfterInk } from \"./alternate-screen.js\";\nimport { Select, type SelectOption } from \"./components/index.js\";\n\n/**\n * Action types available in the action menu.\n */\nexport type ActionType =\n | \"output-ids\"\n | \"output-csl-json\"\n | \"output-bibtex\"\n | \"cite-apa\"\n | \"cite-choose\"\n | \"cancel\";\n\n/**\n * Result from action menu selection.\n */\nexport interface ActionMenuResult {\n /** Selected action type */\n action: ActionType;\n /** Generated output (empty for cancel) */\n output: string;\n /** Whether the prompt was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Result from style selection prompt.\n */\nexport interface StyleSelectResult {\n /** Selected style (undefined if cancelled) */\n style?: string;\n /** Whether the prompt was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Available action choices for the action menu.\n */\nexport const ACTION_CHOICES: SelectOption<ActionType>[] = [\n { label: \"Output IDs (citation keys)\", value: \"output-ids\" },\n { label: \"Output as CSL-JSON\", value: \"output-csl-json\" },\n { label: \"Output as BibTeX\", value: \"output-bibtex\" },\n { label: \"Generate citation (APA)\", value: \"cite-apa\" },\n { label: \"Generate citation (choose style)\", value: \"cite-choose\" },\n { label: \"Cancel\", value: \"cancel\" },\n];\n\n/**\n * Available style choices for citation style selection.\n */\nexport const STYLE_CHOICES: SelectOption<string>[] = [\n { label: \"APA\", value: \"apa\" },\n { label: \"Vancouver\", value: \"vancouver\" },\n { label: \"Harvard\", value: \"harvard\" },\n];\n\n/**\n * Props for the ActionMenuApp component\n */\ninterface ActionMenuAppProps {\n message: string;\n options: SelectOption<ActionType>[];\n onSelect: (value: ActionType) => void;\n onCancel: () => void;\n}\n\n/**\n * ActionMenuApp component - wraps Select for action menu\n */\nfunction ActionMenuApp({\n message,\n options,\n onSelect,\n onCancel,\n}: ActionMenuAppProps): React.ReactElement {\n return createElement(Select<ActionType>, {\n options,\n message,\n onSelect,\n onCancel,\n });\n}\n\n/**\n * Props for the StyleSelectApp component\n */\ninterface StyleSelectAppProps {\n options: SelectOption<string>[];\n onSelect: (value: string) => void;\n onCancel: () => void;\n}\n\n/**\n * StyleSelectApp component - wraps Select for style selection\n */\nfunction StyleSelectApp({ options, onSelect, onCancel }: StyleSelectAppProps): React.ReactElement {\n return createElement(Select<string>, {\n options,\n message: \"Select citation style:\",\n onSelect,\n onCancel,\n });\n}\n\n/**\n * Generate output for the given action and items.\n */\nexport function generateOutput(action: ActionType, items: CslItem[], style = \"apa\"): string {\n switch (action) {\n case \"output-ids\":\n return items.map((item) => item.id).join(\"\\n\");\n\n case \"output-csl-json\":\n return JSON.stringify(items, null, 2);\n\n case \"output-bibtex\":\n return formatBibtex(items);\n\n case \"cite-apa\":\n return formatBibliographyCSL(items, { style: \"apa\" });\n\n case \"cite-choose\":\n return formatBibliographyCSL(items, { style });\n\n case \"cancel\":\n return \"\";\n\n default:\n return \"\";\n }\n}\n\n/**\n * Run the style selection prompt.\n */\nexport async function runStyleSelectPrompt(): Promise<StyleSelectResult> {\n return new Promise<StyleSelectResult>((resolve) => {\n let result: StyleSelectResult = { cancelled: true };\n\n const handleSelect = (value: string): void => {\n result = {\n style: value,\n cancelled: false,\n };\n };\n\n const handleCancel = (): void => {\n result = {\n cancelled: true,\n };\n };\n\n // Render the Ink app\n const { waitUntilExit } = render(\n createElement(StyleSelectApp, {\n options: STYLE_CHOICES,\n onSelect: handleSelect,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, then resolve\n waitUntilExit()\n .then(() => {\n restoreStdinAfterInk();\n resolve(result);\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve({\n cancelled: true,\n });\n });\n });\n}\n\n/**\n * Process the selected action and generate result.\n */\nasync function processAction(action: ActionType, items: CslItem[]): Promise<ActionMenuResult> {\n // Handle cite-choose: prompt for style first\n if (action === \"cite-choose\") {\n const styleResult = await runStyleSelectPrompt();\n if (styleResult.cancelled) {\n return {\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n };\n }\n return {\n action,\n output: generateOutput(action, items, styleResult.style),\n cancelled: false,\n };\n }\n\n // Handle cancel\n if (action === \"cancel\") {\n return {\n action,\n output: \"\",\n cancelled: true,\n };\n }\n\n // Handle other actions\n return {\n action,\n output: generateOutput(action, items),\n cancelled: false,\n };\n}\n\n/**\n * Run the action menu for selected references.\n *\n * @param items - Selected references\n * @returns Action result with output\n */\nexport async function runActionMenu(items: CslItem[]): Promise<ActionMenuResult> {\n const count = items.length;\n const refWord = count === 1 ? \"reference\" : \"references\";\n const message = `Action for ${count} selected ${refWord}:`;\n\n return new Promise<ActionMenuResult>((resolve) => {\n let selectedAction: ActionType | null = null;\n\n const handleSelect = (action: ActionType): void => {\n selectedAction = action;\n };\n\n const handleCancel = (): void => {\n selectedAction = null;\n };\n\n // Render the Ink app\n const { waitUntilExit } = render(\n createElement(ActionMenuApp, {\n message,\n options: ACTION_CHOICES,\n onSelect: handleSelect,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, then process the action\n waitUntilExit()\n .then(async () => {\n restoreStdinAfterInk();\n\n if (selectedAction === null) {\n resolve({\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n });\n } else {\n const result = await processAction(selectedAction, items);\n resolve(result);\n }\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve({\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n });\n });\n });\n}\n","/**\n * SearchFlowApp - Single App for search -t flow\n *\n * Manages state transitions: search → action → (style if needed)\n * Following React Ink Single App Pattern (ADR-015)\n */\n\nimport { Box, useApp } from \"ink\";\nimport type React from \"react\";\nimport { createElement, useEffect, useState } from \"react\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport {\n ACTION_CHOICES,\n type ActionMenuResult,\n type ActionType,\n STYLE_CHOICES,\n generateOutput,\n} from \"../action-menu.js\";\nimport {\n type Choice,\n SearchableMultiSelect,\n Select,\n type SortOption,\n} from \"../components/index.js\";\n\n/**\n * Flow states for the search flow\n */\ntype FlowState = \"search\" | \"action\" | \"style\" | \"exiting\";\n\n/**\n * Props for SearchFlowApp\n */\nexport interface SearchFlowAppProps {\n /** Choices for the search prompt */\n choices: Choice<CslItem>[];\n /** Filter function for search */\n filterFn: (query: string, choices: Choice<CslItem>[]) => Choice<CslItem>[];\n /** Number of visible items */\n visibleCount: number;\n /** Default sort option */\n defaultSort: SortOption;\n /** Callback when flow completes */\n onComplete: (result: ActionMenuResult) => void;\n /** Callback when flow is cancelled */\n onCancel: () => void;\n}\n\n/**\n * SearchFlowApp component\n *\n * Single App that manages search → action → style flow\n */\nexport function SearchFlowApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n onComplete,\n onCancel,\n}: SearchFlowAppProps): React.ReactElement {\n const { exit } = useApp();\n const [state, setState] = useState<FlowState>(\"search\");\n const [selectedItems, setSelectedItems] = useState<CslItem[]>([]);\n const [pendingResult, setPendingResult] = useState<ActionMenuResult | null>(null);\n\n // Exit when entering \"exiting\" state (after rendering empty component)\n useEffect(() => {\n if (state === \"exiting\" && pendingResult) {\n exit();\n if (pendingResult.cancelled) {\n onCancel();\n } else {\n onComplete(pendingResult);\n }\n }\n }, [state, pendingResult, exit, onCancel, onComplete]);\n\n // Transition to exiting state with result\n const exitWith = (result: ActionMenuResult) => {\n setPendingResult(result);\n setState(\"exiting\");\n };\n\n // Handle search submission\n const handleSearchSubmit = (selected: Choice<CslItem>[]) => {\n if (selected.length === 0) {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n return;\n }\n setSelectedItems(selected.map((c) => c.value));\n setState(\"action\");\n };\n\n // Handle search cancel\n const handleSearchCancel = () => {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n };\n\n // Handle action selection\n const handleActionSelect = (action: ActionType) => {\n if (action === \"cancel\") {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n return;\n }\n\n // If cite-choose, go to style selection\n if (action === \"cite-choose\") {\n setState(\"style\");\n return;\n }\n\n // Generate output and complete\n const output = generateOutput(action, selectedItems);\n exitWith({ action, output, cancelled: false });\n };\n\n // Handle action cancel (go back to search)\n const handleActionCancel = () => {\n setState(\"search\");\n };\n\n // Handle style selection\n const handleStyleSelect = (style: string) => {\n const output = generateOutput(\"cite-choose\", selectedItems, style);\n exitWith({ action: \"cite-choose\", output, cancelled: false });\n };\n\n // Handle style cancel (go back to action)\n const handleStyleCancel = () => {\n setState(\"action\");\n };\n\n // Render based on current state\n if (state === \"exiting\") {\n // Empty component - Ink will clear the previous content\n return createElement(Box);\n }\n\n if (state === \"search\") {\n return createElement(SearchableMultiSelect<CslItem>, {\n choices,\n filterFn,\n visibleCount,\n onSubmit: handleSearchSubmit,\n onCancel: handleSearchCancel,\n header: \"Search references\",\n placeholder: \"Type to search...\",\n defaultSort,\n });\n }\n\n if (state === \"action\") {\n const count = selectedItems.length;\n const refWord = count === 1 ? \"reference\" : \"references\";\n return createElement(Select<ActionType>, {\n options: ACTION_CHOICES,\n message: `Action for ${count} selected ${refWord}:`,\n onSelect: handleActionSelect,\n onCancel: handleActionCancel,\n });\n }\n\n // state === \"style\"\n return createElement(Select<string>, {\n options: STYLE_CHOICES,\n message: \"Select citation style:\",\n onSelect: handleStyleSelect,\n onCancel: handleStyleCancel,\n });\n}\n","/**\n * Runner for SearchFlowApp\n *\n * Provides the public API for running the search flow.\n */\n\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport type { SearchResult } from \"../../search/types.js\";\nimport type { ActionMenuResult } from \"../action-menu.js\";\nimport { restoreStdinAfterInk } from \"../alternate-screen.js\";\nimport type { Choice, SortOption } from \"../components/index.js\";\nimport { formatAuthors } from \"../format.js\";\nimport { SearchFlowApp } from \"./SearchFlowApp.js\";\n\n/**\n * Configuration for the search flow\n */\nexport interface SearchFlowConfig {\n /** Maximum number of results to display */\n limit: number;\n /** Debounce delay in milliseconds (not used, kept for API compatibility) */\n debounceMs: number;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\n\n/**\n * Gets terminal height, falling back to 24 if not available\n */\nfunction getTerminalHeight(): number {\n return process.stdout.rows ?? 24;\n}\n\n/**\n * Calculates the effective limit for the autocomplete list\n */\nfunction calculateEffectiveLimit(configLimit: number): number {\n const terminalHeight = getTerminalHeight();\n // Reserve lines for: header(2) + search box(3) + status(1) + scroll indicators(2) + footer(2) = 10\n const reservedLines = 10;\n const linesPerItem = 3;\n const availableLines = terminalHeight - reservedLines;\n const maxVisibleChoices = Math.max(1, Math.floor(availableLines / linesPerItem));\n return configLimit > 0 ? Math.min(configLimit, maxVisibleChoices) : maxVisibleChoices;\n}\n\n/**\n * Extract year from CSL item\n */\nfunction extractYear(item: CslItem): number | undefined {\n const dateParts = item.issued?.[\"date-parts\"];\n if (!dateParts || dateParts.length === 0) return undefined;\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) return undefined;\n return firstDatePart[0];\n}\n\n/**\n * Extract published date from CSL item\n */\nfunction extractPublishedDate(item: CslItem): Date | undefined {\n const dateParts = item.issued?.[\"date-parts\"];\n if (!dateParts || dateParts.length === 0) return undefined;\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) return undefined;\n const [year, month = 1, day = 1] = firstDatePart;\n if (year === undefined) return undefined;\n return new Date(year, month - 1, day);\n}\n\n/**\n * Extract updated date from CSL item (from custom.timestamp)\n */\nfunction extractUpdatedDate(item: CslItem): Date | undefined {\n const dateStr = item.custom?.timestamp;\n if (!dateStr || typeof dateStr !== \"string\") return undefined;\n const date = new Date(dateStr);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\n/**\n * Extract created date from CSL item (from custom.created_at)\n */\nfunction extractCreatedDate(item: CslItem): Date | undefined {\n const dateStr = item.custom?.created_at;\n if (!dateStr || typeof dateStr !== \"string\") return undefined;\n const date = new Date(dateStr);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\n/**\n * Format identifiers for meta line\n */\nfunction formatIdentifiers(item: CslItem): string {\n const parts: string[] = [];\n if (item.DOI) parts.push(`DOI: ${item.DOI}`);\n if (item.PMID) parts.push(`PMID: ${item.PMID}`);\n if (item.PMCID) parts.push(`PMCID: ${item.PMCID}`);\n if (item.ISBN) parts.push(`ISBN: ${item.ISBN}`);\n return parts.join(\" · \");\n}\n\n/**\n * Format item type for display\n */\nfunction formatType(type: string): string {\n const typeMap: Record<string, string> = {\n \"article-journal\": \"Journal article\",\n \"article-magazine\": \"Magazine article\",\n \"article-newspaper\": \"Newspaper article\",\n book: \"Book\",\n chapter: \"Book chapter\",\n \"paper-conference\": \"Conference paper\",\n thesis: \"Thesis\",\n report: \"Report\",\n webpage: \"Web page\",\n };\n return typeMap[type] ?? type;\n}\n\n/**\n * Convert CslItem to Choice for SearchableMultiSelect\n */\nfunction toChoice(item: CslItem): Choice<CslItem> {\n const authors = formatAuthors(item.author);\n const year = extractYear(item);\n const identifiers = formatIdentifiers(item);\n const itemType = formatType(item.type);\n\n // Build meta line: Year · Type · Identifiers\n const metaParts: string[] = [];\n if (year) metaParts.push(String(year));\n metaParts.push(itemType);\n if (identifiers) metaParts.push(identifiers);\n\n const updatedDate = extractUpdatedDate(item);\n const createdDate = extractCreatedDate(item);\n const publishedDate = extractPublishedDate(item);\n\n return {\n id: item.id,\n title: item.title ?? \"(No title)\",\n subtitle: authors || \"(No authors)\",\n meta: metaParts.join(\" · \"),\n value: item,\n ...(updatedDate && { updatedDate }),\n ...(createdDate && { createdDate }),\n ...(publishedDate && { publishedDate }),\n };\n}\n\n/**\n * Run the search flow (search → action → style if needed)\n *\n * This is the main entry point for the `search -t` command.\n */\nexport async function runSearchFlow(\n allReferences: CslItem[],\n searchFn: SearchFunction,\n config: SearchFlowConfig\n): Promise<ActionMenuResult> {\n // Convert references to choices\n const choices = allReferences.map(toChoice);\n\n // Calculate effective visible count\n const effectiveLimit = calculateEffectiveLimit(config.limit);\n\n // Create filter function using the provided search function\n const filterFn = (query: string, choices: Choice<CslItem>[]): Choice<CslItem>[] => {\n if (!query.trim()) return choices;\n\n const results = searchFn(query);\n return results.map((r) => toChoice(r.reference));\n };\n\n // Default sort option\n const defaultSort: SortOption = \"updated-desc\";\n\n // Create a promise to capture the result\n return new Promise<ActionMenuResult>((resolve) => {\n let flowResult: ActionMenuResult = {\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n };\n\n const handleComplete = (result: ActionMenuResult): void => {\n flowResult = result;\n };\n\n const handleCancel = (): void => {\n flowResult = {\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n };\n };\n\n // Render the Ink app (single render for entire flow)\n const { waitUntilExit } = render(\n createElement(SearchFlowApp, {\n choices,\n filterFn,\n visibleCount: effectiveLimit,\n defaultSort,\n onComplete: handleComplete,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, then resolve\n waitUntilExit()\n .then(() => {\n restoreStdinAfterInk();\n resolve(flowResult);\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve({\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n });\n });\n });\n}\n","/**\n * CiteFlowApp - Single App for cite command flow\n *\n * Implements the Single App Pattern (ADR-015) for the cite command.\n * Manages state transitions: reference selection → style selection → exiting\n */\n\nimport { Box, useApp } from \"ink\";\nimport type React from \"react\";\nimport { createElement, useEffect, useState } from \"react\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport { SearchableMultiSelect } from \"../components/SearchableMultiSelect.js\";\nimport { Select, type SelectOption } from \"../components/Select.js\";\nimport type { Choice, SortOption } from \"../components/index.js\";\n\n// Flow states\ntype FlowState = \"search\" | \"style\" | \"exiting\";\n\n/**\n * Result from the cite flow\n */\nexport interface CiteFlowResult {\n /** Selected reference IDs */\n identifiers: string[];\n /** Selected style (if style selection was shown) */\n style?: string;\n /** Whether the flow was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Props for CiteFlowApp\n */\nexport interface CiteFlowAppProps {\n /** All reference choices */\n choices: Choice<CslItem>[];\n /** Filter function for search */\n filterFn: (query: string, choices: Choice<CslItem>[]) => Choice<CslItem>[];\n /** Number of visible items */\n visibleCount: number;\n /** Default sort option */\n defaultSort: SortOption;\n /** Style options for style selection */\n styleOptions: SelectOption<string>[];\n /** Whether to show style selection (false if style already specified) */\n showStyleSelect: boolean;\n /** Callback when flow completes */\n onComplete: (result: CiteFlowResult) => void;\n /** Callback when flow is cancelled */\n onCancel: () => void;\n}\n\n/**\n * CiteFlowApp component\n *\n * Single Ink app that handles the entire cite flow:\n * 1. Reference selection (SearchableMultiSelect)\n * 2. Style selection (Select) - optional\n * 3. Exit\n */\nexport function CiteFlowApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n styleOptions,\n showStyleSelect,\n onComplete,\n onCancel,\n}: CiteFlowAppProps): React.ReactElement {\n const { exit } = useApp();\n const [state, setState] = useState<FlowState>(\"search\");\n const [selectedItems, setSelectedItems] = useState<CslItem[]>([]);\n const [pendingResult, setPendingResult] = useState<CiteFlowResult | null>(null);\n\n // Exit when entering \"exiting\" state\n useEffect(() => {\n if (state === \"exiting\" && pendingResult) {\n exit();\n if (pendingResult.cancelled) {\n onCancel();\n } else {\n onComplete(pendingResult);\n }\n }\n }, [state, pendingResult, exit, onCancel, onComplete]);\n\n // Transition to exiting state with result\n const exitWith = (result: CiteFlowResult) => {\n setPendingResult(result);\n setState(\"exiting\");\n };\n\n // Handle search submission\n const handleSearchSubmit = (selected: Choice<CslItem>[]) => {\n if (selected.length === 0) {\n exitWith({ identifiers: [], cancelled: true });\n return;\n }\n const items = selected.map((c) => c.value);\n setSelectedItems(items);\n\n if (showStyleSelect) {\n setState(\"style\");\n } else {\n // No style selection needed, complete immediately\n exitWith({\n identifiers: items.map((item) => item.id),\n cancelled: false,\n });\n }\n };\n\n // Handle search cancel\n const handleSearchCancel = () => {\n exitWith({ identifiers: [], cancelled: true });\n };\n\n // Handle style selection\n const handleStyleSelect = (style: string) => {\n exitWith({\n identifiers: selectedItems.map((item) => item.id),\n style,\n cancelled: false,\n });\n };\n\n // Handle style cancel (go back to search)\n const handleStyleCancel = () => {\n setState(\"search\");\n };\n\n // Render based on current state\n if (state === \"exiting\") {\n return createElement(Box);\n }\n\n if (state === \"search\") {\n return createElement(SearchableMultiSelect<CslItem>, {\n choices,\n filterFn,\n visibleCount,\n onSubmit: handleSearchSubmit,\n onCancel: handleSearchCancel,\n header: \"Select references to cite\",\n placeholder: \"Type to search...\",\n defaultSort,\n });\n }\n\n // state === \"style\"\n const count = selectedItems.length;\n const refWord = count === 1 ? \"reference\" : \"references\";\n return createElement(Select<string>, {\n options: styleOptions,\n message: `Select citation style for ${count} ${refWord}:`,\n onSelect: handleStyleSelect,\n onCancel: handleStyleCancel,\n });\n}\n","/**\n * Runner for CiteFlowApp\n *\n * Provides the public API for running the cite flow.\n */\n\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport type { SearchResult } from \"../../search/types.js\";\nimport { restoreStdinAfterInk } from \"../alternate-screen.js\";\nimport type { Choice, SelectOption, SortOption } from \"../components/index.js\";\nimport { formatAuthors } from \"../format.js\";\nimport { CiteFlowApp, type CiteFlowResult } from \"./CiteFlowApp.js\";\n\n/**\n * Configuration for the cite flow\n */\nexport interface CiteFlowConfig {\n /** Maximum number of results to display */\n limit: number;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\n\n/**\n * Gets terminal height, falling back to 24 if not available\n */\nfunction getTerminalHeight(): number {\n return process.stdout.rows ?? 24;\n}\n\n/**\n * Calculates the effective limit for the autocomplete list\n */\nfunction calculateEffectiveLimit(configLimit: number): number {\n const terminalHeight = getTerminalHeight();\n // Reserve lines for: header(2) + search box(3) + status(1) + scroll indicators(2) + footer(2) = 10\n const reservedLines = 10;\n const linesPerItem = 3;\n const availableLines = terminalHeight - reservedLines;\n const maxVisibleChoices = Math.max(1, Math.floor(availableLines / linesPerItem));\n return configLimit > 0 ? Math.min(configLimit, maxVisibleChoices) : maxVisibleChoices;\n}\n\n/**\n * Extract year from CSL item\n */\nfunction extractYear(item: CslItem): number | undefined {\n const dateParts = item.issued?.[\"date-parts\"];\n if (!dateParts || dateParts.length === 0) return undefined;\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) return undefined;\n return firstDatePart[0];\n}\n\n/**\n * Extract published date from CSL item\n */\nfunction extractPublishedDate(item: CslItem): Date | undefined {\n const dateParts = item.issued?.[\"date-parts\"];\n if (!dateParts || dateParts.length === 0) return undefined;\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) return undefined;\n const [year, month = 1, day = 1] = firstDatePart;\n if (year === undefined) return undefined;\n return new Date(year, month - 1, day);\n}\n\n/**\n * Extract updated date from CSL item (from custom.timestamp)\n */\nfunction extractUpdatedDate(item: CslItem): Date | undefined {\n const dateStr = item.custom?.timestamp;\n if (!dateStr || typeof dateStr !== \"string\") return undefined;\n const date = new Date(dateStr);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\n/**\n * Extract created date from CSL item (from custom.created_at)\n */\nfunction extractCreatedDate(item: CslItem): Date | undefined {\n const dateStr = item.custom?.created_at;\n if (!dateStr || typeof dateStr !== \"string\") return undefined;\n const date = new Date(dateStr);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\n/**\n * Format identifiers for meta line\n */\nfunction formatIdentifiers(item: CslItem): string {\n const parts: string[] = [];\n if (item.DOI) parts.push(`DOI: ${item.DOI}`);\n if (item.PMID) parts.push(`PMID: ${item.PMID}`);\n if (item.PMCID) parts.push(`PMCID: ${item.PMCID}`);\n if (item.ISBN) parts.push(`ISBN: ${item.ISBN}`);\n return parts.join(\" · \");\n}\n\n/**\n * Format item type for display\n */\nfunction formatType(type: string): string {\n const typeMap: Record<string, string> = {\n \"article-journal\": \"Journal article\",\n \"article-magazine\": \"Magazine article\",\n \"article-newspaper\": \"Newspaper article\",\n book: \"Book\",\n chapter: \"Book chapter\",\n \"paper-conference\": \"Conference paper\",\n thesis: \"Thesis\",\n report: \"Report\",\n webpage: \"Web page\",\n };\n return typeMap[type] ?? type;\n}\n\n/**\n * Convert CslItem to Choice for SearchableMultiSelect\n */\nfunction toChoice(item: CslItem): Choice<CslItem> {\n const authors = formatAuthors(item.author);\n const year = extractYear(item);\n const identifiers = formatIdentifiers(item);\n const itemType = formatType(item.type);\n\n // Build meta line: Year · Type · Identifiers\n const metaParts: string[] = [];\n if (year) metaParts.push(String(year));\n metaParts.push(itemType);\n if (identifiers) metaParts.push(identifiers);\n\n const updatedDate = extractUpdatedDate(item);\n const createdDate = extractCreatedDate(item);\n const publishedDate = extractPublishedDate(item);\n\n return {\n id: item.id,\n title: item.title ?? \"(No title)\",\n subtitle: authors || \"(No authors)\",\n meta: metaParts.join(\" · \"),\n value: item,\n ...(updatedDate && { updatedDate }),\n ...(createdDate && { createdDate }),\n ...(publishedDate && { publishedDate }),\n };\n}\n\n/**\n * Options for running the cite flow\n */\nexport interface RunCiteFlowOptions {\n /** All references available for selection */\n allReferences: CslItem[];\n /** Search function for filtering */\n searchFn: SearchFunction;\n /** Flow configuration */\n config: CiteFlowConfig;\n /** Style options for style selection */\n styleOptions: SelectOption<string>[];\n /** Whether to show style selection */\n showStyleSelect: boolean;\n}\n\n/**\n * Run the cite flow (reference selection → style selection if needed)\n *\n * This is the main entry point for interactive cite command.\n */\nexport async function runCiteFlow(options: RunCiteFlowOptions): Promise<CiteFlowResult> {\n const { allReferences, searchFn, config, styleOptions, showStyleSelect } = options;\n\n // Convert references to choices\n const choices = allReferences.map(toChoice);\n\n // Calculate effective visible count\n const effectiveLimit = calculateEffectiveLimit(config.limit);\n\n // Create filter function using the provided search function\n const filterFn = (query: string, choices: Choice<CslItem>[]): Choice<CslItem>[] => {\n if (!query.trim()) return choices;\n\n const results = searchFn(query);\n return results.map((r) => toChoice(r.reference));\n };\n\n // Default sort option\n const defaultSort: SortOption = \"updated-desc\";\n\n // Create a promise to capture the result\n return new Promise<CiteFlowResult>((resolve) => {\n let flowResult: CiteFlowResult = {\n identifiers: [],\n cancelled: true,\n };\n\n const handleComplete = (result: CiteFlowResult): void => {\n flowResult = result;\n };\n\n const handleCancel = (): void => {\n flowResult = {\n identifiers: [],\n cancelled: true,\n };\n };\n\n // Render the Ink app (single render for entire flow)\n const { waitUntilExit } = render(\n createElement(CiteFlowApp, {\n choices,\n filterFn,\n visibleCount: effectiveLimit,\n defaultSort,\n styleOptions,\n showStyleSelect,\n onComplete: handleComplete,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, then resolve\n waitUntilExit()\n .then(() => {\n restoreStdinAfterInk();\n resolve(flowResult);\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve({\n identifiers: [],\n cancelled: true,\n });\n });\n });\n}\n"],"names":["jsxs","jsx","getTerminalHeight","calculateEffectiveLimit","extractYear","extractPublishedDate","extractUpdatedDate","extractCreatedDate","formatIdentifiers","formatType","toChoice","choices"],"mappings":"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"}
|