pinokiod 7.3.14 → 7.4.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/api/index.js +111 -15
- package/kernel/api/script/index.js +10 -0
- package/kernel/autolaunch.js +111 -0
- package/kernel/environment.js +89 -1
- package/kernel/git.js +9 -19
- package/kernel/index.js +142 -43
- package/kernel/launch_requirements.js +1115 -0
- package/kernel/ready.js +231 -0
- package/kernel/script.js +16 -0
- package/kernel/shells.js +9 -1
- package/kernel/workspace_status.js +111 -45
- package/package.json +1 -1
- package/server/autolaunch.js +725 -0
- package/server/index.js +244 -411
- package/server/public/logs.js +15 -1
- package/server/public/style.css +99 -31
- package/server/views/app.ejs +263 -159
- package/server/views/autolaunch.ejs +363 -75
- package/server/views/index.ejs +550 -26
- package/server/views/logs.ejs +14 -12
- package/server/views/partials/app_autolaunch_dependency_events.ejs +99 -0
- package/server/views/partials/app_autolaunch_dependency_save.ejs +10 -0
- package/server/views/partials/app_autolaunch_dependency_styles.ejs +357 -0
- package/server/views/partials/app_autolaunch_modal_helpers.ejs +347 -0
- package/server/views/partials/autolaunch_dependency_helpers.ejs +204 -0
- package/server/views/partials/autolaunch_dependency_save.ejs +13 -0
- package/server/views/partials/autolaunch_dependency_styles.ejs +347 -0
- package/server/views/partials/home_action_modal.ejs +4 -1
- package/server/views/partials/launch_requirements_status_client.ejs +271 -0
- package/server/views/partials/launch_requirements_status_styles.ejs +171 -0
- package/server/views/partials/launch_settings_dependency_save_factory.ejs +45 -0
- package/server/views/partials/launch_settings_dependency_script_loader_factory.ejs +25 -0
- package/server/views/terminal.ejs +196 -2
- package/test/home-autolaunch-live-ui.test.js +455 -0
- package/test/launch-requirements-browser.test.js +579 -0
- package/test/launch-requirements-contract-coverage.test.js +627 -0
- package/test/launch-requirements-real-browser.js +625 -0
- package/test/launch-requirements-status-client.test.js +132 -0
- package/test/launch-requirements.test.js +1806 -0
- package/test/launch-settings-ui.test.js +370 -0
- package/test/ready-state.test.js +49 -0
- package/test/server-autolaunch.test.js +1052 -0
- package/test/startup-git-index-benchmark.js +409 -0
- package/test/startup-git-index-browser.js +320 -0
- package/test/startup-git-index-performance.test.js +380 -0
- package/test/startup-git-index-refactor.test.js +450 -0
- package/test/startup-git-index-route.test.js +588 -0
- package/test/universal-launcher.smoke.spec.js +10 -9
- package/test/workspace-gitignore-benchmark.js +815 -0
- package/test/workspace-gitignore-path-scoped.test.js +256 -0
- package/spec/INSTRUCTION_SYNC.md +0 -432
package/server/views/app.ejs
CHANGED
|
@@ -603,6 +603,7 @@ body.dark .browserview-network-status {
|
|
|
603
603
|
color: rgba(248, 250, 252, 0.9);
|
|
604
604
|
background: var(--app-page-canvas-bg, #1b1c1d);
|
|
605
605
|
}
|
|
606
|
+
<%- include('./partials/launch_requirements_status_styles') %>
|
|
606
607
|
.browserview-network-status-inner {
|
|
607
608
|
display: flex;
|
|
608
609
|
flex-direction: column;
|
|
@@ -3685,6 +3686,11 @@ body.dark .disk-usage {
|
|
|
3685
3686
|
text-overflow: ellipsis;
|
|
3686
3687
|
color: var(--pinokio-sidebar-tab-muted);
|
|
3687
3688
|
}
|
|
3689
|
+
.app-autolaunch-label > span {
|
|
3690
|
+
min-width: 0;
|
|
3691
|
+
overflow: hidden;
|
|
3692
|
+
text-overflow: ellipsis;
|
|
3693
|
+
}
|
|
3688
3694
|
.app-autolaunch-label i {
|
|
3689
3695
|
width: 16px;
|
|
3690
3696
|
flex: 0 0 16px;
|
|
@@ -3702,6 +3708,7 @@ body.dark .disk-usage {
|
|
|
3702
3708
|
font-size: 11px;
|
|
3703
3709
|
font-weight: 700;
|
|
3704
3710
|
letter-spacing: 0.02em;
|
|
3711
|
+
white-space: nowrap;
|
|
3705
3712
|
}
|
|
3706
3713
|
.app-autolaunch-row[data-enabled="true"] .app-autolaunch-status {
|
|
3707
3714
|
color: rgba(22, 101, 52, 0.95);
|
|
@@ -3793,6 +3800,21 @@ body.dark .app-autolaunch-empty {
|
|
|
3793
3800
|
gap: 8px;
|
|
3794
3801
|
flex: 0 0 auto;
|
|
3795
3802
|
}
|
|
3803
|
+
.app-autolaunch-switch-group {
|
|
3804
|
+
display: inline-flex;
|
|
3805
|
+
align-items: center;
|
|
3806
|
+
gap: 7px;
|
|
3807
|
+
}
|
|
3808
|
+
.app-autolaunch-switch-caption {
|
|
3809
|
+
color: rgba(100, 116, 139, 0.95);
|
|
3810
|
+
font-size: 11px;
|
|
3811
|
+
font-weight: 700;
|
|
3812
|
+
line-height: 1;
|
|
3813
|
+
white-space: nowrap;
|
|
3814
|
+
}
|
|
3815
|
+
body.dark .app-autolaunch-switch-caption {
|
|
3816
|
+
color: rgba(148, 163, 184, 0.95);
|
|
3817
|
+
}
|
|
3796
3818
|
.app-autolaunch-close {
|
|
3797
3819
|
display: inline-flex;
|
|
3798
3820
|
align-items: center;
|
|
@@ -3914,10 +3936,42 @@ body.dark .app-autolaunch-section-label {
|
|
|
3914
3936
|
.app-autolaunch-script-option > div {
|
|
3915
3937
|
min-width: 0;
|
|
3916
3938
|
}
|
|
3939
|
+
.app-autolaunch-clear-script {
|
|
3940
|
+
min-height: 32px;
|
|
3941
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
3942
|
+
border-radius: 6px;
|
|
3943
|
+
background: transparent;
|
|
3944
|
+
color: rgba(100, 116, 139, 0.95);
|
|
3945
|
+
display: inline-flex;
|
|
3946
|
+
align-items: center;
|
|
3947
|
+
justify-content: center;
|
|
3948
|
+
gap: 6px;
|
|
3949
|
+
padding: 0 9px;
|
|
3950
|
+
font: inherit;
|
|
3951
|
+
font-size: 12px;
|
|
3952
|
+
font-weight: 700;
|
|
3953
|
+
cursor: pointer;
|
|
3954
|
+
}
|
|
3955
|
+
.app-autolaunch-clear-script:hover {
|
|
3956
|
+
background: rgba(148, 163, 184, 0.16);
|
|
3957
|
+
color: rgba(15, 23, 42, 0.9);
|
|
3958
|
+
}
|
|
3959
|
+
.app-autolaunch-clear-script:disabled {
|
|
3960
|
+
cursor: not-allowed;
|
|
3961
|
+
opacity: 0.55;
|
|
3962
|
+
}
|
|
3917
3963
|
body.dark .app-autolaunch-script-option {
|
|
3918
3964
|
border-color: rgba(148, 163, 184, 0.16);
|
|
3919
3965
|
background: rgba(15, 23, 42, 0.72);
|
|
3920
3966
|
}
|
|
3967
|
+
body.dark .app-autolaunch-clear-script {
|
|
3968
|
+
border-color: rgba(148, 163, 184, 0.16);
|
|
3969
|
+
color: rgba(203, 213, 225, 0.9);
|
|
3970
|
+
}
|
|
3971
|
+
body.dark .app-autolaunch-clear-script:hover {
|
|
3972
|
+
background: rgba(148, 163, 184, 0.16);
|
|
3973
|
+
color: rgba(226, 232, 240, 0.92);
|
|
3974
|
+
}
|
|
3921
3975
|
body.dark .app-autolaunch-script-option:hover,
|
|
3922
3976
|
body.dark .app-autolaunch-script-option.selected {
|
|
3923
3977
|
border-color: rgba(96, 165, 250, 0.42);
|
|
@@ -3951,6 +4005,7 @@ body.dark .app-autolaunch-tag {
|
|
|
3951
4005
|
background: rgba(148, 163, 184, 0.18);
|
|
3952
4006
|
color: rgba(203, 213, 225, 0.9);
|
|
3953
4007
|
}
|
|
4008
|
+
<%- include('partials/app_autolaunch_dependency_styles') %>
|
|
3954
4009
|
.app-autolaunch-footer {
|
|
3955
4010
|
display: flex;
|
|
3956
4011
|
align-items: center;
|
|
@@ -7819,10 +7874,10 @@ body.dark .pinokio-custom-terminal-header {
|
|
|
7819
7874
|
</div>
|
|
7820
7875
|
</div>
|
|
7821
7876
|
<div class='mobile-drawer-actions' aria-label="App actions">
|
|
7822
|
-
<button type='button' class='btn header-item mobile-nav-logs' data-mobile-proxy="#logs-toggle" data-mobile-panel="logs" data-mobile-close-menu="true" aria-pressed="false" aria-label="
|
|
7877
|
+
<button type='button' class='btn header-item mobile-nav-logs' data-mobile-proxy="#logs-toggle" data-mobile-panel="logs" data-mobile-close-menu="true" aria-pressed="false" aria-label="Report Issue">
|
|
7823
7878
|
<div class='tab'>
|
|
7824
|
-
<i class="fa-solid fa-
|
|
7825
|
-
<div class='display'>
|
|
7879
|
+
<i class="fa-solid fa-circle-exclamation menu-action-leading-icon"></i>
|
|
7880
|
+
<div class='display'>Report Issue</div>
|
|
7826
7881
|
<div class='flexible'></div>
|
|
7827
7882
|
</div>
|
|
7828
7883
|
</button>
|
|
@@ -7856,10 +7911,10 @@ body.dark .pinokio-custom-terminal-header {
|
|
|
7856
7911
|
<% if (type === 'browse') { %>
|
|
7857
7912
|
<%- include('./partials/fs_status') %>
|
|
7858
7913
|
<% } %>
|
|
7859
|
-
<button type='button' id='logs-toggle' class="btn header-item" data-tippy-content="logs" aria-expanded="false" aria-controls="app-logs-drawer">
|
|
7914
|
+
<button type='button' id='logs-toggle' class="btn header-item" data-tippy-content="Report issue with logs" aria-expanded="false" aria-controls="app-logs-drawer">
|
|
7860
7915
|
<div class='tab'>
|
|
7861
|
-
<i class="fa-solid fa-
|
|
7862
|
-
<div class='display'>
|
|
7916
|
+
<i class="fa-solid fa-circle-exclamation menu-action-leading-icon"></i>
|
|
7917
|
+
<div class='display'>Report Issue</div>
|
|
7863
7918
|
<div class='flexible'></div>
|
|
7864
7919
|
<span class='logs-new-badge hidden' id='logs-new-badge'>New</span>
|
|
7865
7920
|
</div>
|
|
@@ -7884,13 +7939,16 @@ body.dark .pinokio-custom-terminal-header {
|
|
|
7884
7939
|
<div class="app-autolaunch-modal-head">
|
|
7885
7940
|
<div>
|
|
7886
7941
|
<div class="app-autolaunch-title">Autolaunch</div>
|
|
7887
|
-
<div class="app-autolaunch-note">
|
|
7942
|
+
<div class="app-autolaunch-note">Choose launch script, startup behavior, and requirements.</div>
|
|
7888
7943
|
</div>
|
|
7889
7944
|
<div class="app-autolaunch-modal-actions">
|
|
7890
|
-
<
|
|
7891
|
-
<span class="app-autolaunch-switch-
|
|
7892
|
-
<
|
|
7893
|
-
|
|
7945
|
+
<div class="app-autolaunch-switch-group">
|
|
7946
|
+
<span class="app-autolaunch-switch-caption">Start with Pinokio</span>
|
|
7947
|
+
<button type="button" class="app-autolaunch-switch" role="switch" aria-checked="<%= autolaunch_app.autolaunch_enabled ? 'true' : 'false' %>" data-app-autolaunch-switch aria-label="Start with Pinokio" title="Start with Pinokio is <%= autolaunch_app.autolaunch_enabled ? 'on' : 'off' %>">
|
|
7948
|
+
<span class="app-autolaunch-switch-label" data-app-autolaunch-switch-label><%= autolaunch_app.autolaunch_enabled ? 'ON' : 'OFF' %></span>
|
|
7949
|
+
<span class="app-autolaunch-switch-thumb" aria-hidden="true"></span>
|
|
7950
|
+
</button>
|
|
7951
|
+
</div>
|
|
7894
7952
|
<button type="button" class="app-autolaunch-close" data-app-autolaunch-close aria-label="Close">
|
|
7895
7953
|
<i class="fa-solid fa-xmark" aria-hidden="true"></i>
|
|
7896
7954
|
</button>
|
|
@@ -7902,6 +7960,12 @@ body.dark .pinokio-custom-terminal-header {
|
|
|
7902
7960
|
<div class="app-autolaunch-empty">Loading scripts...</div>
|
|
7903
7961
|
</div>
|
|
7904
7962
|
</div>
|
|
7963
|
+
<div class="app-autolaunch-modal-section">
|
|
7964
|
+
<div class="app-autolaunch-section-label">Requires</div>
|
|
7965
|
+
<div class="app-autolaunch-dependencies" data-app-autolaunch-dependencies>
|
|
7966
|
+
<div class="app-autolaunch-empty">Loading dependencies...</div>
|
|
7967
|
+
</div>
|
|
7968
|
+
</div>
|
|
7905
7969
|
<div class="app-autolaunch-footer">
|
|
7906
7970
|
<div class="app-autolaunch-feedback" data-app-autolaunch-feedback></div>
|
|
7907
7971
|
<a class="app-autolaunch-link" href="/autolaunch">Manage all</a>
|
|
@@ -7983,6 +8047,7 @@ body.dark .pinokio-custom-terminal-header {
|
|
|
7983
8047
|
<iframe class='selected' src="<%=editor_tab%>" allow="fullscreen *;" allowfullscreen></iframe>
|
|
7984
8048
|
<% } %>
|
|
7985
8049
|
<div id="browserview-network-status" class="browserview-network-status" hidden aria-live="polite"></div>
|
|
8050
|
+
<div id="launch-requirements-status" class="launch-requirements-status" data-launch-requirements-status hidden aria-live="polite"></div>
|
|
7986
8051
|
<%- include('./partials/browser_popout_surface') %>
|
|
7987
8052
|
</main>
|
|
7988
8053
|
<% if (type === 'run') { %>
|
|
@@ -8295,6 +8360,7 @@ body.dark .pinokio-custom-terminal-header {
|
|
|
8295
8360
|
let pendingSelectionRetry = null
|
|
8296
8361
|
let pendingHomeSelectionRetry = null
|
|
8297
8362
|
let initialWorkspaceDiskUsageRequested = false
|
|
8363
|
+
const openWithoutLaunching = <%- JSON.stringify(type === "run" && !autoselect) %>
|
|
8298
8364
|
const scheduleSelectionRetry = (delay = 100) => {
|
|
8299
8365
|
if (pendingSelectionRetry !== null) {
|
|
8300
8366
|
return
|
|
@@ -10004,7 +10070,9 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10004
10070
|
return ""
|
|
10005
10071
|
}
|
|
10006
10072
|
})()
|
|
10007
|
-
|
|
10073
|
+
const triggeredByUser = Boolean(eventParam || explicitTarget)
|
|
10074
|
+
const automaticSelectionDisabled = openWithoutLaunching && !triggeredByUser
|
|
10075
|
+
let persistedSelectionRaw = automaticSelectionDisabled ? null : (selectionKey && storage ? storage.getItem(selectionKey) : null)
|
|
10008
10076
|
const devRouteActive = /\/dev(?:$|\/)/.test(currentPath || "")
|
|
10009
10077
|
let persistedSelectionPayload = null
|
|
10010
10078
|
if (persistedSelectionRaw) {
|
|
@@ -10029,9 +10097,8 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10029
10097
|
persistedSelectionRaw = null
|
|
10030
10098
|
}
|
|
10031
10099
|
const originalHasPersistedSelection = Boolean(persistedSelectionPayload)
|
|
10032
|
-
const
|
|
10033
|
-
const
|
|
10034
|
-
const skipPersistedSelection = followCurrentDefault || (ignorePersistedSelection && Boolean(document.querySelector('[data-default]')))
|
|
10100
|
+
const followCurrentDefault = !automaticSelectionDisabled && forceDefaultSelection && !triggeredByUser
|
|
10101
|
+
const skipPersistedSelection = automaticSelectionDisabled || followCurrentDefault || (ignorePersistedSelection && Boolean(document.querySelector('[data-default]')))
|
|
10035
10102
|
let hasPersistedSelection = skipPersistedSelection ? false : originalHasPersistedSelection
|
|
10036
10103
|
if (skipPersistedSelection) {
|
|
10037
10104
|
persistedSelectionRaw = null
|
|
@@ -10053,7 +10120,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10053
10120
|
target = eventParam.target?.closest(".frame-link") || null
|
|
10054
10121
|
}
|
|
10055
10122
|
|
|
10056
|
-
if (!target && global_selector) {
|
|
10123
|
+
if (!target && !automaticSelectionDisabled && global_selector) {
|
|
10057
10124
|
const candidate = document.querySelector(global_selector)
|
|
10058
10125
|
if (candidate) {
|
|
10059
10126
|
target = candidate
|
|
@@ -10065,11 +10132,11 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10065
10132
|
}
|
|
10066
10133
|
}
|
|
10067
10134
|
|
|
10068
|
-
if (!target && !skipPersistedSelection) {
|
|
10135
|
+
if (!target && !automaticSelectionDisabled && !skipPersistedSelection) {
|
|
10069
10136
|
preselected = document.querySelector('#devtab.frame-link.selected') || document.querySelector('.frame-link.selected')
|
|
10070
10137
|
}
|
|
10071
10138
|
|
|
10072
|
-
if (!target && persistedSelectionPayload) {
|
|
10139
|
+
if (!target && !automaticSelectionDisabled && persistedSelectionPayload) {
|
|
10073
10140
|
target = restorePersistedFrameLink(persistedSelectionPayload)
|
|
10074
10141
|
if (!target && originalHasPersistedSelection) {
|
|
10075
10142
|
scheduleSelectionRetry()
|
|
@@ -10077,7 +10144,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10077
10144
|
}
|
|
10078
10145
|
}
|
|
10079
10146
|
|
|
10080
|
-
if (!target && !skipPersistedSelection) {
|
|
10147
|
+
if (!target && !automaticSelectionDisabled && !skipPersistedSelection) {
|
|
10081
10148
|
const urlKey = selectionUrlStorageKey()
|
|
10082
10149
|
const storedUrl = urlKey && storage ? storage.getItem(urlKey) : null
|
|
10083
10150
|
if (storedUrl) {
|
|
@@ -10085,7 +10152,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10085
10152
|
}
|
|
10086
10153
|
}
|
|
10087
10154
|
|
|
10088
|
-
if (!target && followCurrentDefault) {
|
|
10155
|
+
if (!target && !automaticSelectionDisabled && followCurrentDefault) {
|
|
10089
10156
|
const defaultSelection = getDefaultSelection()
|
|
10090
10157
|
if (defaultSelection) {
|
|
10091
10158
|
target = defaultSelection
|
|
@@ -10093,11 +10160,11 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10093
10160
|
}
|
|
10094
10161
|
|
|
10095
10162
|
const devTab = document.querySelector('#devtab.frame-link')
|
|
10096
|
-
if (!target && preselected && preselected !== devTab) {
|
|
10163
|
+
if (!target && !automaticSelectionDisabled && preselected && preselected !== devTab) {
|
|
10097
10164
|
target = preselected
|
|
10098
10165
|
}
|
|
10099
10166
|
|
|
10100
|
-
if (!triggeredByUser && !resolvedByGlobalSelector && !target && devRouteActive && devTab) {
|
|
10167
|
+
if (!automaticSelectionDisabled && !triggeredByUser && !resolvedByGlobalSelector && !target && devRouteActive && devTab) {
|
|
10101
10168
|
const defaultCandidate = getDefaultSelection()
|
|
10102
10169
|
if (pluginLaunchActive && defaultCandidate) {
|
|
10103
10170
|
target = defaultCandidate
|
|
@@ -10106,7 +10173,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10106
10173
|
}
|
|
10107
10174
|
}
|
|
10108
10175
|
|
|
10109
|
-
if (!target && preselected) {
|
|
10176
|
+
if (!target && !automaticSelectionDisabled && preselected) {
|
|
10110
10177
|
target = preselected
|
|
10111
10178
|
}
|
|
10112
10179
|
|
|
@@ -10141,7 +10208,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10141
10208
|
}
|
|
10142
10209
|
<% } %>
|
|
10143
10210
|
|
|
10144
|
-
if (!target && preselected) {
|
|
10211
|
+
if (!target && !automaticSelectionDisabled && preselected) {
|
|
10145
10212
|
target = preselected
|
|
10146
10213
|
}
|
|
10147
10214
|
|
|
@@ -10278,7 +10345,6 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10278
10345
|
eventParam.preventDefault()
|
|
10279
10346
|
eventParam.stopPropagation()
|
|
10280
10347
|
}
|
|
10281
|
-
|
|
10282
10348
|
// save target.href
|
|
10283
10349
|
<% if (type !== "run") { %>
|
|
10284
10350
|
let _url = new URL(target.href)
|
|
@@ -10372,7 +10438,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10372
10438
|
writeFrameInjectDescriptors(targetFrame, targetInjectDescriptors)
|
|
10373
10439
|
publishBrowserviewInjectTargets([], { sync: true })
|
|
10374
10440
|
targetFrame.classList.remove("hidden")
|
|
10375
|
-
|
|
10441
|
+
let mode = target.getAttribute("data-mode")
|
|
10376
10442
|
if (mode === "refresh") {
|
|
10377
10443
|
if (targetFrame.src !== target.href) {
|
|
10378
10444
|
targetFrame.src = target.href
|
|
@@ -10661,7 +10727,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
10661
10727
|
}
|
|
10662
10728
|
if (mobileLogsButton) {
|
|
10663
10729
|
mobileLogsButton.classList.toggle("has-new", show)
|
|
10664
|
-
mobileLogsButton.setAttribute("aria-label", show ? "
|
|
10730
|
+
mobileLogsButton.setAttribute("aria-label", show ? "Report Issue, new report available" : "Report Issue")
|
|
10665
10731
|
}
|
|
10666
10732
|
try {
|
|
10667
10733
|
if (show) {
|
|
@@ -18480,6 +18546,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18480
18546
|
}
|
|
18481
18547
|
})()
|
|
18482
18548
|
|
|
18549
|
+
<%- include('partials/launch_requirements_status_client') %>
|
|
18550
|
+
|
|
18483
18551
|
;(function() {
|
|
18484
18552
|
const root = document.querySelector("[data-app-autolaunch]")
|
|
18485
18553
|
const initialState = <%- JSON.stringify((typeof autolaunch_app !== "undefined" && autolaunch_app) ? autolaunch_app : null).replace(/</g, "\\u003c") %>
|
|
@@ -18493,8 +18561,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18493
18561
|
const switchLabel = root.querySelector("[data-app-autolaunch-switch-label]")
|
|
18494
18562
|
const closeButton = root.querySelector("[data-app-autolaunch-close]")
|
|
18495
18563
|
const scriptsEl = root.querySelector("[data-app-autolaunch-scripts]")
|
|
18564
|
+
const dependenciesEl = root.querySelector("[data-app-autolaunch-dependencies]")
|
|
18496
18565
|
const feedbackEl = root.querySelector("[data-app-autolaunch-feedback]")
|
|
18497
|
-
if (!appId || !button || !status || !modal || !switchButton || !switchLabel || !closeButton || !scriptsEl || !feedbackEl) return
|
|
18566
|
+
if (!appId || !button || !status || !modal || !switchButton || !switchLabel || !closeButton || !scriptsEl || !dependenciesEl || !feedbackEl) return
|
|
18498
18567
|
|
|
18499
18568
|
let state = Object.assign({}, initialState)
|
|
18500
18569
|
let candidateState = null
|
|
@@ -18502,6 +18571,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18502
18571
|
let selectedScript = state.autolaunch || ""
|
|
18503
18572
|
let loadingCandidates = false
|
|
18504
18573
|
let saving = false
|
|
18574
|
+
let savingDependencies = false
|
|
18575
|
+
let savingDependencyScript = false
|
|
18576
|
+
let dependencyPickerOpen = false
|
|
18577
|
+
let dependencyScriptPickerOpen = ""
|
|
18578
|
+
let pendingDependencyId = ""
|
|
18579
|
+
let dependencyScriptCandidates = {}
|
|
18580
|
+
let dependencyScriptSelections = {}
|
|
18505
18581
|
|
|
18506
18582
|
const escapeHtml = (value) => {
|
|
18507
18583
|
if (value === null || value === undefined) return ""
|
|
@@ -18520,124 +18596,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18520
18596
|
if (!candidateState) return []
|
|
18521
18597
|
return [].concat(candidateState.menu || [], candidateState.other || [])
|
|
18522
18598
|
}
|
|
18523
|
-
|
|
18524
|
-
|
|
18525
|
-
|
|
18526
|
-
|
|
18527
|
-
|
|
18528
|
-
|
|
18529
|
-
|
|
18530
|
-
|
|
18531
|
-
|
|
18532
|
-
|
|
18533
|
-
|
|
18534
|
-
const setBusy = (busy) => {
|
|
18535
|
-
saving = !!busy
|
|
18536
|
-
switchButton.disabled = saving || loadingCandidates
|
|
18537
|
-
button.toggleAttribute("aria-busy", saving)
|
|
18538
|
-
scriptsEl.querySelectorAll("input").forEach((input) => {
|
|
18539
|
-
input.disabled = saving
|
|
18540
|
-
})
|
|
18541
|
-
}
|
|
18542
|
-
const renderStatus = () => {
|
|
18543
|
-
const enabled = !!(state && state.autolaunch_enabled)
|
|
18544
|
-
status.textContent = enabled ? "ON" : "OFF"
|
|
18545
|
-
button.dataset.enabled = enabled ? "true" : "false"
|
|
18546
|
-
button.setAttribute("aria-label", `Auto launch ${enabled ? "On" : "Off"}`)
|
|
18547
|
-
switchButton.setAttribute("aria-checked", enabled ? "true" : "false")
|
|
18548
|
-
switchButton.title = `Auto launch is ${enabled ? "on" : "off"}`
|
|
18549
|
-
switchLabel.textContent = enabled ? "ON" : "OFF"
|
|
18550
|
-
root.classList.toggle("enabled", enabled)
|
|
18551
|
-
}
|
|
18552
|
-
const setOpen = (open) => {
|
|
18553
|
-
root.classList.toggle("open", !!open)
|
|
18554
|
-
button.setAttribute("aria-expanded", open ? "true" : "false")
|
|
18555
|
-
modal.classList.toggle("hidden", !open)
|
|
18556
|
-
if (open) {
|
|
18557
|
-
if (!candidateState && !loadingCandidates) {
|
|
18558
|
-
loadCandidates()
|
|
18559
|
-
}
|
|
18560
|
-
window.setTimeout(() => {
|
|
18561
|
-
if (!modal.classList.contains("hidden")) {
|
|
18562
|
-
switchButton.focus()
|
|
18563
|
-
}
|
|
18564
|
-
}, 0)
|
|
18565
|
-
} else {
|
|
18566
|
-
button.focus()
|
|
18567
|
-
}
|
|
18568
|
-
}
|
|
18569
|
-
const renderScripts = () => {
|
|
18570
|
-
if (loadingCandidates) {
|
|
18571
|
-
scriptsEl.innerHTML = '<div class="app-autolaunch-empty">Loading scripts...</div>'
|
|
18572
|
-
setBusy(false)
|
|
18573
|
-
return
|
|
18574
|
-
}
|
|
18575
|
-
if (candidateError) {
|
|
18576
|
-
scriptsEl.innerHTML = `<div class="app-autolaunch-empty">${escapeHtml(candidateError)}</div>`
|
|
18577
|
-
setBusy(false)
|
|
18578
|
-
return
|
|
18579
|
-
}
|
|
18580
|
-
if (!candidateState) {
|
|
18581
|
-
scriptsEl.innerHTML = '<div class="app-autolaunch-empty">Open to load scripts.</div>'
|
|
18582
|
-
setBusy(false)
|
|
18583
|
-
return
|
|
18584
|
-
}
|
|
18585
|
-
const candidates = allCandidates().slice()
|
|
18586
|
-
const current = candidateState.current || state.autolaunch || ""
|
|
18587
|
-
if (current && !candidates.some((candidate) => candidate && candidate.script === current)) {
|
|
18588
|
-
candidates.unshift({
|
|
18589
|
-
script: current,
|
|
18590
|
-
label: "Current",
|
|
18591
|
-
source: "current",
|
|
18592
|
-
current: true
|
|
18593
|
-
})
|
|
18594
|
-
}
|
|
18595
|
-
if (!selectedScript) {
|
|
18596
|
-
selectedScript = chooseInitialScript(candidateState)
|
|
18597
|
-
}
|
|
18598
|
-
if (candidates.length === 0) {
|
|
18599
|
-
scriptsEl.innerHTML = '<div class="app-autolaunch-empty">No local scripts were found for this app.</div>'
|
|
18600
|
-
setBusy(false)
|
|
18601
|
-
return
|
|
18602
|
-
}
|
|
18603
|
-
scriptsEl.innerHTML = candidates.map((candidate) => {
|
|
18604
|
-
const script = candidate && candidate.script ? candidate.script : ""
|
|
18605
|
-
if (!script) return ""
|
|
18606
|
-
const label = candidate.label || script
|
|
18607
|
-
const checked = script === selectedScript ? "checked" : ""
|
|
18608
|
-
const selected = script === selectedScript ? " selected" : ""
|
|
18609
|
-
const tags = [
|
|
18610
|
-
candidate.current ? '<span class="app-autolaunch-tag">Current</span>' : "",
|
|
18611
|
-
candidate.menu_default ? '<span class="app-autolaunch-tag">Default</span>' : "",
|
|
18612
|
-
candidate.has_params ? '<span class="app-autolaunch-tag">Params ignored</span>' : ""
|
|
18613
|
-
].filter(Boolean).join("")
|
|
18614
|
-
const pathLine = label === script
|
|
18615
|
-
? ""
|
|
18616
|
-
: `<div class="app-autolaunch-script-path" title="${escapeHtml(script)}">${escapeHtml(script)}</div>`
|
|
18617
|
-
return `
|
|
18618
|
-
<label class="app-autolaunch-script-option${selected}">
|
|
18619
|
-
<input type="radio" name="app-autolaunch-script" value="${escapeHtml(script)}" ${checked}>
|
|
18620
|
-
<div>
|
|
18621
|
-
<div class="app-autolaunch-script-title"><span>${escapeHtml(label)}</span>${tags}</div>
|
|
18622
|
-
${pathLine}
|
|
18623
|
-
</div>
|
|
18624
|
-
</label>
|
|
18625
|
-
`
|
|
18626
|
-
}).join("")
|
|
18627
|
-
setBusy(false)
|
|
18628
|
-
}
|
|
18629
|
-
const syncState = (updated) => {
|
|
18630
|
-
if (updated) {
|
|
18631
|
-
state = Object.assign({}, state, updated)
|
|
18632
|
-
if (candidateState) {
|
|
18633
|
-
candidateState.app = Object.assign({}, candidateState.app || {}, updated)
|
|
18634
|
-
candidateState.current = updated.autolaunch || ""
|
|
18635
|
-
}
|
|
18636
|
-
if (updated.autolaunch) {
|
|
18637
|
-
selectedScript = updated.autolaunch
|
|
18638
|
-
}
|
|
18639
|
-
}
|
|
18640
|
-
renderStatus()
|
|
18599
|
+
<%- include('partials/app_autolaunch_modal_helpers') %>
|
|
18600
|
+
const hasLaunchRequirementConfig = () => {
|
|
18601
|
+
return !!(
|
|
18602
|
+
state.autolaunch &&
|
|
18603
|
+
Array.isArray(state.autolaunch_depends) &&
|
|
18604
|
+
state.autolaunch_depends.length > 0
|
|
18605
|
+
)
|
|
18606
|
+
}
|
|
18607
|
+
const notifyLaunchRequirementsConfigured = () => {
|
|
18608
|
+
if (!hasLaunchRequirementConfig()) return
|
|
18609
|
+
window.dispatchEvent(new CustomEvent("pinokio:launch-requirements-configured"))
|
|
18641
18610
|
}
|
|
18642
18611
|
const loadCandidates = async () => {
|
|
18643
18612
|
loadingCandidates = true
|
|
@@ -18654,16 +18623,84 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18654
18623
|
}
|
|
18655
18624
|
candidateState = data
|
|
18656
18625
|
syncState(data.app)
|
|
18657
|
-
selectedScript =
|
|
18626
|
+
selectedScript = configuredScript(data)
|
|
18658
18627
|
setFeedback("")
|
|
18659
18628
|
} catch (error) {
|
|
18660
18629
|
candidateError = error && error.message ? error.message : "Failed to load scripts."
|
|
18661
18630
|
} finally {
|
|
18662
18631
|
loadingCandidates = false
|
|
18663
18632
|
renderScripts()
|
|
18633
|
+
renderDependencies()
|
|
18634
|
+
}
|
|
18635
|
+
}
|
|
18636
|
+
<%- include('partials/app_autolaunch_dependency_save') %>
|
|
18637
|
+
const updateDependencyCandidateState = (updatedApp) => {
|
|
18638
|
+
if (!updatedApp || !updatedApp.id || !candidateState || !Array.isArray(candidateState.dependency_apps)) return
|
|
18639
|
+
candidateState.dependency_apps = candidateState.dependency_apps.map((app) => app.id === updatedApp.id ? updatedApp : app)
|
|
18640
|
+
}
|
|
18641
|
+
<%- include('partials/launch_settings_dependency_script_loader_factory') %>
|
|
18642
|
+
const loadDependencyScriptCandidates = createLaunchSettingsDependencyScriptLoader({
|
|
18643
|
+
getCache: () => dependencyScriptCandidates,
|
|
18644
|
+
render: renderDependencies
|
|
18645
|
+
})
|
|
18646
|
+
const saveDependencyScript = async (dependencyId, script) => {
|
|
18647
|
+
const dependency = dependencyById(dependencyId)
|
|
18648
|
+
if (!dependency || !script || savingDependencyScript) return
|
|
18649
|
+
savingDependencyScript = true
|
|
18650
|
+
setBusy(true)
|
|
18651
|
+
setFeedback("Saving requirement script...")
|
|
18652
|
+
try {
|
|
18653
|
+
const response = await fetch("/autolaunch", {
|
|
18654
|
+
method: "POST",
|
|
18655
|
+
headers: {
|
|
18656
|
+
"Content-Type": "application/json",
|
|
18657
|
+
"Accept": "application/json"
|
|
18658
|
+
},
|
|
18659
|
+
body: JSON.stringify({
|
|
18660
|
+
app: dependencyId,
|
|
18661
|
+
script,
|
|
18662
|
+
enabled: !!dependency.autolaunch_enabled
|
|
18663
|
+
})
|
|
18664
|
+
})
|
|
18665
|
+
const data = await response.json()
|
|
18666
|
+
if (!response.ok || !data.ok) {
|
|
18667
|
+
throw new Error(data.error || "Failed to save launch script.")
|
|
18668
|
+
}
|
|
18669
|
+
updateDependencyCandidateState(data.app)
|
|
18670
|
+
if (pendingDependencyId === dependencyId) {
|
|
18671
|
+
const dependencies = Array.isArray(state.autolaunch_depends) ? state.autolaunch_depends.slice() : []
|
|
18672
|
+
if (!dependencies.includes(dependencyId)) {
|
|
18673
|
+
const savedDependency = await saveDependencies(dependencies.concat(dependencyId), "Requirement saved.")
|
|
18674
|
+
if (savedDependency) {
|
|
18675
|
+
notifyLaunchRequirementsConfigured()
|
|
18676
|
+
pendingDependencyId = ""
|
|
18677
|
+
delete dependencyScriptSelections[dependencyId]
|
|
18678
|
+
dependencyScriptPickerOpen = ""
|
|
18679
|
+
renderDependencies()
|
|
18680
|
+
}
|
|
18681
|
+
} else {
|
|
18682
|
+
pendingDependencyId = ""
|
|
18683
|
+
delete dependencyScriptSelections[dependencyId]
|
|
18684
|
+
dependencyScriptPickerOpen = ""
|
|
18685
|
+
renderDependencies()
|
|
18686
|
+
notifyLaunchRequirementsConfigured()
|
|
18687
|
+
setFeedback("Requirement saved.")
|
|
18688
|
+
}
|
|
18689
|
+
} else {
|
|
18690
|
+
dependencyScriptPickerOpen = ""
|
|
18691
|
+
delete dependencyScriptSelections[dependencyId]
|
|
18692
|
+
renderDependencies()
|
|
18693
|
+
setFeedback("Requirement script saved.")
|
|
18694
|
+
}
|
|
18695
|
+
} catch (error) {
|
|
18696
|
+
setFeedback(error && error.message ? error.message : "Failed to save launch script.", true)
|
|
18697
|
+
renderDependencies()
|
|
18698
|
+
} finally {
|
|
18699
|
+
savingDependencyScript = false
|
|
18700
|
+
setBusy(false)
|
|
18664
18701
|
}
|
|
18665
18702
|
}
|
|
18666
|
-
const saveScript = async (script) => {
|
|
18703
|
+
const saveScript = async (script, enabled = !!state.autolaunch_enabled) => {
|
|
18667
18704
|
if (saving) return
|
|
18668
18705
|
setBusy(true)
|
|
18669
18706
|
setFeedback(script ? "Saving..." : "Disabling...")
|
|
@@ -18674,17 +18711,52 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18674
18711
|
"Content-Type": "application/json",
|
|
18675
18712
|
"Accept": "application/json"
|
|
18676
18713
|
},
|
|
18677
|
-
body: JSON.stringify({ app: appId, script: script || "" })
|
|
18714
|
+
body: JSON.stringify({ app: appId, script: script || "", enabled: !!enabled })
|
|
18715
|
+
})
|
|
18716
|
+
const data = await response.json()
|
|
18717
|
+
if (!response.ok || !data.ok) {
|
|
18718
|
+
throw new Error(data.error || "Failed to save Autolaunch settings.")
|
|
18719
|
+
}
|
|
18720
|
+
syncState(data.app)
|
|
18721
|
+
notifyLaunchRequirementsConfigured()
|
|
18722
|
+
setFeedback(data.app && data.app.autolaunch_enabled ? "Saved." : (script ? "Launch script saved." : "Disabled."))
|
|
18723
|
+
renderScripts()
|
|
18724
|
+
renderDependencies()
|
|
18725
|
+
} catch (error) {
|
|
18726
|
+
setFeedback(error && error.message ? error.message : "Failed to save Autolaunch settings.", true)
|
|
18727
|
+
renderStatus()
|
|
18728
|
+
} finally {
|
|
18729
|
+
setBusy(false)
|
|
18730
|
+
}
|
|
18731
|
+
}
|
|
18732
|
+
const clearScript = async () => {
|
|
18733
|
+
if (saving) return
|
|
18734
|
+
if (Array.isArray(state.autolaunch_depends) && state.autolaunch_depends.length > 0) {
|
|
18735
|
+
setFeedback("Remove requirements before clearing this app's launch script.", true)
|
|
18736
|
+
return
|
|
18737
|
+
}
|
|
18738
|
+
setBusy(true)
|
|
18739
|
+
setFeedback("Clearing launch script...")
|
|
18740
|
+
try {
|
|
18741
|
+
const response = await fetch("/autolaunch", {
|
|
18742
|
+
method: "POST",
|
|
18743
|
+
headers: {
|
|
18744
|
+
"Content-Type": "application/json",
|
|
18745
|
+
"Accept": "application/json"
|
|
18746
|
+
},
|
|
18747
|
+
body: JSON.stringify({ app: appId, clear_script: true })
|
|
18678
18748
|
})
|
|
18679
18749
|
const data = await response.json()
|
|
18680
18750
|
if (!response.ok || !data.ok) {
|
|
18681
|
-
throw new Error(data.error || "Failed to
|
|
18751
|
+
throw new Error(data.error || "Failed to clear launch script.")
|
|
18682
18752
|
}
|
|
18683
18753
|
syncState(data.app)
|
|
18684
|
-
|
|
18754
|
+
notifyLaunchRequirementsConfigured()
|
|
18755
|
+
setFeedback("Launch script cleared.")
|
|
18685
18756
|
renderScripts()
|
|
18757
|
+
renderDependencies()
|
|
18686
18758
|
} catch (error) {
|
|
18687
|
-
setFeedback(error && error.message ? error.message : "Failed to
|
|
18759
|
+
setFeedback(error && error.message ? error.message : "Failed to clear launch script.", true)
|
|
18688
18760
|
renderStatus()
|
|
18689
18761
|
} finally {
|
|
18690
18762
|
setBusy(false)
|
|
@@ -18693,23 +18765,25 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18693
18765
|
const toggleAutolaunch = async () => {
|
|
18694
18766
|
if (saving || loadingCandidates) return
|
|
18695
18767
|
if (state.autolaunch_enabled) {
|
|
18696
|
-
|
|
18768
|
+
const script = selectedScript || state.autolaunch || ""
|
|
18769
|
+
await saveScript(script, false)
|
|
18697
18770
|
return
|
|
18698
18771
|
}
|
|
18699
18772
|
if (!candidateState) {
|
|
18700
18773
|
await loadCandidates()
|
|
18701
18774
|
}
|
|
18702
|
-
const script = selectedScript ||
|
|
18775
|
+
const script = selectedScript || ""
|
|
18703
18776
|
if (!script) {
|
|
18704
|
-
setFeedback("Choose a script first.", true)
|
|
18777
|
+
setFeedback("Choose a launch script first.", true)
|
|
18705
18778
|
return
|
|
18706
18779
|
}
|
|
18707
18780
|
selectedScript = script
|
|
18708
|
-
await saveScript(script)
|
|
18781
|
+
await saveScript(script, true)
|
|
18709
18782
|
}
|
|
18710
18783
|
|
|
18711
18784
|
renderStatus()
|
|
18712
18785
|
renderScripts()
|
|
18786
|
+
renderDependencies()
|
|
18713
18787
|
|
|
18714
18788
|
button.addEventListener("click", (event) => {
|
|
18715
18789
|
event.preventDefault()
|
|
@@ -18738,11 +18812,41 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18738
18812
|
if (!input) return
|
|
18739
18813
|
selectedScript = input.value
|
|
18740
18814
|
renderScripts()
|
|
18741
|
-
if (
|
|
18742
|
-
saveScript(selectedScript)
|
|
18815
|
+
if (selectedScript !== state.autolaunch) {
|
|
18816
|
+
saveScript(selectedScript, !!state.autolaunch_enabled)
|
|
18743
18817
|
}
|
|
18744
18818
|
})
|
|
18745
|
-
|
|
18819
|
+
scriptsEl.addEventListener("click", (event) => {
|
|
18820
|
+
const clear = event.target && event.target.closest("[data-app-autolaunch-clear-script]")
|
|
18821
|
+
if (!clear) return
|
|
18822
|
+
event.preventDefault()
|
|
18823
|
+
clearScript()
|
|
18824
|
+
})
|
|
18825
|
+
<%- include('partials/app_autolaunch_dependency_events') %>
|
|
18826
|
+
window.addEventListener("pinokio:launch-requirements-choose-script", async (event) => {
|
|
18827
|
+
const dependency = event && event.detail ? event.detail.app : ""
|
|
18828
|
+
if (!dependency) return
|
|
18829
|
+
event.preventDefault()
|
|
18830
|
+
if (!candidateState && !loadingCandidates) {
|
|
18831
|
+
await loadCandidates()
|
|
18832
|
+
}
|
|
18833
|
+
setOpen(true)
|
|
18834
|
+
pendingDependencyId = ""
|
|
18835
|
+
dependencyScriptPickerOpen = dependency
|
|
18836
|
+
const dependencyApp = dependencyById(dependency)
|
|
18837
|
+
dependencyScriptSelections[dependency] = dependencyApp && dependencyApp.autolaunch ? dependencyApp.autolaunch : ""
|
|
18838
|
+
renderDependencies()
|
|
18839
|
+
loadDependencyScriptCandidates(dependency)
|
|
18840
|
+
})
|
|
18841
|
+
;(async () => {
|
|
18842
|
+
const params = new URLSearchParams(window.location.search || "")
|
|
18843
|
+
if (params.get("autolaunch") !== "1") return
|
|
18844
|
+
setOpen(true)
|
|
18845
|
+
if (!candidateState && !loadingCandidates) {
|
|
18846
|
+
await loadCandidates()
|
|
18847
|
+
}
|
|
18848
|
+
})()
|
|
18849
|
+
document.addEventListener("keydown", (event) => {
|
|
18746
18850
|
if (event.key === "Escape" && !modal.classList.contains("hidden")) {
|
|
18747
18851
|
setOpen(false)
|
|
18748
18852
|
}
|