pinokiod 3.214.0 → 3.215.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.
- package/kernel/shell.js +2 -2
- package/package.json +1 -1
- package/server/public/layout.js +10 -2
- package/server/public/pinokio-touch.js +86 -0
- package/server/views/app.ejs +201 -12
- package/server/views/bootstrap.ejs +3 -0
- package/server/views/editor.ejs +3 -0
- package/server/views/init/index.ejs +3 -0
- package/server/views/install.ejs +3 -0
- package/server/views/layout.ejs +11 -2
- package/server/views/pro.ejs +3 -0
- package/server/views/prototype/index.ejs +3 -0
- package/server/views/shell.ejs +2 -1
- package/server/views/terminal.ejs +2 -0
package/kernel/shell.js
CHANGED
package/package.json
CHANGED
package/server/public/layout.js
CHANGED
|
@@ -753,9 +753,17 @@
|
|
|
753
753
|
stripTransientQueryParams();
|
|
754
754
|
}
|
|
755
755
|
|
|
756
|
+
let resizeScheduled = false;
|
|
756
757
|
function onResize() {
|
|
757
|
-
|
|
758
|
-
|
|
758
|
+
if (resizeScheduled) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
resizeScheduled = true;
|
|
762
|
+
requestAnimationFrame(() => {
|
|
763
|
+
resizeScheduled = false;
|
|
764
|
+
applyLayout();
|
|
765
|
+
attachGutterHandlers();
|
|
766
|
+
});
|
|
759
767
|
}
|
|
760
768
|
|
|
761
769
|
initLayout();
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
;(function() {
|
|
2
|
+
if (typeof window === 'undefined') {
|
|
3
|
+
return
|
|
4
|
+
}
|
|
5
|
+
if (window.PinokioTouch) {
|
|
6
|
+
return
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const TOUCH_FOCUS_KEY = '__pinokioTouchFocusHandler'
|
|
10
|
+
const TOUCH_OPTIONS = (() => {
|
|
11
|
+
let passiveSupported = false
|
|
12
|
+
try {
|
|
13
|
+
const opts = Object.defineProperty({}, 'passive', {
|
|
14
|
+
get() {
|
|
15
|
+
passiveSupported = true
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
window.addEventListener('test-passive', null, opts)
|
|
20
|
+
window.removeEventListener('test-passive', null, opts)
|
|
21
|
+
} catch (_) {}
|
|
22
|
+
return passiveSupported ? { passive: true } : false
|
|
23
|
+
})()
|
|
24
|
+
|
|
25
|
+
const isTouchLikeEvent = (event) => {
|
|
26
|
+
if (!event || typeof event !== 'object') {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
const pointerType = typeof event.pointerType === 'string' ? event.pointerType.toLowerCase() : ''
|
|
30
|
+
if (pointerType) {
|
|
31
|
+
return pointerType === 'touch' || pointerType === 'pen'
|
|
32
|
+
}
|
|
33
|
+
const touches = event.touches || event.changedTouches
|
|
34
|
+
return Boolean(touches && touches.length > 0)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const bindPointer = (target, handler, options = {}) => {
|
|
38
|
+
if (!target || typeof target.addEventListener !== 'function' || typeof handler !== 'function') {
|
|
39
|
+
return () => {}
|
|
40
|
+
}
|
|
41
|
+
const pointerOpts = Object.prototype.hasOwnProperty.call(options, 'pointer') ? options.pointer : undefined
|
|
42
|
+
const touchOpts = Object.prototype.hasOwnProperty.call(options, 'touch') ? options.touch : TOUCH_OPTIONS
|
|
43
|
+
target.addEventListener('pointerdown', handler, pointerOpts)
|
|
44
|
+
target.addEventListener('touchstart', handler, touchOpts)
|
|
45
|
+
return () => {
|
|
46
|
+
target.removeEventListener('pointerdown', handler, pointerOpts)
|
|
47
|
+
target.removeEventListener('touchstart', handler, touchOpts)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const bindTerminalFocus = (term, container) => {
|
|
52
|
+
if (!term || typeof term.focus !== 'function' || !container) {
|
|
53
|
+
return () => {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const existing = container[TOUCH_FOCUS_KEY]
|
|
57
|
+
if (typeof existing === 'function') {
|
|
58
|
+
existing()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const handler = (event) => {
|
|
62
|
+
if (!isTouchLikeEvent(event)) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
term.focus()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const unbind = bindPointer(container, handler)
|
|
69
|
+
container[TOUCH_FOCUS_KEY] = unbind
|
|
70
|
+
|
|
71
|
+
return () => {
|
|
72
|
+
if (typeof unbind === 'function') {
|
|
73
|
+
unbind()
|
|
74
|
+
}
|
|
75
|
+
if (container[TOUCH_FOCUS_KEY] === unbind) {
|
|
76
|
+
delete container[TOUCH_FOCUS_KEY]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
window.PinokioTouch = {
|
|
82
|
+
bindPointer,
|
|
83
|
+
bindTerminalFocus,
|
|
84
|
+
isTouchLikeEvent
|
|
85
|
+
}
|
|
86
|
+
})()
|
package/server/views/app.ejs
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<link href="/filepond-plugin-image-edit.min.css" rel="stylesheet" />
|
|
15
15
|
<link href="/style.css" rel="stylesheet"/>
|
|
16
16
|
<script src="/modalinput.js"></script>
|
|
17
|
+
<script src="/pinokio-touch.js"></script>
|
|
17
18
|
<% if (agent === "electron") { %>
|
|
18
19
|
<link href="/electron.css" rel="stylesheet"/>
|
|
19
20
|
<% } %>
|
|
@@ -3437,6 +3438,8 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3437
3438
|
let tabLinkRouterInfoPromise = null
|
|
3438
3439
|
let tabLinkRouterInfoExpiry = 0
|
|
3439
3440
|
let tabLinkRouterHttpsActive = null
|
|
3441
|
+
let tabLinkPeerInfoPromise = null
|
|
3442
|
+
let tabLinkPeerInfoExpiry = 0
|
|
3440
3443
|
|
|
3441
3444
|
const ensureTabLinkPopoverEl = () => {
|
|
3442
3445
|
if (!tabLinkPopoverEl) {
|
|
@@ -3475,6 +3478,27 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3475
3478
|
return tabLinkPopoverEl
|
|
3476
3479
|
}
|
|
3477
3480
|
|
|
3481
|
+
const ensurePeerInfo = async () => {
|
|
3482
|
+
const now = Date.now()
|
|
3483
|
+
if (!tabLinkPeerInfoPromise || now > tabLinkPeerInfoExpiry) {
|
|
3484
|
+
tabLinkPeerInfoPromise = fetch("/pinokio/peer", {
|
|
3485
|
+
method: "GET",
|
|
3486
|
+
headers: {
|
|
3487
|
+
"Accept": "application/json"
|
|
3488
|
+
}
|
|
3489
|
+
})
|
|
3490
|
+
.then((response) => {
|
|
3491
|
+
if (!response.ok) {
|
|
3492
|
+
throw new Error("Failed to load peer info")
|
|
3493
|
+
}
|
|
3494
|
+
return response.json()
|
|
3495
|
+
})
|
|
3496
|
+
.catch(() => null)
|
|
3497
|
+
tabLinkPeerInfoExpiry = now + 3000
|
|
3498
|
+
}
|
|
3499
|
+
return tabLinkPeerInfoPromise
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3478
3502
|
const canonicalizeUrl = (value) => {
|
|
3479
3503
|
try {
|
|
3480
3504
|
const parsed = new URL(value, location.origin)
|
|
@@ -4114,6 +4138,24 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4114
4138
|
}
|
|
4115
4139
|
|
|
4116
4140
|
const baseHref = link.href
|
|
4141
|
+
let canonicalBase = canonicalizeUrl(baseHref)
|
|
4142
|
+
if (canonicalBase && isHttpUrl(canonicalBase)) {
|
|
4143
|
+
canonicalBase = ensureHttpDirectoryUrl(canonicalBase)
|
|
4144
|
+
}
|
|
4145
|
+
let parsedBaseUrl = null
|
|
4146
|
+
let sameOrigin = false
|
|
4147
|
+
let basePortNormalized = ""
|
|
4148
|
+
try {
|
|
4149
|
+
parsedBaseUrl = new URL(baseHref, location.origin)
|
|
4150
|
+
sameOrigin = parsedBaseUrl.origin === location.origin
|
|
4151
|
+
if (parsedBaseUrl) {
|
|
4152
|
+
basePortNormalized = parsedBaseUrl.port
|
|
4153
|
+
if (!basePortNormalized) {
|
|
4154
|
+
const proto = parsedBaseUrl.protocol ? parsedBaseUrl.protocol.toLowerCase() : "http:"
|
|
4155
|
+
basePortNormalized = proto === "https:" ? "443" : "80"
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
} catch (_) {}
|
|
4117
4159
|
const projectSlug = extractProjectSlug(link).toLowerCase()
|
|
4118
4160
|
const entries = []
|
|
4119
4161
|
const entryByUrl = new Map()
|
|
@@ -4129,10 +4171,11 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4129
4171
|
return
|
|
4130
4172
|
}
|
|
4131
4173
|
let skip = false
|
|
4174
|
+
const allowSameOrigin = opts && opts.allowSameOrigin === true
|
|
4132
4175
|
try {
|
|
4133
4176
|
const parsed = new URL(canonical)
|
|
4134
4177
|
const originLower = parsed.origin.toLowerCase()
|
|
4135
|
-
if (originLower === location.origin.toLowerCase()) {
|
|
4178
|
+
if (!allowSameOrigin && originLower === location.origin.toLowerCase()) {
|
|
4136
4179
|
skip = true
|
|
4137
4180
|
}
|
|
4138
4181
|
} catch (_) {
|
|
@@ -4158,25 +4201,29 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4158
4201
|
}
|
|
4159
4202
|
|
|
4160
4203
|
if (isHttpUrl(baseHref)) {
|
|
4161
|
-
addEntry("http", "HTTP", baseHref)
|
|
4204
|
+
addEntry("http", "HTTP", baseHref, { allowSameOrigin: true })
|
|
4162
4205
|
} else if (isHttpsUrl(baseHref)) {
|
|
4163
|
-
addEntry("https", "HTTPS", baseHref)
|
|
4206
|
+
addEntry("https", "HTTPS", baseHref, { allowSameOrigin: true })
|
|
4164
4207
|
} else {
|
|
4165
|
-
addEntry("url", "URL", baseHref)
|
|
4208
|
+
addEntry("url", "URL", baseHref, { allowSameOrigin: true })
|
|
4166
4209
|
}
|
|
4167
4210
|
|
|
4168
4211
|
const httpCandidates = new Map() // url -> { qr: boolean }
|
|
4169
4212
|
const httpsCandidates = new Set()
|
|
4170
4213
|
|
|
4171
4214
|
if (isHttpUrl(baseHref)) {
|
|
4172
|
-
httpCandidates.set(canonicalizeUrl(baseHref), { qr: false })
|
|
4215
|
+
httpCandidates.set(canonicalBase || canonicalizeUrl(baseHref), { qr: false })
|
|
4173
4216
|
} else if (isHttpsUrl(baseHref)) {
|
|
4174
|
-
|
|
4217
|
+
if (canonicalBase) {
|
|
4218
|
+
httpsCandidates.add(canonicalBase)
|
|
4219
|
+
} else {
|
|
4220
|
+
httpsCandidates.add(canonicalizeUrl(baseHref))
|
|
4221
|
+
}
|
|
4175
4222
|
}
|
|
4176
4223
|
|
|
4177
4224
|
if (projectSlug) {
|
|
4178
4225
|
try {
|
|
4179
|
-
const baseUrl = new URL(baseHref, location.origin)
|
|
4226
|
+
const baseUrl = parsedBaseUrl || new URL(baseHref, location.origin)
|
|
4180
4227
|
let pathname = baseUrl.pathname || "/"
|
|
4181
4228
|
if (pathname.endsWith("/index.html")) {
|
|
4182
4229
|
pathname = pathname.slice(0, -"/index.html".length)
|
|
@@ -4230,7 +4277,7 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4230
4277
|
|
|
4231
4278
|
// Add external 192.168.* http host:port candidates mapped from the same internal port as base HTTP
|
|
4232
4279
|
try {
|
|
4233
|
-
const base = new URL(baseHref, location.origin)
|
|
4280
|
+
const base = parsedBaseUrl || new URL(baseHref, location.origin)
|
|
4234
4281
|
let basePort = base.port
|
|
4235
4282
|
if (!basePort) {
|
|
4236
4283
|
basePort = base.protocol.toLowerCase() === 'https:' ? '443' : '80'
|
|
@@ -4283,6 +4330,65 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4283
4330
|
addEntry("https", "HTTPS", url)
|
|
4284
4331
|
})
|
|
4285
4332
|
|
|
4333
|
+
if (sameOrigin) {
|
|
4334
|
+
try {
|
|
4335
|
+
const peerInfo = await ensurePeerInfo()
|
|
4336
|
+
const peerHost = peerInfo && typeof peerInfo.host === "string" ? peerInfo.host.trim() : ""
|
|
4337
|
+
if (peerHost) {
|
|
4338
|
+
const peerHostLower = peerHost.toLowerCase()
|
|
4339
|
+
// Skip loopback-style peers
|
|
4340
|
+
if (peerHostLower !== "localhost" && !peerHostLower.startsWith("127.")) {
|
|
4341
|
+
const baseUrl = parsedBaseUrl || new URL(baseHref, location.origin)
|
|
4342
|
+
const baseHostLower = (baseUrl.hostname || "").toLowerCase()
|
|
4343
|
+
if (peerHostLower !== baseHostLower) {
|
|
4344
|
+
const baseProtocol = baseUrl.protocol ? baseUrl.protocol.toLowerCase() : "http:"
|
|
4345
|
+
const scheme = baseProtocol === "https:" ? "https://" : "http://"
|
|
4346
|
+
const port = baseUrl.port || (baseProtocol === "https:" ? "443" : "80")
|
|
4347
|
+
const hostPort = port ? `${peerHostLower}:${port}` : peerHostLower
|
|
4348
|
+
const pathSegment = baseUrl.pathname || "/"
|
|
4349
|
+
const searchSegment = baseUrl.search || ""
|
|
4350
|
+
const fallbackUrl = `${scheme}${hostPort}${pathSegment}${searchSegment}`
|
|
4351
|
+
const label = baseProtocol === "https:" ? "HTTPS" : "HTTP"
|
|
4352
|
+
addEntry(baseProtocol === "https:" ? "https" : "http", label, fallbackUrl, { qr: true })
|
|
4353
|
+
}
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
} catch (_) {}
|
|
4357
|
+
|
|
4358
|
+
const matchesBasePort = (value) => {
|
|
4359
|
+
if (!basePortNormalized) {
|
|
4360
|
+
return true
|
|
4361
|
+
}
|
|
4362
|
+
try {
|
|
4363
|
+
const parsed = new URL(value, location.origin)
|
|
4364
|
+
let port = parsed.port
|
|
4365
|
+
if (!port) {
|
|
4366
|
+
const proto = parsed.protocol ? parsed.protocol.toLowerCase() : "http:"
|
|
4367
|
+
port = proto === "https:" ? "443" : "80"
|
|
4368
|
+
}
|
|
4369
|
+
return port === basePortNormalized
|
|
4370
|
+
} catch (_) {
|
|
4371
|
+
return false
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
|
|
4375
|
+
const filteredEntries = entries.filter((entry) => {
|
|
4376
|
+
if (!entry || !entry.url) {
|
|
4377
|
+
return false
|
|
4378
|
+
}
|
|
4379
|
+
if (entry.url === canonicalBase) {
|
|
4380
|
+
return true
|
|
4381
|
+
}
|
|
4382
|
+
if (entry.qr === true) {
|
|
4383
|
+
return matchesBasePort(entry.url)
|
|
4384
|
+
}
|
|
4385
|
+
return false
|
|
4386
|
+
})
|
|
4387
|
+
if (filteredEntries.length > 0) {
|
|
4388
|
+
return filteredEntries
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4286
4392
|
return entries
|
|
4287
4393
|
}
|
|
4288
4394
|
|
|
@@ -4350,10 +4456,22 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4350
4456
|
|
|
4351
4457
|
let sameOrigin = false
|
|
4352
4458
|
let canonicalBase = canonicalizeUrl(link.href)
|
|
4459
|
+
if (canonicalBase && isHttpUrl(canonicalBase)) {
|
|
4460
|
+
canonicalBase = ensureHttpDirectoryUrl(canonicalBase)
|
|
4461
|
+
}
|
|
4462
|
+
let basePortNormalized = ""
|
|
4353
4463
|
try {
|
|
4354
4464
|
const linkUrl = new URL(link.href, location.href)
|
|
4355
4465
|
sameOrigin = linkUrl.origin === location.origin
|
|
4356
4466
|
canonicalBase = canonicalizeUrl(linkUrl.href)
|
|
4467
|
+
if (canonicalBase && isHttpUrl(canonicalBase)) {
|
|
4468
|
+
canonicalBase = ensureHttpDirectoryUrl(canonicalBase)
|
|
4469
|
+
}
|
|
4470
|
+
basePortNormalized = linkUrl.port
|
|
4471
|
+
if (!basePortNormalized) {
|
|
4472
|
+
const proto = linkUrl.protocol ? linkUrl.protocol.toLowerCase() : "http:"
|
|
4473
|
+
basePortNormalized = proto === "https:" ? "443" : "80"
|
|
4474
|
+
}
|
|
4357
4475
|
} catch (_) {
|
|
4358
4476
|
hideTabLinkPopover({ immediate: true })
|
|
4359
4477
|
return
|
|
@@ -4416,6 +4534,23 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4416
4534
|
|
|
4417
4535
|
if (sameOrigin) {
|
|
4418
4536
|
const slug = extractProjectSlug(link).toLowerCase()
|
|
4537
|
+
const matchesBasePort = (value) => {
|
|
4538
|
+
if (!basePortNormalized) {
|
|
4539
|
+
return true
|
|
4540
|
+
}
|
|
4541
|
+
try {
|
|
4542
|
+
const parsed = new URL(value, location.origin)
|
|
4543
|
+
let port = parsed.port
|
|
4544
|
+
if (!port) {
|
|
4545
|
+
const proto = parsed.protocol ? parsed.protocol.toLowerCase() : "http:"
|
|
4546
|
+
port = proto === "https:" ? "443" : "80"
|
|
4547
|
+
}
|
|
4548
|
+
return port === basePortNormalized
|
|
4549
|
+
} catch (_) {
|
|
4550
|
+
return false
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
|
|
4419
4554
|
if (slug) {
|
|
4420
4555
|
entries = entries.filter((entry) => {
|
|
4421
4556
|
if (!entry || !entry.url) {
|
|
@@ -4424,6 +4559,9 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4424
4559
|
if (entry.url === canonicalBase) {
|
|
4425
4560
|
return true
|
|
4426
4561
|
}
|
|
4562
|
+
if (entry.qr === true) {
|
|
4563
|
+
return matchesBasePort(entry.url)
|
|
4564
|
+
}
|
|
4427
4565
|
try {
|
|
4428
4566
|
const parsed = new URL(entry.url)
|
|
4429
4567
|
const hostLower = parsed.hostname ? parsed.hostname.toLowerCase() : ""
|
|
@@ -4452,9 +4590,33 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
4452
4590
|
return false
|
|
4453
4591
|
})
|
|
4454
4592
|
} else {
|
|
4455
|
-
entries = entries.filter((entry) =>
|
|
4593
|
+
entries = entries.filter((entry) => {
|
|
4594
|
+
if (!entry || !entry.url) {
|
|
4595
|
+
return false
|
|
4596
|
+
}
|
|
4597
|
+
if (entry.url === canonicalBase) {
|
|
4598
|
+
return true
|
|
4599
|
+
}
|
|
4600
|
+
if (entry.qr === true) {
|
|
4601
|
+
return matchesBasePort(entry.url)
|
|
4602
|
+
}
|
|
4603
|
+
return false
|
|
4604
|
+
})
|
|
4456
4605
|
}
|
|
4457
4606
|
|
|
4607
|
+
entries = entries.filter((entry) => {
|
|
4608
|
+
if (!entry || !entry.url) {
|
|
4609
|
+
return false
|
|
4610
|
+
}
|
|
4611
|
+
if (entry.url === canonicalBase) {
|
|
4612
|
+
return true
|
|
4613
|
+
}
|
|
4614
|
+
if (entry.qr === true) {
|
|
4615
|
+
return matchesBasePort(entry.url)
|
|
4616
|
+
}
|
|
4617
|
+
return false
|
|
4618
|
+
})
|
|
4619
|
+
|
|
4458
4620
|
const hasAlternate = entries.some((entry) => entry.url !== canonicalBase)
|
|
4459
4621
|
if (!hasAlternate) {
|
|
4460
4622
|
hideTabLinkPopover({ immediate: true })
|
|
@@ -5692,7 +5854,9 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
5692
5854
|
targetFrame.classList.remove("hidden")
|
|
5693
5855
|
let mode = target.getAttribute("data-mode")
|
|
5694
5856
|
if (mode === "refresh") {
|
|
5695
|
-
targetFrame.src
|
|
5857
|
+
if (targetFrame.src !== target.href) {
|
|
5858
|
+
targetFrame.src = target.href
|
|
5859
|
+
}
|
|
5696
5860
|
} else {
|
|
5697
5861
|
if (eventParam) {
|
|
5698
5862
|
if (target.closest(".h")) {
|
|
@@ -5700,7 +5864,9 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
5700
5864
|
eventParam.stopPropagation()
|
|
5701
5865
|
let sub = subUrlOf(target.href, targetFrame.src)
|
|
5702
5866
|
if (!sub) {
|
|
5703
|
-
targetFrame.src
|
|
5867
|
+
if (targetFrame.src !== target.href) {
|
|
5868
|
+
targetFrame.src = target.href
|
|
5869
|
+
}
|
|
5704
5870
|
}
|
|
5705
5871
|
} else if (target.closest(".s")) {
|
|
5706
5872
|
// load unconditionally => .s doesn't change
|
|
@@ -5713,7 +5879,9 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
5713
5879
|
}
|
|
5714
5880
|
} else {
|
|
5715
5881
|
if (force) {
|
|
5716
|
-
targetFrame.src
|
|
5882
|
+
if (targetFrame.src !== target.href) {
|
|
5883
|
+
targetFrame.src = target.href
|
|
5884
|
+
}
|
|
5717
5885
|
}
|
|
5718
5886
|
}
|
|
5719
5887
|
let frameHref
|
|
@@ -6538,10 +6706,31 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
6538
6706
|
}
|
|
6539
6707
|
}
|
|
6540
6708
|
|
|
6709
|
+
const isTouchLikeEvent = window.PinokioTouch.isTouchLikeEvent
|
|
6710
|
+
const handleTouchSelection = (event) => {
|
|
6711
|
+
if (!isTouchLikeEvent(event)) {
|
|
6712
|
+
return
|
|
6713
|
+
}
|
|
6714
|
+
const link = event.target?.closest?.(".frame-link") || null
|
|
6715
|
+
if (!link) {
|
|
6716
|
+
return
|
|
6717
|
+
}
|
|
6718
|
+
if (link.classList.contains("reveal") || link.hasAttribute("data-action") || link.hasAttribute("data-filepath") || link.hasAttribute("data-confirm")) {
|
|
6719
|
+
return
|
|
6720
|
+
}
|
|
6721
|
+
if (event.target?.closest?.(".del") || event.target?.closest?.(".shutdown")) {
|
|
6722
|
+
return
|
|
6723
|
+
}
|
|
6724
|
+
renderSelection({ explicitTarget: link })
|
|
6725
|
+
}
|
|
6541
6726
|
const navContainers = [document.querySelector("aside"), document.querySelector("#fs-status")]
|
|
6542
6727
|
navContainers.forEach((container) => {
|
|
6543
6728
|
if (container) {
|
|
6544
6729
|
container.addEventListener("click", handleMenuClick)
|
|
6730
|
+
window.PinokioTouch.bindPointer(container, handleTouchSelection, {
|
|
6731
|
+
pointer: true,
|
|
6732
|
+
touch: { passive: true, capture: true }
|
|
6733
|
+
})
|
|
6545
6734
|
}
|
|
6546
6735
|
})
|
|
6547
6736
|
setupTabLinkHover()
|
|
@@ -80,6 +80,7 @@ body {
|
|
|
80
80
|
</style>
|
|
81
81
|
<script src="/noty.js"></script>
|
|
82
82
|
<script src="/notyq.js"></script>
|
|
83
|
+
<script src="/pinokio-touch.js"></script>
|
|
83
84
|
<script src="/xterm.js"></script>
|
|
84
85
|
<script src="/xterm-addon-fit.js"></script>
|
|
85
86
|
<script src="/xterm-addon-web-links.js"></script>
|
|
@@ -158,6 +159,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
158
159
|
term.loadAddon(fitAddon);
|
|
159
160
|
term.loadAddon(new WebLinksAddon.WebLinksAddon());
|
|
160
161
|
term.open(document.querySelector("#terminal"))
|
|
162
|
+
const terminalContainer = document.querySelector("#terminal")
|
|
163
|
+
window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
|
|
161
164
|
fitAddon.fit();
|
|
162
165
|
|
|
163
166
|
let socket = new Socket()
|
package/server/views/editor.ejs
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<script src="/sweetalert2.js"></script>
|
|
15
15
|
<script src="/Socket.js"></script>
|
|
16
16
|
<script src="/terminal-settings.js"></script>
|
|
17
|
+
<script src="/pinokio-touch.js"></script>
|
|
17
18
|
<script src="/common.js"></script>
|
|
18
19
|
<script src="/he.js"></script>
|
|
19
20
|
<script src="/opener.js"></script>
|
|
@@ -688,6 +689,8 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
688
689
|
window.PinokioTerminalSettings.register(term, { baseConfig })
|
|
689
690
|
}
|
|
690
691
|
term.open(document.querySelector("#terminal"))
|
|
692
|
+
const terminalContainer = document.querySelector("#terminal")
|
|
693
|
+
window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
|
|
691
694
|
|
|
692
695
|
|
|
693
696
|
term.attachCustomKeyEventHandler(event => {
|
|
@@ -1498,6 +1498,7 @@ body.dark .ace-editor {
|
|
|
1498
1498
|
<script src="/normalize.js"></script>
|
|
1499
1499
|
<script src="/Socket.js"></script>
|
|
1500
1500
|
<script src="/terminal-settings.js"></script>
|
|
1501
|
+
<script src="/pinokio-touch.js"></script>
|
|
1501
1502
|
<script src="/noty.js"></script>
|
|
1502
1503
|
<script src="/notyq.js"></script>
|
|
1503
1504
|
<script src="/xterm.js"></script>
|
|
@@ -2667,6 +2668,8 @@ const createTerm = async (_theme) => {
|
|
|
2667
2668
|
window.PinokioTerminalSettings.register(term, { baseConfig })
|
|
2668
2669
|
}
|
|
2669
2670
|
term.open(document.querySelector("#terminal2"))
|
|
2671
|
+
const terminalContainer = document.querySelector("#terminal2")
|
|
2672
|
+
window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
|
|
2670
2673
|
document.querySelector("#terminal-container").classList.remove("hidden")
|
|
2671
2674
|
|
|
2672
2675
|
term.attachCustomKeyEventHandler(event => {
|
package/server/views/install.ejs
CHANGED
|
@@ -125,6 +125,7 @@ body {
|
|
|
125
125
|
<script src="/xterm-theme.js"></script>
|
|
126
126
|
<script src="/Socket.js"></script>
|
|
127
127
|
<script src="/terminal-settings.js"></script>
|
|
128
|
+
<script src="/pinokio-touch.js"></script>
|
|
128
129
|
<!--
|
|
129
130
|
<script src="/install.js"></script>
|
|
130
131
|
-->
|
|
@@ -179,6 +180,8 @@ const createTerm = async (_theme) => {
|
|
|
179
180
|
window.PinokioTerminalSettings.register(term, { baseConfig })
|
|
180
181
|
}
|
|
181
182
|
term.open(document.querySelector("#terminal"))
|
|
183
|
+
const terminalContainer = document.querySelector("#terminal")
|
|
184
|
+
window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
|
|
182
185
|
|
|
183
186
|
term.attachCustomKeyEventHandler(event => {
|
|
184
187
|
if (event.ctrlKey && event.key === 'c' && term.hasSelection()) {
|
package/server/views/layout.ejs
CHANGED
|
@@ -123,14 +123,23 @@
|
|
|
123
123
|
<script>
|
|
124
124
|
(function () {
|
|
125
125
|
const root = document.documentElement;
|
|
126
|
+
let lastViewportHeight = null;
|
|
126
127
|
const updateViewportHeight = () => {
|
|
127
128
|
const viewport = window.visualViewport;
|
|
128
129
|
const height = viewport ? viewport.height : window.innerHeight;
|
|
129
130
|
if (!height || !Number.isFinite(height)) {
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
const clampedHeight = Math.max(0, height);
|
|
134
|
+
const roundedHeight = Number(clampedHeight.toFixed(2));
|
|
135
|
+
if (lastViewportHeight === roundedHeight) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
lastViewportHeight = roundedHeight;
|
|
139
|
+
root.style.setProperty('--layout-viewport-height', `${roundedHeight}px`);
|
|
140
|
+
window.dispatchEvent(new CustomEvent('pinokio:viewport-change', {
|
|
141
|
+
detail: { height: roundedHeight },
|
|
142
|
+
}));
|
|
134
143
|
};
|
|
135
144
|
|
|
136
145
|
const resizeEvents = ['resize', 'orientationchange'];
|
package/server/views/pro.ejs
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<script src="/sweetalert2.js"></script>
|
|
15
15
|
<script src="/Socket.js"></script>
|
|
16
16
|
<script src="/terminal-settings.js"></script>
|
|
17
|
+
<script src="/pinokio-touch.js"></script>
|
|
17
18
|
<script src="/common.js"></script>
|
|
18
19
|
<script src="/he.js"></script>
|
|
19
20
|
<script src="/opener.js"></script>
|
|
@@ -238,6 +239,8 @@ const createTerm = async (_theme) => {
|
|
|
238
239
|
window.PinokioTerminalSettings.register(term, { baseConfig })
|
|
239
240
|
}
|
|
240
241
|
term.open(document.querySelector("#terminal"))
|
|
242
|
+
const terminalContainer = document.querySelector("#terminal")
|
|
243
|
+
window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
|
|
241
244
|
|
|
242
245
|
term.attachCustomKeyEventHandler(event => {
|
|
243
246
|
if (event.ctrlKey && event.key === 'c' && term.hasSelection()) {
|
|
@@ -982,6 +982,7 @@ body.dark .appcanvas {
|
|
|
982
982
|
<script src="/normalize.js"></script>
|
|
983
983
|
<script src="/Socket.js"></script>
|
|
984
984
|
<script src="/terminal-settings.js"></script>
|
|
985
|
+
<script src="/pinokio-touch.js"></script>
|
|
985
986
|
<script src="/noty.js"></script>
|
|
986
987
|
<script src="/notyq.js"></script>
|
|
987
988
|
<script src="/xterm.js"></script>
|
|
@@ -1581,6 +1582,8 @@ async function displayResults(config) {
|
|
|
1581
1582
|
window.PinokioTerminalSettings.register(term, { baseConfig })
|
|
1582
1583
|
}
|
|
1583
1584
|
term.open(document.querySelector("#terminal"))
|
|
1585
|
+
const terminalContainer = document.querySelector("#terminal")
|
|
1586
|
+
window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
|
|
1584
1587
|
term.attachCustomKeyEventHandler(event => {
|
|
1585
1588
|
console.log({ event })
|
|
1586
1589
|
if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
|
package/server/views/shell.ejs
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<script src="/Socket.js"></script>
|
|
16
16
|
<script src="/terminal_input_tracker.js"></script>
|
|
17
17
|
<script src="/terminal-settings.js"></script>
|
|
18
|
+
<script src="/pinokio-touch.js"></script>
|
|
18
19
|
<script src="/common.js"></script>
|
|
19
20
|
<script src="/he.js"></script>
|
|
20
21
|
<script src="/opener.js"></script>
|
|
@@ -460,7 +461,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
460
461
|
})
|
|
461
462
|
firstPacketReceived = true
|
|
462
463
|
}
|
|
463
|
-
console.log({ packet, type: packet.type })
|
|
464
464
|
if (packet.type === 'start') {
|
|
465
465
|
console.log("refreshParent", packet)
|
|
466
466
|
refreshParent(packet)
|
|
@@ -1187,6 +1187,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1187
1187
|
dropOverlay.className = "terminal-drop-overlay"
|
|
1188
1188
|
dropOverlay.textContent = "Drop files to upload"
|
|
1189
1189
|
terminalContainer.appendChild(dropOverlay)
|
|
1190
|
+
window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
|
|
1190
1191
|
const dedupeClipboardFiles = (inputs) => {
|
|
1191
1192
|
const seen = new Set()
|
|
1192
1193
|
const results = []
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<script src="/Socket.js"></script>
|
|
16
16
|
<script src="/terminal_input_tracker.js"></script>
|
|
17
17
|
<script src="/terminal-settings.js"></script>
|
|
18
|
+
<script src="/pinokio-touch.js"></script>
|
|
18
19
|
<script src="/common.js"></script>
|
|
19
20
|
<script src="/he.js"></script>
|
|
20
21
|
<script src="/opener.js"></script>
|
|
@@ -1283,6 +1284,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1283
1284
|
dropOverlay.className = "terminal-drop-overlay"
|
|
1284
1285
|
dropOverlay.textContent = "Drop files to upload"
|
|
1285
1286
|
terminalContainer.appendChild(dropOverlay)
|
|
1287
|
+
window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
|
|
1286
1288
|
const dedupeClipboardFiles = (inputs) => {
|
|
1287
1289
|
const seen = new Set()
|
|
1288
1290
|
const results = []
|