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
package/server/index.js
CHANGED
|
@@ -1089,7 +1089,7 @@ class Server {
|
|
|
1089
1089
|
const doneMarker = "PINOKIO_GITHUB_LOGIN_DONE"
|
|
1090
1090
|
const delimiter = this.kernel.platform === "win32" ? " && " : " ; "
|
|
1091
1091
|
const verifyCommand = this.kernel.platform === "win32"
|
|
1092
|
-
? "
|
|
1092
|
+
? "powershell.exe -NoProfile -Command \"$env:GIT_TERMINAL_PROMPT='0'; $env:GCM_INTERACTIVE='never'; @('protocol=https','host=github.com','') | git credential fill > $null; exit $LASTEXITCODE\""
|
|
1093
1093
|
: "printf 'protocol=https\\nhost=github.com\\n\\n' | GIT_TERMINAL_PROMPT=0 GCM_INTERACTIVE=never git credential fill >/dev/null"
|
|
1094
1094
|
const doneCommand = this.kernel.platform === "win32"
|
|
1095
1095
|
? "echo P^INOKIO_GITHUB_LOGIN_DONE"
|
|
@@ -4914,11 +4914,17 @@ class Server {
|
|
|
4914
4914
|
}
|
|
4915
4915
|
async composePeerAccessPayload() {
|
|
4916
4916
|
let peer_access_points = []
|
|
4917
|
+
let peer_access_router_installed = false
|
|
4917
4918
|
try {
|
|
4918
4919
|
peer_access_points = await this.buildPeerAccessPoints()
|
|
4919
4920
|
} catch (error) {
|
|
4920
4921
|
peer_access_points = []
|
|
4921
4922
|
}
|
|
4923
|
+
try {
|
|
4924
|
+
peer_access_router_installed = await this.kernel.network_installed()
|
|
4925
|
+
} catch (error) {
|
|
4926
|
+
peer_access_router_installed = false
|
|
4927
|
+
}
|
|
4922
4928
|
let peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
4923
4929
|
let peer_qr = null
|
|
4924
4930
|
if (peer_access_points.length > 0) {
|
|
@@ -4929,7 +4935,7 @@ class Server {
|
|
|
4929
4935
|
peer_qr = await QRCode.toDataURL(peer_url)
|
|
4930
4936
|
} catch (_) {}
|
|
4931
4937
|
}
|
|
4932
|
-
return { peer_access_points, peer_url, peer_qr }
|
|
4938
|
+
return { peer_access_points, peer_url, peer_qr, peer_access_router_installed }
|
|
4933
4939
|
}
|
|
4934
4940
|
|
|
4935
4941
|
async ensureLogsRootDirectory() {
|
|
@@ -8092,6 +8098,8 @@ class Server {
|
|
|
8092
8098
|
if (!requireSkillsHome(res)) {
|
|
8093
8099
|
return
|
|
8094
8100
|
}
|
|
8101
|
+
const peerAccess = await this.composePeerAccessPayload()
|
|
8102
|
+
const list = this.getPeers()
|
|
8095
8103
|
let skills = []
|
|
8096
8104
|
let indexError = ""
|
|
8097
8105
|
try {
|
|
@@ -8100,8 +8108,11 @@ class Server {
|
|
|
8100
8108
|
indexError = error && error.message ? error.message : "Failed to read managed skills index."
|
|
8101
8109
|
}
|
|
8102
8110
|
res.render("skills", {
|
|
8111
|
+
current_host: this.kernel.peer.host,
|
|
8112
|
+
...peerAccess,
|
|
8103
8113
|
theme: this.theme,
|
|
8104
8114
|
agent: req.agent,
|
|
8115
|
+
list,
|
|
8105
8116
|
skills,
|
|
8106
8117
|
skillsRootPath: ManagedSkills.skillsRoot(this.kernel),
|
|
8107
8118
|
indexPath: ManagedSkills.indexPath(this.kernel),
|
|
@@ -12380,49 +12391,53 @@ class Server {
|
|
|
12380
12391
|
* GET /connect/discord
|
|
12381
12392
|
*/
|
|
12382
12393
|
this.app.get("/connect/:provider", ex(async (req, res) => {
|
|
12394
|
+
const providerUsesDeviceLogin = req.params.provider === "huggingface"
|
|
12395
|
+
const setupMode = providerUsesDeviceLogin ? "connect" : "network"
|
|
12383
12396
|
|
|
12384
12397
|
// check if all the connect related modules are installed
|
|
12385
12398
|
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
12386
|
-
bin: this.kernel.bin.preset(
|
|
12399
|
+
bin: this.kernel.bin.preset(setupMode),
|
|
12387
12400
|
})
|
|
12388
12401
|
if (!requirements_pending && install_required) {
|
|
12389
12402
|
console.log("REDIRECT", req.params.provider)
|
|
12390
|
-
res.redirect(
|
|
12403
|
+
res.redirect(`/setup/${setupMode}?callback=/connect/${req.params.provider}`)
|
|
12391
12404
|
return
|
|
12392
12405
|
}
|
|
12393
12406
|
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12407
|
+
if (!providerUsesDeviceLogin) {
|
|
12408
|
+
let https_running = false
|
|
12409
|
+
try {
|
|
12410
|
+
let res = await axios.get(`http://127.0.0.1:2019/config/`, {
|
|
12411
|
+
timeout: 2000
|
|
12412
|
+
})
|
|
12413
|
+
let test = /pinokio\.localhost/.test(JSON.stringify(res.data))
|
|
12414
|
+
if (test) {
|
|
12415
|
+
https_running = true
|
|
12416
|
+
}
|
|
12417
|
+
} catch (e) {
|
|
12418
|
+
console.log(e)
|
|
12402
12419
|
}
|
|
12403
|
-
|
|
12404
|
-
console.log(e)
|
|
12405
|
-
}
|
|
12406
|
-
if (!https_running) {
|
|
12420
|
+
if (!https_running) {
|
|
12407
12421
|
// res.json({ error: "pinokio.host not yet available" })
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12422
|
+
res.redirect(`/setup/${setupMode}?callback=/connect/${req.params.provider}`)
|
|
12423
|
+
return
|
|
12424
|
+
}
|
|
12411
12425
|
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12426
|
+
// check if pinokio.localhost router is running
|
|
12427
|
+
let router_running = false
|
|
12428
|
+
let router = this.kernel.router.published()
|
|
12429
|
+
for(let ip in router) {
|
|
12430
|
+
let domains = router[ip]
|
|
12431
|
+
if (domains.includes("pinokio.localhost")) {
|
|
12432
|
+
router_running = true
|
|
12433
|
+
break
|
|
12434
|
+
}
|
|
12420
12435
|
}
|
|
12421
|
-
|
|
12422
|
-
if (!router_running) {
|
|
12436
|
+
if (!router_running) {
|
|
12423
12437
|
// res.json({ error: "pinokio.localhost not yet available" })
|
|
12424
|
-
|
|
12425
|
-
|
|
12438
|
+
res.redirect(`/setup/${setupMode}?callback=/connect/${req.params.provider}`)
|
|
12439
|
+
return
|
|
12440
|
+
}
|
|
12426
12441
|
}
|
|
12427
12442
|
|
|
12428
12443
|
|
|
@@ -12437,7 +12452,7 @@ class Server {
|
|
|
12437
12452
|
const config = this.kernel.connect.config[req.params.provider]
|
|
12438
12453
|
const isPinokioHost = req.hostname === 'pinokio.localhost'
|
|
12439
12454
|
const renderProtocol = isPinokioHost ? 'https' : 'http'
|
|
12440
|
-
res.render(`connect/index`, {
|
|
12455
|
+
res.render(providerUsesDeviceLogin ? `connect/huggingface` : `connect/index`, {
|
|
12441
12456
|
protocol: renderProtocol,
|
|
12442
12457
|
name: req.params.provider,
|
|
12443
12458
|
config,
|
|
@@ -13516,7 +13531,7 @@ class Server {
|
|
|
13516
13531
|
1. mode:ai => all
|
|
13517
13532
|
2. mode:coding => conda, nodejs, git
|
|
13518
13533
|
3. mode:network => conda, git, caddy
|
|
13519
|
-
4. mode:connect => conda, git,
|
|
13534
|
+
4. mode:connect => conda, git, huggingface
|
|
13520
13535
|
*/
|
|
13521
13536
|
|
|
13522
13537
|
let bin = this.kernel.bin.preset(req.params.mode)
|
|
@@ -15626,6 +15641,32 @@ class Server {
|
|
|
15626
15641
|
res.json({ https_active: false, router_info: [], rewrite_mapping: {}, router: {} })
|
|
15627
15642
|
}
|
|
15628
15643
|
}))
|
|
15644
|
+
this.app.get("/info/network-sharing", ex(async (req, res) => {
|
|
15645
|
+
res.set("Cache-Control", "no-store")
|
|
15646
|
+
const https_active = !!(this.kernel.peer && this.kernel.peer.https_active)
|
|
15647
|
+
let installed = false
|
|
15648
|
+
let running = false
|
|
15649
|
+
try {
|
|
15650
|
+
const caddy = this.kernel.bin && this.kernel.bin.mod ? this.kernel.bin.mod.caddy : null
|
|
15651
|
+
if (caddy && typeof caddy.installed === "function") {
|
|
15652
|
+
installed = !!(await caddy.installed())
|
|
15653
|
+
}
|
|
15654
|
+
} catch (err) {
|
|
15655
|
+
installed = false
|
|
15656
|
+
}
|
|
15657
|
+
try {
|
|
15658
|
+
await axios.get("http://127.0.0.1:2019/config/", { timeout: 750 })
|
|
15659
|
+
running = true
|
|
15660
|
+
} catch (err) {
|
|
15661
|
+
running = false
|
|
15662
|
+
}
|
|
15663
|
+
res.json({
|
|
15664
|
+
active: https_active,
|
|
15665
|
+
installed,
|
|
15666
|
+
running,
|
|
15667
|
+
available: https_active && installed
|
|
15668
|
+
})
|
|
15669
|
+
}))
|
|
15629
15670
|
this.app.get("/qr", ex(async (req, res) => {
|
|
15630
15671
|
try {
|
|
15631
15672
|
const data = typeof req.query.data === 'string' ? req.query.data : ''
|
|
@@ -16042,6 +16083,7 @@ class Server {
|
|
|
16042
16083
|
})
|
|
16043
16084
|
}))
|
|
16044
16085
|
this.app.get("/pinokio/install", ex((req, res) => {
|
|
16086
|
+
res.set("Cache-Control", "no-store")
|
|
16045
16087
|
let requirements = req.session.requirements
|
|
16046
16088
|
let callback = req.session.callback
|
|
16047
16089
|
req.session.requirements = null
|
package/server/public/common.js
CHANGED
|
@@ -116,6 +116,14 @@ function escapePinokioStatusHtml(value) {
|
|
|
116
116
|
.replace(/'/g, ''')
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
const PINOKIO_WAIT_STATUS_SPINNER_HTML = `
|
|
120
|
+
<span class="pinokio-install-inline-status-spinner" aria-hidden="true">
|
|
121
|
+
<span class="pinokio-install-inline-status-spinner-dot pinokio-install-inline-status-spinner-dot-1"></span>
|
|
122
|
+
<span class="pinokio-install-inline-status-spinner-dot pinokio-install-inline-status-spinner-dot-2"></span>
|
|
123
|
+
<span class="pinokio-install-inline-status-spinner-dot pinokio-install-inline-status-spinner-dot-3"></span>
|
|
124
|
+
<span class="pinokio-install-inline-status-spinner-dot pinokio-install-inline-status-spinner-dot-4"></span>
|
|
125
|
+
</span>`
|
|
126
|
+
|
|
119
127
|
function ensurePinokioWaitFooterStatus() {
|
|
120
128
|
let status = document.getElementById(PINOKIO_WAIT_FOOTER_ID)
|
|
121
129
|
if (status) {
|
|
@@ -148,9 +156,7 @@ function showPinokioWaitFooterStatus(data = {}) {
|
|
|
148
156
|
status.className = 'pinokio-install-inline-status pinokio-process-wait-footer-status is-progress'
|
|
149
157
|
status.innerHTML = `
|
|
150
158
|
<div class="pinokio-install-inline-status-shell">
|
|
151
|
-
|
|
152
|
-
<i class="fa-solid fa-circle-notch fa-spin"></i>
|
|
153
|
-
</span>
|
|
159
|
+
${PINOKIO_WAIT_STATUS_SPINNER_HTML}
|
|
154
160
|
<div class="pinokio-install-inline-status-copy">
|
|
155
161
|
<div class="pinokio-install-inline-status-title">${escapePinokioStatusHtml(title)}</div>
|
|
156
162
|
${description ? `<div class="pinokio-install-inline-status-detail">${escapePinokioStatusHtml(description)}</div>` : ''}
|
|
@@ -445,24 +451,28 @@ class ScreenCaptureModal {
|
|
|
445
451
|
.modal-overlay{position:fixed;inset:0;padding:24px;display:flex;align-items:center;justify-content:center;z-index:9999;opacity:0;visibility:hidden;pointer-events:none;transition:opacity 160ms ease,visibility 0s linear 160ms;}
|
|
446
452
|
.modal-overlay.is-visible{opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s;}
|
|
447
453
|
@media (prefers-reduced-motion: reduce){.modal-overlay{transition:none;}.capture-modal{animation:none;}}
|
|
448
|
-
.capture-modal-overlay{background:rgba(
|
|
449
|
-
.capture-modal{
|
|
450
|
-
|
|
451
|
-
.capture-modal
|
|
454
|
+
.capture-modal-overlay{background:rgba(9,11,15,0.34);-webkit-backdrop-filter:none;backdrop-filter:none;}
|
|
455
|
+
body.dark .capture-modal-overlay{background:rgba(0,0,0,0.58);}
|
|
456
|
+
.capture-modal{width:min(360px,calc(100% - 32px));padding:20px;border-radius:8px;background:#fff;border:1px solid rgba(24,24,27,0.12);box-shadow:0 18px 50px rgba(15,23,42,0.16);display:flex;flex-direction:column;gap:14px;text-align:center;color:#18181b;}
|
|
457
|
+
body.dark .capture-modal{background:#1b1c1d;border-color:rgba(255,255,255,0.1);color:rgba(250,250,250,0.94);box-shadow:0 24px 72px rgba(0,0,0,0.42);}
|
|
458
|
+
.capture-modal-title{font-size:16px;line-height:1.25;font-weight:600;letter-spacing:0;color:inherit;}
|
|
452
459
|
body.dark .capture-modal-title{color:inherit;}
|
|
453
|
-
.capture-modal-description{font-size:
|
|
454
|
-
body.dark .capture-modal-description{color:rgba(
|
|
455
|
-
.capture-modal-actions{display:flex;justify-content:center;gap:
|
|
456
|
-
.capture-modal-button{padding:
|
|
457
|
-
.capture-modal-button.primary{background
|
|
458
|
-
.capture-modal-button.primary:hover{box-shadow:
|
|
459
|
-
.capture-modal-button.
|
|
460
|
-
.capture-modal-button.
|
|
461
|
-
|
|
460
|
+
.capture-modal-description{font-size:12px;line-height:1.45;color:#71717a;}
|
|
461
|
+
body.dark .capture-modal-description{color:rgba(229,231,235,0.62);}
|
|
462
|
+
.capture-modal-actions{display:flex;justify-content:center;gap:8px;}
|
|
463
|
+
.capture-modal-button{min-height:32px;padding:0 12px;border-radius:6px;border:1px solid transparent;font-size:13px;font-weight:600;cursor:pointer;transition:background 0.14s ease,color 0.14s ease,border-color 0.14s ease;}
|
|
464
|
+
.capture-modal-button.primary{background:#18181b;color:#fff;border-color:#18181b;box-shadow:none;}
|
|
465
|
+
.capture-modal-button.primary:hover{background:#27272a;border-color:#27272a;box-shadow:none;transform:none;}
|
|
466
|
+
body.dark .capture-modal-button.primary{background:rgba(250,250,250,0.94);border-color:rgba(250,250,250,0.94);color:#1b1c1d;}
|
|
467
|
+
body.dark .capture-modal-button.primary:hover{background:#fff;border-color:#fff;}
|
|
468
|
+
.capture-modal-button.secondary{background:transparent;color:#52525b;border-color:rgba(24,24,27,0.12);}
|
|
469
|
+
.capture-modal-button.secondary:hover{background:rgba(24,24,27,0.06);border-color:rgba(24,24,27,0.2);box-shadow:none;color:#18181b;}
|
|
470
|
+
body.dark .capture-modal-button.secondary{background:transparent;border-color:rgba(255,255,255,0.1);color:rgba(229,231,235,0.7);}
|
|
471
|
+
body.dark .capture-modal-button.secondary:hover{background:rgba(255,255,255,0.07);border-color:rgba(255,255,255,0.18);color:rgba(250,250,250,0.94);}
|
|
462
472
|
.capture-modal-button:active{transform:translateY(1px);}
|
|
463
|
-
.capture-modal-button:focus-visible{outline:2px solid rgba(
|
|
473
|
+
.capture-modal-button:focus-visible{outline:2px solid rgba(24,24,27,0.28);outline-offset:2px;}
|
|
464
474
|
.modal-overlay.is-visible .capture-modal{animation:captureModalPop 160ms ease-out;}
|
|
465
|
-
@media (max-width: 640px){.modal-overlay{padding:16px;}.capture-modal{width:calc(100% - 24px);padding:
|
|
475
|
+
@media (max-width: 640px){.modal-overlay{padding:16px;}.capture-modal{width:calc(100% - 24px);padding:18px;}.capture-modal-actions{flex-direction:column;}.capture-modal-button{width:100%;}}
|
|
466
476
|
@keyframes captureModalPop{from{opacity:0;transform:scale(0.97) translateY(12px);}to{opacity:1;transform:scale(1) translateY(0);}}
|
|
467
477
|
`;
|
|
468
478
|
document.head.appendChild(style);
|
|
@@ -2579,13 +2589,13 @@ if (typeof hotkeys === 'function') {
|
|
|
2579
2589
|
}
|
|
2580
2590
|
})();
|
|
2581
2591
|
|
|
2582
|
-
// Mobile
|
|
2583
|
-
(function
|
|
2592
|
+
// Mobile first-interaction audio primer for notification sounds on the top-level page
|
|
2593
|
+
(function initMobileAudioPrimer() {
|
|
2584
2594
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
2585
2595
|
return;
|
|
2586
2596
|
}
|
|
2587
2597
|
try {
|
|
2588
|
-
if (window.
|
|
2598
|
+
if (window.__pinokioAudioPrimerInstalled || window.__pinokioAudioPrimerInstalling) {
|
|
2589
2599
|
return;
|
|
2590
2600
|
}
|
|
2591
2601
|
} catch (_) {}
|
|
@@ -2597,7 +2607,7 @@ if (typeof hotkeys === 'function') {
|
|
|
2597
2607
|
// cross-origin parent; just bail
|
|
2598
2608
|
return;
|
|
2599
2609
|
}
|
|
2600
|
-
if (window.
|
|
2610
|
+
if (window.__pinokioAudioPrimerInstalled) {
|
|
2601
2611
|
return;
|
|
2602
2612
|
}
|
|
2603
2613
|
|
|
@@ -2653,37 +2663,6 @@ if (typeof hotkeys === 'function') {
|
|
|
2653
2663
|
return uaMobile;
|
|
2654
2664
|
};
|
|
2655
2665
|
|
|
2656
|
-
const createCurtain = () => {
|
|
2657
|
-
const style = document.createElement('style');
|
|
2658
|
-
style.textContent = `
|
|
2659
|
-
.pinokio-connect-curtain{position:fixed;top:0;left:0;right:0;bottom:0;z-index:2147483646;background:rgba(15,23,42,0.35);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center}
|
|
2660
|
-
.pinokio-connect-msg{user-select:none;-webkit-user-select:none;color:#fff;background:rgba(15,23,42,0.85);padding:14px 18px;border-radius:12px;font:500 15px/1.35 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;box-shadow:0 16px 40px rgba(0,0,0,.38);text-align:center;max-width:200px}
|
|
2661
|
-
.pinokio-connect-msg-title{font-weight:600;font-size:16px;margin-bottom:4px}
|
|
2662
|
-
.pinokio-connect-msg-hint{font-size:13px;opacity:.72}
|
|
2663
|
-
@media (max-width:768px){.pinokio-connect-msg{font-size:14px;padding:12px 16px}}
|
|
2664
|
-
`;
|
|
2665
|
-
document.head.appendChild(style);
|
|
2666
|
-
|
|
2667
|
-
const overlay = document.createElement('div');
|
|
2668
|
-
overlay.className = 'pinokio-connect-curtain';
|
|
2669
|
-
overlay.setAttribute('role', 'button');
|
|
2670
|
-
overlay.setAttribute('aria-label', 'Tap to connect');
|
|
2671
|
-
overlay.tabIndex = 0;
|
|
2672
|
-
const msg = document.createElement('div');
|
|
2673
|
-
msg.className = 'pinokio-connect-msg';
|
|
2674
|
-
const msgTitle = document.createElement('div');
|
|
2675
|
-
msgTitle.className = 'pinokio-connect-msg-title';
|
|
2676
|
-
msgTitle.textContent = 'Tap to connect';
|
|
2677
|
-
const msgHint = document.createElement('div');
|
|
2678
|
-
msgHint.className = 'pinokio-connect-msg-hint';
|
|
2679
|
-
msgHint.textContent = 'To type into the terminal, use the "Input" button.';
|
|
2680
|
-
msg.appendChild(msgTitle);
|
|
2681
|
-
msg.appendChild(msgHint);
|
|
2682
|
-
overlay.appendChild(msg);
|
|
2683
|
-
window.__pinokioConnectCurtainInstalled = true;
|
|
2684
|
-
return overlay;
|
|
2685
|
-
};
|
|
2686
|
-
|
|
2687
2666
|
const SOUND_PREF_STORAGE_KEY = 'pinokio:idle-sound';
|
|
2688
2667
|
const primeAudio = async () => {
|
|
2689
2668
|
// Determine whether the user picked a custom `/sound/...` clip.
|
|
@@ -2747,31 +2726,26 @@ if (typeof hotkeys === 'function') {
|
|
|
2747
2726
|
if (!(forceParam || isLikelyMobile())) {
|
|
2748
2727
|
return;
|
|
2749
2728
|
}
|
|
2750
|
-
if (window.
|
|
2729
|
+
if (window.__pinokioAudioPrimerInstalled || window.__pinokioAudioPrimerInstalling) {
|
|
2751
2730
|
return;
|
|
2752
2731
|
}
|
|
2753
|
-
try { window.
|
|
2754
|
-
const overlay = createCurtain();
|
|
2732
|
+
try { window.__pinokioAudioPrimerInstalling = true; } catch (_) {}
|
|
2755
2733
|
let handled = false;
|
|
2756
|
-
const
|
|
2734
|
+
const cleanup = () => {
|
|
2735
|
+
document.removeEventListener('click', onFirstInteraction, true);
|
|
2736
|
+
document.removeEventListener('touchend', onFirstInteraction, true);
|
|
2737
|
+
document.removeEventListener('keydown', onFirstInteraction, true);
|
|
2738
|
+
};
|
|
2739
|
+
const onFirstInteraction = async () => {
|
|
2757
2740
|
if (handled) return;
|
|
2758
2741
|
handled = true;
|
|
2759
|
-
|
|
2742
|
+
cleanup();
|
|
2760
2743
|
try { await primeAudio(); } catch (_) {}
|
|
2761
|
-
try {
|
|
2762
|
-
try { window.__pinokioConnectCurtainInstalled = true; window.__pinokioConnectCurtainInstalling = false; } catch (_) {}
|
|
2744
|
+
try { window.__pinokioAudioPrimerInstalled = true; window.__pinokioAudioPrimerInstalling = false; } catch (_) {}
|
|
2763
2745
|
};
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
return;
|
|
2768
|
-
}
|
|
2769
|
-
const key = e.key || '';
|
|
2770
|
-
if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
|
|
2771
|
-
onTap(e);
|
|
2772
|
-
}
|
|
2773
|
-
}, { capture: true });
|
|
2774
|
-
document.body.appendChild(overlay);
|
|
2746
|
+
document.addEventListener('click', onFirstInteraction, true);
|
|
2747
|
+
document.addEventListener('touchend', onFirstInteraction, true);
|
|
2748
|
+
document.addEventListener('keydown', onFirstInteraction, true);
|
|
2775
2749
|
};
|
|
2776
2750
|
|
|
2777
2751
|
if (document.readyState === 'loading') {
|
package/server/public/install.js
CHANGED
|
@@ -38,13 +38,14 @@ const installname = async (url, name, options) => {
|
|
|
38
38
|
cancelButtonText: 'Cancel',
|
|
39
39
|
confirmButtonText: 'Download',
|
|
40
40
|
buttonsStyling: false,
|
|
41
|
-
backdrop:
|
|
41
|
+
backdrop: true,
|
|
42
42
|
width: 'min(460px, 92vw)',
|
|
43
43
|
allowOutsideClick: () => !Swal.isLoading(),
|
|
44
44
|
allowEscapeKey: true,
|
|
45
45
|
showLoaderOnConfirm: true,
|
|
46
46
|
loaderHtml: '<span class="pinokio-download-loader-spinner" aria-hidden="true"></span><span class="pinokio-download-loader-text">Downloading...</span>',
|
|
47
47
|
customClass: {
|
|
48
|
+
container: 'pinokio-download-container',
|
|
48
49
|
popup: 'pinokio-download-modal',
|
|
49
50
|
htmlContainer: 'pinokio-download-html',
|
|
50
51
|
inputLabel: 'pinokio-download-label',
|
|
@@ -101,6 +102,23 @@ const escapeInstallHtml = (value) => {
|
|
|
101
102
|
.replace(/'/g, ''')
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
const INLINE_INSTALL_STATUS_SPINNER_HTML = `
|
|
106
|
+
<span class="pinokio-install-inline-status-spinner" aria-hidden="true">
|
|
107
|
+
<span class="pinokio-install-inline-status-spinner-dot pinokio-install-inline-status-spinner-dot-1"></span>
|
|
108
|
+
<span class="pinokio-install-inline-status-spinner-dot pinokio-install-inline-status-spinner-dot-2"></span>
|
|
109
|
+
<span class="pinokio-install-inline-status-spinner-dot pinokio-install-inline-status-spinner-dot-3"></span>
|
|
110
|
+
<span class="pinokio-install-inline-status-spinner-dot pinokio-install-inline-status-spinner-dot-4"></span>
|
|
111
|
+
</span>`
|
|
112
|
+
|
|
113
|
+
const getInlineInstallStatusIconHtml = (iconClass) => {
|
|
114
|
+
const normalizedIconClass = String(iconClass || '')
|
|
115
|
+
const isProgressIcon = /\bfa-circle-notch\b/.test(normalizedIconClass) || /\bfa-spin\b/.test(normalizedIconClass)
|
|
116
|
+
if (isProgressIcon) {
|
|
117
|
+
return INLINE_INSTALL_STATUS_SPINNER_HTML
|
|
118
|
+
}
|
|
119
|
+
return `<span class="pinokio-install-inline-status-icon" aria-hidden="true"><i class="${escapeInstallHtml(normalizedIconClass)}"></i></span>`
|
|
120
|
+
}
|
|
121
|
+
|
|
104
122
|
const ensureInlineInstallStatus = () => {
|
|
105
123
|
let status = document.getElementById(INLINE_INSTALL_STATUS_ID)
|
|
106
124
|
if (status) {
|
|
@@ -126,7 +144,7 @@ const setInlineInstallStatus = ({ state, title, detailHtml, iconClass }) => {
|
|
|
126
144
|
status.className = `pinokio-install-inline-status is-${normalizedState}`
|
|
127
145
|
status.innerHTML = `
|
|
128
146
|
<div class="pinokio-install-inline-status-shell">
|
|
129
|
-
|
|
147
|
+
${getInlineInstallStatusIconHtml(iconClass)}
|
|
130
148
|
<div class="pinokio-install-inline-status-copy">
|
|
131
149
|
<div class="pinokio-install-inline-status-title">${escapeInstallHtml(title || '')}</div>
|
|
132
150
|
${detailHtml ? `<div class="pinokio-install-inline-status-detail">${detailHtml}</div>` : ''}
|
package/server/public/layout.js
CHANGED
|
@@ -801,7 +801,7 @@
|
|
|
801
801
|
};
|
|
802
802
|
|
|
803
803
|
window.PinokioLayout = api;
|
|
804
|
-
// Mobile
|
|
804
|
+
// Mobile notification audio priming is centralized in common.js to avoid duplicates
|
|
805
805
|
|
|
806
806
|
// Top-level notification listener (indicator + optional chime) for mobile
|
|
807
807
|
(function initTopLevelNotificationListener() {
|