pinokiod 7.3.10 → 7.3.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/kernel/bin/brew.js +12 -2
- package/kernel/bin/caddy.js +24 -20
- package/kernel/bin/huggingface.js +2 -2
- package/kernel/bin/setup.js +2 -3
- package/kernel/bin/uv.js +13 -6
- package/kernel/connect/index.js +5 -1
- package/kernel/connect/providers/huggingface/index.js +213 -75
- package/kernel/environment.js +16 -1
- package/kernel/router/localhost_home_router.js +7 -0
- package/kernel/shell.js +1 -5
- package/kernel/util.js +1 -0
- package/package.json +1 -1
- package/server/index.js +75 -33
- package/server/public/common.js +45 -71
- package/server/public/install.js +20 -2
- package/server/public/layout.js +1 -1
- package/server/public/style.css +1432 -511
- package/server/public/tab-link-popover.css +162 -18
- package/server/public/tab-link-popover.js +230 -21
- package/server/public/task-launcher.css +121 -84
- package/server/public/terminal-settings.js +121 -45
- package/server/public/universal-launcher.css +42 -33
- package/server/public/urldropdown.css +284 -0
- package/server/views/app.ejs +1608 -321
- package/server/views/autolaunch.ejs +4 -5
- package/server/views/checkpoints.ejs +223 -50
- package/server/views/connect/huggingface.ejs +406 -325
- package/server/views/connect.ejs +0 -1
- package/server/views/github.ejs +277 -324
- package/server/views/index.ejs +53 -1
- package/server/views/install.ejs +119 -19
- package/server/views/logs.ejs +10 -9
- package/server/views/net.ejs +341 -64
- package/server/views/network.ejs +85 -63
- package/server/views/partials/main_sidebar.ejs +237 -12
- package/server/views/plugins.ejs +141 -3
- package/server/views/settings.ejs +103 -7
- package/server/views/setup.ejs +0 -5
- package/server/views/skills.ejs +0 -1
- package/server/views/task_list.ejs +0 -1
- package/server/views/terminal.ejs +142 -1
- package/server/views/terminals.ejs +346 -6
- package/server/views/tools.ejs +828 -1691
- package/test/caddy-install.test.js +53 -0
- package/test/connect-setup.test.js +16 -0
- package/test/github-connection.test.js +1 -1
- package/test/huggingface-bin.test.js +4 -4
- package/test/huggingface-connect.test.js +73 -0
- package/test/main-sidebar.test.js +31 -0
- package/test/shell-run-template.test.js +5 -1
- package/test/uv-bin.test.js +29 -0
|
@@ -1,42 +1,124 @@
|
|
|
1
1
|
.tab-link-popover {
|
|
2
2
|
position: fixed;
|
|
3
3
|
display: none;
|
|
4
|
-
flex-direction: column;
|
|
5
|
-
gap: 4px;
|
|
6
|
-
padding: 8px 0;
|
|
7
|
-
border-radius: 10px;
|
|
8
|
-
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
9
|
-
background: rgba(255, 255, 255, 0.97);
|
|
10
|
-
box-shadow: 0 12px 32px -8px rgba(15, 23, 42, 0.25);
|
|
11
4
|
z-index: 9999;
|
|
5
|
+
box-sizing: border-box;
|
|
12
6
|
min-width: 240px;
|
|
13
7
|
max-width: 420px;
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
body.dark .tab-link-popover {
|
|
17
|
-
border-color: rgba(255, 255, 255, 0.08);
|
|
18
|
-
background: rgba(17, 24, 39, 0.95);
|
|
19
|
-
box-shadow: 0 12px 36px -12px rgba(15, 23, 42, 0.55);
|
|
8
|
+
color: var(--pinokio-sidebar-tab-active-color, rgba(15, 23, 42, 0.9));
|
|
20
9
|
}
|
|
21
10
|
.tab-link-popover.visible {
|
|
11
|
+
display: block;
|
|
12
|
+
}
|
|
13
|
+
.tab-link-popover--modal {
|
|
14
|
+
inset: 0;
|
|
15
|
+
z-index: 10000060;
|
|
16
|
+
min-width: 0;
|
|
17
|
+
max-width: none;
|
|
18
|
+
padding: 16px;
|
|
19
|
+
}
|
|
20
|
+
.tab-link-popover--modal.visible {
|
|
21
|
+
display: grid;
|
|
22
|
+
place-items: center;
|
|
23
|
+
}
|
|
24
|
+
body.tab-link-popover-open {
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
}
|
|
27
|
+
.tab-link-popover .tab-link-popover-backdrop {
|
|
28
|
+
display: none;
|
|
29
|
+
}
|
|
30
|
+
.tab-link-popover--modal .tab-link-popover-backdrop {
|
|
31
|
+
position: absolute;
|
|
32
|
+
inset: 0;
|
|
33
|
+
display: block;
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 100%;
|
|
36
|
+
appearance: none;
|
|
37
|
+
border: 0;
|
|
38
|
+
border-radius: 0;
|
|
39
|
+
padding: 0;
|
|
40
|
+
background: rgba(15, 23, 42, 0.28);
|
|
41
|
+
cursor: default;
|
|
42
|
+
}
|
|
43
|
+
body.dark .tab-link-popover--modal .tab-link-popover-backdrop {
|
|
44
|
+
background: rgba(0, 0, 0, 0.44);
|
|
45
|
+
}
|
|
46
|
+
.tab-link-popover .tab-link-popover-surface {
|
|
47
|
+
position: relative;
|
|
48
|
+
width: 100%;
|
|
49
|
+
min-width: inherit;
|
|
50
|
+
max-width: inherit;
|
|
51
|
+
max-height: calc(100vh - 24px);
|
|
22
52
|
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: 4px;
|
|
55
|
+
box-sizing: border-box;
|
|
56
|
+
overflow-y: auto;
|
|
57
|
+
padding: 8px 0;
|
|
58
|
+
border-radius: 8px;
|
|
59
|
+
border: 1px solid var(--pinokio-sidebar-tabbar-border, rgba(15, 23, 42, 0.08));
|
|
60
|
+
background: var(--pinokio-sidebar-tabbar-bg, rgba(255, 255, 255, 0.98));
|
|
61
|
+
color: inherit;
|
|
62
|
+
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.18);
|
|
63
|
+
}
|
|
64
|
+
body.dark .tab-link-popover .tab-link-popover-surface {
|
|
65
|
+
box-shadow: 0 18px 45px rgba(0, 0, 0, 0.42);
|
|
66
|
+
}
|
|
67
|
+
.tab-link-popover--modal .tab-link-popover-surface {
|
|
68
|
+
width: min(460px, calc(100vw - 32px));
|
|
69
|
+
min-width: 0;
|
|
70
|
+
max-width: 460px;
|
|
71
|
+
max-height: calc(100dvh - 32px);
|
|
72
|
+
padding-bottom: 10px;
|
|
23
73
|
}
|
|
24
74
|
.tab-link-popover .tab-link-popover-header {
|
|
75
|
+
display: grid;
|
|
76
|
+
grid-template-columns: minmax(0, 1fr) 28px;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: 8px;
|
|
79
|
+
padding: 10px 10px 6px 14px;
|
|
80
|
+
}
|
|
81
|
+
.tab-link-popover--anchored .tab-link-popover-header {
|
|
82
|
+
grid-template-columns: minmax(0, 1fr);
|
|
83
|
+
padding-right: 14px;
|
|
84
|
+
}
|
|
85
|
+
.tab-link-popover .tab-link-popover-title {
|
|
86
|
+
min-width: 0;
|
|
25
87
|
display: flex;
|
|
26
88
|
align-items: center;
|
|
27
89
|
gap: 8px;
|
|
28
|
-
padding: 10px 14px 6px;
|
|
29
90
|
font-size: 11px;
|
|
30
91
|
font-weight: 600;
|
|
31
92
|
letter-spacing: 0.05em;
|
|
32
93
|
text-transform: uppercase;
|
|
33
|
-
color: rgba(15, 23, 42, 0.55);
|
|
94
|
+
color: var(--pinokio-sidebar-action-muted, rgba(15, 23, 42, 0.55));
|
|
34
95
|
}
|
|
35
|
-
.tab-link-popover .tab-link-popover-
|
|
96
|
+
.tab-link-popover .tab-link-popover-title-icon i {
|
|
36
97
|
font-size: 12px;
|
|
37
98
|
}
|
|
38
|
-
|
|
39
|
-
|
|
99
|
+
.tab-link-popover .tab-link-popover-close {
|
|
100
|
+
width: 28px;
|
|
101
|
+
height: 28px;
|
|
102
|
+
min-width: 28px;
|
|
103
|
+
display: inline-flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
justify-content: center;
|
|
106
|
+
appearance: none;
|
|
107
|
+
border: 0;
|
|
108
|
+
border-radius: 6px;
|
|
109
|
+
background: transparent;
|
|
110
|
+
color: var(--pinokio-sidebar-icon-muted, rgba(31, 41, 55, 0.58));
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
113
|
+
}
|
|
114
|
+
.tab-link-popover--anchored .tab-link-popover-close {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
.tab-link-popover .tab-link-popover-close:hover,
|
|
118
|
+
.tab-link-popover .tab-link-popover-close:focus-visible {
|
|
119
|
+
background: var(--pinokio-sidebar-tab-hover, rgba(15, 23, 42, 0.06));
|
|
120
|
+
color: var(--pinokio-sidebar-icon-active, rgba(17, 24, 39, 0.84));
|
|
121
|
+
outline: none;
|
|
40
122
|
}
|
|
41
123
|
.tab-link-popover .tab-link-popover-item {
|
|
42
124
|
width: 100%;
|
|
@@ -115,6 +197,20 @@ body.dark .tab-link-popover .tab-link-popover-item--action .value {
|
|
|
115
197
|
flex: 0 0 auto;
|
|
116
198
|
margin-left: auto;
|
|
117
199
|
}
|
|
200
|
+
.tab-link-popover--modal .tab-link-popover-item.qr-inline {
|
|
201
|
+
align-items: center;
|
|
202
|
+
gap: 16px;
|
|
203
|
+
padding-top: 12px;
|
|
204
|
+
padding-bottom: 12px;
|
|
205
|
+
}
|
|
206
|
+
.tab-link-popover--modal .tab-link-popover-item.qr-inline .qr {
|
|
207
|
+
width: 148px;
|
|
208
|
+
height: 148px;
|
|
209
|
+
padding: 8px;
|
|
210
|
+
border-radius: 6px;
|
|
211
|
+
background: #ffffff;
|
|
212
|
+
box-sizing: border-box;
|
|
213
|
+
}
|
|
118
214
|
.tab-link-popover .tab-link-popover-item:hover,
|
|
119
215
|
.tab-link-popover .tab-link-popover-item:focus-visible {
|
|
120
216
|
background: rgba(15, 23, 42, 0.06);
|
|
@@ -201,6 +297,54 @@ body.dark .tab-link-popover .tab-link-popover-footer:hover,
|
|
|
201
297
|
body.dark .tab-link-popover .tab-link-popover-footer:focus-visible {
|
|
202
298
|
background: rgba(147, 197, 253, 0.35);
|
|
203
299
|
}
|
|
300
|
+
@media only screen and (max-width: 600px) {
|
|
301
|
+
.tab-link-popover--modal {
|
|
302
|
+
padding: 0;
|
|
303
|
+
}
|
|
304
|
+
.tab-link-popover--modal .tab-link-popover-backdrop {
|
|
305
|
+
background: transparent;
|
|
306
|
+
}
|
|
307
|
+
.tab-link-popover--modal .tab-link-popover-surface {
|
|
308
|
+
width: 100vw;
|
|
309
|
+
min-width: 0;
|
|
310
|
+
max-width: none;
|
|
311
|
+
min-height: 100dvh;
|
|
312
|
+
max-height: 100dvh;
|
|
313
|
+
border-radius: 0;
|
|
314
|
+
border-left: 0;
|
|
315
|
+
border-right: 0;
|
|
316
|
+
box-shadow: none;
|
|
317
|
+
}
|
|
318
|
+
.tab-link-popover--modal .tab-link-popover-header {
|
|
319
|
+
position: sticky;
|
|
320
|
+
top: 0;
|
|
321
|
+
z-index: 1;
|
|
322
|
+
padding: 12px 12px 8px 16px;
|
|
323
|
+
background: var(--pinokio-sidebar-tabbar-bg, rgba(255, 255, 255, 0.98));
|
|
324
|
+
}
|
|
325
|
+
.tab-link-popover--modal .tab-link-popover-item {
|
|
326
|
+
padding-right: 16px;
|
|
327
|
+
padding-left: 16px;
|
|
328
|
+
}
|
|
329
|
+
.tab-link-popover--modal .tab-link-popover-item--action {
|
|
330
|
+
min-height: 44px;
|
|
331
|
+
}
|
|
332
|
+
.tab-link-popover--modal .tab-link-popover-item.qr-inline {
|
|
333
|
+
display: grid;
|
|
334
|
+
grid-template-columns: minmax(0, 1fr);
|
|
335
|
+
justify-items: start;
|
|
336
|
+
gap: 10px;
|
|
337
|
+
}
|
|
338
|
+
.tab-link-popover--modal .tab-link-popover-item.qr-inline .qr {
|
|
339
|
+
width: min(220px, 64vw);
|
|
340
|
+
height: min(220px, 64vw);
|
|
341
|
+
margin: 6px auto 0;
|
|
342
|
+
}
|
|
343
|
+
.tab-link-popover--modal .tab-link-popover-section {
|
|
344
|
+
padding-right: 16px;
|
|
345
|
+
padding-left: 16px;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
204
348
|
.appcanvas > aside .menu-container .tab-link-popover-host {
|
|
205
349
|
display: flex;
|
|
206
350
|
align-items: center;
|
|
@@ -11,8 +11,12 @@
|
|
|
11
11
|
let tabLinkRouterHttpsActive = null
|
|
12
12
|
let tabLinkPeerInfoPromise = null
|
|
13
13
|
let tabLinkPeerInfoExpiry = 0
|
|
14
|
+
let tabLinkPopoverMode = "anchored"
|
|
15
|
+
let tabLinkReturnFocusEl = null
|
|
14
16
|
const TAB_LINK_TRIGGER_CLASS = "tab-link-popover-trigger"
|
|
15
17
|
const TAB_LINK_TRIGGER_HOST_CLASS = "tab-link-popover-host"
|
|
18
|
+
const TAB_LINK_POPOVER_SURFACE_CLASS = "tab-link-popover-surface"
|
|
19
|
+
const TAB_LINK_POPOVER_TITLE_ID = `${TAB_LINK_POPOVER_ID}-title`
|
|
16
20
|
|
|
17
21
|
const shouldAttachTabLinkTrigger = (link) => {
|
|
18
22
|
if (!link || !link.classList || !link.classList.contains("frame-link")) {
|
|
@@ -126,11 +130,105 @@
|
|
|
126
130
|
return { available: false, enabled: true }
|
|
127
131
|
}
|
|
128
132
|
|
|
133
|
+
const getTabLinkPopoverSurface = (popover) => {
|
|
134
|
+
if (!popover || typeof popover.querySelector !== "function") {
|
|
135
|
+
return null
|
|
136
|
+
}
|
|
137
|
+
return popover.querySelector(`.${TAB_LINK_POPOVER_SURFACE_CLASS}`)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const getTabLinkFocusables = (root) => {
|
|
141
|
+
if (!root || typeof root.querySelectorAll !== "function") {
|
|
142
|
+
return []
|
|
143
|
+
}
|
|
144
|
+
return Array.from(root.querySelectorAll([
|
|
145
|
+
'a[href]',
|
|
146
|
+
'button:not([disabled])',
|
|
147
|
+
'input:not([disabled])',
|
|
148
|
+
'select:not([disabled])',
|
|
149
|
+
'textarea:not([disabled])',
|
|
150
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
151
|
+
].join(','))).filter((element) => {
|
|
152
|
+
if (!(element instanceof HTMLElement)) {
|
|
153
|
+
return false
|
|
154
|
+
}
|
|
155
|
+
if (element.hidden || element.getAttribute("aria-hidden") === "true") {
|
|
156
|
+
return false
|
|
157
|
+
}
|
|
158
|
+
const rect = element.getBoundingClientRect()
|
|
159
|
+
return rect.width > 0 || rect.height > 0
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const resetTabLinkPopoverContent = (popover, mode) => {
|
|
164
|
+
if (!popover) {
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
167
|
+
tabLinkPopoverMode = mode === "modal" ? "modal" : "anchored"
|
|
168
|
+
popover.classList.toggle("tab-link-popover--modal", tabLinkPopoverMode === "modal")
|
|
169
|
+
popover.classList.toggle("tab-link-popover--anchored", tabLinkPopoverMode !== "modal")
|
|
170
|
+
popover.setAttribute("data-mode", tabLinkPopoverMode)
|
|
171
|
+
popover.removeAttribute("aria-labelledby")
|
|
172
|
+
popover.removeAttribute("aria-modal")
|
|
173
|
+
popover.removeAttribute("role")
|
|
174
|
+
const surface = getTabLinkPopoverSurface(popover)
|
|
175
|
+
if (surface) {
|
|
176
|
+
surface.innerHTML = ""
|
|
177
|
+
surface.removeAttribute("aria-labelledby")
|
|
178
|
+
surface.removeAttribute("aria-modal")
|
|
179
|
+
surface.removeAttribute("role")
|
|
180
|
+
surface.removeAttribute("tabindex")
|
|
181
|
+
if (tabLinkPopoverMode === "modal") {
|
|
182
|
+
surface.setAttribute("role", "dialog")
|
|
183
|
+
surface.setAttribute("aria-modal", "true")
|
|
184
|
+
surface.setAttribute("aria-labelledby", TAB_LINK_POPOVER_TITLE_ID)
|
|
185
|
+
surface.setAttribute("tabindex", "-1")
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return surface
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const createTabLinkPopoverHeader = (title = "Tab actions") => {
|
|
192
|
+
const header = document.createElement("div")
|
|
193
|
+
header.className = "tab-link-popover-header"
|
|
194
|
+
|
|
195
|
+
const heading = document.createElement("div")
|
|
196
|
+
heading.className = "tab-link-popover-title"
|
|
197
|
+
heading.id = TAB_LINK_POPOVER_TITLE_ID
|
|
198
|
+
|
|
199
|
+
const icon = document.createElement("span")
|
|
200
|
+
icon.className = "tab-link-popover-title-icon"
|
|
201
|
+
icon.innerHTML = '<i class="fa-solid fa-ellipsis" aria-hidden="true"></i>'
|
|
202
|
+
|
|
203
|
+
const text = document.createElement("span")
|
|
204
|
+
text.textContent = title
|
|
205
|
+
heading.append(icon, text)
|
|
206
|
+
|
|
207
|
+
const close = document.createElement("button")
|
|
208
|
+
close.type = "button"
|
|
209
|
+
close.className = "tab-link-popover-close"
|
|
210
|
+
close.setAttribute("data-tab-link-popover-close", "")
|
|
211
|
+
close.setAttribute("aria-label", "Close tab actions")
|
|
212
|
+
close.innerHTML = '<i class="fa-solid fa-xmark" aria-hidden="true"></i>'
|
|
213
|
+
|
|
214
|
+
header.append(heading, close)
|
|
215
|
+
return header
|
|
216
|
+
}
|
|
217
|
+
|
|
129
218
|
const ensureTabLinkPopoverEl = () => {
|
|
130
219
|
if (!tabLinkPopoverEl) {
|
|
131
220
|
tabLinkPopoverEl = document.createElement("div")
|
|
132
221
|
tabLinkPopoverEl.id = TAB_LINK_POPOVER_ID
|
|
133
|
-
tabLinkPopoverEl.className = "tab-link-popover"
|
|
222
|
+
tabLinkPopoverEl.className = "tab-link-popover tab-link-popover--anchored"
|
|
223
|
+
const backdrop = document.createElement("button")
|
|
224
|
+
backdrop.type = "button"
|
|
225
|
+
backdrop.className = "tab-link-popover-backdrop"
|
|
226
|
+
backdrop.setAttribute("data-tab-link-popover-close", "")
|
|
227
|
+
backdrop.setAttribute("aria-label", "Close tab actions")
|
|
228
|
+
backdrop.tabIndex = -1
|
|
229
|
+
const surface = document.createElement("div")
|
|
230
|
+
surface.className = TAB_LINK_POPOVER_SURFACE_CLASS
|
|
231
|
+
tabLinkPopoverEl.append(backdrop, surface)
|
|
134
232
|
tabLinkPopoverEl.addEventListener("mouseenter", () => {
|
|
135
233
|
if (tabLinkHideTimer) {
|
|
136
234
|
clearTimeout(tabLinkHideTimer)
|
|
@@ -143,6 +241,13 @@
|
|
|
143
241
|
}
|
|
144
242
|
})
|
|
145
243
|
tabLinkPopoverEl.addEventListener("click", (event) => {
|
|
244
|
+
const closeControl = event.target.closest("[data-tab-link-popover-close]")
|
|
245
|
+
if (closeControl && tabLinkPopoverEl.contains(closeControl)) {
|
|
246
|
+
event.preventDefault()
|
|
247
|
+
event.stopPropagation()
|
|
248
|
+
hideTabLinkPopover({ immediate: true })
|
|
249
|
+
return
|
|
250
|
+
}
|
|
146
251
|
const item = event.target.closest(".tab-link-popover-item")
|
|
147
252
|
if (!item) {
|
|
148
253
|
return
|
|
@@ -1348,14 +1453,52 @@
|
|
|
1348
1453
|
if (!popover || !link) {
|
|
1349
1454
|
return
|
|
1350
1455
|
}
|
|
1456
|
+
const surface = getTabLinkPopoverSurface(popover)
|
|
1457
|
+
if (tabLinkPopoverMode === "modal") {
|
|
1458
|
+
popover.style.minWidth = ""
|
|
1459
|
+
popover.style.left = ""
|
|
1460
|
+
popover.style.top = ""
|
|
1461
|
+
popover.style.maxHeight = ""
|
|
1462
|
+
popover.style.overflowY = ""
|
|
1463
|
+
if (surface) {
|
|
1464
|
+
surface.style.maxHeight = ""
|
|
1465
|
+
surface.style.overflowY = ""
|
|
1466
|
+
}
|
|
1467
|
+
popover.style.display = ""
|
|
1468
|
+
popover.style.visibility = ""
|
|
1469
|
+
popover.classList.add("visible")
|
|
1470
|
+
if (document.body) {
|
|
1471
|
+
document.body.classList.add("tab-link-popover-open")
|
|
1472
|
+
}
|
|
1473
|
+
window.requestAnimationFrame(() => {
|
|
1474
|
+
if (!popover.classList.contains("visible")) {
|
|
1475
|
+
return
|
|
1476
|
+
}
|
|
1477
|
+
const close = popover.querySelector(".tab-link-popover-close")
|
|
1478
|
+
if (close && typeof close.focus === "function") {
|
|
1479
|
+
close.focus()
|
|
1480
|
+
} else if (surface && typeof surface.focus === "function") {
|
|
1481
|
+
surface.focus()
|
|
1482
|
+
}
|
|
1483
|
+
})
|
|
1484
|
+
return
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
if (document.body) {
|
|
1488
|
+
document.body.classList.remove("tab-link-popover-open")
|
|
1489
|
+
}
|
|
1351
1490
|
const rect = link.getBoundingClientRect()
|
|
1352
1491
|
const minWidth = Math.max(rect.width, 260)
|
|
1353
1492
|
popover.style.minWidth = `${Math.round(minWidth)}px`
|
|
1354
|
-
popover.style.display = "
|
|
1493
|
+
popover.style.display = ""
|
|
1355
1494
|
popover.classList.add("visible")
|
|
1356
1495
|
popover.style.visibility = "hidden"
|
|
1357
1496
|
popover.style.maxHeight = ""
|
|
1358
1497
|
popover.style.overflowY = ""
|
|
1498
|
+
if (surface) {
|
|
1499
|
+
surface.style.maxHeight = ""
|
|
1500
|
+
surface.style.overflowY = ""
|
|
1501
|
+
}
|
|
1359
1502
|
const popoverWidth = popover.offsetWidth
|
|
1360
1503
|
let popoverHeight = popover.offsetHeight
|
|
1361
1504
|
const viewportPadding = 12
|
|
@@ -1380,8 +1523,13 @@
|
|
|
1380
1523
|
|
|
1381
1524
|
const availableHeight = Math.max(0, window.innerHeight - viewportPadding * 2)
|
|
1382
1525
|
if (availableHeight > 0 && popoverHeight > availableHeight) {
|
|
1383
|
-
|
|
1384
|
-
|
|
1526
|
+
if (surface) {
|
|
1527
|
+
surface.style.maxHeight = `${Math.round(availableHeight)}px`
|
|
1528
|
+
surface.style.overflowY = "auto"
|
|
1529
|
+
} else {
|
|
1530
|
+
popover.style.maxHeight = `${Math.round(availableHeight)}px`
|
|
1531
|
+
popover.style.overflowY = "auto"
|
|
1532
|
+
}
|
|
1385
1533
|
popoverHeight = Math.min(availableHeight, popover.offsetHeight)
|
|
1386
1534
|
}
|
|
1387
1535
|
|
|
@@ -1404,8 +1552,13 @@
|
|
|
1404
1552
|
|
|
1405
1553
|
const availableBelow = Math.max(0, window.innerHeight - viewportPadding - top)
|
|
1406
1554
|
if (availableBelow > 0 && popoverHeight > availableBelow) {
|
|
1407
|
-
|
|
1408
|
-
|
|
1555
|
+
if (surface) {
|
|
1556
|
+
surface.style.maxHeight = `${Math.round(availableBelow)}px`
|
|
1557
|
+
surface.style.overflowY = "auto"
|
|
1558
|
+
} else {
|
|
1559
|
+
popover.style.maxHeight = `${Math.round(availableBelow)}px`
|
|
1560
|
+
popover.style.overflowY = "auto"
|
|
1561
|
+
}
|
|
1409
1562
|
popoverHeight = Math.min(availableBelow, popover.offsetHeight)
|
|
1410
1563
|
}
|
|
1411
1564
|
}
|
|
@@ -1417,14 +1570,27 @@
|
|
|
1417
1570
|
|
|
1418
1571
|
const hideTabLinkPopover = ({ immediate = false } = {}) => {
|
|
1419
1572
|
const applyHide = () => {
|
|
1573
|
+
const returnFocusEl = tabLinkPopoverMode === "modal" ? tabLinkReturnFocusEl : null
|
|
1420
1574
|
if (tabLinkPopoverEl) {
|
|
1421
1575
|
tabLinkPopoverEl.classList.remove("visible")
|
|
1422
1576
|
tabLinkPopoverEl.style.display = "none"
|
|
1423
1577
|
}
|
|
1578
|
+
if (document.body) {
|
|
1579
|
+
document.body.classList.remove("tab-link-popover-open")
|
|
1580
|
+
}
|
|
1424
1581
|
tabLinkActiveLink = null
|
|
1425
1582
|
tabLinkPendingLink = null
|
|
1426
1583
|
tabLinkHideTimer = null
|
|
1427
1584
|
tabLinkCloseOnMouseLeave = false
|
|
1585
|
+
tabLinkPopoverMode = "anchored"
|
|
1586
|
+
tabLinkReturnFocusEl = null
|
|
1587
|
+
if (returnFocusEl && typeof returnFocusEl.focus === "function" && document.contains(returnFocusEl)) {
|
|
1588
|
+
window.requestAnimationFrame(() => {
|
|
1589
|
+
try {
|
|
1590
|
+
returnFocusEl.focus()
|
|
1591
|
+
} catch (_) {}
|
|
1592
|
+
})
|
|
1593
|
+
}
|
|
1428
1594
|
}
|
|
1429
1595
|
|
|
1430
1596
|
if (tabLinkHideTimer) {
|
|
@@ -1454,6 +1620,10 @@
|
|
|
1454
1620
|
const restrictToBase = options && options.restrictToBase === true
|
|
1455
1621
|
const forceCanonicalQr = options && options.forceCanonicalQr === true
|
|
1456
1622
|
tabLinkCloseOnMouseLeave = options && options.closeOnMouseLeave === true
|
|
1623
|
+
const presentationMode = tabLinkCloseOnMouseLeave ? "anchored" : "modal"
|
|
1624
|
+
tabLinkReturnFocusEl = presentationMode === "modal"
|
|
1625
|
+
? ((link && link.querySelector && link.querySelector(`.${TAB_LINK_TRIGGER_CLASS}`)) || link)
|
|
1626
|
+
: null
|
|
1457
1627
|
let sameOrigin = false
|
|
1458
1628
|
let canonicalBase = canonicalizeUrl(effectiveHref)
|
|
1459
1629
|
if (canonicalBase && isHttpUrl(canonicalBase)) {
|
|
@@ -1515,10 +1685,11 @@
|
|
|
1515
1685
|
// Show lightweight loading popover immediately while mapping fetch runs
|
|
1516
1686
|
try {
|
|
1517
1687
|
const pop = ensureTabLinkPopoverEl()
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1688
|
+
const surface = resetTabLinkPopoverContent(pop, presentationMode)
|
|
1689
|
+
if (!surface) {
|
|
1690
|
+
throw new Error("Missing tab link popover surface")
|
|
1691
|
+
}
|
|
1692
|
+
const header = createTabLinkPopoverHeader("Tab actions")
|
|
1522
1693
|
const item = document.createElement('div')
|
|
1523
1694
|
item.className = 'tab-link-popover-item tab-link-popover-item--action'
|
|
1524
1695
|
const icon = document.createElement('span')
|
|
@@ -1534,7 +1705,7 @@
|
|
|
1534
1705
|
value.textContent = 'Preparing actions'
|
|
1535
1706
|
copy.append(label, value)
|
|
1536
1707
|
item.append(icon, copy)
|
|
1537
|
-
|
|
1708
|
+
surface.append(header, item)
|
|
1538
1709
|
positionTabLinkPopover(pop, link)
|
|
1539
1710
|
} catch (_) {}
|
|
1540
1711
|
|
|
@@ -1708,12 +1879,14 @@
|
|
|
1708
1879
|
}
|
|
1709
1880
|
|
|
1710
1881
|
const popover = ensureTabLinkPopoverEl()
|
|
1711
|
-
|
|
1882
|
+
const popoverSurface = resetTabLinkPopoverContent(popover, presentationMode)
|
|
1883
|
+
if (!popoverSurface) {
|
|
1884
|
+
hideTabLinkPopover({ immediate: true })
|
|
1885
|
+
return
|
|
1886
|
+
}
|
|
1712
1887
|
|
|
1713
|
-
const header =
|
|
1714
|
-
|
|
1715
|
-
header.innerHTML = `<i class="fa-solid fa-ellipsis"></i><span>Tab actions</span>`
|
|
1716
|
-
popover.appendChild(header)
|
|
1888
|
+
const header = createTabLinkPopoverHeader("Tab actions")
|
|
1889
|
+
popoverSurface.appendChild(header)
|
|
1717
1890
|
|
|
1718
1891
|
actionItems.forEach((actionItem) => {
|
|
1719
1892
|
const item = document.createElement("button")
|
|
@@ -1745,20 +1918,20 @@
|
|
|
1745
1918
|
}
|
|
1746
1919
|
copy.append(labelSpan, valueSpan)
|
|
1747
1920
|
item.append(icon, copy)
|
|
1748
|
-
|
|
1921
|
+
popoverSurface.appendChild(item)
|
|
1749
1922
|
})
|
|
1750
1923
|
|
|
1751
1924
|
if (actionItems.length > 0 && routeEntries.length > 0) {
|
|
1752
1925
|
const divider = document.createElement("div")
|
|
1753
1926
|
divider.className = "tab-link-popover-divider"
|
|
1754
|
-
|
|
1927
|
+
popoverSurface.appendChild(divider)
|
|
1755
1928
|
}
|
|
1756
1929
|
|
|
1757
1930
|
if (routeEntries.length > 0) {
|
|
1758
1931
|
const section = document.createElement("div")
|
|
1759
1932
|
section.className = "tab-link-popover-section"
|
|
1760
1933
|
section.textContent = "Browser routes"
|
|
1761
|
-
|
|
1934
|
+
popoverSurface.appendChild(section)
|
|
1762
1935
|
}
|
|
1763
1936
|
|
|
1764
1937
|
routeEntries.forEach((entry) => {
|
|
@@ -1790,7 +1963,7 @@
|
|
|
1790
1963
|
item.className = "tab-link-popover-item"
|
|
1791
1964
|
item.append(labelSpan, valueSpan)
|
|
1792
1965
|
}
|
|
1793
|
-
|
|
1966
|
+
popoverSurface.appendChild(item)
|
|
1794
1967
|
})
|
|
1795
1968
|
|
|
1796
1969
|
const hasHttpsEntry = Array.isArray(entries) && entries.some((entry) => entry && entry.type === "https")
|
|
@@ -1812,7 +1985,7 @@
|
|
|
1812
1985
|
footerValue.textContent = "Click to activate"
|
|
1813
1986
|
|
|
1814
1987
|
footerButton.append(footerLabel, footerValue)
|
|
1815
|
-
|
|
1988
|
+
popoverSurface.appendChild(footerButton)
|
|
1816
1989
|
}
|
|
1817
1990
|
|
|
1818
1991
|
if (actionItems.length === 0 && routeEntries.length === 0 && !(tabLinkRouterHttpsActive === false && !hasHttpsEntry)) {
|
|
@@ -1922,10 +2095,45 @@
|
|
|
1922
2095
|
hideTabLinkPopover({ immediate: true })
|
|
1923
2096
|
}
|
|
1924
2097
|
|
|
2098
|
+
const handleGlobalKeydown = (event) => {
|
|
2099
|
+
if (!tabLinkPopoverEl || !tabLinkPopoverEl.classList.contains("visible")) {
|
|
2100
|
+
return
|
|
2101
|
+
}
|
|
2102
|
+
if (event.key === "Escape") {
|
|
2103
|
+
event.preventDefault()
|
|
2104
|
+
hideTabLinkPopover({ immediate: true })
|
|
2105
|
+
return
|
|
2106
|
+
}
|
|
2107
|
+
if (event.key !== "Tab" || tabLinkPopoverMode !== "modal") {
|
|
2108
|
+
return
|
|
2109
|
+
}
|
|
2110
|
+
const surface = getTabLinkPopoverSurface(tabLinkPopoverEl)
|
|
2111
|
+
const focusables = getTabLinkFocusables(surface)
|
|
2112
|
+
if (focusables.length === 0) {
|
|
2113
|
+
event.preventDefault()
|
|
2114
|
+
if (surface && typeof surface.focus === "function") {
|
|
2115
|
+
surface.focus()
|
|
2116
|
+
}
|
|
2117
|
+
return
|
|
2118
|
+
}
|
|
2119
|
+
const first = focusables[0]
|
|
2120
|
+
const last = focusables[focusables.length - 1]
|
|
2121
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
2122
|
+
event.preventDefault()
|
|
2123
|
+
last.focus()
|
|
2124
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
2125
|
+
event.preventDefault()
|
|
2126
|
+
first.focus()
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
|
|
1925
2130
|
window.addEventListener("scroll", (event) => {
|
|
1926
2131
|
if (!tabLinkPopoverEl || !tabLinkPopoverEl.classList.contains("visible")) {
|
|
1927
2132
|
return
|
|
1928
2133
|
}
|
|
2134
|
+
if (tabLinkPopoverMode === "modal") {
|
|
2135
|
+
return
|
|
2136
|
+
}
|
|
1929
2137
|
if (event && event.target && tabLinkPopoverEl.contains(event.target)) {
|
|
1930
2138
|
return
|
|
1931
2139
|
}
|
|
@@ -1939,6 +2147,7 @@
|
|
|
1939
2147
|
})
|
|
1940
2148
|
|
|
1941
2149
|
document.addEventListener("mousedown", handleGlobalPointer, true)
|
|
2150
|
+
document.addEventListener("keydown", handleGlobalKeydown, true)
|
|
1942
2151
|
try {
|
|
1943
2152
|
document.addEventListener("touchstart", handleGlobalPointer, { passive: true, capture: true })
|
|
1944
2153
|
} catch (_) {
|