one 1.6.10 → 1.6.12
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.
- package/devtools/devtools.mjs +322 -69
- package/dist/cjs/cli/buildPage.cjs +40 -12
- package/dist/cjs/cli/buildPage.js +43 -11
- package/dist/cjs/cli/buildPage.js.map +1 -1
- package/dist/cjs/cli/buildPage.native.js +41 -12
- package/dist/cjs/cli/buildPage.native.js.map +1 -1
- package/dist/cjs/createHandleRequest.cjs +24 -1
- package/dist/cjs/createHandleRequest.js +18 -1
- package/dist/cjs/createHandleRequest.js.map +1 -1
- package/dist/cjs/createHandleRequest.native.js +24 -1
- package/dist/cjs/createHandleRequest.native.js.map +1 -1
- package/dist/cjs/router/router.cjs +10 -3
- package/dist/cjs/router/router.js +9 -3
- package/dist/cjs/router/router.js.map +1 -1
- package/dist/cjs/router/router.native.js +7 -0
- package/dist/cjs/router/router.native.js.map +1 -1
- package/dist/cjs/server/oneServe.cjs +1 -0
- package/dist/cjs/server/oneServe.js +2 -0
- package/dist/cjs/server/oneServe.js.map +1 -1
- package/dist/cjs/server/oneServe.native.js +1 -0
- package/dist/cjs/server/oneServe.native.js.map +1 -1
- package/dist/cjs/useLoader.cjs +31 -0
- package/dist/cjs/useLoader.js +31 -1
- package/dist/cjs/useLoader.js.map +1 -1
- package/dist/cjs/useLoader.native.js +46 -0
- package/dist/cjs/useLoader.native.js.map +1 -1
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.cjs +1 -1
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.js +2 -1
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.js.map +1 -1
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js +1 -1
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
- package/dist/esm/cli/buildPage.js +43 -11
- package/dist/esm/cli/buildPage.js.map +1 -1
- package/dist/esm/cli/buildPage.mjs +40 -12
- package/dist/esm/cli/buildPage.mjs.map +1 -1
- package/dist/esm/cli/buildPage.native.js +41 -12
- package/dist/esm/cli/buildPage.native.js.map +1 -1
- package/dist/esm/createHandleRequest.js +18 -1
- package/dist/esm/createHandleRequest.js.map +1 -1
- package/dist/esm/createHandleRequest.mjs +24 -1
- package/dist/esm/createHandleRequest.mjs.map +1 -1
- package/dist/esm/createHandleRequest.native.js +24 -1
- package/dist/esm/createHandleRequest.native.js.map +1 -1
- package/dist/esm/router/router.js +9 -3
- package/dist/esm/router/router.js.map +1 -1
- package/dist/esm/router/router.mjs +10 -3
- package/dist/esm/router/router.mjs.map +1 -1
- package/dist/esm/router/router.native.js +7 -0
- package/dist/esm/router/router.native.js.map +1 -1
- package/dist/esm/server/oneServe.js +2 -0
- package/dist/esm/server/oneServe.js.map +1 -1
- package/dist/esm/server/oneServe.mjs +1 -0
- package/dist/esm/server/oneServe.mjs.map +1 -1
- package/dist/esm/server/oneServe.native.js +1 -0
- package/dist/esm/server/oneServe.native.js.map +1 -1
- package/dist/esm/useLoader.js +31 -0
- package/dist/esm/useLoader.js.map +1 -1
- package/dist/esm/useLoader.mjs +31 -0
- package/dist/esm/useLoader.mjs.map +1 -1
- package/dist/esm/useLoader.native.js +46 -0
- package/dist/esm/useLoader.native.js.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.js +2 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.js.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
- package/package.json +9 -9
- package/src/cli/buildPage.ts +67 -15
- package/src/createHandleRequest.ts +27 -1
- package/src/router/router.ts +29 -0
- package/src/server/oneServe.ts +7 -0
- package/src/useLoader.ts +58 -0
- package/src/vite/plugins/fileSystemRouterPlugin.tsx +7 -0
- package/types/cli/buildPage.d.ts.map +1 -1
- package/types/createHandleRequest.d.ts.map +1 -1
- package/types/router/router.d.ts.map +1 -1
- package/types/server/oneServe.d.ts.map +1 -1
- package/types/useLoader.d.ts.map +1 -1
- package/types/vite/plugins/fileSystemRouterPlugin.d.ts.map +1 -1
package/devtools/devtools.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// One DevTools - All panels in one draggable window
|
|
2
|
-
// Alt+Space opens
|
|
2
|
+
// Alt+Space opens searchable hot menu
|
|
3
3
|
|
|
4
4
|
;(function () {
|
|
5
5
|
try {
|
|
@@ -14,10 +14,132 @@
|
|
|
14
14
|
const panelPos = { x: 20, y: 20 }
|
|
15
15
|
let snappedEdge = { h: null, v: null }
|
|
16
16
|
let removalObserver = null
|
|
17
|
+
let selectedIndex = 0
|
|
18
|
+
let filteredItems = []
|
|
17
19
|
|
|
18
20
|
const LOGO_SVG =
|
|
19
21
|
'<svg width="24" height="24" viewBox="0 0 100 100"><circle cx="50" cy="50" r="48" fill="#FCD34D" stroke="#222" stroke-width="2"/><circle cx="50" cy="35" r="16" fill="white"/><text x="50" y="41" text-anchor="middle" font-family="system-ui" font-size="16" font-weight="bold" fill="#222">1</text></svg>'
|
|
20
22
|
|
|
23
|
+
const allItems = [
|
|
24
|
+
{ id: 'seo', name: 'SEO Preview', type: 'panel', category: 'Panels' },
|
|
25
|
+
{ id: 'route', name: 'Route Info', type: 'panel', category: 'Panels' },
|
|
26
|
+
{ id: 'loader', name: 'Loader Timing', type: 'panel', category: 'Panels' },
|
|
27
|
+
{ id: 'errors', name: 'Errors', type: 'panel', category: 'Panels' },
|
|
28
|
+
{ id: 'inspect', name: 'Inspect Source', type: 'tool', category: 'Panels' },
|
|
29
|
+
{ id: 'clear-cookies', name: 'Clear Cookies', type: 'action', category: 'Clear Data' },
|
|
30
|
+
{
|
|
31
|
+
id: 'clear-localstorage',
|
|
32
|
+
name: 'Clear localStorage',
|
|
33
|
+
type: 'action',
|
|
34
|
+
category: 'Clear Data',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'clear-sessionstorage',
|
|
38
|
+
name: 'Clear sessionStorage',
|
|
39
|
+
type: 'action',
|
|
40
|
+
category: 'Clear Data',
|
|
41
|
+
},
|
|
42
|
+
{ id: 'clear-indexeddb', name: 'Clear IndexedDB', type: 'action', category: 'Clear Data' },
|
|
43
|
+
{
|
|
44
|
+
id: 'clear-caches',
|
|
45
|
+
name: 'Clear Cache Storage',
|
|
46
|
+
type: 'action',
|
|
47
|
+
category: 'Clear Data',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'clear-sw',
|
|
51
|
+
name: 'Clear Service Workers',
|
|
52
|
+
type: 'action',
|
|
53
|
+
category: 'Clear Data',
|
|
54
|
+
},
|
|
55
|
+
{ id: 'clear-all', name: 'Clear All', type: 'action', category: 'Clear Data' },
|
|
56
|
+
{ id: 'reload', name: 'Reload Page', type: 'action', category: 'Actions' },
|
|
57
|
+
{ id: 'hard-reload', name: 'Hard Reload', type: 'action', category: 'Actions' },
|
|
58
|
+
{ id: 'copy-url', name: 'Copy Current URL', type: 'action', category: 'Actions' },
|
|
59
|
+
{ id: 'copy-route', name: 'Copy Route Info', type: 'action', category: 'Actions' },
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
const itemActions = {
|
|
63
|
+
'clear-cookies': async () => {
|
|
64
|
+
document.cookie.split(';').forEach((cookie) => {
|
|
65
|
+
const name = (cookie.split('=')[0] || '').trim()
|
|
66
|
+
if (!name) return
|
|
67
|
+
const d = location.hostname
|
|
68
|
+
document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/'
|
|
69
|
+
document.cookie =
|
|
70
|
+
name + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;domain=' + d
|
|
71
|
+
document.cookie =
|
|
72
|
+
name + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;domain=.' + d
|
|
73
|
+
})
|
|
74
|
+
},
|
|
75
|
+
'clear-localstorage': async () => {
|
|
76
|
+
localStorage.clear()
|
|
77
|
+
},
|
|
78
|
+
'clear-sessionstorage': async () => {
|
|
79
|
+
sessionStorage.clear()
|
|
80
|
+
},
|
|
81
|
+
'clear-indexeddb': async () => {
|
|
82
|
+
if ('indexedDB' in window && typeof indexedDB.databases === 'function') {
|
|
83
|
+
const dbs = await indexedDB.databases()
|
|
84
|
+
await Promise.all(
|
|
85
|
+
dbs.map((db) =>
|
|
86
|
+
db.name
|
|
87
|
+
? new Promise((resolve) => {
|
|
88
|
+
const req = indexedDB.deleteDatabase(db.name)
|
|
89
|
+
req.onsuccess = resolve
|
|
90
|
+
req.onerror = resolve
|
|
91
|
+
req.onblocked = resolve
|
|
92
|
+
})
|
|
93
|
+
: Promise.resolve()
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
'clear-caches': async () => {
|
|
99
|
+
if ('caches' in window) {
|
|
100
|
+
const names = await caches.keys()
|
|
101
|
+
await Promise.all(names.map((n) => caches.delete(n)))
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
'clear-sw': async () => {
|
|
105
|
+
if ('serviceWorker' in navigator) {
|
|
106
|
+
const regs = await navigator.serviceWorker.getRegistrations()
|
|
107
|
+
await Promise.all(regs.map((r) => r.unregister()))
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
'clear-all': async () => {
|
|
111
|
+
await itemActions['clear-cookies']()
|
|
112
|
+
await itemActions['clear-localstorage']()
|
|
113
|
+
await itemActions['clear-sessionstorage']()
|
|
114
|
+
await itemActions['clear-indexeddb']()
|
|
115
|
+
await itemActions['clear-caches']()
|
|
116
|
+
await itemActions['clear-sw']()
|
|
117
|
+
},
|
|
118
|
+
reload: async () => {
|
|
119
|
+
location.reload()
|
|
120
|
+
},
|
|
121
|
+
'hard-reload': async () => {
|
|
122
|
+
if ('caches' in window) {
|
|
123
|
+
const names = await caches.keys()
|
|
124
|
+
await Promise.all(names.map((n) => caches.delete(n)))
|
|
125
|
+
}
|
|
126
|
+
location.reload()
|
|
127
|
+
},
|
|
128
|
+
'copy-url': async () => {
|
|
129
|
+
await navigator.clipboard.writeText(location.href)
|
|
130
|
+
},
|
|
131
|
+
'copy-route': async () => {
|
|
132
|
+
const dt = window.__oneDevtools || {}
|
|
133
|
+
const info = {
|
|
134
|
+
pathname: location.pathname,
|
|
135
|
+
search: location.search,
|
|
136
|
+
hash: location.hash,
|
|
137
|
+
params: dt.routeInfo?.params || {},
|
|
138
|
+
}
|
|
139
|
+
await navigator.clipboard.writeText(JSON.stringify(info, null, 2))
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
|
|
21
143
|
function escapeHtml(str) {
|
|
22
144
|
if (!str) return ''
|
|
23
145
|
return String(str)
|
|
@@ -39,15 +161,23 @@
|
|
|
39
161
|
dialog::backdrop { background: transparent; }
|
|
40
162
|
#spotlight-dialog::backdrop { background: rgba(0,0,0,0.3); backdrop-filter: blur(8px); }
|
|
41
163
|
.spotlight { display: flex; align-items: center; justify-content: center; position: fixed; inset: 0; }
|
|
42
|
-
.spotlight-box { background: #1a1a1a; border-radius: 12px; width:
|
|
43
|
-
.spotlight-header { display: flex; align-items: center; padding: 12px
|
|
164
|
+
.spotlight-box { background: #1a1a1a; border-radius: 12px; width: 340px; max-width: 90vw; overflow: hidden; box-shadow: 0 16px 48px rgba(0,0,0,0.5); display: flex; flex-direction: column; }
|
|
165
|
+
.spotlight-header { display: flex; align-items: center; padding: 12px 14px; border-bottom: 1px solid #252525; gap: 10px; flex-shrink: 0; }
|
|
44
166
|
.spotlight-header svg { width: 20px; height: 20px; flex-shrink: 0; }
|
|
45
|
-
.spotlight-
|
|
46
|
-
.spotlight-
|
|
47
|
-
.spotlight-
|
|
48
|
-
.spotlight-
|
|
49
|
-
.spotlight-
|
|
50
|
-
.spotlight-
|
|
167
|
+
.spotlight-search { flex: 1; background: transparent; border: none; color: #eee; font: 14px system-ui, sans-serif; outline: none; min-width: 0; }
|
|
168
|
+
.spotlight-search::placeholder { color: #555; }
|
|
169
|
+
.spotlight-items { max-height: 354px; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #333 transparent; }
|
|
170
|
+
.spotlight-items::-webkit-scrollbar { width: 4px; }
|
|
171
|
+
.spotlight-items::-webkit-scrollbar-track { background: transparent; }
|
|
172
|
+
.spotlight-items::-webkit-scrollbar-thumb { background: #333; border-radius: 2px; }
|
|
173
|
+
.spotlight-category { font: 10px system-ui, sans-serif; color: #505050; text-transform: uppercase; letter-spacing: 0.5px; padding: 10px 16px 4px; user-select: none; }
|
|
174
|
+
.spotlight-item { display: flex; align-items: center; padding: 10px 16px; color: #aaa; font: 13px system-ui, sans-serif; cursor: pointer; transition: background 0.08s; }
|
|
175
|
+
.spotlight-item:hover, .spotlight-item.selected { background: #252525; color: #fff; }
|
|
176
|
+
.spotlight-item .name { flex: 1; }
|
|
177
|
+
.spotlight-item .status { font-size: 11px; color: #666; margin-left: 8px; }
|
|
178
|
+
.spotlight-item .status.ok { color: #4ade80; }
|
|
179
|
+
.spotlight-item .status.err { color: #f87171; }
|
|
180
|
+
.spotlight-empty { text-align: center; color: #444; padding: 24px; font: 13px system-ui, sans-serif; }
|
|
51
181
|
.panel-dialog { position: fixed; margin: 0; inset: unset; z-index: 2147483647; }
|
|
52
182
|
.panel-dialog::backdrop { display: none; }
|
|
53
183
|
.panel { width: 420px; max-width: calc(100vw - 40px); max-height: calc(100vh - 40px); background: #161616; border-radius: 10px; box-shadow: 0 12px 40px rgba(0,0,0,0.4); display: flex; flex-direction: column; overflow: hidden; font: 13px system-ui, sans-serif; color: #ccc; }
|
|
@@ -90,7 +220,15 @@
|
|
|
90
220
|
shadow.innerHTML =
|
|
91
221
|
'<style>' +
|
|
92
222
|
css +
|
|
93
|
-
'</style
|
|
223
|
+
'</style>' +
|
|
224
|
+
'<dialog id="spotlight-dialog"><div class="spotlight"><div class="spotlight-box" id="spotlight-box">' +
|
|
225
|
+
'<div class="spotlight-header">' +
|
|
226
|
+
LOGO_SVG +
|
|
227
|
+
'<input type="text" class="spotlight-search" id="spotlight-search" placeholder="Search actions..." autocomplete="off" spellcheck="false" />' +
|
|
228
|
+
'</div>' +
|
|
229
|
+
'<div class="spotlight-items" id="spotlight-items"></div>' +
|
|
230
|
+
'</div></div></dialog>' +
|
|
231
|
+
'<dialog class="panel-dialog" id="panel-dialog"><div class="panel" id="panel"></div></dialog>'
|
|
94
232
|
document.body.appendChild(host)
|
|
95
233
|
|
|
96
234
|
setupSpotlight()
|
|
@@ -121,51 +259,180 @@
|
|
|
121
259
|
removalObserver.observe(document.body, { childList: true })
|
|
122
260
|
}
|
|
123
261
|
|
|
262
|
+
function fuzzyScore(raw, query) {
|
|
263
|
+
// light fuzzy: prefer matching at word starts
|
|
264
|
+
const text = raw.toLowerCase().replace(/\s+/g, '')
|
|
265
|
+
const words = raw.toLowerCase().split(/\s+/)
|
|
266
|
+
const wordStarts = new Set()
|
|
267
|
+
let pos = 0
|
|
268
|
+
for (const w of words) {
|
|
269
|
+
wordStarts.add(pos)
|
|
270
|
+
pos += w.length
|
|
271
|
+
}
|
|
272
|
+
let ti = 0
|
|
273
|
+
let score = 0
|
|
274
|
+
for (let qi = 0; qi < query.length; qi++) {
|
|
275
|
+
const ch = query[qi]
|
|
276
|
+
// scan for first match, but prefer word-start match
|
|
277
|
+
let first = -1
|
|
278
|
+
for (let j = ti; j < text.length; j++) {
|
|
279
|
+
if (text[j] === ch) {
|
|
280
|
+
if (wordStarts.has(j)) { first = j; break }
|
|
281
|
+
if (first === -1) first = j
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (first === -1) return -1
|
|
285
|
+
score += wordStarts.has(first) ? 10 : 1
|
|
286
|
+
ti = first + 1
|
|
287
|
+
}
|
|
288
|
+
return score
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function renderSpotlightItems(query) {
|
|
292
|
+
const q = (query || '').toLowerCase().replace(/\s+/g, '')
|
|
293
|
+
const scored = []
|
|
294
|
+
for (const item of allItems) {
|
|
295
|
+
if (!q) {
|
|
296
|
+
scored.push({ item, score: 0 })
|
|
297
|
+
continue
|
|
298
|
+
}
|
|
299
|
+
const ns = fuzzyScore(item.name, q)
|
|
300
|
+
const cs = fuzzyScore(item.category, q)
|
|
301
|
+
const best = Math.max(ns, cs)
|
|
302
|
+
if (best >= 0) scored.push({ item, score: best })
|
|
303
|
+
}
|
|
304
|
+
scored.sort((a, b) => b.score - a.score)
|
|
305
|
+
filteredItems = scored.map((s) => s.item)
|
|
306
|
+
selectedIndex = filteredItems.length > 0 ? 0 : -1
|
|
307
|
+
|
|
308
|
+
const container = shadow.getElementById('spotlight-items')
|
|
309
|
+
if (!filteredItems.length) {
|
|
310
|
+
container.innerHTML = '<div class="spotlight-empty">No matching actions</div>'
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
let html = ''
|
|
315
|
+
let lastCategory = ''
|
|
316
|
+
filteredItems.forEach((item, index) => {
|
|
317
|
+
if (item.category !== lastCategory) {
|
|
318
|
+
lastCategory = item.category
|
|
319
|
+
html +=
|
|
320
|
+
'<div class="spotlight-category">' + escapeHtml(item.category) + '</div>'
|
|
321
|
+
}
|
|
322
|
+
html +=
|
|
323
|
+
'<div class="spotlight-item' +
|
|
324
|
+
(index === selectedIndex ? ' selected' : '') +
|
|
325
|
+
'" data-id="' +
|
|
326
|
+
item.id +
|
|
327
|
+
'" data-index="' +
|
|
328
|
+
index +
|
|
329
|
+
'"><span class="name">' +
|
|
330
|
+
escapeHtml(item.name) +
|
|
331
|
+
'</span><span class="status"></span></div>'
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
container.innerHTML = html
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function updateSelection() {
|
|
338
|
+
if (!shadow) return
|
|
339
|
+
shadow.querySelectorAll('.spotlight-item').forEach((el) => {
|
|
340
|
+
const idx = parseInt(el.dataset.index)
|
|
341
|
+
el.classList.toggle('selected', idx === selectedIndex)
|
|
342
|
+
})
|
|
343
|
+
const selected = shadow.querySelector('.spotlight-item.selected')
|
|
344
|
+
if (selected) selected.scrollIntoView({ block: 'nearest' })
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function handleItemClick(itemId) {
|
|
348
|
+
const item = allItems.find((i) => i.id === itemId)
|
|
349
|
+
if (!item) return
|
|
350
|
+
|
|
351
|
+
if (item.type === 'panel') {
|
|
352
|
+
hideSpotlight()
|
|
353
|
+
activeTab = item.id
|
|
354
|
+
showPanel()
|
|
355
|
+
return
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (item.id === 'inspect') {
|
|
359
|
+
hideSpotlight()
|
|
360
|
+
window.__oneSourceInspector?.activate()
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const actionFn = itemActions[item.id]
|
|
365
|
+
if (!actionFn) return
|
|
366
|
+
|
|
367
|
+
const el = shadow.querySelector('.spotlight-item[data-id="' + item.id + '"]')
|
|
368
|
+
const statusEl = el?.querySelector('.status')
|
|
369
|
+
if (statusEl) statusEl.textContent = '...'
|
|
370
|
+
|
|
371
|
+
actionFn()
|
|
372
|
+
.then(() => {
|
|
373
|
+
if (statusEl) {
|
|
374
|
+
statusEl.textContent = '\u2713'
|
|
375
|
+
statusEl.className = 'status ok'
|
|
376
|
+
}
|
|
377
|
+
setTimeout(hideSpotlight, 500)
|
|
378
|
+
})
|
|
379
|
+
.catch((err) => {
|
|
380
|
+
if (statusEl) {
|
|
381
|
+
statusEl.textContent = '\u2717'
|
|
382
|
+
statusEl.className = 'status err'
|
|
383
|
+
}
|
|
384
|
+
console.error('[One DevTools]', err)
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
|
|
124
388
|
function setupSpotlight() {
|
|
125
389
|
spotlightDialog = shadow.getElementById('spotlight-dialog')
|
|
126
390
|
const box = shadow.getElementById('spotlight-box')
|
|
391
|
+
const searchInput = shadow.getElementById('spotlight-search')
|
|
392
|
+
const itemsContainer = shadow.getElementById('spotlight-items')
|
|
127
393
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
{ id: 'loader', name: 'Loader Timing', key: '⌥L' },
|
|
132
|
-
{ id: 'errors', name: 'Errors', key: '⌥E' },
|
|
133
|
-
{ id: 'inspect', name: 'Inspect Source', key: '⌥I' },
|
|
134
|
-
]
|
|
394
|
+
searchInput.addEventListener('input', () => {
|
|
395
|
+
renderSpotlightItems(searchInput.value)
|
|
396
|
+
})
|
|
135
397
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
153
|
-
|
|
398
|
+
box.addEventListener('keydown', (e) => {
|
|
399
|
+
if (e.key === 'ArrowDown') {
|
|
400
|
+
e.preventDefault()
|
|
401
|
+
if (filteredItems.length) {
|
|
402
|
+
selectedIndex = (selectedIndex + 1) % filteredItems.length
|
|
403
|
+
updateSelection()
|
|
404
|
+
}
|
|
405
|
+
} else if (e.key === 'ArrowUp') {
|
|
406
|
+
e.preventDefault()
|
|
407
|
+
if (filteredItems.length) {
|
|
408
|
+
selectedIndex =
|
|
409
|
+
selectedIndex <= 0 ? filteredItems.length - 1 : selectedIndex - 1
|
|
410
|
+
updateSelection()
|
|
411
|
+
}
|
|
412
|
+
} else if (e.key === 'Enter') {
|
|
413
|
+
e.preventDefault()
|
|
414
|
+
if (selectedIndex >= 0 && selectedIndex < filteredItems.length) {
|
|
415
|
+
handleItemClick(filteredItems[selectedIndex].id)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
})
|
|
154
419
|
|
|
155
|
-
|
|
420
|
+
itemsContainer.addEventListener('mousemove', (e) => {
|
|
156
421
|
const item = e.target.closest('.spotlight-item')
|
|
157
422
|
if (item) {
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
} else {
|
|
163
|
-
activeTab = tab
|
|
164
|
-
showPanel()
|
|
423
|
+
const idx = parseInt(item.dataset.index)
|
|
424
|
+
if (!isNaN(idx) && idx !== selectedIndex) {
|
|
425
|
+
selectedIndex = idx
|
|
426
|
+
updateSelection()
|
|
165
427
|
}
|
|
166
428
|
}
|
|
167
429
|
})
|
|
168
430
|
|
|
431
|
+
itemsContainer.addEventListener('click', (e) => {
|
|
432
|
+
const item = e.target.closest('.spotlight-item')
|
|
433
|
+
if (item) handleItemClick(item.dataset.id)
|
|
434
|
+
})
|
|
435
|
+
|
|
169
436
|
spotlightDialog.addEventListener('click', (e) => {
|
|
170
437
|
if (e.target === spotlightDialog) spotlightDialog.close()
|
|
171
438
|
})
|
|
@@ -177,7 +444,7 @@
|
|
|
177
444
|
panel.innerHTML =
|
|
178
445
|
'<div class="panel-header" id="panel-header">' +
|
|
179
446
|
LOGO_SVG +
|
|
180
|
-
'<span class="panel-title">DevTools</span><button class="panel-close" id="panel-close"
|
|
447
|
+
'<span class="panel-title">DevTools</span><button class="panel-close" id="panel-close">\u00d7</button></div><div class="tabs" id="tabs"><button class="tab active" data-tab="seo">SEO</button><button class="tab" data-tab="route">Route</button><button class="tab" data-tab="loader">Loader</button><button class="tab" data-tab="errors">Errors</button></div><div class="content" id="content"></div>'
|
|
181
448
|
|
|
182
449
|
shadow.getElementById('panel-close').addEventListener('click', hidePanel)
|
|
183
450
|
|
|
@@ -263,36 +530,18 @@
|
|
|
263
530
|
function setupKeyboard() {
|
|
264
531
|
if (keyboardSetup) return
|
|
265
532
|
keyboardSetup = true
|
|
266
|
-
// use capture: false so app handlers can preventDefault first
|
|
267
533
|
document.addEventListener(
|
|
268
534
|
'keydown',
|
|
269
535
|
(e) => {
|
|
270
|
-
// respect other handlers and browser shortcuts
|
|
271
536
|
if (e.defaultPrevented || e.metaKey || e.ctrlKey) return
|
|
272
537
|
|
|
273
538
|
if (e.altKey && e.code === 'Space') {
|
|
274
539
|
e.preventDefault()
|
|
275
540
|
toggleSpotlight()
|
|
276
|
-
} else if (e.altKey) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
window.__oneSourceInspector?.activate()
|
|
281
|
-
return
|
|
282
|
-
}
|
|
283
|
-
const tabMap = {
|
|
284
|
-
KeyS: 'seo',
|
|
285
|
-
KeyR: 'route',
|
|
286
|
-
KeyL: 'loader',
|
|
287
|
-
KeyE: 'errors',
|
|
288
|
-
}
|
|
289
|
-
const tab = tabMap[e.code]
|
|
290
|
-
if (tab) {
|
|
291
|
-
e.preventDefault()
|
|
292
|
-
activeTab = tab
|
|
293
|
-
hideSpotlight()
|
|
294
|
-
showPanel()
|
|
295
|
-
}
|
|
541
|
+
} else if (e.altKey && e.code === 'KeyI') {
|
|
542
|
+
e.preventDefault()
|
|
543
|
+
hideSpotlight()
|
|
544
|
+
window.__oneSourceInspector?.activate()
|
|
296
545
|
} else if (e.code === 'Escape') {
|
|
297
546
|
if (spotlightDialog?.open) spotlightDialog.close()
|
|
298
547
|
else if (panelDialog?.open) panelDialog.close()
|
|
@@ -309,11 +558,15 @@
|
|
|
309
558
|
|
|
310
559
|
function showSpotlight() {
|
|
311
560
|
if (!host) createHost()
|
|
561
|
+
const searchInput = shadow.getElementById('spotlight-search')
|
|
562
|
+
searchInput.value = ''
|
|
563
|
+
renderSpotlightItems('')
|
|
312
564
|
spotlightDialog.showModal()
|
|
565
|
+
requestAnimationFrame(() => searchInput.focus())
|
|
313
566
|
}
|
|
314
567
|
|
|
315
568
|
function hideSpotlight() {
|
|
316
|
-
spotlightDialog.close()
|
|
569
|
+
if (spotlightDialog?.open) spotlightDialog.close()
|
|
317
570
|
}
|
|
318
571
|
|
|
319
572
|
function showPanel() {
|
|
@@ -391,7 +644,7 @@
|
|
|
391
644
|
issues.forEach((i) => {
|
|
392
645
|
html +=
|
|
393
646
|
'<div class="issue-item"><span class="issue-icon">' +
|
|
394
|
-
(i.type === 'error' ? '
|
|
647
|
+
(i.type === 'error' ? '\u2716' : '\u26a0') +
|
|
395
648
|
'</span><span>' +
|
|
396
649
|
escapeHtml(i.msg) +
|
|
397
650
|
'</span></div>'
|
|
@@ -591,7 +844,7 @@
|
|
|
591
844
|
const errors = devtools.errorHistory || []
|
|
592
845
|
|
|
593
846
|
if (!errors.length) {
|
|
594
|
-
return '<div class="empty"
|
|
847
|
+
return '<div class="empty">\u2713 No errors recorded</div>'
|
|
595
848
|
}
|
|
596
849
|
|
|
597
850
|
let html = ''
|
|
@@ -187,24 +187,34 @@ prefetchCSS()
|
|
|
187
187
|
loaderData: result.loaderData
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
|
-
|
|
190
|
+
recordTiming("layoutLoaders", performance.now() - t0), t0 = performance.now();
|
|
191
|
+
let loaderRedirectInfo = null;
|
|
192
|
+
if (exported.loader) {
|
|
191
193
|
try {
|
|
192
194
|
loaderData = (await exported.loader?.(loaderProps)) ?? null;
|
|
193
195
|
} catch (err) {
|
|
194
|
-
if (
|
|
196
|
+
if ((0, import_isResponse.isResponse)(err)) loaderRedirectInfo = extractRedirectInfo(err);else throw err;
|
|
195
197
|
}
|
|
196
|
-
if (clientJsPath) {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
if (!loaderRedirectInfo && loaderData && ((0, import_isResponse.isResponse)(loaderData) || loaderData instanceof Response || loaderData?.constructor?.name === "Response") && (loaderRedirectInfo = extractRedirectInfo(loaderData), loaderData = {}), clientJsPath) {
|
|
199
|
+
const loaderPartialPath = (0, import_node_path.join)(clientDir, urlPathToFilePath((0, import_cleanUrl.getLoaderPath)(path)));
|
|
200
|
+
if (loaderRedirectInfo) {
|
|
201
|
+
const redirectData = JSON.stringify({
|
|
202
|
+
__oneRedirect: loaderRedirectInfo.path,
|
|
203
|
+
__oneRedirectStatus: loaderRedirectInfo.status
|
|
204
|
+
});
|
|
205
|
+
await outputFile(loaderPartialPath, `export function loader(){return ${redirectData}}`), loaderPath = (0, import_cleanUrl.getLoaderPath)(path), loaderData = {};
|
|
206
|
+
} else {
|
|
207
|
+
const code = await readFile(clientJsPath, "utf-8"),
|
|
208
|
+
withLoader =
|
|
209
|
+
// super dirty to quickly make ssr loaders work until we have better
|
|
210
|
+
`
|
|
201
211
|
if (typeof document === 'undefined') globalThis.document = {}
|
|
202
212
|
` + (0, import_replaceLoader.replaceLoader)({
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
213
|
+
code,
|
|
214
|
+
loaderData
|
|
215
|
+
});
|
|
216
|
+
await outputFile(loaderPartialPath, withLoader), loaderPath = (0, import_cleanUrl.getLoaderPath)(path);
|
|
217
|
+
}
|
|
208
218
|
}
|
|
209
219
|
}
|
|
210
220
|
if (recordTiming("pageLoader", performance.now() - t0), matches.push({
|
|
@@ -303,6 +313,24 @@ async function getRender(serverEntry) {
|
|
|
303
313
|
function removeTrailingSlash(path) {
|
|
304
314
|
return path.endsWith("/") ? path.slice(0, path.length - 1) : path;
|
|
305
315
|
}
|
|
316
|
+
function extractRedirectInfo(response) {
|
|
317
|
+
if (response.status >= 300 && response.status < 400) {
|
|
318
|
+
const location = response.headers.get("location");
|
|
319
|
+
if (location) try {
|
|
320
|
+
const url = new URL(location);
|
|
321
|
+
return {
|
|
322
|
+
path: url.pathname + url.search + url.hash,
|
|
323
|
+
status: response.status
|
|
324
|
+
};
|
|
325
|
+
} catch {
|
|
326
|
+
return {
|
|
327
|
+
path: location,
|
|
328
|
+
status: response.status
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
306
334
|
function applyAfterLCPScriptLoad(html, preloads) {
|
|
307
335
|
html = html.replace(/<script\s+type="module"[^>]*async[^>]*><\/script>/gi, "");
|
|
308
336
|
const loaderScript = `
|
|
@@ -165,24 +165,40 @@ prefetchCSS()
|
|
|
165
165
|
loaderData: result.loaderData
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
|
-
|
|
168
|
+
recordTiming("layoutLoaders", performance.now() - t0), t0 = performance.now();
|
|
169
|
+
let loaderRedirectInfo = null;
|
|
170
|
+
if (exported.loader) {
|
|
169
171
|
try {
|
|
170
172
|
loaderData = await exported.loader?.(loaderProps) ?? null;
|
|
171
173
|
} catch (err) {
|
|
172
|
-
if (
|
|
174
|
+
if ((0, import_isResponse.isResponse)(err))
|
|
175
|
+
loaderRedirectInfo = extractRedirectInfo(err);
|
|
176
|
+
else
|
|
173
177
|
throw err;
|
|
174
178
|
}
|
|
175
|
-
if (clientJsPath) {
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
if (!loaderRedirectInfo && loaderData && ((0, import_isResponse.isResponse)(loaderData) || loaderData instanceof Response || loaderData?.constructor?.name === "Response") && (loaderRedirectInfo = extractRedirectInfo(loaderData), loaderData = {}), clientJsPath) {
|
|
180
|
+
const loaderPartialPath = (0, import_node_path.join)(clientDir, urlPathToFilePath((0, import_cleanUrl.getLoaderPath)(path)));
|
|
181
|
+
if (loaderRedirectInfo) {
|
|
182
|
+
const redirectData = JSON.stringify({
|
|
183
|
+
__oneRedirect: loaderRedirectInfo.path,
|
|
184
|
+
__oneRedirectStatus: loaderRedirectInfo.status
|
|
185
|
+
});
|
|
186
|
+
await outputFile(
|
|
187
|
+
loaderPartialPath,
|
|
188
|
+
`export function loader(){return ${redirectData}}`
|
|
189
|
+
), loaderPath = (0, import_cleanUrl.getLoaderPath)(path), loaderData = {};
|
|
190
|
+
} else {
|
|
191
|
+
const code = await readFile(clientJsPath, "utf-8"), withLoader = (
|
|
192
|
+
// super dirty to quickly make ssr loaders work until we have better
|
|
193
|
+
`
|
|
179
194
|
if (typeof document === 'undefined') globalThis.document = {}
|
|
180
195
|
` + (0, import_replaceLoader.replaceLoader)({
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
196
|
+
code,
|
|
197
|
+
loaderData
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
await outputFile(loaderPartialPath, withLoader), loaderPath = (0, import_cleanUrl.getLoaderPath)(path);
|
|
201
|
+
}
|
|
186
202
|
}
|
|
187
203
|
}
|
|
188
204
|
if (recordTiming("pageLoader", performance.now() - t0), matches.push({
|
|
@@ -280,6 +296,22 @@ async function getRender(serverEntry) {
|
|
|
280
296
|
function removeTrailingSlash(path) {
|
|
281
297
|
return path.endsWith("/") ? path.slice(0, path.length - 1) : path;
|
|
282
298
|
}
|
|
299
|
+
function extractRedirectInfo(response) {
|
|
300
|
+
if (response.status >= 300 && response.status < 400) {
|
|
301
|
+
const location = response.headers.get("location");
|
|
302
|
+
if (location)
|
|
303
|
+
try {
|
|
304
|
+
const url = new URL(location);
|
|
305
|
+
return {
|
|
306
|
+
path: url.pathname + url.search + url.hash,
|
|
307
|
+
status: response.status
|
|
308
|
+
};
|
|
309
|
+
} catch {
|
|
310
|
+
return { path: location, status: response.status };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
283
315
|
function applyAfterLCPScriptLoad(html, preloads) {
|
|
284
316
|
html = html.replace(/<script\s+type="module"[^>]*async[^>]*><\/script>/gi, "");
|
|
285
317
|
const loaderScript = `
|