pinokiod 3.180.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/favicon.js +91 -34
- package/kernel/peer.js +73 -0
- package/kernel/util.js +28 -4
- package/package.json +1 -1
- package/server/index.js +237 -35
- package/server/public/common.js +677 -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 +124 -0
- package/server/public/nav.js +227 -64
- 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 +195 -4
- package/server/public/tab-idle-notifier.js +700 -4
- package/server/public/terminal-settings.js +1131 -0
- package/server/public/urldropdown.css +28 -1
- package/server/socket.js +71 -4
- package/server/views/{terminals.ejs → agents.ejs} +108 -32
- package/server/views/app.ejs +321 -104
- package/server/views/bootstrap.ejs +8 -0
- package/server/views/connect.ejs +10 -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 +10 -1
- package/server/views/init/index.ejs +18 -3
- package/server/views/install.ejs +8 -0
- package/server/views/layout.ejs +2 -0
- package/server/views/net.ejs +10 -1
- package/server/views/network.ejs +10 -1
- package/server/views/pro.ejs +8 -0
- package/server/views/prototype/index.ejs +8 -0
- package/server/views/screenshots.ejs +10 -2
- package/server/views/settings.ejs +10 -2
- package/server/views/shell.ejs +8 -0
- package/server/views/terminal.ejs +8 -0
- package/server/views/tools.ejs +10 -2
|
@@ -167,6 +167,70 @@
|
|
|
167
167
|
display: flex;
|
|
168
168
|
flex-direction: column;
|
|
169
169
|
background: var(--surface-color);
|
|
170
|
+
position: relative;
|
|
171
|
+
overflow: visible;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.files-app__sidebar-toggle {
|
|
175
|
+
position: absolute;
|
|
176
|
+
top: 20px;
|
|
177
|
+
left: 0;
|
|
178
|
+
transform: translateX(-50%);
|
|
179
|
+
display: inline-flex;
|
|
180
|
+
align-items: center;
|
|
181
|
+
justify-content: center;
|
|
182
|
+
width: 32px;
|
|
183
|
+
height: 36px;
|
|
184
|
+
border: 1px solid var(--border-color);
|
|
185
|
+
border-left: none;
|
|
186
|
+
border-radius: 0 8px 8px 0;
|
|
187
|
+
background: var(--surface-color);
|
|
188
|
+
color: var(--muted-color);
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
transition: transform 0.2s ease, color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
|
|
191
|
+
box-shadow: 0 2px 6px rgba(15, 23, 42, 0.08);
|
|
192
|
+
z-index: 10;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.files-app__sidebar-toggle:hover,
|
|
196
|
+
.files-app__sidebar-toggle:focus-visible {
|
|
197
|
+
background: rgba(127, 91, 243, 0.14);
|
|
198
|
+
color: var(--accent-color);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.files-app__sidebar-toggle--collapsed {
|
|
202
|
+
top: 36px;
|
|
203
|
+
left: 8px;
|
|
204
|
+
transform: translateX(0);
|
|
205
|
+
border-left: 1px solid var(--border-color);
|
|
206
|
+
border-radius: 8px;
|
|
207
|
+
background: rgba(127, 91, 243, 0.12);
|
|
208
|
+
color: var(--accent-color);
|
|
209
|
+
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.18);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.files-app__sidebar-toggle--collapsed:hover,
|
|
213
|
+
.files-app__sidebar-toggle--collapsed:focus-visible {
|
|
214
|
+
background: rgba(127, 91, 243, 0.18);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.files-app__sidebar-toggle:focus-visible {
|
|
218
|
+
outline: 2px solid var(--accent-color);
|
|
219
|
+
outline-offset: 2px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.files-app--sidebar-collapsed .files-app__sidebar {
|
|
223
|
+
flex: 0 0 0;
|
|
224
|
+
width: 0;
|
|
225
|
+
min-width: 0;
|
|
226
|
+
max-width: 0;
|
|
227
|
+
opacity: 0;
|
|
228
|
+
pointer-events: none;
|
|
229
|
+
border-right: none;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.files-app--sidebar-collapsed .files-app__sidebar-scroll {
|
|
233
|
+
padding: 0;
|
|
170
234
|
}
|
|
171
235
|
|
|
172
236
|
.files-app__tabs:empty {
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
const SIDEBAR_STORAGE_PREFIX = 'files-app.sidebar-collapsed.';
|
|
32
|
+
|
|
31
33
|
const FilesApp = {
|
|
32
34
|
init(config) {
|
|
33
35
|
if (this._initialized) {
|
|
@@ -48,14 +50,19 @@
|
|
|
48
50
|
activePath: null,
|
|
49
51
|
selectedTreePath: null,
|
|
50
52
|
statusTimer: null,
|
|
53
|
+
sidebarCollapsed: false,
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
this.dom = {
|
|
57
|
+
root: document.querySelector('.files-app'),
|
|
54
58
|
treeRoot: document.getElementById('files-app-tree'),
|
|
55
59
|
tabs: document.getElementById('files-app-tabs'),
|
|
56
60
|
editorContainer: document.getElementById('files-app-editor'),
|
|
57
61
|
status: document.getElementById('files-app-status'),
|
|
58
62
|
saveBtn: document.getElementById('files-app-save'),
|
|
63
|
+
sidebar: document.querySelector('.files-app__sidebar'),
|
|
64
|
+
main: document.querySelector('.files-app__main'),
|
|
65
|
+
sidebarToggle: document.getElementById('files-app-toggle-sidebar'),
|
|
59
66
|
};
|
|
60
67
|
|
|
61
68
|
this.api = createApi(config.workspace, this.state.workspaceRoot);
|
|
@@ -63,6 +70,8 @@
|
|
|
63
70
|
this.modelist = ace.require('ace/ext/modelist');
|
|
64
71
|
this.undoManagerCtor = ace.require('ace/undomanager').UndoManager;
|
|
65
72
|
|
|
73
|
+
setupSidebarToggle.call(this);
|
|
74
|
+
|
|
66
75
|
this.dom.saveBtn.addEventListener('click', (event) => {
|
|
67
76
|
event.preventDefault();
|
|
68
77
|
this.saveActiveFile();
|
|
@@ -227,6 +236,84 @@
|
|
|
227
236
|
},
|
|
228
237
|
};
|
|
229
238
|
|
|
239
|
+
function setupSidebarToggle() {
|
|
240
|
+
if (!this.dom.sidebarToggle || !this.dom.root) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const storageKey = getSidebarStorageKey(this.state.workspace);
|
|
245
|
+
const initialCollapsed = readSidebarPreference(storageKey);
|
|
246
|
+
applySidebarCollapsed.call(this, initialCollapsed);
|
|
247
|
+
|
|
248
|
+
this.dom.sidebarToggle.addEventListener('click', () => {
|
|
249
|
+
const nextState = !this.state.sidebarCollapsed;
|
|
250
|
+
applySidebarCollapsed.call(this, nextState);
|
|
251
|
+
persistSidebarPreference(storageKey, nextState);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
window.addEventListener('storage', (event) => {
|
|
255
|
+
if (event.key === storageKey) {
|
|
256
|
+
const newValue = event.newValue === '1';
|
|
257
|
+
if (newValue !== this.state.sidebarCollapsed) {
|
|
258
|
+
applySidebarCollapsed.call(this, newValue);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function readSidebarPreference(storageKey) {
|
|
265
|
+
try {
|
|
266
|
+
const storedValue = window.localStorage.getItem(storageKey);
|
|
267
|
+
return storedValue === '1';
|
|
268
|
+
} catch (error) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function persistSidebarPreference(storageKey, collapsed) {
|
|
274
|
+
try {
|
|
275
|
+
window.localStorage.setItem(storageKey, collapsed ? '1' : '0');
|
|
276
|
+
} catch (error) {
|
|
277
|
+
/* ignore */
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function applySidebarCollapsed(collapsed) {
|
|
282
|
+
this.state.sidebarCollapsed = collapsed;
|
|
283
|
+
if (this.dom.root) {
|
|
284
|
+
this.dom.root.classList.toggle('files-app--sidebar-collapsed', collapsed);
|
|
285
|
+
}
|
|
286
|
+
if (this.dom.sidebar) {
|
|
287
|
+
if (collapsed) {
|
|
288
|
+
this.dom.sidebar.setAttribute('aria-hidden', 'true');
|
|
289
|
+
} else {
|
|
290
|
+
this.dom.sidebar.removeAttribute('aria-hidden');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (!this.dom.sidebarToggle) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
this.dom.sidebarToggle.setAttribute('aria-expanded', String(!collapsed));
|
|
297
|
+
const label = collapsed ? 'Show files' : 'Hide files';
|
|
298
|
+
this.dom.sidebarToggle.setAttribute('aria-label', label);
|
|
299
|
+
this.dom.sidebarToggle.title = label;
|
|
300
|
+
const srText = this.dom.sidebarToggle.querySelector('.sr-only');
|
|
301
|
+
if (srText) {
|
|
302
|
+
srText.textContent = label;
|
|
303
|
+
}
|
|
304
|
+
this.dom.sidebarToggle.classList.toggle('files-app__sidebar-toggle--collapsed', collapsed);
|
|
305
|
+
const icon = this.dom.sidebarToggle.querySelector('i');
|
|
306
|
+
if (icon) {
|
|
307
|
+
icon.classList.toggle('fa-chevron-left', !collapsed);
|
|
308
|
+
icon.classList.toggle('fa-chevron-right', collapsed);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function getSidebarStorageKey(workspace) {
|
|
313
|
+
const scope = workspace ? String(workspace) : 'default';
|
|
314
|
+
return `${SIDEBAR_STORAGE_PREFIX}${scope}`;
|
|
315
|
+
}
|
|
316
|
+
|
|
230
317
|
function createApi(workspace, workspaceRoot) {
|
|
231
318
|
const list = async (pathPosix) => {
|
|
232
319
|
const params = new URLSearchParams({ workspace });
|
package/server/public/install.js
CHANGED
|
@@ -146,7 +146,7 @@ const install = async (name, url, term, socket, options) => {
|
|
|
146
146
|
text: `Downloaded to ~/${normalizedPath}/${name}`,
|
|
147
147
|
timeout: 4000
|
|
148
148
|
})
|
|
149
|
-
location.href = "/
|
|
149
|
+
location.href = "/agents"
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -166,7 +166,14 @@ const createTerm = async (_theme) => {
|
|
|
166
166
|
if (res && res.config) {
|
|
167
167
|
config = res.config
|
|
168
168
|
}
|
|
169
|
+
const baseConfig = Object.assign({}, config)
|
|
170
|
+
if (window.PinokioTerminalSettings && typeof window.PinokioTerminalSettings.applyToConfig === 'function') {
|
|
171
|
+
config = window.PinokioTerminalSettings.applyToConfig(config)
|
|
172
|
+
}
|
|
169
173
|
const term = new Terminal(config)
|
|
174
|
+
if (window.PinokioTerminalSettings && typeof window.PinokioTerminalSettings.register === 'function') {
|
|
175
|
+
window.PinokioTerminalSettings.register(term, { baseConfig })
|
|
176
|
+
}
|
|
170
177
|
const fitAddon = new FitAddon.FitAddon();
|
|
171
178
|
term.loadAddon(fitAddon);
|
|
172
179
|
term.loadAddon(new WebLinksAddon.WebLinksAddon());
|
package/server/public/layout.js
CHANGED
|
@@ -790,4 +790,128 @@
|
|
|
790
790
|
};
|
|
791
791
|
|
|
792
792
|
window.PinokioLayout = api;
|
|
793
|
+
// Mobile "Tap to connect" curtain is centralized in common.js to avoid duplicates
|
|
794
|
+
|
|
795
|
+
// Top-level notification listener (indicator + optional chime) for mobile
|
|
796
|
+
(function initTopLevelNotificationListener() {
|
|
797
|
+
try { if (window.top && window.top !== window) return; } catch (_) { return; }
|
|
798
|
+
if (window.__pinokioTopNotifyListener) {
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
window.__pinokioTopNotifyListener = true;
|
|
802
|
+
|
|
803
|
+
const ensureIndicator = (() => {
|
|
804
|
+
let el = null;
|
|
805
|
+
let styleInjected = false;
|
|
806
|
+
return () => {
|
|
807
|
+
if (!styleInjected) {
|
|
808
|
+
const style = document.createElement('style');
|
|
809
|
+
style.textContent = `
|
|
810
|
+
.pinokio-notify-indicator{position:fixed;top:12px;right:12px;z-index:2147483647;display:none;align-items:center;gap:8px;padding:8px 10px;border-radius:999px;background:rgba(15,23,42,0.92);color:#fff;font:600 12px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;box-shadow:0 10px 30px rgba(0,0,0,0.35)}
|
|
811
|
+
.pinokio-notify-indicator .bell{font-size:14px}
|
|
812
|
+
.pinokio-notify-indicator.show{display:inline-flex;animation:pinokioNotifyPop 160ms ease-out, pinokioNotifyFade 1600ms ease-in 700ms forwards}
|
|
813
|
+
@keyframes pinokioNotifyPop{from{transform:translateY(-6px) scale(.98);opacity:0}to{transform:translateY(0) scale(1);opacity:1}}
|
|
814
|
+
@keyframes pinokioNotifyFade{to{opacity:0;transform:translateY(-4px)}}
|
|
815
|
+
@media (max-width: 768px){.pinokio-notify-indicator{top:10px;right:10px;padding:7px 9px;font-size:12px}}
|
|
816
|
+
`;
|
|
817
|
+
document.head.appendChild(style);
|
|
818
|
+
styleInjected = true;
|
|
819
|
+
}
|
|
820
|
+
if (!el) {
|
|
821
|
+
el = document.createElement('div');
|
|
822
|
+
el.className = 'pinokio-notify-indicator';
|
|
823
|
+
const icon = document.createElement('span');
|
|
824
|
+
icon.className = 'bell';
|
|
825
|
+
icon.textContent = '🔔';
|
|
826
|
+
const text = document.createElement('span');
|
|
827
|
+
text.className = 'text';
|
|
828
|
+
text.textContent = 'Notification received';
|
|
829
|
+
el.appendChild(icon);
|
|
830
|
+
el.appendChild(text);
|
|
831
|
+
document.body.appendChild(el);
|
|
832
|
+
}
|
|
833
|
+
return el;
|
|
834
|
+
};
|
|
835
|
+
})();
|
|
836
|
+
|
|
837
|
+
const flashIndicator = (message) => {
|
|
838
|
+
const node = ensureIndicator();
|
|
839
|
+
const text = node.querySelector('.text');
|
|
840
|
+
if (text) {
|
|
841
|
+
const msg = (message && typeof message === 'string' && message.trim()) ? message.trim() : 'Notification received';
|
|
842
|
+
text.textContent = msg.length > 80 ? (msg.slice(0,77) + '…') : msg;
|
|
843
|
+
}
|
|
844
|
+
node.classList.remove('show');
|
|
845
|
+
void node.offsetWidth;
|
|
846
|
+
node.classList.add('show');
|
|
847
|
+
setTimeout(() => node.classList.remove('show'), 2400);
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
const isFalseyString = (value) => {
|
|
851
|
+
return typeof value === 'string' && ['false', '0', 'no', 'off'].includes(value.trim().toLowerCase());
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
const tryPlay = (url) => {
|
|
855
|
+
if (!url || url === false || isFalseyString(url)) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
try {
|
|
859
|
+
const isString = typeof url === 'string';
|
|
860
|
+
const trimmed = isString ? url.trim() : '';
|
|
861
|
+
const hasCustom = isString && trimmed.length > 0 && trimmed.toLowerCase() !== 'true';
|
|
862
|
+
const src = hasCustom ? url : '/chime.mp3';
|
|
863
|
+
let a = window.__pinokioChimeAudio;
|
|
864
|
+
if (!a) {
|
|
865
|
+
a = new Audio(src);
|
|
866
|
+
a.preload = 'auto';
|
|
867
|
+
a.loop = false;
|
|
868
|
+
a.muted = false;
|
|
869
|
+
window.__pinokioChimeAudio = a;
|
|
870
|
+
} else {
|
|
871
|
+
try { if (a.src && !a.src.endsWith(src)) a.src = src; } catch (_) {}
|
|
872
|
+
}
|
|
873
|
+
try { a.currentTime = 0; } catch (_) {}
|
|
874
|
+
const p = a.play();
|
|
875
|
+
if (p && typeof p.catch === 'function') { p.catch(() => {}); }
|
|
876
|
+
if (typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function') {
|
|
877
|
+
try { navigator.vibrate(80); } catch (_) {}
|
|
878
|
+
}
|
|
879
|
+
} catch (_) {}
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
const listen = () => {
|
|
883
|
+
const SocketCtor = typeof window.Socket === 'function' ? window.Socket : (typeof Socket === 'function' ? Socket : null);
|
|
884
|
+
if (!SocketCtor || typeof WebSocket === 'undefined') {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
const socket = new SocketCtor();
|
|
888
|
+
try {
|
|
889
|
+
socket.run({ method: 'kernel.notifications', mode: 'listen', device_id: (typeof window.PinokioGetDeviceId === 'function') ? window.PinokioGetDeviceId() : undefined }, (packet) => {
|
|
890
|
+
if (!packet || packet.id !== 'kernel.notifications' || packet.type !== 'notification') {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const payload = packet.data || {};
|
|
894
|
+
// If targeted to a specific device, ignore only when our id exists and mismatches
|
|
895
|
+
try {
|
|
896
|
+
const targetId = (typeof payload.device_id === 'string' && payload.device_id.trim()) ? payload.device_id.trim() : null;
|
|
897
|
+
if (targetId) {
|
|
898
|
+
const myId = (typeof window.PinokioGetDeviceId === 'function') ? window.PinokioGetDeviceId() : null;
|
|
899
|
+
if (myId && myId !== targetId) return;
|
|
900
|
+
}
|
|
901
|
+
} catch (_) {}
|
|
902
|
+
flashIndicator(payload.message);
|
|
903
|
+
tryPlay(payload.sound);
|
|
904
|
+
}).then(() => {
|
|
905
|
+
// socket closed; ignore
|
|
906
|
+
}).catch(() => {});
|
|
907
|
+
window.__pinokioTopNotifySocket = socket;
|
|
908
|
+
} catch (_) {}
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
if (document.readyState === 'loading') {
|
|
912
|
+
document.addEventListener('DOMContentLoaded', listen, { once: true });
|
|
913
|
+
} else {
|
|
914
|
+
listen();
|
|
915
|
+
}
|
|
916
|
+
})();
|
|
793
917
|
})();
|