pinokiod 7.5.38 → 7.5.39
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/package.json
CHANGED
package/server/views/index.ejs
CHANGED
|
@@ -865,7 +865,7 @@ body.dark .menu-btns .toggle-star.is-starred {
|
|
|
865
865
|
padding: 0;
|
|
866
866
|
border: 1px solid rgba(15, 23, 42, 0.14);
|
|
867
867
|
border-radius: 8px;
|
|
868
|
-
background:
|
|
868
|
+
background: var(--home-page-nav-bg, #ffffff);
|
|
869
869
|
color: rgba(15, 23, 42, 0.94);
|
|
870
870
|
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.24);
|
|
871
871
|
}
|
|
@@ -886,7 +886,7 @@ body.dark .menu-btns .toggle-star.is-starred {
|
|
|
886
886
|
}
|
|
887
887
|
body.dark .home-actions-dialog {
|
|
888
888
|
border-color: rgba(255, 255, 255, 0.12);
|
|
889
|
-
background:
|
|
889
|
+
background: var(--home-page-nav-bg, #1b1c1d);
|
|
890
890
|
color: rgba(248, 250, 252, 0.94);
|
|
891
891
|
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.55);
|
|
892
892
|
}
|
|
@@ -1125,6 +1125,35 @@ body.dark .home-mode-command {
|
|
|
1125
1125
|
font-size: 10px;
|
|
1126
1126
|
opacity: 0.6;
|
|
1127
1127
|
}
|
|
1128
|
+
.home-run-command.has-block-label,
|
|
1129
|
+
.home-run-menu-summary.has-block-label {
|
|
1130
|
+
min-height: 52px;
|
|
1131
|
+
padding-top: 8px;
|
|
1132
|
+
padding-bottom: 8px;
|
|
1133
|
+
}
|
|
1134
|
+
.home-run-menu-label {
|
|
1135
|
+
display: flex;
|
|
1136
|
+
align-items: center;
|
|
1137
|
+
gap: 9px;
|
|
1138
|
+
}
|
|
1139
|
+
.home-run-menu-label.has-block-content {
|
|
1140
|
+
align-items: flex-start;
|
|
1141
|
+
white-space: normal;
|
|
1142
|
+
line-height: 1.18;
|
|
1143
|
+
}
|
|
1144
|
+
.home-run-menu-label.has-block-content > i:first-child {
|
|
1145
|
+
margin-top: 1px;
|
|
1146
|
+
}
|
|
1147
|
+
.home-run-menu-label.has-block-content > div {
|
|
1148
|
+
min-width: 0;
|
|
1149
|
+
display: flex;
|
|
1150
|
+
flex-direction: column;
|
|
1151
|
+
gap: 2px;
|
|
1152
|
+
}
|
|
1153
|
+
.home-run-menu-label.has-block-content > div > strong,
|
|
1154
|
+
.home-run-menu-label.has-block-content > div > div {
|
|
1155
|
+
line-height: 1.18;
|
|
1156
|
+
}
|
|
1128
1157
|
.home-run-stop {
|
|
1129
1158
|
width: 38px;
|
|
1130
1159
|
min-height: 38px;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
<%
|
|
2
|
+
const homeActionsAppId = item && (item.uri || item.path || item.name) ? String(item.uri || item.path || item.name) : ""
|
|
3
|
+
const homeActionsSettingsHref = homeActionsAppId
|
|
4
|
+
? `/v/${encodeURIComponent(homeActionsAppId)}?${new URLSearchParams({
|
|
5
|
+
pinokio_home_select: JSON.stringify({
|
|
6
|
+
target: "env-settings",
|
|
7
|
+
hrefAttr: `/env/api/${homeActionsAppId}?host=app`,
|
|
8
|
+
textValue: "Settings"
|
|
9
|
+
})
|
|
10
|
+
}).toString()}`
|
|
11
|
+
: ""
|
|
12
|
+
%>
|
|
1
13
|
<dialog id='<%= id %>' class='home-actions-dialog' aria-labelledby='<%= id %>-title'>
|
|
2
14
|
<div class='home-actions-shell'>
|
|
3
15
|
<div class='home-actions-header'>
|
|
@@ -49,6 +61,12 @@
|
|
|
49
61
|
<span class='home-actions-count'><%= item.terminal_online_count %> online</span>
|
|
50
62
|
<% } %>
|
|
51
63
|
</button>
|
|
64
|
+
<% if (homeActionsSettingsHref) { %>
|
|
65
|
+
<a class='home-mode-command' href='<%= homeActionsSettingsHref %>'>
|
|
66
|
+
<i class='fa-solid fa-gear' aria-hidden='true'></i>
|
|
67
|
+
<span>Settings</span>
|
|
68
|
+
</a>
|
|
69
|
+
<% } %>
|
|
52
70
|
<button type='button' class='home-mode-command' data-filepath='<%= item.filepath %>'>
|
|
53
71
|
<i class='fa-solid fa-folder-open' aria-hidden='true'></i>
|
|
54
72
|
<span>Open in file explorer</span>
|
|
@@ -9,6 +9,7 @@ const stripHtml = (value) => String(value ?? "")
|
|
|
9
9
|
.replace(/<[^>]*>/g, " ")
|
|
10
10
|
.replace(/\s+/g, " ")
|
|
11
11
|
.trim()
|
|
12
|
+
const hasBlockContent = (value) => /<\s*(div|p|section|article|ul|ol|li|h[1-6])(?:\s|>)/i.test(String(value ?? ""))
|
|
12
13
|
const buildHomeSelectionPayload = (menuItem, index) => {
|
|
13
14
|
const dataFilepath = (menuItem.fs || menuItem.command) ? (menuItem.href || "") : ""
|
|
14
15
|
const payload = {
|
|
@@ -49,9 +50,10 @@ const buildHomeSelectionUrl = (menuItem, index) => {
|
|
|
49
50
|
%>
|
|
50
51
|
<% menu.forEach((item, index) => { %>
|
|
51
52
|
<% if (item && item.menu) { %>
|
|
53
|
+
<% const itemBtnHasBlock = hasBlockContent(item.btn) %>
|
|
52
54
|
<details class='home-run-menu-group' open>
|
|
53
|
-
<summary class='home-run-menu-summary'>
|
|
54
|
-
<
|
|
55
|
+
<summary class='home-run-menu-summary <%= itemBtnHasBlock ? "has-block-label" : "" %>'>
|
|
56
|
+
<div class='home-run-menu-label <%= itemBtnHasBlock ? "has-block-content" : "" %>'><%- item.btn %></div>
|
|
55
57
|
<i class='fa-solid fa-chevron-down' aria-hidden='true'></i>
|
|
56
58
|
</summary>
|
|
57
59
|
<div class='home-run-menu-children'>
|
|
@@ -60,13 +62,14 @@ const buildHomeSelectionUrl = (menuItem, index) => {
|
|
|
60
62
|
</details>
|
|
61
63
|
<% } else if (item && item.btn) { %>
|
|
62
64
|
<% const openUrl = buildHomeSelectionUrl(item, index) %>
|
|
65
|
+
<% const itemBtnHasBlock = hasBlockContent(item.btn) %>
|
|
63
66
|
<div class='home-run-menu-row <%= item.running ? "is-running" : "" %>'>
|
|
64
67
|
<a
|
|
65
|
-
class='home-run-command'
|
|
68
|
+
class='home-run-command <%= itemBtnHasBlock ? "has-block-label" : "" %>'
|
|
66
69
|
href='<%= openUrl %>'
|
|
67
70
|
<% if (item.confirm) { %>data-confirm="<%- escapeAttr(item.confirm) %>"<% } %>
|
|
68
71
|
>
|
|
69
|
-
<
|
|
72
|
+
<div class='home-run-menu-label <%= itemBtnHasBlock ? "has-block-content" : "" %>'><%- item.btn %></div>
|
|
70
73
|
<% if (item.running) { %><span class='home-run-menu-status'>Running</span><% } %>
|
|
71
74
|
<i class='fa-solid fa-chevron-right' aria-hidden='true'></i>
|
|
72
75
|
</a>
|
|
@@ -2,6 +2,7 @@ const assert = require("node:assert/strict")
|
|
|
2
2
|
const fs = require("node:fs/promises")
|
|
3
3
|
const path = require("node:path")
|
|
4
4
|
const test = require("node:test")
|
|
5
|
+
const ejs = require("ejs")
|
|
5
6
|
|
|
6
7
|
const root = path.resolve(__dirname, "..")
|
|
7
8
|
|
|
@@ -370,6 +371,11 @@ test("static guard: open without launching is not wired to launch requirement st
|
|
|
370
371
|
assert.match(homeActionModal, /Open without launching/)
|
|
371
372
|
assert.match(homeActionModal, /class='home-mode-command home-browse' data-src='<%= item\.view_url %>'/)
|
|
372
373
|
assert.match(homeActionModal, /Open dev mode/)
|
|
374
|
+
assert.match(homeActionModal, /const homeActionsAppId = item && \(item\.uri \|\| item\.path \|\| item\.name\)/)
|
|
375
|
+
assert.match(homeActionModal, /pinokio_home_select: JSON\.stringify/)
|
|
376
|
+
assert.match(homeActionModal, /target: "env-settings"/)
|
|
377
|
+
assert.match(homeActionModal, /href='<%= homeActionsSettingsHref %>'/)
|
|
378
|
+
assert.match(homeActionModal, /Settings/)
|
|
373
379
|
assert.doesNotMatch(homeActionModal, /home-actions-tabs|home-actions-tab|role='tab'|role='tabpanel'/)
|
|
374
380
|
assert.doesNotMatch(homeActionModal, /Open files mode/)
|
|
375
381
|
assert.doesNotMatch(homeActionModal, /launch.requirements|data-launch-requirements|frame-link/)
|
|
@@ -378,6 +384,66 @@ test("static guard: open without launching is not wired to launch requirement st
|
|
|
378
384
|
assert.doesNotMatch(statusClient, /saved requirement/)
|
|
379
385
|
})
|
|
380
386
|
|
|
387
|
+
test("home actions modal renders an app settings link", async () => {
|
|
388
|
+
const html = await ejs.renderFile(path.resolve(root, "server/views/partials/home_action_modal.ejs"), {
|
|
389
|
+
id: "home-actions-test",
|
|
390
|
+
item: {
|
|
391
|
+
icon: "",
|
|
392
|
+
name: "Test App",
|
|
393
|
+
filepath: "/tmp/pinokio/api/test app.git",
|
|
394
|
+
uri: "test app.git",
|
|
395
|
+
menu: [],
|
|
396
|
+
view_url: "/v/test%20app.git",
|
|
397
|
+
dev_url: "/p/test%20app.git/dev",
|
|
398
|
+
url: "/p/test%20app.git",
|
|
399
|
+
terminal_online_count: 0,
|
|
400
|
+
running: false
|
|
401
|
+
}
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
const hrefMatch = html.match(/href='([^']+)'/)
|
|
405
|
+
assert.ok(hrefMatch)
|
|
406
|
+
const href = new URL(hrefMatch[1], "http://127.0.0.1")
|
|
407
|
+
assert.equal(href.pathname, "/v/test%20app.git")
|
|
408
|
+
const payload = JSON.parse(href.searchParams.get("pinokio_home_select"))
|
|
409
|
+
assert.deepEqual(payload, {
|
|
410
|
+
target: "env-settings",
|
|
411
|
+
hrefAttr: "/env/api/test app.git?host=app",
|
|
412
|
+
textValue: "Settings"
|
|
413
|
+
})
|
|
414
|
+
assert.match(html, />\s*Settings\s*</)
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
test("home actions drawer background follows the home page surface", async () => {
|
|
418
|
+
const homeView = await fs.readFile(path.resolve(root, "server/views/index.ejs"), "utf8")
|
|
419
|
+
|
|
420
|
+
assert.match(homeView, /body\.is-home \{[\s\S]*--home-page-nav-bg: #ffffff;[\s\S]*background: #ffffff;/)
|
|
421
|
+
assert.match(homeView, /body\.dark\.is-home \{[\s\S]*--home-page-nav-bg: #1b1c1d;[\s\S]*background: #1b1c1d;/)
|
|
422
|
+
assert.match(homeView, /\.home-actions-dialog \{[\s\S]*background: var\(--home-page-nav-bg, #ffffff\);/)
|
|
423
|
+
assert.match(homeView, /body\.dark \.home-actions-dialog \{[\s\S]*background: var\(--home-page-nav-bg, #1b1c1d\);/)
|
|
424
|
+
assert.doesNotMatch(homeView, /body\.dark \.home-actions-dialog \{[\s\S]*background: rgba\(18, 20, 25, 0\.98\);/)
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
test("home run menu gives block labels valid padded layout", async () => {
|
|
428
|
+
const homeView = await fs.readFile(path.resolve(root, "server/views/index.ejs"), "utf8")
|
|
429
|
+
const html = await ejs.renderFile(path.resolve(root, "server/views/partials/home_run_menu.ejs"), {
|
|
430
|
+
app: {
|
|
431
|
+
view_url: "/v/test-app"
|
|
432
|
+
},
|
|
433
|
+
menu: [{
|
|
434
|
+
icon: "fa-regular fa-circle-xmark",
|
|
435
|
+
btn: '<i class="fa-regular fa-circle-xmark"></i><div><strong>Reset</strong><div>Revert to pre-install state</div></div>',
|
|
436
|
+
href: "reset.js"
|
|
437
|
+
}]
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
assert.match(html, /class='home-run-command has-block-label'/)
|
|
441
|
+
assert.match(html, /<div class='home-run-menu-label has-block-content'>/)
|
|
442
|
+
assert.doesNotMatch(html, /<span class='home-run-menu-label/)
|
|
443
|
+
assert.match(homeView, /\.home-run-command\.has-block-label,[\s\S]*\.home-run-menu-summary\.has-block-label \{[\s\S]*padding-top: 8px;[\s\S]*padding-bottom: 8px;/)
|
|
444
|
+
assert.match(homeView, /\.home-run-menu-label\.has-block-content \{[\s\S]*white-space: normal;/)
|
|
445
|
+
})
|
|
446
|
+
|
|
381
447
|
test("static guard: open without launching disables page-load script frame selection", async () => {
|
|
382
448
|
const appView = await fs.readFile(path.resolve(root, "server/views/app.ejs"), "utf8")
|
|
383
449
|
|
|
@@ -949,6 +949,81 @@ test('Windows cmd suppresses echo only for explicit conda shell.run commands', (
|
|
|
949
949
|
}, command), { command })
|
|
950
950
|
})
|
|
951
951
|
|
|
952
|
+
test('Windows cmd array shell.run marks only the conda launch quiet', async () => {
|
|
953
|
+
const { context, root } = createContext('win32')
|
|
954
|
+
const records = []
|
|
955
|
+
const originalPrompt = Shell.prototype.prompt
|
|
956
|
+
const originalExec = Shell.prototype.exec
|
|
957
|
+
Shell.prototype.prompt = async () => 'PINOKIO_PROMPT'
|
|
958
|
+
Shell.prototype.exec = async function (params) {
|
|
959
|
+
this.platform = 'win32'
|
|
960
|
+
this.shell = 'cmd.exe'
|
|
961
|
+
const originalParams = {
|
|
962
|
+
input: params && params.input,
|
|
963
|
+
message: params && params.message,
|
|
964
|
+
}
|
|
965
|
+
const activatedParams = await this.activate(params)
|
|
966
|
+
const command = this.build(activatedParams)
|
|
967
|
+
records.push({
|
|
968
|
+
message: originalParams.message,
|
|
969
|
+
prepared: this.prepareCommandExecution(originalParams, command),
|
|
970
|
+
})
|
|
971
|
+
return command
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
try {
|
|
975
|
+
const kernel = {
|
|
976
|
+
homedir: root,
|
|
977
|
+
platform: 'win32',
|
|
978
|
+
bracketedPasteSupport: { 'cmd.exe': false },
|
|
979
|
+
envs: {},
|
|
980
|
+
exists: async () => false,
|
|
981
|
+
which: () => null,
|
|
982
|
+
path: (...parts) => path.join(root, ...parts),
|
|
983
|
+
bin: {
|
|
984
|
+
envs: (env = {}) => env,
|
|
985
|
+
path: (...parts) => path.join(root, 'bin', ...parts),
|
|
986
|
+
activationCommands: () => [],
|
|
987
|
+
},
|
|
988
|
+
api: {
|
|
989
|
+
resolvePath: (_cwd, target) => target,
|
|
990
|
+
running: {},
|
|
991
|
+
},
|
|
992
|
+
git: {
|
|
993
|
+
repos: async () => [],
|
|
994
|
+
restoreNewReposForActiveSnapshot: async () => {},
|
|
995
|
+
},
|
|
996
|
+
template: {
|
|
997
|
+
render: (value) => value,
|
|
998
|
+
},
|
|
999
|
+
}
|
|
1000
|
+
kernel.shell = new Shells(kernel)
|
|
1001
|
+
|
|
1002
|
+
await kernel.shell.run({
|
|
1003
|
+
path: context.appPath,
|
|
1004
|
+
message: [
|
|
1005
|
+
'echo before',
|
|
1006
|
+
'conda install -y -c conda-forge ffmpeg',
|
|
1007
|
+
'python main.py',
|
|
1008
|
+
],
|
|
1009
|
+
}, {}, () => {})
|
|
1010
|
+
|
|
1011
|
+
assert.deepEqual(records.map((record) => record.message), [
|
|
1012
|
+
'echo before',
|
|
1013
|
+
'conda install -y -c conda-forge ffmpeg',
|
|
1014
|
+
'python main.py',
|
|
1015
|
+
])
|
|
1016
|
+
assert.deepEqual(records.map((record) => !!record.prepared.quietCmd), [
|
|
1017
|
+
false,
|
|
1018
|
+
true,
|
|
1019
|
+
false,
|
|
1020
|
+
])
|
|
1021
|
+
} finally {
|
|
1022
|
+
Shell.prototype.prompt = originalPrompt
|
|
1023
|
+
Shell.prototype.exec = originalExec
|
|
1024
|
+
}
|
|
1025
|
+
})
|
|
1026
|
+
|
|
952
1027
|
test('rewrite warning is scoped to each user-visible shell flow', async () => {
|
|
953
1028
|
CondaRuntimeGuard.resetNoticeSessionsForTest()
|
|
954
1029
|
const { context, events, root } = createContext()
|