hexo-theme-stellar 1.42.0 → 1.43.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 (122) hide show
  1. package/.claude/skills/stellar-theme-dev/SKILL.md +2 -0
  2. package/CHANGELOG.md +42 -0
  3. package/THIRD-PARTY-NOTICES.md +28 -0
  4. package/_config.yml +20 -10
  5. package/_data/icons.yml +10 -9
  6. package/languages/en.yml +11 -0
  7. package/languages/zh-CN.yml +11 -0
  8. package/languages/zh-TW.yml +11 -0
  9. package/layout/_partial/comments/artalk/layout.ejs +1 -1
  10. package/layout/_partial/comments/beaudar/layout.ejs +1 -1
  11. package/layout/_partial/comments/giscus/layout.ejs +1 -1
  12. package/layout/_partial/comments/twikoo/layout.ejs +1 -1
  13. package/layout/_partial/comments/utterances/layout.ejs +1 -1
  14. package/layout/_partial/comments/waline/layout.ejs +1 -1
  15. package/layout/_partial/components/collection-item.ejs +44 -0
  16. package/layout/_partial/components/collection.ejs +26 -0
  17. package/layout/_partial/components/widget-frame.ejs +35 -0
  18. package/layout/_partial/cover/wiki_cover.ejs +82 -26
  19. package/layout/_partial/dropdown.ejs +62 -0
  20. package/layout/_partial/head.ejs +9 -0
  21. package/layout/_partial/main/navbar/breadcrumb/blog.ejs +1 -1
  22. package/layout/_partial/main/post_list/archive_item.ejs +12 -0
  23. package/layout/_partial/main/post_list/topic_card.ejs +21 -9
  24. package/layout/_partial/main/post_list/wiki_card.ejs +49 -10
  25. package/layout/_partial/scripts/bootstrap.ejs +3 -1
  26. package/layout/_partial/scripts/defines.ejs +2 -8
  27. package/layout/_partial/scripts/lazyload.ejs +0 -3
  28. package/layout/_partial/scripts.ejs +1 -0
  29. package/layout/_partial/sidebar/index_leftbar.ejs +12 -2
  30. package/layout/_partial/sidebar/logo.ejs +1 -1
  31. package/layout/_partial/sidebar/menu.ejs +21 -23
  32. package/layout/_partial/widgets/components/linklist.ejs +23 -21
  33. package/layout/_partial/widgets/linklist.ejs +7 -12
  34. package/layout/_partial/widgets/recent.ejs +52 -41
  35. package/layout/_partial/widgets/related.ejs +47 -36
  36. package/layout/_partial/widgets/tagtree.ejs +70 -54
  37. package/layout/_partial/widgets/toc.ejs +37 -62
  38. package/layout/_partial/widgets/tree.ejs +38 -51
  39. package/layout/_plugins/index.ejs +25 -0
  40. package/layout/_plugins/mermaid.ejs +3 -4
  41. package/layout/archive.ejs +11 -20
  42. package/layout/collection-preview.ejs +60 -0
  43. package/layout/index_topic.ejs +12 -6
  44. package/layout/index_wiki.ejs +49 -3
  45. package/layout/layout.ejs +4 -3
  46. package/package.json +1 -1
  47. package/scripts/events/index.js +0 -2
  48. package/scripts/filters/lib/img_lazyload.js +184 -40
  49. package/scripts/generators/stellar-icons.js +1 -1
  50. package/scripts/lib/subtitle.js +5 -1
  51. package/scripts/tags/index.js +1 -0
  52. package/scripts/tags/lib/about.js +1 -1
  53. package/scripts/tags/lib/albums.js +1 -1
  54. package/scripts/tags/lib/banner.js +1 -1
  55. package/scripts/tags/lib/dropdown.js +64 -0
  56. package/scripts/tags/lib/friends.js +1 -1
  57. package/scripts/tags/lib/gallery.js +3 -3
  58. package/scripts/tags/lib/image.js +1 -1
  59. package/scripts/tags/lib/posters.js +1 -1
  60. package/scripts/tags/lib/sites.js +8 -4
  61. package/source/css/_common/base.styl +4 -4
  62. package/source/css/_common/blockquote.styl +2 -2
  63. package/source/css/_common/dropdown.styl +132 -0
  64. package/source/css/_common/html.styl +1 -1
  65. package/source/css/_common/loading.styl +5 -2
  66. package/source/css/_common/title.styl +8 -8
  67. package/source/css/_components/collection.styl +188 -0
  68. package/source/css/_components/layout.styl +1 -1
  69. package/source/css/_components/list.styl +44 -46
  70. package/source/css/_components/md.styl +3 -3
  71. package/source/css/_components/page-transition.styl +22 -0
  72. package/source/css/_components/pages/archives.styl +19 -18
  73. package/source/css/_components/pages/article-indent.styl +2 -2
  74. package/source/css/_components/pages/article-story.styl +9 -5
  75. package/source/css/_components/pages/collection-preview.styl +68 -0
  76. package/source/css/_components/pages/notebook.styl +11 -9
  77. package/source/css/_components/partial/article-footer.styl +7 -4
  78. package/source/css/_components/partial/cover.styl +280 -1
  79. package/source/css/_components/partial/related.styl +10 -6
  80. package/source/css/_components/sidebar/footer.styl +65 -12
  81. package/source/css/_components/sidebar/nav-area.styl +6 -1
  82. package/source/css/_components/sidebar/search.styl +6 -2
  83. package/source/css/_components/tag-plugins/chat.styl +1 -1
  84. package/source/css/_components/tag-plugins/folders.styl +4 -4
  85. package/source/css/_components/tag-plugins/folding.styl +3 -3
  86. package/source/css/_components/tag-plugins/hashtag.styl +2 -3
  87. package/source/css/_components/tag-plugins/mdrender.styl +1 -1
  88. package/source/css/_components/tag-plugins/note.styl +4 -4
  89. package/source/css/_components/tag-plugins/poetry.styl +5 -5
  90. package/source/css/_components/tag-plugins/quot.styl +1 -2
  91. package/source/css/_components/tag-plugins/quote.styl +1 -1
  92. package/source/css/_components/tag-plugins/tabs.styl +2 -2
  93. package/source/css/_components/tag-plugins/timeline.styl +4 -4
  94. package/source/css/_components/widgets/ghrepo.styl +4 -0
  95. package/source/css/_components/widgets/markdown.styl +1 -14
  96. package/source/css/_components/widgets/timeline.styl +2 -2
  97. package/source/css/_components/widgets/toc.styl +22 -20
  98. package/source/css/_components/widgets/widgets.styl +13 -1
  99. package/source/css/_components/wiki-card.styl +209 -0
  100. package/source/css/_custom.styl +22 -20
  101. package/source/css/_plugins/lazyload.styl +8 -5
  102. package/source/css/comments/utterances.styl +2 -2
  103. package/source/css/comments/waline.styl +24 -0
  104. package/source/css/plugins/mermaid.styl +31 -12
  105. package/source/js/icons.js +1 -1
  106. package/source/js/main.js +62 -6
  107. package/source/js/plugins/adaptive-text.js +46 -2
  108. package/source/js/plugins/dropdown.js +435 -0
  109. package/source/js/plugins/galaxy.js +423 -0
  110. package/source/js/search/algolia-search.js +1 -1
  111. package/source/js/search/local-search.js +1 -1
  112. package/source/js/services/ghinfo.js +31 -3
  113. package/source/js/services/siteinfo.js +26 -0
  114. package/source/js/services/sites.js +6 -2
  115. package/source/js/services.js +5 -2
  116. package/source/js/tagtree.js +7 -7
  117. package/source/js/utils.js +1 -1
  118. package/layout/_partial/widgets/components/link.ejs +0 -21
  119. package/source/css/_components/sidebar/menu.styl +0 -77
  120. package/source/css/_components/widgets/components.styl +0 -68
  121. package/source/css/_components/widgets/list.styl +0 -46
  122. package/source/css/_components/widgets/related.styl +0 -40
@@ -0,0 +1,435 @@
1
+ // 通用 dropdown 浮层:把打开的菜单挂到 body 下,避免被任意祖先容器裁剪。
2
+ (() => {
3
+ const selector = 'details.dropdown'
4
+ const gap = 8
5
+ const viewportPadding = 8
6
+ let layer = null
7
+ let active = null
8
+ let frame = null
9
+ let menuId = 0
10
+
11
+ function getLayer() {
12
+ if (layer && document.documentElement.contains(layer)) {
13
+ return layer
14
+ }
15
+ layer = document.createElement('div')
16
+ layer.className = 'dropdown-layer'
17
+ document.body.appendChild(layer)
18
+ return layer
19
+ }
20
+
21
+ function getDirectChild(parent, childSelector) {
22
+ for (const child of parent.children) {
23
+ if (child.matches(childSelector)) {
24
+ return child
25
+ }
26
+ }
27
+ return null
28
+ }
29
+
30
+ function getViewportSize() {
31
+ return {
32
+ width: document.documentElement.clientWidth || window.innerWidth,
33
+ height: document.documentElement.clientHeight || window.innerHeight
34
+ }
35
+ }
36
+
37
+ function getDirection(dropdown) {
38
+ const direction = dropdown.getAttribute('direction')
39
+ return direction === 'up' || direction === 'down' ? direction : 'auto'
40
+ }
41
+
42
+ function getAlign(dropdown) {
43
+ const align = dropdown.getAttribute('align')
44
+ return align === 'left' || align === 'right' ? align : 'auto'
45
+ }
46
+
47
+ function setMenuId(dropdown, menu) {
48
+ if (!menu.id) {
49
+ menuId += 1
50
+ menu.id = `stellar-dropdown-menu-${menuId}`
51
+ }
52
+ const trigger = getDirectChild(dropdown, '.dropdown-trigger')
53
+ if (trigger) {
54
+ trigger.setAttribute('aria-controls', menu.id)
55
+ }
56
+ }
57
+
58
+ function clearMenuPosition(menu) {
59
+ menu.style.removeProperty('top')
60
+ menu.style.removeProperty('left')
61
+ menu.style.removeProperty('max-height')
62
+ menu.style.removeProperty('visibility')
63
+ menu.style.removeProperty('opacity')
64
+ menu.removeAttribute('data-placement')
65
+ }
66
+
67
+ function hideMenuBeforeOpen(dropdown) {
68
+ const menu = getDirectChild(dropdown, '.dropdown-menu')
69
+ if (!menu || menu.classList.contains('dropdown-menu-portal')) {
70
+ return
71
+ }
72
+ menu.style.visibility = 'hidden'
73
+ menu.style.opacity = '0'
74
+ }
75
+
76
+ function isPointInRect(point, rect) {
77
+ return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom
78
+ }
79
+
80
+ function isPointInPolygon(point, polygon) {
81
+ let inside = false
82
+ for (let index = 0, previous = polygon.length - 1; index < polygon.length; previous = index++) {
83
+ const currentPoint = polygon[index]
84
+ const previousPoint = polygon[previous]
85
+ const intersects = currentPoint.y > point.y !== previousPoint.y > point.y &&
86
+ point.x < (previousPoint.x - currentPoint.x) * (point.y - currentPoint.y) /
87
+ (previousPoint.y - currentPoint.y) + currentPoint.x
88
+ if (intersects) {
89
+ inside = !inside
90
+ }
91
+ }
92
+ return inside
93
+ }
94
+
95
+ function updateBridge(state, triggerRect, menuRect, placement) {
96
+ const polygon = placement === 'up'
97
+ ? [
98
+ { x: triggerRect.left, y: triggerRect.top },
99
+ { x: menuRect.left, y: menuRect.bottom },
100
+ { x: menuRect.right, y: menuRect.bottom },
101
+ { x: triggerRect.right, y: triggerRect.top }
102
+ ]
103
+ : [
104
+ { x: triggerRect.left, y: triggerRect.bottom },
105
+ { x: triggerRect.right, y: triggerRect.bottom },
106
+ { x: menuRect.right, y: menuRect.top },
107
+ { x: menuRect.left, y: menuRect.top }
108
+ ]
109
+ const clipPath = `polygon(${polygon.map(point => `${Math.round(point.x)}px ${Math.round(point.y)}px`).join(', ')})`
110
+ state.bridge.__stellarDropdownPolygon = polygon
111
+ state.bridge.style.clipPath = clipPath
112
+ state.bridge.style.webkitClipPath = clipPath
113
+ }
114
+
115
+ function isPointerInsideState(state, event) {
116
+ const target = event.target
117
+ if (target && (state.dropdown.contains(target) || state.menu.contains(target) || state.bridge.contains(target))) {
118
+ return true
119
+ }
120
+ const point = { x: event.clientX, y: event.clientY }
121
+ if (isPointInRect(point, state.trigger.getBoundingClientRect()) || isPointInRect(point, state.menu.getBoundingClientRect())) {
122
+ return true
123
+ }
124
+ return isPointInPolygon(point, state.bridge.__stellarDropdownPolygon || [])
125
+ }
126
+
127
+ function closeState(state, focusTrigger) {
128
+ if (!state || active !== state) {
129
+ return
130
+ }
131
+ active = null
132
+ if (state.dropdown.open) {
133
+ state.dropdown.open = false
134
+ }
135
+ if (state.bridge.parentNode) {
136
+ state.bridge.remove()
137
+ }
138
+ state.menu.classList.remove('dropdown-menu-portal')
139
+ state.menu.classList.remove('dropdown-menu-visible')
140
+ state.menu.removeAttribute('data-portal')
141
+ clearMenuPosition(state.menu)
142
+ if (state.placeholder.parentNode) {
143
+ state.placeholder.parentNode.insertBefore(state.menu, state.placeholder)
144
+ state.placeholder.remove()
145
+ } else if (state.menu.parentNode) {
146
+ state.menu.remove()
147
+ }
148
+ state.dropdown.removeAttribute('data-dropdown-open')
149
+ if (focusTrigger && document.documentElement.contains(state.trigger)) {
150
+ state.trigger.focus()
151
+ }
152
+ }
153
+
154
+ function chooseVertical(dropdown, menuHeight, topSpace, bottomSpace) {
155
+ const direction = getDirection(dropdown)
156
+ if (direction === 'up') {
157
+ return 'up'
158
+ }
159
+ if (direction === 'down') {
160
+ return 'down'
161
+ }
162
+ if (menuHeight <= bottomSpace) {
163
+ return 'down'
164
+ }
165
+ if (menuHeight <= topSpace) {
166
+ return 'up'
167
+ }
168
+ return topSpace >= bottomSpace ? 'up' : 'down'
169
+ }
170
+
171
+ function chooseHorizontal(dropdown, rect, menuWidth, viewportWidth) {
172
+ const align = getAlign(dropdown)
173
+ const minLeft = viewportPadding
174
+ const maxLeft = Math.max(minLeft, viewportWidth - viewportPadding - menuWidth)
175
+ if (align === 'left') {
176
+ return Math.min(Math.max(rect.left, minLeft), maxLeft)
177
+ }
178
+ if (align === 'right') {
179
+ return Math.min(Math.max(rect.right - menuWidth, minLeft), maxLeft)
180
+ }
181
+ const leftAlignedLeft = rect.left
182
+ const rightAlignedLeft = rect.right - menuWidth
183
+ const leftFits = leftAlignedLeft >= minLeft && leftAlignedLeft <= maxLeft
184
+ const rightFits = rightAlignedLeft >= minLeft && rightAlignedLeft <= maxLeft
185
+ if (leftFits && rightFits) {
186
+ const rightSpace = viewportWidth - viewportPadding - rect.left
187
+ const leftSpace = rect.right - viewportPadding
188
+ return rightSpace >= leftSpace ? leftAlignedLeft : rightAlignedLeft
189
+ }
190
+ if (leftFits) {
191
+ return leftAlignedLeft
192
+ }
193
+ if (rightFits) {
194
+ return rightAlignedLeft
195
+ }
196
+ const preferLeft = viewportWidth - viewportPadding - rect.left >= rect.right - viewportPadding
197
+ const preferredLeft = preferLeft ? leftAlignedLeft : rightAlignedLeft
198
+ return Math.min(Math.max(preferredLeft, minLeft), maxLeft)
199
+ }
200
+
201
+ function positionActive() {
202
+ if (!active) {
203
+ return
204
+ }
205
+ const state = active
206
+ if (!document.documentElement.contains(state.trigger)) {
207
+ closeState(state, false)
208
+ return
209
+ }
210
+ const rect = state.trigger.getBoundingClientRect()
211
+ const viewport = getViewportSize()
212
+ if (rect.bottom <= 0 || rect.top >= viewport.height) {
213
+ closeState(state, false)
214
+ return
215
+ }
216
+
217
+ const topSpace = Math.max(0, rect.top - gap - viewportPadding)
218
+ const bottomSpace = Math.max(0, viewport.height - rect.bottom - gap - viewportPadding)
219
+ const menu = state.menu
220
+ menu.style.visibility = 'hidden'
221
+ menu.style.maxHeight = 'none'
222
+ menu.style.top = '0px'
223
+ menu.style.left = '0px'
224
+ const naturalRect = menu.getBoundingClientRect()
225
+ const vertical = chooseVertical(state.dropdown, naturalRect.height, topSpace, bottomSpace)
226
+ const availableHeight = vertical === 'up' ? topSpace : bottomSpace
227
+ menu.style.maxHeight = `${Math.max(1, Math.floor(availableHeight))}px`
228
+
229
+ const menuRect = menu.getBoundingClientRect()
230
+ const topLimit = viewportPadding
231
+ const bottomLimit = Math.max(topLimit, viewport.height - viewportPadding - menuRect.height)
232
+ const preferredTop = vertical === 'up'
233
+ ? rect.top - gap - menuRect.height
234
+ : rect.bottom + gap
235
+ const top = Math.min(Math.max(preferredTop, topLimit), bottomLimit)
236
+ const left = chooseHorizontal(state.dropdown, rect, menuRect.width, viewport.width)
237
+ menu.style.top = `${Math.round(top)}px`
238
+ menu.style.left = `${Math.round(left)}px`
239
+ menu.setAttribute('data-placement', vertical)
240
+ menu.style.removeProperty('visibility')
241
+ menu.style.removeProperty('opacity')
242
+ updateBridge(state, rect, menu.getBoundingClientRect(), vertical)
243
+ }
244
+
245
+ function schedulePosition() {
246
+ if (!active || frame !== null) {
247
+ return
248
+ }
249
+ frame = window.requestAnimationFrame(() => {
250
+ frame = null
251
+ positionActive()
252
+ })
253
+ }
254
+
255
+ function openDropdown(dropdown) {
256
+ const menu = getDirectChild(dropdown, '.dropdown-menu')
257
+ const trigger = getDirectChild(dropdown, '.dropdown-trigger')
258
+ if (!menu || !trigger) {
259
+ return
260
+ }
261
+ if (active && active.dropdown !== dropdown) {
262
+ closeState(active, false)
263
+ }
264
+ if (active && active.dropdown === dropdown) {
265
+ schedulePosition()
266
+ return
267
+ }
268
+
269
+ const placeholder = document.createComment('dropdown-menu')
270
+ const bridge = document.createElement('div')
271
+ bridge.className = 'dropdown-bridge'
272
+ if (!window.CSS || typeof window.CSS.supports !== 'function' ||
273
+ (!window.CSS.supports('clip-path', 'polygon(0 0, 1px 1px, 2px 2px)') &&
274
+ !window.CSS.supports('-webkit-clip-path', 'polygon(0 0, 1px 1px, 2px 2px)'))) {
275
+ bridge.style.pointerEvents = 'none'
276
+ }
277
+ menu.style.visibility = 'hidden'
278
+ menu.style.opacity = '0'
279
+ menu.parentNode.insertBefore(placeholder, menu)
280
+ const dropdownLayer = getLayer()
281
+ dropdownLayer.appendChild(bridge)
282
+ dropdownLayer.appendChild(menu)
283
+ setMenuId(dropdown, menu)
284
+ menu.classList.add('dropdown-menu-portal')
285
+ menu.setAttribute('data-portal', 'true')
286
+ dropdown.setAttribute('data-dropdown-open', 'true')
287
+ active = {
288
+ dropdown,
289
+ menu,
290
+ trigger,
291
+ placeholder,
292
+ bridge,
293
+ hoverMode: dropdown.__stellarDropdownHovering
294
+ }
295
+ positionActive()
296
+ if (!dropdown.open) {
297
+ dropdown.open = true
298
+ }
299
+ window.requestAnimationFrame(() => {
300
+ if (active && active.dropdown === dropdown) {
301
+ menu.classList.add('dropdown-menu-visible')
302
+ }
303
+ })
304
+ }
305
+
306
+ function bind(dropdown) {
307
+ if (dropdown.__stellarDropdownBound) {
308
+ return
309
+ }
310
+ dropdown.__stellarDropdownBound = true
311
+ dropdown.addEventListener('mouseenter', () => {
312
+ dropdown.__stellarDropdownHovering = true
313
+ if (active && active.dropdown === dropdown) {
314
+ active.hoverMode = true
315
+ }
316
+ if (!active || active.dropdown !== dropdown) {
317
+ openDropdown(dropdown)
318
+ }
319
+ })
320
+ const trigger = getDirectChild(dropdown, '.dropdown-trigger')
321
+ if (trigger) {
322
+ trigger.addEventListener('click', event => {
323
+ event.preventDefault()
324
+ if (!active || active.dropdown !== dropdown) {
325
+ openDropdown(dropdown)
326
+ }
327
+ })
328
+ trigger.addEventListener('keydown', event => {
329
+ if (event.key === 'Enter' || event.key === ' ') {
330
+ event.preventDefault()
331
+ if (active && active.dropdown === dropdown) {
332
+ closeState(active, false)
333
+ } else {
334
+ openDropdown(dropdown)
335
+ }
336
+ }
337
+ })
338
+ }
339
+ dropdown.addEventListener('mouseleave', () => {
340
+ dropdown.__stellarDropdownHovering = false
341
+ })
342
+ dropdown.addEventListener('toggle', () => {
343
+ if (dropdown.open) {
344
+ if (!active || active.dropdown !== dropdown) {
345
+ hideMenuBeforeOpen(dropdown)
346
+ openDropdown(dropdown)
347
+ } else {
348
+ schedulePosition()
349
+ }
350
+ } else if (active && active.dropdown === dropdown) {
351
+ closeState(active, false)
352
+ }
353
+ })
354
+ if (dropdown.open) {
355
+ openDropdown(dropdown)
356
+ }
357
+ dropdown.__stellarDropdownHovering = false
358
+ }
359
+
360
+ function bindTree(root) {
361
+ if (root.nodeType !== 1) {
362
+ return
363
+ }
364
+ if (root.matches(selector)) {
365
+ bind(root)
366
+ }
367
+ root.querySelectorAll(selector).forEach(bind)
368
+ }
369
+
370
+ function init() {
371
+ if (!document.body || !window.MutationObserver) {
372
+ return
373
+ }
374
+ bindTree(document.body)
375
+ const observer = new MutationObserver(records => {
376
+ records.forEach(record => {
377
+ record.addedNodes.forEach(bindTree)
378
+ record.removedNodes.forEach(node => {
379
+ if (active && node.nodeType === 1 && (node === active.dropdown || node.contains(active.dropdown))) {
380
+ closeState(active, false)
381
+ }
382
+ })
383
+ })
384
+ })
385
+ observer.observe(document.body, { childList: true, subtree: true })
386
+
387
+ document.addEventListener('click', event => {
388
+ if (!active) {
389
+ return
390
+ }
391
+ if (active.dropdown.contains(event.target) || active.menu.contains(event.target)) {
392
+ return
393
+ }
394
+ closeState(active, false)
395
+ }, true)
396
+
397
+ document.addEventListener('mousemove', event => {
398
+ if (!active || !active.hoverMode || isPointerInsideState(active, event)) {
399
+ return
400
+ }
401
+ closeState(active, false)
402
+ }, true)
403
+
404
+ document.addEventListener('keydown', event => {
405
+ if (!active) {
406
+ return
407
+ }
408
+ if (event.key === 'Escape') {
409
+ event.preventDefault()
410
+ closeState(active, true)
411
+ return
412
+ }
413
+ if (event.key === 'Tab' && document.activeElement === active.trigger && !event.shiftKey) {
414
+ const firstItem = active.menu.querySelector('a, button, [tabindex]:not([tabindex="-1"])')
415
+ if (firstItem) {
416
+ event.preventDefault()
417
+ firstItem.focus()
418
+ }
419
+ }
420
+ })
421
+
422
+ window.addEventListener('resize', schedulePosition)
423
+ window.addEventListener('scroll', schedulePosition, true)
424
+ if (window.visualViewport) {
425
+ window.visualViewport.addEventListener('resize', schedulePosition)
426
+ window.visualViewport.addEventListener('scroll', schedulePosition)
427
+ }
428
+ }
429
+
430
+ if (document.readyState === 'loading') {
431
+ document.addEventListener('DOMContentLoaded', init, { once: true })
432
+ } else {
433
+ init()
434
+ }
435
+ })()