@a9i5k4/dsh-literature 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -129,12 +129,17 @@ var init_config = __esm({
129
129
  /** Panel geometry defaults (the client keeps live geometry in localStorage). */
130
130
  panelWidth: 380,
131
131
  readerFit: "fit-width",
132
+ /** Reader night mode: 'auto' follows the host dark theme, 'on'/'off' force it. */
133
+ nightMode: "auto",
132
134
  /** Entry placement: 'auto' (better-sidebar tab when available, else footer),
133
135
  * 'footer' (always the sidebar footer button), 'hide' (settings page only). */
134
136
  entryMode: "auto",
135
137
  /** Watched folder: new PDFs here are auto-imported into the built-in library. */
136
138
  importDir: "",
137
139
  watchImport: false,
140
+ /** UI language: 'auto' follows the host runtime, 'zh' / 'en' pin it. Stored
141
+ * with the rest of the config so the choice survives a host switch. */
142
+ uiLanguage: "auto",
138
143
  /** User-configured custom download sources (mirrors / institutional proxies).
139
144
  * Each: { id, label, urlTemplate, headers?, enabled, order }. URL templates
140
145
  * may use {doi} {arxiv} {isbn} {title} {url}. These are the user's own
@@ -746,7 +751,7 @@ var UA, FetchFailure;
746
751
  var init_net = __esm({
747
752
  "src/node/net.js"() {
748
753
  init_log();
749
- UA = "dsh-literature/0.2.7 (+https://github.com/deepseek-ai/deepseek-harness)";
754
+ UA = "dsh-literature/0.3.3 (+https://github.com/deepseek-ai/deepseek-harness)";
750
755
  FetchFailure = class extends Error {
751
756
  constructor(message, { code = "network", status = 0, retryable = true, cause } = {}) {
752
757
  super(message, { cause });
@@ -1758,10 +1763,261 @@ var init_naming = __esm({
1758
1763
  }
1759
1764
  });
1760
1765
 
1766
+ // src/node/cite.js
1767
+ var cite_exports = {};
1768
+ __export(cite_exports, {
1769
+ bibtex: () => bibtex,
1770
+ cite: () => cite,
1771
+ citeDetailed: () => citeDetailed,
1772
+ listStyles: () => listStyles,
1773
+ segmentsToHtml: () => segmentsToHtml,
1774
+ stripItalic: () => stripItalic,
1775
+ toSegments: () => toSegments
1776
+ });
1777
+ function authorsList(record, { max = 99, ellipsis = ", et al." } = {}) {
1778
+ const list = record.authors ?? [];
1779
+ if (!list.length) return "";
1780
+ const names = list.map((a) => [a.lastName, a.firstName].filter(Boolean).join(", "));
1781
+ if (names.length === 1) return names[0];
1782
+ if (names.length <= max) return names.slice(0, -1).join(", ") + ", & " + names[names.length - 1];
1783
+ return names.slice(0, max).join(", ") + ellipsis;
1784
+ }
1785
+ function authorsShort(record) {
1786
+ const list = record.authors ?? [];
1787
+ if (!list.length) return "";
1788
+ if (list.length === 1) return list[0].lastName;
1789
+ if (list.length === 2) return `${list[0].lastName} & ${list[1].lastName}`;
1790
+ return `${list[0].lastName} et al.`;
1791
+ }
1792
+ function yearStr(record) {
1793
+ return record.year ? String(record.year) : "n.d.";
1794
+ }
1795
+ function pagesStr(record) {
1796
+ const p = record.pages ?? "";
1797
+ return p ? p.replace(/-/g, "\u2013") : "";
1798
+ }
1799
+ function doiUrl(record) {
1800
+ return record.doi ? `https://doi.org/${record.doi}` : record.url ?? "";
1801
+ }
1802
+ function apa(record) {
1803
+ const authors2 = authorsList(record, { max: 20 });
1804
+ const who = authors2 ? `${authors2}. ` : "";
1805
+ const year = `(${yearStr(record)}). `;
1806
+ const title = (record.title ?? "Untitled").trim();
1807
+ if (record.itemType === "journalArticle" || record.itemType === "preprint") {
1808
+ const journal = record.container ? `*${record.container}*, ` : "";
1809
+ const vol = record.volume ? `*${record.volume}*` : "";
1810
+ const issue = record.issue ? `(${record.issue})` : "";
1811
+ const pages = pagesStr(record) ? `, ${pagesStr(record)}` : "";
1812
+ const volPart = vol || issue || pages ? `${vol}${issue}${pages}. ` : "";
1813
+ const url = doiUrl(record) ? `${doiUrl(record)}` : "";
1814
+ return `${who}${year}${title}. ${journal}${volPart}${url}`.trim();
1815
+ }
1816
+ if (record.itemType === "bookSection") {
1817
+ const book = record.container ? ` In *${record.container}*` : "";
1818
+ const pages = pagesStr(record) ? ` (pp. ${pagesStr(record)})` : "";
1819
+ const pub = record.publisher ? `. ${record.publisher}` : "";
1820
+ return `${who}${year}${title}.${book}${pages}${pub}.`.trim();
1821
+ }
1822
+ if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
1823
+ const pub = record.publisher ? `${record.publisher}.` : "";
1824
+ return `${who}${year}*${title}*. ${pub}`.trim();
1825
+ }
1826
+ if (record.itemType === "conferencePaper") {
1827
+ const proc = record.container ? ` In *${record.container}*` : "";
1828
+ const pages = pagesStr(record) ? ` (pp. ${pagesStr(record)})` : "";
1829
+ const pub = record.publisher ? `. ${record.publisher}` : "";
1830
+ return `${who}${year}${title}.${proc}${pages}${pub}.`.trim();
1831
+ }
1832
+ return `${who}${year}${title}.`.trim();
1833
+ }
1834
+ function gb(record) {
1835
+ const list = record.authors ?? [];
1836
+ let who;
1837
+ if (!list.length) who = "";
1838
+ else if (list.length <= 3) who = list.map((a) => `${a.lastName}${a.firstName ? " " + a.firstName : ""}`).join(", ");
1839
+ else who = `${list.slice(0, 3).map((a) => `${a.lastName}${a.firstName ? " " + a.firstName : ""}`).join(", ")}, \u7B49`;
1840
+ const whoPart = who ? `${who}. ` : "";
1841
+ const title = (record.title ?? "").trim();
1842
+ const year = yearStr(record);
1843
+ if (record.itemType === "journalArticle" || record.itemType === "preprint") {
1844
+ const journal = record.container ? `${record.container}` : "";
1845
+ const vol = record.volume ? `, ${record.volume}` : "";
1846
+ const issue = record.issue ? `(${record.issue})` : "";
1847
+ const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
1848
+ return `${whoPart}${title}[J]. ${journal}, ${year}${vol}${issue}${pages}.`.replace(", , ", ", ").trim();
1849
+ }
1850
+ if (record.itemType === "bookSection") {
1851
+ const book = record.container ? ` // ${record.container}` : "";
1852
+ const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
1853
+ return `${whoPart}${title}[M]${book}. ${record.publisher ?? ""}, ${year}${pages}.`.trim();
1854
+ }
1855
+ if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
1856
+ const tag2 = record.itemType === "thesis" ? "D" : record.itemType === "report" ? "R" : "M";
1857
+ const pub = record.publisher ? `${record.publisher}, ` : "";
1858
+ return `${whoPart}${title}[${tag2}]. ${pub}${year}.`.trim();
1859
+ }
1860
+ if (record.itemType === "conferencePaper") {
1861
+ const proc = record.container ? ` // ${record.container}` : "";
1862
+ const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
1863
+ return `${whoPart}${title}[C]${proc}. ${record.publisher ?? ""}, ${year}${pages}.`.trim();
1864
+ }
1865
+ return `${whoPart}${title}[Z]. ${year}.`.trim();
1866
+ }
1867
+ function mla(record) {
1868
+ const list = record.authors ?? [];
1869
+ const who = list.length ? list.map((a) => `${a.lastName}, ${a.firstName ?? ""}`.trim()).join(", ") + ". " : "";
1870
+ const title = (record.title ?? "Untitled").trim();
1871
+ if (record.itemType === "journalArticle" || record.itemType === "preprint") {
1872
+ const journal = record.container ? ` *${record.container}*, ` : "";
1873
+ const vol = record.volume ? ` vol. ${record.volume},` : "";
1874
+ const issue = record.issue ? ` no. ${record.issue},` : "";
1875
+ const year = ` ${yearStr(record)},`;
1876
+ const pages = pagesStr(record) ? ` pp. ${pagesStr(record)}.` : ".";
1877
+ const url = doiUrl(record) ? ` ${doiUrl(record)}.` : "";
1878
+ return `${who}"${title}." ${journal}${vol}${issue}${year}${pages}${url}`.replace(/\s+/g, " ").replace(/ ,/g, ",").trim();
1879
+ }
1880
+ if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
1881
+ const pub = record.publisher ? ` ${record.publisher},` : "";
1882
+ return `${who}*${title}.*${pub} ${yearStr(record)}.`.trim();
1883
+ }
1884
+ return `${who}"${title}." ${record.publisher ?? ""} ${yearStr(record)}.`.trim();
1885
+ }
1886
+ function chicago(record) {
1887
+ const list = record.authors ?? [];
1888
+ const who = list.length ? list.map((a) => `${a.firstName ?? ""} ${a.lastName ?? ""}`.trim()).join(", ") + ". " : "";
1889
+ const title = (record.title ?? "Untitled").trim();
1890
+ if (record.itemType === "journalArticle" || record.itemType === "preprint") {
1891
+ const journal = record.container ? ` *${record.container}*` : "";
1892
+ const vol = record.volume ? ` ${record.volume}` : "";
1893
+ const issue = record.issue ? `, no. ${record.issue}` : "";
1894
+ const pages = pagesStr(record) ? `: ${pagesStr(record)}` : "";
1895
+ return `${who}"${title}."${journal}${vol}${issue} (${yearStr(record)})${pages}.`.replace(/\s+/g, " ").replace(/ ,/g, ",").trim();
1896
+ }
1897
+ const pub = record.publisher ? ` ${record.publisher},` : "";
1898
+ return `${who}*${title}.*${pub} ${yearStr(record)}.`.trim();
1899
+ }
1900
+ function bibtexEscape(value) {
1901
+ return String(value ?? "").replace(/[\r\n]+/g, " ").replace(/\\/g, "\\\\").replace(/[&%#$~^_]/g, (ch) => "\\" + ch).trim();
1902
+ }
1903
+ function bibtex(record) {
1904
+ const first = (record.authors ?? [])[0];
1905
+ const year = record.year ? String(record.year) : "";
1906
+ const title = (record.title ?? "").trim();
1907
+ const word = title.replace(/[^\p{L}\p{N}]+/gu, " ").trim().split(/\s+/)[0] ?? "";
1908
+ const key = [first?.lastName ?? "unknown", year, word].filter(Boolean).join("").replace(/[^\p{L}\p{N}]+/gu, "");
1909
+ const type = {
1910
+ journalArticle: "article",
1911
+ book: "book",
1912
+ bookSection: "inbook",
1913
+ conferencePaper: "inproceedings",
1914
+ preprint: "article",
1915
+ thesis: "phdthesis",
1916
+ report: "techreport",
1917
+ dataset: "misc"
1918
+ }[record.itemType] ?? "misc";
1919
+ const containerKey = type === "article" ? "journal" : type === "inbook" || type === "inproceedings" ? "booktitle" : "journal";
1920
+ const fields = [];
1921
+ const authors2 = (record.authors ?? []).map((a) => [a.lastName, a.firstName].filter(Boolean).join(", ")).join(" and ");
1922
+ if (authors2) fields.push(["author", authors2]);
1923
+ if (title) fields.push(["title", title]);
1924
+ if (record.container) fields.push([containerKey, record.container]);
1925
+ if (year) fields.push(["year", year]);
1926
+ if (record.volume) fields.push(["volume", String(record.volume)]);
1927
+ if (record.issue) fields.push(["number", String(record.issue)]);
1928
+ if (record.pages) fields.push(["pages", String(record.pages)]);
1929
+ if (record.publisher) fields.push(["publisher", String(record.publisher)]);
1930
+ if (record.doi) fields.push(["doi", String(record.doi)]);
1931
+ if (record.isbn) fields.push(["isbn", String(record.isbn)]);
1932
+ if (record.url) fields.push(["url", String(record.url)]);
1933
+ if (record.arxiv) fields.push(["note", "arXiv:" + String(record.arxiv)]);
1934
+ const body = fields.map(([k, v]) => ` ${k} = {${bibtexEscape(v)}}`).join(",\n");
1935
+ return "@" + type + "{" + key + ",\n" + body + "\n}\n";
1936
+ }
1937
+ function stripItalic(text) {
1938
+ return String(text ?? "").replace(/\*([^*]+)\*/g, "$1");
1939
+ }
1940
+ function toSegments(text) {
1941
+ const s = String(text ?? "");
1942
+ const out = [];
1943
+ const re = /\*([^*]+)\*/g;
1944
+ let last = 0;
1945
+ let m;
1946
+ while (m = re.exec(s)) {
1947
+ if (m.index > last) out.push({ text: s.slice(last, m.index) });
1948
+ out.push({ text: m[1], italic: true });
1949
+ last = m.index + m[0].length;
1950
+ }
1951
+ if (last < s.length) out.push({ text: s.slice(last) });
1952
+ return out.length ? out : [{ text: s }];
1953
+ }
1954
+ function escapeHtml(text) {
1955
+ return String(text ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1956
+ }
1957
+ function segmentsToHtml(segments) {
1958
+ return segments.map((seg) => seg.italic ? `<i>${escapeHtml(seg.text)}</i>` : escapeHtml(seg.text)).join("");
1959
+ }
1960
+ function cite(record, opts = {}) {
1961
+ return citeDetailed(record, opts).text;
1962
+ }
1963
+ function citeDetailed(record, opts = {}) {
1964
+ const style = STYLES[opts.style] ?? STYLES.apa;
1965
+ const mode = opts.mode ?? "reference";
1966
+ if (opts.style === "bibtex") {
1967
+ const text2 = style.format(record);
1968
+ return { text: text2, segments: [{ text: text2 }], html: escapeHtml(text2), style: opts.style, mode: "reference" };
1969
+ }
1970
+ let raw;
1971
+ if (mode === "intext") {
1972
+ if (opts.style === "gb") {
1973
+ raw = `\uFF08${authorsShortGb(record)}\uFF0C${yearStr(record)}\uFF09`;
1974
+ } else {
1975
+ raw = `(${authorsShort(record)}, ${yearStr(record)})`;
1976
+ }
1977
+ } else if (mode === "direct") {
1978
+ const pages = opts.pages ? opts.style === "gb" ? `\uFF0C\u7B2C ${opts.pages} \u9875` : `, p. ${opts.pages}` : "";
1979
+ if (opts.style === "gb") raw = `\u201C${(record.title ?? "").trim()}\u201D\uFF08${authorsShortGb(record)}\uFF0C${yearStr(record)}${pages}\uFF09`;
1980
+ else raw = `"${(record.title ?? "").trim()}" (${authorsShort(record)}, ${yearStr(record)}${pages})`;
1981
+ } else {
1982
+ raw = style.format(record);
1983
+ }
1984
+ const text = stripItalic(raw);
1985
+ const segments = toSegments(raw);
1986
+ return { text, segments, html: segmentsToHtml(segments), style: opts.style ?? "apa", mode };
1987
+ }
1988
+ function authorsShortGb(record) {
1989
+ const list = record.authors ?? [];
1990
+ if (!list.length) return "\u4F5A\u540D";
1991
+ if (list.length === 1) return list[0].lastName;
1992
+ if (list.length === 2) return `${list[0].lastName}\u3001${list[1].lastName}`;
1993
+ return `${list[0].lastName}\u7B49`;
1994
+ }
1995
+ function listStyles() {
1996
+ return Object.entries(STYLES).map(([id, s]) => ({ id, label: s.label }));
1997
+ }
1998
+ var STYLES;
1999
+ var init_cite = __esm({
2000
+ "src/node/cite.js"() {
2001
+ STYLES = {
2002
+ apa: { label: "APA 7th", format: apa },
2003
+ gb: { label: "GB/T 7714-2015", format: gb },
2004
+ mla: { label: "MLA 9th", format: mla },
2005
+ chicago: { label: "Chicago 17th", format: chicago },
2006
+ bibtex: { label: "BibTeX", format: bibtex }
2007
+ };
2008
+ }
2009
+ });
2010
+
1761
2011
  // src/node/exporter.js
2012
+ var exporter_exports = {};
2013
+ __export(exporter_exports, {
2014
+ batch: () => batch,
2015
+ exportToDirectory: () => exportToDirectory,
2016
+ notesMarkdown: () => notesMarkdown
2017
+ });
1762
2018
  import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
1763
2019
  import { join as join3, resolve as resolve3 } from "node:path";
1764
- function cslJson(record) {
2020
+ function cslJsonOne(record) {
1765
2021
  const authors2 = (record.authors ?? []).map((a) => ({
1766
2022
  family: a.lastName ?? "",
1767
2023
  given: a.firstName ?? ""
@@ -1776,26 +2032,27 @@ function cslJson(record) {
1776
2032
  report: "report",
1777
2033
  dataset: "dataset"
1778
2034
  }[record.itemType] ?? "article-journal";
1779
- return [
1780
- {
1781
- id: record.doi || record.arxiv || record.title || "item",
1782
- type,
1783
- title: record.title ?? "",
1784
- author: authors2,
1785
- issued: record.year ? { "date-parts": [[record.year]] } : void 0,
1786
- "container-title": record.container || void 0,
1787
- publisher: record.publisher || void 0,
1788
- volume: record.volume || void 0,
1789
- issue: record.issue || void 0,
1790
- page: record.pages || void 0,
1791
- DOI: record.doi || void 0,
1792
- ISBN: record.isbn || void 0,
1793
- ISSN: record.issn || void 0,
1794
- abstract: record.abstract || void 0,
1795
- URL: record.url || void 0,
1796
- note: record.arxiv ? `arXiv:${record.arxiv}` : void 0
1797
- }
1798
- ];
2035
+ return {
2036
+ id: record.doi || record.arxiv || record.title || "item",
2037
+ type,
2038
+ title: record.title ?? "",
2039
+ author: authors2,
2040
+ issued: record.year ? { "date-parts": [[record.year]] } : void 0,
2041
+ "container-title": record.container || void 0,
2042
+ publisher: record.publisher || void 0,
2043
+ volume: record.volume || void 0,
2044
+ issue: record.issue || void 0,
2045
+ page: record.pages || void 0,
2046
+ DOI: record.doi || void 0,
2047
+ ISBN: record.isbn || void 0,
2048
+ ISSN: record.issn || void 0,
2049
+ abstract: record.abstract || void 0,
2050
+ URL: record.url || void 0,
2051
+ note: record.arxiv ? `arXiv:${record.arxiv}` : void 0
2052
+ };
2053
+ }
2054
+ function cslJson(record) {
2055
+ return [cslJsonOne(record)];
1799
2056
  }
1800
2057
  function risLine(tag2, value) {
1801
2058
  return value ? `${tag2} - ${String(value).replace(/[\r\n]+/g, " ")}` : null;
@@ -1851,10 +2108,30 @@ async function exportToDirectory(record, pdfBuffer) {
1851
2108
  if (wantRis) await writeFile2(safePaths[i++], ris(record), "utf8");
1852
2109
  return { dir, pdfPath: safePaths[0], jsonPath: jsonPath ? safePaths[wantCsl ? 1 : 0] : null, risPath: risPath ? safePaths[safePaths.length - 1] : null };
1853
2110
  }
2111
+ function batch(format, records) {
2112
+ const list = records ?? [];
2113
+ if (format === "ris") return list.map(ris).join("");
2114
+ if (format === "bibtex") return list.map(bibtex).join("\n") + (list.length ? "\n" : "");
2115
+ if (format === "csl-json") return JSON.stringify(list.map(cslJsonOne), null, 2);
2116
+ throw Object.assign(new Error("\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u683C\u5F0F: " + format), { code: "bad_format" });
2117
+ }
2118
+ function notesMarkdown(title, annotations) {
2119
+ const sorted = [...annotations ?? []].sort((a, b) => (a.pageIndex ?? 0) - (b.pageIndex ?? 0));
2120
+ const parts = [];
2121
+ if (title) parts.push("# " + title);
2122
+ for (const a of sorted) {
2123
+ const page = (a.pageIndex ?? 0) + 1;
2124
+ parts.push("", "## p." + page);
2125
+ if (a.text) parts.push("> " + a.text);
2126
+ if (a.note) parts.push("", a.note);
2127
+ }
2128
+ return parts.join("\n").replace(/^\n+/, "");
2129
+ }
1854
2130
  var init_exporter = __esm({
1855
2131
  "src/node/exporter.js"() {
1856
2132
  init_config();
1857
2133
  init_naming();
2134
+ init_cite();
1858
2135
  }
1859
2136
  });
1860
2137
 
@@ -2819,173 +3096,6 @@ var init_session_track = __esm({
2819
3096
  }
2820
3097
  });
2821
3098
 
2822
- // src/node/cite.js
2823
- var cite_exports = {};
2824
- __export(cite_exports, {
2825
- cite: () => cite,
2826
- listStyles: () => listStyles
2827
- });
2828
- function authorsList(record, { max = 99, ellipsis = ", et al." } = {}) {
2829
- const list = record.authors ?? [];
2830
- if (!list.length) return "";
2831
- const names = list.map((a) => [a.lastName, a.firstName].filter(Boolean).join(", "));
2832
- if (names.length === 1) return names[0];
2833
- if (names.length <= max) return names.slice(0, -1).join(", ") + ", & " + names[names.length - 1];
2834
- return names.slice(0, max).join(", ") + ellipsis;
2835
- }
2836
- function authorsShort(record) {
2837
- const list = record.authors ?? [];
2838
- if (!list.length) return "";
2839
- if (list.length === 1) return list[0].lastName;
2840
- if (list.length === 2) return `${list[0].lastName} & ${list[1].lastName}`;
2841
- return `${list[0].lastName} et al.`;
2842
- }
2843
- function yearStr(record) {
2844
- return record.year ? String(record.year) : "n.d.";
2845
- }
2846
- function pagesStr(record) {
2847
- const p = record.pages ?? "";
2848
- return p ? p.replace(/-/g, "\u2013") : "";
2849
- }
2850
- function doiUrl(record) {
2851
- return record.doi ? `https://doi.org/${record.doi}` : record.url ?? "";
2852
- }
2853
- function apa(record) {
2854
- const authors2 = authorsList(record, { max: 20 });
2855
- const who = authors2 ? `${authors2}. ` : "";
2856
- const year = `(${yearStr(record)}). `;
2857
- const title = (record.title ?? "Untitled").trim();
2858
- if (record.itemType === "journalArticle" || record.itemType === "preprint") {
2859
- const journal = record.container ? `*${record.container}*, ` : "";
2860
- const vol = record.volume ? `${record.volume}` : "";
2861
- const issue = record.issue ? `(${record.issue})` : "";
2862
- const pages = pagesStr(record) ? `, ${pagesStr(record)}` : "";
2863
- const volPart = vol || issue || pages ? `${vol}${issue}${pages}. ` : "";
2864
- const url = doiUrl(record) ? `${doiUrl(record)}` : "";
2865
- return `${who}${year}${title}. ${journal}${volPart}${url}`.trim();
2866
- }
2867
- if (record.itemType === "bookSection") {
2868
- const book = record.container ? ` In *${record.container}*` : "";
2869
- const pages = pagesStr(record) ? ` (pp. ${pagesStr(record)})` : "";
2870
- const pub = record.publisher ? `. ${record.publisher}` : "";
2871
- return `${who}${year}${title}.${book}${pages}${pub}.`.trim();
2872
- }
2873
- if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
2874
- const pub = record.publisher ? `${record.publisher}.` : "";
2875
- return `${who}${year}*${title}*. ${pub}`.trim();
2876
- }
2877
- if (record.itemType === "conferencePaper") {
2878
- const proc = record.container ? ` In *${record.container}*` : "";
2879
- const pages = pagesStr(record) ? ` (pp. ${pagesStr(record)})` : "";
2880
- const pub = record.publisher ? `. ${record.publisher}` : "";
2881
- return `${who}${year}${title}.${proc}${pages}${pub}.`.trim();
2882
- }
2883
- return `${who}${year}${title}.`.trim();
2884
- }
2885
- function gb(record) {
2886
- const list = record.authors ?? [];
2887
- let who;
2888
- if (!list.length) who = "";
2889
- else if (list.length <= 3) who = list.map((a) => `${a.lastName}${a.firstName ? " " + a.firstName : ""}`).join(", ");
2890
- else who = `${list.slice(0, 3).map((a) => `${a.lastName}${a.firstName ? " " + a.firstName : ""}`).join(", ")}, \u7B49`;
2891
- const whoPart = who ? `${who}. ` : "";
2892
- const title = (record.title ?? "").trim();
2893
- const year = yearStr(record);
2894
- if (record.itemType === "journalArticle" || record.itemType === "preprint") {
2895
- const journal = record.container ? `${record.container}` : "";
2896
- const vol = record.volume ? `, ${record.volume}` : "";
2897
- const issue = record.issue ? `(${record.issue})` : "";
2898
- const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
2899
- return `${whoPart}${title}[J]. ${journal}, ${year}${vol}${issue}${pages}.`.replace(", , ", ", ").trim();
2900
- }
2901
- if (record.itemType === "bookSection") {
2902
- const book = record.container ? ` // ${record.container}` : "";
2903
- const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
2904
- return `${whoPart}${title}[M]${book}. ${record.publisher ?? ""}, ${year}${pages}.`.trim();
2905
- }
2906
- if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
2907
- const tag2 = record.itemType === "thesis" ? "D" : record.itemType === "report" ? "R" : "M";
2908
- const pub = record.publisher ? `${record.publisher}, ` : "";
2909
- return `${whoPart}${title}[${tag2}]. ${pub}${year}.`.trim();
2910
- }
2911
- if (record.itemType === "conferencePaper") {
2912
- const proc = record.container ? ` // ${record.container}` : "";
2913
- const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
2914
- return `${whoPart}${title}[C]${proc}. ${record.publisher ?? ""}, ${year}${pages}.`.trim();
2915
- }
2916
- return `${whoPart}${title}[Z]. ${year}.`.trim();
2917
- }
2918
- function mla(record) {
2919
- const list = record.authors ?? [];
2920
- const who = list.length ? list.map((a) => `${a.lastName}, ${a.firstName ?? ""}`.trim()).join(", ") + ". " : "";
2921
- const title = (record.title ?? "Untitled").trim();
2922
- if (record.itemType === "journalArticle" || record.itemType === "preprint") {
2923
- const journal = record.container ? ` *${record.container}*, ` : "";
2924
- const vol = record.volume ? ` vol. ${record.volume},` : "";
2925
- const issue = record.issue ? ` no. ${record.issue},` : "";
2926
- const year = ` ${yearStr(record)},`;
2927
- const pages = pagesStr(record) ? ` pp. ${pagesStr(record)}.` : ".";
2928
- const url = doiUrl(record) ? ` ${doiUrl(record)}.` : "";
2929
- return `${who}"${title}." ${journal}${vol}${issue}${year}${pages}${url}`.replace(/\s+/g, " ").replace(/ ,/g, ",").trim();
2930
- }
2931
- if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
2932
- const pub = record.publisher ? ` ${record.publisher},` : "";
2933
- return `${who}*${title}.*${pub} ${yearStr(record)}.`.trim();
2934
- }
2935
- return `${who}"${title}." ${record.publisher ?? ""} ${yearStr(record)}.`.trim();
2936
- }
2937
- function chicago(record) {
2938
- const list = record.authors ?? [];
2939
- const who = list.length ? list.map((a) => `${a.firstName ?? ""} ${a.lastName ?? ""}`.trim()).join(", ") + ". " : "";
2940
- const title = (record.title ?? "Untitled").trim();
2941
- if (record.itemType === "journalArticle" || record.itemType === "preprint") {
2942
- const journal = record.container ? ` "${record.container}"` : "";
2943
- const vol = record.volume ? ` ${record.volume}` : "";
2944
- const issue = record.issue ? `, no. ${record.issue}` : "";
2945
- const pages = pagesStr(record) ? `: ${pagesStr(record)}` : "";
2946
- return `${who}"${title}.${journal}" ${vol}${issue} (${yearStr(record)})${pages}.`.trim();
2947
- }
2948
- const pub = record.publisher ? ` ${record.publisher},` : "";
2949
- return `${who}*${title}.*${pub} ${yearStr(record)}.`.trim();
2950
- }
2951
- function cite(record, opts = {}) {
2952
- const style = STYLES[opts.style] ?? STYLES.apa;
2953
- const mode = opts.mode ?? "reference";
2954
- if (mode === "intext") {
2955
- if (opts.style === "gb") {
2956
- return `\uFF08${authorsShortGb(record)}\uFF0C${yearStr(record)}\uFF09`;
2957
- }
2958
- return `(${authorsShort(record)}, ${yearStr(record)})`;
2959
- }
2960
- if (mode === "direct") {
2961
- const pages = opts.pages ? opts.style === "gb" ? `\uFF0C\u7B2C ${opts.pages} \u9875` : `, p. ${opts.pages}` : "";
2962
- if (opts.style === "gb") return `\u201C${(record.title ?? "").trim()}\u201D\uFF08${authorsShortGb(record)}\uFF0C${yearStr(record)}${pages}\uFF09`;
2963
- return `"${(record.title ?? "").trim()}" (${authorsShort(record)}, ${yearStr(record)}${pages})`;
2964
- }
2965
- return style.format(record);
2966
- }
2967
- function authorsShortGb(record) {
2968
- const list = record.authors ?? [];
2969
- if (!list.length) return "\u4F5A\u540D";
2970
- if (list.length === 1) return list[0].lastName;
2971
- if (list.length === 2) return `${list[0].lastName}\u3001${list[1].lastName}`;
2972
- return `${list[0].lastName}\u7B49`;
2973
- }
2974
- function listStyles() {
2975
- return Object.entries(STYLES).map(([id, s]) => ({ id, label: s.label }));
2976
- }
2977
- var STYLES;
2978
- var init_cite = __esm({
2979
- "src/node/cite.js"() {
2980
- STYLES = {
2981
- apa: { label: "APA 7th", format: apa },
2982
- gb: { label: "GB/T 7714-2015", format: gb },
2983
- mla: { label: "MLA 9th", format: mla },
2984
- chicago: { label: "Chicago 17th", format: chicago }
2985
- };
2986
- }
2987
- });
2988
-
2989
3099
  // node_modules/pdfjs-dist/legacy/build/pdf.mjs
2990
3100
  var pdf_exports = {};
2991
3101
  __export(pdf_exports, {
@@ -35521,14 +35631,14 @@ async function servePdf(req, res, key) {
35521
35631
  }
35522
35632
  const path = safePdfPath(decoded);
35523
35633
  if (!path) {
35524
- writeJson(res, 400, { error: "invalid key" });
35634
+ writeJson(res, 400, { error: "invalid key", code: "invalidKey" });
35525
35635
  return;
35526
35636
  }
35527
35637
  let buffer;
35528
35638
  try {
35529
35639
  buffer = await readFile5(path);
35530
35640
  } catch {
35531
- writeJson(res, 404, { error: "pdf not downloaded yet" });
35641
+ writeJson(res, 404, { error: "pdf not downloaded yet", code: "pdfNotDownloaded" });
35532
35642
  return;
35533
35643
  }
35534
35644
  const range = parseRange(req.headers.range, buffer.length);
@@ -35613,7 +35723,7 @@ async function handleAnnotations(req, res, key) {
35613
35723
  writeJson(res, 200, { ok: true });
35614
35724
  return;
35615
35725
  }
35616
- writeJson(res, 405, { error: "method not allowed" });
35726
+ writeJson(res, 405, { error: "method not allowed", code: "methodNotAllowed" });
35617
35727
  }
35618
35728
  async function readJsonBody2(req) {
35619
35729
  const { readJsonBody: read } = await Promise.resolve().then(() => (init_http(), http_exports));
@@ -35621,12 +35731,12 @@ async function readJsonBody2(req) {
35621
35731
  }
35622
35732
  async function handler(req, res, ctx) {
35623
35733
  if (!isLoopbackRequest(req)) {
35624
- writeJson(res, 403, { error: "forbidden: loopback-only" });
35734
+ writeJson(res, 403, { error: "forbidden: loopback-only", code: "forbidden" });
35625
35735
  return;
35626
35736
  }
35627
35737
  const path = pathOf(req);
35628
35738
  if (!path.startsWith(PREFIX2)) {
35629
- writeJson(res, 404, { error: "not found" });
35739
+ writeJson(res, 404, { error: "not found", code: "notFound" });
35630
35740
  return;
35631
35741
  }
35632
35742
  const rest = path.slice(PREFIX2.length).replace(/^\/+/, "");
@@ -35693,20 +35803,71 @@ async function handler(req, res, ctx) {
35693
35803
  writeJson(res, 200, { ok: true });
35694
35804
  return;
35695
35805
  }
35806
+ if (head === "item" && parts[1] && methodOk(req, "PATCH")) {
35807
+ try {
35808
+ parts[1] = decodeURIComponent(parts[1]);
35809
+ } catch {
35810
+ }
35811
+ const body = await readJsonBody2(req);
35812
+ const patch = body?.patch ?? {};
35813
+ const item = await patchItem(parts[1], patch);
35814
+ if (!item) {
35815
+ writeJson(res, 404, { error: "item not found", code: "itemNotFound" });
35816
+ return;
35817
+ }
35818
+ writeJson(res, 200, { item });
35819
+ return;
35820
+ }
35821
+ if (head === "export-notes" && parts[1] && methodOk(req, "GET")) {
35822
+ let key = parts[1];
35823
+ try {
35824
+ key = decodeURIComponent(key);
35825
+ } catch {
35826
+ }
35827
+ const item = await getItem(key);
35828
+ const annotations = await getAnnotations(key);
35829
+ const { notesMarkdown: notesMarkdown2 } = await Promise.resolve().then(() => (init_exporter(), exporter_exports));
35830
+ const title = item?.record?.title || item?.title || "";
35831
+ const markdown = notesMarkdown2(title, annotations);
35832
+ writeJson(res, 200, { markdown, count: annotations.length });
35833
+ return;
35834
+ }
35835
+ if (head === "export-batch" && methodOk(req, "POST")) {
35836
+ const body = await readJsonBody2(req);
35837
+ const keys = Array.isArray(body?.keys) ? body.keys.map(String) : [];
35838
+ const format = String(body?.format ?? "ris");
35839
+ if (!keys.length) {
35840
+ writeJson(res, 400, { error: "\u6CA1\u6709\u9009\u62E9\u8981\u5BFC\u51FA\u7684\u6761\u76EE", code: "noSelection" });
35841
+ return;
35842
+ }
35843
+ const records = [];
35844
+ for (const k of keys) {
35845
+ const item = await getItem(k);
35846
+ if (item?.record) records.push(item.record);
35847
+ }
35848
+ if (!records.length) {
35849
+ writeJson(res, 400, { error: "\u6240\u9009\u6761\u76EE\u7F3A\u5C11\u5143\u6570\u636E\uFF0C\u65E0\u6CD5\u5BFC\u51FA", code: "noMetadata" });
35850
+ return;
35851
+ }
35852
+ const { batch: batch2 } = await Promise.resolve().then(() => (init_exporter(), exporter_exports));
35853
+ const text = batch2(format, records);
35854
+ writeJson(res, 200, { text, format, count: records.length });
35855
+ return;
35856
+ }
35696
35857
  if (head === "cite" && methodOk(req, "POST")) {
35697
35858
  const body = await readJsonBody2(req);
35698
35859
  const item = await getItem(String(body?.key ?? ""));
35699
35860
  if (!item?.record) {
35700
- writeJson(res, 404, { error: "\u6761\u76EE\u7F3A\u5C11\u5143\u6570\u636E\uFF0C\u65E0\u6CD5\u751F\u6210\u5F15\u7528" });
35861
+ writeJson(res, 404, { error: "\u6761\u76EE\u7F3A\u5C11\u5143\u6570\u636E\uFF0C\u65E0\u6CD5\u751F\u6210\u5F15\u7528", code: "noMetadataCite" });
35701
35862
  return;
35702
35863
  }
35703
- const { cite: cite2 } = await Promise.resolve().then(() => (init_cite(), cite_exports));
35704
- const text = cite2(item.record, {
35864
+ const { citeDetailed: citeDetailed2 } = await Promise.resolve().then(() => (init_cite(), cite_exports));
35865
+ const detail = citeDetailed2(item.record, {
35705
35866
  style: body?.style ?? "apa",
35706
35867
  mode: body?.mode ?? "reference",
35707
35868
  pages: body?.pages
35708
35869
  });
35709
- writeJson(res, 200, { text, style: body?.style ?? "apa", mode: body?.mode ?? "reference" });
35870
+ writeJson(res, 200, { ...detail, style: body?.style ?? "apa", mode: body?.mode ?? "reference" });
35710
35871
  return;
35711
35872
  }
35712
35873
  if (head === "scan-dir" && methodOk(req, "POST")) {
@@ -35753,7 +35914,7 @@ async function handler(req, res, ctx) {
35753
35914
  const filename = url.searchParams.get("filename") || "imported.pdf";
35754
35915
  const autoSave = url.searchParams.get("autoSave") !== "0";
35755
35916
  if (!key) {
35756
- writeJson(res, 400, { error: "missing key" });
35917
+ writeJson(res, 400, { error: "missing key", code: "missingKey" });
35757
35918
  return;
35758
35919
  }
35759
35920
  const buffer = await readRawBody(req, 128 * 1024 * 1024);