pinokiod 3.102.0 → 3.103.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/views/app.ejs +722 -91
package/server/views/app.ejs
CHANGED
|
@@ -1313,7 +1313,7 @@ body.dark #fs-status {
|
|
|
1313
1313
|
border-radius: 4px;
|
|
1314
1314
|
display: flex !important;
|
|
1315
1315
|
align-items: center;
|
|
1316
|
-
gap:
|
|
1316
|
+
gap: 4px;
|
|
1317
1317
|
font-size: 12px;
|
|
1318
1318
|
line-height: 1.2;
|
|
1319
1319
|
color: #24292f;
|
|
@@ -1351,6 +1351,13 @@ body.dark #fs-status {
|
|
|
1351
1351
|
min-width: 0;
|
|
1352
1352
|
}
|
|
1353
1353
|
|
|
1354
|
+
.fs-status-btn.fs-status-btn--disabled,
|
|
1355
|
+
.fs-status-btn:disabled {
|
|
1356
|
+
opacity: 0.45;
|
|
1357
|
+
cursor: default;
|
|
1358
|
+
pointer-events: none;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1354
1361
|
.fs-status-dropdown {
|
|
1355
1362
|
position: relative;
|
|
1356
1363
|
}
|
|
@@ -1441,6 +1448,79 @@ body.dark #fs-status .fs-dropdown-menu .frame-link:hover {
|
|
|
1441
1448
|
background: rgba(148, 163, 184, 0.15);
|
|
1442
1449
|
}
|
|
1443
1450
|
|
|
1451
|
+
#fs-status .git-changes {
|
|
1452
|
+
margin-left: auto;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
#fs-changes-menu .fs-dropdown-empty {
|
|
1456
|
+
padding: 8px 14px;
|
|
1457
|
+
font-size: 12px;
|
|
1458
|
+
color: rgba(31, 41, 55, 0.66);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
#fs-changes-menu {
|
|
1462
|
+
left: auto;
|
|
1463
|
+
right: 0;
|
|
1464
|
+
width: max-content;
|
|
1465
|
+
max-width: min(460px, calc(100vw - 32px));
|
|
1466
|
+
max-height: min(70vh, 420px);
|
|
1467
|
+
overflow: auto;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
#fs-changes-menu .fs-dropdown-item {
|
|
1471
|
+
align-items: flex-start;
|
|
1472
|
+
white-space: normal;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
body.dark #fs-changes-menu .fs-dropdown-empty {
|
|
1476
|
+
color: rgba(226, 232, 240, 0.7);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
#fs-changes-menu .git-changes-item-label {
|
|
1480
|
+
display: flex;
|
|
1481
|
+
align-items: center;
|
|
1482
|
+
gap: 6px;
|
|
1483
|
+
min-width: 0;
|
|
1484
|
+
white-space: normal;
|
|
1485
|
+
word-break: break-word;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
#fs-changes-menu .git-changes-item-count {
|
|
1489
|
+
margin-left: auto;
|
|
1490
|
+
font-size: 12px;
|
|
1491
|
+
font-weight: 600;
|
|
1492
|
+
color: #1f2937;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
#fs-changes-menu .git-changes-item-count.git-changes-item-count--dirty {
|
|
1496
|
+
color: #2563eb;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
#fs-changes-menu .git-changes-item-count.git-changes-item-count--clean {
|
|
1500
|
+
font-weight: 500;
|
|
1501
|
+
color: rgba(31, 41, 55, 0.6);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
#fs-changes-menu .git-changes-item.git-changes-item--active {
|
|
1505
|
+
background: rgba(37, 99, 235, 0.08);
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
body.dark #fs-changes-menu .git-changes-item-count {
|
|
1509
|
+
color: #e2e8f0;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
body.dark #fs-changes-menu .git-changes-item-count.git-changes-item-count--dirty {
|
|
1513
|
+
color: #60a5fa;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
body.dark #fs-changes-menu .git-changes-item-count.git-changes-item-count--clean {
|
|
1517
|
+
color: rgba(148, 163, 184, 0.6);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
body.dark #fs-changes-menu .git-changes-item.git-changes-item--active {
|
|
1521
|
+
background: rgba(96, 165, 250, 0.15);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1444
1524
|
#fs-status .app-info {
|
|
1445
1525
|
display: flex;
|
|
1446
1526
|
align-items: center;
|
|
@@ -1508,15 +1588,17 @@ body.dark .fs-status-btn:hover {
|
|
|
1508
1588
|
.fs-status-btn .badge {
|
|
1509
1589
|
background: #dc3545;
|
|
1510
1590
|
color: white;
|
|
1511
|
-
padding:
|
|
1512
|
-
border-radius:
|
|
1591
|
+
padding: 2px 5px;
|
|
1592
|
+
border-radius: 2px;
|
|
1513
1593
|
font-size: 11px;
|
|
1514
1594
|
font-weight: 600;
|
|
1515
1595
|
text-align: center;
|
|
1596
|
+
/*
|
|
1516
1597
|
position: absolute;
|
|
1517
1598
|
top: 0;
|
|
1518
1599
|
right: 0;
|
|
1519
1600
|
transform:translate(25%, -50%);
|
|
1601
|
+
*/
|
|
1520
1602
|
display: inline-flex;
|
|
1521
1603
|
align-items: center;
|
|
1522
1604
|
justify-content: center;
|
|
@@ -2596,7 +2678,7 @@ body.dark {
|
|
|
2596
2678
|
<% } %>
|
|
2597
2679
|
<div class='container'>
|
|
2598
2680
|
<% 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%>">
|
|
2681
|
+
<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
2682
|
<a target="<%=src%>" href="<%=src%>" class='fs-status-btn frame-link' data-index="0" data-mode="refresh" data-type="n">
|
|
2601
2683
|
<span class='fs-status-label'>
|
|
2602
2684
|
<i class="fa-regular fa-folder-open"></i>
|
|
@@ -2618,6 +2700,7 @@ body.dark {
|
|
|
2618
2700
|
<button type='button' class='fs-dropdown-item' id='delete' data-name="<%=name%>"><i class="fa-solid fa-trash-can"></i> Delete</button>
|
|
2619
2701
|
</div>
|
|
2620
2702
|
</div>
|
|
2703
|
+
<!--
|
|
2621
2704
|
<div class='fs-status-dropdown nested-menu git blue'>
|
|
2622
2705
|
<button type='button' class='fs-status-btn frame-link reveal'>
|
|
2623
2706
|
<span class='fs-status-label'>
|
|
@@ -2628,6 +2711,7 @@ body.dark {
|
|
|
2628
2711
|
<div class='fs-dropdown-menu submenu hidden' id='git-repos'>
|
|
2629
2712
|
</div>
|
|
2630
2713
|
</div>
|
|
2714
|
+
-->
|
|
2631
2715
|
<div class='fs-status-dropdown'>
|
|
2632
2716
|
<button type='button' class='fs-status-btn revealer' data-group='#fs-settings-menu'>
|
|
2633
2717
|
<span class='fs-status-label'><i class='fa-solid fa-gear'></i> Settings</span>
|
|
@@ -2641,13 +2725,13 @@ body.dark {
|
|
|
2641
2725
|
</a>
|
|
2642
2726
|
</div>
|
|
2643
2727
|
</div>
|
|
2644
|
-
<
|
|
2645
|
-
<
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
<
|
|
2650
|
-
</
|
|
2728
|
+
<div class='fs-status-dropdown git-changes'>
|
|
2729
|
+
<button id='fs-changes-btn' class='fs-status-btn revealer' data-group='#fs-changes-menu' type='button'>
|
|
2730
|
+
<span class='fs-status-label'><i class="fa-solid fa-code-compare"></i> Changes</span>
|
|
2731
|
+
<div class='badge'></div>
|
|
2732
|
+
</button>
|
|
2733
|
+
<div class='fs-dropdown-menu submenu hidden' id='fs-changes-menu'></div>
|
|
2734
|
+
</div>
|
|
2651
2735
|
<button id='fs-push-btn' class='fs-status-btn'>
|
|
2652
2736
|
<span class='fs-status-label'>
|
|
2653
2737
|
<i class="fa-brands fa-github"></i>
|
|
@@ -2854,6 +2938,82 @@ body.dark {
|
|
|
2854
2938
|
}
|
|
2855
2939
|
}
|
|
2856
2940
|
|
|
2941
|
+
const ensureHttpDirectoryUrl = (value) => {
|
|
2942
|
+
try {
|
|
2943
|
+
const parsed = new URL(value)
|
|
2944
|
+
if (parsed.protocol.toLowerCase() !== "http:") {
|
|
2945
|
+
return value
|
|
2946
|
+
}
|
|
2947
|
+
let pathname = parsed.pathname || "/"
|
|
2948
|
+
const lastSegment = pathname.split("/").pop() || ""
|
|
2949
|
+
const hasExtension = lastSegment.includes(".")
|
|
2950
|
+
if (!hasExtension && !pathname.endsWith("/")) {
|
|
2951
|
+
pathname = `${pathname}/`
|
|
2952
|
+
parsed.pathname = pathname
|
|
2953
|
+
}
|
|
2954
|
+
parsed.hash = parsed.hash || ""
|
|
2955
|
+
parsed.search = parsed.search || ""
|
|
2956
|
+
return parsed.toString()
|
|
2957
|
+
} catch (_) {
|
|
2958
|
+
return value
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
const isLocalHostLike = (hostname) => {
|
|
2963
|
+
if (!hostname) {
|
|
2964
|
+
return false
|
|
2965
|
+
}
|
|
2966
|
+
const hostLower = hostname.toLowerCase()
|
|
2967
|
+
if (hostLower === location.hostname.toLowerCase()) {
|
|
2968
|
+
return true
|
|
2969
|
+
}
|
|
2970
|
+
if (hostLower === "localhost" || hostLower === "0.0.0.0") {
|
|
2971
|
+
return true
|
|
2972
|
+
}
|
|
2973
|
+
if (hostLower.startsWith("127.")) {
|
|
2974
|
+
return true
|
|
2975
|
+
}
|
|
2976
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(hostLower)) {
|
|
2977
|
+
return true
|
|
2978
|
+
}
|
|
2979
|
+
return false
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
const extractProjectSlug = (node) => {
|
|
2983
|
+
if (!node) {
|
|
2984
|
+
return ""
|
|
2985
|
+
}
|
|
2986
|
+
const candidates = []
|
|
2987
|
+
const targetFull = node.getAttribute("data-target-full")
|
|
2988
|
+
if (typeof targetFull === "string" && targetFull.length > 0) {
|
|
2989
|
+
candidates.push(targetFull)
|
|
2990
|
+
}
|
|
2991
|
+
const dataHref = node.getAttribute("href")
|
|
2992
|
+
if (typeof dataHref === "string" && dataHref.length > 0) {
|
|
2993
|
+
candidates.push(dataHref)
|
|
2994
|
+
}
|
|
2995
|
+
try {
|
|
2996
|
+
const absolute = new URL(node.href, location.origin)
|
|
2997
|
+
candidates.push(absolute.pathname)
|
|
2998
|
+
} catch (_) {
|
|
2999
|
+
// ignore
|
|
3000
|
+
}
|
|
3001
|
+
for (const candidate of candidates) {
|
|
3002
|
+
if (typeof candidate !== "string" || candidate.length === 0) {
|
|
3003
|
+
continue
|
|
3004
|
+
}
|
|
3005
|
+
const assetMatch = candidate.match(/\/asset\/api\/([^\/?#]+)/i)
|
|
3006
|
+
if (assetMatch && assetMatch[1]) {
|
|
3007
|
+
return assetMatch[1]
|
|
3008
|
+
}
|
|
3009
|
+
const pageMatch = candidate.match(/\/p\/([^\/?#]+)/i)
|
|
3010
|
+
if (pageMatch && pageMatch[1]) {
|
|
3011
|
+
return pageMatch[1]
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
return ""
|
|
3015
|
+
}
|
|
3016
|
+
|
|
2857
3017
|
const formatDisplayUrl = (value) => {
|
|
2858
3018
|
try {
|
|
2859
3019
|
const parsed = new URL(value, location.origin)
|
|
@@ -3344,13 +3504,17 @@ body.dark {
|
|
|
3344
3504
|
}
|
|
3345
3505
|
|
|
3346
3506
|
const baseHref = link.href
|
|
3507
|
+
const projectSlug = extractProjectSlug(link).toLowerCase()
|
|
3347
3508
|
const entries = []
|
|
3348
3509
|
const entryByUrl = new Map()
|
|
3349
3510
|
const addEntry = (type, label, url) => {
|
|
3350
3511
|
if (!url) {
|
|
3351
3512
|
return
|
|
3352
3513
|
}
|
|
3353
|
-
|
|
3514
|
+
let canonical = canonicalizeUrl(url)
|
|
3515
|
+
if (canonical && type === "http") {
|
|
3516
|
+
canonical = ensureHttpDirectoryUrl(canonical)
|
|
3517
|
+
}
|
|
3354
3518
|
if (!canonical) {
|
|
3355
3519
|
return
|
|
3356
3520
|
}
|
|
@@ -3360,13 +3524,6 @@ body.dark {
|
|
|
3360
3524
|
const originLower = parsed.origin.toLowerCase()
|
|
3361
3525
|
if (originLower === location.origin.toLowerCase()) {
|
|
3362
3526
|
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
3527
|
}
|
|
3371
3528
|
} catch (_) {
|
|
3372
3529
|
// ignore parse failures but do not skip by default
|
|
@@ -3404,6 +3561,26 @@ body.dark {
|
|
|
3404
3561
|
httpsCandidates.add(canonicalizeUrl(baseHref))
|
|
3405
3562
|
}
|
|
3406
3563
|
|
|
3564
|
+
if (projectSlug) {
|
|
3565
|
+
try {
|
|
3566
|
+
const baseUrl = new URL(baseHref, location.origin)
|
|
3567
|
+
let pathname = baseUrl.pathname || "/"
|
|
3568
|
+
if (pathname.endsWith("/index.html")) {
|
|
3569
|
+
pathname = pathname.slice(0, -"/index.html".length)
|
|
3570
|
+
}
|
|
3571
|
+
if (!pathname.endsWith("/")) {
|
|
3572
|
+
pathname = `${pathname}/`
|
|
3573
|
+
}
|
|
3574
|
+
const normalizedPath = pathname.toLowerCase()
|
|
3575
|
+
if (normalizedPath.includes(`/asset/api/${projectSlug}`)) {
|
|
3576
|
+
const fallbackHttp = `http://127.0.0.1:42000${pathname}`
|
|
3577
|
+
httpCandidates.add(canonicalizeUrl(fallbackHttp))
|
|
3578
|
+
}
|
|
3579
|
+
} catch (_) {
|
|
3580
|
+
// ignore fallback errors
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3407
3584
|
const scriptKeys = collectScriptKeys(link)
|
|
3408
3585
|
if (scriptKeys.length > 0) {
|
|
3409
3586
|
const localInfo = await ensureLocalMemory()
|
|
@@ -3437,9 +3614,29 @@ body.dark {
|
|
|
3437
3614
|
})
|
|
3438
3615
|
}
|
|
3439
3616
|
|
|
3440
|
-
const httpList = Array.from(httpCandidates).sort()
|
|
3441
3617
|
const httpsList = Array.from(httpsCandidates).sort()
|
|
3442
3618
|
|
|
3619
|
+
if (httpsList.length > 0) {
|
|
3620
|
+
httpsList.forEach((url) => {
|
|
3621
|
+
try {
|
|
3622
|
+
const parsed = new URL(url)
|
|
3623
|
+
if (parsed.protocol.toLowerCase() !== "https:") {
|
|
3624
|
+
return
|
|
3625
|
+
}
|
|
3626
|
+
if (!parsed.port || parsed.port !== "42000") {
|
|
3627
|
+
return
|
|
3628
|
+
}
|
|
3629
|
+
const hostPort = parsed.port ? `${parsed.hostname}:${parsed.port}` : parsed.hostname
|
|
3630
|
+
const httpUrl = `http://${hostPort}${parsed.pathname || "/"}${parsed.search || ""}`
|
|
3631
|
+
httpCandidates.add(canonicalizeUrl(httpUrl))
|
|
3632
|
+
} catch (_) {
|
|
3633
|
+
// ignore failures
|
|
3634
|
+
}
|
|
3635
|
+
})
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
const httpList = Array.from(httpCandidates).sort()
|
|
3639
|
+
|
|
3443
3640
|
httpList.forEach((url) => {
|
|
3444
3641
|
addEntry("http", "HTTP", url)
|
|
3445
3642
|
})
|
|
@@ -3512,12 +3709,12 @@ body.dark {
|
|
|
3512
3709
|
return
|
|
3513
3710
|
}
|
|
3514
3711
|
|
|
3712
|
+
let sameOrigin = false
|
|
3713
|
+
let canonicalBase = canonicalizeUrl(link.href)
|
|
3515
3714
|
try {
|
|
3516
|
-
const
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
return
|
|
3520
|
-
}
|
|
3715
|
+
const linkUrl = new URL(link.href, location.href)
|
|
3716
|
+
sameOrigin = linkUrl.origin === location.origin
|
|
3717
|
+
canonicalBase = canonicalizeUrl(linkUrl.href)
|
|
3521
3718
|
} catch (_) {
|
|
3522
3719
|
hideTabLinkPopover({ immediate: true })
|
|
3523
3720
|
return
|
|
@@ -3558,6 +3755,59 @@ body.dark {
|
|
|
3558
3755
|
return
|
|
3559
3756
|
}
|
|
3560
3757
|
|
|
3758
|
+
if (sameOrigin) {
|
|
3759
|
+
const slug = extractProjectSlug(link).toLowerCase()
|
|
3760
|
+
if (slug) {
|
|
3761
|
+
entries = entries.filter((entry) => {
|
|
3762
|
+
if (!entry || !entry.url) {
|
|
3763
|
+
return false
|
|
3764
|
+
}
|
|
3765
|
+
if (entry.url === canonicalBase) {
|
|
3766
|
+
return true
|
|
3767
|
+
}
|
|
3768
|
+
try {
|
|
3769
|
+
const parsed = new URL(entry.url)
|
|
3770
|
+
const hostLower = parsed.hostname ? parsed.hostname.toLowerCase() : ""
|
|
3771
|
+
if (isLocalHostLike(hostLower)) {
|
|
3772
|
+
if (entry.type === "http") {
|
|
3773
|
+
const pathLower = parsed.pathname ? parsed.pathname.toLowerCase() : ""
|
|
3774
|
+
if (pathLower.includes(`/asset/api/${slug}`) || pathLower.includes(`/p/${slug}`)) {
|
|
3775
|
+
return true
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
return false
|
|
3779
|
+
}
|
|
3780
|
+
const pathLower = parsed.pathname ? parsed.pathname.toLowerCase() : ""
|
|
3781
|
+
if (pathLower.includes(`/asset/api/${slug}`)) {
|
|
3782
|
+
return true
|
|
3783
|
+
}
|
|
3784
|
+
if (pathLower.includes(`/p/${slug}`)) {
|
|
3785
|
+
return true
|
|
3786
|
+
}
|
|
3787
|
+
if (hostLower.split(".").some((part) => part === slug)) {
|
|
3788
|
+
return true
|
|
3789
|
+
}
|
|
3790
|
+
} catch (_) {
|
|
3791
|
+
return false
|
|
3792
|
+
}
|
|
3793
|
+
return false
|
|
3794
|
+
})
|
|
3795
|
+
} else {
|
|
3796
|
+
entries = entries.filter((entry) => entry.url === canonicalBase)
|
|
3797
|
+
}
|
|
3798
|
+
|
|
3799
|
+
const hasAlternate = entries.some((entry) => entry.url !== canonicalBase)
|
|
3800
|
+
if (!hasAlternate) {
|
|
3801
|
+
hideTabLinkPopover({ immediate: true })
|
|
3802
|
+
return
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
if (!entries || entries.length === 0) {
|
|
3807
|
+
hideTabLinkPopover({ immediate: true })
|
|
3808
|
+
return
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3561
3811
|
const popover = ensureTabLinkPopoverEl()
|
|
3562
3812
|
popover.innerHTML = ""
|
|
3563
3813
|
|
|
@@ -5180,6 +5430,14 @@ body.dark {
|
|
|
5180
5430
|
target = e.target.classList.contains("revealer") ? e.target : e.target.closest(".revealer")
|
|
5181
5431
|
|
|
5182
5432
|
if (target) {
|
|
5433
|
+
if (target.classList.contains('fs-status-btn--disabled') || target.disabled) {
|
|
5434
|
+
e.preventDefault()
|
|
5435
|
+
e.stopPropagation()
|
|
5436
|
+
return
|
|
5437
|
+
}
|
|
5438
|
+
if (target.id === 'fs-changes-btn') {
|
|
5439
|
+
check_git()
|
|
5440
|
+
}
|
|
5183
5441
|
e.preventDefault()
|
|
5184
5442
|
e.stopPropagation()
|
|
5185
5443
|
const group = target.getAttribute("data-group")
|
|
@@ -5612,12 +5870,14 @@ body.dark {
|
|
|
5612
5870
|
restoreAllTabStates()
|
|
5613
5871
|
<% } else { %>
|
|
5614
5872
|
try_dynamic()
|
|
5873
|
+
/*
|
|
5615
5874
|
const repos = await fetch("<%=repos%>").then((res) => {
|
|
5616
5875
|
return res.text()
|
|
5617
5876
|
})
|
|
5618
5877
|
if (document.querySelector("#git-repos")) {
|
|
5619
5878
|
document.querySelector("#git-repos").innerHTML = repos
|
|
5620
5879
|
}
|
|
5880
|
+
*/
|
|
5621
5881
|
<% } %>
|
|
5622
5882
|
|
|
5623
5883
|
|
|
@@ -5725,6 +5985,7 @@ body.dark {
|
|
|
5725
5985
|
refresh_du("logs")
|
|
5726
5986
|
renderSelection({ force: true })
|
|
5727
5987
|
<% if (type !== 'run') { %>
|
|
5988
|
+
/*
|
|
5728
5989
|
fetch("<%=repos%>").then((res) => {
|
|
5729
5990
|
return res.text()
|
|
5730
5991
|
}).then((repos) => {
|
|
@@ -5733,6 +5994,7 @@ body.dark {
|
|
|
5733
5994
|
}
|
|
5734
5995
|
})
|
|
5735
5996
|
refresh()
|
|
5997
|
+
*/
|
|
5736
5998
|
<% } %>
|
|
5737
5999
|
<% if (plugin_menu) { %>
|
|
5738
6000
|
// document.querySelector(".dynamic .reveal").click()
|
|
@@ -5772,31 +6034,266 @@ body.dark {
|
|
|
5772
6034
|
});
|
|
5773
6035
|
*/
|
|
5774
6036
|
<% if (type === "browse") { %>
|
|
6037
|
+
const repoStatusCache = new Map()
|
|
6038
|
+
let lastRepoList = []
|
|
5775
6039
|
let currentChanges = []
|
|
5776
6040
|
let gitCommitUrl = null
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
6041
|
+
let activeRepoKey = null
|
|
6042
|
+
let gitStatusRequest = null
|
|
6043
|
+
|
|
6044
|
+
const fsStatusEl = document.querySelector('#fs-status')
|
|
6045
|
+
const changesDropdownContainer = document.querySelector('#fs-status .git-changes')
|
|
6046
|
+
const changesMenu = document.getElementById('fs-changes-menu')
|
|
6047
|
+
const changesBtn = document.getElementById('fs-changes-btn')
|
|
6048
|
+
const badgeElement = changesBtn ? changesBtn.querySelector('.badge') : null
|
|
6049
|
+
|
|
6050
|
+
const readDataAttr = (node, attr) => {
|
|
6051
|
+
if (!node) {
|
|
6052
|
+
return null
|
|
6053
|
+
}
|
|
6054
|
+
const value = node.getAttribute(attr)
|
|
6055
|
+
if (!value || value === 'null' || value === 'undefined') {
|
|
6056
|
+
return null
|
|
6057
|
+
}
|
|
6058
|
+
return value
|
|
6059
|
+
}
|
|
6060
|
+
|
|
6061
|
+
const statusUri = readDataAttr(fsStatusEl, 'data-status-uri')
|
|
6062
|
+
const monitorUri = readDataAttr(fsStatusEl, 'data-uri')
|
|
6063
|
+
const workspaceName = readDataAttr(fsStatusEl, 'data-workspace')
|
|
6064
|
+
|
|
6065
|
+
const encodeRepoPath = (value) => {
|
|
6066
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
6067
|
+
return ''
|
|
6068
|
+
}
|
|
6069
|
+
return value.split('/').map(encodeURIComponent).join('/')
|
|
6070
|
+
}
|
|
6071
|
+
|
|
6072
|
+
const updateCombinedBadge = (total) => {
|
|
6073
|
+
if (!badgeElement) {
|
|
6074
|
+
return
|
|
6075
|
+
}
|
|
6076
|
+
badgeElement.textContent = total > 0 ? String(total) : ''
|
|
6077
|
+
}
|
|
6078
|
+
|
|
6079
|
+
const updateChangesButtonState = (hasRepos) => {
|
|
6080
|
+
if (!changesBtn) {
|
|
6081
|
+
return
|
|
6082
|
+
}
|
|
6083
|
+
if (hasRepos) {
|
|
6084
|
+
changesBtn.disabled = false
|
|
6085
|
+
changesBtn.classList.remove('fs-status-btn--disabled')
|
|
6086
|
+
} else {
|
|
6087
|
+
changesBtn.disabled = true
|
|
6088
|
+
changesBtn.classList.add('fs-status-btn--disabled')
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6092
|
+
const setChangesMenuMessage = (message) => {
|
|
6093
|
+
if (!changesMenu) {
|
|
6094
|
+
return
|
|
6095
|
+
}
|
|
6096
|
+
const messageEl = document.createElement('div')
|
|
6097
|
+
messageEl.className = 'fs-dropdown-empty'
|
|
6098
|
+
messageEl.textContent = message
|
|
6099
|
+
changesMenu.innerHTML = ''
|
|
6100
|
+
changesMenu.append(messageEl)
|
|
6101
|
+
}
|
|
6102
|
+
|
|
6103
|
+
const attachRepoDropdownHandlers = () => {
|
|
6104
|
+
if (!changesMenu) {
|
|
6105
|
+
return
|
|
6106
|
+
}
|
|
6107
|
+
const items = changesMenu.querySelectorAll('.git-changes-item')
|
|
6108
|
+
items.forEach((item) => {
|
|
6109
|
+
item.addEventListener('click', async (event) => {
|
|
6110
|
+
event.preventDefault()
|
|
6111
|
+
event.stopPropagation()
|
|
6112
|
+
const repoKey = item.dataset.repo
|
|
6113
|
+
const repoName = item.dataset.name
|
|
6114
|
+
if (repoKey) {
|
|
6115
|
+
activeRepoKey = repoKey
|
|
6116
|
+
}
|
|
6117
|
+
items.forEach((node) => {
|
|
6118
|
+
if (node === item) {
|
|
6119
|
+
node.classList.add('git-changes-item--active')
|
|
6120
|
+
} else {
|
|
6121
|
+
node.classList.remove('git-changes-item--active')
|
|
6122
|
+
}
|
|
6123
|
+
})
|
|
6124
|
+
closeStatusDropdowns()
|
|
6125
|
+
try {
|
|
6126
|
+
await showGitDiffModal({ repoParam: repoKey, repoName })
|
|
6127
|
+
} catch (err) {
|
|
6128
|
+
console.error('Failed to open diff modal:', err)
|
|
6129
|
+
}
|
|
6130
|
+
})
|
|
6131
|
+
})
|
|
6132
|
+
}
|
|
6133
|
+
|
|
6134
|
+
const renderChangesDropdown = (repos) => {
|
|
6135
|
+
if (!changesMenu) {
|
|
6136
|
+
return
|
|
6137
|
+
}
|
|
6138
|
+
|
|
6139
|
+
if (!Array.isArray(repos) || repos.length === 0) {
|
|
6140
|
+
setChangesMenuMessage('No Git repositories detected')
|
|
6141
|
+
updateChangesButtonState(false)
|
|
6142
|
+
return
|
|
6143
|
+
}
|
|
6144
|
+
|
|
6145
|
+
const fragment = document.createDocumentFragment()
|
|
6146
|
+
repos.forEach((repo) => {
|
|
6147
|
+
const button = document.createElement('button')
|
|
6148
|
+
button.type = 'button'
|
|
6149
|
+
button.className = 'fs-dropdown-item git-changes-item'
|
|
6150
|
+
button.dataset.repo = repo.repoParam
|
|
6151
|
+
button.dataset.name = repo.name
|
|
6152
|
+
if (repo.main) {
|
|
6153
|
+
button.dataset.main = 'true'
|
|
5795
6154
|
}
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
6155
|
+
if (repo.repoParam === activeRepoKey) {
|
|
6156
|
+
button.classList.add('git-changes-item--active')
|
|
6157
|
+
}
|
|
6158
|
+
|
|
6159
|
+
button.innerHTML = `
|
|
6160
|
+
<span class="git-changes-item-label">
|
|
6161
|
+
<i class="fa-solid fa-code-branch"></i>
|
|
6162
|
+
<span>${repo.name}</span>
|
|
6163
|
+
</span>
|
|
6164
|
+
<span class="git-changes-item-count ${repo.changeCount > 0 ? 'git-changes-item-count--dirty' : 'git-changes-item-count--clean'}">
|
|
6165
|
+
${repo.changeCount > 0 ? repo.changeCount : 'Clean'}
|
|
6166
|
+
</span>
|
|
6167
|
+
`
|
|
6168
|
+
|
|
6169
|
+
fragment.append(button)
|
|
5799
6170
|
})
|
|
6171
|
+
|
|
6172
|
+
changesMenu.innerHTML = ''
|
|
6173
|
+
changesMenu.append(fragment)
|
|
6174
|
+
updateChangesButtonState(true)
|
|
6175
|
+
attachRepoDropdownHandlers()
|
|
6176
|
+
}
|
|
6177
|
+
|
|
6178
|
+
const updateFromLegacyMonitor = async () => {
|
|
6179
|
+
if (!monitorUri) {
|
|
6180
|
+
setChangesMenuMessage('Git status data unavailable')
|
|
6181
|
+
updateCombinedBadge(0)
|
|
6182
|
+
updateChangesButtonState(false)
|
|
6183
|
+
return false
|
|
6184
|
+
}
|
|
6185
|
+
try {
|
|
6186
|
+
const response = await fetch(monitorUri)
|
|
6187
|
+
if (!response.ok) {
|
|
6188
|
+
throw new Error(`HTTP ${response.status}`)
|
|
6189
|
+
}
|
|
6190
|
+
const data = await response.json()
|
|
6191
|
+
const repoKey = workspaceName || ''
|
|
6192
|
+
const fallbackRepo = {
|
|
6193
|
+
name: workspaceName || 'Current workspace',
|
|
6194
|
+
main: true,
|
|
6195
|
+
repoParam: repoKey,
|
|
6196
|
+
changeCount: Array.isArray(data.changes) ? data.changes.length : 0,
|
|
6197
|
+
changes: Array.isArray(data.changes) ? data.changes : [],
|
|
6198
|
+
git_commit_url: data.git_commit_url || null,
|
|
6199
|
+
git_history_url: workspaceName ? `/info/git/HEAD/${encodeRepoPath(workspaceName)}` : readDataAttr(fsStatusEl, 'data-history-uri'),
|
|
6200
|
+
url: null,
|
|
6201
|
+
}
|
|
6202
|
+
|
|
6203
|
+
repoStatusCache.clear()
|
|
6204
|
+
repoStatusCache.set(fallbackRepo.repoParam, fallbackRepo)
|
|
6205
|
+
lastRepoList = [fallbackRepo]
|
|
6206
|
+
activeRepoKey = fallbackRepo.repoParam
|
|
6207
|
+
currentChanges = fallbackRepo.changes
|
|
6208
|
+
gitCommitUrl = fallbackRepo.git_commit_url
|
|
6209
|
+
|
|
6210
|
+
renderChangesDropdown(lastRepoList)
|
|
6211
|
+
updateCombinedBadge(fallbackRepo.changeCount)
|
|
6212
|
+
updatePublishButton()
|
|
6213
|
+
return true
|
|
6214
|
+
} catch (error) {
|
|
6215
|
+
console.error('check_git fallback error:', error)
|
|
6216
|
+
setChangesMenuMessage('Unable to load repositories')
|
|
6217
|
+
updateCombinedBadge(0)
|
|
6218
|
+
updateChangesButtonState(false)
|
|
6219
|
+
return false
|
|
6220
|
+
}
|
|
6221
|
+
}
|
|
6222
|
+
|
|
6223
|
+
const check_git = async () => {
|
|
6224
|
+
if (gitStatusRequest) {
|
|
6225
|
+
return gitStatusRequest
|
|
6226
|
+
}
|
|
6227
|
+
|
|
6228
|
+
gitStatusRequest = (async () => {
|
|
6229
|
+
if (repoStatusCache.size === 0) {
|
|
6230
|
+
setChangesMenuMessage(statusUri ? 'Loading repositories...' : 'Loading changes...')
|
|
6231
|
+
updateChangesButtonState(false)
|
|
6232
|
+
}
|
|
6233
|
+
|
|
6234
|
+
if (!statusUri) {
|
|
6235
|
+
await updateFromLegacyMonitor()
|
|
6236
|
+
return
|
|
6237
|
+
}
|
|
6238
|
+
|
|
6239
|
+
try {
|
|
6240
|
+
const response = await fetch(statusUri)
|
|
6241
|
+
if (!response.ok) {
|
|
6242
|
+
throw new Error(`HTTP ${response.status}`)
|
|
6243
|
+
}
|
|
6244
|
+
const data = await response.json()
|
|
6245
|
+
const repos = Array.isArray(data.repos) ? data.repos : []
|
|
6246
|
+
|
|
6247
|
+
repoStatusCache.clear()
|
|
6248
|
+
repos.forEach((repo) => {
|
|
6249
|
+
repoStatusCache.set(repo.repoParam, repo)
|
|
6250
|
+
})
|
|
6251
|
+
|
|
6252
|
+
const sortedRepos = [...repos].sort((a, b) => {
|
|
6253
|
+
if (a.main === b.main) {
|
|
6254
|
+
return a.name.localeCompare(b.name)
|
|
6255
|
+
}
|
|
6256
|
+
return a.main ? -1 : 1
|
|
6257
|
+
})
|
|
6258
|
+
|
|
6259
|
+
lastRepoList = sortedRepos
|
|
6260
|
+
|
|
6261
|
+
const existingActive = activeRepoKey
|
|
6262
|
+
? sortedRepos.find((repo) => repo.repoParam === activeRepoKey)
|
|
6263
|
+
: null
|
|
6264
|
+
const fallbackRepo = sortedRepos.find((repo) => repo.main) || sortedRepos[0] || null
|
|
6265
|
+
const resolvedActive = existingActive || fallbackRepo
|
|
6266
|
+
activeRepoKey = resolvedActive ? resolvedActive.repoParam : null
|
|
6267
|
+
currentChanges = resolvedActive ? (resolvedActive.changes || []) : []
|
|
6268
|
+
gitCommitUrl = resolvedActive ? (resolvedActive.git_commit_url || null) : null
|
|
6269
|
+
|
|
6270
|
+
if (changesDropdownContainer) {
|
|
6271
|
+
changesDropdownContainer.style.display = ''
|
|
6272
|
+
}
|
|
6273
|
+
|
|
6274
|
+
if (sortedRepos.length === 0) {
|
|
6275
|
+
await updateFromLegacyMonitor()
|
|
6276
|
+
} else {
|
|
6277
|
+
renderChangesDropdown(sortedRepos)
|
|
6278
|
+
const total = typeof data.totalChanges === 'number'
|
|
6279
|
+
? data.totalChanges
|
|
6280
|
+
: sortedRepos.reduce((sum, repo) => sum + (repo.changeCount || 0), 0)
|
|
6281
|
+
updateCombinedBadge(total)
|
|
6282
|
+
updateChangesButtonState(true)
|
|
6283
|
+
}
|
|
6284
|
+
|
|
6285
|
+
updatePublishButton()
|
|
6286
|
+
} catch (error) {
|
|
6287
|
+
console.error('check_git error:', error)
|
|
6288
|
+
await updateFromLegacyMonitor()
|
|
6289
|
+
}
|
|
6290
|
+
})()
|
|
6291
|
+
|
|
6292
|
+
try {
|
|
6293
|
+
await gitStatusRequest
|
|
6294
|
+
} finally {
|
|
6295
|
+
gitStatusRequest = null
|
|
6296
|
+
}
|
|
5800
6297
|
}
|
|
5801
6298
|
|
|
5802
6299
|
let messageListener = null
|
|
@@ -5908,43 +6405,135 @@ body.dark {
|
|
|
5908
6405
|
return messages.join(', ')
|
|
5909
6406
|
}
|
|
5910
6407
|
|
|
5911
|
-
const showGitDiffModal = async (diffData = null, modalTitle =
|
|
5912
|
-
|
|
5913
|
-
let
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
if (!
|
|
6408
|
+
const showGitDiffModal = async (diffData = null, modalTitle = null, showSaveButton = true) => {
|
|
6409
|
+
const diffDataIsObject = diffData && typeof diffData === 'object' && !Array.isArray(diffData)
|
|
6410
|
+
let repoParam = diffDataIsObject ? (diffData.repoParam || diffData.repoKey || null) : null
|
|
6411
|
+
let repoDisplayName = diffDataIsObject ? (diffData.repoName || diffData.name || null) : null
|
|
6412
|
+
|
|
6413
|
+
if (!repoParam) {
|
|
6414
|
+
repoParam = activeRepoKey || null
|
|
6415
|
+
}
|
|
6416
|
+
|
|
6417
|
+
if (!repoParam && repoStatusCache.size > 0) {
|
|
6418
|
+
const firstEntry = repoStatusCache.values().next().value
|
|
6419
|
+
if (firstEntry) {
|
|
6420
|
+
repoParam = firstEntry.repoParam
|
|
6421
|
+
if (!repoDisplayName) {
|
|
6422
|
+
repoDisplayName = firstEntry.name
|
|
6423
|
+
}
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6426
|
+
|
|
6427
|
+
if (!repoParam) {
|
|
6428
|
+
await check_git()
|
|
6429
|
+
const fallback = repoStatusCache.values().next().value
|
|
6430
|
+
if (fallback) {
|
|
6431
|
+
repoParam = fallback.repoParam
|
|
6432
|
+
repoDisplayName = repoDisplayName || fallback.name
|
|
6433
|
+
}
|
|
6434
|
+
}
|
|
6435
|
+
|
|
6436
|
+
if (!repoParam) {
|
|
6437
|
+
Swal.fire({
|
|
6438
|
+
title: 'No repositories',
|
|
6439
|
+
text: 'No Git repositories were detected for this workspace.',
|
|
6440
|
+
icon: 'info'
|
|
6441
|
+
})
|
|
6442
|
+
return
|
|
6443
|
+
}
|
|
6444
|
+
|
|
6445
|
+
let repoData = repoStatusCache.get(repoParam)
|
|
6446
|
+
if (!repoData) {
|
|
6447
|
+
await check_git()
|
|
6448
|
+
repoData = repoStatusCache.get(repoParam)
|
|
6449
|
+
}
|
|
6450
|
+
|
|
6451
|
+
if (!repoDisplayName && repoData) {
|
|
6452
|
+
repoDisplayName = repoData.name
|
|
6453
|
+
}
|
|
6454
|
+
if (!repoDisplayName) {
|
|
6455
|
+
repoDisplayName = repoParam
|
|
6456
|
+
}
|
|
6457
|
+
|
|
6458
|
+
let changes
|
|
6459
|
+
if (diffDataIsObject && Array.isArray(diffData.changes)) {
|
|
6460
|
+
changes = diffData.changes
|
|
6461
|
+
} else if (repoData && Array.isArray(repoData.changes)) {
|
|
6462
|
+
changes = repoData.changes
|
|
6463
|
+
} else {
|
|
6464
|
+
changes = currentChanges
|
|
6465
|
+
}
|
|
6466
|
+
|
|
6467
|
+
let commitUrl
|
|
6468
|
+
if (diffDataIsObject && diffData.git_commit_url) {
|
|
6469
|
+
commitUrl = diffData.git_commit_url
|
|
6470
|
+
} else if (repoData && repoData.git_commit_url) {
|
|
6471
|
+
commitUrl = repoData.git_commit_url
|
|
6472
|
+
} else {
|
|
6473
|
+
commitUrl = gitCommitUrl
|
|
6474
|
+
}
|
|
6475
|
+
|
|
6476
|
+
const shouldForceRefresh = Boolean(diffDataIsObject && diffData.forceRefresh)
|
|
6477
|
+
|
|
6478
|
+
if (!changes || changes.length === 0 || shouldForceRefresh) {
|
|
5917
6479
|
try {
|
|
5918
|
-
const response = await fetch(
|
|
6480
|
+
const response = await fetch(`/gitcommit/HEAD/${encodeRepoPath(repoParam)}`)
|
|
6481
|
+
if (!response.ok) {
|
|
6482
|
+
throw new Error(`HTTP ${response.status}`)
|
|
6483
|
+
}
|
|
5919
6484
|
const freshData = await response.json()
|
|
5920
6485
|
changes = freshData.changes || []
|
|
5921
|
-
commitUrl = freshData.git_commit_url ||
|
|
5922
|
-
|
|
5923
|
-
|
|
6486
|
+
commitUrl = freshData.git_commit_url || commitUrl
|
|
6487
|
+
const updatedRepo = repoData || { repoParam }
|
|
6488
|
+
updatedRepo.changes = changes
|
|
6489
|
+
updatedRepo.changeCount = changes.length
|
|
6490
|
+
updatedRepo.git_commit_url = freshData.git_commit_url || null
|
|
6491
|
+
updatedRepo.name = updatedRepo.name || repoDisplayName || repoParam
|
|
6492
|
+
updatedRepo.git_history_url = updatedRepo.git_history_url || (repoData && repoData.git_history_url) || (repoParam ? `/info/git/HEAD/${encodeRepoPath(repoParam)}` : null)
|
|
6493
|
+
repoStatusCache.set(repoParam, updatedRepo)
|
|
6494
|
+
repoData = updatedRepo
|
|
6495
|
+
const listEntry = lastRepoList.find((entry) => entry.repoParam === repoParam)
|
|
6496
|
+
if (listEntry) {
|
|
6497
|
+
listEntry.changes = changes
|
|
6498
|
+
listEntry.changeCount = changes.length
|
|
6499
|
+
listEntry.git_commit_url = updatedRepo.git_commit_url
|
|
6500
|
+
if (updatedRepo.git_history_url) {
|
|
6501
|
+
listEntry.git_history_url = updatedRepo.git_history_url
|
|
6502
|
+
}
|
|
6503
|
+
}
|
|
6504
|
+
const total = Array.from(repoStatusCache.values()).reduce((sum, repo) => sum + (repo.changeCount || 0), 0)
|
|
6505
|
+
updateCombinedBadge(total)
|
|
6506
|
+
if (lastRepoList.length > 0) {
|
|
6507
|
+
renderChangesDropdown(lastRepoList)
|
|
6508
|
+
}
|
|
5924
6509
|
} catch (error) {
|
|
5925
|
-
console.error('Failed to fetch
|
|
6510
|
+
console.error('Failed to fetch repo changes:', error)
|
|
5926
6511
|
}
|
|
5927
6512
|
}
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
6513
|
+
|
|
6514
|
+
activeRepoKey = repoParam
|
|
6515
|
+
currentChanges = changes || []
|
|
6516
|
+
gitCommitUrl = commitUrl
|
|
6517
|
+
|
|
6518
|
+
const title = modalTitle || `File Changes${repoDisplayName ? ` — ${repoDisplayName}` : ''}`
|
|
6519
|
+
|
|
6520
|
+
if (lastRepoList.length > 0) {
|
|
6521
|
+
renderChangesDropdown(lastRepoList)
|
|
6522
|
+
}
|
|
6523
|
+
|
|
6524
|
+
if (!changes || changes.length === 0) {
|
|
6525
|
+
try {
|
|
6526
|
+
await showGitHistoryModal({
|
|
6527
|
+
repoParam,
|
|
6528
|
+
repoName: repoDisplayName,
|
|
6529
|
+
historyUrl: repoData && repoData.git_history_url ? repoData.git_history_url : null,
|
|
6530
|
+
})
|
|
6531
|
+
} catch (error) {
|
|
6532
|
+
console.error('Failed to open history for clean repository:', error)
|
|
6533
|
+
}
|
|
5945
6534
|
return
|
|
5946
6535
|
}
|
|
5947
|
-
|
|
6536
|
+
|
|
5948
6537
|
const changeSummary = `${changes.length} file${changes.length === 1 ? '' : 's'} changed`
|
|
5949
6538
|
const statusCounts = { added: 0, modified: 0, deleted: 0, renamed: 0 }
|
|
5950
6539
|
changes.forEach(change => {
|
|
@@ -5975,7 +6564,7 @@ body.dark {
|
|
|
5975
6564
|
<div class="pinokio-modal-header">
|
|
5976
6565
|
<div class="pinokio-modal-icon"><i class="fa-solid fa-code-branch"></i></div>
|
|
5977
6566
|
<div class="pinokio-modal-heading">
|
|
5978
|
-
<div class="pinokio-modal-title">${
|
|
6567
|
+
<div class="pinokio-modal-title">${title}</div>
|
|
5979
6568
|
<div class="pinokio-modal-subtitle">${changeSummary}</div>
|
|
5980
6569
|
</div>
|
|
5981
6570
|
</div>
|
|
@@ -6110,9 +6699,54 @@ body.dark {
|
|
|
6110
6699
|
container.innerHTML = diffHtml
|
|
6111
6700
|
}
|
|
6112
6701
|
|
|
6113
|
-
const showGitHistoryModal = async () => {
|
|
6114
|
-
const
|
|
6115
|
-
|
|
6702
|
+
const showGitHistoryModal = async (options = {}) => {
|
|
6703
|
+
const opts = options && typeof options === 'object' ? options : {}
|
|
6704
|
+
const buildHistoryUrl = (param) => {
|
|
6705
|
+
if (!param) {
|
|
6706
|
+
return null
|
|
6707
|
+
}
|
|
6708
|
+
return `/info/git/HEAD/${encodeRepoPath(param)}`
|
|
6709
|
+
}
|
|
6710
|
+
|
|
6711
|
+
let repoParam = opts.repoParam ?? null
|
|
6712
|
+
let repoName = opts.repoName ?? null
|
|
6713
|
+
let historyUrl = opts.historyUrl ?? null
|
|
6714
|
+
|
|
6715
|
+
if (!repoParam) {
|
|
6716
|
+
repoParam = activeRepoKey || null
|
|
6717
|
+
}
|
|
6718
|
+
|
|
6719
|
+
let repoData = repoParam ? repoStatusCache.get(repoParam) : null
|
|
6720
|
+
|
|
6721
|
+
if (!repoName && repoData && repoData.name) {
|
|
6722
|
+
repoName = repoData.name
|
|
6723
|
+
}
|
|
6724
|
+
|
|
6725
|
+
if (!historyUrl && repoData && repoData.git_history_url) {
|
|
6726
|
+
historyUrl = repoData.git_history_url
|
|
6727
|
+
}
|
|
6728
|
+
|
|
6729
|
+
const fallbackHistoryUri = readDataAttr(fsStatusEl, 'data-history-uri')
|
|
6730
|
+
|
|
6731
|
+
if (!historyUrl) {
|
|
6732
|
+
if (repoParam) {
|
|
6733
|
+
historyUrl = buildHistoryUrl(repoParam)
|
|
6734
|
+
} else if (fallbackHistoryUri) {
|
|
6735
|
+
historyUrl = fallbackHistoryUri
|
|
6736
|
+
}
|
|
6737
|
+
} else if (repoParam && typeof historyUrl === 'string' && historyUrl.startsWith('/info/git/HEAD/')) {
|
|
6738
|
+
historyUrl = buildHistoryUrl(repoParam)
|
|
6739
|
+
}
|
|
6740
|
+
|
|
6741
|
+
if (!repoName) {
|
|
6742
|
+
if (repoParam) {
|
|
6743
|
+
repoName = repoParam
|
|
6744
|
+
} else if (workspaceName) {
|
|
6745
|
+
repoName = workspaceName
|
|
6746
|
+
}
|
|
6747
|
+
}
|
|
6748
|
+
|
|
6749
|
+
if (!historyUrl) {
|
|
6116
6750
|
Swal.fire({
|
|
6117
6751
|
title: 'Error',
|
|
6118
6752
|
text: 'Git history URL not available.',
|
|
@@ -6120,16 +6754,15 @@ body.dark {
|
|
|
6120
6754
|
})
|
|
6121
6755
|
return
|
|
6122
6756
|
}
|
|
6123
|
-
|
|
6757
|
+
|
|
6124
6758
|
try {
|
|
6125
|
-
const response = await fetch(
|
|
6759
|
+
const response = await fetch(historyUrl)
|
|
6126
6760
|
if (!response.ok) {
|
|
6127
6761
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
6128
6762
|
}
|
|
6129
|
-
|
|
6763
|
+
|
|
6130
6764
|
const historyData = await response.json()
|
|
6131
|
-
displayGitHistory(historyData)
|
|
6132
|
-
|
|
6765
|
+
displayGitHistory(historyData, { repoName: repoName || null, repoParam: repoParam || null })
|
|
6133
6766
|
} catch (error) {
|
|
6134
6767
|
console.error('Failed to load git history:', error)
|
|
6135
6768
|
Swal.fire({
|
|
@@ -6140,7 +6773,8 @@ body.dark {
|
|
|
6140
6773
|
}
|
|
6141
6774
|
}
|
|
6142
6775
|
|
|
6143
|
-
const displayGitHistory = (historyData) => {
|
|
6776
|
+
const displayGitHistory = (historyData, options = {}) => {
|
|
6777
|
+
const repoName = options && typeof options === 'object' ? options.repoName : null
|
|
6144
6778
|
const commits = historyData.log || []
|
|
6145
6779
|
const remote = historyData.remote || ''
|
|
6146
6780
|
const currentRef = historyData.ref || 'HEAD'
|
|
@@ -6159,12 +6793,14 @@ body.dark {
|
|
|
6159
6793
|
metaBadges.push(`<span class="pinokio-pill"><i class="fa-solid fa-cloud-arrow-up"></i>${remote}</span>`)
|
|
6160
6794
|
}
|
|
6161
6795
|
|
|
6796
|
+
const historyTitle = repoName ? `${repoName} history` : 'Repository history'
|
|
6797
|
+
|
|
6162
6798
|
const historyHtml = `
|
|
6163
6799
|
<div class="pinokio-modal-surface pinokio-modal-surface--history">
|
|
6164
6800
|
<div class="pinokio-modal-header">
|
|
6165
6801
|
<div class="pinokio-modal-icon"><i class="fa-solid fa-clock-rotate-left"></i></div>
|
|
6166
6802
|
<div class="pinokio-modal-heading">
|
|
6167
|
-
<div class="pinokio-modal-title"
|
|
6803
|
+
<div class="pinokio-modal-title">${historyTitle}</div>
|
|
6168
6804
|
<div class="pinokio-modal-subtitle">${subtitleText}</div>
|
|
6169
6805
|
</div>
|
|
6170
6806
|
</div>
|
|
@@ -6719,15 +7355,10 @@ body.dark {
|
|
|
6719
7355
|
}
|
|
6720
7356
|
}
|
|
6721
7357
|
|
|
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
7358
|
// Initialize the publish/create button
|
|
6727
7359
|
updatePublishButton()
|
|
6728
7360
|
|
|
6729
7361
|
check_git()
|
|
6730
|
-
setInterval(check_git, 10000)
|
|
6731
7362
|
<% } %>
|
|
6732
7363
|
|
|
6733
7364
|
setInterval(() => {
|