pinokiod 3.107.0 → 3.109.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/kernel/bin/caddy.js +24 -0
- package/kernel/bin/git.js +12 -0
- package/kernel/environment.js +76 -0
- package/kernel/git.js +21 -23
- package/kernel/scripts/git/fork +21 -0
- package/kernel/scripts/git/push +1 -1
- package/package.json +1 -1
- package/server/index.js +123 -94
- package/server/public/layout.js +21 -0
- package/server/views/app.ejs +1159 -123
- package/server/views/file_explorer.ejs +1 -1
- package/server/views/index.ejs +1 -1
- package/server/views/index2.ejs +1 -1
- package/server/views/partials/dynamic.ejs +1 -1
- package/server/views/partials/menu.ejs +1 -1
- package/server/views/pro.ejs +1 -1
package/server/views/app.ejs
CHANGED
|
@@ -1378,6 +1378,15 @@ body.dark #fs-status {
|
|
|
1378
1378
|
z-index: 20;
|
|
1379
1379
|
}
|
|
1380
1380
|
|
|
1381
|
+
.fs-status-dropdown.git-fork .fs-dropdown-menu,
|
|
1382
|
+
.fs-status-dropdown.git-publish .fs-dropdown-menu {
|
|
1383
|
+
left: auto;
|
|
1384
|
+
right: 0;
|
|
1385
|
+
width: min(420px, calc(100vw - 24px));
|
|
1386
|
+
max-width: min(420px, calc(100vw - 24px));
|
|
1387
|
+
white-space: normal;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1381
1390
|
body.dark .fs-dropdown-menu {
|
|
1382
1391
|
background: rgba(30, 41, 59, 0.96);
|
|
1383
1392
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
|
@@ -1938,6 +1947,40 @@ body.dark {
|
|
|
1938
1947
|
background: #2d6ae0 !important;
|
|
1939
1948
|
}
|
|
1940
1949
|
|
|
1950
|
+
.pinokio-github-login-modal.swal2-popup {
|
|
1951
|
+
max-width: 420px !important;
|
|
1952
|
+
width: calc(100vw - 48px) !important;
|
|
1953
|
+
}
|
|
1954
|
+
.pinokio-github-login {
|
|
1955
|
+
padding: 36px 40px 32px 40px;
|
|
1956
|
+
display: flex;
|
|
1957
|
+
flex-direction: column;
|
|
1958
|
+
align-items: center;
|
|
1959
|
+
text-align: center;
|
|
1960
|
+
gap: 14px;
|
|
1961
|
+
}
|
|
1962
|
+
.pinokio-github-login__icon {
|
|
1963
|
+
width: 64px;
|
|
1964
|
+
height: 64px;
|
|
1965
|
+
border-radius: 18px;
|
|
1966
|
+
display: grid;
|
|
1967
|
+
place-items: center;
|
|
1968
|
+
background: var(--pinokio-modal-icon-bg);
|
|
1969
|
+
color: var(--pinokio-modal-icon-color);
|
|
1970
|
+
font-size: 28px;
|
|
1971
|
+
}
|
|
1972
|
+
.pinokio-github-login__title {
|
|
1973
|
+
font-size: 20px;
|
|
1974
|
+
font-weight: 600;
|
|
1975
|
+
color: var(--pinokio-modal-text);
|
|
1976
|
+
}
|
|
1977
|
+
.pinokio-github-login__body {
|
|
1978
|
+
font-size: 14px;
|
|
1979
|
+
line-height: 1.6;
|
|
1980
|
+
color: var(--pinokio-modal-subtitle-color);
|
|
1981
|
+
max-width: 280px;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1941
1984
|
.pinokio-modern-modal.swal2-popup {
|
|
1942
1985
|
background: var(--pinokio-modal-bg) !important;
|
|
1943
1986
|
color: var(--pinokio-modal-text) !important;
|
|
@@ -2114,6 +2157,157 @@ body.dark {
|
|
|
2114
2157
|
background: rgba(127, 91, 243, 1) !important;
|
|
2115
2158
|
}
|
|
2116
2159
|
|
|
2160
|
+
.pinokio-modal-body--fork {
|
|
2161
|
+
display: flex;
|
|
2162
|
+
flex-direction: column;
|
|
2163
|
+
gap: 16px;
|
|
2164
|
+
padding: 0;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
.pinokio-fork-modal {
|
|
2168
|
+
display: flex;
|
|
2169
|
+
flex-direction: column;
|
|
2170
|
+
gap: 16px;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
.pinokio-fork-help {
|
|
2174
|
+
margin: 0;
|
|
2175
|
+
font-size: 0.875rem;
|
|
2176
|
+
color: rgba(255, 255, 255, 0.75);
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
.pinokio-fork-item {
|
|
2180
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
2181
|
+
border-radius: 10px;
|
|
2182
|
+
padding: 14px 16px;
|
|
2183
|
+
display: flex;
|
|
2184
|
+
flex-direction: column;
|
|
2185
|
+
gap: 10px;
|
|
2186
|
+
background: rgba(15, 18, 24, 0.65);
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
.pinokio-fork-item[data-disabled='true'] {
|
|
2190
|
+
opacity: 0.55;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
.pinokio-fork-item-header {
|
|
2194
|
+
display: flex;
|
|
2195
|
+
align-items: center;
|
|
2196
|
+
gap: 10px;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
.pinokio-fork-item-header label {
|
|
2200
|
+
flex: 1;
|
|
2201
|
+
display: flex;
|
|
2202
|
+
align-items: center;
|
|
2203
|
+
gap: 8px;
|
|
2204
|
+
cursor: pointer;
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
.pinokio-fork-item-title {
|
|
2208
|
+
font-weight: 600;
|
|
2209
|
+
font-size: 0.95rem;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
.pinokio-fork-item-url {
|
|
2213
|
+
font-size: 0.85rem;
|
|
2214
|
+
color: rgba(255, 255, 255, 0.65);
|
|
2215
|
+
word-break: break-word;
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
.pinokio-fork-item-url.empty {
|
|
2219
|
+
font-style: italic;
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
.pinokio-fork-name-input {
|
|
2223
|
+
display: flex;
|
|
2224
|
+
flex-direction: column;
|
|
2225
|
+
gap: 6px;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
.pinokio-fork-name-input label {
|
|
2229
|
+
font-size: 0.8rem;
|
|
2230
|
+
font-weight: 500;
|
|
2231
|
+
text-transform: uppercase;
|
|
2232
|
+
letter-spacing: 0.04em;
|
|
2233
|
+
color: rgba(255, 255, 255, 0.7);
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
.pinokio-modal-input.pinokio-modal-input--error {
|
|
2237
|
+
border-color: #ff6b6b;
|
|
2238
|
+
box-shadow: 0 0 0 1px rgba(255, 107, 107, 0.25);
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
.pinokio-fork-checkbox-row {
|
|
2242
|
+
display: flex;
|
|
2243
|
+
align-items: center;
|
|
2244
|
+
gap: 8px;
|
|
2245
|
+
font-size: 0.85rem;
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
.pinokio-fork-checkbox-row label {
|
|
2249
|
+
cursor: pointer;
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
.pinokio-fork-org-input {
|
|
2253
|
+
display: flex;
|
|
2254
|
+
flex-direction: column;
|
|
2255
|
+
gap: 6px;
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
.pinokio-fork-org-input.hidden {
|
|
2259
|
+
display: none !important;
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
.pinokio-fork-org-input label {
|
|
2263
|
+
font-size: 0.8rem;
|
|
2264
|
+
font-weight: 500;
|
|
2265
|
+
text-transform: uppercase;
|
|
2266
|
+
letter-spacing: 0.04em;
|
|
2267
|
+
color: rgba(255, 255, 255, 0.7);
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
.pinokio-fork-org-hint {
|
|
2271
|
+
margin: 0;
|
|
2272
|
+
font-size: 0.75rem;
|
|
2273
|
+
color: rgba(255, 255, 255, 0.55);
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
.pinokio-fork-dropdown-item,
|
|
2277
|
+
.pinokio-publish-dropdown-item {
|
|
2278
|
+
display: flex;
|
|
2279
|
+
flex-direction: column;
|
|
2280
|
+
align-items: flex-start;
|
|
2281
|
+
gap: 4px;
|
|
2282
|
+
text-align: left;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
.pinokio-fork-dropdown-title,
|
|
2286
|
+
.pinokio-publish-dropdown-title {
|
|
2287
|
+
font-weight: 600;
|
|
2288
|
+
font-size: 0.9rem;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
.pinokio-fork-dropdown-remote,
|
|
2292
|
+
.pinokio-publish-dropdown-remote {
|
|
2293
|
+
font-size: 0.75rem;
|
|
2294
|
+
color: rgba(255, 255, 255, 0.6);
|
|
2295
|
+
word-break: break-word;
|
|
2296
|
+
white-space: normal;
|
|
2297
|
+
overflow-wrap: anywhere;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
.pinokio-fork-dropdown-remote.empty,
|
|
2301
|
+
.pinokio-publish-dropdown-remote.empty {
|
|
2302
|
+
font-style: italic;
|
|
2303
|
+
color: rgba(255, 255, 255, 0.45);
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
.fs-dropdown-item--disabled {
|
|
2307
|
+
opacity: 0.5;
|
|
2308
|
+
cursor: not-allowed;
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2117
2311
|
.pinokio-git-history-list {
|
|
2118
2312
|
display: flex;
|
|
2119
2313
|
flex-direction: column;
|
|
@@ -2526,13 +2720,16 @@ body.dark {
|
|
|
2526
2720
|
font-size: 1rem;
|
|
2527
2721
|
}
|
|
2528
2722
|
*/
|
|
2529
|
-
#fs-push-btn
|
|
2723
|
+
#fs-push-btn,
|
|
2724
|
+
#fs-fork-btn {
|
|
2530
2725
|
min-width: 0;
|
|
2531
2726
|
}
|
|
2532
|
-
#fs-push-btn .fs-status-label
|
|
2727
|
+
#fs-push-btn .fs-status-label,
|
|
2728
|
+
#fs-fork-btn .fs-status-label {
|
|
2533
2729
|
font-size: 0;
|
|
2534
2730
|
}
|
|
2535
|
-
#fs-push-btn .fs-status-label i
|
|
2731
|
+
#fs-push-btn .fs-status-label i,
|
|
2732
|
+
#fs-fork-btn .fs-status-label i {
|
|
2536
2733
|
font-size: 1rem;
|
|
2537
2734
|
}
|
|
2538
2735
|
#fs-status .fs-status-btn .disk-usage,
|
|
@@ -2679,7 +2876,7 @@ body.dark {
|
|
|
2679
2876
|
<% } %>
|
|
2680
2877
|
<div class='container'>
|
|
2681
2878
|
<% if (type === "browse") { %>
|
|
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%>">
|
|
2879
|
+
<div id='fs-status' data-workspace="<%=name%>" data-create-uri="<%=git_create_url%>" data-history-uri="<%=git_history_url%>" data-status-uri="<%=git_status_url%>" data-uri="<%=git_monitor_url%>" data-push-uri="<%=git_push_url%>" data-fork-uri="<%=git_fork_url%>">
|
|
2683
2880
|
<a target="<%=src%>" href="<%=src%>" class='fs-status-btn frame-link' data-index="0" data-mode="refresh" data-type="n">
|
|
2684
2881
|
<span class='fs-status-label'>
|
|
2685
2882
|
<i class="fa-regular fa-folder-open"></i>
|
|
@@ -2733,12 +2930,24 @@ body.dark {
|
|
|
2733
2930
|
</button>
|
|
2734
2931
|
<div class='fs-dropdown-menu submenu hidden' id='fs-changes-menu'></div>
|
|
2735
2932
|
</div>
|
|
2736
|
-
<
|
|
2737
|
-
<
|
|
2738
|
-
<
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2933
|
+
<div class='fs-status-dropdown git-fork'>
|
|
2934
|
+
<button id='fs-fork-btn' class='fs-status-btn revealer' data-group='#fs-fork-menu' type='button'>
|
|
2935
|
+
<span class='fs-status-label'>
|
|
2936
|
+
<i class="fa-solid fa-code-branch"></i>
|
|
2937
|
+
<span class='fs-status-title'>Fork</span>
|
|
2938
|
+
</span>
|
|
2939
|
+
</button>
|
|
2940
|
+
<div class='fs-dropdown-menu submenu hidden' id='fs-fork-menu'></div>
|
|
2941
|
+
</div>
|
|
2942
|
+
<div class='fs-status-dropdown git-publish'>
|
|
2943
|
+
<button id='fs-push-btn' class='fs-status-btn revealer' data-group='#fs-push-menu' type='button'>
|
|
2944
|
+
<span class='fs-status-label'>
|
|
2945
|
+
<i class="fa-brands fa-github"></i>
|
|
2946
|
+
<span class='fs-status-title'>Publish</span>
|
|
2947
|
+
</span>
|
|
2948
|
+
</button>
|
|
2949
|
+
<div class='fs-dropdown-menu submenu hidden' id='fs-push-menu'></div>
|
|
2950
|
+
</div>
|
|
2742
2951
|
</div>
|
|
2743
2952
|
<% } %>
|
|
2744
2953
|
<main class='browserview'>
|
|
@@ -2772,17 +2981,20 @@ body.dark {
|
|
|
2772
2981
|
let ignorePersistedSelection = pluginLaunchActive
|
|
2773
2982
|
let lastForegroundSignature = null
|
|
2774
2983
|
const iframe_onerror = (iframe) => {
|
|
2984
|
+
if (iframe && iframe.dataset && iframe.dataset.forceVisible === 'true') {
|
|
2985
|
+
return
|
|
2986
|
+
}
|
|
2775
2987
|
let originalSrc = iframe.src
|
|
2776
2988
|
iframe.onload = function() {
|
|
2777
2989
|
try {
|
|
2778
2990
|
// Try to access the iframe's document
|
|
2991
|
+
const iframeDoc = iframe.contentDocument || (iframe.contentWindow ? iframe.contentWindow.document : null)
|
|
2779
2992
|
const currentSrc = iframe.src
|
|
2780
2993
|
// Check if it's a chrome error page or empty
|
|
2781
2994
|
if (currentSrc !== originalSrc &&
|
|
2782
2995
|
(currentSrc.includes('chrome-error://') ||
|
|
2783
2996
|
currentSrc === 'about:blank' ||
|
|
2784
2997
|
currentSrc.includes('data:'))) {
|
|
2785
|
-
iframeDoc.classList.add("hidden")
|
|
2786
2998
|
Swal.fire({
|
|
2787
2999
|
html: `<i class="fa-solid fa-circle-notch fa-spin"></i> Loading...`,
|
|
2788
3000
|
customClass: {
|
|
@@ -2798,22 +3010,9 @@ body.dark {
|
|
|
2798
3010
|
}, 3000)
|
|
2799
3011
|
}
|
|
2800
3012
|
} catch (e) {
|
|
2801
|
-
iframe.classList.add("hidden")
|
|
2802
3013
|
// Cross-origin restriction - assume it loaded successfully
|
|
2803
3014
|
// if no error was thrown during the initial load
|
|
2804
|
-
|
|
2805
|
-
html: `<i class="fa-solid fa-circle-notch fa-spin"></i> Loading...`,
|
|
2806
|
-
customClass: {
|
|
2807
|
-
container: "loader-container",
|
|
2808
|
-
popup: "loader-popup",
|
|
2809
|
-
htmlContainer: "loader-dialog",
|
|
2810
|
-
footer: "hidden",
|
|
2811
|
-
actions: "hidden"
|
|
2812
|
-
}
|
|
2813
|
-
});
|
|
2814
|
-
setTimeout(() => {
|
|
2815
|
-
location.href = location.href
|
|
2816
|
-
}, 3000);
|
|
3015
|
+
console.warn('Iframe load warning', e)
|
|
2817
3016
|
}
|
|
2818
3017
|
}
|
|
2819
3018
|
}
|
|
@@ -2821,7 +3020,7 @@ body.dark {
|
|
|
2821
3020
|
document.querySelectorAll(".menu-container .selected").forEach((el) => {
|
|
2822
3021
|
el.classList.remove("selected")
|
|
2823
3022
|
})
|
|
2824
|
-
document.querySelectorAll("iframe").forEach((el) => {
|
|
3023
|
+
document.querySelectorAll("main.browserview iframe").forEach((el) => {
|
|
2825
3024
|
el.classList.add("hidden")
|
|
2826
3025
|
})
|
|
2827
3026
|
let frame = document.createElement("iframe")
|
|
@@ -3995,6 +4194,13 @@ body.dark {
|
|
|
3995
4194
|
})(),
|
|
3996
4195
|
target: node.getAttribute('target') || null,
|
|
3997
4196
|
dataIndex: node.getAttribute('data-index') || null,
|
|
4197
|
+
pagePath: (() => {
|
|
4198
|
+
try {
|
|
4199
|
+
return window.location?.pathname || null
|
|
4200
|
+
} catch (_) {
|
|
4201
|
+
return null
|
|
4202
|
+
}
|
|
4203
|
+
})()
|
|
3998
4204
|
}
|
|
3999
4205
|
try {
|
|
4000
4206
|
const key = selectionStorageKey()
|
|
@@ -4008,24 +4214,41 @@ body.dark {
|
|
|
4008
4214
|
})
|
|
4009
4215
|
} catch (_) {}
|
|
4010
4216
|
}
|
|
4011
|
-
const restorePersistedFrameLink = () => {
|
|
4217
|
+
const restorePersistedFrameLink = (providedPayload = null) => {
|
|
4012
4218
|
const storage = getWindowStorage()
|
|
4013
|
-
if (!storage) {
|
|
4014
|
-
return null
|
|
4015
|
-
}
|
|
4016
4219
|
const key = selectionStorageKey()
|
|
4017
|
-
|
|
4018
|
-
|
|
4220
|
+
const currentPath = (() => {
|
|
4221
|
+
try {
|
|
4222
|
+
return window.location?.pathname || ""
|
|
4223
|
+
} catch (_) {
|
|
4224
|
+
return ""
|
|
4225
|
+
}
|
|
4226
|
+
})()
|
|
4227
|
+
let payload = providedPayload
|
|
4228
|
+
if (!payload) {
|
|
4229
|
+
if (!storage || !key) {
|
|
4230
|
+
return null
|
|
4231
|
+
}
|
|
4232
|
+
const raw = storage.getItem(key)
|
|
4233
|
+
if (!raw) {
|
|
4234
|
+
return null
|
|
4235
|
+
}
|
|
4236
|
+
try {
|
|
4237
|
+
payload = JSON.parse(raw)
|
|
4238
|
+
} catch (_) {
|
|
4239
|
+
payload = { selector: raw }
|
|
4240
|
+
}
|
|
4019
4241
|
}
|
|
4020
|
-
|
|
4021
|
-
if (!raw) {
|
|
4242
|
+
if (!payload) {
|
|
4022
4243
|
return null
|
|
4023
4244
|
}
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4245
|
+
if (typeof payload === 'object' && payload !== null && typeof payload.pagePath === 'string') {
|
|
4246
|
+
if (currentPath && payload.pagePath !== currentPath) {
|
|
4247
|
+
return null
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
if (typeof payload === 'string') {
|
|
4251
|
+
payload = { selector: payload }
|
|
4029
4252
|
}
|
|
4030
4253
|
const trySelector = (selector) => {
|
|
4031
4254
|
if (!selector || typeof selector !== 'string') {
|
|
@@ -4588,7 +4811,7 @@ body.dark {
|
|
|
4588
4811
|
if (!sourceWindow) {
|
|
4589
4812
|
return null
|
|
4590
4813
|
}
|
|
4591
|
-
const frames = Array.from(document.querySelectorAll("iframe"))
|
|
4814
|
+
const frames = Array.from(document.querySelectorAll("main.browserview iframe"))
|
|
4592
4815
|
for (const frame of frames) {
|
|
4593
4816
|
if (frame.contentWindow === sourceWindow) {
|
|
4594
4817
|
return frame.name || null
|
|
@@ -4684,14 +4907,48 @@ body.dark {
|
|
|
4684
4907
|
const renderSelection = async ({ event: eventParam = null, target: explicitTarget = null, force = false } = {}) => {
|
|
4685
4908
|
const storage = getWindowStorage()
|
|
4686
4909
|
const selectionKey = selectionStorageKey()
|
|
4687
|
-
const
|
|
4910
|
+
const currentPath = (() => {
|
|
4911
|
+
try {
|
|
4912
|
+
return window.location?.pathname || ""
|
|
4913
|
+
} catch (_) {
|
|
4914
|
+
return ""
|
|
4915
|
+
}
|
|
4916
|
+
})()
|
|
4688
4917
|
let persistedSelectionRaw = selectionKey && storage ? storage.getItem(selectionKey) : null
|
|
4918
|
+
const devRouteActive = /\/dev(?:$|\/)/.test(currentPath || "")
|
|
4919
|
+
let persistedSelectionPayload = null
|
|
4920
|
+
if (persistedSelectionRaw) {
|
|
4921
|
+
try {
|
|
4922
|
+
const parsed = JSON.parse(persistedSelectionRaw)
|
|
4923
|
+
if (parsed && typeof parsed === "object") {
|
|
4924
|
+
if (typeof parsed.pagePath === "string") {
|
|
4925
|
+
if (!currentPath || parsed.pagePath === currentPath) {
|
|
4926
|
+
persistedSelectionPayload = parsed
|
|
4927
|
+
}
|
|
4928
|
+
} else if (!devRouteActive) {
|
|
4929
|
+
persistedSelectionPayload = parsed
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
} catch (_) {
|
|
4933
|
+
if (!devRouteActive) {
|
|
4934
|
+
persistedSelectionPayload = { selector: persistedSelectionRaw }
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
if (!persistedSelectionPayload) {
|
|
4939
|
+
persistedSelectionRaw = null
|
|
4940
|
+
}
|
|
4941
|
+
const originalHasPersistedSelection = Boolean(persistedSelectionPayload)
|
|
4689
4942
|
const skipPersistedSelection = ignorePersistedSelection
|
|
4690
4943
|
let hasPersistedSelection = skipPersistedSelection ? false : originalHasPersistedSelection
|
|
4691
4944
|
if (skipPersistedSelection) {
|
|
4692
4945
|
persistedSelectionRaw = null
|
|
4946
|
+
persistedSelectionPayload = null
|
|
4693
4947
|
}
|
|
4694
4948
|
|
|
4949
|
+
const triggeredByUser = Boolean(eventParam || explicitTarget)
|
|
4950
|
+
let resolvedByGlobalSelector = false
|
|
4951
|
+
|
|
4695
4952
|
let target = explicitTarget
|
|
4696
4953
|
let preselected = null
|
|
4697
4954
|
|
|
@@ -4704,22 +4961,22 @@ body.dark {
|
|
|
4704
4961
|
if (candidate) {
|
|
4705
4962
|
target = candidate
|
|
4706
4963
|
global_selector = null
|
|
4964
|
+
resolvedByGlobalSelector = true
|
|
4707
4965
|
} else {
|
|
4708
4966
|
scheduleSelectionRetry()
|
|
4709
4967
|
return
|
|
4710
4968
|
}
|
|
4711
4969
|
}
|
|
4712
4970
|
|
|
4971
|
+
console.log({ target, skipPersistedSelection })
|
|
4713
4972
|
if (!target && !skipPersistedSelection) {
|
|
4714
4973
|
preselected = document.querySelector('#devtab.frame-link.selected') || document.querySelector('.frame-link.selected')
|
|
4715
|
-
|
|
4716
|
-
target = preselected
|
|
4717
|
-
}
|
|
4974
|
+
console.log({ preselected })
|
|
4718
4975
|
}
|
|
4719
4976
|
|
|
4720
|
-
if (!target &&
|
|
4721
|
-
target = restorePersistedFrameLink()
|
|
4722
|
-
if (!target &&
|
|
4977
|
+
if (!target && persistedSelectionPayload) {
|
|
4978
|
+
target = restorePersistedFrameLink(persistedSelectionPayload)
|
|
4979
|
+
if (!target && originalHasPersistedSelection) {
|
|
4723
4980
|
scheduleSelectionRetry()
|
|
4724
4981
|
return
|
|
4725
4982
|
}
|
|
@@ -4733,11 +4990,18 @@ body.dark {
|
|
|
4733
4990
|
}
|
|
4734
4991
|
}
|
|
4735
4992
|
|
|
4736
|
-
|
|
4993
|
+
const devTab = document.querySelector('#devtab.frame-link')
|
|
4994
|
+
if (!triggeredByUser && !resolvedByGlobalSelector && !target && devRouteActive && devTab) {
|
|
4995
|
+
target = devTab
|
|
4996
|
+
} else if (!target && preselected) {
|
|
4997
|
+
target = preselected
|
|
4998
|
+
}
|
|
4999
|
+
|
|
5000
|
+
if (!target) {
|
|
4737
5001
|
const defaultSelection = document.querySelector("[data-default]")
|
|
4738
5002
|
if (defaultSelection) {
|
|
4739
5003
|
target = defaultSelection
|
|
4740
|
-
} else {
|
|
5004
|
+
} else if (skipPersistedSelection) {
|
|
4741
5005
|
scheduleSelectionRetry()
|
|
4742
5006
|
return
|
|
4743
5007
|
}
|
|
@@ -4774,6 +5038,12 @@ body.dark {
|
|
|
4774
5038
|
}
|
|
4775
5039
|
<% } %>
|
|
4776
5040
|
|
|
5041
|
+
if (!target && preselected) {
|
|
5042
|
+
target = preselected
|
|
5043
|
+
}
|
|
5044
|
+
|
|
5045
|
+
console.log({ targetAfter: target })
|
|
5046
|
+
|
|
4777
5047
|
if (!target) {
|
|
4778
5048
|
target = document.querySelector(".frame-link")
|
|
4779
5049
|
}
|
|
@@ -4829,7 +5099,7 @@ body.dark {
|
|
|
4829
5099
|
<% } %>
|
|
4830
5100
|
|
|
4831
5101
|
// hide all frames
|
|
4832
|
-
document.querySelectorAll("iframe").forEach((el) => {
|
|
5102
|
+
document.querySelectorAll("main.browserview iframe").forEach((el) => {
|
|
4833
5103
|
el.classList.add("hidden")
|
|
4834
5104
|
})
|
|
4835
5105
|
|
|
@@ -5074,7 +5344,7 @@ body.dark {
|
|
|
5074
5344
|
item.href = url
|
|
5075
5345
|
item.setAttribute("data-index", index)
|
|
5076
5346
|
item.className = "btn header-item frame-link"
|
|
5077
|
-
item.innerHTML = `<div class='tab'><i class="fa-solid fa-link"></i><div class='display'>${url}</div><div class='flexible'></div><button class='btn2 del'><i class="fa-solid fa-
|
|
5347
|
+
item.innerHTML = `<div class='tab'><i class="fa-solid fa-link"></i><div class='display'>${url}</div><div class='flexible'></div><button class='btn2 del'><i class="fa-solid fa-circle-stop"></i></button></div>`
|
|
5078
5348
|
|
|
5079
5349
|
document.querySelector(".temp-menu").appendChild(item)
|
|
5080
5350
|
|
|
@@ -5755,7 +6025,7 @@ body.dark {
|
|
|
5755
6025
|
})
|
|
5756
6026
|
setupTabLinkHover()
|
|
5757
6027
|
document.addEventListener("click", (event) => {
|
|
5758
|
-
if (event.target.closest("#fs-status .fs-dropdown-menu")) {
|
|
6028
|
+
if (event.target.closest("#fs-status .fs-dropdown-menu") || event.target.closest("#fs-status .fs-status-btn")) {
|
|
5759
6029
|
return
|
|
5760
6030
|
}
|
|
5761
6031
|
closeStatusDropdowns()
|
|
@@ -5975,9 +6245,9 @@ body.dark {
|
|
|
5975
6245
|
} else {
|
|
5976
6246
|
global_selector = null
|
|
5977
6247
|
}
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
6248
|
+
const frameExists = Array.from(document.querySelectorAll("main.browserview iframe")).some((frame) => {
|
|
6249
|
+
return frame.name === rawName
|
|
6250
|
+
})
|
|
5981
6251
|
if (!frameExists) {
|
|
5982
6252
|
create_iframe(rawName, event.data.launch.href)
|
|
5983
6253
|
}
|
|
@@ -6082,6 +6352,13 @@ body.dark {
|
|
|
6082
6352
|
const changesMenu = document.getElementById('fs-changes-menu')
|
|
6083
6353
|
const changesBtn = document.getElementById('fs-changes-btn')
|
|
6084
6354
|
const badgeElement = changesBtn ? changesBtn.querySelector('.badge') : null
|
|
6355
|
+
const forkBtn = document.getElementById('fs-fork-btn')
|
|
6356
|
+
const forkDropdownContainer = document.querySelector('#fs-status .git-fork')
|
|
6357
|
+
const forkMenu = document.getElementById('fs-fork-menu')
|
|
6358
|
+
const pushBtn = document.getElementById('fs-push-btn')
|
|
6359
|
+
const publishMenu = document.getElementById('fs-push-menu')
|
|
6360
|
+
|
|
6361
|
+
let latestGitIntegration = null
|
|
6085
6362
|
|
|
6086
6363
|
const readDataAttr = (node, attr) => {
|
|
6087
6364
|
if (!node) {
|
|
@@ -6097,6 +6374,9 @@ body.dark {
|
|
|
6097
6374
|
const statusUri = readDataAttr(fsStatusEl, 'data-status-uri')
|
|
6098
6375
|
const monitorUri = readDataAttr(fsStatusEl, 'data-uri')
|
|
6099
6376
|
const workspaceName = readDataAttr(fsStatusEl, 'data-workspace')
|
|
6377
|
+
const historyUri = readDataAttr(fsStatusEl, 'data-history-uri')
|
|
6378
|
+
const defaultPushUri = readDataAttr(fsStatusEl, 'data-push-uri')
|
|
6379
|
+
const defaultForkUri = readDataAttr(fsStatusEl, 'data-fork-uri')
|
|
6100
6380
|
|
|
6101
6381
|
const encodeRepoPath = (value) => {
|
|
6102
6382
|
if (typeof value !== 'string' || value.length === 0) {
|
|
@@ -6105,42 +6385,369 @@ body.dark {
|
|
|
6105
6385
|
return value.split('/').map(encodeURIComponent).join('/')
|
|
6106
6386
|
}
|
|
6107
6387
|
|
|
6108
|
-
|
|
6109
|
-
if (
|
|
6110
|
-
return
|
|
6388
|
+
function escapeHtml(value) {
|
|
6389
|
+
if (value === null || value === undefined) {
|
|
6390
|
+
return ''
|
|
6111
6391
|
}
|
|
6112
|
-
|
|
6392
|
+
return String(value).replace(/[&<>"']/g, (match) => {
|
|
6393
|
+
switch (match) {
|
|
6394
|
+
case '&':
|
|
6395
|
+
return '&'
|
|
6396
|
+
case '<':
|
|
6397
|
+
return '<'
|
|
6398
|
+
case '>':
|
|
6399
|
+
return '>'
|
|
6400
|
+
case '"':
|
|
6401
|
+
return '"'
|
|
6402
|
+
case '\'':
|
|
6403
|
+
return '''
|
|
6404
|
+
default:
|
|
6405
|
+
return match
|
|
6406
|
+
}
|
|
6407
|
+
})
|
|
6113
6408
|
}
|
|
6114
6409
|
|
|
6115
|
-
|
|
6116
|
-
if (
|
|
6117
|
-
return
|
|
6410
|
+
function getRepoListSnapshot() {
|
|
6411
|
+
if (Array.isArray(lastRepoList) && lastRepoList.length > 0) {
|
|
6412
|
+
return lastRepoList.slice()
|
|
6118
6413
|
}
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6414
|
+
return Array.from(repoStatusCache.values())
|
|
6415
|
+
}
|
|
6416
|
+
|
|
6417
|
+
function parseRemoteSlug(remoteUrl) {
|
|
6418
|
+
if (!remoteUrl || typeof remoteUrl !== 'string') {
|
|
6419
|
+
return { owner: null, name: null, full: null }
|
|
6420
|
+
}
|
|
6421
|
+
const trimmed = remoteUrl.trim()
|
|
6422
|
+
if (!trimmed) {
|
|
6423
|
+
return { owner: null, name: null, full: null }
|
|
6424
|
+
}
|
|
6425
|
+
const withoutGit = trimmed.replace(/\.git$/i, '')
|
|
6426
|
+
|
|
6427
|
+
const parsePathSegments = (pathValue) => {
|
|
6428
|
+
if (!pathValue) {
|
|
6429
|
+
return []
|
|
6430
|
+
}
|
|
6431
|
+
const cleaned = pathValue.replace(/^\/+/, '')
|
|
6432
|
+
return cleaned.split('/').filter(Boolean)
|
|
6433
|
+
}
|
|
6434
|
+
|
|
6435
|
+
let pathSegment = ''
|
|
6436
|
+
if (withoutGit.startsWith('git@')) {
|
|
6437
|
+
const colonSplit = withoutGit.split(':')
|
|
6438
|
+
pathSegment = colonSplit.length > 1 ? colonSplit.slice(1).join(':') : ''
|
|
6122
6439
|
} else {
|
|
6123
|
-
|
|
6124
|
-
|
|
6440
|
+
try {
|
|
6441
|
+
const prefixed = withoutGit.includes('://') ? withoutGit : `https://${withoutGit}`
|
|
6442
|
+
const url = new URL(prefixed)
|
|
6443
|
+
pathSegment = url.pathname
|
|
6444
|
+
} catch (error) {
|
|
6445
|
+
pathSegment = withoutGit
|
|
6446
|
+
}
|
|
6447
|
+
}
|
|
6448
|
+
|
|
6449
|
+
const segments = parsePathSegments(pathSegment)
|
|
6450
|
+
if (segments.length >= 2) {
|
|
6451
|
+
const owner = segments[segments.length - 2]
|
|
6452
|
+
const name = segments[segments.length - 1]
|
|
6453
|
+
return { owner, name, full: `${owner}/${name}` }
|
|
6454
|
+
}
|
|
6455
|
+
|
|
6456
|
+
if (segments.length === 1) {
|
|
6457
|
+
const name = segments[0]
|
|
6458
|
+
return { owner: null, name, full: name }
|
|
6125
6459
|
}
|
|
6460
|
+
|
|
6461
|
+
return { owner: null, name: null, full: null }
|
|
6126
6462
|
}
|
|
6127
6463
|
|
|
6128
|
-
|
|
6129
|
-
if (!
|
|
6130
|
-
return
|
|
6464
|
+
function deriveForkDefaultName(repo) {
|
|
6465
|
+
if (!repo) {
|
|
6466
|
+
return workspaceName || 'fork'
|
|
6131
6467
|
}
|
|
6132
|
-
const
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6468
|
+
const remoteSlug = parseRemoteSlug(repo.url || '')
|
|
6469
|
+
if (remoteSlug && remoteSlug.name) {
|
|
6470
|
+
return remoteSlug.name
|
|
6471
|
+
}
|
|
6472
|
+
if (typeof repo.name === 'string' && repo.name.length > 0) {
|
|
6473
|
+
const segments = repo.name.split('/').filter(Boolean)
|
|
6474
|
+
if (segments.length > 0) {
|
|
6475
|
+
return segments[segments.length - 1]
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
if (typeof repo.repoParam === 'string' && repo.repoParam.length > 0) {
|
|
6479
|
+
const repoSegments = repo.repoParam.split('/').filter(Boolean)
|
|
6480
|
+
if (repoSegments.length > 0) {
|
|
6481
|
+
return repoSegments[repoSegments.length - 1]
|
|
6482
|
+
}
|
|
6483
|
+
}
|
|
6484
|
+
return workspaceName || 'fork'
|
|
6137
6485
|
}
|
|
6138
6486
|
|
|
6139
|
-
|
|
6140
|
-
|
|
6487
|
+
function hasRemoteConfigured(repo) {
|
|
6488
|
+
return Boolean(repo && typeof repo.url === 'string' && repo.url.trim().length > 0)
|
|
6489
|
+
}
|
|
6490
|
+
|
|
6491
|
+
function resolveForkUri(repo) {
|
|
6492
|
+
if (repo && typeof repo.git_fork_url === 'string' && repo.git_fork_url.length > 0) {
|
|
6493
|
+
return repo.git_fork_url
|
|
6494
|
+
}
|
|
6495
|
+
return defaultForkUri || null
|
|
6496
|
+
}
|
|
6497
|
+
|
|
6498
|
+
function resolvePushUri(repo) {
|
|
6499
|
+
if (repo && typeof repo.git_push_url === 'string' && repo.git_push_url.length > 0) {
|
|
6500
|
+
return repo.git_push_url
|
|
6501
|
+
}
|
|
6502
|
+
return defaultPushUri || null
|
|
6503
|
+
}
|
|
6504
|
+
|
|
6505
|
+
function updateForkButton() {
|
|
6506
|
+
if (!forkBtn) {
|
|
6141
6507
|
return
|
|
6142
6508
|
}
|
|
6143
|
-
const
|
|
6509
|
+
const labelEl = forkBtn.querySelector('.fs-status-title')
|
|
6510
|
+
const repos = getRepoListSnapshot()
|
|
6511
|
+
const forkTargets = Array.isArray(repos)
|
|
6512
|
+
? repos.filter((repo) => hasRemoteConfigured(repo) && resolveForkUri(repo))
|
|
6513
|
+
: []
|
|
6514
|
+
|
|
6515
|
+
const detachHandlers = () => {
|
|
6516
|
+
forkBtn.removeEventListener('click', handlePushLogin)
|
|
6517
|
+
forkBtn.removeEventListener('click', requireGitLogin)
|
|
6518
|
+
}
|
|
6519
|
+
|
|
6520
|
+
const enableDropdown = () => {
|
|
6521
|
+
forkBtn.classList.add('revealer')
|
|
6522
|
+
forkBtn.setAttribute('data-group', '#fs-fork-menu')
|
|
6523
|
+
}
|
|
6524
|
+
|
|
6525
|
+
const disableDropdown = () => {
|
|
6526
|
+
forkBtn.classList.remove('revealer')
|
|
6527
|
+
forkBtn.removeAttribute('data-group')
|
|
6528
|
+
const forkMenuEl = document.querySelector('#fs-fork-menu')
|
|
6529
|
+
if (forkMenuEl && !forkMenuEl.classList.contains('hidden')) {
|
|
6530
|
+
forkMenuEl.classList.add('hidden')
|
|
6531
|
+
}
|
|
6532
|
+
setDropdownState(forkBtn, false)
|
|
6533
|
+
}
|
|
6534
|
+
|
|
6535
|
+
detachHandlers()
|
|
6536
|
+
|
|
6537
|
+
const isConnected = Boolean(latestGitIntegration && latestGitIntegration.connected)
|
|
6538
|
+
|
|
6539
|
+
renderForkDropdown(repos, {
|
|
6540
|
+
emptyMessage: isConnected
|
|
6541
|
+
? 'No remotes available to fork'
|
|
6542
|
+
: 'Connect GitHub to enable forking',
|
|
6543
|
+
})
|
|
6544
|
+
|
|
6545
|
+
if (!isConnected) {
|
|
6546
|
+
disableDropdown()
|
|
6547
|
+
if (labelEl) {
|
|
6548
|
+
labelEl.textContent = 'Fork'
|
|
6549
|
+
}
|
|
6550
|
+
forkBtn.disabled = false
|
|
6551
|
+
forkBtn.classList.remove('fs-status-btn--disabled')
|
|
6552
|
+
forkBtn.setAttribute('title', 'Log in to GitHub to fork this workspace')
|
|
6553
|
+
forkBtn.addEventListener('click', requireGitLogin)
|
|
6554
|
+
return
|
|
6555
|
+
}
|
|
6556
|
+
|
|
6557
|
+
if (labelEl) {
|
|
6558
|
+
labelEl.textContent = 'Fork'
|
|
6559
|
+
}
|
|
6560
|
+
|
|
6561
|
+
forkBtn.disabled = false
|
|
6562
|
+
forkBtn.classList.remove('fs-status-btn--disabled')
|
|
6563
|
+
enableDropdown()
|
|
6564
|
+
|
|
6565
|
+
if (!Array.isArray(repos) || repos.length === 0) {
|
|
6566
|
+
forkBtn.setAttribute('title', 'No Git repositories detected')
|
|
6567
|
+
return
|
|
6568
|
+
}
|
|
6569
|
+
|
|
6570
|
+
if (forkTargets.length === 0) {
|
|
6571
|
+
forkBtn.setAttribute('title', 'No remotes available to fork')
|
|
6572
|
+
return
|
|
6573
|
+
}
|
|
6574
|
+
|
|
6575
|
+
forkBtn.removeAttribute('title')
|
|
6576
|
+
}
|
|
6577
|
+
|
|
6578
|
+
const updateCombinedBadge = (total) => {
|
|
6579
|
+
if (!badgeElement) {
|
|
6580
|
+
return
|
|
6581
|
+
}
|
|
6582
|
+
badgeElement.textContent = total > 0 ? String(total) : ''
|
|
6583
|
+
}
|
|
6584
|
+
|
|
6585
|
+
const updateChangesButtonState = (hasRepos) => {
|
|
6586
|
+
if (!changesBtn) {
|
|
6587
|
+
return
|
|
6588
|
+
}
|
|
6589
|
+
if (hasRepos) {
|
|
6590
|
+
changesBtn.disabled = false
|
|
6591
|
+
changesBtn.classList.remove('fs-status-btn--disabled')
|
|
6592
|
+
} else {
|
|
6593
|
+
changesBtn.disabled = true
|
|
6594
|
+
changesBtn.classList.add('fs-status-btn--disabled')
|
|
6595
|
+
}
|
|
6596
|
+
}
|
|
6597
|
+
|
|
6598
|
+
const setChangesMenuMessage = (message) => {
|
|
6599
|
+
if (!changesMenu) {
|
|
6600
|
+
return
|
|
6601
|
+
}
|
|
6602
|
+
const messageEl = document.createElement('div')
|
|
6603
|
+
messageEl.className = 'fs-dropdown-empty'
|
|
6604
|
+
messageEl.textContent = message
|
|
6605
|
+
changesMenu.innerHTML = ''
|
|
6606
|
+
changesMenu.append(messageEl)
|
|
6607
|
+
}
|
|
6608
|
+
|
|
6609
|
+
const setForkMenuMessage = (message) => {
|
|
6610
|
+
if (!forkMenu) {
|
|
6611
|
+
return
|
|
6612
|
+
}
|
|
6613
|
+
const messageEl = document.createElement('div')
|
|
6614
|
+
messageEl.className = 'fs-dropdown-empty'
|
|
6615
|
+
messageEl.textContent = message
|
|
6616
|
+
forkMenu.innerHTML = ''
|
|
6617
|
+
forkMenu.append(messageEl)
|
|
6618
|
+
}
|
|
6619
|
+
|
|
6620
|
+
const setPublishMenuMessage = (message) => {
|
|
6621
|
+
if (!publishMenu) {
|
|
6622
|
+
return
|
|
6623
|
+
}
|
|
6624
|
+
const messageEl = document.createElement('div')
|
|
6625
|
+
messageEl.className = 'fs-dropdown-empty'
|
|
6626
|
+
messageEl.textContent = message
|
|
6627
|
+
publishMenu.innerHTML = ''
|
|
6628
|
+
publishMenu.append(messageEl)
|
|
6629
|
+
}
|
|
6630
|
+
|
|
6631
|
+
const renderForkDropdown = (repos, options = {}) => {
|
|
6632
|
+
if (!forkMenu) {
|
|
6633
|
+
return
|
|
6634
|
+
}
|
|
6635
|
+
|
|
6636
|
+
const opts = typeof options === 'object' && options !== null ? options : {}
|
|
6637
|
+
const list = Array.isArray(repos) ? repos : []
|
|
6638
|
+
|
|
6639
|
+
forkMenu.innerHTML = ''
|
|
6640
|
+
|
|
6641
|
+
if (list.length === 0) {
|
|
6642
|
+
setForkMenuMessage(opts.emptyMessage || 'No repositories available')
|
|
6643
|
+
return
|
|
6644
|
+
}
|
|
6645
|
+
|
|
6646
|
+
const fragment = document.createDocumentFragment()
|
|
6647
|
+
|
|
6648
|
+
list.forEach((repo) => {
|
|
6649
|
+
const key = repo && typeof repo.repoParam === 'string' ? repo.repoParam : ''
|
|
6650
|
+
const name = repo && repo.name ? repo.name : (key || 'Repository')
|
|
6651
|
+
const remoteUrl = repo && repo.url ? repo.url : ''
|
|
6652
|
+
const forkUri = resolveForkUri(repo)
|
|
6653
|
+
const hasRemote = hasRemoteConfigured(repo)
|
|
6654
|
+
|
|
6655
|
+
const item = document.createElement('button')
|
|
6656
|
+
item.type = 'button'
|
|
6657
|
+
item.className = 'fs-dropdown-item pinokio-fork-dropdown-item'
|
|
6658
|
+
item.dataset.repo = key
|
|
6659
|
+
|
|
6660
|
+
const remoteClass = remoteUrl
|
|
6661
|
+
? 'pinokio-fork-dropdown-remote'
|
|
6662
|
+
: 'pinokio-fork-dropdown-remote empty'
|
|
6663
|
+
const remoteDisplay = remoteUrl ? escapeHtml(remoteUrl) : 'No remote detected'
|
|
6664
|
+
|
|
6665
|
+
item.innerHTML = `
|
|
6666
|
+
<div class="pinokio-fork-dropdown-title">${escapeHtml(name)}</div>
|
|
6667
|
+
<div class="${remoteClass}">${remoteDisplay}</div>
|
|
6668
|
+
`
|
|
6669
|
+
|
|
6670
|
+
if (!hasRemote || !forkUri) {
|
|
6671
|
+
item.disabled = true
|
|
6672
|
+
item.classList.add('fs-dropdown-item--disabled')
|
|
6673
|
+
} else {
|
|
6674
|
+
item.addEventListener('click', (event) => {
|
|
6675
|
+
event.preventDefault()
|
|
6676
|
+
event.stopPropagation()
|
|
6677
|
+
closeStatusDropdowns()
|
|
6678
|
+
showForkModalForRepo(key)
|
|
6679
|
+
})
|
|
6680
|
+
}
|
|
6681
|
+
|
|
6682
|
+
fragment.appendChild(item)
|
|
6683
|
+
})
|
|
6684
|
+
|
|
6685
|
+
forkMenu.appendChild(fragment)
|
|
6686
|
+
}
|
|
6687
|
+
|
|
6688
|
+
const renderPublishDropdown = (repos, options = {}) => {
|
|
6689
|
+
if (!publishMenu) {
|
|
6690
|
+
return
|
|
6691
|
+
}
|
|
6692
|
+
|
|
6693
|
+
const opts = typeof options === 'object' && options !== null ? options : {}
|
|
6694
|
+
const list = Array.isArray(repos) ? repos : []
|
|
6695
|
+
|
|
6696
|
+
publishMenu.innerHTML = ''
|
|
6697
|
+
|
|
6698
|
+
if (list.length === 0) {
|
|
6699
|
+
setPublishMenuMessage(opts.emptyMessage || 'No repositories available')
|
|
6700
|
+
return
|
|
6701
|
+
}
|
|
6702
|
+
|
|
6703
|
+
const fragment = document.createDocumentFragment()
|
|
6704
|
+
|
|
6705
|
+
list.forEach((repo) => {
|
|
6706
|
+
const key = repo && typeof repo.repoParam === 'string' ? repo.repoParam : ''
|
|
6707
|
+
const name = repo && repo.name ? repo.name : (key || 'Repository')
|
|
6708
|
+
const remoteUrl = repo && repo.url ? repo.url : ''
|
|
6709
|
+
const pushUri = resolvePushUri(repo)
|
|
6710
|
+
const hasRemote = hasRemoteConfigured(repo)
|
|
6711
|
+
|
|
6712
|
+
const item = document.createElement('button')
|
|
6713
|
+
item.type = 'button'
|
|
6714
|
+
item.className = 'fs-dropdown-item pinokio-publish-dropdown-item'
|
|
6715
|
+
item.dataset.repo = key
|
|
6716
|
+
item.dataset.name = name
|
|
6717
|
+
|
|
6718
|
+
const remoteClass = remoteUrl
|
|
6719
|
+
? 'pinokio-publish-dropdown-remote'
|
|
6720
|
+
: 'pinokio-publish-dropdown-remote empty'
|
|
6721
|
+
const remoteDisplay = remoteUrl ? escapeHtml(remoteUrl) : 'No remote detected'
|
|
6722
|
+
|
|
6723
|
+
item.innerHTML = `
|
|
6724
|
+
<div class="pinokio-publish-dropdown-title">${escapeHtml(name)}</div>
|
|
6725
|
+
<div class="${remoteClass}">${remoteDisplay}</div>
|
|
6726
|
+
`
|
|
6727
|
+
|
|
6728
|
+
if (!hasRemote || !pushUri) {
|
|
6729
|
+
item.disabled = true
|
|
6730
|
+
item.classList.add('fs-dropdown-item--disabled')
|
|
6731
|
+
} else {
|
|
6732
|
+
item.addEventListener('click', (event) => {
|
|
6733
|
+
event.preventDefault()
|
|
6734
|
+
event.stopPropagation()
|
|
6735
|
+
closeStatusDropdowns()
|
|
6736
|
+
showPublishModal({ repoParam: key, repoName: name })
|
|
6737
|
+
})
|
|
6738
|
+
}
|
|
6739
|
+
|
|
6740
|
+
fragment.appendChild(item)
|
|
6741
|
+
})
|
|
6742
|
+
|
|
6743
|
+
publishMenu.appendChild(fragment)
|
|
6744
|
+
}
|
|
6745
|
+
|
|
6746
|
+
const attachRepoDropdownHandlers = () => {
|
|
6747
|
+
if (!changesMenu) {
|
|
6748
|
+
return
|
|
6749
|
+
}
|
|
6750
|
+
const items = changesMenu.querySelectorAll('.git-changes-item')
|
|
6144
6751
|
items.forEach((item) => {
|
|
6145
6752
|
item.addEventListener('click', async (event) => {
|
|
6146
6753
|
event.preventDefault()
|
|
@@ -6233,6 +6840,8 @@ body.dark {
|
|
|
6233
6840
|
changes: Array.isArray(data.changes) ? data.changes : [],
|
|
6234
6841
|
git_commit_url: data.git_commit_url || null,
|
|
6235
6842
|
git_history_url: workspaceName ? `/info/git/HEAD/${encodeRepoPath(workspaceName)}` : readDataAttr(fsStatusEl, 'data-history-uri'),
|
|
6843
|
+
git_fork_url: defaultForkUri,
|
|
6844
|
+
git_push_url: defaultPushUri,
|
|
6236
6845
|
url: null,
|
|
6237
6846
|
}
|
|
6238
6847
|
|
|
@@ -6246,12 +6855,14 @@ body.dark {
|
|
|
6246
6855
|
renderChangesDropdown(lastRepoList)
|
|
6247
6856
|
updateCombinedBadge(fallbackRepo.changeCount)
|
|
6248
6857
|
updatePublishButton()
|
|
6858
|
+
updateForkButton()
|
|
6249
6859
|
return true
|
|
6250
6860
|
} catch (error) {
|
|
6251
6861
|
console.error('check_git fallback error:', error)
|
|
6252
6862
|
setChangesMenuMessage('Unable to load repositories')
|
|
6253
6863
|
updateCombinedBadge(0)
|
|
6254
6864
|
updateChangesButtonState(false)
|
|
6865
|
+
updateForkButton()
|
|
6255
6866
|
return false
|
|
6256
6867
|
}
|
|
6257
6868
|
}
|
|
@@ -6316,6 +6927,7 @@ body.dark {
|
|
|
6316
6927
|
: sortedRepos.reduce((sum, repo) => sum + (repo.changeCount || 0), 0)
|
|
6317
6928
|
updateCombinedBadge(total)
|
|
6318
6929
|
updateChangesButtonState(true)
|
|
6930
|
+
updateForkButton()
|
|
6319
6931
|
}
|
|
6320
6932
|
|
|
6321
6933
|
updatePublishButton()
|
|
@@ -6526,6 +7138,12 @@ body.dark {
|
|
|
6526
7138
|
updatedRepo.git_commit_url = freshData.git_commit_url || null
|
|
6527
7139
|
updatedRepo.name = updatedRepo.name || repoDisplayName || repoParam
|
|
6528
7140
|
updatedRepo.git_history_url = updatedRepo.git_history_url || (repoData && repoData.git_history_url) || (repoParam ? `/info/git/HEAD/${encodeRepoPath(repoParam)}` : null)
|
|
7141
|
+
if (!updatedRepo.git_fork_url) {
|
|
7142
|
+
updatedRepo.git_fork_url = repoData && repoData.git_fork_url ? repoData.git_fork_url : resolveForkUri(updatedRepo)
|
|
7143
|
+
}
|
|
7144
|
+
if (!updatedRepo.git_push_url) {
|
|
7145
|
+
updatedRepo.git_push_url = repoData && repoData.git_push_url ? repoData.git_push_url : resolvePushUri(updatedRepo)
|
|
7146
|
+
}
|
|
6529
7147
|
repoStatusCache.set(repoParam, updatedRepo)
|
|
6530
7148
|
repoData = updatedRepo
|
|
6531
7149
|
const listEntry = lastRepoList.find((entry) => entry.repoParam === repoParam)
|
|
@@ -6536,6 +7154,12 @@ body.dark {
|
|
|
6536
7154
|
if (updatedRepo.git_history_url) {
|
|
6537
7155
|
listEntry.git_history_url = updatedRepo.git_history_url
|
|
6538
7156
|
}
|
|
7157
|
+
if (!listEntry.git_fork_url && updatedRepo.git_fork_url) {
|
|
7158
|
+
listEntry.git_fork_url = updatedRepo.git_fork_url
|
|
7159
|
+
}
|
|
7160
|
+
if (!listEntry.git_push_url && updatedRepo.git_push_url) {
|
|
7161
|
+
listEntry.git_push_url = updatedRepo.git_push_url
|
|
7162
|
+
}
|
|
6539
7163
|
}
|
|
6540
7164
|
const total = Array.from(repoStatusCache.values()).reduce((sum, repo) => sum + (repo.changeCount || 0), 0)
|
|
6541
7165
|
updateCombinedBadge(total)
|
|
@@ -7030,6 +7654,12 @@ body.dark {
|
|
|
7030
7654
|
closeBtn.addEventListener('click', () => Swal.close())
|
|
7031
7655
|
}
|
|
7032
7656
|
|
|
7657
|
+
if (iframe) {
|
|
7658
|
+
iframe.dataset.forceVisible = 'true'
|
|
7659
|
+
iframe.classList.remove('hidden')
|
|
7660
|
+
iframe.removeAttribute('hidden')
|
|
7661
|
+
}
|
|
7662
|
+
|
|
7033
7663
|
disconnectHandler = (event) => {
|
|
7034
7664
|
if (!iframe || event.source !== iframe.contentWindow) return
|
|
7035
7665
|
if (!event.data || event.data.type !== 'pinokio:socket-closed') return
|
|
@@ -7055,16 +7685,62 @@ body.dark {
|
|
|
7055
7685
|
}
|
|
7056
7686
|
}
|
|
7057
7687
|
|
|
7058
|
-
const showPublishModal = () => {
|
|
7059
|
-
|
|
7688
|
+
const showPublishModal = (target) => {
|
|
7689
|
+
let repoParam = null
|
|
7690
|
+
let repoLabel = null
|
|
7691
|
+
|
|
7692
|
+
if (typeof target === 'string') {
|
|
7693
|
+
repoParam = target
|
|
7694
|
+
} else if (target && typeof target === 'object') {
|
|
7695
|
+
if (typeof target.repoParam === 'string') {
|
|
7696
|
+
repoParam = target.repoParam
|
|
7697
|
+
}
|
|
7698
|
+
if (typeof target.repoName === 'string' && target.repoName.trim().length > 0) {
|
|
7699
|
+
repoLabel = target.repoName.trim()
|
|
7700
|
+
}
|
|
7701
|
+
}
|
|
7702
|
+
|
|
7703
|
+
if (!repoParam) {
|
|
7704
|
+
repoParam = activeRepoKey || null
|
|
7705
|
+
}
|
|
7706
|
+
|
|
7707
|
+
let repoData = null
|
|
7708
|
+
if (repoParam) {
|
|
7709
|
+
repoData = repoStatusCache.get(repoParam) || null
|
|
7710
|
+
if (!repoData && lastRepoList.length > 0) {
|
|
7711
|
+
const fallbackMatch = lastRepoList.find((repo) => repo.repoParam === repoParam)
|
|
7712
|
+
if (fallbackMatch) {
|
|
7713
|
+
repoData = fallbackMatch
|
|
7714
|
+
}
|
|
7715
|
+
}
|
|
7716
|
+
}
|
|
7717
|
+
|
|
7718
|
+
if (!repoLabel && repoData && typeof repoData.name === 'string') {
|
|
7719
|
+
repoLabel = repoData.name
|
|
7720
|
+
}
|
|
7721
|
+
if (!repoLabel && repoParam) {
|
|
7722
|
+
repoLabel = repoParam
|
|
7723
|
+
}
|
|
7724
|
+
if (!repoLabel) {
|
|
7725
|
+
repoLabel = workspaceName || 'Repository'
|
|
7726
|
+
}
|
|
7727
|
+
|
|
7728
|
+
const pushUri = resolvePushUri(repoData)
|
|
7060
7729
|
if (!pushUri) {
|
|
7061
7730
|
Swal.fire({
|
|
7062
7731
|
title: 'Error',
|
|
7063
|
-
text: 'Publish URL not available.',
|
|
7732
|
+
text: 'Publish URL not available for this repository.',
|
|
7064
7733
|
icon: 'error'
|
|
7065
7734
|
})
|
|
7066
7735
|
return
|
|
7067
7736
|
}
|
|
7737
|
+
|
|
7738
|
+
const remoteDisplay = repoData && repoData.url ? escapeHtml(repoData.url) : null
|
|
7739
|
+
const subtitle = `${escapeHtml(repoLabel)} — review your latest changes before publishing.`
|
|
7740
|
+
const remoteHtml = remoteDisplay ? `<div class="pinokio-fork-item-url">${remoteDisplay}</div>` : ''
|
|
7741
|
+
const timestampedUri = pushUri.includes('?')
|
|
7742
|
+
? `${pushUri}&ts=${Date.now()}`
|
|
7743
|
+
: `${pushUri}?ts=${Date.now()}`
|
|
7068
7744
|
|
|
7069
7745
|
const modalHtml = `
|
|
7070
7746
|
<div class="pinokio-modal-surface">
|
|
@@ -7072,11 +7748,12 @@ body.dark {
|
|
|
7072
7748
|
<div class="pinokio-modal-icon"><i class="fa-brands fa-github"></i></div>
|
|
7073
7749
|
<div class="pinokio-modal-heading">
|
|
7074
7750
|
<div class="pinokio-modal-title">Publish to GitHub</div>
|
|
7075
|
-
<div class="pinokio-modal-subtitle"
|
|
7751
|
+
<div class="pinokio-modal-subtitle">${subtitle}</div>
|
|
7752
|
+
${remoteHtml}
|
|
7076
7753
|
</div>
|
|
7077
7754
|
</div>
|
|
7078
7755
|
<div class="pinokio-modal-body pinokio-modal-body--iframe">
|
|
7079
|
-
<iframe src="${
|
|
7756
|
+
<iframe src="${timestampedUri}"></iframe>
|
|
7080
7757
|
</div>
|
|
7081
7758
|
<div class="pinokio-modal-footer pinokio-modal-footer--publish" data-publish-footer>
|
|
7082
7759
|
<button type="button" class="pinokio-publish-close-btn" data-publish-close>Close</button>
|
|
@@ -7329,65 +8006,424 @@ body.dark {
|
|
|
7329
8006
|
focusConfirm: false
|
|
7330
8007
|
})
|
|
7331
8008
|
}
|
|
8009
|
+
|
|
8010
|
+
async function showForkModalForRepo(repoParam) {
|
|
8011
|
+
const key = typeof repoParam === 'string' && repoParam.length > 0 ? repoParam : (activeRepoKey || null)
|
|
8012
|
+
if (!key) {
|
|
8013
|
+
Swal.fire({
|
|
8014
|
+
title: 'No repository selected',
|
|
8015
|
+
text: 'Select a repository to fork from the dropdown.',
|
|
8016
|
+
icon: 'info'
|
|
8017
|
+
})
|
|
8018
|
+
return
|
|
8019
|
+
}
|
|
8020
|
+
|
|
8021
|
+
let repo = repoStatusCache.get(key)
|
|
8022
|
+
if (!repo) {
|
|
8023
|
+
await check_git()
|
|
8024
|
+
repo = repoStatusCache.get(key)
|
|
8025
|
+
}
|
|
8026
|
+
|
|
8027
|
+
if (!repo) {
|
|
8028
|
+
Swal.fire({
|
|
8029
|
+
title: 'Repository unavailable',
|
|
8030
|
+
text: 'The selected repository could not be found. Refresh Git status and try again.',
|
|
8031
|
+
icon: 'error'
|
|
8032
|
+
})
|
|
8033
|
+
return
|
|
8034
|
+
}
|
|
8035
|
+
|
|
8036
|
+
if (!hasRemoteConfigured(repo)) {
|
|
8037
|
+
Swal.fire({
|
|
8038
|
+
title: 'Remote required',
|
|
8039
|
+
text: 'This repository does not have a remote configured, so it cannot be forked.',
|
|
8040
|
+
icon: 'info'
|
|
8041
|
+
})
|
|
8042
|
+
return
|
|
8043
|
+
}
|
|
8044
|
+
|
|
8045
|
+
const forkUri = resolveForkUri(repo)
|
|
8046
|
+
if (!forkUri) {
|
|
8047
|
+
Swal.fire({
|
|
8048
|
+
title: 'Fork script unavailable',
|
|
8049
|
+
text: 'Unable to locate the fork script for this repository.',
|
|
8050
|
+
icon: 'error'
|
|
8051
|
+
})
|
|
8052
|
+
return
|
|
8053
|
+
}
|
|
8054
|
+
|
|
8055
|
+
const defaultName = deriveForkDefaultName(repo)
|
|
8056
|
+
const remoteDisplay = repo.url ? escapeHtml(repo.url) : 'No remote detected'
|
|
8057
|
+
const remoteClass = repo.url ? 'pinokio-fork-item-url' : 'pinokio-fork-item-url empty'
|
|
8058
|
+
const nameInputId = 'pinokio-fork-name-input'
|
|
8059
|
+
const orgToggleId = 'pinokio-fork-org-toggle'
|
|
8060
|
+
const orgInputId = 'pinokio-fork-org-input'
|
|
8061
|
+
|
|
8062
|
+
const modalHtml = `
|
|
8063
|
+
<div class="pinokio-modal-surface">
|
|
8064
|
+
<div class="pinokio-modal-header">
|
|
8065
|
+
<div class="pinokio-modal-icon"><i class="fa-brands fa-github"></i></div>
|
|
8066
|
+
<div class="pinokio-modal-heading">
|
|
8067
|
+
<div class="pinokio-modal-title">Fork repository</div>
|
|
8068
|
+
<div class="pinokio-modal-subtitle">${escapeHtml(repo.name || key)}</div>
|
|
8069
|
+
</div>
|
|
8070
|
+
</div>
|
|
8071
|
+
<div class="pinokio-modal-body">
|
|
8072
|
+
<div class="pinokio-fork-modal">
|
|
8073
|
+
<div class="pinokio-fork-item" data-disabled="false">
|
|
8074
|
+
<div class="pinokio-fork-item-url ${remoteClass}">${remoteDisplay}</div>
|
|
8075
|
+
<div class="pinokio-fork-name-input">
|
|
8076
|
+
<label for="${nameInputId}">Fork name</label>
|
|
8077
|
+
<input id="${nameInputId}" class="pinokio-modal-input" value="${escapeHtml(defaultName || '')}">
|
|
8078
|
+
</div>
|
|
8079
|
+
<div class="pinokio-fork-checkbox-row">
|
|
8080
|
+
<input type="checkbox" id="${orgToggleId}" data-fork-org-toggle>
|
|
8081
|
+
<label for="${orgToggleId}">Fork into organization</label>
|
|
8082
|
+
</div>
|
|
8083
|
+
<div class="pinokio-fork-org-input hidden" data-fork-org-section>
|
|
8084
|
+
<label for="${orgInputId}">Organization</label>
|
|
8085
|
+
<input id="${orgInputId}" class="pinokio-modal-input" placeholder="my-org">
|
|
8086
|
+
<p class="pinokio-fork-org-hint">Provide an organization where you have permission to create repositories.</p>
|
|
8087
|
+
</div>
|
|
8088
|
+
</div>
|
|
8089
|
+
</div>
|
|
8090
|
+
</div>
|
|
8091
|
+
</div>
|
|
8092
|
+
`
|
|
8093
|
+
|
|
8094
|
+
Swal.fire({
|
|
8095
|
+
html: modalHtml,
|
|
8096
|
+
customClass: {
|
|
8097
|
+
popup: 'pinokio-modern-modal',
|
|
8098
|
+
htmlContainer: 'pinokio-modern-html',
|
|
8099
|
+
closeButton: 'pinokio-modern-close',
|
|
8100
|
+
confirmButton: 'pinokio-modern-confirm',
|
|
8101
|
+
cancelButton: 'pinokio-modern-cancel'
|
|
8102
|
+
},
|
|
8103
|
+
backdrop: 'rgba(9,11,15,0.65)',
|
|
8104
|
+
width: 'min(520px, 90vw)',
|
|
8105
|
+
buttonsStyling: false,
|
|
8106
|
+
showCloseButton: true,
|
|
8107
|
+
showCancelButton: true,
|
|
8108
|
+
showConfirmButton: true,
|
|
8109
|
+
confirmButtonText: 'Fork on GitHub',
|
|
8110
|
+
cancelButtonText: 'Cancel',
|
|
8111
|
+
focusConfirm: false,
|
|
8112
|
+
didOpen: (popup) => {
|
|
8113
|
+
const input = popup.querySelector(`#${nameInputId}`)
|
|
8114
|
+
if (input) {
|
|
8115
|
+
input.addEventListener('input', () => {
|
|
8116
|
+
input.classList.remove('pinokio-modal-input--error')
|
|
8117
|
+
})
|
|
8118
|
+
input.focus()
|
|
8119
|
+
input.select()
|
|
8120
|
+
}
|
|
8121
|
+
const orgToggle = popup.querySelector('#' + orgToggleId)
|
|
8122
|
+
const orgSection = popup.querySelector('[data-fork-org-section]')
|
|
8123
|
+
const orgInput = popup.querySelector('#' + orgInputId)
|
|
8124
|
+
const syncOrgSection = () => {
|
|
8125
|
+
if (!orgSection) {
|
|
8126
|
+
return
|
|
8127
|
+
}
|
|
8128
|
+
if (orgToggle && orgToggle.checked) {
|
|
8129
|
+
orgSection.classList.remove('hidden')
|
|
8130
|
+
if (orgInput) {
|
|
8131
|
+
requestAnimationFrame(() => orgInput.focus())
|
|
8132
|
+
}
|
|
8133
|
+
} else {
|
|
8134
|
+
orgSection.classList.add('hidden')
|
|
8135
|
+
if (orgInput) {
|
|
8136
|
+
orgInput.classList.remove('pinokio-modal-input--error')
|
|
8137
|
+
}
|
|
8138
|
+
}
|
|
8139
|
+
}
|
|
8140
|
+
if (orgToggle) {
|
|
8141
|
+
orgToggle.addEventListener('change', syncOrgSection)
|
|
8142
|
+
syncOrgSection()
|
|
8143
|
+
}
|
|
8144
|
+
if (orgInput) {
|
|
8145
|
+
orgInput.addEventListener('input', () => {
|
|
8146
|
+
orgInput.classList.remove('pinokio-modal-input--error')
|
|
8147
|
+
})
|
|
8148
|
+
}
|
|
8149
|
+
},
|
|
8150
|
+
preConfirm: () => {
|
|
8151
|
+
const input = document.getElementById(nameInputId)
|
|
8152
|
+
const forkName = input ? input.value.trim() : ''
|
|
8153
|
+
if (!forkName) {
|
|
8154
|
+
if (input) {
|
|
8155
|
+
input.classList.add('pinokio-modal-input--error')
|
|
8156
|
+
setTimeout(() => input.focus(), 0)
|
|
8157
|
+
}
|
|
8158
|
+
Swal.showValidationMessage('Enter a repository name for the fork')
|
|
8159
|
+
return false
|
|
8160
|
+
}
|
|
8161
|
+
const orgToggle = document.getElementById(orgToggleId)
|
|
8162
|
+
const orgInput = document.getElementById(orgInputId)
|
|
8163
|
+
let orgValue = null
|
|
8164
|
+
if (orgToggle && orgToggle.checked) {
|
|
8165
|
+
orgValue = orgInput ? orgInput.value.trim() : ''
|
|
8166
|
+
if (!orgValue) {
|
|
8167
|
+
if (orgInput) {
|
|
8168
|
+
orgInput.classList.add('pinokio-modal-input--error')
|
|
8169
|
+
setTimeout(() => orgInput.focus(), 0)
|
|
8170
|
+
}
|
|
8171
|
+
Swal.showValidationMessage('Enter the organization name or uncheck the option')
|
|
8172
|
+
return false
|
|
8173
|
+
}
|
|
8174
|
+
}
|
|
8175
|
+
return {
|
|
8176
|
+
job: {
|
|
8177
|
+
repoParam: key,
|
|
8178
|
+
repoName: repo.name || key,
|
|
8179
|
+
forkUri,
|
|
8180
|
+
forkName,
|
|
8181
|
+
remoteUrl: repo.url || '',
|
|
8182
|
+
org: orgValue,
|
|
8183
|
+
}
|
|
8184
|
+
}
|
|
8185
|
+
}
|
|
8186
|
+
}).then((result) => {
|
|
8187
|
+
if (result.isConfirmed && result.value && result.value.job) {
|
|
8188
|
+
showForkShellModal(result.value.job)
|
|
8189
|
+
}
|
|
8190
|
+
})
|
|
8191
|
+
}
|
|
8192
|
+
|
|
8193
|
+
function showForkShellModal(job) {
|
|
8194
|
+
if (!job || !job.forkUri) {
|
|
8195
|
+
return
|
|
8196
|
+
}
|
|
8197
|
+
|
|
8198
|
+
const lifecycle = createPublishModalLifecycle()
|
|
8199
|
+
const forkNameValue = typeof job.forkName === 'string' && job.forkName.trim().length > 0
|
|
8200
|
+
? job.forkName.trim()
|
|
8201
|
+
: deriveForkDefaultName({ name: job.repoName, url: job.remoteUrl })
|
|
8202
|
+
const queryParts = [`name=${encodeURIComponent(forkNameValue)}`]
|
|
8203
|
+
if (job.org) {
|
|
8204
|
+
queryParts.push(`org=${encodeURIComponent(job.org)}`)
|
|
8205
|
+
}
|
|
8206
|
+
queryParts.push(`ts=${Date.now()}`)
|
|
8207
|
+
const separator = job.forkUri.includes('?') ? '&' : '?'
|
|
8208
|
+
const finalUri = `${job.forkUri}${separator}${queryParts.join('&')}`
|
|
8209
|
+
|
|
8210
|
+
const remoteDisplay = job.remoteUrl ? escapeHtml(job.remoteUrl) : 'No remote detected'
|
|
8211
|
+
const titleDisplay = job.repoName ? escapeHtml(job.repoName) : 'Repository'
|
|
8212
|
+
const forkDisplay = escapeHtml(forkNameValue)
|
|
8213
|
+
const subtitleParts = [titleDisplay, forkDisplay]
|
|
8214
|
+
if (job.org) {
|
|
8215
|
+
subtitleParts.push(`Org: ${escapeHtml(job.org)}`)
|
|
8216
|
+
}
|
|
8217
|
+
const subtitleHtml = subtitleParts.join(' · ')
|
|
8218
|
+
Swal.fire({
|
|
8219
|
+
html: `
|
|
8220
|
+
<div class="pinokio-modal-surface">
|
|
8221
|
+
<div class="pinokio-modal-header">
|
|
8222
|
+
<div class="pinokio-modal-icon"><i class="fa-brands fa-github"></i></div>
|
|
8223
|
+
<div class="pinokio-modal-heading">
|
|
8224
|
+
<div class="pinokio-modal-title">Fork on GitHub</div>
|
|
8225
|
+
<div class="pinokio-modal-subtitle">${subtitleHtml}</div>
|
|
8226
|
+
<div class="pinokio-fork-item-url">${remoteDisplay}</div>
|
|
8227
|
+
</div>
|
|
8228
|
+
</div>
|
|
8229
|
+
<div class="pinokio-modal-body pinokio-modal-body--iframe">
|
|
8230
|
+
<iframe src="${finalUri}"></iframe>
|
|
8231
|
+
</div>
|
|
8232
|
+
<div class="pinokio-modal-footer pinokio-modal-footer--publish" data-publish-footer>
|
|
8233
|
+
<button type="button" class="pinokio-publish-close-btn" data-publish-close>Close</button>
|
|
8234
|
+
</div>
|
|
8235
|
+
</div>
|
|
8236
|
+
`,
|
|
8237
|
+
customClass: {
|
|
8238
|
+
popup: 'pinokio-modern-modal',
|
|
8239
|
+
htmlContainer: 'pinokio-modern-html',
|
|
8240
|
+
closeButton: 'pinokio-modern-close'
|
|
8241
|
+
},
|
|
8242
|
+
backdrop: 'rgba(9,11,15,0.65)',
|
|
8243
|
+
width: 'min(760px, 94vw)',
|
|
8244
|
+
buttonsStyling: false,
|
|
8245
|
+
showConfirmButton: false,
|
|
8246
|
+
showCloseButton: true,
|
|
8247
|
+
focusConfirm: false,
|
|
8248
|
+
didOpen: lifecycle.didOpen,
|
|
8249
|
+
willClose: lifecycle.willClose
|
|
8250
|
+
}).then(() => {
|
|
8251
|
+
setTimeout(() => {
|
|
8252
|
+
updateForkButton()
|
|
8253
|
+
check_git()
|
|
8254
|
+
}, 250)
|
|
8255
|
+
})
|
|
8256
|
+
}
|
|
7332
8257
|
|
|
7333
|
-
|
|
8258
|
+
function handlePushLogin() {
|
|
7334
8259
|
window.location.href = '/github'
|
|
7335
8260
|
}
|
|
8261
|
+
const promptGitLogin = () => {
|
|
8262
|
+
Swal.fire({
|
|
8263
|
+
html: `
|
|
8264
|
+
<div class="pinokio-github-login">
|
|
8265
|
+
<div class="pinokio-github-login__icon"><i class="fa-brands fa-github"></i></div>
|
|
8266
|
+
<div class="pinokio-github-login__title">Log in to GitHub</div>
|
|
8267
|
+
<div class="pinokio-github-login__body">Connect your GitHub account to fork or publish this workspace.</div>
|
|
8268
|
+
</div>
|
|
8269
|
+
`,
|
|
8270
|
+
showCancelButton: true,
|
|
8271
|
+
confirmButtonText: 'Log in',
|
|
8272
|
+
cancelButtonText: 'Not now',
|
|
8273
|
+
reverseButtons: true,
|
|
8274
|
+
showCloseButton: true,
|
|
8275
|
+
focusConfirm: true,
|
|
8276
|
+
customClass: {
|
|
8277
|
+
popup: 'pinokio-modern-modal pinokio-github-login-modal',
|
|
8278
|
+
htmlContainer: 'pinokio-modern-html',
|
|
8279
|
+
confirmButton: 'pinokio-modern-confirm',
|
|
8280
|
+
cancelButton: 'pinokio-modern-cancel',
|
|
8281
|
+
closeButton: 'pinokio-modern-close'
|
|
8282
|
+
}
|
|
8283
|
+
}).then((result) => {
|
|
8284
|
+
if (result.isConfirmed) {
|
|
8285
|
+
handlePushLogin()
|
|
8286
|
+
}
|
|
8287
|
+
})
|
|
8288
|
+
}
|
|
8289
|
+
const requireGitLogin = (event) => {
|
|
8290
|
+
if (event) {
|
|
8291
|
+
event.preventDefault()
|
|
8292
|
+
event.stopPropagation()
|
|
8293
|
+
}
|
|
8294
|
+
promptGitLogin()
|
|
8295
|
+
}
|
|
7336
8296
|
|
|
7337
8297
|
// Function to update publish/create button
|
|
7338
8298
|
const updatePublishButton = async () => {
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
8299
|
+
if (!pushBtn) {
|
|
8300
|
+
latestGitIntegration = null
|
|
8301
|
+
updateForkButton()
|
|
8302
|
+
return
|
|
8303
|
+
}
|
|
8304
|
+
|
|
8305
|
+
const labelEl = pushBtn.querySelector('.fs-status-title')
|
|
8306
|
+
const setLabel = (text) => {
|
|
8307
|
+
if (labelEl) {
|
|
8308
|
+
labelEl.textContent = text
|
|
8309
|
+
}
|
|
8310
|
+
}
|
|
8311
|
+
const detachHandlers = () => {
|
|
8312
|
+
pushBtn.removeEventListener('click', showPublishModal)
|
|
8313
|
+
pushBtn.removeEventListener('click', showCreateModal)
|
|
8314
|
+
pushBtn.removeEventListener('click', handlePushLogin)
|
|
8315
|
+
pushBtn.removeEventListener('click', requireGitLogin)
|
|
8316
|
+
}
|
|
8317
|
+
const enableDropdown = () => {
|
|
8318
|
+
pushBtn.classList.add('revealer')
|
|
8319
|
+
pushBtn.setAttribute('data-group', '#fs-push-menu')
|
|
8320
|
+
}
|
|
8321
|
+
const disableDropdown = () => {
|
|
8322
|
+
pushBtn.classList.remove('revealer')
|
|
8323
|
+
pushBtn.removeAttribute('data-group')
|
|
8324
|
+
const pushMenuEl = document.querySelector('#fs-push-menu')
|
|
8325
|
+
if (pushMenuEl && !pushMenuEl.classList.contains('hidden')) {
|
|
8326
|
+
pushMenuEl.classList.add('hidden')
|
|
8327
|
+
}
|
|
8328
|
+
setDropdownState(pushBtn, false)
|
|
8329
|
+
}
|
|
8330
|
+
|
|
8331
|
+
const repos = getRepoListSnapshot()
|
|
8332
|
+
const publishTargets = Array.isArray(repos)
|
|
8333
|
+
? repos.filter((repo) => hasRemoteConfigured(repo) && resolvePushUri(repo))
|
|
8334
|
+
: []
|
|
8335
|
+
|
|
8336
|
+
detachHandlers()
|
|
8337
|
+
|
|
8338
|
+
const syncForkButton = () => {
|
|
8339
|
+
updateForkButton()
|
|
8340
|
+
}
|
|
8341
|
+
|
|
8342
|
+
if (!historyUri) {
|
|
8343
|
+
setPublishMenuMessage('Git integration unavailable')
|
|
8344
|
+
setLabel('Publish')
|
|
8345
|
+
disableDropdown()
|
|
8346
|
+
pushBtn.disabled = publishTargets.length === 0
|
|
8347
|
+
if (pushBtn.disabled) {
|
|
8348
|
+
pushBtn.classList.add('fs-status-btn--disabled')
|
|
8349
|
+
} else {
|
|
8350
|
+
pushBtn.classList.remove('fs-status-btn--disabled')
|
|
8351
|
+
}
|
|
8352
|
+
pushBtn.setAttribute('title', 'Git integration is not available')
|
|
8353
|
+
latestGitIntegration = null
|
|
8354
|
+
syncForkButton()
|
|
8355
|
+
return
|
|
8356
|
+
}
|
|
8357
|
+
|
|
8358
|
+
if (!publishMenu) {
|
|
8359
|
+
setLabel('Publish')
|
|
8360
|
+
disableDropdown()
|
|
8361
|
+
} else if (!publishMenu.hasChildNodes()) {
|
|
8362
|
+
setPublishMenuMessage('Loading repositories...')
|
|
8363
|
+
}
|
|
7343
8364
|
|
|
7344
8365
|
try {
|
|
7345
8366
|
const response = await fetch(historyUri)
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
const setLabel = (text) => {
|
|
7349
|
-
if (labelEl) {
|
|
7350
|
-
labelEl.textContent = text
|
|
7351
|
-
}
|
|
8367
|
+
if (!response.ok) {
|
|
8368
|
+
throw new Error(`HTTP ${response.status}`)
|
|
7352
8369
|
}
|
|
7353
|
-
const
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
8370
|
+
const data = await response.json()
|
|
8371
|
+
latestGitIntegration = data
|
|
8372
|
+
|
|
8373
|
+
const isConnected = Boolean(data && data.connected)
|
|
8374
|
+
const emptyMessage = isConnected
|
|
8375
|
+
? 'No Git repositories detected'
|
|
8376
|
+
: 'Connect GitHub to publish this workspace'
|
|
8377
|
+
renderPublishDropdown(repos, { emptyMessage })
|
|
8378
|
+
|
|
8379
|
+
if (!isConnected) {
|
|
8380
|
+
setLabel('Publish')
|
|
8381
|
+
pushBtn.disabled = false
|
|
8382
|
+
pushBtn.classList.remove('fs-status-btn--disabled')
|
|
8383
|
+
pushBtn.setAttribute('title', 'Log in to GitHub to publish this workspace')
|
|
8384
|
+
disableDropdown()
|
|
8385
|
+
pushBtn.addEventListener('click', requireGitLogin)
|
|
8386
|
+
syncForkButton()
|
|
8387
|
+
return
|
|
7357
8388
|
}
|
|
7358
8389
|
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
8390
|
+
if (!Array.isArray(repos) || repos.length === 0) {
|
|
8391
|
+
setLabel('Publish')
|
|
8392
|
+
pushBtn.disabled = true
|
|
8393
|
+
pushBtn.classList.add('fs-status-btn--disabled')
|
|
8394
|
+
pushBtn.setAttribute('title', 'No Git repositories detected')
|
|
8395
|
+
disableDropdown()
|
|
8396
|
+
syncForkButton()
|
|
7365
8397
|
return
|
|
7366
8398
|
}
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
8399
|
+
|
|
8400
|
+
pushBtn.disabled = false
|
|
8401
|
+
pushBtn.classList.remove('fs-status-btn--disabled')
|
|
8402
|
+
|
|
8403
|
+
if (publishTargets.length === 0) {
|
|
7371
8404
|
setLabel('Create')
|
|
7372
|
-
|
|
8405
|
+
pushBtn.setAttribute('title', 'Create a GitHub repository for this project')
|
|
8406
|
+
disableDropdown()
|
|
7373
8407
|
pushBtn.addEventListener('click', showCreateModal)
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
setLabel('Publish')
|
|
7377
|
-
resetHandlers()
|
|
7378
|
-
pushBtn.addEventListener('click', showPublishModal)
|
|
8408
|
+
syncForkButton()
|
|
8409
|
+
return
|
|
7379
8410
|
}
|
|
8411
|
+
|
|
8412
|
+
setLabel('Publish')
|
|
8413
|
+
pushBtn.removeAttribute('title')
|
|
8414
|
+
enableDropdown()
|
|
8415
|
+
syncForkButton()
|
|
7380
8416
|
} catch (error) {
|
|
7381
8417
|
console.error('Error checking remotes:', error)
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
pushBtn.
|
|
7389
|
-
|
|
7390
|
-
|
|
8418
|
+
setPublishMenuMessage('Git integration unavailable')
|
|
8419
|
+
setLabel('Publish')
|
|
8420
|
+
pushBtn.disabled = false
|
|
8421
|
+
pushBtn.classList.remove('fs-status-btn--disabled')
|
|
8422
|
+
pushBtn.setAttribute('title', 'Log in to GitHub to publish this workspace')
|
|
8423
|
+
disableDropdown()
|
|
8424
|
+
pushBtn.addEventListener('click', requireGitLogin)
|
|
8425
|
+
latestGitIntegration = null
|
|
8426
|
+
updateForkButton()
|
|
7391
8427
|
}
|
|
7392
8428
|
}
|
|
7393
8429
|
|