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.
@@ -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.2) !important;
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.2) !important;
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.2) !important;
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: 6px;
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: 4px 8px;
1512
- border-radius: 12px;
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
- <button id='fs-changes-btn' class='fs-status-btn'>
2645
- <span class='fs-status-label'><i class="fa-solid fa-code-compare"></i> Changes</span>
2646
- <div class='badge'>loading...</div>
2647
- </button>
2648
- <button id='fs-history-btn' class='fs-status-btn'>
2649
- <span class='fs-status-label'><i class="fa-solid fa-clock-rotate-left"></i> History</span>
2650
- </button>
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
- const canonical = canonicalizeUrl(url)
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 linkOrigin = new URL(link.href, location.href).origin
3517
- if (linkOrigin === location.origin) {
3518
- hideTabLinkPopover({ immediate: true })
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 hasPersistedSelection = Boolean(selectionKey && storage && storage.getItem(selectionKey))
4428
- const persistedSelectionRaw = selectionKey && storage ? storage.getItem(selectionKey) : null
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
- const check_git = () => {
5779
- fetch("<%=git_monitor_url%>").then((res) => {
5780
- return res.json()
5781
- }).then((res) => {
5782
- currentChanges = res.changes || []
5783
- gitCommitUrl = res.git_commit_url || null
5784
- const changesBtn = document.querySelector("#fs-changes-btn")
5785
- const badgeElement = document.querySelector("#fs-changes-btn .badge")
5786
-
5787
- if (res.changes && res.changes.length > 0) {
5788
- // Show changes button and update badge
5789
- changesBtn.style.display = 'block'
5790
- badgeElement.innerHTML = `${res.changes.length}`
5791
- } else {
5792
- // Hide changes button when no changes
5793
- changesBtn.style.display = 'none'
5794
- badgeElement.innerHTML = ''
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
- updatePublishButton()
5797
- }).catch((error) => {
5798
- console.error('check_git error:', error)
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 = 'File Changes', showSaveButton = true) => {
5912
- let changes = diffData ? (diffData.changes || []) : currentChanges
5913
- let commitUrl = diffData ? (diffData.git_commit_url || null) : gitCommitUrl
5914
-
5915
- // If no diffData provided and currentChanges is empty, try to fetch fresh data
5916
- if (!diffData && changes.length === 0) {
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("<%=git_monitor_url%>")
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 || null
5922
- currentChanges = changes // Update the global variable
5923
- gitCommitUrl = commitUrl
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 current changes:', error)
6538
+ console.error('Failed to fetch repo changes:', error)
5926
6539
  }
5927
6540
  }
5928
-
5929
-
5930
- if (changes.length === 0) {
5931
- Swal.fire({
5932
- html: `
5933
- <div class="pinokio-no-changes-icon"><i class="fa-solid fa-check"></i></div>
5934
- <div class="pinokio-no-changes-title">Workspace is clean</div>
5935
- <div class="pinokio-no-changes-body">There are currently no tracked file changes. Make edits or refresh to check again.</div>
5936
- <div class="pinokio-no-changes-hint">Tip: run your build or tests before committing.</div>
5937
- `,
5938
- customClass: {
5939
- popup: 'pinokio-no-changes-popup',
5940
- confirmButton: 'pinokio-no-changes-confirm'
5941
- },
5942
- confirmButtonText: 'Close',
5943
- backdrop: 'rgba(9,11,15,0.6)'
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">${modalTitle}</div>
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 historyUri = document.querySelector('#fs-status').getAttribute('data-history-uri')
6115
- if (!historyUri) {
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(historyUri)
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">Repository history</div>
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(() => {