pinokiod 5.1.10 → 5.1.11
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/fs/download_worker.js +158 -0
- package/kernel/api/fs/index.js +95 -91
- package/kernel/api/index.js +3 -0
- package/kernel/bin/index.js +5 -2
- package/kernel/environment.js +19 -2
- package/kernel/git.js +972 -1
- package/kernel/index.js +65 -30
- package/kernel/peer.js +1 -2
- package/kernel/plugin.js +0 -8
- package/kernel/procs.js +92 -36
- package/kernel/prototype.js +45 -22
- package/kernel/shells.js +30 -6
- package/kernel/sysinfo.js +33 -13
- package/kernel/util.js +61 -24
- package/kernel/workspace_status.js +131 -7
- package/package.json +1 -1
- package/pipe/index.js +1 -1
- package/server/index.js +1169 -350
- package/server/public/create-launcher.js +157 -2
- package/server/public/install.js +135 -41
- package/server/public/style.css +32 -1
- package/server/public/tab-link-popover.js +45 -14
- package/server/public/terminal-settings.js +51 -35
- package/server/public/urldropdown.css +89 -3
- package/server/socket.js +12 -7
- package/server/views/agents.ejs +4 -3
- package/server/views/app.ejs +798 -30
- package/server/views/bootstrap.ejs +2 -1
- package/server/views/checkpoints.ejs +1014 -0
- package/server/views/checkpoints_registry_beta.ejs +260 -0
- package/server/views/columns.ejs +4 -4
- package/server/views/connect.ejs +1 -0
- package/server/views/d.ejs +74 -4
- package/server/views/download.ejs +28 -28
- package/server/views/editor.ejs +4 -5
- package/server/views/env_editor.ejs +1 -1
- package/server/views/file_explorer.ejs +1 -1
- package/server/views/index.ejs +3 -1
- package/server/views/init/index.ejs +2 -1
- package/server/views/install.ejs +2 -1
- package/server/views/net.ejs +9 -7
- package/server/views/network.ejs +15 -14
- package/server/views/pro.ejs +5 -2
- package/server/views/prototype/index.ejs +2 -1
- package/server/views/registry_link.ejs +76 -0
- package/server/views/rows.ejs +4 -4
- package/server/views/screenshots.ejs +1 -0
- package/server/views/settings.ejs +1 -0
- package/server/views/shell.ejs +4 -6
- package/server/views/terminal.ejs +528 -38
- package/server/views/tools.ejs +1 -0
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/1764297248545 +0 -45
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/1764335557118 +0 -45
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/1764335834126 +0 -45
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/events +0 -12
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/latest +0 -45
package/server/views/app.ejs
CHANGED
|
@@ -24,9 +24,30 @@ body.dark #devtab.selected {
|
|
|
24
24
|
border: none;
|
|
25
25
|
background: rgb(27, 28, 29) !important;
|
|
26
26
|
}
|
|
27
|
+
.config-info {
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
font-size: 12px;
|
|
32
|
+
font-weight: bold;
|
|
33
|
+
}
|
|
34
|
+
body.dark .snapshot-comment {
|
|
35
|
+
color: white;
|
|
36
|
+
}
|
|
37
|
+
.config-info:hover, .config-info:hover i {
|
|
38
|
+
color: rgba(127, 91, 243, 0.95);
|
|
39
|
+
}
|
|
27
40
|
#editortab {
|
|
28
41
|
color: #7f5bf3;
|
|
29
42
|
}
|
|
43
|
+
body.dark #layout-toggle {
|
|
44
|
+
color: white;
|
|
45
|
+
}
|
|
46
|
+
#layout-toggle {
|
|
47
|
+
padding: 10px;
|
|
48
|
+
border: none;
|
|
49
|
+
background: none;
|
|
50
|
+
}
|
|
30
51
|
#devtab {
|
|
31
52
|
align-items: center;
|
|
32
53
|
justify-content: center;
|
|
@@ -133,6 +154,42 @@ body.dark .m {
|
|
|
133
154
|
flex-direction: column;
|
|
134
155
|
flex-grow: 1;
|
|
135
156
|
}
|
|
157
|
+
.appcanvas.vertical {
|
|
158
|
+
flex-direction: row;
|
|
159
|
+
--appcanvas-sidebar-width: 150px;
|
|
160
|
+
}
|
|
161
|
+
.appcanvas.vertical > aside .header-item {
|
|
162
|
+
border-radius: 0;
|
|
163
|
+
}
|
|
164
|
+
.appcanvas.vertical #devtab {
|
|
165
|
+
border-radius: 0;
|
|
166
|
+
}
|
|
167
|
+
.appcanvas.vertical img.meta-icon {
|
|
168
|
+
/*
|
|
169
|
+
padding: 10px;
|
|
170
|
+
display: block;
|
|
171
|
+
margin: 0 auto;
|
|
172
|
+
width: 50px;
|
|
173
|
+
height: 50px;
|
|
174
|
+
*/
|
|
175
|
+
}
|
|
176
|
+
.appcanvas.vertical aside {
|
|
177
|
+
flex-direction: row;
|
|
178
|
+
}
|
|
179
|
+
.appcanvas.vertical > aside .submenu {
|
|
180
|
+
display: block;
|
|
181
|
+
}
|
|
182
|
+
.appcanvas.vertical > aside .m {
|
|
183
|
+
display: block;
|
|
184
|
+
}
|
|
185
|
+
.appcanvas.vertical .menu-container {
|
|
186
|
+
overflow: auto;
|
|
187
|
+
display: block;
|
|
188
|
+
width: var(--appcanvas-sidebar-width);
|
|
189
|
+
}
|
|
190
|
+
.appcanvas.vertical .config-info {
|
|
191
|
+
margin-bottom: 10px;
|
|
192
|
+
}
|
|
136
193
|
.appcanvas {
|
|
137
194
|
display: flex;
|
|
138
195
|
flex-direction: column;
|
|
@@ -158,9 +215,37 @@ body.dark .appcanvas_filler {
|
|
|
158
215
|
height: 5px;
|
|
159
216
|
background: #F1F1F1 !important;
|
|
160
217
|
}
|
|
218
|
+
.appcanvas-resizer {
|
|
219
|
+
display: none;
|
|
220
|
+
}
|
|
221
|
+
.appcanvas.vertical .appcanvas-resizer {
|
|
222
|
+
display: flex;
|
|
223
|
+
flex: 0 0 0px;
|
|
224
|
+
align-items: stretch;
|
|
225
|
+
justify-content: center;
|
|
226
|
+
cursor: grab;
|
|
227
|
+
touch-action: none;
|
|
228
|
+
}
|
|
229
|
+
.appcanvas.vertical .appcanvas-resizer:hover::before {
|
|
230
|
+
background: rgba(0, 0, 0, 0.15);
|
|
231
|
+
}
|
|
232
|
+
.appcanvas.vertical .appcanvas-resizer::before {
|
|
233
|
+
content: '';
|
|
234
|
+
width: 5px;
|
|
235
|
+
border-radius: 999px;
|
|
236
|
+
margin: 3px 0;
|
|
237
|
+
}
|
|
238
|
+
body.dark .appcanvas.vertical .appcanvas-resizer:hover::before {
|
|
239
|
+
background: rgba(255, 255, 255, 0.3);
|
|
240
|
+
}
|
|
241
|
+
.appcanvas.vertical > aside .header-item {
|
|
242
|
+
max-width: none;
|
|
243
|
+
}
|
|
161
244
|
|
|
162
245
|
.appcanvas > .container {
|
|
246
|
+
/*
|
|
163
247
|
order: 1;
|
|
248
|
+
*/
|
|
164
249
|
min-height: 0;
|
|
165
250
|
flex-grow: 1;
|
|
166
251
|
display: flex;
|
|
@@ -1185,11 +1270,15 @@ body.dark .submenu {
|
|
|
1185
1270
|
font-size: 12px;
|
|
1186
1271
|
font-weight: bold;
|
|
1187
1272
|
}
|
|
1273
|
+
body.dark .disk-usage {
|
|
1274
|
+
border-right: 1px solid rgba(255,255,255,0.1);
|
|
1275
|
+
}
|
|
1188
1276
|
.disk-usage {
|
|
1189
|
-
|
|
1277
|
+
border-right: 1px solid rgba(0,0,0,0.1);
|
|
1190
1278
|
font-weight: bold;
|
|
1191
|
-
|
|
1192
|
-
|
|
1279
|
+
color: white;
|
|
1280
|
+
padding: 5px 10px;
|
|
1281
|
+
border-radius: 4px;
|
|
1193
1282
|
}
|
|
1194
1283
|
.disk-usage i {
|
|
1195
1284
|
margin-right: 5px;
|
|
@@ -2375,6 +2464,7 @@ body.dark .pinokio-git-diff-viewer-header {
|
|
|
2375
2464
|
background: var(--pinokio-modal-body-bg);
|
|
2376
2465
|
color: var(--pinokio-modal-body-text-color);
|
|
2377
2466
|
padding: 10px;
|
|
2467
|
+
font-size: 14px;
|
|
2378
2468
|
}
|
|
2379
2469
|
|
|
2380
2470
|
.pinokio-modal-body--history {
|
|
@@ -3227,7 +3317,63 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3227
3317
|
.meta-icon {
|
|
3228
3318
|
width: 25px;
|
|
3229
3319
|
height: 25px;
|
|
3230
|
-
|
|
3320
|
+
margin-right: 10px;
|
|
3321
|
+
}
|
|
3322
|
+
body.dark .snapshot-footer {
|
|
3323
|
+
color: white;
|
|
3324
|
+
border-top: 1px solid rgba(148, 163, 184, 0.2);
|
|
3325
|
+
}
|
|
3326
|
+
.snapshot-footer .btn {
|
|
3327
|
+
font-size: 14px;
|
|
3328
|
+
}
|
|
3329
|
+
.snapshot-footer {
|
|
3330
|
+
border-top: 1px solid rgba(148, 163, 184, 0.35);
|
|
3331
|
+
padding: 8px !important;
|
|
3332
|
+
display: flex;
|
|
3333
|
+
align-items: center;
|
|
3334
|
+
justify-content: flex-start;
|
|
3335
|
+
gap: 10px;
|
|
3336
|
+
font-size: 14px;
|
|
3337
|
+
flex-wrap: wrap;
|
|
3338
|
+
}
|
|
3339
|
+
.snapshot-footer .snapshot-btn {
|
|
3340
|
+
background: rgba(127, 91, 243, 0.95) !important;
|
|
3341
|
+
padding: 8px;
|
|
3342
|
+
}
|
|
3343
|
+
.snapshot-footer .caption {
|
|
3344
|
+
opacity: 0.8;
|
|
3345
|
+
}
|
|
3346
|
+
.snapshot-footer-save {
|
|
3347
|
+
display: flex;
|
|
3348
|
+
align-items: center;
|
|
3349
|
+
gap: 10px;
|
|
3350
|
+
flex-wrap: wrap;
|
|
3351
|
+
}
|
|
3352
|
+
.snapshot-footer-publish {
|
|
3353
|
+
flex-basis: 100%;
|
|
3354
|
+
display: flex;
|
|
3355
|
+
align-items: center;
|
|
3356
|
+
gap: 10px;
|
|
3357
|
+
}
|
|
3358
|
+
.snapshot-footer-publish-text {
|
|
3359
|
+
opacity: 0.8;
|
|
3360
|
+
}
|
|
3361
|
+
.snapshot-footer-input {
|
|
3362
|
+
flex-grow: 1;
|
|
3363
|
+
}
|
|
3364
|
+
body.dark .snapshot-footer-input input {
|
|
3365
|
+
background: rgba(255,255,255,0.07);
|
|
3366
|
+
}
|
|
3367
|
+
.snapshot-footer-input input {
|
|
3368
|
+
background: rgba(0,0,0,0.07);
|
|
3369
|
+
width: 100%;
|
|
3370
|
+
padding: 8px;
|
|
3371
|
+
border-radius: 2px;
|
|
3372
|
+
border: none;
|
|
3373
|
+
font-size: 12px;
|
|
3374
|
+
}
|
|
3375
|
+
.snapshot-footer-input input::placeholder {
|
|
3376
|
+
opacity: 0.7;
|
|
3231
3377
|
}
|
|
3232
3378
|
</style>
|
|
3233
3379
|
<link href="/app.css" rel="stylesheet"/>
|
|
@@ -3304,9 +3450,17 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3304
3450
|
<% if (type !== 'files') { %>
|
|
3305
3451
|
<aside class='active'>
|
|
3306
3452
|
<div class='menu-container'>
|
|
3307
|
-
|
|
3308
|
-
<
|
|
3309
|
-
|
|
3453
|
+
<div class='config-info'>
|
|
3454
|
+
<button type='button' id='layout-toggle'>
|
|
3455
|
+
<i class="fa-solid fa-bars"></i>
|
|
3456
|
+
</button>
|
|
3457
|
+
<% if (config.icon) { %>
|
|
3458
|
+
<img class='meta-icon' src="<%=config.icon%>"/>
|
|
3459
|
+
<% } %>
|
|
3460
|
+
<% if (config.title) { %>
|
|
3461
|
+
<div><%=config.title%></div>
|
|
3462
|
+
<% } %>
|
|
3463
|
+
</div>
|
|
3310
3464
|
<div class='m n system' data-type="n">
|
|
3311
3465
|
<%if (type==='browse') { %>
|
|
3312
3466
|
<a id='devtab' data-mode="refresh" target="<%=dev_link%>" href="<%=dev_link%>" class="btn frame-link selected" data-index="10">
|
|
@@ -3356,6 +3510,7 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3356
3510
|
</div>
|
|
3357
3511
|
</div>
|
|
3358
3512
|
</aside>
|
|
3513
|
+
<div class='appcanvas-resizer' id="appcanvas-resizer" role="separator" aria-orientation="vertical" aria-valuemin="140" aria-valuemax="560" aria-valuenow="150" tabindex="0" aria-label="Resize navigation sidebar"></div>
|
|
3359
3514
|
<% } %>
|
|
3360
3515
|
<% if (type === "run") { %>
|
|
3361
3516
|
<div class='appcanvas_filler'></div>
|
|
@@ -3375,6 +3530,7 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3375
3530
|
</div>
|
|
3376
3531
|
</div>
|
|
3377
3532
|
-->
|
|
3533
|
+
<span class="disk-usage tab-metric__value" data-path="/">--</span>
|
|
3378
3534
|
<div class='fs-status-dropdown fs-open-explorer'>
|
|
3379
3535
|
<button class='fs-status-btn' data-filepath="<%=path%>" type='button'>
|
|
3380
3536
|
<span class='fs-status-label'>
|
|
@@ -3419,11 +3575,6 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3419
3575
|
</div>
|
|
3420
3576
|
<% } else if (type === 'files') { %>
|
|
3421
3577
|
<div id='fs-status' data-workspace="<%=name%>" data-create-uri="<%=git_create_url%>" data-history-uri="<%=git_history_url%>" data-status-uri="<%=git_status_url%>" data-uri="<%=git_monitor_url%>" data-push-uri="<%=git_push_url%>" data-fork-uri="<%=git_fork_url%>">
|
|
3422
|
-
<div class="app-info" title="<%=config.title || name%>">
|
|
3423
|
-
<div class="app-info-card">
|
|
3424
|
-
<img src="<%= config.icon %>" onerror="this.onerror=null; this.src='/pinokio-black.png'" alt="Project icon">
|
|
3425
|
-
</div>
|
|
3426
|
-
</div>
|
|
3427
3578
|
<!--
|
|
3428
3579
|
<div class='fs-status-dropdown nested-menu git blue'>
|
|
3429
3580
|
<button type='button' class='fs-status-btn frame-link reveal'>
|
|
@@ -3436,6 +3587,7 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3436
3587
|
</div>
|
|
3437
3588
|
</div>
|
|
3438
3589
|
-->
|
|
3590
|
+
<span class="disk-usage tab-metric__value" data-path="/">--</span>
|
|
3439
3591
|
<div class='fs-status-dropdown fs-open-explorer'>
|
|
3440
3592
|
<button class='fs-status-btn' data-filepath="<%=path%>" type='button'>
|
|
3441
3593
|
<span class='fs-status-label'>
|
|
@@ -3477,6 +3629,7 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3477
3629
|
</button>
|
|
3478
3630
|
<div class='fs-dropdown-menu submenu hidden' id='fs-push-menu'></div>
|
|
3479
3631
|
</div>
|
|
3632
|
+
<div class='flexible'></div>
|
|
3480
3633
|
</div>
|
|
3481
3634
|
<% } %>
|
|
3482
3635
|
<main class='browserview'>
|
|
@@ -3484,6 +3637,31 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3484
3637
|
<iframe class='selected' src="<%=editor_tab%>"></iframe>
|
|
3485
3638
|
<% } %>
|
|
3486
3639
|
</main>
|
|
3640
|
+
<% const registryEnabled = typeof registryBetaEnabled === "undefined" ? false : registryBetaEnabled; %>
|
|
3641
|
+
<% const showSnapshotFooter = typeof hasSnapshots === "undefined" ? true : (!hasSnapshots || (registryEnabled && pendingSnapshotId)); %>
|
|
3642
|
+
<% if (showSnapshotFooter) { %>
|
|
3643
|
+
<div class='snapshot-footer' data-workspace="<%=name%>" data-pending-snapshot-id="<%= pendingSnapshotId ? pendingSnapshotId : '' %>" data-registry-beta-enabled="<%= registryEnabled ? '1' : '0' %>">
|
|
3644
|
+
<div class="snapshot-footer-save <%= registryEnabled && pendingSnapshotId ? 'hidden' : '' %>">
|
|
3645
|
+
<div class='caption' title="<%= registryEnabled ? 'Saves a checkpoint for this app (exact git commits). You can publish it after saving.' : 'Saves a checkpoint for this app (exact git commits).' %>">
|
|
3646
|
+
Save this exact state and restore later with 1-click
|
|
3647
|
+
</div>
|
|
3648
|
+
<div class="snapshot-footer-actions" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
|
|
3649
|
+
<button type="button" class="btn btn-primary snapshot-btn-save">
|
|
3650
|
+
<i class="fa-solid fa-circle-check"></i> Save checkpoint
|
|
3651
|
+
</button>
|
|
3652
|
+
</div>
|
|
3653
|
+
</div>
|
|
3654
|
+
<% if (registryEnabled) { %>
|
|
3655
|
+
<div class="snapshot-footer-publish <%= pendingSnapshotId ? '' : 'hidden' %>" aria-live="polite">
|
|
3656
|
+
<div class="snapshot-footer-publish-text">Local save success! Publish to the registry?</div>
|
|
3657
|
+
<div class="snapshot-footer-actions" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
|
|
3658
|
+
<button type="button" class="btn btn-primary snapshot-btn-publish"><i class="fa-solid fa-cloud-arrow-up"></i> Publish Checkpoint</button>
|
|
3659
|
+
<button type="button" class="btn snapshot-btn-later">Later</button>
|
|
3660
|
+
</div>
|
|
3661
|
+
</div>
|
|
3662
|
+
<% } %>
|
|
3663
|
+
</div>
|
|
3664
|
+
<% } %>
|
|
3487
3665
|
</div>
|
|
3488
3666
|
</div>
|
|
3489
3667
|
<script>
|
|
@@ -5085,7 +5263,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
5085
5263
|
Swal.close()
|
|
5086
5264
|
if (res) {
|
|
5087
5265
|
if (res.success) {
|
|
5088
|
-
location.href = "/"
|
|
5266
|
+
location.href = "/home"
|
|
5089
5267
|
} else if (res.error) {
|
|
5090
5268
|
alert(res.error)
|
|
5091
5269
|
}
|
|
@@ -5291,9 +5469,9 @@ const rerenderMenuSection = (container, html) => {
|
|
|
5291
5469
|
e.stopPropagation()
|
|
5292
5470
|
return
|
|
5293
5471
|
}
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5472
|
+
if (target.id === 'fs-changes-btn' || target.id === 'fs-push-btn' || target.id === 'fs-fork-btn') {
|
|
5473
|
+
check_git(true)
|
|
5474
|
+
}
|
|
5297
5475
|
e.preventDefault()
|
|
5298
5476
|
e.stopPropagation()
|
|
5299
5477
|
const group = target.getAttribute("data-group")
|
|
@@ -5594,6 +5772,178 @@ const rerenderMenuSection = (container, html) => {
|
|
|
5594
5772
|
})
|
|
5595
5773
|
}
|
|
5596
5774
|
})
|
|
5775
|
+
|
|
5776
|
+
const layoutToggleButton = document.querySelector(".config-info")
|
|
5777
|
+
if (layoutToggleButton) {
|
|
5778
|
+
const layoutToggleIcon = layoutToggleButton.querySelector("i.fa-bars")
|
|
5779
|
+
const appcanvas = document.querySelector(".appcanvas")
|
|
5780
|
+
const sidebarResizer = document.getElementById("appcanvas-resizer")
|
|
5781
|
+
const sidebarContainer = document.querySelector(".appcanvas > aside .menu-container")
|
|
5782
|
+
|
|
5783
|
+
if (appcanvas && typeof localStorage !== "undefined") {
|
|
5784
|
+
const storedLayout = localStorage.getItem("pinokioLayoutVertical")
|
|
5785
|
+
if (storedLayout === "1") {
|
|
5786
|
+
appcanvas.classList.add("vertical")
|
|
5787
|
+
if (layoutToggleIcon) {
|
|
5788
|
+
layoutToggleIcon.classList.add("fa-rotate-90")
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
}
|
|
5792
|
+
|
|
5793
|
+
layoutToggleButton.addEventListener("click", () => {
|
|
5794
|
+
if (!appcanvas) {
|
|
5795
|
+
return
|
|
5796
|
+
}
|
|
5797
|
+
const isVertical = appcanvas.classList.toggle("vertical")
|
|
5798
|
+
if (layoutToggleIcon) {
|
|
5799
|
+
if (isVertical) {
|
|
5800
|
+
layoutToggleIcon.classList.add("fa-rotate-90")
|
|
5801
|
+
} else {
|
|
5802
|
+
layoutToggleIcon.classList.remove("fa-rotate-90")
|
|
5803
|
+
}
|
|
5804
|
+
}
|
|
5805
|
+
if (typeof localStorage !== "undefined") {
|
|
5806
|
+
localStorage.setItem("pinokioLayoutVertical", isVertical ? "1" : "0")
|
|
5807
|
+
}
|
|
5808
|
+
})
|
|
5809
|
+
|
|
5810
|
+
if (appcanvas && sidebarResizer && sidebarContainer) {
|
|
5811
|
+
const SIDEBAR_MIN_WIDTH = 140
|
|
5812
|
+
const SIDEBAR_MAX_WIDTH = 560
|
|
5813
|
+
const workspaceName = "<%= typeof name === 'string' ? name : '' %>"
|
|
5814
|
+
const sidebarWidthStorageKey = workspaceName
|
|
5815
|
+
? `pinokio-app-sidebar-width:${workspaceName}`
|
|
5816
|
+
: "pinokio-app-sidebar-width"
|
|
5817
|
+
|
|
5818
|
+
let sidebarWidth = null
|
|
5819
|
+
let isResizing = false
|
|
5820
|
+
let pointerId = null
|
|
5821
|
+
let resizeState = null
|
|
5822
|
+
let handlePointerMove = null
|
|
5823
|
+
let handlePointerUp = null
|
|
5824
|
+
|
|
5825
|
+
const clampSidebarWidth = (value) => {
|
|
5826
|
+
const numeric = typeof value === "number" ? value : parseInt(value, 10)
|
|
5827
|
+
if (Number.isNaN(numeric)) {
|
|
5828
|
+
return SIDEBAR_MIN_WIDTH
|
|
5829
|
+
}
|
|
5830
|
+
return Math.min(Math.max(numeric, SIDEBAR_MIN_WIDTH), SIDEBAR_MAX_WIDTH)
|
|
5831
|
+
}
|
|
5832
|
+
|
|
5833
|
+
const readSidebarWidth = () => {
|
|
5834
|
+
try {
|
|
5835
|
+
const stored = window.localStorage.getItem(sidebarWidthStorageKey)
|
|
5836
|
+
if (!stored) {
|
|
5837
|
+
return null
|
|
5838
|
+
}
|
|
5839
|
+
const parsed = parseInt(stored, 10)
|
|
5840
|
+
if (Number.isNaN(parsed)) {
|
|
5841
|
+
return null
|
|
5842
|
+
}
|
|
5843
|
+
return parsed
|
|
5844
|
+
} catch (_) {
|
|
5845
|
+
return null
|
|
5846
|
+
}
|
|
5847
|
+
}
|
|
5848
|
+
|
|
5849
|
+
const persistSidebarWidth = (width) => {
|
|
5850
|
+
try {
|
|
5851
|
+
window.localStorage.setItem(sidebarWidthStorageKey, String(width))
|
|
5852
|
+
} catch (_) {}
|
|
5853
|
+
}
|
|
5854
|
+
|
|
5855
|
+
const applySidebarWidth = (value, persist) => {
|
|
5856
|
+
const width = clampSidebarWidth(value)
|
|
5857
|
+
sidebarWidth = width
|
|
5858
|
+
appcanvas.style.setProperty("--appcanvas-sidebar-width", `${width}px`)
|
|
5859
|
+
sidebarResizer.setAttribute("aria-valuenow", String(width))
|
|
5860
|
+
if (persist) {
|
|
5861
|
+
persistSidebarWidth(width)
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5864
|
+
|
|
5865
|
+
const initSidebarWidth = () => {
|
|
5866
|
+
const stored = readSidebarWidth()
|
|
5867
|
+
const base = typeof stored === "number"
|
|
5868
|
+
? stored
|
|
5869
|
+
: parseInt(getComputedStyle(appcanvas).getPropertyValue("--appcanvas-sidebar-width"), 10) || 150
|
|
5870
|
+
applySidebarWidth(base, false)
|
|
5871
|
+
}
|
|
5872
|
+
|
|
5873
|
+
const finishResizing = () => {
|
|
5874
|
+
if (!isResizing) {
|
|
5875
|
+
return
|
|
5876
|
+
}
|
|
5877
|
+
isResizing = false
|
|
5878
|
+
if (pointerId != null && sidebarResizer) {
|
|
5879
|
+
try {
|
|
5880
|
+
sidebarResizer.releasePointerCapture(pointerId)
|
|
5881
|
+
} catch (_) {}
|
|
5882
|
+
}
|
|
5883
|
+
pointerId = null
|
|
5884
|
+
resizeState = null
|
|
5885
|
+
window.removeEventListener("pointermove", handlePointerMove)
|
|
5886
|
+
window.removeEventListener("pointerup", handlePointerUp)
|
|
5887
|
+
if (sidebarWidth != null) {
|
|
5888
|
+
persistSidebarWidth(sidebarWidth)
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
|
|
5892
|
+
const bindResizeListeners = () => {
|
|
5893
|
+
if (!handlePointerMove) {
|
|
5894
|
+
handlePointerMove = (event) => {
|
|
5895
|
+
if (!isResizing || !appcanvas.classList.contains("vertical")) {
|
|
5896
|
+
return
|
|
5897
|
+
}
|
|
5898
|
+
if (pointerId != null && event.pointerId !== pointerId) {
|
|
5899
|
+
return
|
|
5900
|
+
}
|
|
5901
|
+
event.preventDefault()
|
|
5902
|
+
const state = resizeState || {}
|
|
5903
|
+
const baseLeft = typeof state.left === "number"
|
|
5904
|
+
? state.left
|
|
5905
|
+
: sidebarContainer.getBoundingClientRect().left
|
|
5906
|
+
const offset = typeof state.offset === "number" ? state.offset : 0
|
|
5907
|
+
const nextWidth = event.clientX - baseLeft - offset
|
|
5908
|
+
applySidebarWidth(nextWidth, false)
|
|
5909
|
+
}
|
|
5910
|
+
}
|
|
5911
|
+
if (!handlePointerUp) {
|
|
5912
|
+
handlePointerUp = (event) => {
|
|
5913
|
+
if (pointerId != null && event.pointerId !== pointerId) {
|
|
5914
|
+
return
|
|
5915
|
+
}
|
|
5916
|
+
finishResizing()
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
window.addEventListener("pointermove", handlePointerMove)
|
|
5920
|
+
window.addEventListener("pointerup", handlePointerUp, { once: true })
|
|
5921
|
+
}
|
|
5922
|
+
|
|
5923
|
+
sidebarResizer.addEventListener("pointerdown", (event) => {
|
|
5924
|
+
if (!appcanvas.classList.contains("vertical")) {
|
|
5925
|
+
return
|
|
5926
|
+
}
|
|
5927
|
+
if (event.button !== 0 && event.pointerType !== "touch") {
|
|
5928
|
+
return
|
|
5929
|
+
}
|
|
5930
|
+
event.preventDefault()
|
|
5931
|
+
isResizing = true
|
|
5932
|
+
pointerId = event.pointerId
|
|
5933
|
+
try {
|
|
5934
|
+
sidebarResizer.setPointerCapture(event.pointerId)
|
|
5935
|
+
} catch (_) {}
|
|
5936
|
+
const rect = sidebarContainer.getBoundingClientRect()
|
|
5937
|
+
resizeState = {
|
|
5938
|
+
left: rect.left,
|
|
5939
|
+
offset: event.clientX - (rect.left + rect.width)
|
|
5940
|
+
}
|
|
5941
|
+
bindResizeListeners()
|
|
5942
|
+
})
|
|
5943
|
+
|
|
5944
|
+
initSidebarWidth()
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5597
5947
|
setupTabLinkHover()
|
|
5598
5948
|
document.addEventListener("click", (event) => {
|
|
5599
5949
|
if (event.target.closest("#fs-status .fs-dropdown-menu") || event.target.closest("#fs-status .fs-status-btn")) {
|
|
@@ -5654,7 +6004,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
5654
6004
|
// KB
|
|
5655
6005
|
val = `${Math.floor(res.du/k * 100) / 100} KB`
|
|
5656
6006
|
}
|
|
5657
|
-
el.innerHTML = val
|
|
6007
|
+
el.innerHTML = `<i class="fa-solid fa-database"></i> ${val}`
|
|
5658
6008
|
} else {
|
|
5659
6009
|
}
|
|
5660
6010
|
})
|
|
@@ -5854,6 +6204,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
5854
6204
|
let gitCommitUrl = null
|
|
5855
6205
|
let activeRepoKey = null
|
|
5856
6206
|
let gitStatusRequest = null
|
|
6207
|
+
let gitStatusRequestForce = false
|
|
5857
6208
|
|
|
5858
6209
|
const fsStatusEl = document.querySelector('#fs-status')
|
|
5859
6210
|
const changesDropdownContainer = document.querySelector('#fs-status .git-changes')
|
|
@@ -5885,6 +6236,15 @@ const rerenderMenuSection = (container, html) => {
|
|
|
5885
6236
|
const historyUri = readDataAttr(fsStatusEl, 'data-history-uri')
|
|
5886
6237
|
const defaultPushUri = readDataAttr(fsStatusEl, 'data-push-uri')
|
|
5887
6238
|
const defaultForkUri = readDataAttr(fsStatusEl, 'data-fork-uri')
|
|
6239
|
+
const buildStatusUrl = (forceRefresh = false) => {
|
|
6240
|
+
if (!statusUri) {
|
|
6241
|
+
return null
|
|
6242
|
+
}
|
|
6243
|
+
if (!forceRefresh) {
|
|
6244
|
+
return statusUri
|
|
6245
|
+
}
|
|
6246
|
+
return statusUri.includes('?') ? `${statusUri}&force=1` : `${statusUri}?force=1`
|
|
6247
|
+
}
|
|
5888
6248
|
|
|
5889
6249
|
const buildWorkspaceLogsUrl = () => {
|
|
5890
6250
|
if (workspaceName && workspaceName.length > 0) {
|
|
@@ -6348,12 +6708,22 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6348
6708
|
}
|
|
6349
6709
|
let repoData = repoStatusCache.get(repoParam) || null
|
|
6350
6710
|
if (!repoData && typeof check_git === 'function') {
|
|
6351
|
-
await check_git()
|
|
6711
|
+
await check_git(true)
|
|
6352
6712
|
repoData = repoStatusCache.get(repoParam) || null
|
|
6353
6713
|
}
|
|
6354
6714
|
return repoData
|
|
6355
6715
|
}
|
|
6356
6716
|
|
|
6717
|
+
async function refreshRepoData(repoParam) {
|
|
6718
|
+
if (!repoParam) {
|
|
6719
|
+
return null
|
|
6720
|
+
}
|
|
6721
|
+
if (typeof check_git === 'function') {
|
|
6722
|
+
await check_git(true)
|
|
6723
|
+
}
|
|
6724
|
+
return repoStatusCache.get(repoParam) || null
|
|
6725
|
+
}
|
|
6726
|
+
|
|
6357
6727
|
const getRepoChangeCount = (repo) => {
|
|
6358
6728
|
if (!repo) {
|
|
6359
6729
|
return 0
|
|
@@ -6369,8 +6739,11 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6369
6739
|
|
|
6370
6740
|
async function ensureRemoteForPublish({ repoParam, repoName }) {
|
|
6371
6741
|
const label = repoName || repoParam || 'Repository'
|
|
6372
|
-
|
|
6373
|
-
if (hasRemoteConfigured(
|
|
6742
|
+
let repoCandidate = await getRepoData(repoParam)
|
|
6743
|
+
if (!hasRemoteConfigured(repoCandidate)) {
|
|
6744
|
+
repoCandidate = await refreshRepoData(repoParam)
|
|
6745
|
+
}
|
|
6746
|
+
if (hasRemoteConfigured(repoCandidate)) {
|
|
6374
6747
|
return { remoteReady: true, shouldOpenPublish: true }
|
|
6375
6748
|
}
|
|
6376
6749
|
|
|
@@ -6381,7 +6754,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6381
6754
|
}
|
|
6382
6755
|
|
|
6383
6756
|
if (typeof check_git === 'function') {
|
|
6384
|
-
await check_git()
|
|
6757
|
+
await check_git(true)
|
|
6385
6758
|
}
|
|
6386
6759
|
const refreshedRepo = await getRepoData(repoParam)
|
|
6387
6760
|
if (hasRemoteConfigured(refreshedRepo)) {
|
|
@@ -6468,7 +6841,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6468
6841
|
console.error('Failed to open commit modal before publish:', error)
|
|
6469
6842
|
}
|
|
6470
6843
|
if (typeof check_git === 'function') {
|
|
6471
|
-
await check_git()
|
|
6844
|
+
await check_git(true)
|
|
6472
6845
|
}
|
|
6473
6846
|
return { proceed: true, action: 'commit', changeCount }
|
|
6474
6847
|
}
|
|
@@ -6544,6 +6917,11 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6544
6917
|
}
|
|
6545
6918
|
|
|
6546
6919
|
const runPublishFlow = async ({ repoParam, repoName }) => {
|
|
6920
|
+
if (typeof check_git === 'function') {
|
|
6921
|
+
try {
|
|
6922
|
+
await check_git(true)
|
|
6923
|
+
} catch (_) {}
|
|
6924
|
+
}
|
|
6547
6925
|
let preflight = await fetchPublishPreflightState(repoParam)
|
|
6548
6926
|
if (!preflight.ok) {
|
|
6549
6927
|
handlePreflightFailure({ preflight, repoParam, repoName })
|
|
@@ -6783,24 +7161,29 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6783
7161
|
}
|
|
6784
7162
|
}
|
|
6785
7163
|
|
|
6786
|
-
const check_git = async () => {
|
|
7164
|
+
const check_git = async (forceRefresh = false) => {
|
|
6787
7165
|
if (gitStatusRequest) {
|
|
7166
|
+
if (forceRefresh && !gitStatusRequestForce) {
|
|
7167
|
+
return gitStatusRequest.then(() => check_git(true))
|
|
7168
|
+
}
|
|
6788
7169
|
return gitStatusRequest
|
|
6789
7170
|
}
|
|
6790
7171
|
|
|
7172
|
+
const targetStatusUrl = buildStatusUrl(forceRefresh)
|
|
7173
|
+
gitStatusRequestForce = Boolean(forceRefresh)
|
|
6791
7174
|
gitStatusRequest = (async () => {
|
|
6792
7175
|
if (repoStatusCache.size === 0) {
|
|
6793
7176
|
setChangesMenuMessage(statusUri ? 'Loading repositories...' : 'Loading changes...')
|
|
6794
7177
|
updateChangesButtonState(false)
|
|
6795
7178
|
}
|
|
6796
7179
|
|
|
6797
|
-
if (!
|
|
7180
|
+
if (!targetStatusUrl) {
|
|
6798
7181
|
await updateFromLegacyMonitor()
|
|
6799
7182
|
return
|
|
6800
7183
|
}
|
|
6801
7184
|
|
|
6802
7185
|
try {
|
|
6803
|
-
const response = await fetch(
|
|
7186
|
+
const response = await fetch(targetStatusUrl)
|
|
6804
7187
|
if (!response.ok) {
|
|
6805
7188
|
throw new Error(`HTTP ${response.status}`)
|
|
6806
7189
|
}
|
|
@@ -6857,6 +7240,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6857
7240
|
await gitStatusRequest
|
|
6858
7241
|
} finally {
|
|
6859
7242
|
gitStatusRequest = null
|
|
7243
|
+
gitStatusRequestForce = false
|
|
6860
7244
|
}
|
|
6861
7245
|
}
|
|
6862
7246
|
|
|
@@ -6922,7 +7306,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6922
7306
|
typeof event.data.callback === 'string' &&
|
|
6923
7307
|
event.data.callback.startsWith('$')) {
|
|
6924
7308
|
if (typeof check_git === 'function') {
|
|
6925
|
-
check_git()
|
|
7309
|
+
check_git(true)
|
|
6926
7310
|
}
|
|
6927
7311
|
if (reopenDiffOnCallback && typeof showGitDiffModal === 'function') {
|
|
6928
7312
|
reopenDiffOnCallback = false
|
|
@@ -7029,7 +7413,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
7029
7413
|
}
|
|
7030
7414
|
|
|
7031
7415
|
if (!repoParam) {
|
|
7032
|
-
await check_git()
|
|
7416
|
+
await check_git(true)
|
|
7033
7417
|
const fallback = repoStatusCache.values().next().value
|
|
7034
7418
|
if (fallback) {
|
|
7035
7419
|
repoParam = fallback.repoParam
|
|
@@ -7048,7 +7432,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
7048
7432
|
|
|
7049
7433
|
let repoData = repoStatusCache.get(repoParam)
|
|
7050
7434
|
if (!repoData) {
|
|
7051
|
-
await check_git()
|
|
7435
|
+
await check_git(true)
|
|
7052
7436
|
repoData = repoStatusCache.get(repoParam)
|
|
7053
7437
|
}
|
|
7054
7438
|
|
|
@@ -8626,7 +9010,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
8626
9010
|
|
|
8627
9011
|
let repo = repoStatusCache.get(key)
|
|
8628
9012
|
if (!repo) {
|
|
8629
|
-
await check_git()
|
|
9013
|
+
await check_git(true)
|
|
8630
9014
|
repo = repoStatusCache.get(key)
|
|
8631
9015
|
}
|
|
8632
9016
|
|
|
@@ -8864,7 +9248,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
8864
9248
|
forkShellModalPromise.then(() => {
|
|
8865
9249
|
setTimeout(() => {
|
|
8866
9250
|
updateForkButton()
|
|
8867
|
-
check_git()
|
|
9251
|
+
check_git(true)
|
|
8868
9252
|
}, 250)
|
|
8869
9253
|
})
|
|
8870
9254
|
}
|
|
@@ -9033,6 +9417,39 @@ const rerenderMenuSection = (container, html) => {
|
|
|
9033
9417
|
// Initialize the publish/create button
|
|
9034
9418
|
updatePublishButton()
|
|
9035
9419
|
|
|
9420
|
+
// If this page was opened explicitly to publish from the Backups screen,
|
|
9421
|
+
// auto-open the publish flow after the button has been initialized.
|
|
9422
|
+
try {
|
|
9423
|
+
const searchParams = new URLSearchParams(window.location.search || "")
|
|
9424
|
+
if (searchParams.get("pinokio.publish") === "1" && pushBtn) {
|
|
9425
|
+
setTimeout(() => {
|
|
9426
|
+
try {
|
|
9427
|
+
pushBtn.click()
|
|
9428
|
+
setTimeout(() => {
|
|
9429
|
+
try {
|
|
9430
|
+
const menu = document.getElementById("fs-push-menu")
|
|
9431
|
+
if (!menu) return
|
|
9432
|
+
const items = menu.querySelectorAll(".pinokio-publish-dropdown-item")
|
|
9433
|
+
const target = Array.from(items).find((el) => {
|
|
9434
|
+
const dataName = (el.getAttribute("data-name") || "").trim()
|
|
9435
|
+
return dataName === workspaceName || dataName === (workspaceName + ".git")
|
|
9436
|
+
}) || items[0]
|
|
9437
|
+
if (target) {
|
|
9438
|
+
target.click()
|
|
9439
|
+
}
|
|
9440
|
+
} catch (_) {}
|
|
9441
|
+
}, 300)
|
|
9442
|
+
try {
|
|
9443
|
+
searchParams.delete("pinokio.publish")
|
|
9444
|
+
const qs = searchParams.toString()
|
|
9445
|
+
const newUrl = window.location.pathname + (qs ? `?${qs}` : "") + window.location.hash
|
|
9446
|
+
window.history.replaceState(null, "", newUrl)
|
|
9447
|
+
} catch (_) {}
|
|
9448
|
+
} catch (_) {}
|
|
9449
|
+
}, 500)
|
|
9450
|
+
}
|
|
9451
|
+
} catch (_) {}
|
|
9452
|
+
|
|
9036
9453
|
check_git()
|
|
9037
9454
|
setInterval(() => {
|
|
9038
9455
|
if (typeof check_git === 'function') {
|
|
@@ -9228,5 +9645,356 @@ const rerenderMenuSection = (container, html) => {
|
|
|
9228
9645
|
})()
|
|
9229
9646
|
</script>
|
|
9230
9647
|
<script src="/tab-idle-notifier.js"></script>
|
|
9648
|
+
<script>
|
|
9649
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
9650
|
+
const footer = document.querySelector(".snapshot-footer")
|
|
9651
|
+
if (!footer) return
|
|
9652
|
+
const saveBtn = footer.querySelector(".snapshot-btn-save")
|
|
9653
|
+
const savePanel = footer.querySelector(".snapshot-footer-save")
|
|
9654
|
+
const publishPanel = footer.querySelector(".snapshot-footer-publish")
|
|
9655
|
+
const publishBtn = footer.querySelector(".snapshot-btn-publish")
|
|
9656
|
+
const laterBtn = footer.querySelector(".snapshot-btn-later")
|
|
9657
|
+
const publishText = publishPanel ? publishPanel.querySelector(".snapshot-footer-publish-text") : null
|
|
9658
|
+
const publishActions = publishPanel ? publishPanel.querySelector(".snapshot-footer-actions") : null
|
|
9659
|
+
if (!saveBtn) return
|
|
9660
|
+
const registryBetaEnabled = footer.getAttribute("data-registry-beta-enabled") === "1"
|
|
9661
|
+
const workspace = footer.getAttribute("data-workspace")
|
|
9662
|
+
if (!workspace) {
|
|
9663
|
+
footer.classList.add("hidden")
|
|
9664
|
+
return
|
|
9665
|
+
}
|
|
9666
|
+
let savedSnapshotId = null
|
|
9667
|
+
const saveOriginal = saveBtn.innerHTML
|
|
9668
|
+
const publishOriginal = publishBtn ? publishBtn.innerHTML : ""
|
|
9669
|
+
const setSaveLoading = (isLoading, primaryText) => {
|
|
9670
|
+
saveBtn.disabled = isLoading
|
|
9671
|
+
if (primaryText) {
|
|
9672
|
+
saveBtn.innerHTML = primaryText
|
|
9673
|
+
}
|
|
9674
|
+
}
|
|
9675
|
+
const setPublishLoading = (isLoading, primaryText) => {
|
|
9676
|
+
if (publishBtn) publishBtn.disabled = isLoading
|
|
9677
|
+
if (laterBtn) laterBtn.disabled = isLoading
|
|
9678
|
+
if (primaryText && publishBtn) {
|
|
9679
|
+
publishBtn.innerHTML = primaryText
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9682
|
+
const showPublishPanel = () => {
|
|
9683
|
+
if (!registryBetaEnabled) {
|
|
9684
|
+
footer.classList.add("hidden")
|
|
9685
|
+
return
|
|
9686
|
+
}
|
|
9687
|
+
if (publishPanel) publishPanel.classList.remove("hidden")
|
|
9688
|
+
if (savePanel) savePanel.classList.add("hidden")
|
|
9689
|
+
}
|
|
9690
|
+
const pendingSnapshotId = footer.getAttribute("data-pending-snapshot-id")
|
|
9691
|
+
if (pendingSnapshotId) {
|
|
9692
|
+
savedSnapshotId = pendingSnapshotId
|
|
9693
|
+
showPublishPanel()
|
|
9694
|
+
}
|
|
9695
|
+
|
|
9696
|
+
const waitForRegistryLink = async () => {
|
|
9697
|
+
const startedAt = Date.now()
|
|
9698
|
+
while (Date.now() - startedAt < 120000) {
|
|
9699
|
+
try {
|
|
9700
|
+
const s = await fetch("/api/registry/status", { method: "GET", headers: { "Accept": "application/json" } })
|
|
9701
|
+
if (s.ok) {
|
|
9702
|
+
const data = await s.json()
|
|
9703
|
+
if (data && data.linked) return true
|
|
9704
|
+
}
|
|
9705
|
+
} catch (_) {}
|
|
9706
|
+
await new Promise((r) => setTimeout(r, 2000))
|
|
9707
|
+
}
|
|
9708
|
+
return false
|
|
9709
|
+
}
|
|
9710
|
+
|
|
9711
|
+
const publishSnapshot = async (snapshotId) => {
|
|
9712
|
+
const qs = new URLSearchParams()
|
|
9713
|
+
qs.set("snapshotId", String(snapshotId))
|
|
9714
|
+
const res = await fetch(`/checkpoints/publish?${qs.toString()}`, { method: "POST", headers: { "Accept": "application/json" } })
|
|
9715
|
+
if (!res.ok) return { ok: false }
|
|
9716
|
+
try { return await res.json() } catch (_) { return { ok: false } }
|
|
9717
|
+
}
|
|
9718
|
+
|
|
9719
|
+
let isSaving = false
|
|
9720
|
+
let isPublishing = false
|
|
9721
|
+
const showPublishedLink = (publishUrl) => {
|
|
9722
|
+
showPublishPanel()
|
|
9723
|
+
if (publishText) publishText.textContent = "Published."
|
|
9724
|
+
if (publishActions) {
|
|
9725
|
+
publishActions.innerHTML = publishUrl
|
|
9726
|
+
? `<a class="btn btn-primary" href="${escapePublishUrl(publishUrl)}" target="_blank" rel="noopener"><i class="fa-solid fa-arrow-up-right-from-square"></i> View published checkpoint</a>`
|
|
9727
|
+
: ''
|
|
9728
|
+
}
|
|
9729
|
+
}
|
|
9730
|
+
const escapePublishUrl = (value) => {
|
|
9731
|
+
if (value === null || value === undefined) {
|
|
9732
|
+
return ''
|
|
9733
|
+
}
|
|
9734
|
+
return String(value).replace(/[&<>"']/g, (match) => {
|
|
9735
|
+
switch (match) {
|
|
9736
|
+
case '&':
|
|
9737
|
+
return '&'
|
|
9738
|
+
case '<':
|
|
9739
|
+
return '<'
|
|
9740
|
+
case '>':
|
|
9741
|
+
return '>'
|
|
9742
|
+
case '"':
|
|
9743
|
+
return '"'
|
|
9744
|
+
case '\'':
|
|
9745
|
+
return '''
|
|
9746
|
+
default:
|
|
9747
|
+
return match
|
|
9748
|
+
}
|
|
9749
|
+
})
|
|
9750
|
+
}
|
|
9751
|
+
|
|
9752
|
+
const promptRegistryConnect = async (suggestedConnectUrl) => {
|
|
9753
|
+
const connectHtml = `
|
|
9754
|
+
<div class="pinokio-modal-surface">
|
|
9755
|
+
<div class="pinokio-modal-header">
|
|
9756
|
+
<div class="pinokio-modal-icon"><i class="fa-solid fa-link"></i></div>
|
|
9757
|
+
<div class="pinokio-modal-heading">
|
|
9758
|
+
<div class="pinokio-modal-title">Connect to Registry</div>
|
|
9759
|
+
<div class="pinokio-modal-subtitle">
|
|
9760
|
+
Enter your registry connect URL to link Pinokio, then we’ll retry the publish.
|
|
9761
|
+
</div>
|
|
9762
|
+
</div>
|
|
9763
|
+
</div>
|
|
9764
|
+
<div class="pinokio-modal-body">
|
|
9765
|
+
<div class="pinokio-modal-form">
|
|
9766
|
+
<div>
|
|
9767
|
+
<label class="pinokio-modal-label" for="pinokio-registry-connect-url">Registry URL</label>
|
|
9768
|
+
<input
|
|
9769
|
+
id="pinokio-registry-connect-url"
|
|
9770
|
+
class="pinokio-modal-input"
|
|
9771
|
+
type="url"
|
|
9772
|
+
placeholder="https://your-registry/connect/pinokio"
|
|
9773
|
+
autocomplete="off"
|
|
9774
|
+
/>
|
|
9775
|
+
</div>
|
|
9776
|
+
</div>
|
|
9777
|
+
</div>
|
|
9778
|
+
</div>
|
|
9779
|
+
`
|
|
9780
|
+
const choice = await Swal.fire({
|
|
9781
|
+
html: connectHtml,
|
|
9782
|
+
backdrop: 'rgba(9,11,15,0.65)',
|
|
9783
|
+
width: 'min(520px, 92vw)',
|
|
9784
|
+
showCancelButton: true,
|
|
9785
|
+
showConfirmButton: true,
|
|
9786
|
+
confirmButtonText: "Open connect page",
|
|
9787
|
+
cancelButtonText: "Cancel",
|
|
9788
|
+
buttonsStyling: false,
|
|
9789
|
+
focusConfirm: false,
|
|
9790
|
+
customClass: {
|
|
9791
|
+
popup: 'pinokio-modern-modal',
|
|
9792
|
+
htmlContainer: 'pinokio-modern-html',
|
|
9793
|
+
closeButton: 'pinokio-modern-close',
|
|
9794
|
+
confirmButton: 'pinokio-modern-confirm',
|
|
9795
|
+
cancelButton: 'pinokio-modern-cancel'
|
|
9796
|
+
},
|
|
9797
|
+
didOpen: () => {
|
|
9798
|
+
const input = document.getElementById("pinokio-registry-connect-url")
|
|
9799
|
+
if (input) {
|
|
9800
|
+
input.value = suggestedConnectUrl || ""
|
|
9801
|
+
try { input.focus() } catch (_) {}
|
|
9802
|
+
try { input.select() } catch (_) {}
|
|
9803
|
+
}
|
|
9804
|
+
},
|
|
9805
|
+
preConfirm: () => {
|
|
9806
|
+
const input = document.getElementById("pinokio-registry-connect-url")
|
|
9807
|
+
const v = input && input.value != null ? String(input.value).trim() : ""
|
|
9808
|
+
if (!v) {
|
|
9809
|
+
Swal.showValidationMessage("Registry URL is required")
|
|
9810
|
+
return false
|
|
9811
|
+
}
|
|
9812
|
+
return v
|
|
9813
|
+
}
|
|
9814
|
+
})
|
|
9815
|
+
if (!choice || !choice.isConfirmed) return false
|
|
9816
|
+
const connectUrl = choice.value ? String(choice.value).trim() : ""
|
|
9817
|
+
if (!connectUrl) return false
|
|
9818
|
+
try {
|
|
9819
|
+
window.open(connectUrl, "pinokio-registry-connect")
|
|
9820
|
+
} catch (_) {
|
|
9821
|
+
window.location.href = connectUrl
|
|
9822
|
+
}
|
|
9823
|
+
const linked = await waitForRegistryLink()
|
|
9824
|
+
if (!linked) {
|
|
9825
|
+
Swal.fire({ icon: "error", title: "Not connected", text: "Could not confirm the registry connection." })
|
|
9826
|
+
return false
|
|
9827
|
+
}
|
|
9828
|
+
return true
|
|
9829
|
+
}
|
|
9830
|
+
|
|
9831
|
+
const handleSave = async (event) => {
|
|
9832
|
+
event.preventDefault()
|
|
9833
|
+
if (isSaving) return
|
|
9834
|
+
isSaving = true
|
|
9835
|
+
setSaveLoading(true, `<i class="fa-solid fa-circle-notch fa-spin"></i> Saving...`)
|
|
9836
|
+
try {
|
|
9837
|
+
const infoRes = await fetch(`/info/api/${encodeURIComponent(workspace)}`, {
|
|
9838
|
+
method: "GET",
|
|
9839
|
+
headers: { "Accept": "application/json" }
|
|
9840
|
+
})
|
|
9841
|
+
let repos = []
|
|
9842
|
+
if (infoRes.ok) {
|
|
9843
|
+
try {
|
|
9844
|
+
const payload = await infoRes.json()
|
|
9845
|
+
repos = Array.isArray(payload && payload.repos) ? payload.repos : []
|
|
9846
|
+
} catch (_) {}
|
|
9847
|
+
}
|
|
9848
|
+
const hasMainWithRemote = repos.some((repo) => repo && repo.main && repo.url)
|
|
9849
|
+
if (!hasMainWithRemote) {
|
|
9850
|
+
setSaveLoading(false, saveOriginal)
|
|
9851
|
+
const modalHtml = `
|
|
9852
|
+
<div class="pinokio-modal-surface">
|
|
9853
|
+
<div class="pinokio-modal-header">
|
|
9854
|
+
<div class="pinokio-modal-icon"><i class="fa-brands fa-github"></i></div>
|
|
9855
|
+
<div class="pinokio-modal-heading">
|
|
9856
|
+
<div class="pinokio-modal-title">Publish this workspace first</div>
|
|
9857
|
+
<div class="pinokio-modal-subtitle">
|
|
9858
|
+
Backups are indexed by git remote. Publish this workspace to GitHub (or add a remote) before creating a snapshot.
|
|
9859
|
+
</div>
|
|
9860
|
+
</div>
|
|
9861
|
+
</div>
|
|
9862
|
+
<div class="pinokio-modal-body">
|
|
9863
|
+
<p class="pinokio-modal-text">
|
|
9864
|
+
We couldn't find a Git remote for this workspace. Once you publish it, you'll be able to create snapshots and restore them from the Backups page.
|
|
9865
|
+
</p>
|
|
9866
|
+
</div>
|
|
9867
|
+
</div>
|
|
9868
|
+
`
|
|
9869
|
+
Swal.fire({
|
|
9870
|
+
html: modalHtml,
|
|
9871
|
+
customClass: {
|
|
9872
|
+
popup: 'pinokio-modern-modal',
|
|
9873
|
+
htmlContainer: 'pinokio-modern-html',
|
|
9874
|
+
closeButton: 'pinokio-modern-close',
|
|
9875
|
+
confirmButton: 'pinokio-modern-confirm',
|
|
9876
|
+
cancelButton: 'pinokio-modern-cancel'
|
|
9877
|
+
},
|
|
9878
|
+
backdrop: 'rgba(9,11,15,0.65)',
|
|
9879
|
+
width: 'min(520px, 90vw)',
|
|
9880
|
+
buttonsStyling: false,
|
|
9881
|
+
showCloseButton: false,
|
|
9882
|
+
showConfirmButton: true,
|
|
9883
|
+
showCancelButton: true,
|
|
9884
|
+
confirmButtonText: 'Open Publish',
|
|
9885
|
+
cancelButtonText: 'Cancel',
|
|
9886
|
+
focusConfirm: false,
|
|
9887
|
+
}).then((result) => {
|
|
9888
|
+
if (result && result.isConfirmed) {
|
|
9889
|
+
const params = new URLSearchParams(window.location.search || "")
|
|
9890
|
+
params.set("pinokio.publish", "1")
|
|
9891
|
+
const qs = params.toString()
|
|
9892
|
+
const url = `/p/${encodeURIComponent(workspace)}/dev${qs ? `?${qs}` : ""}`
|
|
9893
|
+
window.location.href = url
|
|
9894
|
+
}
|
|
9895
|
+
})
|
|
9896
|
+
return
|
|
9897
|
+
}
|
|
9898
|
+
const params = new URLSearchParams()
|
|
9899
|
+
params.set("workspace", workspace)
|
|
9900
|
+
const res = await fetch(`/checkpoints/snapshot?${params.toString()}`, {
|
|
9901
|
+
method: "POST",
|
|
9902
|
+
headers: { "Accept": "application/json" }
|
|
9903
|
+
})
|
|
9904
|
+
const payload = res.ok ? await res.json().catch(() => null) : null
|
|
9905
|
+
if (!payload || !payload.ok) {
|
|
9906
|
+
setSaveLoading(false, saveOriginal)
|
|
9907
|
+
Swal.fire({ icon: "error", title: "Error", text: "Save failed" })
|
|
9908
|
+
return
|
|
9909
|
+
}
|
|
9910
|
+
const snapshotId = payload && payload.created && payload.created.id ? payload.created.id : null
|
|
9911
|
+
if (!snapshotId) {
|
|
9912
|
+
setSaveLoading(false, saveOriginal)
|
|
9913
|
+
Swal.fire({ icon: "error", title: "Error", text: "Save failed" })
|
|
9914
|
+
return
|
|
9915
|
+
}
|
|
9916
|
+
savedSnapshotId = snapshotId
|
|
9917
|
+
setSaveLoading(false, saveOriginal)
|
|
9918
|
+
saveBtn.disabled = true
|
|
9919
|
+
showPublishPanel()
|
|
9920
|
+
} catch (_) {
|
|
9921
|
+
setSaveLoading(false, saveOriginal)
|
|
9922
|
+
Swal.fire({ icon: "error", title: "Error", text: "Save failed" })
|
|
9923
|
+
} finally {
|
|
9924
|
+
isSaving = false
|
|
9925
|
+
}
|
|
9926
|
+
}
|
|
9927
|
+
|
|
9928
|
+
const handlePublish = async (event) => {
|
|
9929
|
+
event.preventDefault()
|
|
9930
|
+
if (isPublishing || !savedSnapshotId) return
|
|
9931
|
+
isPublishing = true
|
|
9932
|
+
setPublishLoading(true, `<i class="fa-solid fa-circle-notch fa-spin"></i> Publishing...`)
|
|
9933
|
+
try {
|
|
9934
|
+
const published = await publishSnapshot(savedSnapshotId)
|
|
9935
|
+
if (published && published.publish && published.publish.ok) {
|
|
9936
|
+
const publishUrl = published && published.publish && published.publish.url ? String(published.publish.url) : ""
|
|
9937
|
+
showPublishedLink(publishUrl)
|
|
9938
|
+
return
|
|
9939
|
+
}
|
|
9940
|
+
if (published && published.publish && published.publish.code === "not_linked") {
|
|
9941
|
+
const suggestedConnectUrl = published.publish.connectUrl ? String(published.publish.connectUrl) : ""
|
|
9942
|
+
const linked = await promptRegistryConnect(suggestedConnectUrl)
|
|
9943
|
+
if (!linked) {
|
|
9944
|
+
setPublishLoading(false, publishOriginal)
|
|
9945
|
+
return
|
|
9946
|
+
}
|
|
9947
|
+
const retry = await publishSnapshot(savedSnapshotId)
|
|
9948
|
+
if (retry && retry.publish && retry.publish.ok) {
|
|
9949
|
+
footer.classList.add("hidden")
|
|
9950
|
+
return
|
|
9951
|
+
}
|
|
9952
|
+
const retryMsg = retry && retry.publish && retry.publish.error ? retry.publish.error : "Publish failed"
|
|
9953
|
+
Swal.fire({ icon: "error", title: "Error", text: retryMsg })
|
|
9954
|
+
setPublishLoading(false, publishOriginal)
|
|
9955
|
+
return
|
|
9956
|
+
}
|
|
9957
|
+
const msg = published && published.publish && published.publish.error ? published.publish.error : "Publish failed"
|
|
9958
|
+
Swal.fire({ icon: "error", title: "Error", text: msg })
|
|
9959
|
+
setPublishLoading(false, publishOriginal)
|
|
9960
|
+
} catch (_) {
|
|
9961
|
+
setPublishLoading(false, publishOriginal)
|
|
9962
|
+
Swal.fire({ icon: "error", title: "Error", text: "Publish failed" })
|
|
9963
|
+
} finally {
|
|
9964
|
+
isPublishing = false
|
|
9965
|
+
}
|
|
9966
|
+
}
|
|
9967
|
+
|
|
9968
|
+
const handleLater = async (event) => {
|
|
9969
|
+
event.preventDefault()
|
|
9970
|
+
if (!savedSnapshotId) {
|
|
9971
|
+
footer.classList.add("hidden")
|
|
9972
|
+
return
|
|
9973
|
+
}
|
|
9974
|
+
setPublishLoading(true)
|
|
9975
|
+
try {
|
|
9976
|
+
const qs = new URLSearchParams()
|
|
9977
|
+
qs.set("snapshotId", String(savedSnapshotId))
|
|
9978
|
+
qs.set("decision", "later")
|
|
9979
|
+
const res = await fetch(`/checkpoints/decision?${qs.toString()}`, {
|
|
9980
|
+
method: "POST",
|
|
9981
|
+
headers: { "Accept": "application/json" }
|
|
9982
|
+
})
|
|
9983
|
+
const payload = res.ok ? await res.json().catch(() => null) : null
|
|
9984
|
+
if (!payload || !payload.ok) {
|
|
9985
|
+
throw new Error("Failed to save decision")
|
|
9986
|
+
}
|
|
9987
|
+
footer.classList.add("hidden")
|
|
9988
|
+
} catch (_) {
|
|
9989
|
+
Swal.fire({ icon: "error", title: "Error", text: "Could not save decision" })
|
|
9990
|
+
setPublishLoading(false, publishOriginal)
|
|
9991
|
+
}
|
|
9992
|
+
}
|
|
9993
|
+
|
|
9994
|
+
saveBtn.addEventListener("click", handleSave)
|
|
9995
|
+
if (registryBetaEnabled && publishBtn) publishBtn.addEventListener("click", handlePublish)
|
|
9996
|
+
if (registryBetaEnabled && laterBtn) laterBtn.addEventListener("click", handleLater)
|
|
9997
|
+
})
|
|
9998
|
+
</script>
|
|
9231
9999
|
</body>
|
|
9232
10000
|
</html>
|