@khorsheed/dsh-reader 0.2.0
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.en.md +90 -0
- package/README.i18n.yaml +6 -0
- package/README.md +90 -0
- package/cordis.patch.yml +13 -0
- package/lib/client.js +15119 -0
- package/lib/index.js +3660 -0
- package/lib/invariant.js +38 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +1738 -0
- package/lib/typert.remote-client.d.ts +82 -0
- package/lib/typert.remote-client.js +1115 -0
- package/lib/types/arxiv.d.ts +47 -0
- package/lib/types/arxiv.js +76 -0
- package/lib/types/client/ReaderPane.d.ts +71 -0
- package/lib/types/client/ReaderPane.js +3135 -0
- package/lib/types/client/contract.d.ts +254 -0
- package/lib/types/client/contract.js +1 -0
- package/lib/types/client/definition.d.ts +27 -0
- package/lib/types/client/definition.js +32 -0
- package/lib/types/client/extract-article.d.ts +102 -0
- package/lib/types/client/extract-article.js +1024 -0
- package/lib/types/client/icons.d.ts +27 -0
- package/lib/types/client/icons.js +11 -0
- package/lib/types/client/index.d.ts +34 -0
- package/lib/types/client/index.js +222 -0
- package/lib/types/client/locales.d.ts +391 -0
- package/lib/types/client/locales.js +382 -0
- package/lib/types/client/parse-rss.d.ts +69 -0
- package/lib/types/client/parse-rss.js +656 -0
- package/lib/types/client/quote.d.ts +89 -0
- package/lib/types/client/quote.js +119 -0
- package/lib/types/client/selectors.d.ts +167 -0
- package/lib/types/client/selectors.js +413 -0
- package/lib/types/client/session.d.ts +171 -0
- package/lib/types/client/session.js +223 -0
- package/lib/types/client/store.d.ts +181 -0
- package/lib/types/client/store.js +226 -0
- package/lib/types/client/translate.d.ts +379 -0
- package/lib/types/client/translate.js +873 -0
- package/lib/types/index.d.ts +48 -0
- package/lib/types/index.js +25 -0
- package/lib/types/invariant.d.ts +29 -0
- package/lib/types/invariant.js +38 -0
- package/lib/types/link-resolvers.d.ts +107 -0
- package/lib/types/link-resolvers.js +299 -0
- package/lib/types/remote.d.ts +224 -0
- package/lib/types/remote.js +275 -0
- package/lib/types/schedule.d.ts +78 -0
- package/lib/types/schedule.js +114 -0
- package/lib/types/service.d.ts +633 -0
- package/lib/types/service.js +1697 -0
- package/lib/types/store.d.ts +224 -0
- package/lib/types/store.js +916 -0
- package/lib/types/types.d.ts +710 -0
- package/lib/types/types.js +182 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +128 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* arXiv link recognition, and the upgrade to the official HTML version.
|
|
3
|
+
*
|
|
4
|
+
* Many papers carry a LaTeXML-built HTML rendering next to the abstract and the
|
|
5
|
+
* PDF (`arxiv.org/html/<id>`): real tables, semantic sections and MathML
|
|
6
|
+
* formulas, where the PDF is a byte stream and the abstract page is a summary.
|
|
7
|
+
* A pasted `/abs` or `/pdf` link therefore upgrades to the HTML version BEFORE
|
|
8
|
+
* the first fetch — the reader who pasted the paper meant the paper.
|
|
9
|
+
*
|
|
10
|
+
* Pure and network-free, so both the add flow and the link resolvers build on
|
|
11
|
+
* it; the fetch policy (try the HTML version, fall back on 404) lives in the
|
|
12
|
+
* service.
|
|
13
|
+
*
|
|
14
|
+
* @module @khorsheed/dsh-reader/arxiv
|
|
15
|
+
*/
|
|
16
|
+
/** One parsed arXiv article link. */
|
|
17
|
+
export interface ArxivLink {
|
|
18
|
+
/** Which of the paper's three faces the link points at. */
|
|
19
|
+
readonly kind: 'abs' | 'pdf' | 'html';
|
|
20
|
+
/** The paper id as the link carries it, version suffix (`v2`) included. */
|
|
21
|
+
readonly id: string;
|
|
22
|
+
}
|
|
23
|
+
/** Whether a bare string is a syntactically valid arXiv id (the resolvers validate API answers with it). */
|
|
24
|
+
export declare function isArxivId(value: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Parse an arxiv.org link to one paper.
|
|
27
|
+
*
|
|
28
|
+
* Only the three single-paper paths count: `/abs`, `/pdf`, `/html`. Listing,
|
|
29
|
+
* search and author pages name no paper, so they read as ordinary links.
|
|
30
|
+
*
|
|
31
|
+
* @param url - the URL as pasted (already normalized by the caller).
|
|
32
|
+
* @returns the parsed link, or `undefined`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function arxivLink(url: string): ArxivLink | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* The official HTML version of an `/abs` or `/pdf` link.
|
|
37
|
+
*
|
|
38
|
+
* `undefined` when the link IS already the HTML page (no rewrite, so the add
|
|
39
|
+
* flow can never loop) or is not an arXiv paper link at all. The HTML version
|
|
40
|
+
* is not guaranteed to exist — old papers lack it — so the caller must treat
|
|
41
|
+
* a 404 as "no upgrade", not as a failure of the link.
|
|
42
|
+
*
|
|
43
|
+
* @param url - the pasted arXiv URL.
|
|
44
|
+
* @returns the HTML version's URL, or `undefined`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function arxivHtmlUrl(url: string): string | undefined;
|
|
47
|
+
//# sourceMappingURL=arxiv.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* arXiv link recognition, and the upgrade to the official HTML version.
|
|
3
|
+
*
|
|
4
|
+
* Many papers carry a LaTeXML-built HTML rendering next to the abstract and the
|
|
5
|
+
* PDF (`arxiv.org/html/<id>`): real tables, semantic sections and MathML
|
|
6
|
+
* formulas, where the PDF is a byte stream and the abstract page is a summary.
|
|
7
|
+
* A pasted `/abs` or `/pdf` link therefore upgrades to the HTML version BEFORE
|
|
8
|
+
* the first fetch — the reader who pasted the paper meant the paper.
|
|
9
|
+
*
|
|
10
|
+
* Pure and network-free, so both the add flow and the link resolvers build on
|
|
11
|
+
* it; the fetch policy (try the HTML version, fall back on 404) lives in the
|
|
12
|
+
* service.
|
|
13
|
+
*
|
|
14
|
+
* @module @khorsheed/dsh-reader/arxiv
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The id shapes arXiv assigns: new-style `YYMM.NNNNN(vN)` (month-keyed, four or
|
|
18
|
+
* five sequence digits — six from 2027 on), or the archived subject-class form
|
|
19
|
+
* `hep-th/9901001(vN)` / `math.GT/0309136(vN)`.
|
|
20
|
+
*/
|
|
21
|
+
const ARXIV_ID = /^(?:\d{4}\.\d{4,6}|[a-z-]+(?:\.[A-Z]{2})?\/\d{7})(?:v\d+)?$/;
|
|
22
|
+
/** Whether a bare string is a syntactically valid arXiv id (the resolvers validate API answers with it). */
|
|
23
|
+
export function isArxivId(value) {
|
|
24
|
+
return ARXIV_ID.test(value);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse an arxiv.org link to one paper.
|
|
28
|
+
*
|
|
29
|
+
* Only the three single-paper paths count: `/abs`, `/pdf`, `/html`. Listing,
|
|
30
|
+
* search and author pages name no paper, so they read as ordinary links.
|
|
31
|
+
*
|
|
32
|
+
* @param url - the URL as pasted (already normalized by the caller).
|
|
33
|
+
* @returns the parsed link, or `undefined`.
|
|
34
|
+
*/
|
|
35
|
+
export function arxivLink(url) {
|
|
36
|
+
let parsed;
|
|
37
|
+
try {
|
|
38
|
+
parsed = new URL(url);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const host = parsed.hostname.toLowerCase();
|
|
44
|
+
if (host !== 'arxiv.org' && host !== 'www.arxiv.org')
|
|
45
|
+
return undefined;
|
|
46
|
+
// The id itself may carry one slash (the old `archive/YYMMNNN` form), so the
|
|
47
|
+
// capture runs to the query/fragment and the id pattern below is the judge.
|
|
48
|
+
const match = /^\/(abs|pdf|html)\/([^?#]+)/.exec(parsed.pathname);
|
|
49
|
+
const kind = match?.[1];
|
|
50
|
+
let id = match?.[2];
|
|
51
|
+
if (kind === undefined || id === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
// A pdf link may spell the id with its file suffix: `/pdf/1706.03762v7.pdf`.
|
|
54
|
+
if (kind === 'pdf' && /\.pdf$/i.test(id))
|
|
55
|
+
id = id.slice(0, -4);
|
|
56
|
+
if (!ARXIV_ID.test(id))
|
|
57
|
+
return undefined;
|
|
58
|
+
return { kind, id };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The official HTML version of an `/abs` or `/pdf` link.
|
|
62
|
+
*
|
|
63
|
+
* `undefined` when the link IS already the HTML page (no rewrite, so the add
|
|
64
|
+
* flow can never loop) or is not an arXiv paper link at all. The HTML version
|
|
65
|
+
* is not guaranteed to exist — old papers lack it — so the caller must treat
|
|
66
|
+
* a 404 as "no upgrade", not as a failure of the link.
|
|
67
|
+
*
|
|
68
|
+
* @param url - the pasted arXiv URL.
|
|
69
|
+
* @returns the HTML version's URL, or `undefined`.
|
|
70
|
+
*/
|
|
71
|
+
export function arxivHtmlUrl(url) {
|
|
72
|
+
const link = arxivLink(url);
|
|
73
|
+
if (link === undefined || link.kind === 'html')
|
|
74
|
+
return undefined;
|
|
75
|
+
return `https://arxiv.org/html/${link.id}`;
|
|
76
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The inspiration space: the right-Sidebar tab body.
|
|
3
|
+
*
|
|
4
|
+
* It is the only React surface this package ships, and it owns the whole
|
|
5
|
+
* interaction the design settled on:
|
|
6
|
+
*
|
|
7
|
+
* - **the wall** — one card per entry, with a search box, an all/today filter,
|
|
8
|
+
* an unread-only toggle and a sort menu. All four are local predicates over
|
|
9
|
+
* entries this process parsed (design decision D14), so none of them costs a
|
|
10
|
+
* round trip.
|
|
11
|
+
* - **the detail view** — clicking a card opens it. This is the whole point:
|
|
12
|
+
* the body renders as DOM text, which is what makes passage quoting work
|
|
13
|
+
* (the QUOTE plugin's own selection menu sees it), and it is why the pane
|
|
14
|
+
* never hosts a sandboxed iframe.
|
|
15
|
+
* - **the add dialog** — an overlay in the members-tab idiom, because adding is
|
|
16
|
+
* a momentary act and the wall should stay where it was behind it. The host
|
|
17
|
+
* decides whether a pasted URL is a feed or an article from what comes back
|
|
18
|
+
* (D15); this surface reports the verdict, plus the fetch seam's own words
|
|
19
|
+
* when the fetch failed.
|
|
20
|
+
* - **the subscription page** — every source with its schedule, its pause
|
|
21
|
+
* switch and its last-fetch status. It exists because "why am I not seeing
|
|
22
|
+
* anything" and "stop refreshing this one" are questions about SOURCES, and
|
|
23
|
+
* neither belongs on the wall.
|
|
24
|
+
*
|
|
25
|
+
* The browser has no XML parser on the host side to lean on, so the pane parses
|
|
26
|
+
* payloads itself through `parse-rss` / `extract-article`.
|
|
27
|
+
*
|
|
28
|
+
* @module @khorsheed/dsh-reader/client/ReaderPane
|
|
29
|
+
*/
|
|
30
|
+
import { type ReactNode } from 'react';
|
|
31
|
+
import type { ReaderPaneProps } from './contract.ts';
|
|
32
|
+
import { type ReaderReadingAnchor } from './session.ts';
|
|
33
|
+
/**
|
|
34
|
+
* One top-level block, measured.
|
|
35
|
+
*
|
|
36
|
+
* `textLength` is captured in the same pass so the scroll handler can record a
|
|
37
|
+
* TEXT offset without touching the DOM; `element` is what lets a restore
|
|
38
|
+
* resolve that offset to an exact pixel spot (a Range rect) later.
|
|
39
|
+
*/
|
|
40
|
+
export interface ArticleBlockMetrics {
|
|
41
|
+
readonly top: number;
|
|
42
|
+
readonly height: number;
|
|
43
|
+
/** How long the block's text was at measure time. */
|
|
44
|
+
readonly textLength: number;
|
|
45
|
+
/** The block itself, for the text-offset resolution. */
|
|
46
|
+
readonly element: HTMLElement;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* How far into the anchor block the viewport top sat, in the CURRENT layout.
|
|
50
|
+
*
|
|
51
|
+
* A pixel offset into a block is only valid for the layout it was measured in,
|
|
52
|
+
* and the translated view IS a different layout: the same paragraph sets
|
|
53
|
+
* different words at a different height, so the recorded `offset` points at the
|
|
54
|
+
* wrong sentence — the drift the reader reported. The recorded TEXT offset is
|
|
55
|
+
* the fix: when the block still holds the same text it resolves to an exact
|
|
56
|
+
* pixel spot (a Range rect over the text node that holds that character); when
|
|
57
|
+
* the words changed, the same FRACTION of the text is the honest place. An
|
|
58
|
+
* anchor written before the text offset existed falls back to the pixel offset,
|
|
59
|
+
* unchanged.
|
|
60
|
+
*
|
|
61
|
+
* Exported for the specs; the pane itself reaches it through
|
|
62
|
+
* `applyReadingPosition`.
|
|
63
|
+
*
|
|
64
|
+
* @param block - the anchor block's CURRENT metrics.
|
|
65
|
+
* @param anchor - the recorded place.
|
|
66
|
+
* @returns pixels into the block.
|
|
67
|
+
*/
|
|
68
|
+
export declare function offsetInBlock(block: ArticleBlockMetrics, anchor: ReaderReadingAnchor): number;
|
|
69
|
+
/** The reader tab body. */
|
|
70
|
+
export declare function ReaderPane(props: ReaderPaneProps): ReactNode;
|
|
71
|
+
//# sourceMappingURL=ReaderPane.d.ts.map
|