@ncukondo/reference-manager 0.23.1 → 0.23.3

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 (27) hide show
  1. package/dist/chunks/{action-menu-7QLr1lTM.js → action-menu-D0d5ph_3.js} +3 -3
  2. package/dist/chunks/{action-menu-7QLr1lTM.js.map → action-menu-D0d5ph_3.js.map} +1 -1
  3. package/dist/chunks/{format-0YxykmO-.js → format-CH02ev3l.js} +2 -2
  4. package/dist/chunks/{format-0YxykmO-.js.map → format-CH02ev3l.js.map} +1 -1
  5. package/dist/chunks/{index-sH2o68X1.js → index-7R-uEYOu.js} +14 -4
  6. package/dist/chunks/{index-sH2o68X1.js.map → index-7R-uEYOu.js.map} +1 -1
  7. package/dist/chunks/{index-C5G3Nm7i.js → index-CFwvg1Ju.js} +27 -27
  8. package/dist/chunks/{index-C5G3Nm7i.js.map → index-CFwvg1Ju.js.map} +1 -1
  9. package/dist/chunks/{index-BZDH5KRo.js → index-Dktc7Q6f.js} +4 -4
  10. package/dist/chunks/{index-BZDH5KRo.js.map → index-Dktc7Q6f.js.map} +1 -1
  11. package/dist/chunks/{index-COAh_acl.js → index-Dotd2g8H.js} +3 -3
  12. package/dist/chunks/index-Dotd2g8H.js.map +1 -0
  13. package/dist/chunks/{loader-CzviN_sy.js → loader-DOYCI2xk.js} +4 -2
  14. package/dist/chunks/loader-DOYCI2xk.js.map +1 -0
  15. package/dist/chunks/{reference-select-D0qAyipM.js → reference-select-D_Clnq0X.js} +3 -3
  16. package/dist/chunks/{reference-select-D0qAyipM.js.map → reference-select-D_Clnq0X.js.map} +1 -1
  17. package/dist/chunks/{style-select-CcVzhqgU.js → style-select-VJryW_Ms.js} +3 -3
  18. package/dist/chunks/{style-select-CcVzhqgU.js.map → style-select-VJryW_Ms.js.map} +1 -1
  19. package/dist/cli.js +1 -1
  20. package/dist/config/schema.d.ts +2 -0
  21. package/dist/config/schema.d.ts.map +1 -1
  22. package/dist/features/operations/fulltext/fetch.d.ts.map +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/server.js +1 -1
  25. package/package.json +1 -1
  26. package/dist/chunks/index-COAh_acl.js.map +0 -1
  27. package/dist/chunks/loader-CzviN_sy.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { useApp, Box, render } from "ink";
2
2
  import { useState, useEffect, createElement } from "react";
3
- import { getActionChoices, OUTPUT_FORMAT_CHOICES, STYLE_CHOICES, isSideEffectAction, generateOutput } from "./action-menu-7QLr1lTM.js";
4
- import { S as SearchableMultiSelect, c as calculateEffectiveLimit, f as formatAuthors } from "./format-0YxykmO-.js";
5
- import { S as Select, r as restoreStdinAfterInk, b as buildResourceIndicators } from "./index-C5G3Nm7i.js";
3
+ import { getActionChoices, OUTPUT_FORMAT_CHOICES, STYLE_CHOICES, isSideEffectAction, generateOutput } from "./action-menu-D0d5ph_3.js";
4
+ import { S as SearchableMultiSelect, c as calculateEffectiveLimit, f as formatAuthors } from "./format-CH02ev3l.js";
5
+ import { S as Select, r as restoreStdinAfterInk, b as buildResourceIndicators } from "./index-CFwvg1Ju.js";
6
6
  function SearchFlowApp({
7
7
  choices,
8
8
  filterFn,
@@ -476,4 +476,4 @@ export {
476
476
  runCiteFlow,
477
477
  runSearchFlow
478
478
  };
479
- //# sourceMappingURL=index-BZDH5KRo.js.map
479
+ //# sourceMappingURL=index-Dktc7Q6f.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-BZDH5KRo.js","sources":["../../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 * SearchFlowApp - Single App for search -t flow\n *\n * Manages state transitions: search → action → (style/output-format 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 { CitationKeyFormat } from \"../../../config/schema.js\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport {\n type ActionMenuResult,\n type ActionType,\n OUTPUT_FORMAT_CHOICES,\n type OutputFormatType,\n STYLE_CHOICES,\n generateOutput,\n getActionChoices,\n isSideEffectAction,\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\" | \"output-format\" | \"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 /** Default citation key format */\n defaultKeyFormat: CitationKeyFormat;\n /** Default citation style */\n defaultStyle: string;\n /** Debounce delay in milliseconds for search filtering */\n debounceMs?: number;\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/output-format flow\n */\nexport function SearchFlowApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n defaultKeyFormat,\n defaultStyle,\n debounceMs,\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 // If output-format, go to output format submenu\n if (action === \"output-format\") {\n setState(\"output-format\");\n return;\n }\n\n // Handle side-effect actions\n if (isSideEffectAction(action)) {\n exitWith({ action, output: \"\", cancelled: false, selectedItems });\n return;\n }\n\n // Generate output and complete\n const output = generateOutput(action, selectedItems, {\n defaultKeyFormat,\n defaultStyle,\n });\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, {\n defaultKeyFormat,\n defaultStyle: style,\n });\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 // Handle output format selection\n const handleOutputFormatSelect = (format: OutputFormatType) => {\n if (format === \"cancel\") {\n setState(\"action\");\n return;\n }\n\n const output = generateOutput(format, selectedItems, {\n defaultKeyFormat,\n defaultStyle,\n });\n exitWith({ action: \"output-format\", output, cancelled: false });\n };\n\n // Handle output format cancel (go back to action)\n const handleOutputFormatCancel = () => {\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 ...(debounceMs !== undefined && { debounceMs }),\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 key: \"action\",\n options: getActionChoices(count, { defaultKeyFormat }),\n message: `Action for ${count} selected ${refWord}:`,\n onSelect: handleActionSelect,\n onCancel: handleActionCancel,\n });\n }\n\n if (state === \"output-format\") {\n return createElement(Select<OutputFormatType>, {\n key: \"output-format\",\n options: OUTPUT_FORMAT_CHOICES,\n message: \"Select output format:\",\n onSelect: handleOutputFormatSelect,\n onCancel: handleOutputFormatCancel,\n });\n }\n\n // state === \"style\"\n return createElement(Select<string>, {\n key: \"style\",\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 { CitationKeyFormat } from \"../../../config/schema.js\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport { buildResourceIndicators } from \"../../format/resource-indicators.js\";\nimport type { SearchResult } from \"../../search/types.js\";\nimport type { ActionMenuResult } from \"../action-menu.js\";\nimport { restoreStdinAfterInk } from \"../alternate-screen.js\";\nimport { type Choice, type SortOption, calculateEffectiveLimit } 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 for search filtering */\n debounceMs: number;\n /** Default citation key format */\n defaultKeyFormat?: CitationKeyFormat;\n /** Default citation style */\n defaultStyle?: string;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\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 */\nexport function 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 // Prepend resource indicators to meta if present\n const indicators = buildResourceIndicators(item);\n const metaStr = metaParts.join(\" · \");\n const meta = indicators ? `${indicators} ${metaStr}` : metaStr;\n\n return {\n id: item.id,\n title: item.title ?? \"(No title)\",\n subtitle: authors || \"(No authors)\",\n meta,\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 and build lookup map\n const choices = allReferences.map(toChoice);\n const choiceMap = new Map(choices.map((c) => [c.id, c]));\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.flatMap((r) => {\n const choice = choiceMap.get(r.reference.id);\n return choice ? [choice] : [];\n });\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 defaultKeyFormat: config.defaultKeyFormat ?? \"pandoc\",\n defaultStyle: config.defaultStyle ?? \"apa\",\n debounceMs: config.debounceMs,\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 {\n type Choice,\n type SelectOption,\n type SortOption,\n calculateEffectiveLimit,\n} 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 * 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":["extractYear","extractPublishedDate","extractUpdatedDate","extractCreatedDate","formatIdentifiers","formatType","toChoice","choices"],"mappings":"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"}
1
+ {"version":3,"file":"index-Dktc7Q6f.js","sources":["../../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 * SearchFlowApp - Single App for search -t flow\n *\n * Manages state transitions: search → action → (style/output-format 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 { CitationKeyFormat } from \"../../../config/schema.js\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport {\n type ActionMenuResult,\n type ActionType,\n OUTPUT_FORMAT_CHOICES,\n type OutputFormatType,\n STYLE_CHOICES,\n generateOutput,\n getActionChoices,\n isSideEffectAction,\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\" | \"output-format\" | \"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 /** Default citation key format */\n defaultKeyFormat: CitationKeyFormat;\n /** Default citation style */\n defaultStyle: string;\n /** Debounce delay in milliseconds for search filtering */\n debounceMs?: number;\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/output-format flow\n */\nexport function SearchFlowApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n defaultKeyFormat,\n defaultStyle,\n debounceMs,\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 // If output-format, go to output format submenu\n if (action === \"output-format\") {\n setState(\"output-format\");\n return;\n }\n\n // Handle side-effect actions\n if (isSideEffectAction(action)) {\n exitWith({ action, output: \"\", cancelled: false, selectedItems });\n return;\n }\n\n // Generate output and complete\n const output = generateOutput(action, selectedItems, {\n defaultKeyFormat,\n defaultStyle,\n });\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, {\n defaultKeyFormat,\n defaultStyle: style,\n });\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 // Handle output format selection\n const handleOutputFormatSelect = (format: OutputFormatType) => {\n if (format === \"cancel\") {\n setState(\"action\");\n return;\n }\n\n const output = generateOutput(format, selectedItems, {\n defaultKeyFormat,\n defaultStyle,\n });\n exitWith({ action: \"output-format\", output, cancelled: false });\n };\n\n // Handle output format cancel (go back to action)\n const handleOutputFormatCancel = () => {\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 ...(debounceMs !== undefined && { debounceMs }),\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 key: \"action\",\n options: getActionChoices(count, { defaultKeyFormat }),\n message: `Action for ${count} selected ${refWord}:`,\n onSelect: handleActionSelect,\n onCancel: handleActionCancel,\n });\n }\n\n if (state === \"output-format\") {\n return createElement(Select<OutputFormatType>, {\n key: \"output-format\",\n options: OUTPUT_FORMAT_CHOICES,\n message: \"Select output format:\",\n onSelect: handleOutputFormatSelect,\n onCancel: handleOutputFormatCancel,\n });\n }\n\n // state === \"style\"\n return createElement(Select<string>, {\n key: \"style\",\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 { CitationKeyFormat } from \"../../../config/schema.js\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport { buildResourceIndicators } from \"../../format/resource-indicators.js\";\nimport type { SearchResult } from \"../../search/types.js\";\nimport type { ActionMenuResult } from \"../action-menu.js\";\nimport { restoreStdinAfterInk } from \"../alternate-screen.js\";\nimport { type Choice, type SortOption, calculateEffectiveLimit } 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 for search filtering */\n debounceMs: number;\n /** Default citation key format */\n defaultKeyFormat?: CitationKeyFormat;\n /** Default citation style */\n defaultStyle?: string;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\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 */\nexport function 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 // Prepend resource indicators to meta if present\n const indicators = buildResourceIndicators(item);\n const metaStr = metaParts.join(\" · \");\n const meta = indicators ? `${indicators} ${metaStr}` : metaStr;\n\n return {\n id: item.id,\n title: item.title ?? \"(No title)\",\n subtitle: authors || \"(No authors)\",\n meta,\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 and build lookup map\n const choices = allReferences.map(toChoice);\n const choiceMap = new Map(choices.map((c) => [c.id, c]));\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.flatMap((r) => {\n const choice = choiceMap.get(r.reference.id);\n return choice ? [choice] : [];\n });\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 defaultKeyFormat: config.defaultKeyFormat ?? \"pandoc\",\n defaultStyle: config.defaultStyle ?? \"apa\",\n debounceMs: config.debounceMs,\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 {\n type Choice,\n type SelectOption,\n type SortOption,\n calculateEffectiveLimit,\n} 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 * 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":["extractYear","extractPublishedDate","extractUpdatedDate","extractCreatedDate","formatIdentifiers","formatType","toChoice","choices"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { a } from "./index-sH2o68X1.js";
2
- import { d, g, l, o, s } from "./index-C5G3Nm7i.js";
1
+ import { a } from "./index-7R-uEYOu.js";
2
+ import { d, g, l, o, s } from "./index-CFwvg1Ju.js";
3
3
  export {
4
4
  a as addAttachment,
5
5
  d as detachAttachment,
@@ -8,4 +8,4 @@ export {
8
8
  o as openAttachment,
9
9
  s as syncAttachments
10
10
  };
11
- //# sourceMappingURL=index-COAh_acl.js.map
11
+ //# sourceMappingURL=index-Dotd2g8H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-Dotd2g8H.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -230,7 +230,9 @@ const partialConfigSchema = z.object({
230
230
  tui: z.object({
231
231
  limit: z.number().int().nonnegative().optional(),
232
232
  debounceMs: z.number().int().nonnegative().optional(),
233
- debounce_ms: z.number().int().nonnegative().optional()
233
+ debounce_ms: z.number().int().nonnegative().optional(),
234
+ clipboardAutoCopy: z.boolean().optional(),
235
+ clipboard_auto_copy: z.boolean().optional()
234
236
  }).optional(),
235
237
  edit: z.object({
236
238
  defaultFormat: editFormatSchema.optional(),
@@ -837,4 +839,4 @@ export {
837
839
  partialConfigSchema as p,
838
840
  watchConfigSchema as w
839
841
  };
840
- //# sourceMappingURL=loader-CzviN_sy.js.map
842
+ //# sourceMappingURL=loader-DOYCI2xk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader-DOYCI2xk.js","sources":["../../src/utils/opener.ts","../../src/config/schema.ts","../../node_modules/env-paths/index.js","../../src/config/paths.ts","../../src/config/defaults.ts","../../src/config/loader.ts"],"sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\n\n/**\n * Detect if running in Windows Subsystem for Linux (WSL)\n */\nexport function isWSL(): boolean {\n // Check for WSL_DISTRO_NAME environment variable\n if (process.env.WSL_DISTRO_NAME !== undefined) {\n return true;\n }\n\n // Check for WSLInterop file\n try {\n return fs.existsSync(\"/proc/sys/fs/binfmt_misc/WSLInterop\");\n } catch {\n return false;\n }\n}\n\n/**\n * Get the system opener command for the specified platform.\n * @param platform - The platform (darwin, linux, win32)\n * @param wsl - Whether running in WSL (defaults to isWSL())\n * @returns The command array to execute\n */\nexport function getOpenerCommand(platform: string, wsl: boolean = isWSL()): string[] {\n switch (platform) {\n case \"darwin\":\n return [\"open\"];\n case \"linux\":\n if (wsl) {\n return [\"wslview\"];\n }\n return [\"xdg-open\"];\n case \"win32\":\n return [\"cmd\", \"/c\", \"start\", \"\"];\n default:\n throw new Error(`Unsupported platform: ${platform}`);\n }\n}\n\n/**\n * Open a file with the system's default application.\n * @param filePath - The path to the file to open\n * @param platform - The platform (defaults to process.platform)\n */\nexport async function openWithSystemApp(\n filePath: string,\n platform: string = process.platform\n): Promise<void> {\n const wsl = isWSL();\n const commandParts = getOpenerCommand(platform, wsl);\n const [command, ...baseArgs] = commandParts;\n const args = [...baseArgs, filePath];\n\n // Use a timer to keep the event loop alive while waiting for the close event.\n // In Node.js 22+, after enquirer prompts complete, the event loop may have no\n // active handles. Combined with proc.unref(), this can cause Node.js to exit\n // before the close event fires.\n const keepAliveTimer = setInterval(() => {\n // No-op: timer exists solely to keep event loop active\n }, 60000);\n\n return new Promise<void>((resolve, reject) => {\n const proc: ChildProcess = spawn(command as string, args, {\n detached: true,\n stdio: \"ignore\",\n });\n\n const cleanup = (): void => {\n clearInterval(keepAliveTimer);\n };\n\n proc.on(\"error\", (err: NodeJS.ErrnoException) => {\n cleanup();\n // Check if the command was not found (ENOENT)\n if (err.code === \"ENOENT\") {\n if (wsl && command === \"wslview\") {\n reject(new Error(\"wslview not found. Install with: sudo apt install wslu\"));\n } else if (command === \"xdg-open\") {\n reject(new Error(\"xdg-open not found. Install a desktop environment or xdg-utils.\"));\n } else {\n reject(new Error(`Opener command '${command}' not found`));\n }\n } else {\n reject(new Error(`Failed to open: ${filePath}`));\n }\n });\n\n proc.on(\"close\", (code: number | null) => {\n cleanup();\n // wslview may return non-zero exit codes even on success\n // We treat the operation as successful if the process exited without error\n if (code === 0 || (wsl && command === \"wslview\")) {\n resolve();\n } else {\n reject(new Error(`Failed to open: ${filePath}`));\n }\n });\n\n proc.unref();\n });\n}\n","/**\n * Configuration schema using Zod\n */\n\nimport { z } from \"zod\";\nimport { sortFieldSchema, sortOrderSchema } from \"../features/pagination/types.js\";\n\n/**\n * Log level schema\n */\nexport const logLevelSchema = z.enum([\"silent\", \"info\", \"debug\"]);\n\n/**\n * TUI (interactive) search configuration schema\n */\nexport const tuiConfigSchema = z.object({\n limit: z.number().int().nonnegative(),\n debounceMs: z.number().int().nonnegative(),\n clipboardAutoCopy: z.boolean(),\n});\n\n/**\n * Edit format schema\n */\nexport const editFormatSchema = z.enum([\"yaml\", \"json\"]);\n\n/**\n * Edit command configuration schema\n */\nexport const editConfigSchema = z.object({\n defaultFormat: editFormatSchema,\n});\n\n/**\n * CLI configuration schema\n */\nexport const cliConfigSchema = z.object({\n defaultLimit: z.number().int().nonnegative(),\n defaultSort: sortFieldSchema,\n defaultOrder: sortOrderSchema,\n tui: tuiConfigSchema,\n edit: editConfigSchema,\n});\n\n/**\n * MCP configuration schema\n */\nexport const mcpConfigSchema = z.object({\n defaultLimit: z.number().int().nonnegative(),\n});\n\n/**\n * Backup configuration schema\n */\nexport const backupConfigSchema = z.object({\n maxGenerations: z.number().int().positive(),\n maxAgeDays: z.number().int().positive(),\n directory: z.string().min(1),\n});\n\n/**\n * File watching configuration schema\n * Note: File watching is always enabled in server mode (HTTP/MCP).\n * CLI mode does not use file watching.\n */\nexport const watchConfigSchema = z.object({\n debounceMs: z.number().int().nonnegative(),\n pollIntervalMs: z.number().int().positive(),\n retryIntervalMs: z.number().int().positive(),\n maxRetries: z.number().int().nonnegative(),\n});\n\n/**\n * Server configuration schema\n */\nexport const serverConfigSchema = z.object({\n autoStart: z.boolean(),\n autoStopMinutes: z.number().int().nonnegative(),\n});\n\n/**\n * Citation format schema\n */\nexport const citationFormatSchema = z.enum([\"text\", \"html\", \"rtf\"]);\n\n/**\n * Citation key format schema\n */\nexport const citationKeyFormatSchema = z.enum([\"pandoc\", \"latex\"]);\n\n/**\n * Citation configuration schema\n */\nexport const citationConfigSchema = z.object({\n defaultStyle: z.string(),\n cslDirectory: z.array(z.string()),\n defaultLocale: z.string(),\n defaultFormat: citationFormatSchema,\n defaultKeyFormat: citationKeyFormatSchema,\n});\n\n/**\n * PubMed API configuration schema\n */\nexport const pubmedConfigSchema = z.object({\n email: z.string().optional(),\n apiKey: z.string().optional(),\n});\n\n/**\n * Fulltext source enum schema\n */\nexport const fulltextSourceSchema = z.enum([\"pmc\", \"arxiv\", \"unpaywall\", \"core\"]);\nconst fulltextPreferredTypeSchema = z.enum([\"pdf\", \"markdown\"]);\n\n/**\n * Fulltext retrieval configuration schema\n */\nexport const fulltextConfigSchema = z.object({\n preferSources: z.array(fulltextSourceSchema),\n preferredType: fulltextPreferredTypeSchema.optional(),\n autoFetchOnAdd: z.boolean(),\n sources: z.object({\n unpaywallEmail: z.string().optional(),\n coreApiKey: z.string().optional(),\n ncbiEmail: z.string().optional(),\n ncbiTool: z.string().optional(),\n }),\n});\n\n/**\n * Attachments storage configuration schema\n */\nexport const attachmentsConfigSchema = z.object({\n directory: z.string().min(1),\n});\n\n/**\n * Complete configuration schema\n */\nexport const configSchema = z.object({\n library: z.string().min(1),\n logLevel: logLevelSchema,\n backup: backupConfigSchema,\n watch: watchConfigSchema,\n server: serverConfigSchema,\n citation: citationConfigSchema,\n pubmed: pubmedConfigSchema,\n fulltext: fulltextConfigSchema,\n attachments: attachmentsConfigSchema,\n cli: cliConfigSchema,\n mcp: mcpConfigSchema,\n});\n\n/**\n * Partial configuration schema (for TOML files)\n * Supports both camelCase and snake_case field names\n */\nexport const partialConfigSchema = z\n .object({\n library: z.string().min(1).optional(),\n logLevel: logLevelSchema.optional(),\n log_level: logLevelSchema.optional(), // snake_case support\n backup: z\n .object({\n maxGenerations: z.number().int().positive().optional(),\n max_generations: z.number().int().positive().optional(),\n maxAgeDays: z.number().int().positive().optional(),\n max_age_days: z.number().int().positive().optional(),\n directory: z.string().min(1).optional(),\n })\n .optional(),\n watch: z\n .object({\n debounceMs: z.number().int().nonnegative().optional(),\n debounce_ms: z.number().int().nonnegative().optional(),\n pollIntervalMs: z.number().int().positive().optional(),\n poll_interval_ms: z.number().int().positive().optional(),\n retryIntervalMs: z.number().int().positive().optional(),\n retry_interval_ms: z.number().int().positive().optional(),\n maxRetries: z.number().int().nonnegative().optional(),\n max_retries: z.number().int().nonnegative().optional(),\n })\n .optional(),\n server: z\n .object({\n autoStart: z.boolean().optional(),\n auto_start: z.boolean().optional(),\n autoStopMinutes: z.number().int().nonnegative().optional(),\n auto_stop_minutes: z.number().int().nonnegative().optional(),\n })\n .optional(),\n citation: z\n .object({\n defaultStyle: z.string().optional(),\n default_style: z.string().optional(),\n cslDirectory: z.union([z.string(), z.array(z.string())]).optional(),\n csl_directory: z.union([z.string(), z.array(z.string())]).optional(),\n defaultLocale: z.string().optional(),\n default_locale: z.string().optional(),\n defaultFormat: citationFormatSchema.optional(),\n default_format: citationFormatSchema.optional(),\n defaultKeyFormat: citationKeyFormatSchema.optional(),\n default_key_format: citationKeyFormatSchema.optional(),\n })\n .optional(),\n pubmed: z\n .object({\n email: z.string().optional(),\n apiKey: z.string().optional(),\n api_key: z.string().optional(),\n })\n .optional(),\n fulltext: z\n .object({\n preferSources: z.array(fulltextSourceSchema).optional(),\n prefer_sources: z.array(fulltextSourceSchema).optional(),\n preferredType: fulltextPreferredTypeSchema.optional(),\n preferred_type: fulltextPreferredTypeSchema.optional(),\n autoFetchOnAdd: z.boolean().optional(),\n auto_fetch_on_add: z.boolean().optional(),\n sources: z\n .object({\n unpaywallEmail: z.string().optional(),\n unpaywall_email: z.string().optional(),\n coreApiKey: z.string().optional(),\n core_api_key: z.string().optional(),\n ncbiEmail: z.string().optional(),\n ncbi_email: z.string().optional(),\n ncbiTool: z.string().optional(),\n ncbi_tool: z.string().optional(),\n })\n .optional(),\n })\n .optional(),\n attachments: z\n .object({\n directory: z.string().min(1).optional(),\n })\n .optional(),\n cli: z\n .object({\n defaultLimit: z.number().int().nonnegative().optional(),\n default_limit: z.number().int().nonnegative().optional(),\n defaultSort: sortFieldSchema.optional(),\n default_sort: sortFieldSchema.optional(),\n defaultOrder: sortOrderSchema.optional(),\n default_order: sortOrderSchema.optional(),\n tui: z\n .object({\n limit: z.number().int().nonnegative().optional(),\n debounceMs: z.number().int().nonnegative().optional(),\n debounce_ms: z.number().int().nonnegative().optional(),\n clipboardAutoCopy: z.boolean().optional(),\n clipboard_auto_copy: z.boolean().optional(),\n })\n .optional(),\n edit: z\n .object({\n defaultFormat: editFormatSchema.optional(),\n default_format: editFormatSchema.optional(),\n })\n .optional(),\n })\n .optional(),\n mcp: z\n .object({\n defaultLimit: z.number().int().nonnegative().optional(),\n default_limit: z.number().int().nonnegative().optional(),\n })\n .optional(),\n })\n .passthrough(); // Allow unknown fields in TOML files\n\n/**\n * Inferred types from schemas\n */\nexport type LogLevel = z.infer<typeof logLevelSchema>;\nexport type BackupConfig = z.infer<typeof backupConfigSchema>;\nexport type WatchConfig = z.infer<typeof watchConfigSchema>;\nexport type ServerConfig = z.infer<typeof serverConfigSchema>;\nexport type CitationFormat = z.infer<typeof citationFormatSchema>;\nexport type CitationKeyFormat = z.infer<typeof citationKeyFormatSchema>;\nexport type CitationConfig = z.infer<typeof citationConfigSchema>;\nexport type PubmedConfig = z.infer<typeof pubmedConfigSchema>;\nexport type FulltextSource = z.infer<typeof fulltextSourceSchema>;\nexport type FulltextPreferredType = z.infer<typeof fulltextPreferredTypeSchema>;\nexport type FulltextConfig = z.infer<typeof fulltextConfigSchema>;\nexport type AttachmentsConfig = z.infer<typeof attachmentsConfigSchema>;\nexport type TuiConfig = z.infer<typeof tuiConfigSchema>;\nexport type EditConfigFormat = z.infer<typeof editFormatSchema>;\nexport type EditConfig = z.infer<typeof editConfigSchema>;\nexport type CliConfig = z.infer<typeof cliConfigSchema>;\nexport type McpConfig = z.infer<typeof mcpConfigSchema>;\nexport type Config = z.infer<typeof configSchema>;\nexport type PartialConfig = z.infer<typeof partialConfigSchema>;\n\n/**\n * Deep partial type for Config\n */\nexport type DeepPartialConfig = {\n library?: string;\n logLevel?: LogLevel;\n backup?: Partial<BackupConfig>;\n watch?: Partial<WatchConfig>;\n server?: Partial<ServerConfig>;\n citation?: Partial<CitationConfig>;\n pubmed?: Partial<PubmedConfig>;\n fulltext?: Partial<Omit<FulltextConfig, \"sources\">> & {\n sources?: Partial<FulltextConfig[\"sources\"]>;\n };\n attachments?: Partial<AttachmentsConfig>;\n cli?: Partial<Omit<CliConfig, \"tui\" | \"edit\">> & {\n tui?: Partial<TuiConfig>;\n edit?: Partial<EditConfig>;\n };\n mcp?: Partial<McpConfig>;\n};\n\n/**\n * Normalize backup configuration from snake_case to camelCase\n */\nfunction normalizeBackupConfig(\n backup: Partial<{\n maxGenerations?: number;\n max_generations?: number;\n maxAgeDays?: number;\n max_age_days?: number;\n directory?: string;\n }>\n): Partial<BackupConfig> | undefined {\n const normalized: Partial<BackupConfig> = {};\n\n const maxGenerations = backup.maxGenerations ?? backup.max_generations;\n if (maxGenerations !== undefined) {\n normalized.maxGenerations = maxGenerations;\n }\n\n const maxAgeDays = backup.maxAgeDays ?? backup.max_age_days;\n if (maxAgeDays !== undefined) {\n normalized.maxAgeDays = maxAgeDays;\n }\n\n if (backup.directory !== undefined) {\n normalized.directory = backup.directory;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize watch configuration from snake_case to camelCase\n */\nfunction normalizeWatchConfig(\n watch: Partial<{\n debounceMs?: number;\n debounce_ms?: number;\n pollIntervalMs?: number;\n poll_interval_ms?: number;\n retryIntervalMs?: number;\n retry_interval_ms?: number;\n maxRetries?: number;\n max_retries?: number;\n }>\n): Partial<WatchConfig> | undefined {\n const normalized: Partial<WatchConfig> = {};\n\n const debounceMs = watch.debounceMs ?? watch.debounce_ms;\n if (debounceMs !== undefined) {\n normalized.debounceMs = debounceMs;\n }\n\n const pollIntervalMs = watch.pollIntervalMs ?? watch.poll_interval_ms;\n if (pollIntervalMs !== undefined) {\n normalized.pollIntervalMs = pollIntervalMs;\n }\n\n const retryIntervalMs = watch.retryIntervalMs ?? watch.retry_interval_ms;\n if (retryIntervalMs !== undefined) {\n normalized.retryIntervalMs = retryIntervalMs;\n }\n\n const maxRetries = watch.maxRetries ?? watch.max_retries;\n if (maxRetries !== undefined) {\n normalized.maxRetries = maxRetries;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize server configuration from snake_case to camelCase\n */\nfunction normalizeServerConfig(\n server: Partial<{\n autoStart?: boolean;\n auto_start?: boolean;\n autoStopMinutes?: number;\n auto_stop_minutes?: number;\n }>\n): Partial<ServerConfig> | undefined {\n const normalized: Partial<ServerConfig> = {};\n\n const autoStart = server.autoStart ?? server.auto_start;\n if (autoStart !== undefined) {\n normalized.autoStart = autoStart;\n }\n\n const autoStopMinutes = server.autoStopMinutes ?? server.auto_stop_minutes;\n if (autoStopMinutes !== undefined) {\n normalized.autoStopMinutes = autoStopMinutes;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize citation configuration from snake_case to camelCase\n */\nfunction normalizeCitationConfig(\n citation: Partial<{\n defaultStyle?: string;\n default_style?: string;\n cslDirectory?: string | string[];\n csl_directory?: string | string[];\n defaultLocale?: string;\n default_locale?: string;\n defaultFormat?: CitationFormat;\n default_format?: CitationFormat;\n defaultKeyFormat?: CitationKeyFormat;\n default_key_format?: CitationKeyFormat;\n }>\n): Partial<CitationConfig> | undefined {\n const normalized: Partial<CitationConfig> = {};\n\n const defaultStyle = citation.defaultStyle ?? citation.default_style;\n if (defaultStyle !== undefined) {\n normalized.defaultStyle = defaultStyle;\n }\n\n const cslDirectory = citation.cslDirectory ?? citation.csl_directory;\n if (cslDirectory !== undefined) {\n // Normalize to array: string -> [string]\n normalized.cslDirectory = Array.isArray(cslDirectory) ? cslDirectory : [cslDirectory];\n }\n\n const defaultLocale = citation.defaultLocale ?? citation.default_locale;\n if (defaultLocale !== undefined) {\n normalized.defaultLocale = defaultLocale;\n }\n\n const defaultFormat = citation.defaultFormat ?? citation.default_format;\n if (defaultFormat !== undefined) {\n normalized.defaultFormat = defaultFormat;\n }\n\n const defaultKeyFormat =\n (citation as Record<string, unknown>).defaultKeyFormat ??\n (citation as Record<string, unknown>).default_key_format;\n if (defaultKeyFormat !== undefined) {\n normalized.defaultKeyFormat = defaultKeyFormat as CitationKeyFormat;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize pubmed configuration from snake_case to camelCase\n */\nfunction normalizePubmedConfig(\n pubmed: Partial<{\n email?: string;\n apiKey?: string;\n api_key?: string;\n }>\n): Partial<PubmedConfig> | undefined {\n const normalized: Partial<PubmedConfig> = {};\n\n if (pubmed.email !== undefined) {\n normalized.email = pubmed.email;\n }\n\n const apiKey = pubmed.apiKey ?? pubmed.api_key;\n if (apiKey !== undefined) {\n normalized.apiKey = apiKey;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize fulltext configuration from snake_case to camelCase\n */\nfunction normalizeFulltextConfig(\n fulltext: Partial<{\n preferSources?: FulltextSource[];\n prefer_sources?: FulltextSource[];\n preferredType?: FulltextPreferredType;\n preferred_type?: FulltextPreferredType;\n autoFetchOnAdd?: boolean;\n auto_fetch_on_add?: boolean;\n sources?: Partial<{\n unpaywallEmail?: string;\n unpaywall_email?: string;\n coreApiKey?: string;\n core_api_key?: string;\n }>;\n }>\n): DeepPartialConfig[\"fulltext\"] | undefined {\n const normalized: NonNullable<DeepPartialConfig[\"fulltext\"]> = {};\n\n const preferSources = fulltext.preferSources ?? fulltext.prefer_sources;\n if (preferSources !== undefined) {\n normalized.preferSources = preferSources;\n }\n\n const preferredType = fulltext.preferredType ?? fulltext.preferred_type;\n if (preferredType !== undefined) {\n normalized.preferredType = preferredType;\n }\n\n const autoFetchOnAdd = fulltext.autoFetchOnAdd ?? fulltext.auto_fetch_on_add;\n if (autoFetchOnAdd !== undefined) {\n normalized.autoFetchOnAdd = autoFetchOnAdd;\n }\n\n const sources = normalizeFulltextSources(fulltext.sources);\n if (sources !== undefined) {\n normalized.sources = sources;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\nfunction normalizeFulltextSources(\n sources?: Partial<{\n unpaywallEmail?: string;\n unpaywall_email?: string;\n coreApiKey?: string;\n core_api_key?: string;\n ncbiEmail?: string;\n ncbi_email?: string;\n ncbiTool?: string;\n ncbi_tool?: string;\n }>\n): Partial<FulltextConfig[\"sources\"]> | undefined {\n if (sources === undefined) {\n return undefined;\n }\n\n const normalized: Partial<FulltextConfig[\"sources\"]> = {};\n\n const unpaywallEmail = sources.unpaywallEmail ?? sources.unpaywall_email;\n if (unpaywallEmail !== undefined) {\n normalized.unpaywallEmail = unpaywallEmail;\n }\n\n const coreApiKey = sources.coreApiKey ?? sources.core_api_key;\n if (coreApiKey !== undefined) {\n normalized.coreApiKey = coreApiKey;\n }\n\n const ncbiEmail = sources.ncbiEmail ?? sources.ncbi_email;\n if (ncbiEmail !== undefined) {\n normalized.ncbiEmail = ncbiEmail;\n }\n\n const ncbiTool = sources.ncbiTool ?? sources.ncbi_tool;\n if (ncbiTool !== undefined) {\n normalized.ncbiTool = ncbiTool;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Section normalizers mapping\n */\nconst sectionNormalizers = {\n backup: normalizeBackupConfig,\n watch: normalizeWatchConfig,\n server: normalizeServerConfig,\n citation: normalizeCitationConfig,\n pubmed: normalizePubmedConfig,\n fulltext: normalizeFulltextConfig,\n attachments: normalizeAttachmentsConfig,\n cli: normalizeCliConfig,\n mcp: normalizeMcpConfig,\n} as const;\n\ntype SectionKey = keyof typeof sectionNormalizers;\n\n/**\n * Helper to apply a normalizer function to a config section\n */\nfunction applyNormalizer<K extends SectionKey>(\n normalized: DeepPartialConfig,\n partial: PartialConfig,\n key: K,\n normalizer: (typeof sectionNormalizers)[K]\n): void {\n const value = partial[key];\n if (value !== undefined) {\n const result = (normalizer as (input: unknown) => DeepPartialConfig[K] | undefined)(value);\n if (result) {\n normalized[key] = result;\n }\n }\n}\n\n/**\n * Normalize snake_case fields to camelCase\n */\nexport function normalizePartialConfig(partial: PartialConfig): DeepPartialConfig {\n const normalized: DeepPartialConfig = {};\n\n // Simple fields\n if (partial.library !== undefined) {\n normalized.library = partial.library;\n }\n const logLevel = partial.logLevel ?? partial.log_level;\n if (logLevel !== undefined) {\n normalized.logLevel = logLevel;\n }\n\n // Section fields\n for (const key of Object.keys(sectionNormalizers) as SectionKey[]) {\n applyNormalizer(normalized, partial, key, sectionNormalizers[key]);\n }\n\n return normalized;\n}\n\n/**\n * Normalize attachments configuration\n */\nfunction normalizeAttachmentsConfig(attachments: {\n directory?: string | undefined;\n}): Partial<AttachmentsConfig> | undefined {\n const normalized: Partial<AttachmentsConfig> = {};\n\n if (attachments.directory !== undefined) {\n normalized.directory = attachments.directory;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize TUI config subsection\n */\nfunction normalizeTuiSection(\n tui: Partial<{\n limit?: number;\n debounceMs?: number;\n debounce_ms?: number;\n clipboardAutoCopy?: boolean;\n clipboard_auto_copy?: boolean;\n }>\n): Partial<TuiConfig> | undefined {\n const normalized: Partial<TuiConfig> = {};\n if (tui.limit !== undefined) {\n normalized.limit = tui.limit;\n }\n const debounceMs = tui.debounceMs ?? tui.debounce_ms;\n if (debounceMs !== undefined) {\n normalized.debounceMs = debounceMs;\n }\n const clipboardAutoCopy = tui.clipboardAutoCopy ?? tui.clipboard_auto_copy;\n if (clipboardAutoCopy !== undefined) {\n normalized.clipboardAutoCopy = clipboardAutoCopy;\n }\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize edit config subsection\n */\nfunction normalizeEditSection(\n edit: Partial<{\n defaultFormat?: EditConfigFormat;\n default_format?: EditConfigFormat;\n }>\n): Partial<EditConfig> | undefined {\n const normalized: Partial<EditConfig> = {};\n const defaultFormat = edit.defaultFormat ?? edit.default_format;\n if (defaultFormat !== undefined) {\n normalized.defaultFormat = defaultFormat;\n }\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize CLI configuration from snake_case to camelCase\n */\nfunction normalizeCliConfig(\n cli: Partial<{\n defaultLimit?: number;\n default_limit?: number;\n defaultSort?: CliConfig[\"defaultSort\"];\n default_sort?: CliConfig[\"defaultSort\"];\n defaultOrder?: CliConfig[\"defaultOrder\"];\n default_order?: CliConfig[\"defaultOrder\"];\n tui?: Partial<{\n limit?: number;\n debounceMs?: number;\n debounce_ms?: number;\n }>;\n edit?: Partial<{\n defaultFormat?: EditConfigFormat;\n default_format?: EditConfigFormat;\n }>;\n }>\n): Partial<CliConfig> | undefined {\n const normalized: Partial<CliConfig> = {};\n\n const defaultLimit = cli.defaultLimit ?? cli.default_limit;\n if (defaultLimit !== undefined) {\n normalized.defaultLimit = defaultLimit;\n }\n\n const defaultSort = cli.defaultSort ?? cli.default_sort;\n if (defaultSort !== undefined) {\n normalized.defaultSort = defaultSort;\n }\n\n const defaultOrder = cli.defaultOrder ?? cli.default_order;\n if (defaultOrder !== undefined) {\n normalized.defaultOrder = defaultOrder;\n }\n\n if (cli.tui !== undefined) {\n const tui = normalizeTuiSection(cli.tui);\n if (tui) {\n normalized.tui = tui as TuiConfig;\n }\n }\n\n if (cli.edit !== undefined) {\n const edit = normalizeEditSection(cli.edit);\n if (edit) {\n normalized.edit = edit as EditConfig;\n }\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/**\n * Normalize MCP configuration from snake_case to camelCase\n */\nfunction normalizeMcpConfig(\n mcp: Partial<{\n defaultLimit?: number;\n default_limit?: number;\n }>\n): Partial<McpConfig> | undefined {\n const normalized: Partial<McpConfig> = {};\n\n const defaultLimit = mcp.defaultLimit ?? mcp.default_limit;\n if (defaultLimit !== undefined) {\n normalized.defaultLimit = defaultLimit;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n","import path from 'node:path';\nimport os from 'node:os';\nimport process from 'node:process';\n\nconst homedir = os.homedir();\nconst tmpdir = os.tmpdir();\nconst {env} = process;\n\nconst macos = name => {\n\tconst library = path.join(homedir, 'Library');\n\n\treturn {\n\t\tdata: path.join(library, 'Application Support', name),\n\t\tconfig: path.join(library, 'Preferences', name),\n\t\tcache: path.join(library, 'Caches', name),\n\t\tlog: path.join(library, 'Logs', name),\n\t\ttemp: path.join(tmpdir, name),\n\t};\n};\n\nconst windows = name => {\n\tconst appData = env.APPDATA || path.join(homedir, 'AppData', 'Roaming');\n\tconst localAppData = env.LOCALAPPDATA || path.join(homedir, 'AppData', 'Local');\n\n\treturn {\n\t\t// Data/config/cache/log are invented by me as Windows isn't opinionated about this\n\t\tdata: path.join(localAppData, name, 'Data'),\n\t\tconfig: path.join(appData, name, 'Config'),\n\t\tcache: path.join(localAppData, name, 'Cache'),\n\t\tlog: path.join(localAppData, name, 'Log'),\n\t\ttemp: path.join(tmpdir, name),\n\t};\n};\n\n// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html\nconst linux = name => {\n\tconst username = path.basename(homedir);\n\n\treturn {\n\t\tdata: path.join(env.XDG_DATA_HOME || path.join(homedir, '.local', 'share'), name),\n\t\tconfig: path.join(env.XDG_CONFIG_HOME || path.join(homedir, '.config'), name),\n\t\tcache: path.join(env.XDG_CACHE_HOME || path.join(homedir, '.cache'), name),\n\t\t// https://wiki.debian.org/XDGBaseDirectorySpecification#state\n\t\tlog: path.join(env.XDG_STATE_HOME || path.join(homedir, '.local', 'state'), name),\n\t\ttemp: path.join(tmpdir, username, name),\n\t};\n};\n\nexport default function envPaths(name, {suffix = 'nodejs'} = {}) {\n\tif (typeof name !== 'string') {\n\t\tthrow new TypeError(`Expected a string, got ${typeof name}`);\n\t}\n\n\tif (suffix) {\n\t\t// Add suffix to prevent possible conflict with native apps\n\t\tname += `-${suffix}`;\n\t}\n\n\tif (process.platform === 'darwin') {\n\t\treturn macos(name);\n\t}\n\n\tif (process.platform === 'win32') {\n\t\treturn windows(name);\n\t}\n\n\treturn linux(name);\n}\n","/**\n * Platform-specific paths using env-paths\n *\n * Returns XDG-compliant paths on Linux, standard paths on macOS/Windows\n */\n\nimport envPaths from \"env-paths\";\n\nconst paths = envPaths(\"reference-manager\", { suffix: \"\" });\n\nexport interface Paths {\n config: string;\n data: string;\n cache: string;\n}\n\n/**\n * Get platform-specific paths for config, data, and cache directories\n *\n * - Linux: XDG Base Directory Specification (~/.config, ~/.local/share, ~/.cache)\n * - macOS: ~/Library/Preferences, ~/Library/Application Support, ~/Library/Caches\n * - Windows: %APPDATA%, %LOCALAPPDATA%\n */\nexport function getPaths(): Paths {\n return {\n config: paths.config,\n data: paths.data,\n cache: paths.cache,\n };\n}\n","/**\n * Default configuration values\n */\n\nimport { join } from \"node:path\";\nimport { getPaths } from \"./paths.js\";\nimport type { Config } from \"./schema.js\";\n\n/**\n * Get the default backup directory\n * Uses platform-specific cache path + backups/\n */\nexport function getDefaultBackupDirectory(): string {\n return join(getPaths().cache, \"backups\");\n}\n\n/**\n * Get the default library path\n * Uses platform-specific data path + library.json\n */\nexport function getDefaultLibraryPath(): string {\n return join(getPaths().data, \"library.json\");\n}\n\n/**\n * Get the default user config path\n * Uses platform-specific config path + config.toml\n */\nexport function getDefaultUserConfigPath(): string {\n return join(getPaths().config, \"config.toml\");\n}\n\n/**\n * Get the default current directory config filename\n * Uses .reference-manager.config.toml\n */\nexport function getDefaultCurrentDirConfigFilename(): string {\n return \".reference-manager.config.toml\";\n}\n\n/**\n * Get the default CSL directory\n * Uses platform-specific data path + csl/\n */\nexport function getDefaultCslDirectory(): string {\n return join(getPaths().data, \"csl\");\n}\n\n/**\n * Get the default attachments directory\n * Uses platform-specific data path + attachments/\n */\nexport function getDefaultAttachmentsDirectory(): string {\n return join(getPaths().data, \"attachments\");\n}\n\n/**\n * Default configuration\n */\nexport const defaultConfig: Config = {\n library: getDefaultLibraryPath(),\n logLevel: \"info\",\n backup: {\n maxGenerations: 50,\n maxAgeDays: 365,\n directory: getDefaultBackupDirectory(),\n },\n watch: {\n debounceMs: 500,\n pollIntervalMs: 5000,\n retryIntervalMs: 200,\n maxRetries: 10,\n },\n server: {\n autoStart: false,\n autoStopMinutes: 0,\n },\n citation: {\n defaultStyle: \"apa\",\n cslDirectory: [getDefaultCslDirectory()],\n defaultLocale: \"en-US\",\n defaultFormat: \"text\",\n defaultKeyFormat: \"pandoc\",\n },\n pubmed: {\n email: undefined,\n apiKey: undefined,\n },\n fulltext: {\n preferSources: [\"pmc\", \"arxiv\", \"unpaywall\", \"core\"],\n preferredType: undefined,\n autoFetchOnAdd: false,\n sources: {\n unpaywallEmail: undefined,\n coreApiKey: undefined,\n ncbiEmail: undefined,\n ncbiTool: undefined,\n },\n },\n attachments: {\n directory: getDefaultAttachmentsDirectory(),\n },\n cli: {\n defaultLimit: 0,\n defaultSort: \"updated\",\n defaultOrder: \"desc\",\n tui: {\n limit: 20,\n debounceMs: 200,\n clipboardAutoCopy: false,\n },\n edit: {\n defaultFormat: \"yaml\",\n },\n },\n mcp: {\n defaultLimit: 20,\n },\n};\n","/**\n * Configuration loader\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parse as parseTOML } from \"@iarna/toml\";\nimport {\n defaultConfig,\n getDefaultCurrentDirConfigFilename,\n getDefaultUserConfigPath,\n} from \"./defaults.js\";\nimport {\n type Config,\n type DeepPartialConfig,\n type PartialConfig,\n configSchema,\n normalizePartialConfig,\n partialConfigSchema,\n} from \"./schema.js\";\n\n/**\n * Options for loading configuration\n */\nexport interface LoadConfigOptions {\n /** Current working directory (default: process.cwd()) */\n cwd?: string;\n /** User config path (default: ~/.reference-manager/config.toml) */\n userConfigPath?: string;\n /** CLI --config flag: explicit config file path (highest file priority) */\n configPath?: string;\n /** CLI argument overrides */\n overrides?: Partial<Config>;\n}\n\n/**\n * Load and parse a TOML config file\n */\nfunction loadTOMLFile(path: string): PartialConfig | null {\n if (!existsSync(path)) {\n return null;\n }\n\n try {\n const content = readFileSync(path, \"utf-8\");\n const parsed = parseTOML(content);\n\n // Validate with partial schema\n const validated = partialConfigSchema.parse(parsed);\n return validated;\n } catch (error) {\n throw new Error(\n `Failed to load config from ${path}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Merge CLI config with nested interactive section\n */\nfunction mergeCliConfig(\n base: DeepPartialConfig[\"cli\"],\n override: NonNullable<DeepPartialConfig[\"cli\"]>\n): NonNullable<DeepPartialConfig[\"cli\"]> {\n const { tui: overrideTui, ...overrideCliRest } = override;\n const { tui: baseTui, ...baseCliRest } = base ?? {};\n const mergedTui = overrideTui !== undefined ? { ...baseTui, ...overrideTui } : baseTui;\n return {\n ...baseCliRest,\n ...overrideCliRest,\n ...(mergedTui !== undefined ? { tui: mergedTui } : {}),\n };\n}\n\n/**\n * Merge fulltext config with nested sources section\n */\nfunction mergeFulltextConfig(\n base: DeepPartialConfig[\"fulltext\"],\n override: NonNullable<DeepPartialConfig[\"fulltext\"]>\n): NonNullable<DeepPartialConfig[\"fulltext\"]> {\n const { sources: overrideSources, ...overrideRest } = override;\n const { sources: baseSources, ...baseRest } = base ?? {};\n const mergedSources =\n overrideSources !== undefined ? { ...baseSources, ...overrideSources } : baseSources;\n return {\n ...baseRest,\n ...overrideRest,\n ...(mergedSources !== undefined ? { sources: mergedSources } : {}),\n };\n}\n\nconst flatSectionKeys = [\n \"backup\",\n \"watch\",\n \"server\",\n \"citation\",\n \"pubmed\",\n \"attachments\",\n \"mcp\",\n] as const;\n\n/**\n * Apply a single override to the result config\n */\nfunction applyOverride(result: DeepPartialConfig, override: DeepPartialConfig): void {\n // Merge top-level primitive fields\n if (override.library !== undefined) {\n result.library = override.library;\n }\n if (override.logLevel !== undefined) {\n result.logLevel = override.logLevel;\n }\n\n // Merge flat section configs\n for (const key of flatSectionKeys) {\n if (override[key] !== undefined) {\n result[key] = {\n ...result[key],\n ...override[key],\n };\n }\n }\n\n // Merge nested section configs\n if (override.fulltext !== undefined) {\n result.fulltext = mergeFulltextConfig(result.fulltext, override.fulltext);\n }\n if (override.cli !== undefined) {\n result.cli = mergeCliConfig(result.cli, override.cli);\n }\n}\n\n/**\n * Merge partial configurations\n * Later configs override earlier ones\n */\nfunction mergeConfigs(\n base: DeepPartialConfig,\n ...overrides: (DeepPartialConfig | null | undefined)[]\n): DeepPartialConfig {\n const result: DeepPartialConfig = { ...base };\n\n for (const override of overrides) {\n if (!override) continue;\n applyOverride(result, override);\n }\n\n return result;\n}\n\n/**\n * Fill missing fields with defaults\n */\nfunction fillDefaults(partial: DeepPartialConfig): Config {\n const envLibrary = process.env.REFERENCE_MANAGER_LIBRARY;\n const library = envLibrary ?? partial.library ?? defaultConfig.library;\n return {\n library: expandTilde(library),\n logLevel: partial.logLevel ?? defaultConfig.logLevel,\n backup: {\n maxGenerations: partial.backup?.maxGenerations ?? defaultConfig.backup.maxGenerations,\n maxAgeDays: partial.backup?.maxAgeDays ?? defaultConfig.backup.maxAgeDays,\n directory: partial.backup?.directory ?? defaultConfig.backup.directory,\n },\n watch: {\n debounceMs: partial.watch?.debounceMs ?? defaultConfig.watch.debounceMs,\n pollIntervalMs: partial.watch?.pollIntervalMs ?? defaultConfig.watch.pollIntervalMs,\n retryIntervalMs: partial.watch?.retryIntervalMs ?? defaultConfig.watch.retryIntervalMs,\n maxRetries: partial.watch?.maxRetries ?? defaultConfig.watch.maxRetries,\n },\n server: {\n autoStart: partial.server?.autoStart ?? defaultConfig.server.autoStart,\n autoStopMinutes: partial.server?.autoStopMinutes ?? defaultConfig.server.autoStopMinutes,\n },\n citation: fillCitationDefaults(partial.citation),\n pubmed: fillPubmedDefaults(partial.pubmed),\n fulltext: fillFulltextDefaults(partial.fulltext),\n attachments: fillAttachmentsDefaults(partial.attachments),\n cli: fillCliDefaults(partial.cli),\n mcp: fillMcpDefaults(partial.mcp),\n };\n}\n\n/**\n * Fill citation config with defaults\n */\nfunction fillCitationDefaults(partial: DeepPartialConfig[\"citation\"]): Config[\"citation\"] {\n return {\n defaultStyle: partial?.defaultStyle ?? defaultConfig.citation.defaultStyle,\n cslDirectory: partial?.cslDirectory ?? defaultConfig.citation.cslDirectory,\n defaultLocale: partial?.defaultLocale ?? defaultConfig.citation.defaultLocale,\n defaultFormat: partial?.defaultFormat ?? defaultConfig.citation.defaultFormat,\n defaultKeyFormat: partial?.defaultKeyFormat ?? defaultConfig.citation.defaultKeyFormat,\n };\n}\n\n/**\n * Fill pubmed config with defaults\n * Environment variables take priority over config file values\n */\nfunction fillPubmedDefaults(partial: DeepPartialConfig[\"pubmed\"]): Config[\"pubmed\"] {\n // Environment variables take priority\n const email = process.env.PUBMED_EMAIL ?? partial?.email ?? defaultConfig.pubmed.email;\n const apiKey = process.env.PUBMED_API_KEY ?? partial?.apiKey ?? defaultConfig.pubmed.apiKey;\n\n return {\n email,\n apiKey,\n };\n}\n\n/**\n * Fill fulltext config with defaults\n * Environment variables take priority over config file values\n */\nfunction fillFulltextDefaults(partial: DeepPartialConfig[\"fulltext\"]): Config[\"fulltext\"] {\n // Environment variables take priority\n const unpaywallEmail =\n process.env.UNPAYWALL_EMAIL ??\n partial?.sources?.unpaywallEmail ??\n defaultConfig.fulltext.sources.unpaywallEmail;\n const coreApiKey =\n process.env.CORE_API_KEY ??\n partial?.sources?.coreApiKey ??\n defaultConfig.fulltext.sources.coreApiKey;\n const ncbiEmail =\n process.env.NCBI_EMAIL ??\n partial?.sources?.ncbiEmail ??\n defaultConfig.fulltext.sources.ncbiEmail;\n const ncbiTool =\n process.env.NCBI_TOOL ?? partial?.sources?.ncbiTool ?? defaultConfig.fulltext.sources.ncbiTool;\n\n const envPreferredType = process.env.REFERENCE_MANAGER_FULLTEXT_PREFERRED_TYPE;\n if (\n envPreferredType !== undefined &&\n envPreferredType !== \"pdf\" &&\n envPreferredType !== \"markdown\"\n ) {\n throw new Error(\n `Invalid value for REFERENCE_MANAGER_FULLTEXT_PREFERRED_TYPE: \"${envPreferredType}\". Must be \"pdf\" or \"markdown\".`\n );\n }\n const preferredType =\n envPreferredType ?? partial?.preferredType ?? defaultConfig.fulltext.preferredType;\n\n return {\n preferSources: partial?.preferSources ?? defaultConfig.fulltext.preferSources,\n preferredType,\n autoFetchOnAdd: partial?.autoFetchOnAdd ?? defaultConfig.fulltext.autoFetchOnAdd,\n sources: {\n unpaywallEmail,\n coreApiKey,\n ncbiEmail,\n ncbiTool,\n },\n };\n}\n\n/**\n * Expand ~ to home directory\n */\nfunction expandTilde(path: string): string {\n if (path.startsWith(\"~/\")) {\n return join(homedir(), path.slice(2));\n }\n return path;\n}\n\n/**\n * Fill attachments config with defaults\n *\n * Priority:\n * 1. Environment variable REFERENCE_MANAGER_ATTACHMENTS_DIR\n * 2. Config file setting\n * 3. Default value\n */\nfunction fillAttachmentsDefaults(partial: DeepPartialConfig[\"attachments\"]): Config[\"attachments\"] {\n const envDir = process.env.REFERENCE_MANAGER_ATTACHMENTS_DIR;\n const directory = envDir ?? partial?.directory ?? defaultConfig.attachments.directory;\n return {\n directory: expandTilde(directory),\n };\n}\n\n/**\n * Fill CLI config with defaults\n *\n * Priority:\n * 1. Environment variable REFERENCE_MANAGER_CLI_DEFAULT_LIMIT\n * 2. Config file setting\n * 3. Default value\n */\nfunction fillCliDefaults(partial: DeepPartialConfig[\"cli\"]): Config[\"cli\"] {\n const envLimit = process.env.REFERENCE_MANAGER_CLI_DEFAULT_LIMIT;\n const defaultLimit =\n envLimit !== undefined\n ? Number(envLimit)\n : (partial?.defaultLimit ?? defaultConfig.cli.defaultLimit);\n const envClipboard = process.env.REFERENCE_MANAGER_CLIPBOARD_AUTO_COPY;\n const clipboardAutoCopy =\n envClipboard !== undefined\n ? envClipboard === \"1\" || envClipboard === \"true\"\n : (partial?.tui?.clipboardAutoCopy ?? defaultConfig.cli.tui.clipboardAutoCopy);\n return {\n defaultLimit,\n defaultSort: partial?.defaultSort ?? defaultConfig.cli.defaultSort,\n defaultOrder: partial?.defaultOrder ?? defaultConfig.cli.defaultOrder,\n tui: {\n limit: partial?.tui?.limit ?? defaultConfig.cli.tui.limit,\n debounceMs: partial?.tui?.debounceMs ?? defaultConfig.cli.tui.debounceMs,\n clipboardAutoCopy,\n },\n edit: {\n defaultFormat: partial?.edit?.defaultFormat ?? defaultConfig.cli.edit.defaultFormat,\n },\n };\n}\n\n/**\n * Fill MCP config with defaults\n *\n * Priority:\n * 1. Environment variable REFERENCE_MANAGER_MCP_DEFAULT_LIMIT\n * 2. Config file setting\n * 3. Default value\n */\nfunction fillMcpDefaults(partial: DeepPartialConfig[\"mcp\"]): Config[\"mcp\"] {\n const envLimit = process.env.REFERENCE_MANAGER_MCP_DEFAULT_LIMIT;\n const defaultLimit =\n envLimit !== undefined\n ? Number(envLimit)\n : (partial?.defaultLimit ?? defaultConfig.mcp.defaultLimit);\n return {\n defaultLimit,\n };\n}\n\n/**\n * Load configuration from multiple sources\n *\n * Priority (highest to lowest):\n * 1. CLI argument overrides\n * 2. CLI --config file (configPath)\n * 3. Current directory config (.reference-manager.config.toml)\n * 4. Environment variable (REFERENCE_MANAGER_CONFIG)\n * 5. User config (~/.reference-manager/config.toml)\n * 6. Default values\n */\nexport function loadConfig(options: LoadConfigOptions = {}): Config {\n const cwd = options.cwd ?? process.cwd();\n const userConfigPath = options.userConfigPath ?? getDefaultUserConfigPath();\n\n // If configPath is specified, it must exist (explicit user intent)\n if (options.configPath && !existsSync(options.configPath)) {\n throw new Error(`Config file not found: ${options.configPath}`);\n }\n\n // 1. Load user config (lowest priority)\n const userConfig = loadTOMLFile(userConfigPath);\n\n // 2. Load environment variable config\n const envConfigPath = process.env.REFERENCE_MANAGER_CONFIG;\n const envConfig = envConfigPath ? loadTOMLFile(envConfigPath) : null;\n\n // 3. Load current directory config\n const currentConfigPath = join(cwd, getDefaultCurrentDirConfigFilename());\n const currentConfig = loadTOMLFile(currentConfigPath);\n\n // 4. Load CLI --config file (highest file priority)\n const cliConfig = options.configPath ? loadTOMLFile(options.configPath) : null;\n\n // Normalize snake_case to camelCase\n const normalizedUser = userConfig ? normalizePartialConfig(userConfig) : null;\n const normalizedEnv = envConfig ? normalizePartialConfig(envConfig) : null;\n const normalizedCurrent = currentConfig ? normalizePartialConfig(currentConfig) : null;\n const normalizedCli = cliConfig ? normalizePartialConfig(cliConfig) : null;\n\n // Merge configs (priority: cli > current > env > user > defaults)\n const merged = mergeConfigs(\n {},\n normalizedUser,\n normalizedEnv,\n normalizedCurrent,\n normalizedCli,\n options.overrides\n );\n\n // Fill missing fields with defaults\n const config = fillDefaults(merged);\n\n // Validate final config\n try {\n return configSchema.parse(config);\n } catch (error) {\n throw new Error(\n `Invalid configuration: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n"],"names":["fs","os","process","path","parseTOML","homedir"],"mappings":"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","x_google_ignoreList":[2]}
@@ -1,8 +1,8 @@
1
1
  import { t as tokenize, s as search } from "./file-watcher-Dyns9dxx.js";
2
2
  import { render, useApp } from "ink";
3
3
  import { createElement } from "react";
4
- import { r as restoreStdinAfterInk } from "./index-C5G3Nm7i.js";
5
- import { c as calculateEffectiveLimit, f as formatAuthors, S as SearchableMultiSelect } from "./format-0YxykmO-.js";
4
+ import { r as restoreStdinAfterInk } from "./index-CFwvg1Ju.js";
5
+ import { c as calculateEffectiveLimit, f as formatAuthors, S as SearchableMultiSelect } from "./format-CH02ev3l.js";
6
6
  import { checkTTY } from "./tty-BMyaEOhX.js";
7
7
  function extractYear(item) {
8
8
  const dateParts = item.issued?.["date-parts"];
@@ -199,4 +199,4 @@ export {
199
199
  selectReferenceItemsOrExit,
200
200
  selectReferencesOrExit
201
201
  };
202
- //# sourceMappingURL=reference-select-D0qAyipM.js.map
202
+ //# sourceMappingURL=reference-select-D_Clnq0X.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reference-select-D0qAyipM.js","sources":["../../src/features/interactive/search-prompt.ts","../../src/features/interactive/reference-select.ts"],"sourcesContent":["/**\n * Interactive search prompt using React Ink\n *\n * Provides real-time incremental search with multiple selection support.\n */\n\nimport { render, useApp } from \"ink\";\nimport type React from \"react\";\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 {\n type Choice,\n SearchableMultiSelect,\n type SortOption,\n calculateEffectiveLimit,\n getTerminalHeight,\n getTerminalWidth,\n} from \"./components/index.js\";\nimport { formatAuthors } from \"./format.js\";\n\n/**\n * Configuration for the search prompt\n */\nexport interface SearchPromptConfig {\n /** Maximum number of results to display */\n limit: number;\n /** Debounce delay in milliseconds (not used in Ink version, 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 * Result from the search prompt\n */\nexport interface SearchPromptResult {\n /** Selected references */\n selected: CslItem[];\n /** Whether the prompt was cancelled */\n cancelled: boolean;\n}\n\nexport { calculateEffectiveLimit, getTerminalHeight, getTerminalWidth };\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 * Props for the SearchPromptApp component\n */\ninterface SearchPromptAppProps {\n choices: Choice<CslItem>[];\n filterFn: (query: string, choices: Choice<CslItem>[]) => Choice<CslItem>[];\n visibleCount: number;\n defaultSort: SortOption;\n onSubmit: (selected: Choice<CslItem>[]) => void;\n onCancel: () => void;\n}\n\n/**\n * SearchPromptApp component - wraps SearchableMultiSelect for search prompt\n */\nfunction SearchPromptApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n onSubmit,\n onCancel,\n}: SearchPromptAppProps): React.ReactElement {\n const { exit } = useApp();\n\n const handleSubmit = (selected: Choice<CslItem>[]): void => {\n onSubmit(selected);\n exit();\n };\n\n const handleCancel = (): void => {\n onCancel();\n exit();\n };\n\n return createElement(SearchableMultiSelect<CslItem>, {\n choices,\n filterFn,\n visibleCount,\n onSubmit: handleSubmit,\n onCancel: handleCancel,\n header: \"Search references\",\n placeholder: \"Type to search...\",\n defaultSort,\n });\n}\n\n/**\n * Creates and runs an interactive search prompt\n */\nexport async function runSearchPrompt(\n allReferences: CslItem[],\n searchFn: SearchFunction,\n config: SearchPromptConfig,\n _initialQuery = \"\" // kept for API compatibility, not used in Ink version\n): Promise<SearchPromptResult> {\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 // Create a promise to capture the result\n return new Promise<SearchPromptResult>((resolve) => {\n let result: SearchPromptResult = { selected: [], cancelled: true };\n\n const handleSubmit = (selected: Choice<CslItem>[]): void => {\n result = {\n selected: selected.map((c) => c.value),\n cancelled: false,\n };\n };\n\n const handleCancel = (): void => {\n result = {\n selected: [],\n cancelled: true,\n };\n };\n\n // Render the Ink app\n const { waitUntilExit, clear } = render(\n createElement(SearchPromptApp, {\n choices,\n filterFn,\n visibleCount: effectiveLimit,\n defaultSort: \"updated-desc\",\n onSubmit: handleSubmit,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, clear the screen, then resolve\n waitUntilExit()\n .then(() => {\n clear();\n restoreStdinAfterInk();\n resolve(result);\n })\n .catch(() => {\n clear();\n restoreStdinAfterInk();\n resolve({\n selected: [],\n cancelled: true,\n });\n });\n });\n}\n\n// Export legacy functions for backward compatibility with existing tests\n// These are no longer used by the React Ink implementation\nexport interface AutoCompleteChoice {\n name: string;\n message: string;\n}\n\n/**\n * Creates choices from search results (legacy, for test compatibility)\n */\nexport function createChoices(\n results: SearchResult[],\n _terminalWidth: number\n): AutoCompleteChoice[] {\n return results.map((result, index) => ({\n name: JSON.stringify({ index, item: result.reference }),\n message: `[${index + 1}] ${result.reference.title ?? \"(No title)\"}`,\n }));\n}\n\n/**\n * Parses selected values back to CslItems (legacy, for test compatibility)\n */\nexport function parseSelectedValues(values: string | string[]): CslItem[] {\n const valueArray = Array.isArray(values) ? values : [values];\n const items: CslItem[] = [];\n\n for (const value of valueArray) {\n if (!value) continue;\n try {\n const data = JSON.parse(value) as { index: number; item: CslItem };\n items.push(data.item);\n } catch {\n // Ignore parse errors\n }\n }\n\n return items;\n}\n","/**\n * Shared reference selection utility for interactive ID selection.\n *\n * This module provides a reusable function to select references interactively\n * using the existing search prompt infrastructure.\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport { search } from \"../search/matcher.js\";\nimport { tokenize } from \"../search/tokenizer.js\";\nimport { type SearchPromptConfig, runSearchPrompt } from \"./search-prompt.js\";\nimport { checkTTY } from \"./tty.js\";\n\n/**\n * Options for reference selection\n */\nexport interface ReferenceSelectOptions {\n /** Whether to allow multiple selection (default: true) */\n multiSelect: boolean;\n /** Custom prompt message */\n prompt?: string;\n /** Initial search query */\n initialQuery?: string;\n}\n\n/**\n * Result from reference selection\n */\nexport interface ReferenceSelectResult {\n /** Selected references */\n selected: CslItem[];\n /** Whether the selection was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Run interactive reference selection.\n *\n * Launches an interactive search prompt to select references from the library.\n * Supports both single and multiple selection modes.\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Selection result with selected references\n * @throws TTYError if not running in a TTY environment\n */\nexport async function runReferenceSelect(\n allReferences: CslItem[],\n options: ReferenceSelectOptions,\n config: SearchPromptConfig\n): Promise<ReferenceSelectResult> {\n // Check TTY requirement\n checkTTY();\n\n // Create search function for runSearchPrompt\n const searchFn = (query: string) => {\n const { tokens } = tokenize(query);\n return search(allReferences, tokens);\n };\n\n // Run search prompt\n const searchResult = await runSearchPrompt(\n allReferences,\n searchFn,\n config,\n options.initialQuery ?? \"\"\n );\n\n if (searchResult.cancelled) {\n return { selected: [], cancelled: true };\n }\n\n // For single-select mode, return only the first selected item\n const selected = options.multiSelect ? searchResult.selected : searchResult.selected.slice(0, 1);\n\n return {\n selected,\n cancelled: false,\n };\n}\n\n/**\n * Options for selectReferencesOrExit helper\n */\nexport interface SelectReferencesOrExitOptions {\n /** Whether to allow multiple selection */\n multiSelect: boolean;\n /** Initial search query */\n initialQuery?: string;\n}\n\n/**\n * Select references interactively or exit if cancelled/empty.\n *\n * This is a convenience wrapper around runReferenceSelect that handles\n * common patterns:\n * - Exits with code 0 if library is empty\n * - Exits with code 0 if selection is cancelled\n * - Returns selected identifiers\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Array of selected reference IDs (never empty)\n */\nexport async function selectReferencesOrExit(\n allReferences: CslItem[],\n options: SelectReferencesOrExitOptions,\n config: SearchPromptConfig\n): Promise<string[]> {\n if (allReferences.length === 0) {\n process.stderr.write(\"No references in library.\\n\");\n process.exit(0);\n }\n\n const selectResult = await runReferenceSelect(allReferences, options, config);\n\n if (selectResult.cancelled || selectResult.selected.length === 0) {\n process.exit(0);\n }\n\n return selectResult.selected.map((item) => item.id);\n}\n\n/**\n * Select reference items interactively or exit if cancelled/empty.\n *\n * Similar to selectReferencesOrExit but returns full CslItem objects\n * instead of just identifiers. Useful when the caller needs access to\n * the full reference data (e.g., for confirmation dialogs).\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Array of selected CslItem objects (never empty)\n */\nexport async function selectReferenceItemsOrExit(\n allReferences: CslItem[],\n options: SelectReferencesOrExitOptions,\n config: SearchPromptConfig\n): Promise<CslItem[]> {\n if (allReferences.length === 0) {\n process.stderr.write(\"No references in library.\\n\");\n process.exit(0);\n }\n\n const selectResult = await runReferenceSelect(allReferences, options, config);\n\n if (selectResult.cancelled || selectResult.selected.length === 0) {\n process.exit(0);\n }\n\n return selectResult.selected;\n}\n"],"names":["choices"],"mappings":"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"}
1
+ {"version":3,"file":"reference-select-D_Clnq0X.js","sources":["../../src/features/interactive/search-prompt.ts","../../src/features/interactive/reference-select.ts"],"sourcesContent":["/**\n * Interactive search prompt using React Ink\n *\n * Provides real-time incremental search with multiple selection support.\n */\n\nimport { render, useApp } from \"ink\";\nimport type React from \"react\";\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 {\n type Choice,\n SearchableMultiSelect,\n type SortOption,\n calculateEffectiveLimit,\n getTerminalHeight,\n getTerminalWidth,\n} from \"./components/index.js\";\nimport { formatAuthors } from \"./format.js\";\n\n/**\n * Configuration for the search prompt\n */\nexport interface SearchPromptConfig {\n /** Maximum number of results to display */\n limit: number;\n /** Debounce delay in milliseconds (not used in Ink version, 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 * Result from the search prompt\n */\nexport interface SearchPromptResult {\n /** Selected references */\n selected: CslItem[];\n /** Whether the prompt was cancelled */\n cancelled: boolean;\n}\n\nexport { calculateEffectiveLimit, getTerminalHeight, getTerminalWidth };\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 * Props for the SearchPromptApp component\n */\ninterface SearchPromptAppProps {\n choices: Choice<CslItem>[];\n filterFn: (query: string, choices: Choice<CslItem>[]) => Choice<CslItem>[];\n visibleCount: number;\n defaultSort: SortOption;\n onSubmit: (selected: Choice<CslItem>[]) => void;\n onCancel: () => void;\n}\n\n/**\n * SearchPromptApp component - wraps SearchableMultiSelect for search prompt\n */\nfunction SearchPromptApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n onSubmit,\n onCancel,\n}: SearchPromptAppProps): React.ReactElement {\n const { exit } = useApp();\n\n const handleSubmit = (selected: Choice<CslItem>[]): void => {\n onSubmit(selected);\n exit();\n };\n\n const handleCancel = (): void => {\n onCancel();\n exit();\n };\n\n return createElement(SearchableMultiSelect<CslItem>, {\n choices,\n filterFn,\n visibleCount,\n onSubmit: handleSubmit,\n onCancel: handleCancel,\n header: \"Search references\",\n placeholder: \"Type to search...\",\n defaultSort,\n });\n}\n\n/**\n * Creates and runs an interactive search prompt\n */\nexport async function runSearchPrompt(\n allReferences: CslItem[],\n searchFn: SearchFunction,\n config: SearchPromptConfig,\n _initialQuery = \"\" // kept for API compatibility, not used in Ink version\n): Promise<SearchPromptResult> {\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 // Create a promise to capture the result\n return new Promise<SearchPromptResult>((resolve) => {\n let result: SearchPromptResult = { selected: [], cancelled: true };\n\n const handleSubmit = (selected: Choice<CslItem>[]): void => {\n result = {\n selected: selected.map((c) => c.value),\n cancelled: false,\n };\n };\n\n const handleCancel = (): void => {\n result = {\n selected: [],\n cancelled: true,\n };\n };\n\n // Render the Ink app\n const { waitUntilExit, clear } = render(\n createElement(SearchPromptApp, {\n choices,\n filterFn,\n visibleCount: effectiveLimit,\n defaultSort: \"updated-desc\",\n onSubmit: handleSubmit,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, clear the screen, then resolve\n waitUntilExit()\n .then(() => {\n clear();\n restoreStdinAfterInk();\n resolve(result);\n })\n .catch(() => {\n clear();\n restoreStdinAfterInk();\n resolve({\n selected: [],\n cancelled: true,\n });\n });\n });\n}\n\n// Export legacy functions for backward compatibility with existing tests\n// These are no longer used by the React Ink implementation\nexport interface AutoCompleteChoice {\n name: string;\n message: string;\n}\n\n/**\n * Creates choices from search results (legacy, for test compatibility)\n */\nexport function createChoices(\n results: SearchResult[],\n _terminalWidth: number\n): AutoCompleteChoice[] {\n return results.map((result, index) => ({\n name: JSON.stringify({ index, item: result.reference }),\n message: `[${index + 1}] ${result.reference.title ?? \"(No title)\"}`,\n }));\n}\n\n/**\n * Parses selected values back to CslItems (legacy, for test compatibility)\n */\nexport function parseSelectedValues(values: string | string[]): CslItem[] {\n const valueArray = Array.isArray(values) ? values : [values];\n const items: CslItem[] = [];\n\n for (const value of valueArray) {\n if (!value) continue;\n try {\n const data = JSON.parse(value) as { index: number; item: CslItem };\n items.push(data.item);\n } catch {\n // Ignore parse errors\n }\n }\n\n return items;\n}\n","/**\n * Shared reference selection utility for interactive ID selection.\n *\n * This module provides a reusable function to select references interactively\n * using the existing search prompt infrastructure.\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport { search } from \"../search/matcher.js\";\nimport { tokenize } from \"../search/tokenizer.js\";\nimport { type SearchPromptConfig, runSearchPrompt } from \"./search-prompt.js\";\nimport { checkTTY } from \"./tty.js\";\n\n/**\n * Options for reference selection\n */\nexport interface ReferenceSelectOptions {\n /** Whether to allow multiple selection (default: true) */\n multiSelect: boolean;\n /** Custom prompt message */\n prompt?: string;\n /** Initial search query */\n initialQuery?: string;\n}\n\n/**\n * Result from reference selection\n */\nexport interface ReferenceSelectResult {\n /** Selected references */\n selected: CslItem[];\n /** Whether the selection was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Run interactive reference selection.\n *\n * Launches an interactive search prompt to select references from the library.\n * Supports both single and multiple selection modes.\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Selection result with selected references\n * @throws TTYError if not running in a TTY environment\n */\nexport async function runReferenceSelect(\n allReferences: CslItem[],\n options: ReferenceSelectOptions,\n config: SearchPromptConfig\n): Promise<ReferenceSelectResult> {\n // Check TTY requirement\n checkTTY();\n\n // Create search function for runSearchPrompt\n const searchFn = (query: string) => {\n const { tokens } = tokenize(query);\n return search(allReferences, tokens);\n };\n\n // Run search prompt\n const searchResult = await runSearchPrompt(\n allReferences,\n searchFn,\n config,\n options.initialQuery ?? \"\"\n );\n\n if (searchResult.cancelled) {\n return { selected: [], cancelled: true };\n }\n\n // For single-select mode, return only the first selected item\n const selected = options.multiSelect ? searchResult.selected : searchResult.selected.slice(0, 1);\n\n return {\n selected,\n cancelled: false,\n };\n}\n\n/**\n * Options for selectReferencesOrExit helper\n */\nexport interface SelectReferencesOrExitOptions {\n /** Whether to allow multiple selection */\n multiSelect: boolean;\n /** Initial search query */\n initialQuery?: string;\n}\n\n/**\n * Select references interactively or exit if cancelled/empty.\n *\n * This is a convenience wrapper around runReferenceSelect that handles\n * common patterns:\n * - Exits with code 0 if library is empty\n * - Exits with code 0 if selection is cancelled\n * - Returns selected identifiers\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Array of selected reference IDs (never empty)\n */\nexport async function selectReferencesOrExit(\n allReferences: CslItem[],\n options: SelectReferencesOrExitOptions,\n config: SearchPromptConfig\n): Promise<string[]> {\n if (allReferences.length === 0) {\n process.stderr.write(\"No references in library.\\n\");\n process.exit(0);\n }\n\n const selectResult = await runReferenceSelect(allReferences, options, config);\n\n if (selectResult.cancelled || selectResult.selected.length === 0) {\n process.exit(0);\n }\n\n return selectResult.selected.map((item) => item.id);\n}\n\n/**\n * Select reference items interactively or exit if cancelled/empty.\n *\n * Similar to selectReferencesOrExit but returns full CslItem objects\n * instead of just identifiers. Useful when the caller needs access to\n * the full reference data (e.g., for confirmation dialogs).\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Array of selected CslItem objects (never empty)\n */\nexport async function selectReferenceItemsOrExit(\n allReferences: CslItem[],\n options: SelectReferencesOrExitOptions,\n config: SearchPromptConfig\n): Promise<CslItem[]> {\n if (allReferences.length === 0) {\n process.stderr.write(\"No references in library.\\n\");\n process.exit(0);\n }\n\n const selectResult = await runReferenceSelect(allReferences, options, config);\n\n if (selectResult.cancelled || selectResult.selected.length === 0) {\n process.exit(0);\n }\n\n return selectResult.selected;\n}\n"],"names":["choices"],"mappings":"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"}
@@ -2,8 +2,8 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import "ink";
4
4
  import "react";
5
- import { B as BUILTIN_STYLES } from "./index-sH2o68X1.js";
6
- import "./index-C5G3Nm7i.js";
5
+ import { B as BUILTIN_STYLES } from "./index-7R-uEYOu.js";
6
+ import "./index-CFwvg1Ju.js";
7
7
  function expandTilde(filePath) {
8
8
  if (filePath.startsWith("~/")) {
9
9
  const home = process.env.HOME || process.env.USERPROFILE || "";
@@ -58,4 +58,4 @@ export {
58
58
  buildStyleChoices,
59
59
  listCustomStyles
60
60
  };
61
- //# sourceMappingURL=style-select-CcVzhqgU.js.map
61
+ //# sourceMappingURL=style-select-VJryW_Ms.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"style-select-CcVzhqgU.js","sources":["../../src/features/interactive/style-select.ts"],"sourcesContent":["/**\n * Style selection prompt for citation style selection.\n *\n * Lists built-in styles and custom styles from csl_directory,\n * with the default style shown first.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { render, useApp } from \"ink\";\nimport { createElement } from \"react\";\nimport type React from \"react\";\nimport { BUILTIN_STYLES } from \"../../config/csl-styles.js\";\nimport { restoreStdinAfterInk } from \"./alternate-screen.js\";\nimport { Select, type SelectOption } from \"./components/index.js\";\n\n/**\n * Options for style selection\n */\nexport interface StyleSelectOptions {\n /** Directory or directories containing custom CSL files */\n cslDirectory?: string | string[];\n /** Default style to show first */\n defaultStyle?: string;\n}\n\n/**\n * Result from style selection\n */\nexport interface StyleSelectResult {\n /** Selected style name */\n style?: string;\n /** Whether the selection was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Expand tilde (~) in path to home directory\n */\nfunction expandTilde(filePath: string): string {\n if (filePath.startsWith(\"~/\")) {\n const home = process.env.HOME || process.env.USERPROFILE || \"\";\n return path.join(home, filePath.slice(2));\n }\n return filePath;\n}\n\n/**\n * List custom CSL style names from the specified directory/directories.\n *\n * @param cslDirectory - Directory or directories to search for CSL files\n * @returns Array of style names (without .csl extension)\n */\nexport function listCustomStyles(cslDirectory: string | string[] | undefined): string[] {\n if (!cslDirectory) {\n return [];\n }\n\n const directories = Array.isArray(cslDirectory) ? cslDirectory : [cslDirectory];\n const styles = new Set<string>();\n\n for (const dir of directories) {\n const expandedDir = expandTilde(dir);\n\n if (!fs.existsSync(expandedDir)) {\n continue;\n }\n\n try {\n const files = fs.readdirSync(expandedDir);\n for (const file of files) {\n if (file.endsWith(\".csl\")) {\n styles.add(file.slice(0, -4)); // Remove .csl extension\n }\n }\n } catch {\n // Ignore read errors\n }\n }\n\n return Array.from(styles).sort();\n}\n\n/**\n * Build style choices for the selection prompt.\n *\n * @param customStyles - Custom style names from csl_directory\n * @param defaultStyle - Default style to show first\n * @returns Array of SelectOption for Ink Select prompt\n */\nexport function buildStyleChoices(\n customStyles: string[],\n defaultStyle = \"apa\"\n): SelectOption<string>[] {\n const choices: SelectOption<string>[] = [];\n const addedStyles = new Set<string>();\n\n // Helper to add a style choice\n const addChoice = (styleName: string, isDefault: boolean): void => {\n if (addedStyles.has(styleName)) return;\n addedStyles.add(styleName);\n\n choices.push({\n label: isDefault ? `${styleName} (default)` : styleName,\n value: styleName,\n });\n };\n\n // Add default style first\n addChoice(defaultStyle, true);\n\n // Add built-in styles\n for (const style of BUILTIN_STYLES) {\n addChoice(style, false);\n }\n\n // Add custom styles (excluding any that match built-in names)\n for (const style of customStyles) {\n addChoice(style, false);\n }\n\n return choices;\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 const { exit } = useApp();\n\n const handleSelect = (value: string): void => {\n onSelect(value);\n exit();\n };\n\n const handleCancel = (): void => {\n onCancel();\n exit();\n };\n\n return createElement(Select<string>, {\n options,\n message: \"Select citation style:\",\n onSelect: handleSelect,\n onCancel: handleCancel,\n });\n}\n\n/**\n * Run the style selection prompt.\n *\n * @param options - Style selection options\n * @returns Selection result with style name\n */\nexport async function runStyleSelect(options: StyleSelectOptions): Promise<StyleSelectResult> {\n // List custom styles from csl_directory\n const customStyles = listCustomStyles(options.cslDirectory);\n\n // Build choices with default first\n const choices = buildStyleChoices(customStyles, options.defaultStyle);\n\n // Create a promise to capture the result\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, clear } = render(\n createElement(StyleSelectApp, {\n options: choices,\n onSelect: handleSelect,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, clear the screen, then resolve\n waitUntilExit()\n .then(() => {\n clear();\n restoreStdinAfterInk();\n resolve(result);\n })\n .catch(() => {\n clear();\n restoreStdinAfterInk();\n resolve({\n cancelled: true,\n });\n });\n });\n}\n"],"names":[],"mappings":";;;;;;AAuCA,SAAS,YAAY,UAA0B;AAC7C,MAAI,SAAS,WAAW,IAAI,GAAG;AAC7B,UAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,WAAO,KAAK,KAAK,MAAM,SAAS,MAAM,CAAC,CAAC;AAAA,EAC1C;AACA,SAAO;AACT;AAQO,SAAS,iBAAiB,cAAuD;AACtF,MAAI,CAAC,cAAc;AACjB,WAAO,CAAA;AAAA,EACT;AAEA,QAAM,cAAc,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAC,YAAY;AAC9E,QAAM,6BAAa,IAAA;AAEnB,aAAW,OAAO,aAAa;AAC7B,UAAM,cAAc,YAAY,GAAG;AAEnC,QAAI,CAAC,GAAG,WAAW,WAAW,GAAG;AAC/B;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAQ,GAAG,YAAY,WAAW;AACxC,iBAAW,QAAQ,OAAO;AACxB,YAAI,KAAK,SAAS,MAAM,GAAG;AACzB,iBAAO,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,MAAM,EAAE,KAAA;AAC5B;AASO,SAAS,kBACd,cACA,eAAe,OACS;AACxB,QAAM,UAAkC,CAAA;AACxC,QAAM,kCAAkB,IAAA;AAGxB,QAAM,YAAY,CAAC,WAAmB,cAA6B;AACjE,QAAI,YAAY,IAAI,SAAS,EAAG;AAChC,gBAAY,IAAI,SAAS;AAEzB,YAAQ,KAAK;AAAA,MACX,OAAO,YAAY,GAAG,SAAS,eAAe;AAAA,MAC9C,OAAO;AAAA,IAAA,CACR;AAAA,EACH;AAGA,YAAU,cAAc,IAAI;AAG5B,aAAW,SAAS,gBAAgB;AAClC,cAAU,OAAO,KAAK;AAAA,EACxB;AAGA,aAAW,SAAS,cAAc;AAChC,cAAU,OAAO,KAAK;AAAA,EACxB;AAEA,SAAO;AACT;"}
1
+ {"version":3,"file":"style-select-VJryW_Ms.js","sources":["../../src/features/interactive/style-select.ts"],"sourcesContent":["/**\n * Style selection prompt for citation style selection.\n *\n * Lists built-in styles and custom styles from csl_directory,\n * with the default style shown first.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { render, useApp } from \"ink\";\nimport { createElement } from \"react\";\nimport type React from \"react\";\nimport { BUILTIN_STYLES } from \"../../config/csl-styles.js\";\nimport { restoreStdinAfterInk } from \"./alternate-screen.js\";\nimport { Select, type SelectOption } from \"./components/index.js\";\n\n/**\n * Options for style selection\n */\nexport interface StyleSelectOptions {\n /** Directory or directories containing custom CSL files */\n cslDirectory?: string | string[];\n /** Default style to show first */\n defaultStyle?: string;\n}\n\n/**\n * Result from style selection\n */\nexport interface StyleSelectResult {\n /** Selected style name */\n style?: string;\n /** Whether the selection was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Expand tilde (~) in path to home directory\n */\nfunction expandTilde(filePath: string): string {\n if (filePath.startsWith(\"~/\")) {\n const home = process.env.HOME || process.env.USERPROFILE || \"\";\n return path.join(home, filePath.slice(2));\n }\n return filePath;\n}\n\n/**\n * List custom CSL style names from the specified directory/directories.\n *\n * @param cslDirectory - Directory or directories to search for CSL files\n * @returns Array of style names (without .csl extension)\n */\nexport function listCustomStyles(cslDirectory: string | string[] | undefined): string[] {\n if (!cslDirectory) {\n return [];\n }\n\n const directories = Array.isArray(cslDirectory) ? cslDirectory : [cslDirectory];\n const styles = new Set<string>();\n\n for (const dir of directories) {\n const expandedDir = expandTilde(dir);\n\n if (!fs.existsSync(expandedDir)) {\n continue;\n }\n\n try {\n const files = fs.readdirSync(expandedDir);\n for (const file of files) {\n if (file.endsWith(\".csl\")) {\n styles.add(file.slice(0, -4)); // Remove .csl extension\n }\n }\n } catch {\n // Ignore read errors\n }\n }\n\n return Array.from(styles).sort();\n}\n\n/**\n * Build style choices for the selection prompt.\n *\n * @param customStyles - Custom style names from csl_directory\n * @param defaultStyle - Default style to show first\n * @returns Array of SelectOption for Ink Select prompt\n */\nexport function buildStyleChoices(\n customStyles: string[],\n defaultStyle = \"apa\"\n): SelectOption<string>[] {\n const choices: SelectOption<string>[] = [];\n const addedStyles = new Set<string>();\n\n // Helper to add a style choice\n const addChoice = (styleName: string, isDefault: boolean): void => {\n if (addedStyles.has(styleName)) return;\n addedStyles.add(styleName);\n\n choices.push({\n label: isDefault ? `${styleName} (default)` : styleName,\n value: styleName,\n });\n };\n\n // Add default style first\n addChoice(defaultStyle, true);\n\n // Add built-in styles\n for (const style of BUILTIN_STYLES) {\n addChoice(style, false);\n }\n\n // Add custom styles (excluding any that match built-in names)\n for (const style of customStyles) {\n addChoice(style, false);\n }\n\n return choices;\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 const { exit } = useApp();\n\n const handleSelect = (value: string): void => {\n onSelect(value);\n exit();\n };\n\n const handleCancel = (): void => {\n onCancel();\n exit();\n };\n\n return createElement(Select<string>, {\n options,\n message: \"Select citation style:\",\n onSelect: handleSelect,\n onCancel: handleCancel,\n });\n}\n\n/**\n * Run the style selection prompt.\n *\n * @param options - Style selection options\n * @returns Selection result with style name\n */\nexport async function runStyleSelect(options: StyleSelectOptions): Promise<StyleSelectResult> {\n // List custom styles from csl_directory\n const customStyles = listCustomStyles(options.cslDirectory);\n\n // Build choices with default first\n const choices = buildStyleChoices(customStyles, options.defaultStyle);\n\n // Create a promise to capture the result\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, clear } = render(\n createElement(StyleSelectApp, {\n options: choices,\n onSelect: handleSelect,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, clear the screen, then resolve\n waitUntilExit()\n .then(() => {\n clear();\n restoreStdinAfterInk();\n resolve(result);\n })\n .catch(() => {\n clear();\n restoreStdinAfterInk();\n resolve({\n cancelled: true,\n });\n });\n });\n}\n"],"names":[],"mappings":";;;;;;AAuCA,SAAS,YAAY,UAA0B;AAC7C,MAAI,SAAS,WAAW,IAAI,GAAG;AAC7B,UAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,WAAO,KAAK,KAAK,MAAM,SAAS,MAAM,CAAC,CAAC;AAAA,EAC1C;AACA,SAAO;AACT;AAQO,SAAS,iBAAiB,cAAuD;AACtF,MAAI,CAAC,cAAc;AACjB,WAAO,CAAA;AAAA,EACT;AAEA,QAAM,cAAc,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAC,YAAY;AAC9E,QAAM,6BAAa,IAAA;AAEnB,aAAW,OAAO,aAAa;AAC7B,UAAM,cAAc,YAAY,GAAG;AAEnC,QAAI,CAAC,GAAG,WAAW,WAAW,GAAG;AAC/B;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAQ,GAAG,YAAY,WAAW;AACxC,iBAAW,QAAQ,OAAO;AACxB,YAAI,KAAK,SAAS,MAAM,GAAG;AACzB,iBAAO,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,MAAM,EAAE,KAAA;AAC5B;AASO,SAAS,kBACd,cACA,eAAe,OACS;AACxB,QAAM,UAAkC,CAAA;AACxC,QAAM,kCAAkB,IAAA;AAGxB,QAAM,YAAY,CAAC,WAAmB,cAA6B;AACjE,QAAI,YAAY,IAAI,SAAS,EAAG;AAChC,gBAAY,IAAI,SAAS;AAEzB,YAAQ,KAAK;AAAA,MACX,OAAO,YAAY,GAAG,SAAS,eAAe;AAAA,MAC9C,OAAO;AAAA,IAAA,CACR;AAAA,EACH;AAGA,YAAU,cAAc,IAAI;AAG5B,aAAW,SAAS,gBAAgB;AAClC,cAAU,OAAO,KAAK;AAAA,EACxB;AAGA,aAAW,SAAS,cAAc;AAChC,cAAU,OAAO,KAAK;AAAA,EACxB;AAEA,SAAO;AACT;"}
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "commander";
2
- import { c, m } from "./chunks/index-C5G3Nm7i.js";
2
+ import { c, m } from "./chunks/index-CFwvg1Ju.js";
3
3
  import "./chunks/file-watcher-Dyns9dxx.js";
4
4
  export {
5
5
  c as createProgram,
@@ -404,6 +404,8 @@ export declare const partialConfigSchema: z.ZodObject<{
404
404
  limit: z.ZodOptional<z.ZodNumber>;
405
405
  debounceMs: z.ZodOptional<z.ZodNumber>;
406
406
  debounce_ms: z.ZodOptional<z.ZodNumber>;
407
+ clipboardAutoCopy: z.ZodOptional<z.ZodBoolean>;
408
+ clipboard_auto_copy: z.ZodOptional<z.ZodBoolean>;
407
409
  }, z.core.$strip>>;
408
410
  edit: z.ZodOptional<z.ZodObject<{
409
411
  defaultFormat: z.ZodOptional<z.ZodEnum<{