pinokiod 3.86.0 → 3.87.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/Dockerfile +61 -0
- package/docker-entrypoint.sh +75 -0
- package/kernel/api/hf/index.js +1 -1
- package/kernel/api/index.js +1 -1
- package/kernel/api/shell/index.js +6 -0
- package/kernel/api/terminal/index.js +166 -0
- package/kernel/bin/conda.js +3 -2
- package/kernel/bin/index.js +53 -2
- package/kernel/bin/setup.js +32 -0
- package/kernel/bin/vs.js +11 -2
- package/kernel/index.js +42 -2
- package/kernel/info.js +36 -0
- package/kernel/peer.js +42 -15
- package/kernel/router/index.js +23 -15
- package/kernel/router/localhost_static_router.js +0 -3
- package/kernel/router/pinokio_domain_router.js +333 -0
- package/kernel/shells.js +21 -1
- package/kernel/util.js +2 -2
- package/package.json +2 -1
- package/script/install-mode.js +33 -0
- package/script/pinokio.json +7 -0
- package/server/index.js +513 -173
- package/server/public/Socket.js +48 -0
- package/server/public/common.js +1441 -276
- package/server/public/fseditor.js +71 -12
- package/server/public/install.js +1 -1
- package/server/public/layout.js +740 -0
- package/server/public/modalinput.js +0 -1
- package/server/public/style.css +97 -105
- package/server/public/tab-idle-notifier.js +629 -0
- package/server/public/terminal_input_tracker.js +63 -0
- package/server/public/urldropdown.css +319 -53
- package/server/public/urldropdown.js +615 -159
- package/server/public/window_storage.js +97 -28
- package/server/socket.js +40 -9
- package/server/views/500.ejs +2 -2
- package/server/views/app.ejs +3136 -1367
- package/server/views/bookmarklet.ejs +1 -1
- package/server/views/bootstrap.ejs +1 -1
- package/server/views/columns.ejs +2 -13
- package/server/views/connect.ejs +3 -4
- package/server/views/container.ejs +1 -2
- package/server/views/d.ejs +223 -53
- package/server/views/editor.ejs +1 -1
- package/server/views/file_explorer.ejs +1 -1
- package/server/views/index.ejs +12 -11
- package/server/views/index2.ejs +4 -4
- package/server/views/init/index.ejs +4 -5
- package/server/views/install.ejs +1 -1
- package/server/views/layout.ejs +105 -0
- package/server/views/net.ejs +39 -7
- package/server/views/network.ejs +20 -6
- package/server/views/network2.ejs +1 -1
- package/server/views/old_network.ejs +2 -2
- package/server/views/partials/dynamic.ejs +3 -5
- package/server/views/partials/menu.ejs +3 -5
- package/server/views/partials/running.ejs +1 -1
- package/server/views/pro.ejs +1 -1
- package/server/views/prototype/index.ejs +1 -1
- package/server/views/review.ejs +11 -23
- package/server/views/rows.ejs +2 -13
- package/server/views/screenshots.ejs +293 -138
- package/server/views/settings.ejs +3 -4
- package/server/views/setup.ejs +1 -2
- package/server/views/shell.ejs +277 -26
- package/server/views/terminal.ejs +322 -49
- package/server/views/tools.ejs +448 -4
package/server/views/shell.ejs
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
<script src="/xterm-addon-search-bar.js"></script>
|
|
14
14
|
<script src="/sweetalert2.js"></script>
|
|
15
15
|
<script src="/Socket.js"></script>
|
|
16
|
+
<script src="/terminal_input_tracker.js"></script>
|
|
16
17
|
<script src="/common.js"></script>
|
|
17
18
|
<script src="/he.js"></script>
|
|
18
19
|
<script src="/opener.js"></script>
|
|
@@ -105,6 +106,28 @@ header {
|
|
|
105
106
|
.xterm .xterm-viewport {
|
|
106
107
|
width: initial !important;
|
|
107
108
|
}
|
|
109
|
+
#terminal {
|
|
110
|
+
position: relative;
|
|
111
|
+
}
|
|
112
|
+
.terminal-drop-overlay {
|
|
113
|
+
position: absolute;
|
|
114
|
+
inset: 0;
|
|
115
|
+
background: rgba(0,0,0,0.6);
|
|
116
|
+
color: #fff;
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
justify-content: center;
|
|
120
|
+
font-size: 14px;
|
|
121
|
+
text-align: center;
|
|
122
|
+
pointer-events: none;
|
|
123
|
+
opacity: 0;
|
|
124
|
+
transition: opacity 0.15s ease-in-out;
|
|
125
|
+
padding: 12px;
|
|
126
|
+
z-index: 5;
|
|
127
|
+
}
|
|
128
|
+
.terminal-drop-overlay.active {
|
|
129
|
+
opacity: 1;
|
|
130
|
+
}
|
|
108
131
|
.navheader3 {
|
|
109
132
|
padding: 10px;
|
|
110
133
|
}
|
|
@@ -177,12 +200,113 @@ body.dark #status-window b {
|
|
|
177
200
|
<link href="/terminal.css" rel="stylesheet"/>
|
|
178
201
|
<script>
|
|
179
202
|
let shell_id
|
|
203
|
+
let lastNotifiedShellId = null
|
|
204
|
+
const notifyShellSession = (id) => {
|
|
205
|
+
if (!id || id === lastNotifiedShellId) {
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
lastNotifiedShellId = id
|
|
209
|
+
if (window && window.parent && typeof window.parent.postMessage === "function") {
|
|
210
|
+
window.parent.postMessage({
|
|
211
|
+
type: "shell-session-id",
|
|
212
|
+
frame: window.name || null,
|
|
213
|
+
shellId: id
|
|
214
|
+
}, "*")
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function buildBinaryRpcPayload(rpc, fileEntries) {
|
|
218
|
+
const metadata = {
|
|
219
|
+
rpc,
|
|
220
|
+
buffer_keys: fileEntries.map((entry) => entry.key)
|
|
221
|
+
}
|
|
222
|
+
const metaBytes = new TextEncoder().encode(JSON.stringify(metadata))
|
|
223
|
+
const separator = new Uint8Array([0])
|
|
224
|
+
const parts = [metaBytes, separator]
|
|
225
|
+
for (const entry of fileEntries) {
|
|
226
|
+
const lenBytes = new Uint8Array(4)
|
|
227
|
+
new DataView(lenBytes.buffer).setUint32(0, entry.buffer.byteLength)
|
|
228
|
+
parts.push(lenBytes, entry.buffer)
|
|
229
|
+
}
|
|
230
|
+
const totalLength = parts.reduce((sum, part) => sum + part.length, 0)
|
|
231
|
+
const combined = new Uint8Array(totalLength)
|
|
232
|
+
let offset = 0
|
|
233
|
+
for (const part of parts) {
|
|
234
|
+
combined.set(part, offset)
|
|
235
|
+
offset += part.length
|
|
236
|
+
}
|
|
237
|
+
return combined.buffer
|
|
238
|
+
}
|
|
180
239
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
181
240
|
const n = new N()
|
|
182
241
|
class RPC {
|
|
183
242
|
constructor() {
|
|
184
243
|
this.socket = new Socket()
|
|
185
244
|
this.buffer = []
|
|
245
|
+
this.uploadContext = {
|
|
246
|
+
cwd: "~" + location.pathname
|
|
247
|
+
}
|
|
248
|
+
this.inputBuffer = ""
|
|
249
|
+
this.inputTracker = window.TerminalInputTracker ? new window.TerminalInputTracker({
|
|
250
|
+
getFrameName: () => window.name || null,
|
|
251
|
+
getWindow: () => window
|
|
252
|
+
}) : null
|
|
253
|
+
}
|
|
254
|
+
resetInputBuffer() {
|
|
255
|
+
if (this.inputTracker) {
|
|
256
|
+
this.inputTracker.reset()
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
this.inputBuffer = ""
|
|
260
|
+
}
|
|
261
|
+
handleBackspace() {
|
|
262
|
+
if (this.inputTracker) {
|
|
263
|
+
this.inputTracker.handleBackspace()
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
if (this.inputBuffer.length > 0) {
|
|
267
|
+
this.inputBuffer = this.inputBuffer.slice(0, -1)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
captureTextInput(text) {
|
|
271
|
+
if (this.inputTracker) {
|
|
272
|
+
this.inputTracker.capture(text)
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
const normalized = text.replace(/\r/g, "\n")
|
|
279
|
+
const segments = normalized.split("\n")
|
|
280
|
+
for (let i = 0; i < segments.length; i++) {
|
|
281
|
+
const segment = segments[i]
|
|
282
|
+
const isLast = (i === segments.length - 1)
|
|
283
|
+
if (!isLast) {
|
|
284
|
+
const line = this.inputBuffer + segment
|
|
285
|
+
this.inputBuffer = ""
|
|
286
|
+
this.notifyLineSubmitted(line)
|
|
287
|
+
} else {
|
|
288
|
+
this.inputBuffer += segment
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
notifyLineSubmitted(line) {
|
|
293
|
+
if (this.inputTracker) {
|
|
294
|
+
this.inputTracker.submit(line)
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
if (!window || !window.parent || typeof window.parent.postMessage !== "function") {
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
const safeLine = (line || "").replace(/[\x00-\x1F\x7F]/g, "")
|
|
301
|
+
const preview = safeLine.trim()
|
|
302
|
+
const limit = 200
|
|
303
|
+
const truncated = preview.length > limit ? preview.slice(0, limit) + "..." : preview
|
|
304
|
+
window.parent.postMessage({
|
|
305
|
+
type: "terminal-input",
|
|
306
|
+
frame: window.name || null,
|
|
307
|
+
line: truncated,
|
|
308
|
+
hasContent: truncated.length > 0
|
|
309
|
+
}, "*")
|
|
186
310
|
}
|
|
187
311
|
write(text) {
|
|
188
312
|
if (text !== "\u0007") {
|
|
@@ -317,9 +441,11 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
317
441
|
// </div>`
|
|
318
442
|
}
|
|
319
443
|
} else if (packet.type === "stream") {
|
|
444
|
+
refreshParent(packet)
|
|
320
445
|
// set the current shell id
|
|
321
446
|
if (packet.data.id) {
|
|
322
447
|
shell_id = packet.data.id
|
|
448
|
+
notifyShellSession(shell_id)
|
|
323
449
|
}
|
|
324
450
|
if (packet.data.raw) {
|
|
325
451
|
// console.log({ packet })
|
|
@@ -363,6 +489,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
363
489
|
if (packet.data) {
|
|
364
490
|
if (packet.data.shell) {
|
|
365
491
|
shell_id = packet.data.shell
|
|
492
|
+
notifyShellSession(shell_id)
|
|
366
493
|
}
|
|
367
494
|
if (packet.data.state) {
|
|
368
495
|
this.write(packet.data.state)
|
|
@@ -669,6 +796,71 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
669
796
|
<% } %>
|
|
670
797
|
await this.start(mode)
|
|
671
798
|
}
|
|
799
|
+
async uploadFiles(files, overlay) {
|
|
800
|
+
if (!files || files.length === 0) {
|
|
801
|
+
return
|
|
802
|
+
}
|
|
803
|
+
if (!this.socket || !this.socket.ws || this.socket.ws.readyState !== WebSocket.OPEN) {
|
|
804
|
+
n.Noty({
|
|
805
|
+
text: "Terminal connection is not ready for uploads",
|
|
806
|
+
type: "error"
|
|
807
|
+
})
|
|
808
|
+
return
|
|
809
|
+
}
|
|
810
|
+
const entries = []
|
|
811
|
+
for (let i = 0; i < files.length; i++) {
|
|
812
|
+
const file = files[i]
|
|
813
|
+
if (!file || typeof file.arrayBuffer !== "function") {
|
|
814
|
+
continue
|
|
815
|
+
}
|
|
816
|
+
try {
|
|
817
|
+
const arrayBuffer = await file.arrayBuffer()
|
|
818
|
+
const key = `file_${Date.now()}_${i}_${Math.random().toString(16).slice(2, 8)}`
|
|
819
|
+
entries.push({
|
|
820
|
+
key,
|
|
821
|
+
name: file.name || `upload-${i + 1}`,
|
|
822
|
+
size: file.size,
|
|
823
|
+
type: file.type || "",
|
|
824
|
+
buffer: new Uint8Array(arrayBuffer)
|
|
825
|
+
})
|
|
826
|
+
} catch (error) {
|
|
827
|
+
console.error("Failed to read dropped file", error)
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
if (entries.length === 0) {
|
|
831
|
+
n.Noty({
|
|
832
|
+
text: "No readable files were dropped",
|
|
833
|
+
type: "error"
|
|
834
|
+
})
|
|
835
|
+
return
|
|
836
|
+
}
|
|
837
|
+
const rpcPayload = {
|
|
838
|
+
method: "terminal.upload",
|
|
839
|
+
params: {
|
|
840
|
+
id: shell_id,
|
|
841
|
+
cwd: this.uploadContext.cwd,
|
|
842
|
+
files: entries.map(({ key, name, size, type }) => ({ key, name, size, type }))
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
try {
|
|
846
|
+
if (overlay) {
|
|
847
|
+
overlay.classList.add("active")
|
|
848
|
+
overlay.textContent = "Uploading..."
|
|
849
|
+
}
|
|
850
|
+
await this.socket.sendBinary(buildBinaryRpcPayload(rpcPayload, entries))
|
|
851
|
+
} catch (error) {
|
|
852
|
+
console.error("Upload failed", error)
|
|
853
|
+
n.Noty({
|
|
854
|
+
text: `Upload failed: ${error.message}`,
|
|
855
|
+
type: "error"
|
|
856
|
+
})
|
|
857
|
+
} finally {
|
|
858
|
+
if (overlay) {
|
|
859
|
+
overlay.classList.remove("active")
|
|
860
|
+
overlay.textContent = "Drop files to upload"
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
672
864
|
async createTerm (_theme) {
|
|
673
865
|
console.log(xtermTheme)
|
|
674
866
|
if (!this.term) {
|
|
@@ -684,7 +876,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
684
876
|
let config = {
|
|
685
877
|
scrollback: 9999999,
|
|
686
878
|
//fontSize: 12,
|
|
687
|
-
fontSize:
|
|
879
|
+
fontSize: 12,
|
|
688
880
|
theme,
|
|
689
881
|
//theme: xtermTheme.FrontEndDelight
|
|
690
882
|
//theme: xtermTheme.Afterglow
|
|
@@ -706,8 +898,41 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
706
898
|
}
|
|
707
899
|
const term = new Terminal(config)
|
|
708
900
|
term.open(document.querySelector("#terminal"))
|
|
901
|
+
const terminalContainer = document.querySelector("#terminal")
|
|
902
|
+
const dropOverlay = document.createElement("div")
|
|
903
|
+
dropOverlay.className = "terminal-drop-overlay"
|
|
904
|
+
dropOverlay.textContent = "Drop files to upload"
|
|
905
|
+
terminalContainer.appendChild(dropOverlay)
|
|
906
|
+
let dragDepth = 0
|
|
907
|
+
const prevent = (event) => {
|
|
908
|
+
event.preventDefault()
|
|
909
|
+
event.stopPropagation()
|
|
910
|
+
}
|
|
911
|
+
terminalContainer.addEventListener("dragenter", (event) => {
|
|
912
|
+
prevent(event)
|
|
913
|
+
dragDepth += 1
|
|
914
|
+
dropOverlay.classList.add("active")
|
|
915
|
+
})
|
|
916
|
+
terminalContainer.addEventListener("dragover", prevent)
|
|
917
|
+
terminalContainer.addEventListener("dragleave", (event) => {
|
|
918
|
+
prevent(event)
|
|
919
|
+
dragDepth = Math.max(0, dragDepth - 1)
|
|
920
|
+
if (dragDepth === 0) {
|
|
921
|
+
dropOverlay.classList.remove("active")
|
|
922
|
+
}
|
|
923
|
+
})
|
|
924
|
+
terminalContainer.addEventListener("drop", async (event) => {
|
|
925
|
+
prevent(event)
|
|
926
|
+
dragDepth = 0
|
|
927
|
+
dropOverlay.classList.remove("active")
|
|
928
|
+
const dropped = Array.from(event.dataTransfer ? event.dataTransfer.files || [] : [])
|
|
929
|
+
if (!dropped.length) {
|
|
930
|
+
return
|
|
931
|
+
}
|
|
932
|
+
await this.uploadFiles(dropped, dropOverlay)
|
|
933
|
+
this.term.focus()
|
|
934
|
+
})
|
|
709
935
|
term.attachCustomKeyEventHandler(event => {
|
|
710
|
-
console.log({ event })
|
|
711
936
|
if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
|
|
712
937
|
const selection = term.getSelection();
|
|
713
938
|
if (selection) {
|
|
@@ -717,18 +942,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
717
942
|
}
|
|
718
943
|
if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
|
|
719
944
|
navigator.clipboard.readText().then((text) => {
|
|
720
|
-
console.log({ text })
|
|
721
|
-
//this.socket.run({
|
|
722
|
-
// //key: "\x1b[200~" + text + "\x1b[201~",
|
|
723
|
-
// key: text,
|
|
724
|
-
// id: shell_id
|
|
725
|
-
//})
|
|
726
945
|
this.socket.run({
|
|
727
946
|
//key: "\x1b[200~" + text + "\x1b[201~",
|
|
728
947
|
key: text,
|
|
729
948
|
id: shell_id,
|
|
730
|
-
|
|
949
|
+
paste: true
|
|
731
950
|
})
|
|
951
|
+
this.captureTextInput(text)
|
|
732
952
|
|
|
733
953
|
})
|
|
734
954
|
return false
|
|
@@ -742,15 +962,48 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
742
962
|
////term.resize(cols, rows-5);
|
|
743
963
|
//term.resize(cols, rows);
|
|
744
964
|
|
|
745
|
-
term.onKey(({ key }) => {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
965
|
+
term.onKey(({ key, domEvent }) => {
|
|
966
|
+
if (this.socket && shell_id) {
|
|
967
|
+
this.socket.run({
|
|
968
|
+
key,
|
|
969
|
+
id: shell_id
|
|
970
|
+
})
|
|
971
|
+
}
|
|
972
|
+
if (!domEvent) {
|
|
973
|
+
return
|
|
974
|
+
}
|
|
975
|
+
if (domEvent.key === "Backspace") {
|
|
976
|
+
this.handleBackspace()
|
|
977
|
+
return
|
|
978
|
+
}
|
|
979
|
+
if (domEvent.key === "Enter") {
|
|
980
|
+
this.captureTextInput("\n")
|
|
981
|
+
return
|
|
982
|
+
}
|
|
983
|
+
if (domEvent.key === "Tab") {
|
|
984
|
+
this.captureTextInput("\t")
|
|
985
|
+
return
|
|
986
|
+
}
|
|
987
|
+
if (domEvent.key === "Escape") {
|
|
988
|
+
this.resetInputBuffer()
|
|
989
|
+
return
|
|
990
|
+
}
|
|
991
|
+
if (domEvent.ctrlKey || domEvent.metaKey || domEvent.altKey) {
|
|
992
|
+
if (key === "\u0015" || key === "\u0017" || key === "\u000b" || key === "\u0003" || domEvent.key === "c" || domEvent.key === "C" || domEvent.key === "u" || domEvent.key === "U") {
|
|
993
|
+
this.resetInputBuffer()
|
|
753
994
|
}
|
|
995
|
+
return
|
|
996
|
+
}
|
|
997
|
+
if (key === "\u0015" || key === "\u0017" || key === "\u000b") {
|
|
998
|
+
this.resetInputBuffer()
|
|
999
|
+
return
|
|
1000
|
+
}
|
|
1001
|
+
if (key === "\u0008") {
|
|
1002
|
+
this.handleBackspace()
|
|
1003
|
+
return
|
|
1004
|
+
}
|
|
1005
|
+
if (typeof key === "string" && key.length === 1 && key >= " ") {
|
|
1006
|
+
this.captureTextInput(key)
|
|
754
1007
|
}
|
|
755
1008
|
});
|
|
756
1009
|
|
|
@@ -838,17 +1091,15 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
838
1091
|
|
|
839
1092
|
window.addEventListener('message', function(event) {
|
|
840
1093
|
console.log("Message received from the parent: ", event.data); // Message received from parent
|
|
841
|
-
// "foreground" message
|
|
842
|
-
// Needed to fix the issue where the terminal won't write anything when in background
|
|
843
|
-
// so when coming back from background, need to refresh by just adding a newline.
|
|
1094
|
+
// "foreground" message triggers a redraw so buffered output appears after backgrounding.
|
|
844
1095
|
if (event.data && event.data.action === "foreground") {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1096
|
+
if (rpc.dirty && rpc.term) {
|
|
1097
|
+
const endRow = Math.max(rpc.term.rows - 1, 0)
|
|
1098
|
+
rpc.term.refresh(0, endRow)
|
|
1099
|
+
if (typeof rpc.term.scrollToBottom === "function") {
|
|
1100
|
+
rpc.term.scrollToBottom()
|
|
1101
|
+
}
|
|
850
1102
|
rpc.dirty = false
|
|
851
|
-
} else {
|
|
852
1103
|
}
|
|
853
1104
|
}
|
|
854
1105
|
});
|