mcp-scraper 0.97.1 → 0.98.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 (104) hide show
  1. package/CHANGELOG.md +5 -2442
  2. package/README.md +16 -412
  3. package/dist/bin/api-server.js +3 -2
  4. package/dist/bin/mcp-scraper-cli.js +33 -27
  5. package/dist/bin/mcp-scraper-core.js +9 -1
  6. package/dist/bin/mcp-scraper-install.js +17 -2
  7. package/dist/bin/mcp-stdio-server.js +9 -1
  8. package/dist/bin/paa-harvest.js +8 -1
  9. package/dist/index.cjs +5 -21830
  10. package/dist/index.d.ts +14 -607
  11. package/dist/index.js +5 -39
  12. package/dist/transport.d.ts +35 -0
  13. package/package.json +11 -51
  14. package/THIRD_PARTY_NOTICES.html +0 -203
  15. package/dist/actions-5X5CQAKO.js +0 -1
  16. package/dist/artifacts-4ZDNP2JW.js +0 -1
  17. package/dist/artifacts-N62OPIT5.js +0 -1
  18. package/dist/artifacts-PSADD5OC.js +0 -1
  19. package/dist/artifacts-RUR3OHAK.js +0 -1
  20. package/dist/blob-store-GIH26EDO.js +0 -1
  21. package/dist/bundle-GS657HXG.js +0 -1
  22. package/dist/chunk-24YDQ3EO.js +0 -5
  23. package/dist/chunk-2L7CHN5N.js +0 -4
  24. package/dist/chunk-3F7AFM53.js +0 -1
  25. package/dist/chunk-4AEW53RU.js +0 -954
  26. package/dist/chunk-4HZXATHB.js +0 -27
  27. package/dist/chunk-4YZ74SJ6.js +0 -1
  28. package/dist/chunk-4ZVO6GX4.js +0 -1
  29. package/dist/chunk-55ZANTHL.js +0 -3
  30. package/dist/chunk-5RQWFYXY.js +0 -930
  31. package/dist/chunk-5TQINRZN.js +0 -12
  32. package/dist/chunk-5UQAL7HV.js +0 -41
  33. package/dist/chunk-6D25Q5BR.js +0 -21728
  34. package/dist/chunk-6N5EC7ZO.js +0 -1
  35. package/dist/chunk-7POHNTVW.js +0 -1
  36. package/dist/chunk-7SR7MEU5.js +0 -62
  37. package/dist/chunk-ANQ4JJVI.js +0 -71
  38. package/dist/chunk-B3JRL45T.js +0 -1
  39. package/dist/chunk-B4ZKPLSP.js +0 -1
  40. package/dist/chunk-B5Y26E3A.js +0 -1
  41. package/dist/chunk-C3JWX6KM.js +0 -10
  42. package/dist/chunk-C7MGJUTU.js +0 -1
  43. package/dist/chunk-CNSVZTTT.js +0 -13
  44. package/dist/chunk-EYGA6LOU.js +0 -1
  45. package/dist/chunk-F3M7INSN.js +0 -41
  46. package/dist/chunk-FLAWC5PF.js +0 -5
  47. package/dist/chunk-FM7BX2LU.js +0 -159
  48. package/dist/chunk-HCK6PLM3.js +0 -3
  49. package/dist/chunk-HNNYXMSI.js +0 -1
  50. package/dist/chunk-IFPION5D.js +0 -121
  51. package/dist/chunk-IPQOBOFY.js +0 -1
  52. package/dist/chunk-J7XDXIB6.js +0 -163
  53. package/dist/chunk-JHDD6R3O.js +0 -8
  54. package/dist/chunk-JMZIDHYQ.js +0 -2
  55. package/dist/chunk-K3VDGJNK.js +0 -1
  56. package/dist/chunk-KBZY2OGP.js +0 -11
  57. package/dist/chunk-KJQXUZ4Y.js +0 -4
  58. package/dist/chunk-KXJCGV3H.js +0 -1
  59. package/dist/chunk-NALXKTUU.js +0 -1
  60. package/dist/chunk-NNOX3FYG.js +0 -198
  61. package/dist/chunk-NQFTETVC.js +0 -189
  62. package/dist/chunk-OILOHQNL.js +0 -2
  63. package/dist/chunk-OLYPLX5C.js +0 -1
  64. package/dist/chunk-P6CDR3RM.js +0 -1
  65. package/dist/chunk-PKQHHCO4.js +0 -20
  66. package/dist/chunk-QCBCOEH5.js +0 -1
  67. package/dist/chunk-QER2ELZP.js +0 -1
  68. package/dist/chunk-RDHGQ2WZ.js +0 -1
  69. package/dist/chunk-SDQW7IJN.js +0 -1
  70. package/dist/chunk-SEZETSB3.js +0 -1
  71. package/dist/chunk-SJNSTO3C.js +0 -42
  72. package/dist/chunk-TZYITYL4.js +0 -2
  73. package/dist/chunk-UIOSADPB.js +0 -1718
  74. package/dist/chunk-UNEJYZ4T.js +0 -102
  75. package/dist/chunk-UUJYICAJ.js +0 -1
  76. package/dist/chunk-VVSHZV5S.js +0 -73
  77. package/dist/chunk-WNUXMPVX.js +0 -1
  78. package/dist/chunk-XNTAHMPA.js +0 -218
  79. package/dist/chunk-ZDECILF6.js +0 -16
  80. package/dist/chunk-ZNPGM3XE.js +0 -4
  81. package/dist/compatibility-UPPKZ4KY.js +0 -1
  82. package/dist/contracts-GUXGFCVV.js +0 -1
  83. package/dist/core-schema-Y47YFKH2.js +0 -899
  84. package/dist/direct-completion-ZVPPEBDU.js +0 -1
  85. package/dist/dispatch-GRMMPKQD.js +0 -1
  86. package/dist/editorial-reading-room/assets/app.js +0 -433
  87. package/dist/editorial-reading-room/assets/index.html +0 -139
  88. package/dist/editorial-reading-room/assets/styles.css +0 -1091
  89. package/dist/index.d.cts +0 -607
  90. package/dist/jobs-NRZHGQ7I.js +0 -1
  91. package/dist/media-artifacts-67CJWI7O.js +0 -1
  92. package/dist/metering-YHZ46GV4.js +0 -1
  93. package/dist/migrations-3AKEC5QE.js +0 -1
  94. package/dist/queue-2VKW7SGH.js +0 -1
  95. package/dist/reconciliation-QC6SILLR.js +0 -1
  96. package/dist/report-artifact-offload-PQNFDNPL.js +0 -1
  97. package/dist/repository-CYXVMYAI.js +0 -1
  98. package/dist/repository-UKDA4IWP.js +0 -1
  99. package/dist/server-EYMJROPX.js +0 -5544
  100. package/dist/settlement-47EUTSGX.js +0 -1
  101. package/dist/vendor-errors-647LSH6H.js +0 -1
  102. package/dist/worker-AWJF7UYR.js +0 -1
  103. package/public/.well-known/xray-install.json +0 -183
  104. package/public/agent-install.md +0 -200
@@ -1 +0,0 @@
1
- import{a,b,c,d}from"./chunk-2L7CHN5N.js";import"./chunk-SJNSTO3C.js";import"./chunk-XNTAHMPA.js";import"./chunk-6D25Q5BR.js";import"./chunk-C3JWX6KM.js";import"./chunk-KBZY2OGP.js";import"./chunk-JHDD6R3O.js";import"./chunk-7SR7MEU5.js";import"./chunk-ANQ4JJVI.js";import"./chunk-FLAWC5PF.js";import"./chunk-B4ZKPLSP.js";import"./chunk-VVSHZV5S.js";import"./chunk-PKQHHCO4.js";import"./chunk-4ZVO6GX4.js";import"./chunk-C7MGJUTU.js";import"./chunk-WNUXMPVX.js";import"./chunk-6N5EC7ZO.js";import"./chunk-HNNYXMSI.js";export{a as captureDirectCompletion,d as recoverDirectCompletions,b as resumeDirectCompletion,c as settlePaaHarvestJob};
@@ -1 +0,0 @@
1
- import{f as a,g as b}from"./chunk-UUJYICAJ.js";import"./chunk-NQFTETVC.js";import"./chunk-KXJCGV3H.js";import"./chunk-SEZETSB3.js";import"./chunk-SDQW7IJN.js";import"./chunk-ANQ4JJVI.js";import"./chunk-PKQHHCO4.js";import"./chunk-4ZVO6GX4.js";import"./chunk-C7MGJUTU.js";import"./chunk-WNUXMPVX.js";import"./chunk-OILOHQNL.js";import"./chunk-HNNYXMSI.js";export{a as dispatchSiteExtractJob,b as redispatchFundedSiteExtractJobs};
@@ -1,433 +0,0 @@
1
- const elements = {
2
- home: document.querySelector("#home-view"),
3
- article: document.querySelector("#article-view"),
4
- loading: document.querySelector("#loading-screen"),
5
- menuButton: document.querySelector("#menu-button"),
6
- menuClose: document.querySelector("#menu-close"),
7
- backdrop: document.querySelector("#drawer-backdrop"),
8
- drawer: document.querySelector("#navigation-drawer"),
9
- drawerNav: document.querySelector("#drawer-navigation"),
10
- search: document.querySelector("#menu-search"),
11
- progress: document.querySelector("#reading-progress-bar"),
12
- desktopJumps: document.querySelector("#desktop-jump-links")
13
- };
14
-
15
- function storageGet(key, fallback = null) {
16
- try {
17
- return localStorage.getItem(key) ?? fallback;
18
- } catch {
19
- return fallback;
20
- }
21
- }
22
-
23
- function storageSet(key, value) {
24
- try {
25
- localStorage.setItem(key, value);
26
- } catch {
27
- // Sandboxed previews can intentionally deny storage. The reading room
28
- // remains fully usable for the current session without persistence.
29
- }
30
- }
31
-
32
- function storageArray(key) {
33
- try {
34
- const value = JSON.parse(storageGet(key, "[]"));
35
- return Array.isArray(value) ? value : [];
36
- } catch {
37
- return [];
38
- }
39
- }
40
-
41
- const state = {
42
- data: null,
43
- current: null,
44
- storageKey: "editorial-reading-room",
45
- typeIndex: Number(storageGet("editorial-reading-room:type", "0")),
46
- completed: new Set(storageArray("editorial-reading-room:completed"))
47
- };
48
-
49
- const typeModes = ["default", "large", "larger"];
50
-
51
- function escapeHtml(value = "") {
52
- return value
53
- .replace(/&/g, "&")
54
- .replace(/</g, "&lt;")
55
- .replace(/>/g, "&gt;")
56
- .replace(/"/g, "&quot;");
57
- }
58
-
59
- function socialImage(article = null) {
60
- return article?.ogImage || article?.image || state.data.site.ogImage || null;
61
- }
62
-
63
- function setMeta(selector, attribute, value) {
64
- const element = document.querySelector(selector);
65
- if (!element) return;
66
- if (value) element.setAttribute(attribute, value);
67
- else element.removeAttribute(attribute);
68
- }
69
-
70
- function updateSocialMeta(article = null) {
71
- const title = article ? `${article.title} — ${state.data.site.title}` : state.data.site.title;
72
- const description = article?.summary || state.data.deck || state.data.site.heroTitle;
73
- const image = socialImage(article);
74
- setMeta('meta[property="og:type"]', "content", article ? "article" : "website");
75
- setMeta('meta[property="og:title"]', "content", title);
76
- setMeta('meta[property="og:description"]', "content", description);
77
- setMeta('meta[name="twitter:title"]', "content", title);
78
- setMeta('meta[name="twitter:description"]', "content", description);
79
- setMeta('meta[property="og:image"]', "content", image?.url || "");
80
- setMeta('meta[property="og:image:alt"]', "content", image?.alt || "");
81
- setMeta('meta[property="og:image:width"]', "content", image?.width ? String(image.width) : "");
82
- setMeta('meta[property="og:image:height"]', "content", image?.height ? String(image.height) : "");
83
- setMeta('meta[name="twitter:image"]', "content", image?.url || "");
84
- setMeta('meta[name="twitter:image:alt"]', "content", image?.alt || "");
85
- }
86
-
87
- function imageMarkup(image, className) {
88
- if (!image) return "";
89
- return `<img class="${className}" src="${escapeHtml(image.url)}" alt="${escapeHtml(image.alt)}" loading="lazy">`;
90
- }
91
-
92
- function setTypeMode() {
93
- const mode = typeModes[state.typeIndex % typeModes.length];
94
- if (mode === "default") document.documentElement.removeAttribute("data-type");
95
- else document.documentElement.dataset.type = mode;
96
- storageSet(`${state.storageKey}:type`, String(state.typeIndex % typeModes.length));
97
- }
98
-
99
- function setTheme(theme) {
100
- if (theme === "evening") document.documentElement.dataset.theme = "evening";
101
- else document.documentElement.removeAttribute("data-theme");
102
- storageSet(`${state.storageKey}:theme`, theme);
103
- }
104
-
105
- function editionBasePath() {
106
- const path = new URL(window.location.href).pathname.replace(/\/+$/, "");
107
- const editionMatch = path.match(/^(\/editions\/[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/[a-z0-9-]+)?$/);
108
- return editionMatch ? editionMatch[1] : "";
109
- }
110
-
111
- function routeSlug() {
112
- const url = new URL(window.location.href);
113
- const legacy = url.searchParams.get("article");
114
- if (legacy) return legacy;
115
- const path = url.pathname.replace(/\/+$/, "");
116
- const base = editionBasePath();
117
- const remainder = base ? path.slice(base.length) : path;
118
- const match = remainder.match(/^\/([a-z0-9]+(?:-[a-z0-9]+)*)$/);
119
- return match ? match[1] : null;
120
- }
121
-
122
- function articleUrl(slug, section = "") {
123
- const url = new URL(window.location.href);
124
- const base = editionBasePath();
125
- url.pathname = slug ? `${base}/${slug}` : `${base || "/"}`;
126
- url.searchParams.delete("article");
127
- url.hash = section ? `#${section}` : "";
128
- return `${url.pathname}${url.search}${url.hash}`;
129
- }
130
-
131
- function navigate(slug, section = "", replace = false) {
132
- const method = replace ? "replaceState" : "pushState";
133
- history[method]({}, "", articleUrl(slug, section));
134
- renderRoute();
135
- }
136
-
137
- function openDrawer() {
138
- document.body.classList.add("drawer-open");
139
- elements.drawer.setAttribute("aria-hidden", "false");
140
- elements.menuButton.setAttribute("aria-expanded", "true");
141
- renderDrawer();
142
- requestAnimationFrame(() => elements.search.focus());
143
- }
144
-
145
- function closeDrawer() {
146
- document.body.classList.remove("drawer-open");
147
- elements.drawer.setAttribute("aria-hidden", "true");
148
- elements.menuButton.setAttribute("aria-expanded", "false");
149
- elements.search.value = "";
150
- elements.menuButton.focus();
151
- }
152
-
153
- function groupArticles(articles) {
154
- const groups = new Map();
155
- articles.forEach((article) => {
156
- if (!groups.has(article.category)) groups.set(article.category, []);
157
- groups.get(article.category).push(article);
158
- });
159
- return groups;
160
- }
161
-
162
- function renderDrawer() {
163
- const query = elements.search.value.trim().toLowerCase();
164
- const current = state.current;
165
- const articles = state.data.articles.filter((article) => {
166
- if (!query) return true;
167
- return `${article.title} ${article.summary} ${article.category}`
168
- .toLowerCase()
169
- .includes(query);
170
- });
171
- const groups = groupArticles(articles);
172
- let html = "";
173
- for (const [category, items] of groups) {
174
- html += `<section class="drawer-group"><p class="drawer-group-title">${escapeHtml(
175
- category
176
- )}</p>`;
177
- items.forEach((article) => {
178
- const index = String(article.order).padStart(2, "0");
179
- const completed = state.completed.has(article.slug) ? "✓" : index;
180
- html += `<button class="drawer-link" data-article="${article.slug}" ${
181
- current?.slug === article.slug ? 'aria-current="page"' : ""
182
- }><span class="drawer-index">${completed}</span><span class="drawer-title">${escapeHtml(
183
- article.title
184
- )}</span></button>`;
185
- });
186
- html += "</section>";
187
- }
188
- if (current && (!query || current.title.toLowerCase().includes(query))) {
189
- html += `<section class="jump-group"><p class="drawer-group-title">Jump within this article</p>`;
190
- current.toc.forEach((item) => {
191
- html += `<button class="jump-link level-${item.level}" data-section="${item.id}">${escapeHtml(
192
- item.title
193
- )}</button>`;
194
- });
195
- html += "</section>";
196
- }
197
- elements.drawerNav.innerHTML = html || "<p>No matching articles.</p>";
198
- }
199
-
200
- function renderHome() {
201
- state.current = null;
202
- elements.home.hidden = false;
203
- elements.article.hidden = true;
204
- elements.progress.style.width = "0";
205
- document.title = state.data.site.title;
206
- updateSocialMeta();
207
- document.querySelector("#masthead-product").textContent = state.data.site.product;
208
- document.querySelector("#masthead-title").textContent = state.data.site.edition;
209
- document.querySelector("#edition-label").textContent = state.data.site.editionLabel;
210
- document.querySelector("#issue-label").textContent = state.data.site.issueLabel;
211
- document.querySelector("#home-eyebrow").textContent = state.data.site.eyebrow;
212
- document.querySelector("#home-title").textContent = state.data.site.heroTitle;
213
- document.querySelector("#start-label").textContent = state.data.site.startLabel;
214
- document.querySelector("#footer-title").textContent = state.data.site.title;
215
- document.querySelector("#home-deck").textContent = state.data.deck;
216
- document.querySelector("#article-count").textContent = `${state.data.articles.length} readings`;
217
- document.querySelector("#generated-at").textContent = `Updated ${new Date(
218
- state.data.generatedAt
219
- ).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
220
-
221
- const featured = state.data.articles.slice(0, 3);
222
- document.querySelector("#featured-grid").innerHTML = featured
223
- .map(
224
- (article) => `<article class="feature-card">
225
- <button data-article="${article.slug}">
226
- ${imageMarkup(article.image, "feature-card-image")}
227
- <p class="card-kicker">${escapeHtml(article.kicker)}</p>
228
- <h2>${escapeHtml(article.title)}</h2>
229
- <p>${escapeHtml(article.summary)}</p>
230
- </button>
231
- </article>`
232
- )
233
- .join("");
234
-
235
- document.querySelector("#contents-list").innerHTML = state.data.articles
236
- .map(
237
- (article) => `<article class="content-row">
238
- <span class="content-number">${String(article.order).padStart(2, "0")}</span>
239
- <button data-article="${article.slug}"><h3>${escapeHtml(article.title)}</h3></button>
240
- <p>${escapeHtml(article.summary)}</p>
241
- <span class="content-meta">${article.readingMinutes} min${
242
- state.completed.has(article.slug) ? " · Read" : ""
243
- }</span>
244
- </article>`
245
- )
246
- .join("");
247
- window.scrollTo({ top: 0, behavior: "instant" });
248
- }
249
-
250
- function renderDesktopJumps(article) {
251
- elements.desktopJumps.innerHTML = article.toc
252
- .map(
253
- (item) =>
254
- `<a class="level-${item.level}" href="#${item.id}" data-section-link="${item.id}">${escapeHtml(
255
- item.title
256
- )}</a>`
257
- )
258
- .join("");
259
- }
260
-
261
- function renderArticle(article) {
262
- state.current = article;
263
- elements.home.hidden = true;
264
- elements.article.hidden = false;
265
- document.title = `${article.title} — ${state.data.site.title}`;
266
- updateSocialMeta(article);
267
- document.querySelector("#article-category").textContent = article.category;
268
- document.querySelector("#article-kicker").textContent = article.kicker;
269
- document.querySelector("#article-title").textContent = article.title;
270
- document.querySelector("#article-summary").textContent = article.summary;
271
- document.querySelector("#article-reading-time").textContent = `${article.readingMinutes} min read`;
272
- document.querySelector("#article-word-count").textContent = `${article.wordCount.toLocaleString()} words`;
273
- document.querySelector("#article-revision").textContent = article.revision
274
- ? `Revision ${article.revision}`
275
- : article.sourceType || "Source material";
276
- const imageFigure = document.querySelector("#article-image");
277
- const imageElement = document.querySelector("#article-image-src");
278
- const imageCaption = document.querySelector("#article-image-caption");
279
- const imageCaptionText = document.querySelector("#article-image-caption-text");
280
- const imageSource = document.querySelector("#article-image-source");
281
- if (article.image) {
282
- imageFigure.hidden = false;
283
- imageElement.src = article.image.url;
284
- imageElement.alt = article.image.alt;
285
- imageCaptionText.textContent = [article.image.caption, article.image.credit].filter(Boolean).join(" — ");
286
- if (article.image.sourceUrl) {
287
- imageSource.href = article.image.sourceUrl;
288
- imageSource.hidden = false;
289
- } else {
290
- imageSource.removeAttribute("href");
291
- imageSource.hidden = true;
292
- }
293
- imageCaption.hidden = !imageCaptionText.textContent && !article.image.sourceUrl;
294
- } else {
295
- imageFigure.hidden = true;
296
- imageElement.removeAttribute("src");
297
- imageElement.alt = "";
298
- imageCaptionText.textContent = "";
299
- imageSource.removeAttribute("href");
300
- imageSource.hidden = true;
301
- }
302
- document.querySelector("#article-body").innerHTML = article.html;
303
- document.querySelector("#article-source").textContent = article.sourceLabel;
304
- renderDesktopJumps(article);
305
- renderAdjacent(article);
306
- requestAnimationFrame(() => {
307
- if (window.location.hash) {
308
- document.querySelector(window.location.hash)?.scrollIntoView();
309
- } else {
310
- window.scrollTo({ top: 0, behavior: "instant" });
311
- }
312
- });
313
- }
314
-
315
- function renderAdjacent(article) {
316
- const index = state.data.articles.findIndex((item) => item.slug === article.slug);
317
- const previous = state.data.articles[index - 1];
318
- const next = state.data.articles[index + 1];
319
- const previousButton = document.querySelector("#previous-article");
320
- const nextButton = document.querySelector("#next-article");
321
-
322
- previousButton.disabled = !previous;
323
- previousButton.dataset.article = previous?.slug || "";
324
- previousButton.innerHTML = previous
325
- ? `<span class="eyebrow">Previous</span><br>${escapeHtml(previous.title)}`
326
- : "Beginning of the edition";
327
-
328
- nextButton.disabled = !next;
329
- nextButton.dataset.article = next?.slug || "";
330
- nextButton.innerHTML = next
331
- ? `<span class="eyebrow">Next</span><br>${escapeHtml(next.title)}`
332
- : "End of the edition";
333
- }
334
-
335
- function renderRoute() {
336
- const slug = routeSlug();
337
- if (!slug) renderHome();
338
- else {
339
- const article = state.data.articles.find((item) => item.slug === slug);
340
- if (article) renderArticle(article);
341
- else navigate(null, "", true);
342
- }
343
- renderDrawer();
344
- }
345
-
346
- function updateReadingProgress() {
347
- if (!state.current) return;
348
- const article = document.querySelector("#article-body");
349
- const rect = article.getBoundingClientRect();
350
- const start = window.scrollY + rect.top - window.innerHeight * 0.18;
351
- const end = start + article.offsetHeight - window.innerHeight * 0.55;
352
- const percent = Math.max(0, Math.min(1, (window.scrollY - start) / Math.max(1, end - start)));
353
- elements.progress.style.width = `${percent * 100}%`;
354
- storageSet(`${state.storageKey}:progress:${state.current.slug}`, String(percent));
355
- if (percent > 0.94 && !state.completed.has(state.current.slug)) {
356
- state.completed.add(state.current.slug);
357
- storageSet(`${state.storageKey}:completed`, JSON.stringify([...state.completed]));
358
- }
359
-
360
- let active = null;
361
- for (const heading of document.querySelectorAll(".article-body h2, .article-body h3")) {
362
- if (heading.getBoundingClientRect().top <= 150) active = heading.id;
363
- }
364
- document.querySelectorAll("[data-section-link]").forEach((link) => {
365
- link.classList.toggle("active", link.dataset.sectionLink === active);
366
- });
367
- }
368
-
369
- document.addEventListener("click", (event) => {
370
- const articleButton = event.target.closest("[data-article]");
371
- if (articleButton?.dataset.article) {
372
- navigate(articleButton.dataset.article);
373
- closeDrawer();
374
- return;
375
- }
376
- const jump = event.target.closest("[data-section]");
377
- if (jump && state.current) {
378
- closeDrawer();
379
- history.replaceState({}, "", articleUrl(state.current.slug, jump.dataset.section));
380
- document.querySelector(`#${CSS.escape(jump.dataset.section)}`)?.scrollIntoView();
381
- }
382
- });
383
-
384
- elements.menuButton.addEventListener("click", openDrawer);
385
- elements.menuClose.addEventListener("click", closeDrawer);
386
- elements.backdrop.addEventListener("click", closeDrawer);
387
- elements.search.addEventListener("input", renderDrawer);
388
- document.querySelector("#home-button").addEventListener("click", () => navigate(null));
389
- document.querySelector("#footer-contents").addEventListener("click", () => navigate(null));
390
- document.querySelector("#article-category").addEventListener("click", () => navigate(null));
391
- document.querySelector("#start-reading").addEventListener("click", () =>
392
- navigate(state.data.articles[0].slug)
393
- );
394
- document.querySelector("#type-button").addEventListener("click", () => {
395
- state.typeIndex = (state.typeIndex + 1) % typeModes.length;
396
- setTypeMode();
397
- });
398
- document.querySelector("#theme-button").addEventListener("click", () => {
399
- setTheme(document.documentElement.dataset.theme === "evening" ? "day" : "evening");
400
- });
401
-
402
- window.addEventListener("popstate", renderRoute);
403
- window.addEventListener("scroll", updateReadingProgress, { passive: true });
404
- window.addEventListener("keydown", (event) => {
405
- if (event.key === "Escape" && document.body.classList.contains("drawer-open")) closeDrawer();
406
- if (event.key.toLowerCase() === "m" && !/input|textarea/i.test(event.target.tagName)) openDrawer();
407
- if (!state.current || /input|textarea/i.test(event.target.tagName)) return;
408
- const index = state.data.articles.findIndex((item) => item.slug === state.current.slug);
409
- if (event.key === "[" && index > 0) navigate(state.data.articles[index - 1].slug);
410
- if (event.key === "]" && index < state.data.articles.length - 1)
411
- navigate(state.data.articles[index + 1].slug);
412
- });
413
-
414
- async function boot() {
415
- const dataElement = document.querySelector("#reading-room-data");
416
- if (!dataElement?.textContent) throw new Error("Could not load the reading-room content.");
417
- state.data = JSON.parse(dataElement.textContent);
418
- state.storageKey = `editorial-reading-room:${state.data.site.slug}`;
419
- state.typeIndex = Number(storageGet(`${state.storageKey}:type`, "0"));
420
- state.completed = new Set(storageArray(`${state.storageKey}:completed`));
421
- const savedTheme = storageGet(`${state.storageKey}:theme`);
422
- if (savedTheme === "evening") setTheme("evening");
423
- setTypeMode();
424
- renderRoute();
425
- elements.loading.classList.add("loaded");
426
- document.querySelector("#main-content").focus({ preventScroll: true });
427
- }
428
-
429
- boot().catch((error) => {
430
- elements.loading.innerHTML = `<span class="loading-mark">!</span><p>${escapeHtml(
431
- error.message
432
- )}</p>`;
433
- });
@@ -1,139 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <meta
7
- name="description"
8
- content="A mobile-first editorial reading room."
9
- />
10
- <meta name="theme-color" content="#f5f1e8" />
11
- <!--__READING_ROOM_SOCIAL_META__-->
12
- <title>Editorial Reading Room</title>
13
- <style>/*__READING_ROOM_STYLES__*/</style>
14
- </head>
15
- <body>
16
- <a class="skip-link" href="#main-content">Skip to the article</a>
17
- <div class="reading-progress" aria-hidden="true">
18
- <span id="reading-progress-bar"></span>
19
- </div>
20
-
21
- <header class="site-header">
22
- <button
23
- class="menu-button"
24
- id="menu-button"
25
- type="button"
26
- aria-label="Open contents and jump links"
27
- aria-controls="navigation-drawer"
28
- aria-expanded="false"
29
- >
30
- <span></span><span></span><span></span>
31
- </button>
32
- <button class="masthead" id="home-button" type="button" aria-label="Return to contents">
33
- <span class="masthead-product" id="masthead-product">Reading Room</span>
34
- <span class="masthead-title" id="masthead-title">Field Notes</span>
35
- </button>
36
- <div class="header-tools">
37
- <button class="text-button" id="type-button" type="button" aria-label="Change text size">
38
- <span aria-hidden="true">A<span class="type-large">A</span></span>
39
- <span class="tool-label">Type</span>
40
- </button>
41
- <button class="text-button" id="theme-button" type="button" aria-label="Toggle evening mode">
42
- <span aria-hidden="true">◐</span>
43
- <span class="tool-label">Evening</span>
44
- </button>
45
- </div>
46
- </header>
47
-
48
- <aside class="navigation-drawer" id="navigation-drawer" aria-hidden="true">
49
- <div class="drawer-header">
50
- <p class="eyebrow">Reading desk</p>
51
- <button id="menu-close" type="button" aria-label="Close navigation">Close</button>
52
- </div>
53
- <label class="search-field">
54
- <span class="sr-only">Search the reading room</span>
55
- <input id="menu-search" type="search" placeholder="Find an article or section" />
56
- </label>
57
- <nav id="drawer-navigation" aria-label="Reading room contents"></nav>
58
- </aside>
59
- <button class="drawer-backdrop" id="drawer-backdrop" type="button" aria-label="Close navigation"></button>
60
-
61
- <main id="main-content" tabindex="-1">
62
- <section id="home-view" class="home-view" hidden>
63
- <div class="issue-line">
64
- <span id="edition-label">Reader’s edition</span>
65
- <span id="issue-label">Current edition</span>
66
- <span id="article-count"></span>
67
- </div>
68
- <section class="home-hero">
69
- <p class="eyebrow" id="home-eyebrow">A guided collection</p>
70
- <h1 id="home-title">Make complex material easy to explore.</h1>
71
- <p class="home-deck" id="home-deck"></p>
72
- <button class="primary-link" id="start-reading" type="button">
73
- <span id="start-label">Start reading</span> <span aria-hidden="true">→</span>
74
- </button>
75
- </section>
76
- <section class="featured-grid" id="featured-grid" aria-label="Featured reading"></section>
77
- <section class="contents-section">
78
- <div class="section-heading">
79
- <p class="eyebrow">The complete edition</p>
80
- <h2>Contents</h2>
81
- </div>
82
- <div id="contents-list" class="contents-list"></div>
83
- </section>
84
- </section>
85
-
86
- <section id="article-view" class="article-view" hidden>
87
- <div class="article-shell">
88
- <aside class="article-rail" aria-label="In this article">
89
- <p class="rail-label">In this article</p>
90
- <nav id="desktop-jump-links"></nav>
91
- </aside>
92
- <article>
93
- <header class="article-header">
94
- <button class="breadcrumb" id="article-category" type="button">Field Notes</button>
95
- <p class="article-kicker" id="article-kicker"></p>
96
- <h1 id="article-title"></h1>
97
- <p class="article-deck" id="article-summary"></p>
98
- <div class="article-meta">
99
- <span id="article-reading-time"></span>
100
- <span id="article-word-count"></span>
101
- <span id="article-revision"></span>
102
- </div>
103
- <figure class="article-image" id="article-image" hidden>
104
- <img id="article-image-src" alt="" />
105
- <figcaption id="article-image-caption">
106
- <span id="article-image-caption-text"></span>
107
- <a id="article-image-source" target="_blank" rel="noopener noreferrer">Image source</a>
108
- </figcaption>
109
- </figure>
110
- </header>
111
- <div class="article-body" id="article-body"></div>
112
- <footer class="article-source">
113
- <p class="eyebrow">Source record</p>
114
- <p id="article-source"></p>
115
- </footer>
116
- </article>
117
- </div>
118
- <nav class="next-previous" aria-label="Previous and next articles">
119
- <button id="previous-article" type="button"></button>
120
- <button id="next-article" type="button"></button>
121
- </nav>
122
- </section>
123
- </main>
124
-
125
- <footer class="site-footer">
126
- <span id="footer-title">Editorial Reading Room</span>
127
- <button id="footer-contents" type="button">Contents</button>
128
- <span id="generated-at"></span>
129
- </footer>
130
-
131
- <div class="loading-screen" id="loading-screen" role="status">
132
- <span class="loading-mark">M</span>
133
- <p>Preparing the reading room…</p>
134
- </div>
135
-
136
- <script id="reading-room-data" type="application/json">/*__READING_ROOM_DATA__*/</script>
137
- <script>/*__READING_ROOM_SCRIPT__*/</script>
138
- </body>
139
- </html>