pinokiod 3.311.0 → 3.313.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/api/index.js +23 -18
- package/kernel/index.js +1 -1
- package/package.json +1 -1
- package/server/index.js +28 -76
- package/server/public/create-launcher.js +25 -27
- package/server/public/install.js +2 -1
- package/server/views/agents.ejs +339 -29
- package/server/views/settings.ejs +117 -2
- package/server/views/terminal.ejs +4 -0
package/kernel/api/index.js
CHANGED
|
@@ -795,6 +795,9 @@ class Api {
|
|
|
795
795
|
let port = await this.kernel.port()
|
|
796
796
|
|
|
797
797
|
let { cwd, script } = await this.resolveScript(request.path)
|
|
798
|
+
const actionKey = request.action || 'run'
|
|
799
|
+
const steps = (script && Array.isArray(script[actionKey])) ? script[actionKey] : []
|
|
800
|
+
const totalSteps = steps.length
|
|
798
801
|
|
|
799
802
|
let name = path.relative(this.kernel.path("api"), cwd)
|
|
800
803
|
|
|
@@ -827,7 +830,7 @@ class Api {
|
|
|
827
830
|
...this.kernel.vars,
|
|
828
831
|
}
|
|
829
832
|
|
|
830
|
-
if (i <
|
|
833
|
+
if (i < totalSteps - 1) {
|
|
831
834
|
memory.next = i+1
|
|
832
835
|
} else {
|
|
833
836
|
memory.next = null
|
|
@@ -911,7 +914,7 @@ class Api {
|
|
|
911
914
|
|
|
912
915
|
rpc.current = i
|
|
913
916
|
|
|
914
|
-
rpc.total =
|
|
917
|
+
rpc.total = totalSteps
|
|
915
918
|
|
|
916
919
|
rpc.input = input
|
|
917
920
|
|
|
@@ -926,7 +929,7 @@ class Api {
|
|
|
926
929
|
if (rpc.hasOwnProperty("next")) {
|
|
927
930
|
console.log("next already exists, don't touch", rpc.next)
|
|
928
931
|
if (typeof rpc.next === "string") {
|
|
929
|
-
let run =
|
|
932
|
+
let run = steps
|
|
930
933
|
console.log("run", run)
|
|
931
934
|
for(let i=0; i<run.length; i++) {
|
|
932
935
|
let step = run[i]
|
|
@@ -937,7 +940,7 @@ class Api {
|
|
|
937
940
|
}
|
|
938
941
|
}
|
|
939
942
|
} else {
|
|
940
|
-
if (i <
|
|
943
|
+
if (i < totalSteps-1) {
|
|
941
944
|
rpc.next = i+1
|
|
942
945
|
} else {
|
|
943
946
|
rpc.next = null
|
|
@@ -961,7 +964,7 @@ class Api {
|
|
|
961
964
|
|
|
962
965
|
if (!should_run) {
|
|
963
966
|
// the current step is skipped, therefore the next value should be ignored as well
|
|
964
|
-
if (i <
|
|
967
|
+
if (i < totalSteps-1) {
|
|
965
968
|
rpc.next = i+1
|
|
966
969
|
} else {
|
|
967
970
|
rpc.next = null
|
|
@@ -1007,12 +1010,12 @@ class Api {
|
|
|
1007
1010
|
})
|
|
1008
1011
|
}
|
|
1009
1012
|
}
|
|
1010
|
-
return { request, input: null, step: rpc.next, total:
|
|
1013
|
+
return { request, input: null, step: rpc.next, total: totalSteps, args }
|
|
1011
1014
|
} else {
|
|
1012
1015
|
// still ongoing
|
|
1013
|
-
let next_rpc =
|
|
1016
|
+
let next_rpc = steps[rpc.next]
|
|
1014
1017
|
if (next_rpc) {
|
|
1015
|
-
return { request, rawrpc: next_rpc, input: null, step: rpc.next, total:
|
|
1018
|
+
return { request, rawrpc: next_rpc, input: null, step: rpc.next, total: totalSteps, args }
|
|
1016
1019
|
}
|
|
1017
1020
|
}
|
|
1018
1021
|
}
|
|
@@ -1213,7 +1216,7 @@ class Api {
|
|
|
1213
1216
|
description: "All scripts finished running. Running in daemon mode..."
|
|
1214
1217
|
}
|
|
1215
1218
|
})
|
|
1216
|
-
return { request, input: result, step: rpc.next, total:
|
|
1219
|
+
return { request, input: result, step: rpc.next, total: totalSteps, args }
|
|
1217
1220
|
} else {
|
|
1218
1221
|
// no next rpc to execute. Finish
|
|
1219
1222
|
this.kernel.memory.local[id] = {}
|
|
@@ -1237,13 +1240,13 @@ class Api {
|
|
|
1237
1240
|
}
|
|
1238
1241
|
})
|
|
1239
1242
|
}
|
|
1240
|
-
return { request, input: result, step: rpc.next, total:
|
|
1243
|
+
return { request, input: result, step: rpc.next, total: totalSteps, args }
|
|
1241
1244
|
}
|
|
1242
1245
|
|
|
1243
1246
|
} else {
|
|
1244
1247
|
// still going
|
|
1245
|
-
let next_rpc =
|
|
1246
|
-
return { request, rawrpc: next_rpc, input: result, step: rpc.next, total:
|
|
1248
|
+
let next_rpc = steps[rpc.next]
|
|
1249
|
+
return { request, rawrpc: next_rpc, input: result, step: rpc.next, total: totalSteps, args }
|
|
1247
1250
|
}
|
|
1248
1251
|
|
|
1249
1252
|
} catch (e) {
|
|
@@ -1491,9 +1494,12 @@ class Api {
|
|
|
1491
1494
|
data: "the endpoint does not exist: " + request.uri,
|
|
1492
1495
|
})
|
|
1493
1496
|
} else {
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
+
const actionKey = request.action || 'run'
|
|
1498
|
+
request.action = actionKey
|
|
1499
|
+
const steps = script ? script[actionKey] : null
|
|
1500
|
+
|
|
1501
|
+
// 3. Check if the resolved endpoint has the requested action attribute and it's an array
|
|
1502
|
+
if (Array.isArray(steps) && steps.length > 0) {
|
|
1497
1503
|
|
|
1498
1504
|
if (request.id) {
|
|
1499
1505
|
this.running[request.id] = true
|
|
@@ -1514,14 +1520,13 @@ class Api {
|
|
|
1514
1520
|
}
|
|
1515
1521
|
|
|
1516
1522
|
|
|
1517
|
-
|
|
1518
|
-
this.queue(request, script.run[0], request.input, 0, script.run.length, cwd, request.input)
|
|
1523
|
+
this.queue(request, steps[0], request.input, 0, steps.length, cwd, request.input)
|
|
1519
1524
|
|
|
1520
1525
|
} else {
|
|
1521
1526
|
this.ondata({
|
|
1522
1527
|
id: request.id || request.path,
|
|
1523
1528
|
type: "error",
|
|
1524
|
-
data:
|
|
1529
|
+
data: `missing or invalid attribute: ${actionKey}`
|
|
1525
1530
|
})
|
|
1526
1531
|
}
|
|
1527
1532
|
}
|
package/kernel/index.js
CHANGED
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -1650,10 +1650,6 @@ class Server {
|
|
|
1650
1650
|
}
|
|
1651
1651
|
let configArray = [{
|
|
1652
1652
|
key: "home",
|
|
1653
|
-
description: [
|
|
1654
|
-
"* NO white spaces (' ')",
|
|
1655
|
-
"* NO exFAT drives",
|
|
1656
|
-
],
|
|
1657
1653
|
val: this.kernel.homedir,
|
|
1658
1654
|
placeholder: "Enter the absolute path to use as your Pinokio home folder (D:\\pinokio, /Users/alice/pinokiofs, etc.)"
|
|
1659
1655
|
// }, {
|
|
@@ -1834,6 +1830,7 @@ class Server {
|
|
|
1834
1830
|
schemaPath = ""
|
|
1835
1831
|
}
|
|
1836
1832
|
|
|
1833
|
+
const actionKey = req.action || 'run'
|
|
1837
1834
|
let runnable
|
|
1838
1835
|
let resolved
|
|
1839
1836
|
if (typeof runner === "function") {
|
|
@@ -1842,9 +1839,9 @@ class Server {
|
|
|
1842
1839
|
} else {
|
|
1843
1840
|
resolved = runner(this.kernel, this.kernel.info)
|
|
1844
1841
|
}
|
|
1845
|
-
runnable = resolved && resolved
|
|
1842
|
+
runnable = resolved && Array.isArray(resolved[actionKey]) && resolved[actionKey].length > 0
|
|
1846
1843
|
} else {
|
|
1847
|
-
runnable = runner && runner
|
|
1844
|
+
runnable = runner && Array.isArray(runner[actionKey]) && runner[actionKey].length > 0
|
|
1848
1845
|
resolved = runner
|
|
1849
1846
|
}
|
|
1850
1847
|
|
|
@@ -1965,6 +1962,7 @@ class Server {
|
|
|
1965
1962
|
run: (req.query && req.query.mode === "source" ? false : true),
|
|
1966
1963
|
stop: (req.query && req.query.stop ? true : false),
|
|
1967
1964
|
pinokioPath,
|
|
1965
|
+
action: actionKey,
|
|
1968
1966
|
runnable,
|
|
1969
1967
|
agent: req.agent,
|
|
1970
1968
|
rawpath,
|
|
@@ -3579,65 +3577,7 @@ class Server {
|
|
|
3579
3577
|
}
|
|
3580
3578
|
logHomeCheck({ step: 'resolved', resolvedHome, ancestor })
|
|
3581
3579
|
|
|
3582
|
-
|
|
3583
|
-
const blockDeviceMounts = []
|
|
3584
|
-
const mountTypeLookup = new Map()
|
|
3585
|
-
try {
|
|
3586
|
-
// fsSize() on some platforms can obscure the actual fs type; pull blockDevices for more accurate mount FS info (e.g. exFAT on macOS/Windows)
|
|
3587
|
-
const blockDevices = await system.blockDevices()
|
|
3588
|
-
for (const device of blockDevices || []) {
|
|
3589
|
-
const mountPath = normalizeMountPath(device.mount)
|
|
3590
|
-
if (!mountPath) continue
|
|
3591
|
-
const fsType = (device.fsType || device.type || '').toLowerCase()
|
|
3592
|
-
blockDeviceMounts.push({ mount: mountPath, type: fsType })
|
|
3593
|
-
if (fsType) {
|
|
3594
|
-
mountTypeLookup.set(mountPath.toLowerCase(), fsType)
|
|
3595
|
-
}
|
|
3596
|
-
}
|
|
3597
|
-
} catch (_) {
|
|
3598
|
-
// ignore - fallback to fsSize data only
|
|
3599
|
-
}
|
|
3600
|
-
logHomeCheck({ step: 'mountSources', fsSizeCount: mounts.length, blockDevicesCount: blockDeviceMounts.length })
|
|
3601
|
-
|
|
3602
|
-
const normalizedAncestor = normalizeMountPath(ancestor)
|
|
3603
|
-
const isParentMount = (mountPath) => {
|
|
3604
|
-
if (!mountPath || !normalizedAncestor) return false
|
|
3605
|
-
if (mountPath === "/") return normalizedAncestor.startsWith("/")
|
|
3606
|
-
return normalizedAncestor === mountPath || normalizedAncestor.startsWith(mountPath + "/")
|
|
3607
|
-
}
|
|
3608
|
-
const isExfat = (fsType) => {
|
|
3609
|
-
const normalized = (fsType || '').toLowerCase().replace(/[^a-z0-9]/g, '')
|
|
3610
|
-
return normalized.includes('exfat')
|
|
3611
|
-
}
|
|
3612
|
-
let bestMount = null
|
|
3613
|
-
for (const volume of mounts) {
|
|
3614
|
-
const mountPath = normalizeMountPath(volume.mount)
|
|
3615
|
-
if (!isParentMount(mountPath)) continue
|
|
3616
|
-
const blockType = mountTypeLookup.get((mountPath || '').toLowerCase())
|
|
3617
|
-
const detectedType = (blockType || volume.type || '').toLowerCase()
|
|
3618
|
-
if (!bestMount || mountPath.length > bestMount.mount.length) {
|
|
3619
|
-
bestMount = { mount: mountPath, type: detectedType }
|
|
3620
|
-
logHomeCheck({ step: 'candidate', source: 'fsSize', mount: mountPath, type: detectedType })
|
|
3621
|
-
}
|
|
3622
|
-
}
|
|
3623
|
-
|
|
3624
|
-
if (!bestMount) {
|
|
3625
|
-
for (const deviceMount of blockDeviceMounts) {
|
|
3626
|
-
if (!isParentMount(deviceMount.mount)) continue
|
|
3627
|
-
if (!bestMount || deviceMount.mount.length > bestMount.mount.length) {
|
|
3628
|
-
bestMount = { ...deviceMount }
|
|
3629
|
-
logHomeCheck({ step: 'candidate', source: 'blockDevices', mount: deviceMount.mount, type: deviceMount.type })
|
|
3630
|
-
}
|
|
3631
|
-
}
|
|
3632
|
-
}
|
|
3633
|
-
|
|
3634
|
-
logHomeCheck({ step: 'bestMount', bestMount })
|
|
3635
|
-
|
|
3636
|
-
if (bestMount && bestMount.type && isExfat(bestMount.type)) {
|
|
3637
|
-
logHomeCheck({ step: 'reject', reason: 'exfat', bestMount })
|
|
3638
|
-
throw new Error("Pinokio home cannot be located on an exFAT drive. Please choose a different location.")
|
|
3639
|
-
}
|
|
3640
|
-
logHomeCheck({ step: 'accept', bestMount })
|
|
3580
|
+
logHomeCheck({ step: 'accept' })
|
|
3641
3581
|
|
|
3642
3582
|
// // check if the destination already exists => throw error
|
|
3643
3583
|
// let exists = await fse.pathExists(config.home)
|
|
@@ -4559,16 +4499,20 @@ class Server {
|
|
|
4559
4499
|
})
|
|
4560
4500
|
}))
|
|
4561
4501
|
this.app.get("/agents", ex(async (req, res) => {
|
|
4562
|
-
|
|
4563
|
-
|
|
4502
|
+
let pluginMenu = []
|
|
4503
|
+
try {
|
|
4504
|
+
if (!this.kernel.plugin.config) {
|
|
4564
4505
|
await this.kernel.plugin.init()
|
|
4565
|
-
}
|
|
4566
|
-
|
|
4506
|
+
} else {
|
|
4507
|
+
// Refresh the plugin list so newly downloaded plugins show up immediately
|
|
4508
|
+
await this.kernel.plugin.setConfig()
|
|
4567
4509
|
}
|
|
4510
|
+
if (this.kernel.plugin && this.kernel.plugin.config && Array.isArray(this.kernel.plugin.config.menu)) {
|
|
4511
|
+
pluginMenu = this.kernel.plugin.config.menu
|
|
4512
|
+
}
|
|
4513
|
+
} catch (err) {
|
|
4514
|
+
console.warn('Failed to initialize plugins', err)
|
|
4568
4515
|
}
|
|
4569
|
-
const pluginMenu = this.kernel.plugin && this.kernel.plugin.config && Array.isArray(this.kernel.plugin.config.menu)
|
|
4570
|
-
? this.kernel.plugin.config.menu
|
|
4571
|
-
: []
|
|
4572
4516
|
|
|
4573
4517
|
const apps = []
|
|
4574
4518
|
try {
|
|
@@ -5170,12 +5114,9 @@ class Server {
|
|
|
5170
5114
|
if (this.kernel.homedir) {
|
|
5171
5115
|
system_env = await Environment.get(this.kernel.homedir, this.kernel)
|
|
5172
5116
|
}
|
|
5117
|
+
const hasHome = !!this.kernel.homedir
|
|
5173
5118
|
let configArray = [{
|
|
5174
5119
|
key: "home",
|
|
5175
|
-
description: [
|
|
5176
|
-
"* NO white spaces (' ')",
|
|
5177
|
-
"* NO exFAT drives",
|
|
5178
|
-
],
|
|
5179
5120
|
val: this.kernel.homedir ? this.kernel.homedir : _home,
|
|
5180
5121
|
placeholder: "Enter the absolute path to use as your Pinokio home folder (D\\pinokio, /Users/alice/pinokiofs, etc.)"
|
|
5181
5122
|
// }, {
|
|
@@ -7676,6 +7617,17 @@ class Server {
|
|
|
7676
7617
|
res.status(404).send(e.message)
|
|
7677
7618
|
}
|
|
7678
7619
|
}))
|
|
7620
|
+
this.app.get("/action/:action/*", ex(async (req, res) => {
|
|
7621
|
+
const action = typeof req.params.action === 'string' ? req.params.action : ''
|
|
7622
|
+
const pathComponents = req.params[0] ? req.params[0].split("/") : []
|
|
7623
|
+
req.base = this.kernel.homedir
|
|
7624
|
+
req.action = action
|
|
7625
|
+
try {
|
|
7626
|
+
await this.render(req, res, pathComponents)
|
|
7627
|
+
} catch (e) {
|
|
7628
|
+
res.status(404).send(e.message)
|
|
7629
|
+
}
|
|
7630
|
+
}))
|
|
7679
7631
|
this.app.get("/run/*", ex(async (req, res) => {
|
|
7680
7632
|
let pathComponents = req.params[0].split("/")
|
|
7681
7633
|
req.base = this.kernel.homedir
|
|
@@ -59,26 +59,30 @@
|
|
|
59
59
|
const href = typeof plugin.href === 'string' ? plugin.href.trim() : '';
|
|
60
60
|
const label = plugin.title || plugin.text || plugin.name || href || '';
|
|
61
61
|
|
|
62
|
-
let
|
|
62
|
+
let value = '';
|
|
63
63
|
if (href) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
// Normalize href to a plugin-relative path for the backend (e.g., code/codex)
|
|
65
|
+
const normalized = href.replace(/^\/run/, '').replace(/^\/+/, '');
|
|
66
|
+
const parts = normalized.split('/').filter(Boolean);
|
|
67
|
+
// Expect /plugin/<path...>/pinokio.js -> want <path...>
|
|
68
|
+
if (parts[0] === 'plugin' && parts.length >= 3) {
|
|
69
|
+
value = parts.slice(1, -1).join('/');
|
|
70
|
+
} else {
|
|
71
|
+
value = normalized;
|
|
67
72
|
}
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
if (slug.endsWith('.js')) {
|
|
72
|
-
slug = slug.replace(/\.js$/i, '');
|
|
73
|
+
if (value.endsWith('/pinokio.js')) {
|
|
74
|
+
value = value.replace(/\/pinokio\.js$/i, '');
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
|
-
if (!
|
|
76
|
-
|
|
77
|
+
if (!value && label) {
|
|
78
|
+
value = label
|
|
77
79
|
.toLowerCase()
|
|
78
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
80
|
+
.replace(/[^a-z0-9/]+/g, '-')
|
|
79
81
|
.replace(/^-+|-+$/g, '');
|
|
80
82
|
}
|
|
81
|
-
|
|
83
|
+
if (!value && typeof plugin.link === 'string') {
|
|
84
|
+
value = plugin.link.trim();
|
|
85
|
+
}
|
|
82
86
|
if (!value) {
|
|
83
87
|
return null;
|
|
84
88
|
}
|
|
@@ -620,17 +624,6 @@
|
|
|
620
624
|
return ui && ui.templateManager ? ui.templateManager.getTemplateValues() : new Map();
|
|
621
625
|
}
|
|
622
626
|
|
|
623
|
-
function getSelectedTool(ui) {
|
|
624
|
-
if (!ui || !Array.isArray(ui.toolEntries)) {
|
|
625
|
-
return '';
|
|
626
|
-
}
|
|
627
|
-
const checked = ui.toolEntries.find((entry) => entry.input.checked);
|
|
628
|
-
if (checked && checked.input && checked.input.value) {
|
|
629
|
-
return checked.input.value;
|
|
630
|
-
}
|
|
631
|
-
return ui.toolEntries.length > 0 ? (ui.toolEntries[0].input.value || '') : '';
|
|
632
|
-
}
|
|
633
|
-
|
|
634
627
|
async function submitFromUi(ui) {
|
|
635
628
|
if (!ui) return;
|
|
636
629
|
ui.error.textContent = '';
|
|
@@ -641,9 +634,13 @@
|
|
|
641
634
|
const targetProject = isAskVariant ? (ui.projectName || folderName) : folderName;
|
|
642
635
|
const rawPrompt = ui.promptTextarea.value;
|
|
643
636
|
const templateValues = readTemplateValues(ui);
|
|
644
|
-
const
|
|
637
|
+
const selectedEntry = ui && Array.isArray(ui.toolEntries)
|
|
638
|
+
? (ui.toolEntries.find((entry) => entry.input.checked) || ui.toolEntries[0])
|
|
639
|
+
: null
|
|
640
|
+
const selectedTool = selectedEntry && selectedEntry.input ? selectedEntry.input.value : ''
|
|
641
|
+
const selectedHref = selectedEntry && selectedEntry.input ? selectedEntry.input.dataset.agentHref : ''
|
|
645
642
|
|
|
646
|
-
if (!
|
|
643
|
+
if (!selectedEntry || !selectedHref) {
|
|
647
644
|
ui.error.textContent = 'Please select an agent.';
|
|
648
645
|
return;
|
|
649
646
|
}
|
|
@@ -689,7 +686,8 @@
|
|
|
689
686
|
|
|
690
687
|
if (isAskVariant) {
|
|
691
688
|
const params = new URLSearchParams();
|
|
692
|
-
|
|
689
|
+
const pluginPath = selectedHref.replace(/^\/run/, '')
|
|
690
|
+
params.set('plugin', pluginPath);
|
|
693
691
|
if (prompt) {
|
|
694
692
|
params.set('prompt', prompt);
|
|
695
693
|
}
|
package/server/public/install.js
CHANGED
|
@@ -146,7 +146,8 @@ const install = async (name, url, term, socket, options) => {
|
|
|
146
146
|
text: `Downloaded to ~/${normalizedPath}/${name}`,
|
|
147
147
|
timeout: 4000
|
|
148
148
|
})
|
|
149
|
-
|
|
149
|
+
const relativePluginPath = `${normalizedPath}/${name}`
|
|
150
|
+
location.href = `/agents?path=${encodeURIComponent(relativePluginPath)}`
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
}
|
package/server/views/agents.ejs
CHANGED
|
@@ -102,31 +102,28 @@ body.plugin-page .btn-tab .btn {
|
|
|
102
102
|
}
|
|
103
103
|
.plugin-card {
|
|
104
104
|
display: flex;
|
|
105
|
-
|
|
105
|
+
flex-direction: column;
|
|
106
|
+
align-items: stretch;
|
|
106
107
|
gap: 12px;
|
|
107
108
|
padding: 12px;
|
|
108
109
|
border-radius: 10px;
|
|
109
110
|
background: rgba(0, 0, 0, 0.03);
|
|
110
|
-
cursor:
|
|
111
|
-
transition: background 0.15s ease
|
|
111
|
+
cursor: default;
|
|
112
|
+
transition: background 0.15s ease;
|
|
112
113
|
text-decoration: none;
|
|
113
114
|
color: inherit;
|
|
114
115
|
min-height: 68px;
|
|
115
116
|
}
|
|
116
117
|
.plugin-card:hover,
|
|
117
|
-
.plugin-card:focus-
|
|
118
|
+
.plugin-card:focus-within {
|
|
118
119
|
background: rgba(0, 0, 0, 0.08);
|
|
119
120
|
}
|
|
120
|
-
.plugin-card:focus-visible {
|
|
121
|
-
outline: 2px solid rgba(127, 91, 243, 0.6);
|
|
122
|
-
outline-offset: 2px;
|
|
123
|
-
}
|
|
124
121
|
body.dark .plugin-card {
|
|
125
122
|
background: rgba(255, 255, 255, 0.05);
|
|
126
123
|
color: white;
|
|
127
124
|
}
|
|
128
125
|
body.dark .plugin-card:hover,
|
|
129
|
-
body.dark .plugin-card:focus-
|
|
126
|
+
body.dark .plugin-card:focus-within {
|
|
130
127
|
background: rgba(255, 255, 255, 0.12);
|
|
131
128
|
}
|
|
132
129
|
.plugin-card img,
|
|
@@ -156,6 +153,16 @@ body.dark .plugin-card .plugin-icon {
|
|
|
156
153
|
display: flex;
|
|
157
154
|
flex-direction: column;
|
|
158
155
|
gap: 4px;
|
|
156
|
+
flex: 1;
|
|
157
|
+
}
|
|
158
|
+
.plugin-card .plugin-top {
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: 12px;
|
|
162
|
+
}
|
|
163
|
+
.plugin-card .plugin-footer {
|
|
164
|
+
display: flex;
|
|
165
|
+
justify-content: flex-end;
|
|
159
166
|
}
|
|
160
167
|
.plugin-card h2 {
|
|
161
168
|
margin: 0;
|
|
@@ -191,6 +198,171 @@ body.dark .plugin-card .plugin-info:focus-visible {
|
|
|
191
198
|
background: rgba(255, 255, 255, 0.18);
|
|
192
199
|
color: rgba(255, 255, 255, 0.95);
|
|
193
200
|
}
|
|
201
|
+
.plugin-card .plugin-actions {
|
|
202
|
+
margin-left: auto;
|
|
203
|
+
display: flex;
|
|
204
|
+
flex-wrap: wrap;
|
|
205
|
+
gap: 8px;
|
|
206
|
+
justify-content: flex-end;
|
|
207
|
+
}
|
|
208
|
+
.plugin-card .plugin-action-button {
|
|
209
|
+
padding: 8px 12px;
|
|
210
|
+
border-radius: 8px;
|
|
211
|
+
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
212
|
+
background: white;
|
|
213
|
+
color: rgba(0, 0, 0, 0.8);
|
|
214
|
+
cursor: pointer;
|
|
215
|
+
font-weight: 600;
|
|
216
|
+
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
|
|
217
|
+
}
|
|
218
|
+
body.dark .plugin-card .plugin-action-button {
|
|
219
|
+
background: rgba(255, 255, 255, 0.08);
|
|
220
|
+
border-color: rgba(255, 255, 255, 0.16);
|
|
221
|
+
color: white;
|
|
222
|
+
}
|
|
223
|
+
.plugin-card .plugin-action-button:hover,
|
|
224
|
+
.plugin-card .plugin-action-button:focus-visible {
|
|
225
|
+
background: rgba(127, 91, 243, 0.12);
|
|
226
|
+
border-color: rgba(127, 91, 243, 0.45);
|
|
227
|
+
color: rgba(0, 0, 0, 0.9);
|
|
228
|
+
outline: none;
|
|
229
|
+
}
|
|
230
|
+
body.dark .plugin-card .plugin-action-button:hover,
|
|
231
|
+
body.dark .plugin-card .plugin-action-button:focus-visible {
|
|
232
|
+
color: white;
|
|
233
|
+
background: rgba(127, 91, 243, 0.25);
|
|
234
|
+
border-color: rgba(127, 91, 243, 0.65);
|
|
235
|
+
}
|
|
236
|
+
.plugin-card .plugin-action-button:active {
|
|
237
|
+
transform: translateY(1px);
|
|
238
|
+
}
|
|
239
|
+
:root {
|
|
240
|
+
--pinokio-modal-bg: #ffffff;
|
|
241
|
+
--pinokio-modal-text: #0f172a;
|
|
242
|
+
--pinokio-modal-icon-bg: linear-gradient(135deg, rgba(59, 130, 246, 0.18), rgba(59, 130, 246, 0.03));
|
|
243
|
+
--pinokio-modal-icon-color: #2563eb;
|
|
244
|
+
--pinokio-modal-subtitle: rgba(71, 85, 105, 0.75);
|
|
245
|
+
--pinokio-modal-body-bg: rgba(241, 245, 249, 0.9);
|
|
246
|
+
}
|
|
247
|
+
body.dark {
|
|
248
|
+
--pinokio-modal-bg: #0f172a;
|
|
249
|
+
--pinokio-modal-text: #e2e8f0;
|
|
250
|
+
--pinokio-modal-icon-bg: linear-gradient(135deg, rgba(59, 130, 246, 0.25), rgba(59, 130, 246, 0.05));
|
|
251
|
+
--pinokio-modal-icon-color: #60a5fa;
|
|
252
|
+
--pinokio-modal-subtitle: rgba(148, 163, 184, 0.85);
|
|
253
|
+
--pinokio-modal-body-bg: rgba(15, 23, 42, 0.6);
|
|
254
|
+
}
|
|
255
|
+
.pinokio-modern-modal.swal2-popup {
|
|
256
|
+
padding: 0 !important;
|
|
257
|
+
border-radius: 20px !important;
|
|
258
|
+
background: var(--pinokio-modal-bg) !important;
|
|
259
|
+
box-shadow: 0 32px 80px rgba(15, 23, 42, 0.25);
|
|
260
|
+
}
|
|
261
|
+
body.dark .pinokio-modern-modal.swal2-popup {
|
|
262
|
+
box-shadow: 0 40px 120px rgba(2, 8, 23, 0.7);
|
|
263
|
+
}
|
|
264
|
+
.pinokio-modern-html {
|
|
265
|
+
padding: 0 !important;
|
|
266
|
+
}
|
|
267
|
+
.pinokio-modern-close {
|
|
268
|
+
color: rgba(71, 85, 105, 0.65) !important;
|
|
269
|
+
top: 12px !important;
|
|
270
|
+
right: 12px !important;
|
|
271
|
+
}
|
|
272
|
+
body.dark .pinokio-modern-close {
|
|
273
|
+
color: rgba(226, 232, 240, 0.6) !important;
|
|
274
|
+
}
|
|
275
|
+
.pinokio-modern-close:hover {
|
|
276
|
+
background: rgba(148, 163, 184, 0.18) !important;
|
|
277
|
+
color: #0f172a !important;
|
|
278
|
+
}
|
|
279
|
+
body.dark .pinokio-modern-close:hover {
|
|
280
|
+
background: rgba(148, 163, 184, 0.12) !important;
|
|
281
|
+
color: #f8fafc !important;
|
|
282
|
+
}
|
|
283
|
+
.pinokio-modal-surface {
|
|
284
|
+
background: var(--pinokio-modal-bg);
|
|
285
|
+
color: var(--pinokio-modal-text);
|
|
286
|
+
border-radius: 20px;
|
|
287
|
+
overflow: hidden;
|
|
288
|
+
}
|
|
289
|
+
.pinokio-modal-header {
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
gap: 12px;
|
|
293
|
+
padding: 22px 24px 10px;
|
|
294
|
+
}
|
|
295
|
+
.pinokio-modal-icon {
|
|
296
|
+
width: 48px;
|
|
297
|
+
height: 48px;
|
|
298
|
+
border-radius: 14px;
|
|
299
|
+
background: var(--pinokio-modal-icon-bg);
|
|
300
|
+
color: var(--pinokio-modal-icon-color);
|
|
301
|
+
display: grid;
|
|
302
|
+
place-items: center;
|
|
303
|
+
font-size: 22px;
|
|
304
|
+
}
|
|
305
|
+
.pinokio-modal-heading {
|
|
306
|
+
display: flex;
|
|
307
|
+
flex-direction: column;
|
|
308
|
+
gap: 4px;
|
|
309
|
+
}
|
|
310
|
+
.pinokio-modal-title {
|
|
311
|
+
font-size: 20px;
|
|
312
|
+
font-weight: 700;
|
|
313
|
+
color: var(--pinokio-modal-text);
|
|
314
|
+
}
|
|
315
|
+
.pinokio-modal-subtitle {
|
|
316
|
+
font-size: 13px;
|
|
317
|
+
color: var(--pinokio-modal-subtitle);
|
|
318
|
+
}
|
|
319
|
+
.pinokio-modal-body {
|
|
320
|
+
padding: 14px 24px 12px;
|
|
321
|
+
background: var(--pinokio-modal-body-bg);
|
|
322
|
+
}
|
|
323
|
+
.pinokio-modal-body--iframe {
|
|
324
|
+
padding: 14px 24px 12px;
|
|
325
|
+
}
|
|
326
|
+
.pinokio-modal-body--iframe iframe {
|
|
327
|
+
width: 100%;
|
|
328
|
+
height: 440px;
|
|
329
|
+
border: none;
|
|
330
|
+
border-radius: 12px;
|
|
331
|
+
background: inherit;
|
|
332
|
+
}
|
|
333
|
+
.pinokio-modal-footer {
|
|
334
|
+
display: flex;
|
|
335
|
+
justify-content: flex-end;
|
|
336
|
+
gap: 10px;
|
|
337
|
+
padding: 0 24px 18px;
|
|
338
|
+
background: var(--pinokio-modal-bg);
|
|
339
|
+
}
|
|
340
|
+
.pinokio-modal-footer--publish {
|
|
341
|
+
align-items: center;
|
|
342
|
+
}
|
|
343
|
+
.pinokio-publish-close-btn,
|
|
344
|
+
.pinokio-modal-secondary-btn {
|
|
345
|
+
border: none;
|
|
346
|
+
border-radius: 10px;
|
|
347
|
+
padding: 9px 14px;
|
|
348
|
+
font-weight: 600;
|
|
349
|
+
cursor: pointer;
|
|
350
|
+
background: rgba(0, 0, 0, 0.08);
|
|
351
|
+
color: var(--pinokio-modal-text);
|
|
352
|
+
}
|
|
353
|
+
body.dark .pinokio-publish-close-btn,
|
|
354
|
+
body.dark .pinokio-modal-secondary-btn {
|
|
355
|
+
background: rgba(255, 255, 255, 0.12);
|
|
356
|
+
color: var(--pinokio-modal-text);
|
|
357
|
+
}
|
|
358
|
+
.pinokio-publish-close-btn:hover,
|
|
359
|
+
.pinokio-modal-secondary-btn:hover {
|
|
360
|
+
background: rgba(127, 91, 243, 0.18);
|
|
361
|
+
}
|
|
362
|
+
body.dark .pinokio-publish-close-btn:hover,
|
|
363
|
+
body.dark .pinokio-modal-secondary-btn:hover {
|
|
364
|
+
background: rgba(127, 91, 243, 0.25);
|
|
365
|
+
}
|
|
194
366
|
.plugin-card .disclosure-indicator {
|
|
195
367
|
display: flex;
|
|
196
368
|
align-items: center;
|
|
@@ -201,7 +373,7 @@ body.dark .plugin-card .plugin-info:focus-visible {
|
|
|
201
373
|
transition: transform 0.15s ease, color 0.15s ease;
|
|
202
374
|
}
|
|
203
375
|
.plugin-card:hover .disclosure-indicator,
|
|
204
|
-
.plugin-card:focus-
|
|
376
|
+
.plugin-card:focus-within .disclosure-indicator {
|
|
205
377
|
transform: translateX(2px);
|
|
206
378
|
color: rgba(0, 0, 0, 0.65);
|
|
207
379
|
}
|
|
@@ -209,7 +381,7 @@ body.dark .plugin-card .disclosure-indicator {
|
|
|
209
381
|
color: rgba(255, 255, 255, 0.4);
|
|
210
382
|
}
|
|
211
383
|
body.dark .plugin-card:hover .disclosure-indicator,
|
|
212
|
-
body.dark .plugin-card:focus-
|
|
384
|
+
body.dark .plugin-card:focus-within .disclosure-indicator {
|
|
213
385
|
color: rgba(255, 255, 255, 0.85);
|
|
214
386
|
}
|
|
215
387
|
.plugin-empty {
|
|
@@ -403,7 +575,8 @@ body.dark .plugin-option:hover {
|
|
|
403
575
|
image: plugin.image || null,
|
|
404
576
|
icon: plugin.icon || null,
|
|
405
577
|
pluginPath,
|
|
406
|
-
extraParams
|
|
578
|
+
extraParams,
|
|
579
|
+
hasInstall: Array.isArray(plugin.install)
|
|
407
580
|
}
|
|
408
581
|
}) %>
|
|
409
582
|
</head>
|
|
@@ -471,19 +644,35 @@ body.dark .plugin-option:hover {
|
|
|
471
644
|
<% if (items.length) { %>
|
|
472
645
|
<div class='plugin-grid'>
|
|
473
646
|
<% items.forEach(({ pluginItem, index }) => { %>
|
|
474
|
-
<div class='plugin-card'
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
<div class='plugin-details'>
|
|
483
|
-
<h2><%=pluginItem.title || pluginItem.text || pluginItem.name || 'Plugin'%></h2>
|
|
484
|
-
<% if (pluginItem.description) { %>
|
|
485
|
-
<div class='subtitle'><%=pluginItem.description%></div>
|
|
647
|
+
<div class='plugin-card' data-plugin-index="<%=index%>">
|
|
648
|
+
<div class="plugin-top">
|
|
649
|
+
<% if (pluginItem.image) { %>
|
|
650
|
+
<img src="<%=pluginItem.image%>" alt="<%=pluginItem.title || pluginItem.text || 'Plugin'%> icon">
|
|
651
|
+
<% } else if (pluginItem.icon) { %>
|
|
652
|
+
<div class='plugin-icon'><i class="<%=pluginItem.icon%>"></i></div>
|
|
653
|
+
<% } else { %>
|
|
654
|
+
<div class='plugin-icon'><i class="fa-solid fa-robot"></i></div>
|
|
486
655
|
<% } %>
|
|
656
|
+
<div class='plugin-details'>
|
|
657
|
+
<h2><%=pluginItem.title || pluginItem.text || pluginItem.name || 'Plugin'%></h2>
|
|
658
|
+
<% if (pluginItem.description) { %>
|
|
659
|
+
<div class='subtitle'><%=pluginItem.description%></div>
|
|
660
|
+
<% } %>
|
|
661
|
+
</div>
|
|
662
|
+
</div>
|
|
663
|
+
<div class="plugin-footer">
|
|
664
|
+
<div class="plugin-actions">
|
|
665
|
+
<button type="button" class="plugin-action-button" data-action="open">Open in</button>
|
|
666
|
+
<% if (Array.isArray(pluginItem.install)) { %>
|
|
667
|
+
<button type="button" class="plugin-action-button" data-plugin-action="install">Install</button>
|
|
668
|
+
<% } %>
|
|
669
|
+
<% if (Array.isArray(pluginItem.uninstall)) { %>
|
|
670
|
+
<button type="button" class="plugin-action-button" data-plugin-action="uninstall">Uninstall</button>
|
|
671
|
+
<% } %>
|
|
672
|
+
<% if (Array.isArray(pluginItem.update)) { %>
|
|
673
|
+
<button type="button" class="plugin-action-button" data-plugin-action="update">Update</button>
|
|
674
|
+
<% } %>
|
|
675
|
+
</div>
|
|
487
676
|
</div>
|
|
488
677
|
</div>
|
|
489
678
|
<% }) %>
|
|
@@ -828,6 +1017,119 @@ body.dark .plugin-option:hover {
|
|
|
828
1017
|
|
|
829
1018
|
const modal = createPluginModal(apps)
|
|
830
1019
|
|
|
1020
|
+
const ACTION_LABELS = {
|
|
1021
|
+
install: 'Install',
|
|
1022
|
+
uninstall: 'Uninstall',
|
|
1023
|
+
update: 'Update'
|
|
1024
|
+
}
|
|
1025
|
+
const ACTION_ICONS = {
|
|
1026
|
+
install: 'fa-solid fa-download',
|
|
1027
|
+
uninstall: 'fa-solid fa-trash-can',
|
|
1028
|
+
update: 'fa-solid fa-rotate-right'
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
function buildActionUrl(plugin, actionType) {
|
|
1032
|
+
if (!plugin || !plugin.pluginPath) return null
|
|
1033
|
+
const normalizedPath = plugin.pluginPath.startsWith('/') ? plugin.pluginPath.slice(1) : plugin.pluginPath
|
|
1034
|
+
if (!normalizedPath) return null
|
|
1035
|
+
const encodedPath = normalizedPath.split('/').map((segment) => encodeURIComponent(segment)).join('/')
|
|
1036
|
+
const ts = Date.now()
|
|
1037
|
+
return `/action/${encodeURIComponent(actionType)}/${encodedPath}?ts=${ts}`
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
function showActionModal(actionType, plugin) {
|
|
1041
|
+
const targetUrl = buildActionUrl(plugin, actionType)
|
|
1042
|
+
if (!targetUrl) {
|
|
1043
|
+
alert('This action is missing a target script.')
|
|
1044
|
+
return
|
|
1045
|
+
}
|
|
1046
|
+
const pluginTitle = plugin && (plugin.title || plugin.text || plugin.name) ? (plugin.title || plugin.text || plugin.name) : 'Plugin'
|
|
1047
|
+
const title = `${ACTION_LABELS[actionType] || 'Run'} ${escapeHtml(pluginTitle)}`
|
|
1048
|
+
const subtitle = plugin && plugin.description ? escapeHtml(plugin.description) : ''
|
|
1049
|
+
const iconClass = ACTION_ICONS[actionType] || 'fa-solid fa-terminal'
|
|
1050
|
+
const modalHtml = `
|
|
1051
|
+
<div class="pinokio-modal-surface">
|
|
1052
|
+
<div class="pinokio-modal-header">
|
|
1053
|
+
<div class="pinokio-modal-icon"><i class="${iconClass}"></i></div>
|
|
1054
|
+
<div class="pinokio-modal-heading">
|
|
1055
|
+
<div class="pinokio-modal-title">${title}</div>
|
|
1056
|
+
<div class="pinokio-modal-subtitle">${subtitle}</div>
|
|
1057
|
+
</div>
|
|
1058
|
+
</div>
|
|
1059
|
+
<div class="pinokio-modal-body pinokio-modal-body--iframe">
|
|
1060
|
+
<iframe src="${targetUrl}"></iframe>
|
|
1061
|
+
</div>
|
|
1062
|
+
<div class="pinokio-modal-footer pinokio-modal-footer--publish" data-publish-footer>
|
|
1063
|
+
<button type="button" class="pinokio-publish-close-btn" data-publish-close>Close</button>
|
|
1064
|
+
</div>
|
|
1065
|
+
</div>
|
|
1066
|
+
`
|
|
1067
|
+
|
|
1068
|
+
Swal.fire({
|
|
1069
|
+
html: modalHtml,
|
|
1070
|
+
customClass: {
|
|
1071
|
+
popup: 'pinokio-modern-modal',
|
|
1072
|
+
htmlContainer: 'pinokio-modern-html',
|
|
1073
|
+
closeButton: 'pinokio-modern-close'
|
|
1074
|
+
},
|
|
1075
|
+
backdrop: 'rgba(9,11,15,0.65)',
|
|
1076
|
+
width: 'min(760px, 90vw)',
|
|
1077
|
+
showConfirmButton: false,
|
|
1078
|
+
showCloseButton: true,
|
|
1079
|
+
buttonsStyling: false,
|
|
1080
|
+
focusConfirm: false,
|
|
1081
|
+
didOpen: (popup) => {
|
|
1082
|
+
const iframe = popup.querySelector('iframe')
|
|
1083
|
+
const closeBtn = popup.querySelector('[data-publish-close]')
|
|
1084
|
+
if (iframe) {
|
|
1085
|
+
iframe.dataset.forceVisible = 'true'
|
|
1086
|
+
iframe.classList.remove('hidden')
|
|
1087
|
+
iframe.removeAttribute('hidden')
|
|
1088
|
+
}
|
|
1089
|
+
if (closeBtn) {
|
|
1090
|
+
closeBtn.addEventListener('click', () => {
|
|
1091
|
+
Swal.close()
|
|
1092
|
+
})
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
})
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function normalizePluginPath(value) {
|
|
1099
|
+
if (typeof value !== 'string') return ''
|
|
1100
|
+
return value.replace(/\\/g, '/').replace(/^\/+/, '').replace(/\/+$/, '')
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
function findPluginByPath(targetPath) {
|
|
1104
|
+
if (!targetPath) return null
|
|
1105
|
+
const normalizedTarget = normalizePluginPath(targetPath)
|
|
1106
|
+
const targetWithFile = normalizedTarget.endsWith('pinokio.js')
|
|
1107
|
+
? normalizedTarget
|
|
1108
|
+
: `${normalizedTarget}/pinokio.js`
|
|
1109
|
+
return plugins.find((plugin) => {
|
|
1110
|
+
const pluginPath = normalizePluginPath(plugin.pluginPath || '')
|
|
1111
|
+
if (!pluginPath) return false
|
|
1112
|
+
return pluginPath === targetWithFile || pluginPath === `/${targetWithFile}`
|
|
1113
|
+
}) || null
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
function autoActivateFromQuery() {
|
|
1117
|
+
const params = new URLSearchParams(window.location.search)
|
|
1118
|
+
const targetPath = params.get('path')
|
|
1119
|
+
if (!targetPath) {
|
|
1120
|
+
return
|
|
1121
|
+
}
|
|
1122
|
+
const plugin = findPluginByPath(targetPath)
|
|
1123
|
+
if (!plugin) {
|
|
1124
|
+
return
|
|
1125
|
+
}
|
|
1126
|
+
if (plugin.hasInstall) {
|
|
1127
|
+
showActionModal('install', plugin)
|
|
1128
|
+
} else {
|
|
1129
|
+
modal.open(plugin)
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
831
1133
|
function attachHandlers() {
|
|
832
1134
|
const cards = document.querySelectorAll('.plugin-card')
|
|
833
1135
|
if (!cards.length) return
|
|
@@ -849,6 +1151,7 @@ body.dark .plugin-option:hover {
|
|
|
849
1151
|
}
|
|
850
1152
|
})
|
|
851
1153
|
}
|
|
1154
|
+
const openButton = card.querySelector('[data-action="open"]')
|
|
852
1155
|
const open = (event) => {
|
|
853
1156
|
if (event) {
|
|
854
1157
|
event.preventDefault()
|
|
@@ -859,11 +1162,17 @@ body.dark .plugin-option:hover {
|
|
|
859
1162
|
}
|
|
860
1163
|
modal.open(plugin)
|
|
861
1164
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1165
|
+
if (openButton) {
|
|
1166
|
+
openButton.addEventListener('click', open)
|
|
1167
|
+
}
|
|
1168
|
+
const actionButtons = card.querySelectorAll('[data-plugin-action]')
|
|
1169
|
+
actionButtons.forEach((button) => {
|
|
1170
|
+
const actionType = button.getAttribute('data-plugin-action')
|
|
1171
|
+
if (!actionType) return
|
|
1172
|
+
button.addEventListener('click', (event) => {
|
|
1173
|
+
event.preventDefault()
|
|
1174
|
+
showActionModal(actionType, plugin)
|
|
1175
|
+
})
|
|
867
1176
|
})
|
|
868
1177
|
})
|
|
869
1178
|
}
|
|
@@ -871,6 +1180,7 @@ body.dark .plugin-option:hover {
|
|
|
871
1180
|
document.addEventListener('DOMContentLoaded', () => {
|
|
872
1181
|
initUrlFeatures()
|
|
873
1182
|
attachHandlers()
|
|
1183
|
+
autoActivateFromQuery()
|
|
874
1184
|
})
|
|
875
1185
|
})()
|
|
876
1186
|
</script>
|
|
@@ -163,6 +163,78 @@ body.dark .item select {
|
|
|
163
163
|
.timestamp {
|
|
164
164
|
color: rgba(0,0,0,0.5);
|
|
165
165
|
}
|
|
166
|
+
.home-warning {
|
|
167
|
+
margin-top: 8px;
|
|
168
|
+
padding: 10px 12px;
|
|
169
|
+
border: 1px solid rgba(0,0,0,0.08);
|
|
170
|
+
border-radius: 6px;
|
|
171
|
+
font-size: 12px;
|
|
172
|
+
line-height: 1.4;
|
|
173
|
+
background: rgba(0,0,0,0.03);
|
|
174
|
+
}
|
|
175
|
+
body.dark .home-warning {
|
|
176
|
+
border-color: rgba(255,255,255,0.12);
|
|
177
|
+
background: rgba(255,255,255,0.04);
|
|
178
|
+
color: rgba(255,255,255,0.8);
|
|
179
|
+
}
|
|
180
|
+
.home-warning ul {
|
|
181
|
+
margin: 0;
|
|
182
|
+
padding-left: 18px;
|
|
183
|
+
}
|
|
184
|
+
.swal2-popup.custom-home-modal {
|
|
185
|
+
border-radius: 10px;
|
|
186
|
+
padding: 18px 18px 14px 18px;
|
|
187
|
+
background: #fdfdfd;
|
|
188
|
+
box-shadow: 0 8px 28px rgba(0,0,0,0.12);
|
|
189
|
+
text-align: left;
|
|
190
|
+
}
|
|
191
|
+
body.dark .swal2-popup.custom-home-modal {
|
|
192
|
+
background: #1f1f25;
|
|
193
|
+
box-shadow: 0 10px 36px rgba(0,0,0,0.45);
|
|
194
|
+
}
|
|
195
|
+
.swal2-popup.custom-home-modal .swal2-title {
|
|
196
|
+
text-align: left !important;
|
|
197
|
+
font-size: 16px;
|
|
198
|
+
font-weight: 700;
|
|
199
|
+
color: #111;
|
|
200
|
+
margin: 0 0 6px 0;
|
|
201
|
+
text-align: left;
|
|
202
|
+
}
|
|
203
|
+
body.dark .swal2-popup.custom-home-modal .swal2-title {
|
|
204
|
+
color: #f5f5f5;
|
|
205
|
+
}
|
|
206
|
+
.swal2-popup.custom-home-modal .swal2-html-container {
|
|
207
|
+
font-size: 14px;
|
|
208
|
+
color: #333;
|
|
209
|
+
margin: 0 0 10px 0;
|
|
210
|
+
text-align: left;
|
|
211
|
+
}
|
|
212
|
+
body.dark .swal2-popup.custom-home-modal .swal2-html-container {
|
|
213
|
+
color: rgba(255,255,255,0.8);
|
|
214
|
+
}
|
|
215
|
+
.swal2-popup.custom-home-modal .swal2-actions {
|
|
216
|
+
margin-top: 4px;
|
|
217
|
+
gap: 8px;
|
|
218
|
+
justify-content: flex-end !important;
|
|
219
|
+
}
|
|
220
|
+
.swal2-popup.custom-home-modal .swal2-styled.swal2-confirm {
|
|
221
|
+
background: #4a4ae0;
|
|
222
|
+
color: #fff;
|
|
223
|
+
border-radius: 6px;
|
|
224
|
+
padding: 8px 16px;
|
|
225
|
+
font-weight: 600;
|
|
226
|
+
}
|
|
227
|
+
.swal2-popup.custom-home-modal .swal2-styled.swal2-cancel {
|
|
228
|
+
background: transparent;
|
|
229
|
+
color: #555;
|
|
230
|
+
border: 1px solid rgba(0,0,0,0.15);
|
|
231
|
+
border-radius: 6px;
|
|
232
|
+
padding: 8px 16px;
|
|
233
|
+
}
|
|
234
|
+
body.dark .swal2-popup.custom-home-modal .swal2-styled.swal2-cancel {
|
|
235
|
+
color: rgba(255,255,255,0.8);
|
|
236
|
+
border-color: rgba(255,255,255,0.2);
|
|
237
|
+
}
|
|
166
238
|
body.dark .loading {
|
|
167
239
|
background: rgba(255,255,255,0.06);
|
|
168
240
|
}
|
|
@@ -440,9 +512,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
440
512
|
</select>
|
|
441
513
|
<% } else { %>
|
|
442
514
|
<% if (c.val) { %>
|
|
443
|
-
<input class='homepath' name="<%=c.key%>" type='text' value="<%=c.val%>" placeholder="<%=c.placeholder ? c.placeholder : ''%>"
|
|
515
|
+
<input class='homepath' name="<%=c.key%>" type='text' value="<%=c.val%>" placeholder="<%=c.placeholder ? c.placeholder : ''%>" <%= c.key === 'home' && !hasHome ? `data-first-home=\"true\"` : "" %>>
|
|
444
516
|
<% } else { %>
|
|
445
|
-
<input class='homepath' name="<%=c.key%>" type='text' placeholder="<%=c.placeholder ? c.placeholder : ''%>"
|
|
517
|
+
<input class='homepath' name="<%=c.key%>" type='text' placeholder="<%=c.placeholder ? c.placeholder : ''%>" <%= c.key === 'home' && !hasHome ? `data-first-home=\"true\"` : "" %>>
|
|
446
518
|
<% } %>
|
|
447
519
|
<% } %>
|
|
448
520
|
<% if (c.description) { %>
|
|
@@ -578,6 +650,29 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
578
650
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
579
651
|
//Reporter()
|
|
580
652
|
const n = new N()
|
|
653
|
+
const homeInput = document.querySelector('input.homepath[name="home"]')
|
|
654
|
+
let showHomeWarning = () => {}
|
|
655
|
+
const originalHome = homeInput ? (homeInput.defaultValue || '') : ''
|
|
656
|
+
const isFirstHome = homeInput ? homeInput.dataset.firstHome === 'true' : false
|
|
657
|
+
if (homeInput) {
|
|
658
|
+
const warning = document.createElement('div')
|
|
659
|
+
warning.className = 'home-warning hidden'
|
|
660
|
+
warning.innerHTML = `
|
|
661
|
+
<ul>
|
|
662
|
+
<li>Avoid exFAT; it can cause permission/metadata issues.</li>
|
|
663
|
+
<li>Avoid spaces in the path; they can break tooling.</li>
|
|
664
|
+
</ul>
|
|
665
|
+
`
|
|
666
|
+
const parent = homeInput.parentElement
|
|
667
|
+
if (parent) {
|
|
668
|
+
parent.appendChild(warning)
|
|
669
|
+
}
|
|
670
|
+
const revealWarning = () => warning.classList.remove('hidden')
|
|
671
|
+
showHomeWarning = revealWarning
|
|
672
|
+
homeInput.addEventListener('focus', revealWarning)
|
|
673
|
+
homeInput.addEventListener('input', revealWarning)
|
|
674
|
+
homeInput.addEventListener('click', revealWarning)
|
|
675
|
+
}
|
|
581
676
|
const updateThemeClassAcrossShells = (nextTheme) => {
|
|
582
677
|
if (!nextTheme) {
|
|
583
678
|
return
|
|
@@ -722,6 +817,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
722
817
|
document.querySelector("main form").addEventListener("submit", async (e) => {
|
|
723
818
|
e.preventDefault()
|
|
724
819
|
e.stopPropagation()
|
|
820
|
+
showHomeWarning()
|
|
725
821
|
let val = document.querySelector(`[name=home]`).value
|
|
726
822
|
if (/.*\s+.*/.test(val)) {
|
|
727
823
|
alert("Please use a home path that does NOT include a space")
|
|
@@ -733,6 +829,25 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
733
829
|
document.querySelector("[name=home]").focus()
|
|
734
830
|
return
|
|
735
831
|
}
|
|
832
|
+
const isChanged = val !== originalHome
|
|
833
|
+
if (homeInput && (isFirstHome || isChanged)) {
|
|
834
|
+
const confirmResult = await Swal.fire({
|
|
835
|
+
title: 'Reminder',
|
|
836
|
+
html: 'exFAT drives often break permissions/metadata.<br>Please double-check the path isn\u2019t on exFAT. If it looks good, click \u201cSelect\u201d to continue.',
|
|
837
|
+
showCancelButton: true,
|
|
838
|
+
confirmButtonText: 'Select',
|
|
839
|
+
cancelButtonText: 'Cancel',
|
|
840
|
+
reverseButtons: true,
|
|
841
|
+
allowOutsideClick: false,
|
|
842
|
+
allowEscapeKey: false,
|
|
843
|
+
customClass: {
|
|
844
|
+
popup: 'custom-home-modal'
|
|
845
|
+
}
|
|
846
|
+
})
|
|
847
|
+
if (!confirmResult.isConfirmed) {
|
|
848
|
+
return
|
|
849
|
+
}
|
|
850
|
+
}
|
|
736
851
|
|
|
737
852
|
// let drive = document.querySelector(`[name=drive]`).value
|
|
738
853
|
// if (/.*\s+.*/.test(drive)) {
|
|
@@ -304,6 +304,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
304
304
|
const baseScriptId = <% if (script_id) { %><%- JSON.stringify(script_id) %><% } else { %>null<% } %>
|
|
305
305
|
const scriptUri = <% if (script_path) { %><%- JSON.stringify(script_path) %><% } else { %>null<% } %>
|
|
306
306
|
const scriptCwd = <% if (cwd) { %><%- JSON.stringify(cwd) %><% } else { %>null<% } %>
|
|
307
|
+
const scriptAction = <% if (typeof action !== 'undefined') { %><%- JSON.stringify(action) %><% } else { %>null<% } %>
|
|
307
308
|
class RPC {
|
|
308
309
|
constructor() {
|
|
309
310
|
this.socket = new Socket()
|
|
@@ -458,6 +459,9 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
458
459
|
rows: this.term.rows,
|
|
459
460
|
}
|
|
460
461
|
}
|
|
462
|
+
if (scriptAction) {
|
|
463
|
+
payload.action = scriptAction
|
|
464
|
+
}
|
|
461
465
|
if (scriptCwd) {
|
|
462
466
|
payload.cwd = scriptCwd
|
|
463
467
|
}
|