@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.
Files changed (58) hide show
  1. package/README.en.md +90 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +90 -0
  4. package/cordis.patch.yml +13 -0
  5. package/lib/client.js +15119 -0
  6. package/lib/index.js +3660 -0
  7. package/lib/invariant.js +38 -0
  8. package/lib/typert.host.d.ts +3 -0
  9. package/lib/typert.host.js +1738 -0
  10. package/lib/typert.remote-client.d.ts +82 -0
  11. package/lib/typert.remote-client.js +1115 -0
  12. package/lib/types/arxiv.d.ts +47 -0
  13. package/lib/types/arxiv.js +76 -0
  14. package/lib/types/client/ReaderPane.d.ts +71 -0
  15. package/lib/types/client/ReaderPane.js +3135 -0
  16. package/lib/types/client/contract.d.ts +254 -0
  17. package/lib/types/client/contract.js +1 -0
  18. package/lib/types/client/definition.d.ts +27 -0
  19. package/lib/types/client/definition.js +32 -0
  20. package/lib/types/client/extract-article.d.ts +102 -0
  21. package/lib/types/client/extract-article.js +1024 -0
  22. package/lib/types/client/icons.d.ts +27 -0
  23. package/lib/types/client/icons.js +11 -0
  24. package/lib/types/client/index.d.ts +34 -0
  25. package/lib/types/client/index.js +222 -0
  26. package/lib/types/client/locales.d.ts +391 -0
  27. package/lib/types/client/locales.js +382 -0
  28. package/lib/types/client/parse-rss.d.ts +69 -0
  29. package/lib/types/client/parse-rss.js +656 -0
  30. package/lib/types/client/quote.d.ts +89 -0
  31. package/lib/types/client/quote.js +119 -0
  32. package/lib/types/client/selectors.d.ts +167 -0
  33. package/lib/types/client/selectors.js +413 -0
  34. package/lib/types/client/session.d.ts +171 -0
  35. package/lib/types/client/session.js +223 -0
  36. package/lib/types/client/store.d.ts +181 -0
  37. package/lib/types/client/store.js +226 -0
  38. package/lib/types/client/translate.d.ts +379 -0
  39. package/lib/types/client/translate.js +873 -0
  40. package/lib/types/index.d.ts +48 -0
  41. package/lib/types/index.js +25 -0
  42. package/lib/types/invariant.d.ts +29 -0
  43. package/lib/types/invariant.js +38 -0
  44. package/lib/types/link-resolvers.d.ts +107 -0
  45. package/lib/types/link-resolvers.js +299 -0
  46. package/lib/types/remote.d.ts +224 -0
  47. package/lib/types/remote.js +275 -0
  48. package/lib/types/schedule.d.ts +78 -0
  49. package/lib/types/schedule.js +114 -0
  50. package/lib/types/service.d.ts +633 -0
  51. package/lib/types/service.js +1697 -0
  52. package/lib/types/store.d.ts +224 -0
  53. package/lib/types/store.js +916 -0
  54. package/lib/types/types.d.ts +710 -0
  55. package/lib/types/types.js +182 -0
  56. package/locale/en.json +6 -0
  57. package/locale/zh.json +6 -0
  58. 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