| Name | Message | Date |
|---|---|---|
| 📁 ui | 10 hours ago | |
| 📄 api.ts | 10 hours ago | |
| 📄 auth.ts | 10 hours ago | |
| 📄 content.ts | 10 hours ago | |
| 📄 errors.ts | 10 hours ago | |
| 📄 hash.ts | 10 hours ago | |
| 📄 md5.ts | 10 hours ago | |
| 📄 selectors.ts | 10 hours ago | |
| 📄 titles.ts | 10 hours ago |
📄
src/selectors.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
export interface IssueRef {
titleId: string;
issueId: string;
}
/**
* Primary selector: the reader's own URL contract (stable across site builds):
*
* /p/<title-slug>/<date>/p/<title_id>/<issue_id> (default view)
* /p/<title-slug>/<date>/r/<part>/<spread>/<title_id>/<issue_id> (replica view)
*
* The issue id is always the last path segment, the title id second to last.
* Article links (/a/…) are intentionally not matched.
*/
const ISSUE_HREF = /^\/p\/[^/]+\/\d{4}-\d{2}-\d{2}\/(?:p|r)(?:\/\d+)*\/(\d+)\/(\d+)\/?$/;
/** Article-mode links are never issue links, even in the loose fallback. */
const ARTICLE_HREF = /^\/p\/[^/]+\/\d{4}-\d{2}-\d{2}\/a\//;
/**
* Fallback classes observed on the site (Nov 2026). styled-components class
* names regenerate on every build of the site, so these are only a secondary
* signal — the href pattern above is the main selector.
*/
const FALLBACK_CELL_CLASS = 'sc-djvmMF';
const FALLBACK_LINK_CLASS = 'sc-iTONeN';
/** Loose shape: /p/… ending in two numeric segments (title id + issue id). */
const LOOSE_ISSUE_HREF = /^\/p\/.+\/(\d+)\/(\d+)\/?$/;
export function parseIssueAnchor(anchor: HTMLAnchorElement): IssueRef | null {
const href = anchor.getAttribute('href');
if (!href) return null;
const path = href.split(/[?#]/, 1)[0] ?? href;
const strict = ISSUE_HREF.exec(path);
if (strict) {
return { titleId: strict[1] ?? '', issueId: strict[2] ?? '' };
}
if (ARTICLE_HREF.test(path)) return null;
// Fallback for future URL tweaks: same rough shape + known carousel classes.
const hasClassSignal =
anchor.classList.contains(FALLBACK_LINK_CLASS) ||
anchor.closest(`.${FALLBACK_CELL_CLASS}`) !== null;
if (hasClassSignal) {
const loose = LOOSE_ISSUE_HREF.exec(path);
if (loose) {
return { titleId: loose[1] ?? '', issueId: loose[2] ?? '' };
}
}
return null;
}