pinokiod 3.102.0 → 3.104.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/package.json +1 -1
- package/server/index.js +396 -31
- package/server/public/style.css +2 -2
- package/server/views/app.ejs +761 -102
- package/server/views/pro.ejs +1 -1
package/server/views/app.ejs
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
<link href="/electron.css" rel="stylesheet"/>
|
|
19
19
|
<% } %>
|
|
20
20
|
<style>
|
|
21
|
-
body.dark #devtab {
|
|
21
|
+
body.dark #devtab.selected {
|
|
22
22
|
border: none;
|
|
23
|
+
background: rgba(255,255,255,0.07);
|
|
23
24
|
}
|
|
24
25
|
#devtab {
|
|
25
26
|
align-items: center;
|
|
@@ -144,7 +145,7 @@ body.dark .appcanvas {
|
|
|
144
145
|
}
|
|
145
146
|
*/
|
|
146
147
|
body.dark .appcanvas_filler {
|
|
147
|
-
background: rgba(255,255,255,0.
|
|
148
|
+
background: rgba(255,255,255,0.07) !important;
|
|
148
149
|
border: none;
|
|
149
150
|
}
|
|
150
151
|
.appcanvas_filler {
|
|
@@ -370,6 +371,9 @@ body.dark .appcanvas > aside .m.menu .nested-menu > .submenu .frame-link:hover {
|
|
|
370
371
|
background: none !important;
|
|
371
372
|
border: none;
|
|
372
373
|
}
|
|
374
|
+
body.dark .appcanvas > aside .header-item.btn:not(.selected) {
|
|
375
|
+
background: none;
|
|
376
|
+
}
|
|
373
377
|
|
|
374
378
|
.appcanvas > aside .header-item .tab {
|
|
375
379
|
display: flex;
|
|
@@ -702,7 +706,7 @@ body .frame-link.selected {
|
|
|
702
706
|
color: white;
|
|
703
707
|
}
|
|
704
708
|
body.dark .frame-link.selected {
|
|
705
|
-
background: rgba(255,255,255,0.
|
|
709
|
+
background: rgba(255,255,255,0.07) !important;
|
|
706
710
|
}
|
|
707
711
|
.frame-link .loader {
|
|
708
712
|
/*
|
|
@@ -1273,11 +1277,8 @@ body.dark .top-menu .btn2.selected {
|
|
|
1273
1277
|
}
|
|
1274
1278
|
|
|
1275
1279
|
body.dark #fs-status {
|
|
1276
|
-
/*
|
|
1277
|
-
border-bottom: 1px solid rgba(255,255,255,0.04);
|
|
1278
|
-
*/
|
|
1279
1280
|
border: none;
|
|
1280
|
-
background: rgba(255,255,255,0.
|
|
1281
|
+
background: rgba(255,255,255,0.07) !important;
|
|
1281
1282
|
}
|
|
1282
1283
|
#fs-status {
|
|
1283
1284
|
padding: 5px;
|
|
@@ -1313,7 +1314,7 @@ body.dark #fs-status {
|
|
|
1313
1314
|
border-radius: 4px;
|
|
1314
1315
|
display: flex !important;
|
|
1315
1316
|
align-items: center;
|
|
1316
|
-
gap:
|
|
1317
|
+
gap: 4px;
|
|
1317
1318
|
font-size: 12px;
|
|
1318
1319
|
line-height: 1.2;
|
|
1319
1320
|
color: #24292f;
|
|
@@ -1351,6 +1352,13 @@ body.dark #fs-status {
|
|
|
1351
1352
|
min-width: 0;
|
|
1352
1353
|
}
|
|
1353
1354
|
|
|
1355
|
+
.fs-status-btn.fs-status-btn--disabled,
|
|
1356
|
+
.fs-status-btn:disabled {
|
|
1357
|
+
opacity: 0.45;
|
|
1358
|
+
cursor: default;
|
|
1359
|
+
pointer-events: none;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1354
1362
|
.fs-status-dropdown {
|
|
1355
1363
|
position: relative;
|
|
1356
1364
|
}
|
|
@@ -1441,6 +1449,79 @@ body.dark #fs-status .fs-dropdown-menu .frame-link:hover {
|
|
|
1441
1449
|
background: rgba(148, 163, 184, 0.15);
|
|
1442
1450
|
}
|
|
1443
1451
|
|
|
1452
|
+
#fs-status .git-changes {
|
|
1453
|
+
margin-left: auto;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
#fs-changes-menu .fs-dropdown-empty {
|
|
1457
|
+
padding: 8px 14px;
|
|
1458
|
+
font-size: 12px;
|
|
1459
|
+
color: rgba(31, 41, 55, 0.66);
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
#fs-changes-menu {
|
|
1463
|
+
left: auto;
|
|
1464
|
+
right: 0;
|
|
1465
|
+
width: max-content;
|
|
1466
|
+
max-width: min(460px, calc(100vw - 32px));
|
|
1467
|
+
max-height: min(70vh, 420px);
|
|
1468
|
+
overflow: auto;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
#fs-changes-menu .fs-dropdown-item {
|
|
1472
|
+
align-items: flex-start;
|
|
1473
|
+
white-space: normal;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
body.dark #fs-changes-menu .fs-dropdown-empty {
|
|
1477
|
+
color: rgba(226, 232, 240, 0.7);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
#fs-changes-menu .git-changes-item-label {
|
|
1481
|
+
display: flex;
|
|
1482
|
+
align-items: center;
|
|
1483
|
+
gap: 6px;
|
|
1484
|
+
min-width: 0;
|
|
1485
|
+
white-space: normal;
|
|
1486
|
+
word-break: break-word;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
#fs-changes-menu .git-changes-item-count {
|
|
1490
|
+
margin-left: auto;
|
|
1491
|
+
font-size: 12px;
|
|
1492
|
+
font-weight: 600;
|
|
1493
|
+
color: #1f2937;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
#fs-changes-menu .git-changes-item-count.git-changes-item-count--dirty {
|
|
1497
|
+
color: #2563eb;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
#fs-changes-menu .git-changes-item-count.git-changes-item-count--clean {
|
|
1501
|
+
font-weight: 500;
|
|
1502
|
+
color: rgba(31, 41, 55, 0.6);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
#fs-changes-menu .git-changes-item.git-changes-item--active {
|
|
1506
|
+
background: rgba(37, 99, 235, 0.08);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
body.dark #fs-changes-menu .git-changes-item-count {
|
|
1510
|
+
color: #e2e8f0;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
body.dark #fs-changes-menu .git-changes-item-count.git-changes-item-count--dirty {
|
|
1514
|
+
color: #60a5fa;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
body.dark #fs-changes-menu .git-changes-item-count.git-changes-item-count--clean {
|
|
1518
|
+
color: rgba(148, 163, 184, 0.6);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
body.dark #fs-changes-menu .git-changes-item.git-changes-item--active {
|
|
1522
|
+
background: rgba(96, 165, 250, 0.15);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1444
1525
|
#fs-status .app-info {
|
|
1445
1526
|
display: flex;
|
|
1446
1527
|
align-items: center;
|
|
@@ -1508,15 +1589,17 @@ body.dark .fs-status-btn:hover {
|
|
|
1508
1589
|
.fs-status-btn .badge {
|
|
1509
1590
|
background: #dc3545;
|
|
1510
1591
|
color: white;
|
|
1511
|
-
padding:
|
|
1512
|
-
border-radius:
|
|
1592
|
+
padding: 2px 5px;
|
|
1593
|
+
border-radius: 2px;
|
|
1513
1594
|
font-size: 11px;
|
|
1514
1595
|
font-weight: 600;
|
|
1515
1596
|
text-align: center;
|
|
1597
|
+
/*
|
|
1516
1598
|
position: absolute;
|
|
1517
1599
|
top: 0;
|
|
1518
1600
|
right: 0;
|
|
1519
1601
|
transform:translate(25%, -50%);
|
|
1602
|
+
*/
|
|
1520
1603
|
display: inline-flex;
|
|
1521
1604
|
align-items: center;
|
|
1522
1605
|
justify-content: center;
|
|
@@ -2596,7 +2679,7 @@ body.dark {
|
|
|
2596
2679
|
<% } %>
|
|
2597
2680
|
<div class='container'>
|
|
2598
2681
|
<% if (type === "browse") { %>
|
|
2599
|
-
<div id='fs-status' data-create-uri="<%=git_create_url%>" data-history-uri="<%=git_history_url%>" data-uri="<%=git_monitor_url%>" data-push-uri="<%=git_push_url%>">
|
|
2682
|
+
<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%>">
|
|
2600
2683
|
<a target="<%=src%>" href="<%=src%>" class='fs-status-btn frame-link' data-index="0" data-mode="refresh" data-type="n">
|
|
2601
2684
|
<span class='fs-status-label'>
|
|
2602
2685
|
<i class="fa-regular fa-folder-open"></i>
|
|
@@ -2618,6 +2701,7 @@ body.dark {
|
|
|
2618
2701
|
<button type='button' class='fs-dropdown-item' id='delete' data-name="<%=name%>"><i class="fa-solid fa-trash-can"></i> Delete</button>
|
|
2619
2702
|
</div>
|
|
2620
2703
|
</div>
|
|
2704
|
+
<!--
|
|
2621
2705
|
<div class='fs-status-dropdown nested-menu git blue'>
|
|
2622
2706
|
<button type='button' class='fs-status-btn frame-link reveal'>
|
|
2623
2707
|
<span class='fs-status-label'>
|
|
@@ -2628,6 +2712,7 @@ body.dark {
|
|
|
2628
2712
|
<div class='fs-dropdown-menu submenu hidden' id='git-repos'>
|
|
2629
2713
|
</div>
|
|
2630
2714
|
</div>
|
|
2715
|
+
-->
|
|
2631
2716
|
<div class='fs-status-dropdown'>
|
|
2632
2717
|
<button type='button' class='fs-status-btn revealer' data-group='#fs-settings-menu'>
|
|
2633
2718
|
<span class='fs-status-label'><i class='fa-solid fa-gear'></i> Settings</span>
|
|
@@ -2641,13 +2726,13 @@ body.dark {
|
|
|
2641
2726
|
</a>
|
|
2642
2727
|
</div>
|
|
2643
2728
|
</div>
|
|
2644
|
-
<
|
|
2645
|
-
<
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
<
|
|
2650
|
-
</
|
|
2729
|
+
<div class='fs-status-dropdown git-changes'>
|
|
2730
|
+
<button id='fs-changes-btn' class='fs-status-btn revealer' data-group='#fs-changes-menu' type='button'>
|
|
2731
|
+
<span class='fs-status-label'><i class="fa-solid fa-code-compare"></i> Changes</span>
|
|
2732
|
+
<div class='badge'></div>
|
|
2733
|
+
</button>
|
|
2734
|
+
<div class='fs-dropdown-menu submenu hidden' id='fs-changes-menu'></div>
|
|
2735
|
+
</div>
|
|
2651
2736
|
<button id='fs-push-btn' class='fs-status-btn'>
|
|
2652
2737
|
<span class='fs-status-label'>
|
|
2653
2738
|
<i class="fa-brands fa-github"></i>
|
|
@@ -2676,6 +2761,15 @@ body.dark {
|
|
|
2676
2761
|
renderSelection({ force: true })
|
|
2677
2762
|
}, delay)
|
|
2678
2763
|
}
|
|
2764
|
+
const pluginLaunchActive = (() => {
|
|
2765
|
+
try {
|
|
2766
|
+
const params = new URLSearchParams(window.location.search)
|
|
2767
|
+
return params.has('plugin')
|
|
2768
|
+
} catch (_) {
|
|
2769
|
+
return false
|
|
2770
|
+
}
|
|
2771
|
+
})()
|
|
2772
|
+
let ignorePersistedSelection = pluginLaunchActive
|
|
2679
2773
|
let lastForegroundSignature = null
|
|
2680
2774
|
const iframe_onerror = (iframe) => {
|
|
2681
2775
|
let originalSrc = iframe.src
|
|
@@ -2854,6 +2948,82 @@ body.dark {
|
|
|
2854
2948
|
}
|
|
2855
2949
|
}
|
|
2856
2950
|
|
|
2951
|
+
const ensureHttpDirectoryUrl = (value) => {
|
|
2952
|
+
try {
|
|
2953
|
+
const parsed = new URL(value)
|
|
2954
|
+
if (parsed.protocol.toLowerCase() !== "http:") {
|
|
2955
|
+
return value
|
|
2956
|
+
}
|
|
2957
|
+
let pathname = parsed.pathname || "/"
|
|
2958
|
+
const lastSegment = pathname.split("/").pop() || ""
|
|
2959
|
+
const hasExtension = lastSegment.includes(".")
|
|
2960
|
+
if (!hasExtension && !pathname.endsWith("/")) {
|
|
2961
|
+
pathname = `${pathname}/`
|
|
2962
|
+
parsed.pathname = pathname
|
|
2963
|
+
}
|
|
2964
|
+
parsed.hash = parsed.hash || ""
|
|
2965
|
+
parsed.search = parsed.search || ""
|
|
2966
|
+
return parsed.toString()
|
|
2967
|
+
} catch (_) {
|
|
2968
|
+
return value
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
const isLocalHostLike = (hostname) => {
|
|
2973
|
+
if (!hostname) {
|
|
2974
|
+
return false
|
|
2975
|
+
}
|
|
2976
|
+
const hostLower = hostname.toLowerCase()
|
|
2977
|
+
if (hostLower === location.hostname.toLowerCase()) {
|
|
2978
|
+
return true
|
|
2979
|
+
}
|
|
2980
|
+
if (hostLower === "localhost" || hostLower === "0.0.0.0") {
|
|
2981
|
+
return true
|
|
2982
|
+
}
|
|
2983
|
+
if (hostLower.startsWith("127.")) {
|
|
2984
|
+
return true
|
|
2985
|
+
}
|
|
2986
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(hostLower)) {
|
|
2987
|
+
return true
|
|
2988
|
+
}
|
|
2989
|
+
return false
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
const extractProjectSlug = (node) => {
|
|
2993
|
+
if (!node) {
|
|
2994
|
+
return ""
|
|
2995
|
+
}
|
|
2996
|
+
const candidates = []
|
|
2997
|
+
const targetFull = node.getAttribute("data-target-full")
|
|
2998
|
+
if (typeof targetFull === "string" && targetFull.length > 0) {
|
|
2999
|
+
candidates.push(targetFull)
|
|
3000
|
+
}
|
|
3001
|
+
const dataHref = node.getAttribute("href")
|
|
3002
|
+
if (typeof dataHref === "string" && dataHref.length > 0) {
|
|
3003
|
+
candidates.push(dataHref)
|
|
3004
|
+
}
|
|
3005
|
+
try {
|
|
3006
|
+
const absolute = new URL(node.href, location.origin)
|
|
3007
|
+
candidates.push(absolute.pathname)
|
|
3008
|
+
} catch (_) {
|
|
3009
|
+
// ignore
|
|
3010
|
+
}
|
|
3011
|
+
for (const candidate of candidates) {
|
|
3012
|
+
if (typeof candidate !== "string" || candidate.length === 0) {
|
|
3013
|
+
continue
|
|
3014
|
+
}
|
|
3015
|
+
const assetMatch = candidate.match(/\/asset\/api\/([^\/?#]+)/i)
|
|
3016
|
+
if (assetMatch && assetMatch[1]) {
|
|
3017
|
+
return assetMatch[1]
|
|
3018
|
+
}
|
|
3019
|
+
const pageMatch = candidate.match(/\/p\/([^\/?#]+)/i)
|
|
3020
|
+
if (pageMatch && pageMatch[1]) {
|
|
3021
|
+
return pageMatch[1]
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
return ""
|
|
3025
|
+
}
|
|
3026
|
+
|
|
2857
3027
|
const formatDisplayUrl = (value) => {
|
|
2858
3028
|
try {
|
|
2859
3029
|
const parsed = new URL(value, location.origin)
|
|
@@ -3344,13 +3514,17 @@ body.dark {
|
|
|
3344
3514
|
}
|
|
3345
3515
|
|
|
3346
3516
|
const baseHref = link.href
|
|
3517
|
+
const projectSlug = extractProjectSlug(link).toLowerCase()
|
|
3347
3518
|
const entries = []
|
|
3348
3519
|
const entryByUrl = new Map()
|
|
3349
3520
|
const addEntry = (type, label, url) => {
|
|
3350
3521
|
if (!url) {
|
|
3351
3522
|
return
|
|
3352
3523
|
}
|
|
3353
|
-
|
|
3524
|
+
let canonical = canonicalizeUrl(url)
|
|
3525
|
+
if (canonical && type === "http") {
|
|
3526
|
+
canonical = ensureHttpDirectoryUrl(canonical)
|
|
3527
|
+
}
|
|
3354
3528
|
if (!canonical) {
|
|
3355
3529
|
return
|
|
3356
3530
|
}
|
|
@@ -3360,13 +3534,6 @@ body.dark {
|
|
|
3360
3534
|
const originLower = parsed.origin.toLowerCase()
|
|
3361
3535
|
if (originLower === location.origin.toLowerCase()) {
|
|
3362
3536
|
skip = true
|
|
3363
|
-
} else {
|
|
3364
|
-
const hostLower = parsed.hostname.toLowerCase()
|
|
3365
|
-
const port = parsed.port || (parsed.protocol === "http:" ? "80" : parsed.protocol === "https:" ? "443" : "")
|
|
3366
|
-
const localHosts = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"])
|
|
3367
|
-
if (parsed.protocol === "http:" && port === "42000" && localHosts.has(hostLower)) {
|
|
3368
|
-
skip = true
|
|
3369
|
-
}
|
|
3370
3537
|
}
|
|
3371
3538
|
} catch (_) {
|
|
3372
3539
|
// ignore parse failures but do not skip by default
|
|
@@ -3404,6 +3571,26 @@ body.dark {
|
|
|
3404
3571
|
httpsCandidates.add(canonicalizeUrl(baseHref))
|
|
3405
3572
|
}
|
|
3406
3573
|
|
|
3574
|
+
if (projectSlug) {
|
|
3575
|
+
try {
|
|
3576
|
+
const baseUrl = new URL(baseHref, location.origin)
|
|
3577
|
+
let pathname = baseUrl.pathname || "/"
|
|
3578
|
+
if (pathname.endsWith("/index.html")) {
|
|
3579
|
+
pathname = pathname.slice(0, -"/index.html".length)
|
|
3580
|
+
}
|
|
3581
|
+
if (!pathname.endsWith("/")) {
|
|
3582
|
+
pathname = `${pathname}/`
|
|
3583
|
+
}
|
|
3584
|
+
const normalizedPath = pathname.toLowerCase()
|
|
3585
|
+
if (normalizedPath.includes(`/asset/api/${projectSlug}`)) {
|
|
3586
|
+
const fallbackHttp = `http://127.0.0.1:42000${pathname}`
|
|
3587
|
+
httpCandidates.add(canonicalizeUrl(fallbackHttp))
|
|
3588
|
+
}
|
|
3589
|
+
} catch (_) {
|
|
3590
|
+
// ignore fallback errors
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3407
3594
|
const scriptKeys = collectScriptKeys(link)
|
|
3408
3595
|
if (scriptKeys.length > 0) {
|
|
3409
3596
|
const localInfo = await ensureLocalMemory()
|
|
@@ -3437,9 +3624,29 @@ body.dark {
|
|
|
3437
3624
|
})
|
|
3438
3625
|
}
|
|
3439
3626
|
|
|
3440
|
-
const httpList = Array.from(httpCandidates).sort()
|
|
3441
3627
|
const httpsList = Array.from(httpsCandidates).sort()
|
|
3442
3628
|
|
|
3629
|
+
if (httpsList.length > 0) {
|
|
3630
|
+
httpsList.forEach((url) => {
|
|
3631
|
+
try {
|
|
3632
|
+
const parsed = new URL(url)
|
|
3633
|
+
if (parsed.protocol.toLowerCase() !== "https:") {
|
|
3634
|
+
return
|
|
3635
|
+
}
|
|
3636
|
+
if (!parsed.port || parsed.port !== "42000") {
|
|
3637
|
+
return
|
|
3638
|
+
}
|
|
3639
|
+
const hostPort = parsed.port ? `${parsed.hostname}:${parsed.port}` : parsed.hostname
|
|
3640
|
+
const httpUrl = `http://${hostPort}${parsed.pathname || "/"}${parsed.search || ""}`
|
|
3641
|
+
httpCandidates.add(canonicalizeUrl(httpUrl))
|
|
3642
|
+
} catch (_) {
|
|
3643
|
+
// ignore failures
|
|
3644
|
+
}
|
|
3645
|
+
})
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
const httpList = Array.from(httpCandidates).sort()
|
|
3649
|
+
|
|
3443
3650
|
httpList.forEach((url) => {
|
|
3444
3651
|
addEntry("http", "HTTP", url)
|
|
3445
3652
|
})
|
|
@@ -3512,12 +3719,12 @@ body.dark {
|
|
|
3512
3719
|
return
|
|
3513
3720
|
}
|
|
3514
3721
|
|
|
3722
|
+
let sameOrigin = false
|
|
3723
|
+
let canonicalBase = canonicalizeUrl(link.href)
|
|
3515
3724
|
try {
|
|
3516
|
-
const
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
return
|
|
3520
|
-
}
|
|
3725
|
+
const linkUrl = new URL(link.href, location.href)
|
|
3726
|
+
sameOrigin = linkUrl.origin === location.origin
|
|
3727
|
+
canonicalBase = canonicalizeUrl(linkUrl.href)
|
|
3521
3728
|
} catch (_) {
|
|
3522
3729
|
hideTabLinkPopover({ immediate: true })
|
|
3523
3730
|
return
|
|
@@ -3558,6 +3765,59 @@ body.dark {
|
|
|
3558
3765
|
return
|
|
3559
3766
|
}
|
|
3560
3767
|
|
|
3768
|
+
if (sameOrigin) {
|
|
3769
|
+
const slug = extractProjectSlug(link).toLowerCase()
|
|
3770
|
+
if (slug) {
|
|
3771
|
+
entries = entries.filter((entry) => {
|
|
3772
|
+
if (!entry || !entry.url) {
|
|
3773
|
+
return false
|
|
3774
|
+
}
|
|
3775
|
+
if (entry.url === canonicalBase) {
|
|
3776
|
+
return true
|
|
3777
|
+
}
|
|
3778
|
+
try {
|
|
3779
|
+
const parsed = new URL(entry.url)
|
|
3780
|
+
const hostLower = parsed.hostname ? parsed.hostname.toLowerCase() : ""
|
|
3781
|
+
if (isLocalHostLike(hostLower)) {
|
|
3782
|
+
if (entry.type === "http") {
|
|
3783
|
+
const pathLower = parsed.pathname ? parsed.pathname.toLowerCase() : ""
|
|
3784
|
+
if (pathLower.includes(`/asset/api/${slug}`) || pathLower.includes(`/p/${slug}`)) {
|
|
3785
|
+
return true
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
return false
|
|
3789
|
+
}
|
|
3790
|
+
const pathLower = parsed.pathname ? parsed.pathname.toLowerCase() : ""
|
|
3791
|
+
if (pathLower.includes(`/asset/api/${slug}`)) {
|
|
3792
|
+
return true
|
|
3793
|
+
}
|
|
3794
|
+
if (pathLower.includes(`/p/${slug}`)) {
|
|
3795
|
+
return true
|
|
3796
|
+
}
|
|
3797
|
+
if (hostLower.split(".").some((part) => part === slug)) {
|
|
3798
|
+
return true
|
|
3799
|
+
}
|
|
3800
|
+
} catch (_) {
|
|
3801
|
+
return false
|
|
3802
|
+
}
|
|
3803
|
+
return false
|
|
3804
|
+
})
|
|
3805
|
+
} else {
|
|
3806
|
+
entries = entries.filter((entry) => entry.url === canonicalBase)
|
|
3807
|
+
}
|
|
3808
|
+
|
|
3809
|
+
const hasAlternate = entries.some((entry) => entry.url !== canonicalBase)
|
|
3810
|
+
if (!hasAlternate) {
|
|
3811
|
+
hideTabLinkPopover({ immediate: true })
|
|
3812
|
+
return
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
if (!entries || entries.length === 0) {
|
|
3817
|
+
hideTabLinkPopover({ immediate: true })
|
|
3818
|
+
return
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3561
3821
|
const popover = ensureTabLinkPopoverEl()
|
|
3562
3822
|
popover.innerHTML = ""
|
|
3563
3823
|
|
|
@@ -4424,8 +4684,13 @@ body.dark {
|
|
|
4424
4684
|
const renderSelection = async ({ event: eventParam = null, target: explicitTarget = null, force = false } = {}) => {
|
|
4425
4685
|
const storage = getWindowStorage()
|
|
4426
4686
|
const selectionKey = selectionStorageKey()
|
|
4427
|
-
const
|
|
4428
|
-
|
|
4687
|
+
const originalHasPersistedSelection = Boolean(selectionKey && storage && storage.getItem(selectionKey))
|
|
4688
|
+
let persistedSelectionRaw = selectionKey && storage ? storage.getItem(selectionKey) : null
|
|
4689
|
+
const skipPersistedSelection = ignorePersistedSelection
|
|
4690
|
+
let hasPersistedSelection = skipPersistedSelection ? false : originalHasPersistedSelection
|
|
4691
|
+
if (skipPersistedSelection) {
|
|
4692
|
+
persistedSelectionRaw = null
|
|
4693
|
+
}
|
|
4429
4694
|
|
|
4430
4695
|
let target = explicitTarget
|
|
4431
4696
|
|
|
@@ -4444,7 +4709,7 @@ body.dark {
|
|
|
4444
4709
|
}
|
|
4445
4710
|
}
|
|
4446
4711
|
|
|
4447
|
-
if (!target) {
|
|
4712
|
+
if (!target && persistedSelectionRaw) {
|
|
4448
4713
|
target = restorePersistedFrameLink()
|
|
4449
4714
|
if (!target && persistedSelectionRaw) {
|
|
4450
4715
|
scheduleSelectionRetry()
|
|
@@ -4452,7 +4717,7 @@ body.dark {
|
|
|
4452
4717
|
}
|
|
4453
4718
|
}
|
|
4454
4719
|
|
|
4455
|
-
if (!target) {
|
|
4720
|
+
if (!target && !skipPersistedSelection) {
|
|
4456
4721
|
const urlKey = selectionUrlStorageKey()
|
|
4457
4722
|
const storedUrl = urlKey && storage ? storage.getItem(urlKey) : null
|
|
4458
4723
|
if (storedUrl) {
|
|
@@ -4460,6 +4725,16 @@ body.dark {
|
|
|
4460
4725
|
}
|
|
4461
4726
|
}
|
|
4462
4727
|
|
|
4728
|
+
if (!target && skipPersistedSelection) {
|
|
4729
|
+
const defaultSelection = document.querySelector("[data-default]")
|
|
4730
|
+
if (defaultSelection) {
|
|
4731
|
+
target = defaultSelection
|
|
4732
|
+
} else {
|
|
4733
|
+
scheduleSelectionRetry()
|
|
4734
|
+
return
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4463
4738
|
<% if (type === "run" && env.PINOKIO_SCRIPT_DEFAULT && env.PINOKIO_SCRIPT_DEFAULT.toString().toLowerCase() === "true") { %>
|
|
4464
4739
|
if (!target && !hasPersistedSelection) {
|
|
4465
4740
|
const defaultSelection = document.querySelector("[data-default]")
|
|
@@ -4531,6 +4806,9 @@ body.dark {
|
|
|
4531
4806
|
el.classList.remove("selected")
|
|
4532
4807
|
})
|
|
4533
4808
|
target.classList.add("selected")
|
|
4809
|
+
if (skipPersistedSelection && target.hasAttribute('data-default')) {
|
|
4810
|
+
ignorePersistedSelection = false
|
|
4811
|
+
}
|
|
4534
4812
|
persistFrameLinkSelection(target)
|
|
4535
4813
|
|
|
4536
4814
|
// save target.href
|
|
@@ -5180,6 +5458,14 @@ body.dark {
|
|
|
5180
5458
|
target = e.target.classList.contains("revealer") ? e.target : e.target.closest(".revealer")
|
|
5181
5459
|
|
|
5182
5460
|
if (target) {
|
|
5461
|
+
if (target.classList.contains('fs-status-btn--disabled') || target.disabled) {
|
|
5462
|
+
e.preventDefault()
|
|
5463
|
+
e.stopPropagation()
|
|
5464
|
+
return
|
|
5465
|
+
}
|
|
5466
|
+
if (target.id === 'fs-changes-btn') {
|
|
5467
|
+
check_git()
|
|
5468
|
+
}
|
|
5183
5469
|
e.preventDefault()
|
|
5184
5470
|
e.stopPropagation()
|
|
5185
5471
|
const group = target.getAttribute("data-group")
|
|
@@ -5612,12 +5898,14 @@ body.dark {
|
|
|
5612
5898
|
restoreAllTabStates()
|
|
5613
5899
|
<% } else { %>
|
|
5614
5900
|
try_dynamic()
|
|
5901
|
+
/*
|
|
5615
5902
|
const repos = await fetch("<%=repos%>").then((res) => {
|
|
5616
5903
|
return res.text()
|
|
5617
5904
|
})
|
|
5618
5905
|
if (document.querySelector("#git-repos")) {
|
|
5619
5906
|
document.querySelector("#git-repos").innerHTML = repos
|
|
5620
5907
|
}
|
|
5908
|
+
*/
|
|
5621
5909
|
<% } %>
|
|
5622
5910
|
|
|
5623
5911
|
|
|
@@ -5725,6 +6013,7 @@ body.dark {
|
|
|
5725
6013
|
refresh_du("logs")
|
|
5726
6014
|
renderSelection({ force: true })
|
|
5727
6015
|
<% if (type !== 'run') { %>
|
|
6016
|
+
/*
|
|
5728
6017
|
fetch("<%=repos%>").then((res) => {
|
|
5729
6018
|
return res.text()
|
|
5730
6019
|
}).then((repos) => {
|
|
@@ -5733,6 +6022,7 @@ body.dark {
|
|
|
5733
6022
|
}
|
|
5734
6023
|
})
|
|
5735
6024
|
refresh()
|
|
6025
|
+
*/
|
|
5736
6026
|
<% } %>
|
|
5737
6027
|
<% if (plugin_menu) { %>
|
|
5738
6028
|
// document.querySelector(".dynamic .reveal").click()
|
|
@@ -5772,31 +6062,266 @@ body.dark {
|
|
|
5772
6062
|
});
|
|
5773
6063
|
*/
|
|
5774
6064
|
<% if (type === "browse") { %>
|
|
6065
|
+
const repoStatusCache = new Map()
|
|
6066
|
+
let lastRepoList = []
|
|
5775
6067
|
let currentChanges = []
|
|
5776
6068
|
let gitCommitUrl = null
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
6069
|
+
let activeRepoKey = null
|
|
6070
|
+
let gitStatusRequest = null
|
|
6071
|
+
|
|
6072
|
+
const fsStatusEl = document.querySelector('#fs-status')
|
|
6073
|
+
const changesDropdownContainer = document.querySelector('#fs-status .git-changes')
|
|
6074
|
+
const changesMenu = document.getElementById('fs-changes-menu')
|
|
6075
|
+
const changesBtn = document.getElementById('fs-changes-btn')
|
|
6076
|
+
const badgeElement = changesBtn ? changesBtn.querySelector('.badge') : null
|
|
6077
|
+
|
|
6078
|
+
const readDataAttr = (node, attr) => {
|
|
6079
|
+
if (!node) {
|
|
6080
|
+
return null
|
|
6081
|
+
}
|
|
6082
|
+
const value = node.getAttribute(attr)
|
|
6083
|
+
if (!value || value === 'null' || value === 'undefined') {
|
|
6084
|
+
return null
|
|
6085
|
+
}
|
|
6086
|
+
return value
|
|
6087
|
+
}
|
|
6088
|
+
|
|
6089
|
+
const statusUri = readDataAttr(fsStatusEl, 'data-status-uri')
|
|
6090
|
+
const monitorUri = readDataAttr(fsStatusEl, 'data-uri')
|
|
6091
|
+
const workspaceName = readDataAttr(fsStatusEl, 'data-workspace')
|
|
6092
|
+
|
|
6093
|
+
const encodeRepoPath = (value) => {
|
|
6094
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
6095
|
+
return ''
|
|
6096
|
+
}
|
|
6097
|
+
return value.split('/').map(encodeURIComponent).join('/')
|
|
6098
|
+
}
|
|
6099
|
+
|
|
6100
|
+
const updateCombinedBadge = (total) => {
|
|
6101
|
+
if (!badgeElement) {
|
|
6102
|
+
return
|
|
6103
|
+
}
|
|
6104
|
+
badgeElement.textContent = total > 0 ? String(total) : ''
|
|
6105
|
+
}
|
|
6106
|
+
|
|
6107
|
+
const updateChangesButtonState = (hasRepos) => {
|
|
6108
|
+
if (!changesBtn) {
|
|
6109
|
+
return
|
|
6110
|
+
}
|
|
6111
|
+
if (hasRepos) {
|
|
6112
|
+
changesBtn.disabled = false
|
|
6113
|
+
changesBtn.classList.remove('fs-status-btn--disabled')
|
|
6114
|
+
} else {
|
|
6115
|
+
changesBtn.disabled = true
|
|
6116
|
+
changesBtn.classList.add('fs-status-btn--disabled')
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
|
|
6120
|
+
const setChangesMenuMessage = (message) => {
|
|
6121
|
+
if (!changesMenu) {
|
|
6122
|
+
return
|
|
6123
|
+
}
|
|
6124
|
+
const messageEl = document.createElement('div')
|
|
6125
|
+
messageEl.className = 'fs-dropdown-empty'
|
|
6126
|
+
messageEl.textContent = message
|
|
6127
|
+
changesMenu.innerHTML = ''
|
|
6128
|
+
changesMenu.append(messageEl)
|
|
6129
|
+
}
|
|
6130
|
+
|
|
6131
|
+
const attachRepoDropdownHandlers = () => {
|
|
6132
|
+
if (!changesMenu) {
|
|
6133
|
+
return
|
|
6134
|
+
}
|
|
6135
|
+
const items = changesMenu.querySelectorAll('.git-changes-item')
|
|
6136
|
+
items.forEach((item) => {
|
|
6137
|
+
item.addEventListener('click', async (event) => {
|
|
6138
|
+
event.preventDefault()
|
|
6139
|
+
event.stopPropagation()
|
|
6140
|
+
const repoKey = item.dataset.repo
|
|
6141
|
+
const repoName = item.dataset.name
|
|
6142
|
+
if (repoKey) {
|
|
6143
|
+
activeRepoKey = repoKey
|
|
6144
|
+
}
|
|
6145
|
+
items.forEach((node) => {
|
|
6146
|
+
if (node === item) {
|
|
6147
|
+
node.classList.add('git-changes-item--active')
|
|
6148
|
+
} else {
|
|
6149
|
+
node.classList.remove('git-changes-item--active')
|
|
6150
|
+
}
|
|
6151
|
+
})
|
|
6152
|
+
closeStatusDropdowns()
|
|
6153
|
+
try {
|
|
6154
|
+
await showGitDiffModal({ repoParam: repoKey, repoName })
|
|
6155
|
+
} catch (err) {
|
|
6156
|
+
console.error('Failed to open diff modal:', err)
|
|
6157
|
+
}
|
|
6158
|
+
})
|
|
6159
|
+
})
|
|
6160
|
+
}
|
|
6161
|
+
|
|
6162
|
+
const renderChangesDropdown = (repos) => {
|
|
6163
|
+
if (!changesMenu) {
|
|
6164
|
+
return
|
|
6165
|
+
}
|
|
6166
|
+
|
|
6167
|
+
if (!Array.isArray(repos) || repos.length === 0) {
|
|
6168
|
+
setChangesMenuMessage('No Git repositories detected')
|
|
6169
|
+
updateChangesButtonState(false)
|
|
6170
|
+
return
|
|
6171
|
+
}
|
|
6172
|
+
|
|
6173
|
+
const fragment = document.createDocumentFragment()
|
|
6174
|
+
repos.forEach((repo) => {
|
|
6175
|
+
const button = document.createElement('button')
|
|
6176
|
+
button.type = 'button'
|
|
6177
|
+
button.className = 'fs-dropdown-item git-changes-item'
|
|
6178
|
+
button.dataset.repo = repo.repoParam
|
|
6179
|
+
button.dataset.name = repo.name
|
|
6180
|
+
if (repo.main) {
|
|
6181
|
+
button.dataset.main = 'true'
|
|
5795
6182
|
}
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
6183
|
+
if (repo.repoParam === activeRepoKey) {
|
|
6184
|
+
button.classList.add('git-changes-item--active')
|
|
6185
|
+
}
|
|
6186
|
+
|
|
6187
|
+
button.innerHTML = `
|
|
6188
|
+
<span class="git-changes-item-label">
|
|
6189
|
+
<i class="fa-solid fa-code-branch"></i>
|
|
6190
|
+
<span>${repo.name}</span>
|
|
6191
|
+
</span>
|
|
6192
|
+
<span class="git-changes-item-count ${repo.changeCount > 0 ? 'git-changes-item-count--dirty' : 'git-changes-item-count--clean'}">
|
|
6193
|
+
${repo.changeCount > 0 ? repo.changeCount : 'Clean'}
|
|
6194
|
+
</span>
|
|
6195
|
+
`
|
|
6196
|
+
|
|
6197
|
+
fragment.append(button)
|
|
5799
6198
|
})
|
|
6199
|
+
|
|
6200
|
+
changesMenu.innerHTML = ''
|
|
6201
|
+
changesMenu.append(fragment)
|
|
6202
|
+
updateChangesButtonState(true)
|
|
6203
|
+
attachRepoDropdownHandlers()
|
|
6204
|
+
}
|
|
6205
|
+
|
|
6206
|
+
const updateFromLegacyMonitor = async () => {
|
|
6207
|
+
if (!monitorUri) {
|
|
6208
|
+
setChangesMenuMessage('Git status data unavailable')
|
|
6209
|
+
updateCombinedBadge(0)
|
|
6210
|
+
updateChangesButtonState(false)
|
|
6211
|
+
return false
|
|
6212
|
+
}
|
|
6213
|
+
try {
|
|
6214
|
+
const response = await fetch(monitorUri)
|
|
6215
|
+
if (!response.ok) {
|
|
6216
|
+
throw new Error(`HTTP ${response.status}`)
|
|
6217
|
+
}
|
|
6218
|
+
const data = await response.json()
|
|
6219
|
+
const repoKey = workspaceName || ''
|
|
6220
|
+
const fallbackRepo = {
|
|
6221
|
+
name: workspaceName || 'Current workspace',
|
|
6222
|
+
main: true,
|
|
6223
|
+
repoParam: repoKey,
|
|
6224
|
+
changeCount: Array.isArray(data.changes) ? data.changes.length : 0,
|
|
6225
|
+
changes: Array.isArray(data.changes) ? data.changes : [],
|
|
6226
|
+
git_commit_url: data.git_commit_url || null,
|
|
6227
|
+
git_history_url: workspaceName ? `/info/git/HEAD/${encodeRepoPath(workspaceName)}` : readDataAttr(fsStatusEl, 'data-history-uri'),
|
|
6228
|
+
url: null,
|
|
6229
|
+
}
|
|
6230
|
+
|
|
6231
|
+
repoStatusCache.clear()
|
|
6232
|
+
repoStatusCache.set(fallbackRepo.repoParam, fallbackRepo)
|
|
6233
|
+
lastRepoList = [fallbackRepo]
|
|
6234
|
+
activeRepoKey = fallbackRepo.repoParam
|
|
6235
|
+
currentChanges = fallbackRepo.changes
|
|
6236
|
+
gitCommitUrl = fallbackRepo.git_commit_url
|
|
6237
|
+
|
|
6238
|
+
renderChangesDropdown(lastRepoList)
|
|
6239
|
+
updateCombinedBadge(fallbackRepo.changeCount)
|
|
6240
|
+
updatePublishButton()
|
|
6241
|
+
return true
|
|
6242
|
+
} catch (error) {
|
|
6243
|
+
console.error('check_git fallback error:', error)
|
|
6244
|
+
setChangesMenuMessage('Unable to load repositories')
|
|
6245
|
+
updateCombinedBadge(0)
|
|
6246
|
+
updateChangesButtonState(false)
|
|
6247
|
+
return false
|
|
6248
|
+
}
|
|
6249
|
+
}
|
|
6250
|
+
|
|
6251
|
+
const check_git = async () => {
|
|
6252
|
+
if (gitStatusRequest) {
|
|
6253
|
+
return gitStatusRequest
|
|
6254
|
+
}
|
|
6255
|
+
|
|
6256
|
+
gitStatusRequest = (async () => {
|
|
6257
|
+
if (repoStatusCache.size === 0) {
|
|
6258
|
+
setChangesMenuMessage(statusUri ? 'Loading repositories...' : 'Loading changes...')
|
|
6259
|
+
updateChangesButtonState(false)
|
|
6260
|
+
}
|
|
6261
|
+
|
|
6262
|
+
if (!statusUri) {
|
|
6263
|
+
await updateFromLegacyMonitor()
|
|
6264
|
+
return
|
|
6265
|
+
}
|
|
6266
|
+
|
|
6267
|
+
try {
|
|
6268
|
+
const response = await fetch(statusUri)
|
|
6269
|
+
if (!response.ok) {
|
|
6270
|
+
throw new Error(`HTTP ${response.status}`)
|
|
6271
|
+
}
|
|
6272
|
+
const data = await response.json()
|
|
6273
|
+
const repos = Array.isArray(data.repos) ? data.repos : []
|
|
6274
|
+
|
|
6275
|
+
repoStatusCache.clear()
|
|
6276
|
+
repos.forEach((repo) => {
|
|
6277
|
+
repoStatusCache.set(repo.repoParam, repo)
|
|
6278
|
+
})
|
|
6279
|
+
|
|
6280
|
+
const sortedRepos = [...repos].sort((a, b) => {
|
|
6281
|
+
if (a.main === b.main) {
|
|
6282
|
+
return a.name.localeCompare(b.name)
|
|
6283
|
+
}
|
|
6284
|
+
return a.main ? -1 : 1
|
|
6285
|
+
})
|
|
6286
|
+
|
|
6287
|
+
lastRepoList = sortedRepos
|
|
6288
|
+
|
|
6289
|
+
const existingActive = activeRepoKey
|
|
6290
|
+
? sortedRepos.find((repo) => repo.repoParam === activeRepoKey)
|
|
6291
|
+
: null
|
|
6292
|
+
const fallbackRepo = sortedRepos.find((repo) => repo.main) || sortedRepos[0] || null
|
|
6293
|
+
const resolvedActive = existingActive || fallbackRepo
|
|
6294
|
+
activeRepoKey = resolvedActive ? resolvedActive.repoParam : null
|
|
6295
|
+
currentChanges = resolvedActive ? (resolvedActive.changes || []) : []
|
|
6296
|
+
gitCommitUrl = resolvedActive ? (resolvedActive.git_commit_url || null) : null
|
|
6297
|
+
|
|
6298
|
+
if (changesDropdownContainer) {
|
|
6299
|
+
changesDropdownContainer.style.display = ''
|
|
6300
|
+
}
|
|
6301
|
+
|
|
6302
|
+
if (sortedRepos.length === 0) {
|
|
6303
|
+
await updateFromLegacyMonitor()
|
|
6304
|
+
} else {
|
|
6305
|
+
renderChangesDropdown(sortedRepos)
|
|
6306
|
+
const total = typeof data.totalChanges === 'number'
|
|
6307
|
+
? data.totalChanges
|
|
6308
|
+
: sortedRepos.reduce((sum, repo) => sum + (repo.changeCount || 0), 0)
|
|
6309
|
+
updateCombinedBadge(total)
|
|
6310
|
+
updateChangesButtonState(true)
|
|
6311
|
+
}
|
|
6312
|
+
|
|
6313
|
+
updatePublishButton()
|
|
6314
|
+
} catch (error) {
|
|
6315
|
+
console.error('check_git error:', error)
|
|
6316
|
+
await updateFromLegacyMonitor()
|
|
6317
|
+
}
|
|
6318
|
+
})()
|
|
6319
|
+
|
|
6320
|
+
try {
|
|
6321
|
+
await gitStatusRequest
|
|
6322
|
+
} finally {
|
|
6323
|
+
gitStatusRequest = null
|
|
6324
|
+
}
|
|
5800
6325
|
}
|
|
5801
6326
|
|
|
5802
6327
|
let messageListener = null
|
|
@@ -5908,43 +6433,135 @@ body.dark {
|
|
|
5908
6433
|
return messages.join(', ')
|
|
5909
6434
|
}
|
|
5910
6435
|
|
|
5911
|
-
const showGitDiffModal = async (diffData = null, modalTitle =
|
|
5912
|
-
|
|
5913
|
-
let
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
if (!
|
|
6436
|
+
const showGitDiffModal = async (diffData = null, modalTitle = null, showSaveButton = true) => {
|
|
6437
|
+
const diffDataIsObject = diffData && typeof diffData === 'object' && !Array.isArray(diffData)
|
|
6438
|
+
let repoParam = diffDataIsObject ? (diffData.repoParam || diffData.repoKey || null) : null
|
|
6439
|
+
let repoDisplayName = diffDataIsObject ? (diffData.repoName || diffData.name || null) : null
|
|
6440
|
+
|
|
6441
|
+
if (!repoParam) {
|
|
6442
|
+
repoParam = activeRepoKey || null
|
|
6443
|
+
}
|
|
6444
|
+
|
|
6445
|
+
if (!repoParam && repoStatusCache.size > 0) {
|
|
6446
|
+
const firstEntry = repoStatusCache.values().next().value
|
|
6447
|
+
if (firstEntry) {
|
|
6448
|
+
repoParam = firstEntry.repoParam
|
|
6449
|
+
if (!repoDisplayName) {
|
|
6450
|
+
repoDisplayName = firstEntry.name
|
|
6451
|
+
}
|
|
6452
|
+
}
|
|
6453
|
+
}
|
|
6454
|
+
|
|
6455
|
+
if (!repoParam) {
|
|
6456
|
+
await check_git()
|
|
6457
|
+
const fallback = repoStatusCache.values().next().value
|
|
6458
|
+
if (fallback) {
|
|
6459
|
+
repoParam = fallback.repoParam
|
|
6460
|
+
repoDisplayName = repoDisplayName || fallback.name
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
|
|
6464
|
+
if (!repoParam) {
|
|
6465
|
+
Swal.fire({
|
|
6466
|
+
title: 'No repositories',
|
|
6467
|
+
text: 'No Git repositories were detected for this workspace.',
|
|
6468
|
+
icon: 'info'
|
|
6469
|
+
})
|
|
6470
|
+
return
|
|
6471
|
+
}
|
|
6472
|
+
|
|
6473
|
+
let repoData = repoStatusCache.get(repoParam)
|
|
6474
|
+
if (!repoData) {
|
|
6475
|
+
await check_git()
|
|
6476
|
+
repoData = repoStatusCache.get(repoParam)
|
|
6477
|
+
}
|
|
6478
|
+
|
|
6479
|
+
if (!repoDisplayName && repoData) {
|
|
6480
|
+
repoDisplayName = repoData.name
|
|
6481
|
+
}
|
|
6482
|
+
if (!repoDisplayName) {
|
|
6483
|
+
repoDisplayName = repoParam
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6486
|
+
let changes
|
|
6487
|
+
if (diffDataIsObject && Array.isArray(diffData.changes)) {
|
|
6488
|
+
changes = diffData.changes
|
|
6489
|
+
} else if (repoData && Array.isArray(repoData.changes)) {
|
|
6490
|
+
changes = repoData.changes
|
|
6491
|
+
} else {
|
|
6492
|
+
changes = currentChanges
|
|
6493
|
+
}
|
|
6494
|
+
|
|
6495
|
+
let commitUrl
|
|
6496
|
+
if (diffDataIsObject && diffData.git_commit_url) {
|
|
6497
|
+
commitUrl = diffData.git_commit_url
|
|
6498
|
+
} else if (repoData && repoData.git_commit_url) {
|
|
6499
|
+
commitUrl = repoData.git_commit_url
|
|
6500
|
+
} else {
|
|
6501
|
+
commitUrl = gitCommitUrl
|
|
6502
|
+
}
|
|
6503
|
+
|
|
6504
|
+
const shouldForceRefresh = Boolean(diffDataIsObject && diffData.forceRefresh)
|
|
6505
|
+
|
|
6506
|
+
if (!changes || changes.length === 0 || shouldForceRefresh) {
|
|
5917
6507
|
try {
|
|
5918
|
-
const response = await fetch(
|
|
6508
|
+
const response = await fetch(`/gitcommit/HEAD/${encodeRepoPath(repoParam)}`)
|
|
6509
|
+
if (!response.ok) {
|
|
6510
|
+
throw new Error(`HTTP ${response.status}`)
|
|
6511
|
+
}
|
|
5919
6512
|
const freshData = await response.json()
|
|
5920
6513
|
changes = freshData.changes || []
|
|
5921
|
-
commitUrl = freshData.git_commit_url ||
|
|
5922
|
-
|
|
5923
|
-
|
|
6514
|
+
commitUrl = freshData.git_commit_url || commitUrl
|
|
6515
|
+
const updatedRepo = repoData || { repoParam }
|
|
6516
|
+
updatedRepo.changes = changes
|
|
6517
|
+
updatedRepo.changeCount = changes.length
|
|
6518
|
+
updatedRepo.git_commit_url = freshData.git_commit_url || null
|
|
6519
|
+
updatedRepo.name = updatedRepo.name || repoDisplayName || repoParam
|
|
6520
|
+
updatedRepo.git_history_url = updatedRepo.git_history_url || (repoData && repoData.git_history_url) || (repoParam ? `/info/git/HEAD/${encodeRepoPath(repoParam)}` : null)
|
|
6521
|
+
repoStatusCache.set(repoParam, updatedRepo)
|
|
6522
|
+
repoData = updatedRepo
|
|
6523
|
+
const listEntry = lastRepoList.find((entry) => entry.repoParam === repoParam)
|
|
6524
|
+
if (listEntry) {
|
|
6525
|
+
listEntry.changes = changes
|
|
6526
|
+
listEntry.changeCount = changes.length
|
|
6527
|
+
listEntry.git_commit_url = updatedRepo.git_commit_url
|
|
6528
|
+
if (updatedRepo.git_history_url) {
|
|
6529
|
+
listEntry.git_history_url = updatedRepo.git_history_url
|
|
6530
|
+
}
|
|
6531
|
+
}
|
|
6532
|
+
const total = Array.from(repoStatusCache.values()).reduce((sum, repo) => sum + (repo.changeCount || 0), 0)
|
|
6533
|
+
updateCombinedBadge(total)
|
|
6534
|
+
if (lastRepoList.length > 0) {
|
|
6535
|
+
renderChangesDropdown(lastRepoList)
|
|
6536
|
+
}
|
|
5924
6537
|
} catch (error) {
|
|
5925
|
-
console.error('Failed to fetch
|
|
6538
|
+
console.error('Failed to fetch repo changes:', error)
|
|
5926
6539
|
}
|
|
5927
6540
|
}
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
6541
|
+
|
|
6542
|
+
activeRepoKey = repoParam
|
|
6543
|
+
currentChanges = changes || []
|
|
6544
|
+
gitCommitUrl = commitUrl
|
|
6545
|
+
|
|
6546
|
+
const title = modalTitle || `File Changes${repoDisplayName ? ` — ${repoDisplayName}` : ''}`
|
|
6547
|
+
|
|
6548
|
+
if (lastRepoList.length > 0) {
|
|
6549
|
+
renderChangesDropdown(lastRepoList)
|
|
6550
|
+
}
|
|
6551
|
+
|
|
6552
|
+
if (!changes || changes.length === 0) {
|
|
6553
|
+
try {
|
|
6554
|
+
await showGitHistoryModal({
|
|
6555
|
+
repoParam,
|
|
6556
|
+
repoName: repoDisplayName,
|
|
6557
|
+
historyUrl: repoData && repoData.git_history_url ? repoData.git_history_url : null,
|
|
6558
|
+
})
|
|
6559
|
+
} catch (error) {
|
|
6560
|
+
console.error('Failed to open history for clean repository:', error)
|
|
6561
|
+
}
|
|
5945
6562
|
return
|
|
5946
6563
|
}
|
|
5947
|
-
|
|
6564
|
+
|
|
5948
6565
|
const changeSummary = `${changes.length} file${changes.length === 1 ? '' : 's'} changed`
|
|
5949
6566
|
const statusCounts = { added: 0, modified: 0, deleted: 0, renamed: 0 }
|
|
5950
6567
|
changes.forEach(change => {
|
|
@@ -5975,7 +6592,7 @@ body.dark {
|
|
|
5975
6592
|
<div class="pinokio-modal-header">
|
|
5976
6593
|
<div class="pinokio-modal-icon"><i class="fa-solid fa-code-branch"></i></div>
|
|
5977
6594
|
<div class="pinokio-modal-heading">
|
|
5978
|
-
<div class="pinokio-modal-title">${
|
|
6595
|
+
<div class="pinokio-modal-title">${title}</div>
|
|
5979
6596
|
<div class="pinokio-modal-subtitle">${changeSummary}</div>
|
|
5980
6597
|
</div>
|
|
5981
6598
|
</div>
|
|
@@ -6110,9 +6727,54 @@ body.dark {
|
|
|
6110
6727
|
container.innerHTML = diffHtml
|
|
6111
6728
|
}
|
|
6112
6729
|
|
|
6113
|
-
const showGitHistoryModal = async () => {
|
|
6114
|
-
const
|
|
6115
|
-
|
|
6730
|
+
const showGitHistoryModal = async (options = {}) => {
|
|
6731
|
+
const opts = options && typeof options === 'object' ? options : {}
|
|
6732
|
+
const buildHistoryUrl = (param) => {
|
|
6733
|
+
if (!param) {
|
|
6734
|
+
return null
|
|
6735
|
+
}
|
|
6736
|
+
return `/info/git/HEAD/${encodeRepoPath(param)}`
|
|
6737
|
+
}
|
|
6738
|
+
|
|
6739
|
+
let repoParam = opts.repoParam ?? null
|
|
6740
|
+
let repoName = opts.repoName ?? null
|
|
6741
|
+
let historyUrl = opts.historyUrl ?? null
|
|
6742
|
+
|
|
6743
|
+
if (!repoParam) {
|
|
6744
|
+
repoParam = activeRepoKey || null
|
|
6745
|
+
}
|
|
6746
|
+
|
|
6747
|
+
let repoData = repoParam ? repoStatusCache.get(repoParam) : null
|
|
6748
|
+
|
|
6749
|
+
if (!repoName && repoData && repoData.name) {
|
|
6750
|
+
repoName = repoData.name
|
|
6751
|
+
}
|
|
6752
|
+
|
|
6753
|
+
if (!historyUrl && repoData && repoData.git_history_url) {
|
|
6754
|
+
historyUrl = repoData.git_history_url
|
|
6755
|
+
}
|
|
6756
|
+
|
|
6757
|
+
const fallbackHistoryUri = readDataAttr(fsStatusEl, 'data-history-uri')
|
|
6758
|
+
|
|
6759
|
+
if (!historyUrl) {
|
|
6760
|
+
if (repoParam) {
|
|
6761
|
+
historyUrl = buildHistoryUrl(repoParam)
|
|
6762
|
+
} else if (fallbackHistoryUri) {
|
|
6763
|
+
historyUrl = fallbackHistoryUri
|
|
6764
|
+
}
|
|
6765
|
+
} else if (repoParam && typeof historyUrl === 'string' && historyUrl.startsWith('/info/git/HEAD/')) {
|
|
6766
|
+
historyUrl = buildHistoryUrl(repoParam)
|
|
6767
|
+
}
|
|
6768
|
+
|
|
6769
|
+
if (!repoName) {
|
|
6770
|
+
if (repoParam) {
|
|
6771
|
+
repoName = repoParam
|
|
6772
|
+
} else if (workspaceName) {
|
|
6773
|
+
repoName = workspaceName
|
|
6774
|
+
}
|
|
6775
|
+
}
|
|
6776
|
+
|
|
6777
|
+
if (!historyUrl) {
|
|
6116
6778
|
Swal.fire({
|
|
6117
6779
|
title: 'Error',
|
|
6118
6780
|
text: 'Git history URL not available.',
|
|
@@ -6120,16 +6782,15 @@ body.dark {
|
|
|
6120
6782
|
})
|
|
6121
6783
|
return
|
|
6122
6784
|
}
|
|
6123
|
-
|
|
6785
|
+
|
|
6124
6786
|
try {
|
|
6125
|
-
const response = await fetch(
|
|
6787
|
+
const response = await fetch(historyUrl)
|
|
6126
6788
|
if (!response.ok) {
|
|
6127
6789
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
6128
6790
|
}
|
|
6129
|
-
|
|
6791
|
+
|
|
6130
6792
|
const historyData = await response.json()
|
|
6131
|
-
displayGitHistory(historyData)
|
|
6132
|
-
|
|
6793
|
+
displayGitHistory(historyData, { repoName: repoName || null, repoParam: repoParam || null })
|
|
6133
6794
|
} catch (error) {
|
|
6134
6795
|
console.error('Failed to load git history:', error)
|
|
6135
6796
|
Swal.fire({
|
|
@@ -6140,7 +6801,8 @@ body.dark {
|
|
|
6140
6801
|
}
|
|
6141
6802
|
}
|
|
6142
6803
|
|
|
6143
|
-
const displayGitHistory = (historyData) => {
|
|
6804
|
+
const displayGitHistory = (historyData, options = {}) => {
|
|
6805
|
+
const repoName = options && typeof options === 'object' ? options.repoName : null
|
|
6144
6806
|
const commits = historyData.log || []
|
|
6145
6807
|
const remote = historyData.remote || ''
|
|
6146
6808
|
const currentRef = historyData.ref || 'HEAD'
|
|
@@ -6159,12 +6821,14 @@ body.dark {
|
|
|
6159
6821
|
metaBadges.push(`<span class="pinokio-pill"><i class="fa-solid fa-cloud-arrow-up"></i>${remote}</span>`)
|
|
6160
6822
|
}
|
|
6161
6823
|
|
|
6824
|
+
const historyTitle = repoName ? `${repoName} history` : 'Repository history'
|
|
6825
|
+
|
|
6162
6826
|
const historyHtml = `
|
|
6163
6827
|
<div class="pinokio-modal-surface pinokio-modal-surface--history">
|
|
6164
6828
|
<div class="pinokio-modal-header">
|
|
6165
6829
|
<div class="pinokio-modal-icon"><i class="fa-solid fa-clock-rotate-left"></i></div>
|
|
6166
6830
|
<div class="pinokio-modal-heading">
|
|
6167
|
-
<div class="pinokio-modal-title"
|
|
6831
|
+
<div class="pinokio-modal-title">${historyTitle}</div>
|
|
6168
6832
|
<div class="pinokio-modal-subtitle">${subtitleText}</div>
|
|
6169
6833
|
</div>
|
|
6170
6834
|
</div>
|
|
@@ -6719,15 +7383,10 @@ body.dark {
|
|
|
6719
7383
|
}
|
|
6720
7384
|
}
|
|
6721
7385
|
|
|
6722
|
-
// Add click handlers for the buttons
|
|
6723
|
-
document.querySelector('#fs-changes-btn').addEventListener('click', () => showGitDiffModal())
|
|
6724
|
-
document.querySelector('#fs-history-btn').addEventListener('click', showGitHistoryModal)
|
|
6725
|
-
|
|
6726
7386
|
// Initialize the publish/create button
|
|
6727
7387
|
updatePublishButton()
|
|
6728
7388
|
|
|
6729
7389
|
check_git()
|
|
6730
|
-
setInterval(check_git, 10000)
|
|
6731
7390
|
<% } %>
|
|
6732
7391
|
|
|
6733
7392
|
setInterval(() => {
|