pinokiod 3.181.0 → 3.182.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/util.js +15 -2
- package/package.json +1 -1
- package/server/index.js +111 -12
- package/server/public/common.js +433 -240
- package/server/public/files-app/app.css +64 -0
- package/server/public/files-app/app.js +87 -0
- package/server/public/install.js +8 -1
- package/server/public/layout.js +11 -1
- package/server/public/sound/beep.mp3 +0 -0
- package/server/public/sound/bell.mp3 +0 -0
- package/server/public/sound/bright-ring.mp3 +0 -0
- package/server/public/sound/clap.mp3 +0 -0
- package/server/public/sound/deep-ring.mp3 +0 -0
- package/server/public/sound/gasp.mp3 +0 -0
- package/server/public/sound/hehe.mp3 +0 -0
- package/server/public/sound/levelup.mp3 +0 -0
- package/server/public/sound/light-pop.mp3 +0 -0
- package/server/public/sound/light-ring.mp3 +0 -0
- package/server/public/sound/meow.mp3 +0 -0
- package/server/public/sound/piano.mp3 +0 -0
- package/server/public/sound/pop.mp3 +0 -0
- package/server/public/sound/uhoh.mp3 +0 -0
- package/server/public/sound/whistle.mp3 +0 -0
- package/server/public/style.css +173 -2
- package/server/public/tab-idle-notifier.js +697 -4
- package/server/public/terminal-settings.js +1131 -0
- package/server/public/urldropdown.css +28 -1
- package/server/views/{terminals.ejs → agents.ejs} +97 -30
- package/server/views/app.ejs +112 -65
- package/server/views/bootstrap.ejs +8 -0
- package/server/views/connect.ejs +1 -1
- package/server/views/d.ejs +172 -18
- package/server/views/editor.ejs +8 -0
- package/server/views/file_browser.ejs +4 -0
- package/server/views/index.ejs +1 -1
- package/server/views/init/index.ejs +9 -1
- package/server/views/install.ejs +8 -0
- package/server/views/net.ejs +1 -1
- package/server/views/network.ejs +1 -1
- package/server/views/pro.ejs +8 -0
- package/server/views/prototype/index.ejs +8 -0
- package/server/views/screenshots.ejs +1 -2
- package/server/views/settings.ejs +1 -2
- package/server/views/shell.ejs +8 -0
- package/server/views/terminal.ejs +8 -0
- package/server/views/tools.ejs +1 -2
package/kernel/util.js
CHANGED
|
@@ -664,7 +664,18 @@ function push(params) {
|
|
|
664
664
|
if (HTTP_URL_REGEX.test(trimmed)) {
|
|
665
665
|
clientSoundUrl = trimmed
|
|
666
666
|
} else {
|
|
667
|
-
|
|
667
|
+
let normalised = trimmed.startsWith('/') ? trimmed : `/${trimmed}`
|
|
668
|
+
let decoded = normalised
|
|
669
|
+
try {
|
|
670
|
+
decoded = decodeURIComponent(normalised)
|
|
671
|
+
} catch (_) {
|
|
672
|
+
// Ignore decode errors; fall back to original string
|
|
673
|
+
}
|
|
674
|
+
if (decoded.startsWith('/sound/') && !decoded.includes('..')) {
|
|
675
|
+
clientSoundUrl = normalised
|
|
676
|
+
} else {
|
|
677
|
+
console.warn(`Ignoring notification sound (expected http/https URL or /sound asset): ${trimmed}`)
|
|
678
|
+
}
|
|
668
679
|
}
|
|
669
680
|
}
|
|
670
681
|
|
|
@@ -700,13 +711,15 @@ function push(params) {
|
|
|
700
711
|
const clientImage = resolvePublicAssetUrl(notifyParams.contentImage) || resolvePublicAssetUrl(notifyParams.image)
|
|
701
712
|
const deviceId = (typeof notifyParams.device_id === 'string' && notifyParams.device_id.trim()) ? notifyParams.device_id.trim() : null
|
|
702
713
|
const audience = (typeof notifyParams.audience === 'string' && notifyParams.audience.trim()) ? notifyParams.audience.trim() : null
|
|
714
|
+
const clientEventSound = requestedSound === false ? false : (clientSoundUrl || null)
|
|
715
|
+
|
|
703
716
|
const eventPayload = {
|
|
704
717
|
id: randomUUID(),
|
|
705
718
|
title: notifyParams.title,
|
|
706
719
|
subtitle: notifyParams.subtitle || null,
|
|
707
720
|
message: notifyParams.message || '',
|
|
708
721
|
image: clientImage,
|
|
709
|
-
sound:
|
|
722
|
+
sound: clientEventSound,
|
|
710
723
|
timestamp: Date.now(),
|
|
711
724
|
platform,
|
|
712
725
|
device_id: deviceId,
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -38,6 +38,7 @@ const ini = require('ini')
|
|
|
38
38
|
const ejs = require('ejs');
|
|
39
39
|
|
|
40
40
|
const DEFAULT_PORT = 42000
|
|
41
|
+
const NOTIFICATION_SOUND_EXTENSIONS = new Set(['.aac', '.flac', '.m4a', '.mp3', '.ogg', '.wav', '.webm'])
|
|
41
42
|
|
|
42
43
|
const ex = fn => (req, res, next) => {
|
|
43
44
|
Promise.resolve(fn(req, res, next)).catch(next);
|
|
@@ -3444,8 +3445,8 @@ class Server {
|
|
|
3444
3445
|
}
|
|
3445
3446
|
terminal = {
|
|
3446
3447
|
icon: "fa-solid fa-terminal",
|
|
3447
|
-
title: "
|
|
3448
|
-
subtitle: "Open
|
|
3448
|
+
title: "Shell",
|
|
3449
|
+
subtitle: "Open an interactive terminal in the browser",
|
|
3449
3450
|
menu: terminals
|
|
3450
3451
|
}
|
|
3451
3452
|
} else {
|
|
@@ -3912,6 +3913,46 @@ class Server {
|
|
|
3912
3913
|
getTheme: () => this.theme,
|
|
3913
3914
|
exists: (target) => this.exists(target),
|
|
3914
3915
|
});
|
|
3916
|
+
|
|
3917
|
+
this.app.get('/pinokio/notification-sounds', ex(async (req, res) => {
|
|
3918
|
+
const soundRoot = path.resolve(__dirname, 'public', 'sound');
|
|
3919
|
+
let entries = [];
|
|
3920
|
+
try {
|
|
3921
|
+
const dirEntries = await fs.promises.readdir(soundRoot, { withFileTypes: true });
|
|
3922
|
+
entries = dirEntries
|
|
3923
|
+
.filter((entry) => entry.isFile())
|
|
3924
|
+
.map((entry) => entry.name)
|
|
3925
|
+
.filter((name) => NOTIFICATION_SOUND_EXTENSIONS.has(path.extname(name).toLowerCase()))
|
|
3926
|
+
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
|
|
3927
|
+
} catch (error) {
|
|
3928
|
+
if (error && error.code === 'ENOENT') {
|
|
3929
|
+
return res.json({ sounds: [] });
|
|
3930
|
+
}
|
|
3931
|
+
return res.status(500).json({
|
|
3932
|
+
error: 'Failed to enumerate notification sounds',
|
|
3933
|
+
details: error && error.message ? error.message : String(error || ''),
|
|
3934
|
+
});
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
const normalizeLabel = (filename) => {
|
|
3938
|
+
const withoutExt = filename.replace(/\.[^.]+$/, '');
|
|
3939
|
+
return withoutExt
|
|
3940
|
+
.replace(/[-_]+/g, ' ')
|
|
3941
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
3942
|
+
};
|
|
3943
|
+
|
|
3944
|
+
const sounds = entries.map((filename) => {
|
|
3945
|
+
const encoded = filename.split('/').map(encodeURIComponent).join('/');
|
|
3946
|
+
return {
|
|
3947
|
+
id: filename,
|
|
3948
|
+
label: normalizeLabel(filename),
|
|
3949
|
+
url: `/sound/${encoded}`,
|
|
3950
|
+
filename,
|
|
3951
|
+
};
|
|
3952
|
+
});
|
|
3953
|
+
|
|
3954
|
+
res.json({ sounds });
|
|
3955
|
+
}));
|
|
3915
3956
|
/*
|
|
3916
3957
|
this.app.get("/asset/*", ex((req, res) => {
|
|
3917
3958
|
let pathComponents = req.params[0].split("/")
|
|
@@ -3982,7 +4023,7 @@ class Server {
|
|
|
3982
4023
|
list,
|
|
3983
4024
|
})
|
|
3984
4025
|
}))
|
|
3985
|
-
this.app.get("/
|
|
4026
|
+
this.app.get("/agents", ex(async (req, res) => {
|
|
3986
4027
|
if (!this.kernel.plugin.config) {
|
|
3987
4028
|
try {
|
|
3988
4029
|
await this.kernel.plugin.init()
|
|
@@ -4059,7 +4100,7 @@ class Server {
|
|
|
4059
4100
|
let peer_qr = null
|
|
4060
4101
|
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
4061
4102
|
const list = this.getPeers()
|
|
4062
|
-
res.render("
|
|
4103
|
+
res.render("agents", {
|
|
4063
4104
|
current_host: this.kernel.peer.host,
|
|
4064
4105
|
peer_url,
|
|
4065
4106
|
peer_qr,
|
|
@@ -4072,8 +4113,25 @@ class Server {
|
|
|
4072
4113
|
list,
|
|
4073
4114
|
})
|
|
4074
4115
|
}))
|
|
4116
|
+
this.app.get("/api/plugin/menu", ex(async (req, res) => {
|
|
4117
|
+
try {
|
|
4118
|
+
if (!this.kernel.plugin.config) {
|
|
4119
|
+
await this.kernel.plugin.init()
|
|
4120
|
+
}
|
|
4121
|
+
const pluginMenu = this.kernel.plugin && this.kernel.plugin.config && Array.isArray(this.kernel.plugin.config.menu)
|
|
4122
|
+
? this.kernel.plugin.config.menu
|
|
4123
|
+
: []
|
|
4124
|
+
res.json({ menu: pluginMenu })
|
|
4125
|
+
} catch (error) {
|
|
4126
|
+
console.warn('Failed to load plugin menu for create launcher modal', error)
|
|
4127
|
+
res.json({ menu: [] })
|
|
4128
|
+
}
|
|
4129
|
+
}))
|
|
4075
4130
|
this.app.get("/plugins", (req, res) => {
|
|
4076
|
-
res.redirect(301, "/
|
|
4131
|
+
res.redirect(301, "/agents")
|
|
4132
|
+
})
|
|
4133
|
+
this.app.get("/terminals", (req, res) => {
|
|
4134
|
+
res.redirect(301, "/agents")
|
|
4077
4135
|
})
|
|
4078
4136
|
this.app.get("/screenshots", ex(async (req, res) => {
|
|
4079
4137
|
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
@@ -6491,6 +6549,40 @@ class Server {
|
|
|
6491
6549
|
let exec_menus = []
|
|
6492
6550
|
let shell_menus = []
|
|
6493
6551
|
let href_menus = []
|
|
6552
|
+
const normalizeForSort = (value) => {
|
|
6553
|
+
if (typeof value !== 'string') {
|
|
6554
|
+
return ''
|
|
6555
|
+
}
|
|
6556
|
+
return value.trim().toLocaleLowerCase()
|
|
6557
|
+
}
|
|
6558
|
+
const compareMenuItems = (a = {}, b = {}) => {
|
|
6559
|
+
const titleDiff = normalizeForSort(a.title).localeCompare(normalizeForSort(b.title))
|
|
6560
|
+
if (titleDiff !== 0) {
|
|
6561
|
+
return titleDiff
|
|
6562
|
+
}
|
|
6563
|
+
const subtitleDiff = normalizeForSort(a.subtitle).localeCompare(normalizeForSort(b.subtitle))
|
|
6564
|
+
if (subtitleDiff !== 0) {
|
|
6565
|
+
return subtitleDiff
|
|
6566
|
+
}
|
|
6567
|
+
return normalizeForSort(a.href || a.link).localeCompare(normalizeForSort(b.href || b.link))
|
|
6568
|
+
}
|
|
6569
|
+
const sortMenuEntries = (menuArray) => {
|
|
6570
|
+
if (!Array.isArray(menuArray) || menuArray.length < 2) {
|
|
6571
|
+
return
|
|
6572
|
+
}
|
|
6573
|
+
menuArray.sort(compareMenuItems)
|
|
6574
|
+
}
|
|
6575
|
+
const sortNestedMenus = (menuArray) => {
|
|
6576
|
+
if (!Array.isArray(menuArray)) {
|
|
6577
|
+
return
|
|
6578
|
+
}
|
|
6579
|
+
sortMenuEntries(menuArray)
|
|
6580
|
+
for (const entry of menuArray) {
|
|
6581
|
+
if (entry && Array.isArray(entry.menu)) {
|
|
6582
|
+
sortNestedMenus(entry.menu)
|
|
6583
|
+
}
|
|
6584
|
+
}
|
|
6585
|
+
}
|
|
6494
6586
|
if (plugin_menu.length > 0) {
|
|
6495
6587
|
for(let item of plugin_menu) {
|
|
6496
6588
|
// if shell.run method exists
|
|
@@ -6518,30 +6610,37 @@ class Server {
|
|
|
6518
6610
|
href_menus.push(item)
|
|
6519
6611
|
}
|
|
6520
6612
|
}
|
|
6521
|
-
exec_menus
|
|
6522
|
-
shell_menus
|
|
6523
|
-
href_menus
|
|
6613
|
+
sortNestedMenus(exec_menus)
|
|
6614
|
+
sortNestedMenus(shell_menus)
|
|
6615
|
+
sortNestedMenus(href_menus)
|
|
6524
6616
|
}
|
|
6525
6617
|
|
|
6526
6618
|
// let terminal = await this.terminals(filepath)
|
|
6527
6619
|
// let online_terminal = await this.getPluginGlobal(req, terminal, filepath)
|
|
6528
6620
|
// console.log("online_terminal", online_terminal)
|
|
6529
6621
|
terminal.menus = href_menus
|
|
6622
|
+
sortNestedMenus(terminal.menu)
|
|
6623
|
+
sortNestedMenus(terminal.menus)
|
|
6530
6624
|
let dynamic = [
|
|
6531
6625
|
terminal,
|
|
6532
6626
|
{
|
|
6533
6627
|
icon: "fa-solid fa-robot",
|
|
6534
|
-
title: "
|
|
6535
|
-
subtitle: "
|
|
6628
|
+
title: "Terminal Agents",
|
|
6629
|
+
subtitle: "Start a session in Pinokio",
|
|
6536
6630
|
menu: shell_menus
|
|
6537
6631
|
},
|
|
6538
6632
|
{
|
|
6539
6633
|
icon: "fa-solid fa-arrow-up-right-from-square",
|
|
6540
|
-
title: "
|
|
6541
|
-
subtitle: "Open
|
|
6634
|
+
title: "IDE Agents",
|
|
6635
|
+
subtitle: "Open the project in external IDEs",
|
|
6542
6636
|
menu: exec_menus
|
|
6543
6637
|
},
|
|
6544
6638
|
]
|
|
6639
|
+
for (const item of dynamic) {
|
|
6640
|
+
if (item && Array.isArray(item.menu)) {
|
|
6641
|
+
sortNestedMenus(item.menu)
|
|
6642
|
+
}
|
|
6643
|
+
}
|
|
6545
6644
|
|
|
6546
6645
|
let spec = ""
|
|
6547
6646
|
try {
|