pinokiod 7.1.13 → 7.1.15
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/index.js +5 -0
- package/package.json +1 -1
- package/server/index.js +130 -10
- package/server/public/common.js +0 -3
- package/server/public/install.js +34 -20
- package/server/public/style.css +212 -14
- package/server/views/app.ejs +38 -43
- package/server/views/d.ejs +162 -51
- package/server/views/partials/d_terminal_column.ejs +40 -0
- package/server/views/partials/d_terminal_options.ejs +28 -0
- package/server/views/terminal.ejs +76 -4
package/kernel/index.js
CHANGED
|
@@ -906,6 +906,11 @@ class Kernel {
|
|
|
906
906
|
/// })
|
|
907
907
|
/// }
|
|
908
908
|
async init(options) {
|
|
909
|
+
// Re-arm startup readiness on every init cycle so restart waits for the
|
|
910
|
+
// newly rebuilt template/sysinfo state instead of a stale resolved promise.
|
|
911
|
+
this.sysReady = new Promise((resolve) => {
|
|
912
|
+
this._resolveSysReady = resolve
|
|
913
|
+
})
|
|
909
914
|
|
|
910
915
|
let home = this.store.get("home") || process.env.PINOKIO_HOME
|
|
911
916
|
this.homedir = home
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -970,12 +970,11 @@ class Server {
|
|
|
970
970
|
console.log("Chrome")
|
|
971
971
|
|
|
972
972
|
let name = req.params.name
|
|
973
|
-
|
|
974
|
-
console.time(binCheckLabel)
|
|
973
|
+
console.time("bin check")
|
|
975
974
|
let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
|
|
976
975
|
bin: this.kernel.bin.preset("dev"),
|
|
977
976
|
})
|
|
978
|
-
console.timeEnd(
|
|
977
|
+
console.timeEnd("bin check")
|
|
979
978
|
if (!requirements_pending && install_required) {
|
|
980
979
|
res.redirect(`/setup/dev?callback=${req.originalUrl}`)
|
|
981
980
|
return
|
|
@@ -4765,8 +4764,12 @@ class Server {
|
|
|
4765
4764
|
// }
|
|
4766
4765
|
}
|
|
4767
4766
|
}
|
|
4768
|
-
async terminals(filepath) {
|
|
4769
|
-
|
|
4767
|
+
async terminals(filepath, options = {}) {
|
|
4768
|
+
const includeVenvs = options.includeVenvs !== false
|
|
4769
|
+
let venvs = []
|
|
4770
|
+
if (includeVenvs) {
|
|
4771
|
+
venvs = await Util.find_venv(filepath)
|
|
4772
|
+
}
|
|
4770
4773
|
let terminal
|
|
4771
4774
|
const windowsBashPath = await this.resolveWindowsBashPath()
|
|
4772
4775
|
const hasWindowsBashOption = this.kernel.platform === "win32" && typeof windowsBashPath === "string" && windowsBashPath.length > 0
|
|
@@ -4845,6 +4848,107 @@ class Server {
|
|
|
4845
4848
|
}
|
|
4846
4849
|
return terminal
|
|
4847
4850
|
}
|
|
4851
|
+
async resolveDevTerminalShell(shellKey) {
|
|
4852
|
+
if (this.kernel.platform === "win32") {
|
|
4853
|
+
if (shellKey === "cmd") {
|
|
4854
|
+
return {
|
|
4855
|
+
key: "cmd",
|
|
4856
|
+
title: "Cmd",
|
|
4857
|
+
icon: "fa-brands fa-windows",
|
|
4858
|
+
groupIndex: 0,
|
|
4859
|
+
shellPath: null,
|
|
4860
|
+
}
|
|
4861
|
+
}
|
|
4862
|
+
if (shellKey === "bash") {
|
|
4863
|
+
const windowsBashPath = await this.resolveWindowsBashPath()
|
|
4864
|
+
if (typeof windowsBashPath === "string" && windowsBashPath.length > 0) {
|
|
4865
|
+
return {
|
|
4866
|
+
key: "bash",
|
|
4867
|
+
title: "Bash",
|
|
4868
|
+
icon: "fa-solid fa-terminal",
|
|
4869
|
+
groupIndex: 1,
|
|
4870
|
+
shellPath: windowsBashPath,
|
|
4871
|
+
}
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
return null
|
|
4875
|
+
}
|
|
4876
|
+
if (shellKey === "bash") {
|
|
4877
|
+
return {
|
|
4878
|
+
key: "bash",
|
|
4879
|
+
title: "Bash",
|
|
4880
|
+
icon: "fa-solid fa-terminal",
|
|
4881
|
+
groupIndex: 0,
|
|
4882
|
+
shellPath: null,
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
return null
|
|
4886
|
+
}
|
|
4887
|
+
async devTerminals(filepath, refPath) {
|
|
4888
|
+
const shellKeys = this.kernel.platform === "win32" ? ["cmd", "bash"] : ["bash"]
|
|
4889
|
+
const menu = []
|
|
4890
|
+
for (const shellKey of shellKeys) {
|
|
4891
|
+
const shell = await this.resolveDevTerminalShell(shellKey)
|
|
4892
|
+
if (!shell) {
|
|
4893
|
+
continue
|
|
4894
|
+
}
|
|
4895
|
+
menu.push({
|
|
4896
|
+
icon: shell.icon,
|
|
4897
|
+
title: shell.title,
|
|
4898
|
+
subtitle: "Open shell options",
|
|
4899
|
+
shell_key: shell.key,
|
|
4900
|
+
options_url: `/pinokio/d-terminal-options/${refPath}?shell=${encodeURIComponent(shell.key)}`,
|
|
4901
|
+
})
|
|
4902
|
+
}
|
|
4903
|
+
return {
|
|
4904
|
+
icon: "fa-solid fa-terminal",
|
|
4905
|
+
title: "Terminals",
|
|
4906
|
+
subtitle: "Choose a shell, then open it with or without Python activated.",
|
|
4907
|
+
skip_sort: true,
|
|
4908
|
+
menu,
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
async devTerminalOptions(filepath, shellKey) {
|
|
4912
|
+
const shell = await this.resolveDevTerminalShell(shellKey)
|
|
4913
|
+
if (!shell) {
|
|
4914
|
+
return []
|
|
4915
|
+
}
|
|
4916
|
+
const shellOptions = [
|
|
4917
|
+
this.renderShell(filepath, shell.groupIndex, 0, {
|
|
4918
|
+
icon: shell.icon,
|
|
4919
|
+
title: "Shell",
|
|
4920
|
+
subtitle: `Open a plain ${shell.title} shell`,
|
|
4921
|
+
text: "Shell",
|
|
4922
|
+
type: "Start",
|
|
4923
|
+
shell: {
|
|
4924
|
+
...(shell.shellPath ? { shell: shell.shellPath } : {}),
|
|
4925
|
+
input: true,
|
|
4926
|
+
}
|
|
4927
|
+
})
|
|
4928
|
+
]
|
|
4929
|
+
const venvs = await Util.find_venv(filepath)
|
|
4930
|
+
for (let i = 0; i < venvs.length; i++) {
|
|
4931
|
+
const venv = venvs[i]
|
|
4932
|
+
const parsed = path.parse(venv)
|
|
4933
|
+
let relativeVenv = path.relative(filepath, venv)
|
|
4934
|
+
if (!relativeVenv || relativeVenv.startsWith("..")) {
|
|
4935
|
+
relativeVenv = parsed.base || path.basename(venv)
|
|
4936
|
+
}
|
|
4937
|
+
shellOptions.push(this.renderShell(filepath, shell.groupIndex, i + 1, {
|
|
4938
|
+
icon: "fa-brands fa-python",
|
|
4939
|
+
title: "Python Shell",
|
|
4940
|
+
subtitle: `Activates ${relativeVenv}`,
|
|
4941
|
+
text: `Python Shell: ${relativeVenv}`,
|
|
4942
|
+
type: "Start",
|
|
4943
|
+
shell: {
|
|
4944
|
+
...(shell.shellPath ? { shell: shell.shellPath } : {}),
|
|
4945
|
+
venv,
|
|
4946
|
+
input: true,
|
|
4947
|
+
}
|
|
4948
|
+
}))
|
|
4949
|
+
}
|
|
4950
|
+
return shellOptions
|
|
4951
|
+
}
|
|
4848
4952
|
async getPluginGlobal(req, config, terminal, filepath) {
|
|
4849
4953
|
// if (!this.kernel.plugin.config) {
|
|
4850
4954
|
// await this.kernel.plugin.init()
|
|
@@ -10766,8 +10870,8 @@ class Server {
|
|
|
10766
10870
|
}))
|
|
10767
10871
|
this.app.get("/d/*", ex(async (req, res) => {
|
|
10768
10872
|
let filepath = Util.u2p(req.params[0])
|
|
10769
|
-
let terminal = await this.
|
|
10770
|
-
let plugin = await this.getPluginGlobal(req, this.kernel.plugin.config,
|
|
10873
|
+
let terminal = await this.devTerminals(filepath, req.params[0])
|
|
10874
|
+
let plugin = await this.getPluginGlobal(req, this.kernel.plugin.config, { menu: [] }, filepath)
|
|
10771
10875
|
let html = ""
|
|
10772
10876
|
let plugin_menu
|
|
10773
10877
|
try {
|
|
@@ -10779,7 +10883,7 @@ class Server {
|
|
|
10779
10883
|
let current_urls = await this.current_urls(req.originalUrl.slice(1))
|
|
10780
10884
|
let retry = false
|
|
10781
10885
|
// if plugin_menu is empty, try again in 1 sec
|
|
10782
|
-
if (plugin_menu.length === 0) {
|
|
10886
|
+
if (plugin_menu.length === 0 && (!terminal.menu || terminal.menu.length === 0)) {
|
|
10783
10887
|
retry = true
|
|
10784
10888
|
}
|
|
10785
10889
|
|
|
@@ -10867,7 +10971,6 @@ class Server {
|
|
|
10867
10971
|
// let online_terminal = await this.getPluginGlobal(req, terminal, filepath)
|
|
10868
10972
|
// console.log("online_terminal", online_terminal)
|
|
10869
10973
|
terminal.menus = href_menus
|
|
10870
|
-
sortNestedMenus(terminal.menu)
|
|
10871
10974
|
sortNestedMenus(terminal.menus)
|
|
10872
10975
|
let dynamic = [
|
|
10873
10976
|
terminal,
|
|
@@ -10889,7 +10992,7 @@ class Server {
|
|
|
10889
10992
|
},
|
|
10890
10993
|
]
|
|
10891
10994
|
for (const item of dynamic) {
|
|
10892
|
-
if (item && Array.isArray(item.menu)) {
|
|
10995
|
+
if (item && Array.isArray(item.menu) && !item.skip_sort) {
|
|
10893
10996
|
sortNestedMenus(item.menu)
|
|
10894
10997
|
}
|
|
10895
10998
|
}
|
|
@@ -11051,6 +11154,23 @@ class Server {
|
|
|
11051
11154
|
res.send("")
|
|
11052
11155
|
}
|
|
11053
11156
|
}))
|
|
11157
|
+
this.app.get("/pinokio/d-terminal-options/*", ex(async (req, res) => {
|
|
11158
|
+
let filepath = Util.u2p(req.params[0])
|
|
11159
|
+
const shellKey = typeof req.query.shell === "string" ? req.query.shell.trim().toLowerCase() : ""
|
|
11160
|
+
let options = await this.devTerminalOptions(filepath, shellKey)
|
|
11161
|
+
const html = await new Promise((resolve, reject) => {
|
|
11162
|
+
ejs.renderFile(path.resolve(__dirname, "views/partials/d_terminal_options.ejs"), {
|
|
11163
|
+
options,
|
|
11164
|
+
}, (err, html) => {
|
|
11165
|
+
if (err) {
|
|
11166
|
+
reject(err)
|
|
11167
|
+
return
|
|
11168
|
+
}
|
|
11169
|
+
resolve(html)
|
|
11170
|
+
})
|
|
11171
|
+
})
|
|
11172
|
+
res.send(html)
|
|
11173
|
+
}))
|
|
11054
11174
|
this.app.get("/pinokio/dynamic/:name", ex(async (req, res) => {
|
|
11055
11175
|
// await this.kernel.plugin.init()
|
|
11056
11176
|
|
package/server/public/common.js
CHANGED
|
@@ -3928,9 +3928,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
3928
3928
|
}
|
|
3929
3929
|
const fallbackUrl = buildAskAiLaunchUrl(tool.href, workspaceCwd);
|
|
3930
3930
|
if (fallbackUrl) {
|
|
3931
|
-
refreshTerminalSessions(fallbackUrl, workspaceCwd, {
|
|
3932
|
-
retryDelays: []
|
|
3933
|
-
});
|
|
3934
3931
|
window.location.href = fallbackUrl;
|
|
3935
3932
|
}
|
|
3936
3933
|
}
|
package/server/public/install.js
CHANGED
|
@@ -5,42 +5,56 @@ const installname = async (url, name, options) => {
|
|
|
5
5
|
if (!defaultName.endsWith(".git")) {
|
|
6
6
|
defaultName = defaultName + ".git"
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
const normalizedPath = options && options.path ? normalizeInstallPath(options.path) : null
|
|
9
|
+
const relativePath = normalizedPath || DEFAULT_INSTALL_RELATIVE_PATH
|
|
10
|
+
const inputValue = name || defaultName
|
|
9
11
|
let result = await Swal.fire({
|
|
10
12
|
title: 'Save as',
|
|
11
|
-
html:
|
|
13
|
+
html: `<p class="pinokio-download-note">Saved in <code>~/${relativePath}</code></p>`,
|
|
14
|
+
input: 'text',
|
|
15
|
+
inputLabel: 'Folder name',
|
|
16
|
+
inputValue,
|
|
17
|
+
inputPlaceholder: defaultName,
|
|
18
|
+
inputAttributes: {
|
|
19
|
+
autocapitalize: 'off',
|
|
20
|
+
autocorrect: 'off',
|
|
21
|
+
autocomplete: 'off',
|
|
22
|
+
spellcheck: 'false'
|
|
23
|
+
},
|
|
12
24
|
focusConfirm: false,
|
|
13
25
|
focusCancel: false,
|
|
14
26
|
showCancelButton: true,
|
|
15
27
|
showCloseButton: true,
|
|
16
28
|
cancelButtonText: 'Cancel',
|
|
17
29
|
confirmButtonText: 'Download',
|
|
30
|
+
buttonsStyling: false,
|
|
31
|
+
backdrop: 'rgba(9, 11, 15, 0.65)',
|
|
32
|
+
width: 'min(460px, 92vw)',
|
|
18
33
|
allowOutsideClick: () => !Swal.isLoading(),
|
|
19
34
|
allowEscapeKey: true,
|
|
20
35
|
showLoaderOnConfirm: true,
|
|
36
|
+
loaderHtml: '<span class="pinokio-download-loader-spinner" aria-hidden="true"></span><span class="pinokio-download-loader-text">Downloading...</span>',
|
|
21
37
|
customClass: {
|
|
22
|
-
popup: 'pinokio-download-modal'
|
|
38
|
+
popup: 'pinokio-download-modal',
|
|
39
|
+
htmlContainer: 'pinokio-download-html',
|
|
40
|
+
inputLabel: 'pinokio-download-label',
|
|
41
|
+
input: 'pinokio-download-input',
|
|
42
|
+
validationMessage: 'pinokio-download-validation',
|
|
43
|
+
actions: 'pinokio-download-actions',
|
|
44
|
+
loader: 'pinokio-download-loader',
|
|
45
|
+
closeButton: 'pinokio-download-close',
|
|
46
|
+
confirmButton: 'pinokio-download-confirm',
|
|
47
|
+
cancelButton: 'pinokio-download-cancel'
|
|
23
48
|
},
|
|
24
49
|
didOpen: () => {
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
input.value = name
|
|
28
|
-
} else {
|
|
29
|
-
input.value = defaultName;
|
|
30
|
-
}
|
|
31
|
-
input.addEventListener("keypress", (e) => {
|
|
32
|
-
if (e.key === "Enter") {
|
|
33
|
-
e.preventDefault()
|
|
34
|
-
e.stopPropagation()
|
|
35
|
-
Swal.clickConfirm()
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
setTimeout(() => {
|
|
50
|
+
const input = Swal.getInput()
|
|
51
|
+
if (input) {
|
|
39
52
|
input.focus()
|
|
40
|
-
|
|
53
|
+
input.select()
|
|
54
|
+
}
|
|
41
55
|
},
|
|
42
|
-
preConfirm: async () => {
|
|
43
|
-
const folderName = (
|
|
56
|
+
preConfirm: async (value) => {
|
|
57
|
+
const folderName = String(value || "").trim()
|
|
44
58
|
const validationError = validateInstallFolderName(folderName)
|
|
45
59
|
if (validationError) {
|
|
46
60
|
Swal.showValidationMessage(validationError)
|
package/server/public/style.css
CHANGED
|
@@ -2248,23 +2248,221 @@ body.dark .swal2-title {
|
|
|
2248
2248
|
background: rgba(0,0,0,0.8) !important;
|
|
2249
2249
|
*/
|
|
2250
2250
|
}
|
|
2251
|
-
.pinokio-download-modal
|
|
2252
|
-
|
|
2253
|
-
padding:
|
|
2251
|
+
.swal2-popup.pinokio-download-modal {
|
|
2252
|
+
border-radius: 14px !important;
|
|
2253
|
+
padding: 18px !important;
|
|
2254
|
+
background: #ffffff !important;
|
|
2255
|
+
color: #0f172a !important;
|
|
2256
|
+
border: 1px solid rgba(148, 163, 184, 0.2) !important;
|
|
2257
|
+
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18) !important;
|
|
2258
|
+
}
|
|
2259
|
+
body.dark .swal2-popup.pinokio-download-modal {
|
|
2260
|
+
background: #0f172a !important;
|
|
2261
|
+
color: #e2e8f0 !important;
|
|
2262
|
+
border: 1px solid rgba(148, 163, 184, 0.24) !important;
|
|
2263
|
+
box-shadow: 0 32px 90px rgba(2, 8, 23, 0.72) !important;
|
|
2264
|
+
}
|
|
2265
|
+
.swal2-popup.pinokio-download-modal .swal2-title {
|
|
2266
|
+
margin: 0 40px 6px 0 !important;
|
|
2267
|
+
padding: 0 !important;
|
|
2268
|
+
text-align: left !important;
|
|
2269
|
+
font-size: 20px !important;
|
|
2270
|
+
font-weight: 700 !important;
|
|
2271
|
+
line-height: 1.2 !important;
|
|
2272
|
+
letter-spacing: -0.02em !important;
|
|
2273
|
+
color: #0f172a !important;
|
|
2254
2274
|
}
|
|
2255
|
-
.pinokio-download-modal .swal2-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2275
|
+
body.dark .swal2-popup.pinokio-download-modal .swal2-title {
|
|
2276
|
+
color: #f8fafc !important;
|
|
2277
|
+
}
|
|
2278
|
+
.pinokio-download-html.swal2-html-container {
|
|
2279
|
+
margin: 0 0 12px 0 !important;
|
|
2280
|
+
padding: 0 !important;
|
|
2281
|
+
text-align: left !important;
|
|
2282
|
+
font-size: 13px !important;
|
|
2283
|
+
line-height: 1.45 !important;
|
|
2284
|
+
color: rgba(71, 85, 105, 0.84) !important;
|
|
2285
|
+
}
|
|
2286
|
+
body.dark .pinokio-download-html.swal2-html-container {
|
|
2287
|
+
color: rgba(148, 163, 184, 0.88) !important;
|
|
2288
|
+
}
|
|
2289
|
+
.pinokio-download-note {
|
|
2262
2290
|
margin: 0;
|
|
2263
|
-
font-size: 28px;
|
|
2264
|
-
color: rgba(0,0,0,0.55);
|
|
2265
2291
|
}
|
|
2266
|
-
|
|
2267
|
-
|
|
2292
|
+
.pinokio-download-note code {
|
|
2293
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
2294
|
+
font-size: 12px;
|
|
2295
|
+
padding: 2px 6px;
|
|
2296
|
+
border-radius: 999px;
|
|
2297
|
+
background: rgba(15, 23, 42, 0.06);
|
|
2298
|
+
color: inherit;
|
|
2299
|
+
}
|
|
2300
|
+
body.dark .pinokio-download-note code {
|
|
2301
|
+
background: rgba(148, 163, 184, 0.16);
|
|
2302
|
+
}
|
|
2303
|
+
.pinokio-download-label.swal2-input-label {
|
|
2304
|
+
display: block !important;
|
|
2305
|
+
width: 100% !important;
|
|
2306
|
+
margin: 0 0 6px 0 !important;
|
|
2307
|
+
text-align: left !important;
|
|
2308
|
+
font-size: 12px !important;
|
|
2309
|
+
font-weight: 600 !important;
|
|
2310
|
+
line-height: 1.3 !important;
|
|
2311
|
+
color: rgba(15, 23, 42, 0.78) !important;
|
|
2312
|
+
}
|
|
2313
|
+
body.dark .pinokio-download-label.swal2-input-label {
|
|
2314
|
+
color: rgba(241, 245, 249, 0.84) !important;
|
|
2315
|
+
}
|
|
2316
|
+
.pinokio-download-input.swal2-input {
|
|
2317
|
+
width: 100% !important;
|
|
2318
|
+
margin: 0 !important;
|
|
2319
|
+
height: 40px !important;
|
|
2320
|
+
padding: 0 12px !important;
|
|
2321
|
+
border-radius: 10px !important;
|
|
2322
|
+
border: 1px solid rgba(148, 163, 184, 0.58) !important;
|
|
2323
|
+
background: #ffffff !important;
|
|
2324
|
+
color: #0f172a !important;
|
|
2325
|
+
box-shadow: none !important;
|
|
2326
|
+
font-size: 14px !important;
|
|
2327
|
+
line-height: 1.2 !important;
|
|
2328
|
+
}
|
|
2329
|
+
.pinokio-download-input.swal2-input:focus {
|
|
2330
|
+
border-color: rgba(59, 130, 246, 0.78) !important;
|
|
2331
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.16) !important;
|
|
2332
|
+
}
|
|
2333
|
+
body.dark .pinokio-download-input.swal2-input {
|
|
2334
|
+
border-color: rgba(148, 163, 184, 0.45) !important;
|
|
2335
|
+
background: rgba(15, 23, 42, 0.65) !important;
|
|
2336
|
+
color: rgba(248, 250, 252, 0.96) !important;
|
|
2337
|
+
}
|
|
2338
|
+
.pinokio-download-actions.swal2-actions {
|
|
2339
|
+
width: 100% !important;
|
|
2340
|
+
justify-content: flex-end !important;
|
|
2341
|
+
gap: 8px !important;
|
|
2342
|
+
margin-top: 14px !important;
|
|
2343
|
+
padding: 0 !important;
|
|
2344
|
+
}
|
|
2345
|
+
.pinokio-download-actions.swal2-actions.swal2-loading {
|
|
2346
|
+
justify-content: flex-end !important;
|
|
2347
|
+
gap: 8px !important;
|
|
2348
|
+
}
|
|
2349
|
+
.pinokio-download-confirm.swal2-confirm,
|
|
2350
|
+
.pinokio-download-cancel.swal2-cancel {
|
|
2351
|
+
min-width: 92px !important;
|
|
2352
|
+
height: 36px !important;
|
|
2353
|
+
margin: 0 !important;
|
|
2354
|
+
padding: 0 14px !important;
|
|
2355
|
+
border-radius: 10px !important;
|
|
2356
|
+
font-size: 13px !important;
|
|
2357
|
+
font-weight: 650 !important;
|
|
2358
|
+
}
|
|
2359
|
+
.swal2-popup.pinokio-download-modal .pinokio-download-confirm.swal2-confirm {
|
|
2360
|
+
border: none !important;
|
|
2361
|
+
background: royalblue !important;
|
|
2362
|
+
color: #ffffff !important;
|
|
2363
|
+
}
|
|
2364
|
+
.swal2-popup.pinokio-download-modal .pinokio-download-confirm.swal2-confirm:hover {
|
|
2365
|
+
background: #315cd8 !important;
|
|
2366
|
+
}
|
|
2367
|
+
.swal2-popup.pinokio-download-modal .pinokio-download-cancel.swal2-cancel {
|
|
2368
|
+
border: 1px solid rgba(148, 163, 184, 0.6) !important;
|
|
2369
|
+
background: #ffffff !important;
|
|
2370
|
+
color: #0f172a !important;
|
|
2371
|
+
}
|
|
2372
|
+
body.dark .swal2-popup.pinokio-download-modal .pinokio-download-cancel.swal2-cancel {
|
|
2373
|
+
border-color: rgba(148, 163, 184, 0.52) !important;
|
|
2374
|
+
background: rgba(15, 23, 42, 0.64) !important;
|
|
2375
|
+
color: rgba(248, 250, 252, 0.96) !important;
|
|
2376
|
+
}
|
|
2377
|
+
.swal2-popup.pinokio-download-modal .pinokio-download-cancel.swal2-cancel:hover {
|
|
2378
|
+
background: rgba(148, 163, 184, 0.12) !important;
|
|
2379
|
+
}
|
|
2380
|
+
body.dark .swal2-popup.pinokio-download-modal .pinokio-download-cancel.swal2-cancel:hover {
|
|
2381
|
+
background: rgba(148, 163, 184, 0.18) !important;
|
|
2382
|
+
}
|
|
2383
|
+
.pinokio-download-close.swal2-close {
|
|
2384
|
+
position: absolute !important;
|
|
2385
|
+
top: 14px !important;
|
|
2386
|
+
right: 14px !important;
|
|
2387
|
+
width: 28px !important;
|
|
2388
|
+
height: 28px !important;
|
|
2389
|
+
line-height: 28px !important;
|
|
2390
|
+
margin: 0 !important;
|
|
2391
|
+
font-size: 20px !important;
|
|
2392
|
+
color: rgba(71, 85, 105, 0.7) !important;
|
|
2393
|
+
border-radius: 999px !important;
|
|
2394
|
+
display: flex !important;
|
|
2395
|
+
align-items: center !important;
|
|
2396
|
+
justify-content: center !important;
|
|
2397
|
+
transition: background 0.2s ease, color 0.2s ease !important;
|
|
2398
|
+
}
|
|
2399
|
+
.pinokio-download-close.swal2-close:hover {
|
|
2400
|
+
background: rgba(148, 163, 184, 0.14) !important;
|
|
2401
|
+
color: #0f172a !important;
|
|
2402
|
+
}
|
|
2403
|
+
body.dark .pinokio-download-close.swal2-close {
|
|
2404
|
+
color: rgba(226, 232, 240, 0.72) !important;
|
|
2405
|
+
}
|
|
2406
|
+
body.dark .pinokio-download-close.swal2-close:hover {
|
|
2407
|
+
background: rgba(148, 163, 184, 0.16) !important;
|
|
2408
|
+
color: #f8fafc !important;
|
|
2409
|
+
}
|
|
2410
|
+
.pinokio-download-validation.swal2-validation-message {
|
|
2411
|
+
margin: 10px 0 0 0 !important;
|
|
2412
|
+
padding: 10px 12px !important;
|
|
2413
|
+
border-radius: 10px !important;
|
|
2414
|
+
background: rgba(255, 66, 66, 0.1) !important;
|
|
2415
|
+
border: 1px solid rgba(255, 66, 66, 0.18) !important;
|
|
2416
|
+
border-left: 4px solid rgba(255, 66, 66, 0.5) !important;
|
|
2417
|
+
color: rgba(15, 23, 42, 0.92) !important;
|
|
2418
|
+
font-size: 13px !important;
|
|
2419
|
+
line-height: 1.4 !important;
|
|
2420
|
+
justify-content: flex-start !important;
|
|
2421
|
+
align-items: flex-start !important;
|
|
2422
|
+
}
|
|
2423
|
+
.pinokio-download-validation.swal2-validation-message::before {
|
|
2424
|
+
display: none !important;
|
|
2425
|
+
}
|
|
2426
|
+
body.dark .pinokio-download-validation.swal2-validation-message {
|
|
2427
|
+
background: rgba(255, 66, 66, 0.16) !important;
|
|
2428
|
+
border-color: rgba(255, 66, 66, 0.24) !important;
|
|
2429
|
+
border-left-color: rgba(255, 66, 66, 0.68) !important;
|
|
2430
|
+
color: rgba(248, 250, 252, 0.94) !important;
|
|
2431
|
+
}
|
|
2432
|
+
.pinokio-download-loader.swal2-loader {
|
|
2433
|
+
align-items: center !important;
|
|
2434
|
+
justify-content: center !important;
|
|
2435
|
+
gap: 8px !important;
|
|
2436
|
+
width: auto !important;
|
|
2437
|
+
height: 36px !important;
|
|
2438
|
+
margin: 0 !important;
|
|
2439
|
+
padding: 0 14px !important;
|
|
2440
|
+
animation: none !important;
|
|
2441
|
+
border: none !important;
|
|
2442
|
+
border-radius: 10px !important;
|
|
2443
|
+
background: royalblue !important;
|
|
2444
|
+
color: #ffffff !important;
|
|
2445
|
+
}
|
|
2446
|
+
.pinokio-download-actions.swal2-actions.swal2-loading .pinokio-download-loader.swal2-loader {
|
|
2447
|
+
display: inline-flex !important;
|
|
2448
|
+
}
|
|
2449
|
+
.pinokio-download-loader-spinner {
|
|
2450
|
+
width: 14px;
|
|
2451
|
+
height: 14px;
|
|
2452
|
+
border: 2px solid rgba(255, 255, 255, 0.34);
|
|
2453
|
+
border-top-color: #ffffff;
|
|
2454
|
+
border-radius: 50%;
|
|
2455
|
+
animation: pinokio-download-spin 0.8s linear infinite;
|
|
2456
|
+
}
|
|
2457
|
+
.pinokio-download-loader-text {
|
|
2458
|
+
font-size: 13px;
|
|
2459
|
+
font-weight: 700;
|
|
2460
|
+
line-height: 1;
|
|
2461
|
+
}
|
|
2462
|
+
@keyframes pinokio-download-spin {
|
|
2463
|
+
to {
|
|
2464
|
+
transform: rotate(360deg);
|
|
2465
|
+
}
|
|
2268
2466
|
}
|
|
2269
2467
|
.swal2-validation-message {
|
|
2270
2468
|
margin: 12px 15px 0;
|
package/server/views/app.ejs
CHANGED
|
@@ -5302,6 +5302,7 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
5302
5302
|
let interacted = false
|
|
5303
5303
|
let global_selector
|
|
5304
5304
|
let pendingSelectionRetry = null
|
|
5305
|
+
let initialWorkspaceDiskUsageRequested = false
|
|
5305
5306
|
const scheduleSelectionRetry = (delay = 100) => {
|
|
5306
5307
|
if (pendingSelectionRetry !== null) {
|
|
5307
5308
|
return
|
|
@@ -5470,10 +5471,12 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
5470
5471
|
})
|
|
5471
5472
|
let frame = document.createElement("iframe")
|
|
5472
5473
|
frame.name = name
|
|
5473
|
-
refreshTerminalSessions(href)
|
|
5474
5474
|
frame.src = href
|
|
5475
5475
|
frame.classList.add("selected")
|
|
5476
5476
|
iframe_onerror(frame)
|
|
5477
|
+
frame.addEventListener("load", () => {
|
|
5478
|
+
requestInitialWorkspaceDiskUsage()
|
|
5479
|
+
}, { once: true })
|
|
5477
5480
|
frame.setAttribute(
|
|
5478
5481
|
"allow",
|
|
5479
5482
|
"clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; fullscreen *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
|
|
@@ -5484,17 +5487,6 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
5484
5487
|
document.querySelector("main").appendChild(frame)
|
|
5485
5488
|
loaded[name] = true
|
|
5486
5489
|
}
|
|
5487
|
-
function refreshTerminalSessions(rawUrl, workspaceCwd = "", options = {}) {
|
|
5488
|
-
try {
|
|
5489
|
-
const discovery = window.PinokioTerminalsDiscovery
|
|
5490
|
-
if (!discovery || typeof discovery.refreshTerminalSessions !== "function") {
|
|
5491
|
-
return false
|
|
5492
|
-
}
|
|
5493
|
-
return discovery.refreshTerminalSessions(rawUrl, workspaceCwd, options)
|
|
5494
|
-
} catch (_) {
|
|
5495
|
-
return false
|
|
5496
|
-
}
|
|
5497
|
-
}
|
|
5498
5490
|
installBrowserviewInjectTargetObserver()
|
|
5499
5491
|
document.addEventListener("click", (e) => {
|
|
5500
5492
|
interacted = true
|
|
@@ -6579,14 +6571,10 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6579
6571
|
|
|
6580
6572
|
|
|
6581
6573
|
// Instantiate a frame with the selected target's href
|
|
6582
|
-
let url = target.href
|
|
6583
6574
|
const targetInjectDescriptors = readLinkInjectDescriptors(target)
|
|
6584
|
-
if (!
|
|
6575
|
+
if (!target.href) {
|
|
6585
6576
|
return
|
|
6586
6577
|
}
|
|
6587
|
-
// document.querySelector("#open-browser").href = url
|
|
6588
|
-
// document.querySelector("#clone-tab").setAttribute("data-href", url)
|
|
6589
|
-
|
|
6590
6578
|
//document.querySelector("#location").value = url
|
|
6591
6579
|
|
|
6592
6580
|
// document.querySelector("#open-location").setAttribute('href', target.href)
|
|
@@ -6634,6 +6622,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6634
6622
|
eventParam.preventDefault()
|
|
6635
6623
|
eventParam.stopPropagation()
|
|
6636
6624
|
}
|
|
6625
|
+
requestInitialWorkspaceDiskUsage()
|
|
6637
6626
|
browserPopoutSurface.show(target)
|
|
6638
6627
|
return
|
|
6639
6628
|
}
|
|
@@ -6709,7 +6698,6 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6709
6698
|
let mode = target.getAttribute("data-mode")
|
|
6710
6699
|
if (mode === "refresh") {
|
|
6711
6700
|
if (targetFrame.src !== target.href) {
|
|
6712
|
-
refreshTerminalSessions(target.href)
|
|
6713
6701
|
targetFrame.src = target.href
|
|
6714
6702
|
}
|
|
6715
6703
|
} else {
|
|
@@ -6720,7 +6708,6 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6720
6708
|
let sub = subUrlOf(target.href, targetFrame.src)
|
|
6721
6709
|
if (!sub) {
|
|
6722
6710
|
if (targetFrame.src !== target.href) {
|
|
6723
|
-
refreshTerminalSessions(target.href)
|
|
6724
6711
|
targetFrame.src = target.href
|
|
6725
6712
|
}
|
|
6726
6713
|
}
|
|
@@ -6736,7 +6723,6 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6736
6723
|
} else {
|
|
6737
6724
|
if (force) {
|
|
6738
6725
|
if (targetFrame.src !== target.href) {
|
|
6739
|
-
refreshTerminalSessions(target.href)
|
|
6740
6726
|
targetFrame.src = target.href
|
|
6741
6727
|
}
|
|
6742
6728
|
}
|
|
@@ -6758,15 +6744,17 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6758
6744
|
} else {
|
|
6759
6745
|
let frame = document.createElement("iframe")
|
|
6760
6746
|
frame.name = target.target
|
|
6761
|
-
refreshTerminalSessions(target.href)
|
|
6762
6747
|
frame.src = target.href
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6748
|
+
iframe_onerror(frame)
|
|
6749
|
+
frame.addEventListener("load", () => {
|
|
6750
|
+
requestInitialWorkspaceDiskUsage()
|
|
6751
|
+
}, { once: true })
|
|
6752
|
+
frame.setAttribute(
|
|
6753
|
+
"allow",
|
|
6754
|
+
"clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; fullscreen *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
|
|
6767
6755
|
)
|
|
6768
6756
|
frame.setAttribute("allowfullscreen", "")
|
|
6769
|
-
|
|
6757
|
+
writeFrameInjectDescriptors(frame, targetInjectDescriptors)
|
|
6770
6758
|
publishBrowserviewInjectTargets([frame], { sync: true })
|
|
6771
6759
|
document.querySelector("main").appendChild(frame)
|
|
6772
6760
|
loaded[target.target] = true
|
|
@@ -6922,14 +6910,17 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6922
6910
|
|
|
6923
6911
|
document.querySelector(".temp-menu").appendChild(item)
|
|
6924
6912
|
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6913
|
+
let frame = document.createElement("iframe")
|
|
6914
|
+
frame.name = id
|
|
6915
|
+
frame.src = item.href
|
|
6916
|
+
iframe_onerror(frame)
|
|
6917
|
+
frame.addEventListener("load", () => {
|
|
6918
|
+
requestInitialWorkspaceDiskUsage()
|
|
6919
|
+
}, { once: true })
|
|
6920
|
+
frame.setAttribute(
|
|
6921
|
+
"allow",
|
|
6922
|
+
"clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; fullscreen *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
|
|
6923
|
+
)
|
|
6933
6924
|
frame.setAttribute("allowfullscreen", "")
|
|
6934
6925
|
document.querySelector("main").appendChild(frame)
|
|
6935
6926
|
|
|
@@ -7998,6 +7989,14 @@ const rerenderMenuSection = (container, html) => {
|
|
|
7998
7989
|
})
|
|
7999
7990
|
}
|
|
8000
7991
|
}
|
|
7992
|
+
const requestInitialWorkspaceDiskUsage = () => {
|
|
7993
|
+
if (initialWorkspaceDiskUsageRequested) {
|
|
7994
|
+
return
|
|
7995
|
+
}
|
|
7996
|
+
initialWorkspaceDiskUsageRequested = true
|
|
7997
|
+
refresh_du()
|
|
7998
|
+
refresh_du("logs")
|
|
7999
|
+
}
|
|
8001
8000
|
|
|
8002
8001
|
const try_dynamic = async (options = {}) => {
|
|
8003
8002
|
let rendered
|
|
@@ -8032,7 +8031,8 @@ const rerenderMenuSection = (container, html) => {
|
|
|
8032
8031
|
} else {
|
|
8033
8032
|
rendered = false
|
|
8034
8033
|
}
|
|
8035
|
-
if (
|
|
8034
|
+
if (rendered) {
|
|
8035
|
+
} else {
|
|
8036
8036
|
if (options.retryOnEmpty === false) {
|
|
8037
8037
|
return
|
|
8038
8038
|
}
|
|
@@ -8089,13 +8089,9 @@ const rerenderMenuSection = (container, html) => {
|
|
|
8089
8089
|
renderSelection()
|
|
8090
8090
|
}
|
|
8091
8091
|
}
|
|
8092
|
-
let init = document.querySelector("[data-init]")
|
|
8093
|
-
if (init) {
|
|
8094
|
-
init.click()
|
|
8095
|
-
}
|
|
8096
8092
|
<% if (type === 'browse') { %>
|
|
8097
8093
|
setTimeout(() => {
|
|
8098
|
-
try_dynamic(
|
|
8094
|
+
try_dynamic()
|
|
8099
8095
|
}, 0)
|
|
8100
8096
|
<% } %>
|
|
8101
8097
|
window.addEventListener('message', (event) => {
|
|
@@ -8155,6 +8151,8 @@ const rerenderMenuSection = (container, html) => {
|
|
|
8155
8151
|
if (event.data.type === 'stream') {
|
|
8156
8152
|
const frameName = resolveFrameName(null, event.source)
|
|
8157
8153
|
updateTabTimestamp(frameName, Date.now())
|
|
8154
|
+
} else if (event.data.type === 'idle') {
|
|
8155
|
+
return
|
|
8158
8156
|
} else if (event.data.type === 'restart') {
|
|
8159
8157
|
<% if (type === 'run') { %>
|
|
8160
8158
|
clearPersistedFrameLinkSelection()
|
|
@@ -8183,8 +8181,6 @@ const rerenderMenuSection = (container, html) => {
|
|
|
8183
8181
|
|
|
8184
8182
|
|
|
8185
8183
|
});
|
|
8186
|
-
refresh_du()
|
|
8187
|
-
refresh_du("logs")
|
|
8188
8184
|
renderSelection({ force: true })
|
|
8189
8185
|
<% if (type === "browse" || type === "files") { %>
|
|
8190
8186
|
const repoStatusCache = new Map()
|
|
@@ -12405,7 +12401,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
12405
12401
|
if (!normalized) {
|
|
12406
12402
|
return false
|
|
12407
12403
|
}
|
|
12408
|
-
refreshTerminalSessions(normalized, workspaceCwd)
|
|
12409
12404
|
pickerRequestId += 1
|
|
12410
12405
|
currentUrl = normalized
|
|
12411
12406
|
setLocation(normalized)
|
package/server/views/d.ejs
CHANGED
|
@@ -292,6 +292,42 @@ body.dark .menu-column .tab-header {
|
|
|
292
292
|
.menu-column .tab-content .tab {
|
|
293
293
|
flex: 0 0 auto;
|
|
294
294
|
}
|
|
295
|
+
.terminal-shell-group {
|
|
296
|
+
display: flex;
|
|
297
|
+
flex-direction: column;
|
|
298
|
+
gap: 4px;
|
|
299
|
+
}
|
|
300
|
+
.terminal-shell-group.is-expanded > .terminal-shell-tab .disclosure-indicator {
|
|
301
|
+
transform: rotate(90deg);
|
|
302
|
+
}
|
|
303
|
+
.terminal-shell-tab.is-loading {
|
|
304
|
+
cursor: progress;
|
|
305
|
+
}
|
|
306
|
+
.terminal-shell-tab.is-loading .disclosure-indicator {
|
|
307
|
+
transform: none;
|
|
308
|
+
}
|
|
309
|
+
.terminal-shell-options {
|
|
310
|
+
display: flex;
|
|
311
|
+
flex-direction: column;
|
|
312
|
+
gap: 4px;
|
|
313
|
+
margin-left: 20px;
|
|
314
|
+
padding-left: 12px;
|
|
315
|
+
border-left: 1px solid rgba(0, 0, 0, 0.08);
|
|
316
|
+
}
|
|
317
|
+
body.dark .terminal-shell-options {
|
|
318
|
+
border-left-color: rgba(255, 255, 255, 0.12);
|
|
319
|
+
}
|
|
320
|
+
.terminal-option-tab {
|
|
321
|
+
padding-left: 12px;
|
|
322
|
+
}
|
|
323
|
+
.terminal-shell-status {
|
|
324
|
+
display: flex;
|
|
325
|
+
align-items: center;
|
|
326
|
+
gap: 8px;
|
|
327
|
+
font-size: 12px;
|
|
328
|
+
opacity: 0.72;
|
|
329
|
+
padding: 8px 10px 10px 12px;
|
|
330
|
+
}
|
|
295
331
|
@media (max-width: 920px) {
|
|
296
332
|
.menu-grid.menu-grid--triptych {
|
|
297
333
|
grid-template-columns: 1fr;
|
|
@@ -524,51 +560,7 @@ body.dark #update-spec {
|
|
|
524
560
|
|
|
525
561
|
<div class='menu-grid <%= primaryColumnCount === 3 ? "menu-grid--triptych" : "" %>'>
|
|
526
562
|
<% if (userTerminal && userTerminal.menu && userTerminal.menu.length) { %>
|
|
527
|
-
|
|
528
|
-
<div class='tab-header'>
|
|
529
|
-
<h3><i class='<%= userTerminal.icon %>'></i> <%= userTerminal.title %></h3>
|
|
530
|
-
</div>
|
|
531
|
-
<% if (userTerminal.subtitle || userTerminal.subtitle_link_href) { %>
|
|
532
|
-
<div class='column-subtitle'>
|
|
533
|
-
<% if (userTerminal.subtitle) { %>
|
|
534
|
-
<span><%= userTerminal.subtitle %></span><% if (userTerminal.subtitle_link_href && userTerminal.subtitle_link_label) { %> <% } %>
|
|
535
|
-
<% } %>
|
|
536
|
-
<% if (userTerminal.subtitle_link_href && userTerminal.subtitle_link_label) { %>
|
|
537
|
-
<a class='column-subtitle-link' href="<%= userTerminal.subtitle_link_href %>" target="_parent"><%= userTerminal.subtitle_link_label %></a>
|
|
538
|
-
<% } %>
|
|
539
|
-
</div>
|
|
540
|
-
<% } %>
|
|
541
|
-
<div class='tab-content'>
|
|
542
|
-
<% userTerminal.menu.forEach((i) => { %>
|
|
543
|
-
<div class='tab' role="button" tabindex="0" data-index="<%= index++ %>" data-target="@<%= i.href %>" data-href="<%= i.href %>">
|
|
544
|
-
<% if (i.image) { %>
|
|
545
|
-
<img src="<%= i.image %>">
|
|
546
|
-
<% } else if (i.icon) { %>
|
|
547
|
-
<i class="img <%= i.icon %>"></i>
|
|
548
|
-
<% } %>
|
|
549
|
-
<div class='tab-copy'>
|
|
550
|
-
<h2 class='tab-title'><%= i.title %></h2>
|
|
551
|
-
<% if (i.subtitle) { %>
|
|
552
|
-
<div class='tab-subtitle subtitle'><%= i.subtitle %></div>
|
|
553
|
-
<% } %>
|
|
554
|
-
</div>
|
|
555
|
-
<div class='tab-actions'>
|
|
556
|
-
<% if (i.link) { %>
|
|
557
|
-
<button class="tab-action-link" type="button" data-doc-link="true" data-href="<%= i.link %>" title="Open docs" data-tippy-content="Open docs" aria-label="Open docs">
|
|
558
|
-
<i class="fa-solid fa-circle-info"></i>
|
|
559
|
-
</button>
|
|
560
|
-
<% } %>
|
|
561
|
-
<button class="ai-perm-link" type="button" data-ai-consent="<%= i.href %>" title="AI permissions" data-tippy-content="AI permissions" aria-label="AI permissions">
|
|
562
|
-
<i class="fa-solid fa-shield-halved"></i>
|
|
563
|
-
</button>
|
|
564
|
-
<div class='disclosure-indicator' aria-hidden="true">
|
|
565
|
-
<i class="fa-solid fa-chevron-right"></i>
|
|
566
|
-
</div>
|
|
567
|
-
</div>
|
|
568
|
-
</div>
|
|
569
|
-
<% }) %>
|
|
570
|
-
</div>
|
|
571
|
-
</div>
|
|
563
|
+
<%- include('./partials/d_terminal_column', { userTerminal }) %>
|
|
572
564
|
<% } %>
|
|
573
565
|
|
|
574
566
|
<% if (cliMenu && cliMenu.menu && cliMenu.menu.length) { %>
|
|
@@ -797,12 +789,17 @@ document.querySelector("#update-spec").addEventListener("click", (e) => {
|
|
|
797
789
|
})
|
|
798
790
|
*/
|
|
799
791
|
let list = []
|
|
800
|
-
|
|
801
|
-
list
|
|
802
|
-
|
|
803
|
-
|
|
792
|
+
const rebuildTabList = () => {
|
|
793
|
+
list = []
|
|
794
|
+
document.querySelectorAll(".tab").forEach((el, index) => {
|
|
795
|
+
el.setAttribute("data-index", String(index))
|
|
796
|
+
list.push({
|
|
797
|
+
index,
|
|
798
|
+
text: el.textContent,
|
|
799
|
+
isTerminalOption: el.hasAttribute("data-terminal-option-tab"),
|
|
800
|
+
})
|
|
804
801
|
})
|
|
805
|
-
}
|
|
802
|
+
}
|
|
806
803
|
const search = (items, value) => {
|
|
807
804
|
let filtered = []
|
|
808
805
|
for(let i=0; i<items.length; i++) {
|
|
@@ -822,8 +819,9 @@ let filteredBtnsCount = 0
|
|
|
822
819
|
|
|
823
820
|
const renderSearch = () => {
|
|
824
821
|
let target = document.querySelector("form input[type=search]")
|
|
822
|
+
const searching = target.value.trim().length > 0
|
|
825
823
|
let result
|
|
826
|
-
if (
|
|
824
|
+
if (!searching) {
|
|
827
825
|
result = list.map((i, index) => {
|
|
828
826
|
return { item: i }
|
|
829
827
|
})
|
|
@@ -835,12 +833,36 @@ const renderSearch = () => {
|
|
|
835
833
|
el.classList.add("hidden")
|
|
836
834
|
el.classList.remove("selected")
|
|
837
835
|
}
|
|
836
|
+
document.querySelectorAll(".terminal-shell-group").forEach((group) => {
|
|
837
|
+
const panel = group.querySelector("[data-terminal-options-panel]")
|
|
838
|
+
if (!panel) {
|
|
839
|
+
return
|
|
840
|
+
}
|
|
841
|
+
if (searching) {
|
|
842
|
+
panel.classList.add("hidden")
|
|
843
|
+
} else {
|
|
844
|
+
panel.classList.toggle("hidden", !group.classList.contains("is-expanded"))
|
|
845
|
+
}
|
|
846
|
+
})
|
|
838
847
|
|
|
839
848
|
filteredCount = result.length
|
|
840
849
|
for(let i=0; i<result.length; i++) {
|
|
841
850
|
let selector = result[i]
|
|
842
851
|
let el = document.querySelector(".tab[data-index='" + selector.item.index + "']")
|
|
843
852
|
el.classList.remove("hidden")
|
|
853
|
+
if (selector.item.isTerminalOption) {
|
|
854
|
+
const group = el.closest(".terminal-shell-group")
|
|
855
|
+
if (group) {
|
|
856
|
+
const shellTab = group.querySelector(".tab[data-terminal-group]")
|
|
857
|
+
if (shellTab) {
|
|
858
|
+
shellTab.classList.remove("hidden")
|
|
859
|
+
}
|
|
860
|
+
const panel = group.querySelector("[data-terminal-options-panel]")
|
|
861
|
+
if (panel) {
|
|
862
|
+
panel.classList.remove("hidden")
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
844
866
|
if (i === selectedIndex) {
|
|
845
867
|
el.classList.add("selected")
|
|
846
868
|
} else {
|
|
@@ -848,6 +870,7 @@ const renderSearch = () => {
|
|
|
848
870
|
}
|
|
849
871
|
}
|
|
850
872
|
}
|
|
873
|
+
rebuildTabList()
|
|
851
874
|
renderSearch()
|
|
852
875
|
const workspaceCwd = (() => {
|
|
853
876
|
const node = document.querySelector(".file-open")
|
|
@@ -915,6 +938,8 @@ const revokeConsentForHref = (href) => {
|
|
|
915
938
|
}
|
|
916
939
|
const START_ICON_HTML = '<i class="fa-solid fa-chevron-right"></i>'
|
|
917
940
|
const STARTING_ICON_HTML = '<i class="fa-solid fa-circle-notch fa-spin"></i>'
|
|
941
|
+
const TERMINAL_GROUP_ICON_HTML = '<i class="fa-solid fa-chevron-right"></i>'
|
|
942
|
+
const TERMINAL_GROUP_LOADING_HTML = '<i class="fa-solid fa-circle-notch fa-spin"></i>'
|
|
918
943
|
const setTabLaunchState = (tab, state) => {
|
|
919
944
|
if (!tab) return
|
|
920
945
|
const indicator = tab.querySelector('.disclosure-indicator')
|
|
@@ -927,6 +952,76 @@ const setTabLaunchState = (tab, state) => {
|
|
|
927
952
|
tab.classList.remove('is-starting')
|
|
928
953
|
}
|
|
929
954
|
}
|
|
955
|
+
const setTerminalGroupState = (tab, state) => {
|
|
956
|
+
if (!tab) {
|
|
957
|
+
return
|
|
958
|
+
}
|
|
959
|
+
const indicator = tab.querySelector('.disclosure-indicator')
|
|
960
|
+
if (indicator) {
|
|
961
|
+
indicator.innerHTML = state === 'loading' ? TERMINAL_GROUP_LOADING_HTML : TERMINAL_GROUP_ICON_HTML
|
|
962
|
+
}
|
|
963
|
+
if (state === 'loading') {
|
|
964
|
+
tab.classList.add('is-loading')
|
|
965
|
+
} else {
|
|
966
|
+
tab.classList.remove('is-loading')
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
const setTerminalGroupExpanded = (group, expanded) => {
|
|
970
|
+
if (!group) {
|
|
971
|
+
return
|
|
972
|
+
}
|
|
973
|
+
group.classList.toggle('is-expanded', expanded)
|
|
974
|
+
const panel = group.querySelector('[data-terminal-options-panel]')
|
|
975
|
+
if (panel) {
|
|
976
|
+
panel.classList.toggle('hidden', !expanded)
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
const toggleTerminalGroup = async (tab) => {
|
|
980
|
+
if (!tab) {
|
|
981
|
+
return
|
|
982
|
+
}
|
|
983
|
+
const group = tab.closest('.terminal-shell-group')
|
|
984
|
+
if (!group) {
|
|
985
|
+
return
|
|
986
|
+
}
|
|
987
|
+
const panel = group.querySelector('[data-terminal-options-panel]')
|
|
988
|
+
if (!panel) {
|
|
989
|
+
return
|
|
990
|
+
}
|
|
991
|
+
if (group.dataset.loaded === '1') {
|
|
992
|
+
setTerminalGroupExpanded(group, !group.classList.contains('is-expanded'))
|
|
993
|
+
rebuildTabList()
|
|
994
|
+
renderSearch()
|
|
995
|
+
return
|
|
996
|
+
}
|
|
997
|
+
if (group.dataset.loading === '1') {
|
|
998
|
+
return
|
|
999
|
+
}
|
|
1000
|
+
const url = tab.dataset.optionsUrl
|
|
1001
|
+
if (!url) {
|
|
1002
|
+
return
|
|
1003
|
+
}
|
|
1004
|
+
group.dataset.loading = '1'
|
|
1005
|
+
setTerminalGroupExpanded(group, true)
|
|
1006
|
+
setTerminalGroupState(tab, 'loading')
|
|
1007
|
+
panel.innerHTML = '<div class="terminal-shell-status"><i class="fa-solid fa-circle-notch fa-spin"></i><span>Loading shell options...</span></div>'
|
|
1008
|
+
try {
|
|
1009
|
+
const html = await fetch(url).then((res) => res.text())
|
|
1010
|
+
if (!html || html.trim().length === 0) {
|
|
1011
|
+
panel.innerHTML = '<div class="terminal-shell-status"><span>No shell options available.</span></div>'
|
|
1012
|
+
} else {
|
|
1013
|
+
panel.innerHTML = html
|
|
1014
|
+
group.dataset.loaded = '1'
|
|
1015
|
+
}
|
|
1016
|
+
} catch (_) {
|
|
1017
|
+
panel.innerHTML = '<div class="terminal-shell-status"><span>Unable to load shell options.</span></div>'
|
|
1018
|
+
} finally {
|
|
1019
|
+
delete group.dataset.loading
|
|
1020
|
+
setTerminalGroupState(tab, 'idle')
|
|
1021
|
+
rebuildTabList()
|
|
1022
|
+
renderSearch()
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
930
1025
|
const pendingLaunchTabs = new Map()
|
|
931
1026
|
const launchTab = (tab) => {
|
|
932
1027
|
if (!tab) {
|
|
@@ -997,6 +1092,14 @@ document.addEventListener("click", (e) => {
|
|
|
997
1092
|
return
|
|
998
1093
|
}
|
|
999
1094
|
|
|
1095
|
+
const groupTab = e.target.closest('.tab[data-terminal-group]')
|
|
1096
|
+
if (groupTab) {
|
|
1097
|
+
e.preventDefault()
|
|
1098
|
+
e.stopPropagation()
|
|
1099
|
+
toggleTerminalGroup(groupTab)
|
|
1100
|
+
return
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1000
1103
|
const tab = e.target.closest('.tab[data-href]')
|
|
1001
1104
|
if (!tab) {
|
|
1002
1105
|
return
|
|
@@ -1018,6 +1121,14 @@ document.addEventListener('keydown', (e) => {
|
|
|
1018
1121
|
return
|
|
1019
1122
|
}
|
|
1020
1123
|
|
|
1124
|
+
const groupTab = target.closest('.tab[data-terminal-group]')
|
|
1125
|
+
if (groupTab && groupTab === target) {
|
|
1126
|
+
e.preventDefault()
|
|
1127
|
+
e.stopPropagation()
|
|
1128
|
+
toggleTerminalGroup(groupTab)
|
|
1129
|
+
return
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1021
1132
|
const tab = target.closest('.tab[data-href]')
|
|
1022
1133
|
if (!tab || tab !== target) {
|
|
1023
1134
|
return
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<div class='menu-container menu-column user-terminal'>
|
|
2
|
+
<div class='tab-header'>
|
|
3
|
+
<h3><i class='<%= userTerminal.icon %>'></i> <%= userTerminal.title %></h3>
|
|
4
|
+
</div>
|
|
5
|
+
<% if (userTerminal.subtitle || userTerminal.subtitle_link_href) { %>
|
|
6
|
+
<div class='column-subtitle'>
|
|
7
|
+
<% if (userTerminal.subtitle) { %>
|
|
8
|
+
<span><%= userTerminal.subtitle %></span><% if (userTerminal.subtitle_link_href && userTerminal.subtitle_link_label) { %> <% } %>
|
|
9
|
+
<% } %>
|
|
10
|
+
<% if (userTerminal.subtitle_link_href && userTerminal.subtitle_link_label) { %>
|
|
11
|
+
<a class='column-subtitle-link' href="<%= userTerminal.subtitle_link_href %>" target="_parent"><%= userTerminal.subtitle_link_label %></a>
|
|
12
|
+
<% } %>
|
|
13
|
+
</div>
|
|
14
|
+
<% } %>
|
|
15
|
+
<div class='tab-content'>
|
|
16
|
+
<% userTerminal.menu.forEach((i) => { %>
|
|
17
|
+
<div class='terminal-shell-group' data-terminal-shell-group="<%= i.shell_key %>">
|
|
18
|
+
<div class='tab terminal-shell-tab' role="button" tabindex="0" data-terminal-group="<%= i.shell_key %>" data-options-url="<%= i.options_url %>">
|
|
19
|
+
<% if (i.image) { %>
|
|
20
|
+
<img src="<%= i.image %>">
|
|
21
|
+
<% } else if (i.icon) { %>
|
|
22
|
+
<i class="img <%= i.icon %>"></i>
|
|
23
|
+
<% } %>
|
|
24
|
+
<div class='tab-copy'>
|
|
25
|
+
<h2 class='tab-title'><%= i.title %></h2>
|
|
26
|
+
<% if (i.subtitle) { %>
|
|
27
|
+
<div class='tab-subtitle subtitle'><%= i.subtitle %></div>
|
|
28
|
+
<% } %>
|
|
29
|
+
</div>
|
|
30
|
+
<div class='tab-actions'>
|
|
31
|
+
<div class='disclosure-indicator' aria-hidden="true">
|
|
32
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div class='terminal-shell-options hidden' data-terminal-options-panel="<%= i.shell_key %>"></div>
|
|
37
|
+
</div>
|
|
38
|
+
<% }) %>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<% options.forEach((i) => { %>
|
|
2
|
+
<div class='tab terminal-option-tab' role="button" tabindex="0" data-terminal-option-tab="true" data-target="@<%= i.href %>" data-href="<%= i.href %>">
|
|
3
|
+
<% if (i.image) { %>
|
|
4
|
+
<img src="<%= i.image %>">
|
|
5
|
+
<% } else if (i.icon) { %>
|
|
6
|
+
<i class="img <%= i.icon %>"></i>
|
|
7
|
+
<% } %>
|
|
8
|
+
<div class='tab-copy'>
|
|
9
|
+
<h2 class='tab-title'><%= i.title %></h2>
|
|
10
|
+
<% if (i.subtitle) { %>
|
|
11
|
+
<div class='tab-subtitle subtitle'><%= i.subtitle %></div>
|
|
12
|
+
<% } %>
|
|
13
|
+
</div>
|
|
14
|
+
<div class='tab-actions'>
|
|
15
|
+
<% if (i.link) { %>
|
|
16
|
+
<button class="tab-action-link" type="button" data-doc-link="true" data-href="<%= i.link %>" title="Open docs" data-tippy-content="Open docs" aria-label="Open docs">
|
|
17
|
+
<i class="fa-solid fa-circle-info"></i>
|
|
18
|
+
</button>
|
|
19
|
+
<% } %>
|
|
20
|
+
<button class="ai-perm-link" type="button" data-ai-consent="<%= i.href %>" title="AI permissions" data-tippy-content="AI permissions" aria-label="AI permissions">
|
|
21
|
+
<i class="fa-solid fa-shield-halved"></i>
|
|
22
|
+
</button>
|
|
23
|
+
<div class='disclosure-indicator' aria-hidden="true">
|
|
24
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<% }) %>
|
|
@@ -552,6 +552,67 @@ const createRunControls = () => {
|
|
|
552
552
|
}
|
|
553
553
|
return { set }
|
|
554
554
|
}
|
|
555
|
+
const PLUGIN_TERMINAL_IDLE_WINDOW_MS = 1200
|
|
556
|
+
const createPluginTerminalDiscoveryRefresher = (context = {}) => {
|
|
557
|
+
const enabled = (() => {
|
|
558
|
+
try {
|
|
559
|
+
return window.location.pathname.startsWith("/run/plugin/")
|
|
560
|
+
} catch (_) {
|
|
561
|
+
return false
|
|
562
|
+
}
|
|
563
|
+
})()
|
|
564
|
+
let idleTimer = null
|
|
565
|
+
let armed = false
|
|
566
|
+
let sawStream = false
|
|
567
|
+
const clearIdleTimer = () => {
|
|
568
|
+
if (idleTimer) {
|
|
569
|
+
clearTimeout(idleTimer)
|
|
570
|
+
idleTimer = null
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const refresh = () => {
|
|
574
|
+
if (!enabled || !armed || !sawStream) {
|
|
575
|
+
return
|
|
576
|
+
}
|
|
577
|
+
armed = false
|
|
578
|
+
sawStream = false
|
|
579
|
+
try {
|
|
580
|
+
const discovery = window.PinokioTerminalsDiscovery
|
|
581
|
+
if (!discovery || typeof discovery.refreshTerminalSessions !== "function") {
|
|
582
|
+
return
|
|
583
|
+
}
|
|
584
|
+
discovery.refreshTerminalSessions(window.location.href, context.cwd || "", {
|
|
585
|
+
retryDelays: []
|
|
586
|
+
})
|
|
587
|
+
} catch (_) {}
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
arm(meaningful) {
|
|
591
|
+
if (!enabled || !meaningful) {
|
|
592
|
+
return
|
|
593
|
+
}
|
|
594
|
+
armed = true
|
|
595
|
+
sawStream = false
|
|
596
|
+
clearIdleTimer()
|
|
597
|
+
},
|
|
598
|
+
markActivity() {
|
|
599
|
+
if (!enabled || !armed) {
|
|
600
|
+
return
|
|
601
|
+
}
|
|
602
|
+
sawStream = true
|
|
603
|
+
clearIdleTimer()
|
|
604
|
+
idleTimer = setTimeout(() => {
|
|
605
|
+
idleTimer = null
|
|
606
|
+
refresh()
|
|
607
|
+
}, PLUGIN_TERMINAL_IDLE_WINDOW_MS)
|
|
608
|
+
},
|
|
609
|
+
clear() {
|
|
610
|
+
armed = false
|
|
611
|
+
sawStream = false
|
|
612
|
+
clearIdleTimer()
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
555
616
|
const createAiConsentManager = (context = {}) => {
|
|
556
617
|
const storage = getSafeLocalStorage()
|
|
557
618
|
const storageSupported = !!storage
|
|
@@ -848,6 +909,9 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
848
909
|
uri: scriptUri || ("~" + location.pathname)
|
|
849
910
|
})
|
|
850
911
|
const runControls = createRunControls()
|
|
912
|
+
const pluginTerminalDiscoveryRefresher = createPluginTerminalDiscoveryRefresher({
|
|
913
|
+
cwd: scriptCwd
|
|
914
|
+
})
|
|
851
915
|
consentManager.showChipIfRemembered()
|
|
852
916
|
class RPC {
|
|
853
917
|
constructor() {
|
|
@@ -923,16 +987,17 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
923
987
|
}
|
|
924
988
|
}
|
|
925
989
|
notifyLineSubmitted(line, meta = {}) {
|
|
926
|
-
if (this.inputTracker) {
|
|
927
|
-
this.inputTracker.submit(line, meta)
|
|
928
|
-
return
|
|
929
|
-
}
|
|
930
990
|
const safeLine = sanitizePreviewLine(line || "")
|
|
931
991
|
const preview = safeLine.trim()
|
|
932
992
|
const limit = 200
|
|
933
993
|
const truncated = preview.length > limit ? preview.slice(0, limit) + "..." : preview
|
|
934
994
|
const hadLineBreak = Boolean(meta && meta.hadLineBreak)
|
|
935
995
|
const meaningful = truncated.length > 0 || hadLineBreak
|
|
996
|
+
pluginTerminalDiscoveryRefresher.arm(meaningful)
|
|
997
|
+
if (this.inputTracker) {
|
|
998
|
+
this.inputTracker.submit(line, meta)
|
|
999
|
+
return
|
|
1000
|
+
}
|
|
936
1001
|
postMessageToAncestors({
|
|
937
1002
|
type: "terminal-input",
|
|
938
1003
|
frame: window.name || null,
|
|
@@ -1096,6 +1161,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1096
1161
|
}
|
|
1097
1162
|
} else if (packet.type === "stream") {
|
|
1098
1163
|
refreshParent(packet)
|
|
1164
|
+
pluginTerminalDiscoveryRefresher.markActivity()
|
|
1099
1165
|
// set the current shell id
|
|
1100
1166
|
const previousShellId = shell_id
|
|
1101
1167
|
if (packet.data.id) {
|
|
@@ -1128,6 +1194,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1128
1194
|
runControls.set("running")
|
|
1129
1195
|
} else if (packet.type === 'disconnect') {
|
|
1130
1196
|
refreshParent(packet)
|
|
1197
|
+
pluginTerminalDiscoveryRefresher.clear()
|
|
1131
1198
|
reloadMemory()
|
|
1132
1199
|
this.term.write("\r\nDisconnected...\r\n")
|
|
1133
1200
|
document.querySelector("#status-window").innerHTML = "<b>Ready</b>"
|
|
@@ -1423,6 +1490,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1423
1490
|
n.Noty(payload)
|
|
1424
1491
|
}
|
|
1425
1492
|
} else if (packet.type === "restart") {
|
|
1493
|
+
pluginTerminalDiscoveryRefresher.clear()
|
|
1426
1494
|
try {
|
|
1427
1495
|
if (window.parent && window.parent !== window && typeof window.parent.postMessage === "function") {
|
|
1428
1496
|
window.parent.postMessage(packet, "*")
|
|
@@ -1469,6 +1537,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1469
1537
|
text: `${packet.data}`,
|
|
1470
1538
|
})
|
|
1471
1539
|
} else if (packet.type === "error") {
|
|
1540
|
+
pluginTerminalDiscoveryRefresher.clear()
|
|
1472
1541
|
runControls.set("idle")
|
|
1473
1542
|
|
|
1474
1543
|
|
|
@@ -1547,6 +1616,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1547
1616
|
type: 'success'
|
|
1548
1617
|
})
|
|
1549
1618
|
*/
|
|
1619
|
+
pluginTerminalDiscoveryRefresher.clear()
|
|
1550
1620
|
runControls.set("idle")
|
|
1551
1621
|
}, 0)
|
|
1552
1622
|
//this.socket.close()
|
|
@@ -1575,6 +1645,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1575
1645
|
this.mode = (mode ? mode : "run")
|
|
1576
1646
|
const allowed = aiConsentRequired ? await consentManager.ensureAllowed() : true
|
|
1577
1647
|
if (!allowed) {
|
|
1648
|
+
pluginTerminalDiscoveryRefresher.clear()
|
|
1578
1649
|
runControls.set("idle")
|
|
1579
1650
|
n.Noty({
|
|
1580
1651
|
text: "Run canceled; AI agents remain blocked for this folder.",
|
|
@@ -1601,6 +1672,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1601
1672
|
await this.start(mode)
|
|
1602
1673
|
return true
|
|
1603
1674
|
} catch (error) {
|
|
1675
|
+
pluginTerminalDiscoveryRefresher.clear()
|
|
1604
1676
|
runControls.set("idle")
|
|
1605
1677
|
const message = error && error.message ? error.message : "Failed to start"
|
|
1606
1678
|
n.Noty({
|