@abreen/tada 1.11.2 → 1.13.2

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 (80) hide show
  1. package/README.md +65 -64
  2. package/bin/tada.ts +61 -66
  3. package/build/build-manifest.ts +14 -3
  4. package/build/bundle.ts +0 -1
  5. package/build/config-files.ts +183 -0
  6. package/build/config-loader.ts +159 -0
  7. package/build/copy.ts +0 -83
  8. package/build/generate-content-assets.ts +75 -160
  9. package/build/globals.ts +48 -89
  10. package/build/log.ts +27 -18
  11. package/build/pagefind.ts +33 -26
  12. package/build/pipeline.ts +98 -79
  13. package/build/reachability.ts +51 -248
  14. package/build/serve.ts +35 -6
  15. package/build/site-variables.ts +20 -11
  16. package/build/source-model.ts +400 -0
  17. package/build/{watch/records.ts → source-records.ts} +111 -49
  18. package/build/template-globals.ts +91 -0
  19. package/build/templates.ts +50 -40
  20. package/build/test-helpers.ts +15 -0
  21. package/build/toc-plugin.ts +5 -1
  22. package/build/types.d.ts +9 -1
  23. package/build/util.ts +8 -0
  24. package/build/utils/code.ts +6 -16
  25. package/build/utils/content-files.ts +57 -126
  26. package/build/utils/final-html.ts +239 -0
  27. package/build/utils/markdown.ts +237 -17
  28. package/build/utils/paths.ts +2 -1
  29. package/build/utils/render.ts +108 -99
  30. package/build/validate-config-links.ts +91 -14
  31. package/build/watch/assets.ts +0 -2
  32. package/build/watch/build-full.ts +14 -15
  33. package/build/watch/build-incremental.ts +15 -8
  34. package/build/watch/compiler-types.d.ts +6 -1
  35. package/build/watch/compiler.ts +27 -15
  36. package/build/watch/index.ts +2 -7
  37. package/build/watch/planner.ts +32 -16
  38. package/build/watch/reload.ts +4 -0
  39. package/build/watch/runtime.ts +29 -45
  40. package/build/watch/snapshot.ts +50 -402
  41. package/build/watch/validation.ts +6 -3
  42. package/build/watch-reachability-state.ts +16 -23
  43. package/build/watch-reload-client.ts +11 -4
  44. package/init/content/index.md +1 -1
  45. package/init/content/labs/02/index.md +61 -0
  46. package/init/content/labs/02/selection_sort.py +23 -0
  47. package/init/content/labs/index.md +4 -2
  48. package/init/content/lectures/01/index.md +1 -1
  49. package/init/content/markdown.md +1 -1
  50. package/package.json +3 -4
  51. package/src/code/index.ts +46 -18
  52. package/src/code.scss +2 -1
  53. package/src/globals.ts +47 -0
  54. package/src/index.ts +1 -0
  55. package/src/navigate/index.ts +2 -5
  56. package/src/navigate/lifecycle.ts +3 -1
  57. package/src/navigate/runtime.ts +6 -5
  58. package/src/page-update/index.ts +20 -16
  59. package/src/search/index.ts +53 -25
  60. package/src/search/style.scss +1 -1
  61. package/src/slides/index.ts +467 -0
  62. package/src/slides/style.scss +104 -0
  63. package/src/style.scss +8 -3
  64. package/src/timezone/index.ts +16 -20
  65. package/src/top/index.ts +2 -4
  66. package/src/trace/index.ts +91 -7
  67. package/src/trace/style.scss +1 -1
  68. package/src/util.ts +6 -2
  69. package/templates/_author.html +2 -2
  70. package/templates/_heading.html +1 -1
  71. package/templates/_nav.html +2 -2
  72. package/templates/_theme.scss +3 -3
  73. package/templates/_top.html +14 -14
  74. package/templates/default.html +8 -0
  75. package/types/dev.d.ts +1 -1
  76. package/types/site-variables.d.ts +4 -5
  77. package/watch/fs-commit.ts +63 -10
  78. package/build/apply-base-path-plugin.ts +0 -186
  79. package/build/content-watch.ts +0 -104
  80. package/build/validate-internal-links-plugin.ts +0 -207
@@ -0,0 +1,61 @@
1
+ ---
2
+ parent: ../index.html
3
+ parentLabel: Labs
4
+ title: Lab 2
5
+ author: alex
6
+ description: A page demonstrating slides mode.
7
+ slides: true
8
+ ---
9
+
10
+ ## Selection sort
11
+
12
+ Selection sort is a well-known comparison-based sorting algorithm.
13
+
14
+ - Divides the array into a sorted left part and an unsorted right part
15
+ - Repeatedly scans left-to-right, looking for the smallest element
16
+ - Swaps the smallest in the right part into correct position in the left part
17
+
18
+ ---
19
+
20
+ <%= renderTrace('selection_sort.py') %>
21
+
22
+ ---
23
+
24
+ ## Impressions
25
+
26
+ Selection sort's strength is its simplicity.
27
+
28
+ - `index_smallest()` always scans from `start` to the end
29
+ - It requires no additional memory (other than local variables)
30
+
31
+ However
32
+
33
+ - It has no `break` statements & can't return early
34
+ - No difference between best, average and worst cases
35
+
36
+ ---
37
+
38
+ ## Performance
39
+
40
+ - The outer loop always performs $n - 1$ iterations
41
+ - There are always $n - 1$ swaps
42
+ - At each value of `i`, `index_smallest()` iterates from `i + 1` to the end
43
+
44
+ ---
45
+
46
+ ## Comparisons
47
+
48
+ The innermost statement compares `nums[i]` and `nums[curr]`.
49
+
50
+ - when `i` is 0, the comparison is executed $n - 1$ times
51
+ - when `i` is 1, the innermost statement is executed $n - 2$ times
52
+ - $\dots$
53
+ - when `i` is $n - 3$, the innermost statement is executed two times
54
+ - when `i` is $n - 2$, the innermost statement is executed one time
55
+
56
+ Therefore the total number of comparisons is
57
+
58
+ $$
59
+ (n-1) + (n-2) + \dots + 1 = \sum_{i=1}^{n-1}i
60
+ $$
61
+
@@ -0,0 +1,23 @@
1
+ def swap(items, i, j):
2
+ temp = items[i]
3
+ items[i] = items[j]
4
+ items[j] = temp
5
+
6
+
7
+ def index_smallest(nums, start):
8
+ curr = start
9
+ for i in range(start + 1, len(nums)):
10
+ if nums[i] < nums[curr]:
11
+ curr = i
12
+
13
+ return curr
14
+
15
+
16
+ def selection(nums):
17
+ for i in range(len(nums) - 1):
18
+ swap(nums, i, index_smallest(nums, i))
19
+
20
+
21
+ input1 = [4, 9, 2, 10, 19, 5, 7]
22
+ selection(input1)
23
+ print(input1)
@@ -8,6 +8,8 @@ In each one-hour lab, we'll dive deeper and write code together, exploring how
8
8
  to apply the concepts we learned in lecture to solve real-world problems and
9
9
  write full-length programs.
10
10
 
11
- 0. [`VowelCounter` and user input](/labs/00/index.html)
11
+ 0. [`VowelCounter` and user input](00/index.html)
12
12
 
13
- 1. [Memory diagrams](/labs/01/index.html)
13
+ 1. [Memory diagrams](01/index.html)
14
+
15
+ 2. [Slides mode demo](02/index.html)
@@ -10,7 +10,7 @@ Here is a sample description of the topics covered at the first lecture.
10
10
 
11
11
  The value of `vars.foobar` is <%= vars.foobar %>.
12
12
 
13
- You can add any variables you want to the `site.dev.json` and `site.prod.json`
13
+ You can add any variables you want to the `site.dev.yaml` and `site.prod.yaml`
14
14
  files under the `"vars"` property and access them using Lodash template syntax
15
15
  from Markdown files, HTML files, and source code. (Lodash template subtitution
16
16
  only works in source code files whose extension is listed in
@@ -126,7 +126,7 @@ The syntax for links is unchanged from Presto. However,
126
126
  - external links (links whose domain is not one of `site.internalDomains`) are
127
127
  automatically given `target="_blank"` and special styling
128
128
 
129
- See `config/site.dev.json` and `config/site.prod.json` to change `site.basePath`
129
+ See `site.dev.yaml` and `site.prod.yaml` to change `site.basePath`
130
130
  and `site.internalDomains`.
131
131
 
132
132
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abreen/tada",
3
- "version": "1.11.2",
3
+ "version": "1.13.2",
4
4
  "type": "module",
5
5
  "description": "A static site generator",
6
6
  "license": "MIT",
@@ -38,6 +38,7 @@
38
38
  "lint:sass": "bun run scripts/lint-sass.ts",
39
39
  "typecheck": "tsc --noEmit",
40
40
  "format": "prettier --write \"**/*.{ts,js,scss,json}\"",
41
+ "test:unit": "bun run scripts/test.ts unit",
41
42
  "test:playwright": "bun run scripts/test.ts playwright",
42
43
  "test:functional": "bun run scripts/test.ts functional",
43
44
  "test:all": "bun run scripts/test.ts all",
@@ -79,8 +80,7 @@
79
80
  "sass": "^1.99.0",
80
81
  "sharp": "^0.34.5",
81
82
  "shiki": "^4.0.2",
82
- "string-strip-html": "^8.5.0",
83
- "ws": "^8.20.0"
83
+ "string-strip-html": "^8.5.0"
84
84
  },
85
85
  "lint-staged": {
86
86
  "*.{ts,js}": "prettier --write",
@@ -110,7 +110,6 @@
110
110
  "@types/istanbul-reports": "^3.0.4",
111
111
  "@types/lodash": "^4.17.24",
112
112
  "@types/markdown-it-container": "^4.0.0",
113
- "@types/ws": "^8.18.1",
114
113
  "eslint": "^10.2.1",
115
114
  "husky": "^9.1.7",
116
115
  "istanbul-lib-coverage": "^3.2.2",
package/src/code/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { globals } from '../globals';
2
+
1
3
  export default async function mount(
2
4
  window: Window,
3
5
  ): Promise<void | (() => void)> {
@@ -6,7 +8,17 @@ export default async function mount(
6
8
  return;
7
9
  }
8
10
 
9
- document.addEventListener('copy', (e: ClipboardEvent) => {
11
+ const codeBodyEl = document.querySelector<HTMLElement>('.code-body');
12
+ const scrollbarEl = document.querySelector<HTMLElement>('.code-scrollbar');
13
+ if (!codeBodyEl || !scrollbarEl) {
14
+ return;
15
+ }
16
+ const codeBody = codeBodyEl;
17
+ const scrollbar = scrollbarEl;
18
+
19
+ const cleanups: Array<() => void> = [];
20
+
21
+ const handleCopy = (e: ClipboardEvent) => {
10
22
  const selection = window.getSelection();
11
23
  if (!selection || selection.rangeCount === 0) {
12
24
  return;
@@ -54,19 +66,23 @@ export default async function mount(
54
66
 
55
67
  e.clipboardData!.setData('text/plain', lines.join('\n'));
56
68
  e.preventDefault();
69
+ };
70
+ document.addEventListener('copy', handleCopy);
71
+ cleanups.push(() => {
72
+ document.removeEventListener('copy', handleCopy);
57
73
  });
58
74
 
59
75
  const downloadLink = document.querySelector<HTMLAnchorElement>(
60
76
  '.file-header a[download]',
61
77
  );
62
78
  if (downloadLink && 'showSaveFilePicker' in window) {
63
- downloadLink.addEventListener('click', async (e: MouseEvent) => {
79
+ const handleDownloadClick = async (e: MouseEvent) => {
64
80
  e.preventDefault();
65
81
  try {
66
82
  const handle = await window.showSaveFilePicker!({
67
83
  suggestedName: downloadLink.download,
68
84
  });
69
- const response = await fetch(downloadLink.href);
85
+ const response = await globals.fetch(downloadLink.href);
70
86
  const writable = await handle.createWritable();
71
87
  await writable.write(await response.blob());
72
88
  await writable.close();
@@ -75,47 +91,59 @@ export default async function mount(
75
91
  throw err;
76
92
  }
77
93
  }
94
+ };
95
+ downloadLink.addEventListener('click', handleDownloadClick);
96
+ cleanups.push(() => {
97
+ downloadLink.removeEventListener('click', handleDownloadClick);
78
98
  });
79
99
  }
80
100
 
81
- const codeBody = document.querySelector<HTMLElement>('.code-body');
82
- const scrollbar = document.querySelector<HTMLElement>('.code-scrollbar');
83
- if (!codeBody || !scrollbar) {
84
- return;
85
- }
86
-
87
101
  const inner = scrollbar.firstElementChild as HTMLElement;
88
102
  let syncing = false;
89
103
 
90
104
  function updateScrollbar(): void {
91
- const hasOverflow = codeBody!.scrollWidth > codeBody!.clientWidth;
92
- scrollbar!.style.display = hasOverflow ? '' : 'none';
93
- inner.style.width = codeBody!.scrollWidth + 'px';
105
+ const hasOverflow = codeBody.scrollWidth > codeBody.clientWidth;
106
+ scrollbar.style.display = hasOverflow ? '' : 'none';
107
+ inner.style.width = codeBody.scrollWidth + 'px';
94
108
  }
95
109
 
96
- codeBody.addEventListener('scroll', () => {
110
+ const handleCodeBodyScroll = () => {
97
111
  if (syncing) {
98
112
  return;
99
113
  }
100
114
  syncing = true;
101
- scrollbar!.scrollLeft = codeBody!.scrollLeft;
115
+ scrollbar.scrollLeft = codeBody.scrollLeft;
102
116
  syncing = false;
117
+ };
118
+ codeBody.addEventListener('scroll', handleCodeBodyScroll);
119
+ cleanups.push(() => {
120
+ codeBody.removeEventListener('scroll', handleCodeBodyScroll);
103
121
  });
104
122
 
105
- scrollbar.addEventListener('scroll', () => {
123
+ const handleScrollbarScroll = () => {
106
124
  if (syncing) {
107
125
  return;
108
126
  }
109
127
  syncing = true;
110
- codeBody!.scrollLeft = scrollbar!.scrollLeft;
128
+ codeBody.scrollLeft = scrollbar.scrollLeft;
111
129
  syncing = false;
130
+ };
131
+ scrollbar.addEventListener('scroll', handleScrollbarScroll);
132
+ cleanups.push(() => {
133
+ scrollbar.removeEventListener('scroll', handleScrollbarScroll);
112
134
  });
113
135
 
114
- const resizeObserver = new ResizeObserver(updateScrollbar);
136
+ const resizeObserver = globals.createResizeObserver(updateScrollbar);
115
137
  resizeObserver.observe(codeBody);
116
138
  updateScrollbar();
139
+ cleanups.push(() => {
140
+ resizeObserver.disconnect();
141
+ });
117
142
 
118
143
  return () => {
119
- resizeObserver.disconnect();
144
+ cleanups
145
+ .splice(0)
146
+ .reverse()
147
+ .forEach(cleanup => cleanup());
120
148
  };
121
149
  }
package/src/code.scss CHANGED
@@ -4,7 +4,8 @@ body.code {
4
4
  --code-gutter-width: 4rem;
5
5
  }
6
6
 
7
- .file-header {
7
+ .file-header,
8
+ .slides-header {
8
9
  display: flex;
9
10
  gap: var(--gap);
10
11
  align-items: center;
package/src/globals.ts ADDED
@@ -0,0 +1,47 @@
1
+ type FetchFunction = (
2
+ input: RequestInfo | URL,
3
+ init?: RequestInit,
4
+ ) => Promise<Response>;
5
+
6
+ type ImportModuleFunction = (specifier: string) => Promise<unknown>;
7
+ type LocationMutationFunction = (window: Window, value: string) => void;
8
+
9
+ export interface Globals {
10
+ replaceLocation: LocationMutationFunction;
11
+ createResizeObserver: (
12
+ callback: ResizeObserverCallback,
13
+ ) => Pick<ResizeObserver, 'disconnect' | 'observe'>;
14
+ fetch: FetchFunction;
15
+ importModule: ImportModuleFunction;
16
+ isDocumentHidden: (document: Document) => boolean;
17
+ now: () => number;
18
+ setLocationHash: LocationMutationFunction;
19
+ setLocationHref: LocationMutationFunction;
20
+ }
21
+
22
+ export const globals: Globals = {
23
+ replaceLocation(window, value) {
24
+ window.location.replace(value);
25
+ },
26
+ createResizeObserver(callback) {
27
+ return new ResizeObserver(callback);
28
+ },
29
+ fetch(input: RequestInfo | URL, init?: RequestInit) {
30
+ return fetch(input, init);
31
+ },
32
+ importModule(specifier) {
33
+ return import(specifier);
34
+ },
35
+ isDocumentHidden(document) {
36
+ return document.hidden;
37
+ },
38
+ now() {
39
+ return Date.now();
40
+ },
41
+ setLocationHash(window, value) {
42
+ window.location.hash = value;
43
+ },
44
+ setLocationHref(window, value) {
45
+ window.location.href = value;
46
+ },
47
+ };
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import './header/style.scss';
8
8
  import './print/style.scss';
9
9
  import './question/style.scss';
10
10
  import './search/style.scss';
11
+ import './slides/style.scss';
11
12
  import './timezone/style.scss';
12
13
  import './toc/style.scss';
13
14
  import './trace/style.scss';
@@ -12,6 +12,7 @@ import {
12
12
  setCurrentPath,
13
13
  setHistoryIndex,
14
14
  } from './runtime';
15
+ import { globals } from '../globals';
15
16
 
16
17
  function findAnchor(event: MouseEvent): HTMLAnchorElement | null {
17
18
  const target = event.target as HTMLElement | null;
@@ -77,11 +78,7 @@ export default function mountNavigate(window: Window): () => void {
77
78
  clearSearch(window.document);
78
79
  closeHeaderDetails(window.document);
79
80
  if (url.hash) {
80
- // location.hash assignment performs a real fragment navigation:
81
- // updates :target, fires hashchange (which the TOC listens for),
82
- // creates a new history entry, and scrolls to the element.
83
- // pushState would do none of those things.
84
- window.location.hash = url.hash.slice(1);
81
+ globals.setLocationHash(window, url.hash);
85
82
  }
86
83
  return;
87
84
  }
@@ -5,6 +5,7 @@ import mountTimeZone from '../timezone';
5
5
  import mountCode from '../code';
6
6
  import mountTrace from '../trace';
7
7
  import mountPrint from '../print';
8
+ import mountSlides from '../slides';
8
9
  import { scheduleTask } from '../util';
9
10
 
10
11
  const PER_PAGE_COMPONENTS: Record<
@@ -17,6 +18,7 @@ const PER_PAGE_COMPONENTS: Record<
17
18
  timeZone: mountTimeZone,
18
19
  code: mountCode,
19
20
  trace: mountTrace,
21
+ slides: mountSlides,
20
22
  print: mountPrint,
21
23
  };
22
24
 
@@ -39,7 +41,7 @@ export async function mountPerPageComponents(
39
41
  }
40
42
  resolve();
41
43
  } catch (err) {
42
- if (typeof __IS_DEV__ !== 'undefined' && __IS_DEV__) {
44
+ if (__IS_DEV__) {
43
45
  console.error(`Failed to mount ${name} component:`, String(err));
44
46
  }
45
47
  reject();
@@ -1,4 +1,5 @@
1
1
  import { mountPerPageComponents, teardownPerPageComponents } from './lifecycle';
2
+ import { globals } from '../globals';
2
3
 
3
4
  export const NAVIGATION_EVENT = 'tada:navigation';
4
5
 
@@ -196,19 +197,19 @@ export async function navigateToUrl(
196
197
 
197
198
  let response: Response;
198
199
  try {
199
- response = await fetch(url, { signal: controller.signal });
200
+ response = await globals.fetch(url, { signal: controller.signal });
200
201
  } catch (err: unknown) {
201
202
  header?.classList.remove('loading');
202
203
  if (err instanceof Error && err.name === 'AbortError') {
203
204
  return;
204
205
  }
205
- window.location.href = url;
206
+ globals.setLocationHref(window, url);
206
207
  return;
207
208
  }
208
209
 
209
210
  if (!response.ok) {
210
211
  header?.classList.remove('loading');
211
- window.location.href = url;
212
+ globals.setLocationHref(window, url);
212
213
  return;
213
214
  }
214
215
 
@@ -226,7 +227,7 @@ export async function navigateToUrl(
226
227
  .querySelector('meta[name="generator"]')
227
228
  ?.getAttribute('content');
228
229
  if (!currentGenerator || !newGenerator || newGenerator !== currentGenerator) {
229
- window.location.href = url;
230
+ globals.setLocationHref(window, url);
230
231
  return;
231
232
  }
232
233
 
@@ -246,7 +247,7 @@ export async function navigateToUrl(
246
247
 
247
248
  if (typeof scrollTarget === 'string') {
248
249
  const urlWithHash = parsed.pathname + parsed.search + '#' + scrollTarget;
249
- window.location.replace(urlWithHash);
250
+ globals.replaceLocation(window, urlWithHash);
250
251
  window.history.replaceState({ navIndex: historyIndex }, '', urlWithHash);
251
252
  } else if (typeof scrollTarget === 'number') {
252
253
  window.scrollTo({ top: scrollTarget });
@@ -7,6 +7,7 @@ import {
7
7
  type ResponseValidators,
8
8
  } from '../validators';
9
9
  import { NAVIGATION_EVENT, refreshCurrentPage } from '../navigate/runtime';
10
+ import { globals } from '../globals';
10
11
 
11
12
  const POLL_INTERVAL_MS = 600_000;
12
13
 
@@ -96,7 +97,7 @@ export function mountPageUpdate(
96
97
 
97
98
  function scheduleNextCheck() {
98
99
  clearTimer();
99
- if (document.hidden) {
100
+ if (globals.isDocumentHidden(document)) {
100
101
  return;
101
102
  }
102
103
  timer = window.setTimeout(() => {
@@ -107,17 +108,20 @@ export function mountPageUpdate(
107
108
  async function fetchValidators(
108
109
  path: string,
109
110
  ): Promise<ResponseValidators | null> {
110
- if (typeof __IS_DEV__ !== 'undefined' && __IS_DEV__) {
111
+ if (__IS_DEV__) {
111
112
  console.log(
112
- `[page-update] HEAD ${path} visibility=${document.visibilityState} focus=${document.hasFocus()}`,
113
+ `[page-update] HEAD ${path} hidden=${globals.isDocumentHidden(document)} visibility=${document.visibilityState} focus=${document.hasFocus()}`,
113
114
  );
114
115
  }
115
116
 
116
- const res = await fetch(path, { method: 'HEAD', cache: 'no-cache' });
117
+ const res = await globals.fetch(path, {
118
+ method: 'HEAD',
119
+ cache: 'no-cache',
120
+ });
117
121
 
118
- if (typeof __IS_DEV__ !== 'undefined' && __IS_DEV__) {
122
+ if (__IS_DEV__) {
119
123
  console.log(
120
- `[page-update] HEAD ${path} -> ${res.status} etag=${res.headers.get('ETag')} last-modified=${res.headers.get('Last-Modified')} visibility=${document.visibilityState} focus=${document.hasFocus()}`,
124
+ `[page-update] HEAD ${path} -> ${res.status} etag=${res.headers.get('ETag')} last-modified=${res.headers.get('Last-Modified')} hidden=${globals.isDocumentHidden(document)} visibility=${document.visibilityState} focus=${document.hasFocus()}`,
121
125
  );
122
126
  }
123
127
 
@@ -170,7 +174,7 @@ export function mountPageUpdate(
170
174
  // best-effort
171
175
  } finally {
172
176
  checkInFlight = false;
173
- if (rerunAfterCurrentCheck && !document.hidden) {
177
+ if (rerunAfterCurrentCheck && !globals.isDocumentHidden(document)) {
174
178
  rerunAfterCurrentCheck = false;
175
179
  shouldRerun = true;
176
180
  } else {
@@ -191,20 +195,20 @@ export function mountPageUpdate(
191
195
  hasBaseline = false;
192
196
  hideToast();
193
197
  clearTimer();
194
- if (!document.hidden) {
198
+ if (!globals.isDocumentHidden(document)) {
195
199
  void checkForUpdate();
196
200
  }
197
201
  }
198
202
 
199
203
  function handleVisibilityChange() {
200
- if (typeof __IS_DEV__ !== 'undefined' && __IS_DEV__) {
204
+ if (__IS_DEV__) {
201
205
  console.log(
202
- `[page-update] visibilitychange hidden=${document.hidden} visibility=${document.visibilityState} focus=${document.hasFocus()}`,
206
+ `[page-update] visibilitychange hidden=${globals.isDocumentHidden(document)} visibility=${document.visibilityState} focus=${document.hasFocus()}`,
203
207
  );
204
208
  }
205
209
 
206
210
  clearTimer();
207
- if (document.hidden) {
211
+ if (globals.isDocumentHidden(document)) {
208
212
  return;
209
213
  }
210
214
 
@@ -212,7 +216,7 @@ export function mountPageUpdate(
212
216
  }
213
217
 
214
218
  function handleNavigation() {
215
- if (typeof __IS_DEV__ !== 'undefined' && __IS_DEV__) {
219
+ if (__IS_DEV__) {
216
220
  console.log(
217
221
  `[page-update] navigation path=${window.location.pathname} visibility=${document.visibilityState} focus=${document.hasFocus()}`,
218
222
  );
@@ -227,7 +231,7 @@ export function mountPageUpdate(
227
231
  }
228
232
 
229
233
  function handleWindowFocus() {
230
- if (typeof __IS_DEV__ !== 'undefined' && __IS_DEV__) {
234
+ if (__IS_DEV__) {
231
235
  console.log(
232
236
  `[page-update] window focus visibility=${document.visibilityState} focus=${document.hasFocus()}`,
233
237
  );
@@ -235,7 +239,7 @@ export function mountPageUpdate(
235
239
  }
236
240
 
237
241
  function handleWindowBlur() {
238
- if (typeof __IS_DEV__ !== 'undefined' && __IS_DEV__) {
242
+ if (__IS_DEV__) {
239
243
  console.log(
240
244
  `[page-update] window blur visibility=${document.visibilityState} focus=${document.hasFocus()}`,
241
245
  );
@@ -253,8 +257,8 @@ export function mountPageUpdate(
253
257
  window.addEventListener('blur', handleWindowBlur);
254
258
  window.addEventListener(NAVIGATION_EVENT, handleNavigation);
255
259
 
256
- if (!document.hidden) {
257
- if (typeof __IS_DEV__ !== 'undefined' && __IS_DEV__) {
260
+ if (!globals.isDocumentHidden(document)) {
261
+ if (__IS_DEV__) {
258
262
  console.log(
259
263
  `[page-update] mount path=${currentPath} interval=${pollIntervalMs} visibility=${document.visibilityState} focus=${document.hasFocus()}`,
260
264
  );