@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/README.md +6 -4
- package/README_EN.md +6 -4
- package/lib/client.js +177 -14
- package/lib/index.js +319 -196
- package/package.json +4 -3
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.
|
|
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
|
|
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
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
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.
|
|
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": {
|