@ncukondo/reference-manager 0.6.0 → 0.7.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.
Files changed (39) hide show
  1. package/README.md +36 -0
  2. package/dist/chunks/action-menu-CTtINmWd.js +118 -0
  3. package/dist/chunks/action-menu-CTtINmWd.js.map +1 -0
  4. package/dist/chunks/{file-watcher-Cwfnnw92.js → file-watcher-D7oyc-9z.js} +14 -2
  5. package/dist/chunks/{file-watcher-Cwfnnw92.js.map → file-watcher-D7oyc-9z.js.map} +1 -1
  6. package/dist/chunks/{index-CQO8hLYm.js → index-_7NEUoS7.js} +13 -9
  7. package/dist/chunks/index-_7NEUoS7.js.map +1 -0
  8. package/dist/chunks/{loader-B_ZLxCQW.js → loader-BItrdVWG.js} +59 -7
  9. package/dist/chunks/loader-BItrdVWG.js.map +1 -0
  10. package/dist/chunks/search-prompt-D67WyKrY.js +179 -0
  11. package/dist/chunks/search-prompt-D67WyKrY.js.map +1 -0
  12. package/dist/chunks/tty-CDBIQraQ.js +17 -0
  13. package/dist/chunks/tty-CDBIQraQ.js.map +1 -0
  14. package/dist/cli/commands/search.d.ts +19 -0
  15. package/dist/cli/commands/search.d.ts.map +1 -1
  16. package/dist/cli/index.d.ts.map +1 -1
  17. package/dist/cli.js +64 -10
  18. package/dist/cli.js.map +1 -1
  19. package/dist/config/defaults.d.ts.map +1 -1
  20. package/dist/config/loader.d.ts.map +1 -1
  21. package/dist/config/schema.d.ts +24 -1
  22. package/dist/config/schema.d.ts.map +1 -1
  23. package/dist/features/import/importer.d.ts.map +1 -1
  24. package/dist/features/interactive/action-menu.d.ts +56 -0
  25. package/dist/features/interactive/action-menu.d.ts.map +1 -0
  26. package/dist/features/interactive/debounce.d.ts +22 -0
  27. package/dist/features/interactive/debounce.d.ts.map +1 -0
  28. package/dist/features/interactive/format.d.ts +52 -0
  29. package/dist/features/interactive/format.d.ts.map +1 -0
  30. package/dist/features/interactive/search-prompt.d.ts +47 -0
  31. package/dist/features/interactive/search-prompt.d.ts.map +1 -0
  32. package/dist/features/interactive/tty.d.ts +20 -0
  33. package/dist/features/interactive/tty.d.ts.map +1 -0
  34. package/dist/features/operations/add.d.ts.map +1 -1
  35. package/dist/index.js +3 -3
  36. package/dist/server.js +2 -2
  37. package/package.json +2 -1
  38. package/dist/chunks/index-CQO8hLYm.js.map +0 -1
  39. package/dist/chunks/loader-B_ZLxCQW.js.map +0 -1
package/README.md CHANGED
@@ -42,6 +42,10 @@ ref list --format json > references.json
42
42
  Streamline your writing workflow:
43
43
 
44
44
  ```bash
45
+ # Find and select references interactively
46
+ ref search -i "machine learning"
47
+ # → Select references with Space, then export as BibTeX or generate citations
48
+
45
49
  # Generate citations
46
50
  ref cite smith2024 jones2023 --style apa
47
51
  # Output: (Smith, 2024; Jones, 2023)
@@ -254,6 +258,10 @@ ref search "author:smith"
254
258
  ref search "author:jones year:2024"
255
259
  ref search "title:\"deep learning\""
256
260
 
261
+ # Interactive search (with real-time filtering)
262
+ ref search -i # Start interactive mode
263
+ ref search -i "machine learning" # Pre-fill query
264
+
257
265
  # Add references
258
266
  ref add paper.json # From CSL-JSON file
259
267
  ref add references.bib # From BibTeX
@@ -314,6 +322,34 @@ ref fulltext detach smith2024 --pdf --delete # Also delete the file
314
322
 
315
323
  Supported field prefixes: `author:`, `title:`, `doi:`, `pmid:`, `pmcid:`, `url:`, `keyword:`, `tag:`
316
324
 
325
+ ### Interactive Search
326
+
327
+ Start an interactive search session with real-time filtering:
328
+
329
+ ```bash
330
+ ref search -i # Start with empty query
331
+ ref search -i "machine learning" # Pre-fill the search query
332
+ ```
333
+
334
+ **Features:**
335
+ - Real-time filtering as you type (200ms debounce)
336
+ - Multi-select with Space key
337
+ - Action menu for selected references:
338
+ - Output IDs (citation keys)
339
+ - Output as CSL-JSON
340
+ - Output as BibTeX
341
+ - Generate citation (APA or choose style)
342
+
343
+ **Navigation:**
344
+ | Key | Action |
345
+ |-----|--------|
346
+ | `↑` / `↓` | Move cursor |
347
+ | `Space` | Toggle selection |
348
+ | `Enter` | Open action menu |
349
+ | `Esc` / `Ctrl+C` | Cancel |
350
+
351
+ > **Note**: Interactive mode requires a TTY (terminal). It won't work in pipes or scripts.
352
+
317
353
  ### Sorting and Pagination
318
354
 
319
355
  ```bash
@@ -0,0 +1,118 @@
1
+ import { f as formatBibliographyCSL, a as formatBibtex } from "./index-_7NEUoS7.js";
2
+ const ACTION_CHOICES = [
3
+ { name: "output-ids", message: "Output IDs (citation keys)", value: "output-ids" },
4
+ { name: "output-csl-json", message: "Output as CSL-JSON", value: "output-csl-json" },
5
+ { name: "output-bibtex", message: "Output as BibTeX", value: "output-bibtex" },
6
+ { name: "cite-apa", message: "Generate citation (APA)", value: "cite-apa" },
7
+ { name: "cite-choose", message: "Generate citation (choose style)", value: "cite-choose" },
8
+ { name: "cancel", message: "Cancel", value: "cancel" }
9
+ ];
10
+ const STYLE_CHOICES = [
11
+ { name: "apa", message: "APA", value: "apa" },
12
+ { name: "vancouver", message: "Vancouver", value: "vancouver" },
13
+ { name: "harvard", message: "Harvard", value: "harvard" }
14
+ ];
15
+ function generateOutput(action, items, style = "apa") {
16
+ switch (action) {
17
+ case "output-ids":
18
+ return items.map((item) => item.id).join("\n");
19
+ case "output-csl-json":
20
+ return JSON.stringify(items, null, 2);
21
+ case "output-bibtex":
22
+ return formatBibtex(items);
23
+ case "cite-apa":
24
+ return formatBibliographyCSL(items, { style: "apa" });
25
+ case "cite-choose":
26
+ return formatBibliographyCSL(items, { style });
27
+ case "cancel":
28
+ return "";
29
+ default:
30
+ return "";
31
+ }
32
+ }
33
+ async function processAction(action, items) {
34
+ if (action === "cite-choose") {
35
+ const styleResult = await runStyleSelectPrompt();
36
+ if (styleResult.cancelled) {
37
+ return {
38
+ action: "cancel",
39
+ output: "",
40
+ cancelled: true
41
+ };
42
+ }
43
+ return {
44
+ action,
45
+ output: generateOutput(action, items, styleResult.style),
46
+ cancelled: false
47
+ };
48
+ }
49
+ if (action === "cancel") {
50
+ return {
51
+ action,
52
+ output: "",
53
+ cancelled: true
54
+ };
55
+ }
56
+ return {
57
+ action,
58
+ output: generateOutput(action, items),
59
+ cancelled: false
60
+ };
61
+ }
62
+ async function runStyleSelectPrompt() {
63
+ const enquirer = await import("enquirer");
64
+ const Select = enquirer.default.Select;
65
+ const promptOptions = {
66
+ name: "style",
67
+ message: "Select citation style:",
68
+ choices: STYLE_CHOICES
69
+ };
70
+ try {
71
+ const prompt = new Select(promptOptions);
72
+ const result = await prompt.run();
73
+ return {
74
+ style: result,
75
+ cancelled: false
76
+ };
77
+ } catch (error) {
78
+ if (error === "" || error instanceof Error && error.message === "") {
79
+ return {
80
+ cancelled: true
81
+ };
82
+ }
83
+ throw error;
84
+ }
85
+ }
86
+ async function runActionMenu(items) {
87
+ const enquirer = await import("enquirer");
88
+ const Select = enquirer.default.Select;
89
+ const count = items.length;
90
+ const refWord = count === 1 ? "reference" : "references";
91
+ const message = `Action for ${count} selected ${refWord}:`;
92
+ const promptOptions = {
93
+ name: "action",
94
+ message,
95
+ choices: ACTION_CHOICES
96
+ };
97
+ try {
98
+ const prompt = new Select(promptOptions);
99
+ const action = await prompt.run();
100
+ return processAction(action, items);
101
+ } catch (error) {
102
+ if (error === "" || error instanceof Error && error.message === "") {
103
+ return {
104
+ action: "cancel",
105
+ output: "",
106
+ cancelled: true
107
+ };
108
+ }
109
+ throw error;
110
+ }
111
+ }
112
+ export {
113
+ ACTION_CHOICES,
114
+ STYLE_CHOICES,
115
+ runActionMenu,
116
+ runStyleSelectPrompt
117
+ };
118
+ //# sourceMappingURL=action-menu-CTtINmWd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-menu-CTtINmWd.js","sources":["../../src/features/interactive/action-menu.ts"],"sourcesContent":["/**\n * Action menu for interactive search mode.\n * Allows users to perform actions on selected references.\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport { formatBibliographyCSL, formatBibtex } from \"../format/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 * Choice definition for Enquirer Select prompt.\n */\ninterface SelectChoice {\n name: string;\n message: string;\n value: ActionType | string;\n}\n\n/**\n * Available action choices for the action menu.\n */\nexport const ACTION_CHOICES: SelectChoice[] = [\n { name: \"output-ids\", message: \"Output IDs (citation keys)\", value: \"output-ids\" },\n { name: \"output-csl-json\", message: \"Output as CSL-JSON\", value: \"output-csl-json\" },\n { name: \"output-bibtex\", message: \"Output as BibTeX\", value: \"output-bibtex\" },\n { name: \"cite-apa\", message: \"Generate citation (APA)\", value: \"cite-apa\" },\n { name: \"cite-choose\", message: \"Generate citation (choose style)\", value: \"cite-choose\" },\n { name: \"cancel\", message: \"Cancel\", value: \"cancel\" },\n];\n\n/**\n * Available style choices for citation style selection.\n * Uses BUILTIN_STYLES from config/csl-styles.ts\n */\nexport const STYLE_CHOICES: SelectChoice[] = [\n { name: \"apa\", message: \"APA\", value: \"apa\" },\n { name: \"vancouver\", message: \"Vancouver\", value: \"vancouver\" },\n { name: \"harvard\", message: \"Harvard\", value: \"harvard\" },\n];\n\n/**\n * Run the style selection prompt.\n */\n\n/**\n * Generate output for the given action and items.\n */\nfunction 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 * 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\nexport async function runStyleSelectPrompt(): Promise<StyleSelectResult> {\n // Dynamic import to allow mocking in tests\n // enquirer is a CommonJS module, so we must use default import\n const enquirer = await import(\"enquirer\");\n const Select = (enquirer.default as unknown as Record<string, unknown>).Select as new (\n options: Record<string, unknown>\n ) => { run(): Promise<string> };\n\n const promptOptions = {\n name: \"style\",\n message: \"Select citation style:\",\n choices: STYLE_CHOICES,\n };\n\n try {\n const prompt = new Select(promptOptions);\n const result = (await prompt.run()) as string;\n\n return {\n style: result,\n cancelled: false,\n };\n } catch (error) {\n // Enquirer throws an empty string when cancelled\n if (error === \"\" || (error instanceof Error && error.message === \"\")) {\n return {\n cancelled: true,\n };\n }\n throw error;\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 // Dynamic import to allow mocking in tests\n // enquirer is a CommonJS module, so we must use default import\n const enquirer = await import(\"enquirer\");\n const Select = (enquirer.default as unknown as Record<string, unknown>).Select as new (\n options: Record<string, unknown>\n ) => { run(): Promise<string> };\n\n const count = items.length;\n const refWord = count === 1 ? \"reference\" : \"references\";\n const message = `Action for ${count} selected ${refWord}:`;\n\n const promptOptions = {\n name: \"action\",\n message,\n choices: ACTION_CHOICES,\n };\n\n try {\n const prompt = new Select(promptOptions);\n const action = (await prompt.run()) as ActionType;\n\n return processAction(action, items);\n } catch (error) {\n // Enquirer throws an empty string when cancelled\n if (error === \"\" || (error instanceof Error && error.message === \"\")) {\n return {\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n };\n }\n throw error;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -956,6 +956,10 @@ function parseUnquotedValue(query, startIndex, includeQuotes = false) {
956
956
  nextIndex: i
957
957
  };
958
958
  }
959
+ const tokenizer = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
960
+ __proto__: null,
961
+ tokenize
962
+ }, Symbol.toStringTag, { value: "Module" }));
959
963
  function normalize(text) {
960
964
  let normalized = text.normalize("NFKC");
961
965
  normalized = normalized.toLowerCase();
@@ -1293,6 +1297,12 @@ function search(references, tokens) {
1293
1297
  }
1294
1298
  return results;
1295
1299
  }
1300
+ const matcher = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1301
+ __proto__: null,
1302
+ matchReference,
1303
+ matchToken,
1304
+ search
1305
+ }, Symbol.toStringTag, { value: "Module" }));
1296
1306
  function extractYear$1(reference) {
1297
1307
  if (reference.issued?.["date-parts"]?.[0]?.[0]) {
1298
1308
  return String(reference.issued["date-parts"][0][0]);
@@ -1691,6 +1701,8 @@ export {
1691
1701
  tokenize as t,
1692
1702
  sortFieldSchema as u,
1693
1703
  searchSortFieldSchema as v,
1694
- writeCslJson as w
1704
+ writeCslJson as w,
1705
+ tokenizer as x,
1706
+ matcher as y
1695
1707
  };
1696
- //# sourceMappingURL=file-watcher-Cwfnnw92.js.map
1708
+ //# sourceMappingURL=file-watcher-D7oyc-9z.js.map