@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/README.md +6 -4
- package/README_EN.md +6 -4
- package/lib/client.js +233 -14
- package/lib/index.js +360 -199
- 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,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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
|
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
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
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 {
|
|
35704
|
-
const
|
|
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, {
|
|
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);
|