@a9i5k4/dsh-literature 0.2.7 → 0.2.8

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,223 @@ 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
+ listStyles: () => listStyles
1772
+ });
1773
+ function authorsList(record, { max = 99, ellipsis = ", et al." } = {}) {
1774
+ const list = record.authors ?? [];
1775
+ if (!list.length) return "";
1776
+ const names = list.map((a) => [a.lastName, a.firstName].filter(Boolean).join(", "));
1777
+ if (names.length === 1) return names[0];
1778
+ if (names.length <= max) return names.slice(0, -1).join(", ") + ", & " + names[names.length - 1];
1779
+ return names.slice(0, max).join(", ") + ellipsis;
1780
+ }
1781
+ function authorsShort(record) {
1782
+ const list = record.authors ?? [];
1783
+ if (!list.length) return "";
1784
+ if (list.length === 1) return list[0].lastName;
1785
+ if (list.length === 2) return `${list[0].lastName} & ${list[1].lastName}`;
1786
+ return `${list[0].lastName} et al.`;
1787
+ }
1788
+ function yearStr(record) {
1789
+ return record.year ? String(record.year) : "n.d.";
1790
+ }
1791
+ function pagesStr(record) {
1792
+ const p = record.pages ?? "";
1793
+ return p ? p.replace(/-/g, "\u2013") : "";
1794
+ }
1795
+ function doiUrl(record) {
1796
+ return record.doi ? `https://doi.org/${record.doi}` : record.url ?? "";
1797
+ }
1798
+ function apa(record) {
1799
+ const authors2 = authorsList(record, { max: 20 });
1800
+ const who = authors2 ? `${authors2}. ` : "";
1801
+ const year = `(${yearStr(record)}). `;
1802
+ const title = (record.title ?? "Untitled").trim();
1803
+ if (record.itemType === "journalArticle" || record.itemType === "preprint") {
1804
+ const journal = record.container ? `*${record.container}*, ` : "";
1805
+ const vol = record.volume ? `${record.volume}` : "";
1806
+ const issue = record.issue ? `(${record.issue})` : "";
1807
+ const pages = pagesStr(record) ? `, ${pagesStr(record)}` : "";
1808
+ const volPart = vol || issue || pages ? `${vol}${issue}${pages}. ` : "";
1809
+ const url = doiUrl(record) ? `${doiUrl(record)}` : "";
1810
+ return `${who}${year}${title}. ${journal}${volPart}${url}`.trim();
1811
+ }
1812
+ if (record.itemType === "bookSection") {
1813
+ const book = record.container ? ` In *${record.container}*` : "";
1814
+ const pages = pagesStr(record) ? ` (pp. ${pagesStr(record)})` : "";
1815
+ const pub = record.publisher ? `. ${record.publisher}` : "";
1816
+ return `${who}${year}${title}.${book}${pages}${pub}.`.trim();
1817
+ }
1818
+ if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
1819
+ const pub = record.publisher ? `${record.publisher}.` : "";
1820
+ return `${who}${year}*${title}*. ${pub}`.trim();
1821
+ }
1822
+ if (record.itemType === "conferencePaper") {
1823
+ const proc = record.container ? ` In *${record.container}*` : "";
1824
+ const pages = pagesStr(record) ? ` (pp. ${pagesStr(record)})` : "";
1825
+ const pub = record.publisher ? `. ${record.publisher}` : "";
1826
+ return `${who}${year}${title}.${proc}${pages}${pub}.`.trim();
1827
+ }
1828
+ return `${who}${year}${title}.`.trim();
1829
+ }
1830
+ function gb(record) {
1831
+ const list = record.authors ?? [];
1832
+ let who;
1833
+ if (!list.length) who = "";
1834
+ else if (list.length <= 3) who = list.map((a) => `${a.lastName}${a.firstName ? " " + a.firstName : ""}`).join(", ");
1835
+ else who = `${list.slice(0, 3).map((a) => `${a.lastName}${a.firstName ? " " + a.firstName : ""}`).join(", ")}, \u7B49`;
1836
+ const whoPart = who ? `${who}. ` : "";
1837
+ const title = (record.title ?? "").trim();
1838
+ const year = yearStr(record);
1839
+ if (record.itemType === "journalArticle" || record.itemType === "preprint") {
1840
+ const journal = record.container ? `${record.container}` : "";
1841
+ const vol = record.volume ? `, ${record.volume}` : "";
1842
+ const issue = record.issue ? `(${record.issue})` : "";
1843
+ const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
1844
+ return `${whoPart}${title}[J]. ${journal}, ${year}${vol}${issue}${pages}.`.replace(", , ", ", ").trim();
1845
+ }
1846
+ if (record.itemType === "bookSection") {
1847
+ const book = record.container ? ` // ${record.container}` : "";
1848
+ const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
1849
+ return `${whoPart}${title}[M]${book}. ${record.publisher ?? ""}, ${year}${pages}.`.trim();
1850
+ }
1851
+ if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
1852
+ const tag2 = record.itemType === "thesis" ? "D" : record.itemType === "report" ? "R" : "M";
1853
+ const pub = record.publisher ? `${record.publisher}, ` : "";
1854
+ return `${whoPart}${title}[${tag2}]. ${pub}${year}.`.trim();
1855
+ }
1856
+ if (record.itemType === "conferencePaper") {
1857
+ const proc = record.container ? ` // ${record.container}` : "";
1858
+ const pages = pagesStr(record) ? `: ${pagesStr(record).replace(/–/g, "-")}` : "";
1859
+ return `${whoPart}${title}[C]${proc}. ${record.publisher ?? ""}, ${year}${pages}.`.trim();
1860
+ }
1861
+ return `${whoPart}${title}[Z]. ${year}.`.trim();
1862
+ }
1863
+ function mla(record) {
1864
+ const list = record.authors ?? [];
1865
+ const who = list.length ? list.map((a) => `${a.lastName}, ${a.firstName ?? ""}`.trim()).join(", ") + ". " : "";
1866
+ const title = (record.title ?? "Untitled").trim();
1867
+ if (record.itemType === "journalArticle" || record.itemType === "preprint") {
1868
+ const journal = record.container ? ` *${record.container}*, ` : "";
1869
+ const vol = record.volume ? ` vol. ${record.volume},` : "";
1870
+ const issue = record.issue ? ` no. ${record.issue},` : "";
1871
+ const year = ` ${yearStr(record)},`;
1872
+ const pages = pagesStr(record) ? ` pp. ${pagesStr(record)}.` : ".";
1873
+ const url = doiUrl(record) ? ` ${doiUrl(record)}.` : "";
1874
+ return `${who}"${title}." ${journal}${vol}${issue}${year}${pages}${url}`.replace(/\s+/g, " ").replace(/ ,/g, ",").trim();
1875
+ }
1876
+ if (record.itemType === "book" || record.itemType === "report" || record.itemType === "thesis") {
1877
+ const pub = record.publisher ? ` ${record.publisher},` : "";
1878
+ return `${who}*${title}.*${pub} ${yearStr(record)}.`.trim();
1879
+ }
1880
+ return `${who}"${title}." ${record.publisher ?? ""} ${yearStr(record)}.`.trim();
1881
+ }
1882
+ function chicago(record) {
1883
+ const list = record.authors ?? [];
1884
+ const who = list.length ? list.map((a) => `${a.firstName ?? ""} ${a.lastName ?? ""}`.trim()).join(", ") + ". " : "";
1885
+ const title = (record.title ?? "Untitled").trim();
1886
+ if (record.itemType === "journalArticle" || record.itemType === "preprint") {
1887
+ const journal = record.container ? ` "${record.container}"` : "";
1888
+ const vol = record.volume ? ` ${record.volume}` : "";
1889
+ const issue = record.issue ? `, no. ${record.issue}` : "";
1890
+ const pages = pagesStr(record) ? `: ${pagesStr(record)}` : "";
1891
+ return `${who}"${title}.${journal}" ${vol}${issue} (${yearStr(record)})${pages}.`.trim();
1892
+ }
1893
+ const pub = record.publisher ? ` ${record.publisher},` : "";
1894
+ return `${who}*${title}.*${pub} ${yearStr(record)}.`.trim();
1895
+ }
1896
+ function bibtexEscape(value) {
1897
+ return String(value ?? "").replace(/[\r\n]+/g, " ").replace(/\\/g, "\\\\").replace(/[&%#$~^_]/g, (ch) => "\\" + ch).trim();
1898
+ }
1899
+ function bibtex(record) {
1900
+ const first = (record.authors ?? [])[0];
1901
+ const year = record.year ? String(record.year) : "";
1902
+ const title = (record.title ?? "").trim();
1903
+ const word = title.replace(/[^\p{L}\p{N}]+/gu, " ").trim().split(/\s+/)[0] ?? "";
1904
+ const key = [first?.lastName ?? "unknown", year, word].filter(Boolean).join("").replace(/[^\p{L}\p{N}]+/gu, "");
1905
+ const type = {
1906
+ journalArticle: "article",
1907
+ book: "book",
1908
+ bookSection: "inbook",
1909
+ conferencePaper: "inproceedings",
1910
+ preprint: "article",
1911
+ thesis: "phdthesis",
1912
+ report: "techreport",
1913
+ dataset: "misc"
1914
+ }[record.itemType] ?? "misc";
1915
+ const containerKey = type === "article" ? "journal" : type === "inbook" || type === "inproceedings" ? "booktitle" : "journal";
1916
+ const fields = [];
1917
+ const authors2 = (record.authors ?? []).map((a) => [a.lastName, a.firstName].filter(Boolean).join(", ")).join(" and ");
1918
+ if (authors2) fields.push(["author", authors2]);
1919
+ if (title) fields.push(["title", title]);
1920
+ if (record.container) fields.push([containerKey, record.container]);
1921
+ if (year) fields.push(["year", year]);
1922
+ if (record.volume) fields.push(["volume", String(record.volume)]);
1923
+ if (record.issue) fields.push(["number", String(record.issue)]);
1924
+ if (record.pages) fields.push(["pages", String(record.pages)]);
1925
+ if (record.publisher) fields.push(["publisher", String(record.publisher)]);
1926
+ if (record.doi) fields.push(["doi", String(record.doi)]);
1927
+ if (record.isbn) fields.push(["isbn", String(record.isbn)]);
1928
+ if (record.url) fields.push(["url", String(record.url)]);
1929
+ if (record.arxiv) fields.push(["note", "arXiv:" + String(record.arxiv)]);
1930
+ const body = fields.map(([k, v]) => ` ${k} = {${bibtexEscape(v)}}`).join(",\n");
1931
+ return "@" + type + "{" + key + ",\n" + body + "\n}\n";
1932
+ }
1933
+ function cite(record, opts = {}) {
1934
+ const style = STYLES[opts.style] ?? STYLES.apa;
1935
+ const mode = opts.mode ?? "reference";
1936
+ if (opts.style === "bibtex") return style.format(record);
1937
+ if (mode === "intext") {
1938
+ if (opts.style === "gb") {
1939
+ return `\uFF08${authorsShortGb(record)}\uFF0C${yearStr(record)}\uFF09`;
1940
+ }
1941
+ return `(${authorsShort(record)}, ${yearStr(record)})`;
1942
+ }
1943
+ if (mode === "direct") {
1944
+ const pages = opts.pages ? opts.style === "gb" ? `\uFF0C\u7B2C ${opts.pages} \u9875` : `, p. ${opts.pages}` : "";
1945
+ if (opts.style === "gb") return `\u201C${(record.title ?? "").trim()}\u201D\uFF08${authorsShortGb(record)}\uFF0C${yearStr(record)}${pages}\uFF09`;
1946
+ return `"${(record.title ?? "").trim()}" (${authorsShort(record)}, ${yearStr(record)}${pages})`;
1947
+ }
1948
+ return style.format(record);
1949
+ }
1950
+ function authorsShortGb(record) {
1951
+ const list = record.authors ?? [];
1952
+ if (!list.length) return "\u4F5A\u540D";
1953
+ if (list.length === 1) return list[0].lastName;
1954
+ if (list.length === 2) return `${list[0].lastName}\u3001${list[1].lastName}`;
1955
+ return `${list[0].lastName}\u7B49`;
1956
+ }
1957
+ function listStyles() {
1958
+ return Object.entries(STYLES).map(([id, s]) => ({ id, label: s.label }));
1959
+ }
1960
+ var STYLES;
1961
+ var init_cite = __esm({
1962
+ "src/node/cite.js"() {
1963
+ STYLES = {
1964
+ apa: { label: "APA 7th", format: apa },
1965
+ gb: { label: "GB/T 7714-2015", format: gb },
1966
+ mla: { label: "MLA 9th", format: mla },
1967
+ chicago: { label: "Chicago 17th", format: chicago },
1968
+ bibtex: { label: "BibTeX", format: bibtex }
1969
+ };
1970
+ }
1971
+ });
1972
+
1761
1973
  // src/node/exporter.js
1974
+ var exporter_exports = {};
1975
+ __export(exporter_exports, {
1976
+ batch: () => batch,
1977
+ exportToDirectory: () => exportToDirectory,
1978
+ notesMarkdown: () => notesMarkdown
1979
+ });
1762
1980
  import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
1763
1981
  import { join as join3, resolve as resolve3 } from "node:path";
1764
- function cslJson(record) {
1982
+ function cslJsonOne(record) {
1765
1983
  const authors2 = (record.authors ?? []).map((a) => ({
1766
1984
  family: a.lastName ?? "",
1767
1985
  given: a.firstName ?? ""
@@ -1776,26 +1994,27 @@ function cslJson(record) {
1776
1994
  report: "report",
1777
1995
  dataset: "dataset"
1778
1996
  }[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
- ];
1997
+ return {
1998
+ id: record.doi || record.arxiv || record.title || "item",
1999
+ type,
2000
+ title: record.title ?? "",
2001
+ author: authors2,
2002
+ issued: record.year ? { "date-parts": [[record.year]] } : void 0,
2003
+ "container-title": record.container || void 0,
2004
+ publisher: record.publisher || void 0,
2005
+ volume: record.volume || void 0,
2006
+ issue: record.issue || void 0,
2007
+ page: record.pages || void 0,
2008
+ DOI: record.doi || void 0,
2009
+ ISBN: record.isbn || void 0,
2010
+ ISSN: record.issn || void 0,
2011
+ abstract: record.abstract || void 0,
2012
+ URL: record.url || void 0,
2013
+ note: record.arxiv ? `arXiv:${record.arxiv}` : void 0
2014
+ };
2015
+ }
2016
+ function cslJson(record) {
2017
+ return [cslJsonOne(record)];
1799
2018
  }
1800
2019
  function risLine(tag2, value) {
1801
2020
  return value ? `${tag2} - ${String(value).replace(/[\r\n]+/g, " ")}` : null;
@@ -1851,10 +2070,30 @@ async function exportToDirectory(record, pdfBuffer) {
1851
2070
  if (wantRis) await writeFile2(safePaths[i++], ris(record), "utf8");
1852
2071
  return { dir, pdfPath: safePaths[0], jsonPath: jsonPath ? safePaths[wantCsl ? 1 : 0] : null, risPath: risPath ? safePaths[safePaths.length - 1] : null };
1853
2072
  }
2073
+ function batch(format, records) {
2074
+ const list = records ?? [];
2075
+ if (format === "ris") return list.map(ris).join("");
2076
+ if (format === "bibtex") return list.map(bibtex).join("\n") + (list.length ? "\n" : "");
2077
+ if (format === "csl-json") return JSON.stringify(list.map(cslJsonOne), null, 2);
2078
+ throw Object.assign(new Error("\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u683C\u5F0F: " + format), { code: "bad_format" });
2079
+ }
2080
+ function notesMarkdown(title, annotations) {
2081
+ const sorted = [...annotations ?? []].sort((a, b) => (a.pageIndex ?? 0) - (b.pageIndex ?? 0));
2082
+ const parts = [];
2083
+ if (title) parts.push("# " + title);
2084
+ for (const a of sorted) {
2085
+ const page = (a.pageIndex ?? 0) + 1;
2086
+ parts.push("", "## p." + page);
2087
+ if (a.text) parts.push("> " + a.text);
2088
+ if (a.note) parts.push("", a.note);
2089
+ }
2090
+ return parts.join("\n").replace(/^\n+/, "");
2091
+ }
1854
2092
  var init_exporter = __esm({
1855
2093
  "src/node/exporter.js"() {
1856
2094
  init_config();
1857
2095
  init_naming();
2096
+ init_cite();
1858
2097
  }
1859
2098
  });
1860
2099
 
@@ -2819,173 +3058,6 @@ var init_session_track = __esm({
2819
3058
  }
2820
3059
  });
2821
3060
 
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
3061
  // node_modules/pdfjs-dist/legacy/build/pdf.mjs
2990
3062
  var pdf_exports = {};
2991
3063
  __export(pdf_exports, {
@@ -35521,14 +35593,14 @@ async function servePdf(req, res, key) {
35521
35593
  }
35522
35594
  const path = safePdfPath(decoded);
35523
35595
  if (!path) {
35524
- writeJson(res, 400, { error: "invalid key" });
35596
+ writeJson(res, 400, { error: "invalid key", code: "invalidKey" });
35525
35597
  return;
35526
35598
  }
35527
35599
  let buffer;
35528
35600
  try {
35529
35601
  buffer = await readFile5(path);
35530
35602
  } catch {
35531
- writeJson(res, 404, { error: "pdf not downloaded yet" });
35603
+ writeJson(res, 404, { error: "pdf not downloaded yet", code: "pdfNotDownloaded" });
35532
35604
  return;
35533
35605
  }
35534
35606
  const range = parseRange(req.headers.range, buffer.length);
@@ -35613,7 +35685,7 @@ async function handleAnnotations(req, res, key) {
35613
35685
  writeJson(res, 200, { ok: true });
35614
35686
  return;
35615
35687
  }
35616
- writeJson(res, 405, { error: "method not allowed" });
35688
+ writeJson(res, 405, { error: "method not allowed", code: "methodNotAllowed" });
35617
35689
  }
35618
35690
  async function readJsonBody2(req) {
35619
35691
  const { readJsonBody: read } = await Promise.resolve().then(() => (init_http(), http_exports));
@@ -35621,12 +35693,12 @@ async function readJsonBody2(req) {
35621
35693
  }
35622
35694
  async function handler(req, res, ctx) {
35623
35695
  if (!isLoopbackRequest(req)) {
35624
- writeJson(res, 403, { error: "forbidden: loopback-only" });
35696
+ writeJson(res, 403, { error: "forbidden: loopback-only", code: "forbidden" });
35625
35697
  return;
35626
35698
  }
35627
35699
  const path = pathOf(req);
35628
35700
  if (!path.startsWith(PREFIX2)) {
35629
- writeJson(res, 404, { error: "not found" });
35701
+ writeJson(res, 404, { error: "not found", code: "notFound" });
35630
35702
  return;
35631
35703
  }
35632
35704
  const rest = path.slice(PREFIX2.length).replace(/^\/+/, "");
@@ -35693,11 +35765,62 @@ async function handler(req, res, ctx) {
35693
35765
  writeJson(res, 200, { ok: true });
35694
35766
  return;
35695
35767
  }
35768
+ if (head === "item" && parts[1] && methodOk(req, "PATCH")) {
35769
+ try {
35770
+ parts[1] = decodeURIComponent(parts[1]);
35771
+ } catch {
35772
+ }
35773
+ const body = await readJsonBody2(req);
35774
+ const patch = body?.patch ?? {};
35775
+ const item = await patchItem(parts[1], patch);
35776
+ if (!item) {
35777
+ writeJson(res, 404, { error: "item not found", code: "itemNotFound" });
35778
+ return;
35779
+ }
35780
+ writeJson(res, 200, { item });
35781
+ return;
35782
+ }
35783
+ if (head === "export-notes" && parts[1] && methodOk(req, "GET")) {
35784
+ let key = parts[1];
35785
+ try {
35786
+ key = decodeURIComponent(key);
35787
+ } catch {
35788
+ }
35789
+ const item = await getItem(key);
35790
+ const annotations = await getAnnotations(key);
35791
+ const { notesMarkdown: notesMarkdown2 } = await Promise.resolve().then(() => (init_exporter(), exporter_exports));
35792
+ const title = item?.record?.title || item?.title || "";
35793
+ const markdown = notesMarkdown2(title, annotations);
35794
+ writeJson(res, 200, { markdown, count: annotations.length });
35795
+ return;
35796
+ }
35797
+ if (head === "export-batch" && methodOk(req, "POST")) {
35798
+ const body = await readJsonBody2(req);
35799
+ const keys = Array.isArray(body?.keys) ? body.keys.map(String) : [];
35800
+ const format = String(body?.format ?? "ris");
35801
+ if (!keys.length) {
35802
+ writeJson(res, 400, { error: "\u6CA1\u6709\u9009\u62E9\u8981\u5BFC\u51FA\u7684\u6761\u76EE", code: "noSelection" });
35803
+ return;
35804
+ }
35805
+ const records = [];
35806
+ for (const k of keys) {
35807
+ const item = await getItem(k);
35808
+ if (item?.record) records.push(item.record);
35809
+ }
35810
+ if (!records.length) {
35811
+ writeJson(res, 400, { error: "\u6240\u9009\u6761\u76EE\u7F3A\u5C11\u5143\u6570\u636E\uFF0C\u65E0\u6CD5\u5BFC\u51FA", code: "noMetadata" });
35812
+ return;
35813
+ }
35814
+ const { batch: batch2 } = await Promise.resolve().then(() => (init_exporter(), exporter_exports));
35815
+ const text = batch2(format, records);
35816
+ writeJson(res, 200, { text, format, count: records.length });
35817
+ return;
35818
+ }
35696
35819
  if (head === "cite" && methodOk(req, "POST")) {
35697
35820
  const body = await readJsonBody2(req);
35698
35821
  const item = await getItem(String(body?.key ?? ""));
35699
35822
  if (!item?.record) {
35700
- writeJson(res, 404, { error: "\u6761\u76EE\u7F3A\u5C11\u5143\u6570\u636E\uFF0C\u65E0\u6CD5\u751F\u6210\u5F15\u7528" });
35823
+ writeJson(res, 404, { error: "\u6761\u76EE\u7F3A\u5C11\u5143\u6570\u636E\uFF0C\u65E0\u6CD5\u751F\u6210\u5F15\u7528", code: "noMetadataCite" });
35701
35824
  return;
35702
35825
  }
35703
35826
  const { cite: cite2 } = await Promise.resolve().then(() => (init_cite(), cite_exports));
@@ -35753,7 +35876,7 @@ async function handler(req, res, ctx) {
35753
35876
  const filename = url.searchParams.get("filename") || "imported.pdf";
35754
35877
  const autoSave = url.searchParams.get("autoSave") !== "0";
35755
35878
  if (!key) {
35756
- writeJson(res, 400, { error: "missing key" });
35879
+ writeJson(res, 400, { error: "missing key", code: "missingKey" });
35757
35880
  return;
35758
35881
  }
35759
35882
  const buffer = await readRawBody(req, 128 * 1024 * 1024);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a9i5k4/dsh-literature",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "DSH Literature 文献侧窗:在 DeepSeek Harness 侧边栏识别 DOI/arXiv/标题、抓取元数据与全文、写入本地文献库或导出目录,并提供内置 PDF 阅读器(缩放/翻页/目录/搜索/高亮/笔记)。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -23,8 +23,9 @@
23
23
  "test": "node scripts/test-host.mjs",
24
24
  "check:client": "node scripts/check-client.mjs && node scripts/check-apply.mjs",
25
25
  "check:render": "node scripts/render-check.cjs",
26
- "prepublishOnly": "node build.mjs && node scripts/smoke.mjs && node scripts/check-client.mjs && node scripts/check-apply.mjs && node scripts/check-settings.mjs && node scripts/test-host.mjs",
27
- "check:settings": "node scripts/check-settings.mjs"
26
+ "prepublishOnly": "node build.mjs && node scripts/smoke.mjs && node scripts/check-client.mjs && node scripts/check-apply.mjs && node scripts/check-settings.mjs && node scripts/check-i18n.mjs && node scripts/test-host.mjs",
27
+ "check:settings": "node scripts/check-settings.mjs",
28
+ "check:i18n": "node scripts/check-i18n.mjs"
28
29
  },
29
30
  "dsh": {
30
31
  "bundle": {