create-interview-cockpit 0.30.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/client/src/components/GithubActionsLabModal.tsx +251 -161
- package/template/client/src/components/LabsPanel.tsx +7 -0
- package/template/client/src/githubActionsLab.ts +14 -0
- package/template/client/tsconfig.tsbuildinfo +1 -1
- package/template/cockpit.json +1 -1
package/package.json
CHANGED
|
@@ -236,18 +236,103 @@ function mapJobStatusToCheck(
|
|
|
236
236
|
return s;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
239
|
+
interface FileTreeFileNode {
|
|
240
|
+
type: "file";
|
|
241
|
+
name: string;
|
|
242
|
+
path: string;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
interface FileTreeFolderNode {
|
|
246
|
+
type: "folder";
|
|
247
|
+
displayName: string;
|
|
248
|
+
path: string;
|
|
249
|
+
children: FileTreeNode[];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
type FileTreeNode = FileTreeFileNode | FileTreeFolderNode;
|
|
253
|
+
|
|
254
|
+
interface MutableFileTreeFolder {
|
|
255
|
+
name: string;
|
|
256
|
+
path: string;
|
|
257
|
+
files: FileTreeFileNode[];
|
|
258
|
+
folders: Map<string, MutableFileTreeFolder>;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function sortFileTreeNodes<T extends { name?: string; displayName?: string }>(
|
|
262
|
+
a: T,
|
|
263
|
+
b: T,
|
|
264
|
+
) {
|
|
265
|
+
return (a.displayName ?? a.name ?? "").localeCompare(
|
|
266
|
+
b.displayName ?? b.name ?? "",
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function getMutableFolderChildren(folder: MutableFileTreeFolder): FileTreeNode[] {
|
|
271
|
+
const files = [...folder.files].sort(sortFileTreeNodes);
|
|
272
|
+
const folders = Array.from(folder.folders.values())
|
|
273
|
+
.map(compactFileTreeFolder)
|
|
274
|
+
.sort(sortFileTreeNodes);
|
|
275
|
+
return [...files, ...folders];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function compactFileTreeFolder(
|
|
279
|
+
folder: MutableFileTreeFolder,
|
|
280
|
+
): FileTreeFolderNode {
|
|
281
|
+
const names = [folder.name];
|
|
282
|
+
let current = folder;
|
|
283
|
+
|
|
284
|
+
while (current.files.length === 0 && current.folders.size === 1) {
|
|
285
|
+
const next = Array.from(current.folders.values())[0];
|
|
286
|
+
names.push(next.name);
|
|
287
|
+
current = next;
|
|
247
288
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
type: "folder",
|
|
292
|
+
displayName: names.join("/"),
|
|
293
|
+
path: current.path,
|
|
294
|
+
children: getMutableFolderChildren(current),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function buildCompactFileTree(paths: string[]): FileTreeNode[] {
|
|
299
|
+
const root: MutableFileTreeFolder = {
|
|
300
|
+
name: "",
|
|
301
|
+
path: "",
|
|
302
|
+
files: [],
|
|
303
|
+
folders: new Map(),
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
for (const filePath of paths) {
|
|
307
|
+
const parts = filePath.split("/").filter(Boolean);
|
|
308
|
+
if (parts.length === 0) continue;
|
|
309
|
+
|
|
310
|
+
let current = root;
|
|
311
|
+
let currentPath = "";
|
|
312
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
313
|
+
const name = parts[i];
|
|
314
|
+
currentPath = currentPath ? `${currentPath}/${name}` : name;
|
|
315
|
+
let next = current.folders.get(name);
|
|
316
|
+
if (!next) {
|
|
317
|
+
next = {
|
|
318
|
+
name,
|
|
319
|
+
path: currentPath,
|
|
320
|
+
files: [],
|
|
321
|
+
folders: new Map(),
|
|
322
|
+
};
|
|
323
|
+
current.folders.set(name, next);
|
|
324
|
+
}
|
|
325
|
+
current = next;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
current.files.push({
|
|
329
|
+
type: "file",
|
|
330
|
+
name: parts[parts.length - 1],
|
|
331
|
+
path: filePath,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return getMutableFolderChildren(root);
|
|
251
336
|
}
|
|
252
337
|
|
|
253
338
|
// ─── Component ───────────────────────────────────────────────────────────
|
|
@@ -514,7 +599,7 @@ export default function GithubActionsLabModal() {
|
|
|
514
599
|
|
|
515
600
|
// ── File operations ───────────────────────────────────────────────
|
|
516
601
|
const fileOrder = useMemo(() => getGhaLabFileOrder(workspace), [workspace]);
|
|
517
|
-
const
|
|
602
|
+
const fileTree = useMemo(() => buildCompactFileTree(fileOrder), [fileOrder]);
|
|
518
603
|
const [collapsedFolders, setCollapsedFolders] = useState<Set<string>>(
|
|
519
604
|
() => new Set(),
|
|
520
605
|
);
|
|
@@ -1706,6 +1791,159 @@ interface ImportMeta {
|
|
|
1706
1791
|
minHeight: MIN_H,
|
|
1707
1792
|
};
|
|
1708
1793
|
|
|
1794
|
+
const renderFileTreeNode = (node: FileTreeNode, depth: number) => {
|
|
1795
|
+
const paddingLeft = 6 + depth * 14;
|
|
1796
|
+
|
|
1797
|
+
if (node.type === "folder") {
|
|
1798
|
+
const collapsed = collapsedFolders.has(node.path);
|
|
1799
|
+
return (
|
|
1800
|
+
<div key={`folder:${node.path || node.displayName}`}>
|
|
1801
|
+
<button
|
|
1802
|
+
onClick={() => toggleFolder(node.path)}
|
|
1803
|
+
onDragOver={(e) => handleFolderDragOver(e, node.path)}
|
|
1804
|
+
onDragLeave={() =>
|
|
1805
|
+
setDragOverFolder((current) =>
|
|
1806
|
+
current === node.path ? null : current,
|
|
1807
|
+
)
|
|
1808
|
+
}
|
|
1809
|
+
onDrop={(e) => handleFolderDrop(e, node.path)}
|
|
1810
|
+
className="flex items-center gap-1 w-full pr-1 py-0.5 text-slate-400 hover:text-slate-200"
|
|
1811
|
+
style={{ paddingLeft }}
|
|
1812
|
+
title="Drop a file here to move it. Hold Option/Alt while dropping to copy."
|
|
1813
|
+
>
|
|
1814
|
+
{collapsed ? (
|
|
1815
|
+
<ChevronRight className="w-3 h-3 shrink-0" />
|
|
1816
|
+
) : (
|
|
1817
|
+
<ChevronDown className="w-3 h-3 shrink-0" />
|
|
1818
|
+
)}
|
|
1819
|
+
<Folder className="w-3 h-3 shrink-0" />
|
|
1820
|
+
<span
|
|
1821
|
+
className={`truncate rounded px-1 ${
|
|
1822
|
+
dragOverFolder === node.path
|
|
1823
|
+
? "bg-amber-500/15 text-amber-200"
|
|
1824
|
+
: ""
|
|
1825
|
+
}`}
|
|
1826
|
+
>
|
|
1827
|
+
{node.displayName}/
|
|
1828
|
+
</span>
|
|
1829
|
+
</button>
|
|
1830
|
+
{!collapsed &&
|
|
1831
|
+
node.children.map((child) =>
|
|
1832
|
+
renderFileTreeNode(child, depth + 1),
|
|
1833
|
+
)}
|
|
1834
|
+
</div>
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
const filePath = node.path;
|
|
1839
|
+
return (
|
|
1840
|
+
<div
|
|
1841
|
+
key={`file:${filePath}`}
|
|
1842
|
+
data-selected={selectedFiles.has(filePath)}
|
|
1843
|
+
draggable
|
|
1844
|
+
onDragStart={(e) => handleFileDragStart(e, filePath)}
|
|
1845
|
+
onDragEnd={() => {
|
|
1846
|
+
setDraggingFile(null);
|
|
1847
|
+
setDragOverFolder(null);
|
|
1848
|
+
}}
|
|
1849
|
+
className={`group relative flex items-center gap-1 pr-1 py-0.5 rounded cursor-pointer ${
|
|
1850
|
+
activeFile === filePath
|
|
1851
|
+
? "bg-amber-500/15 text-amber-200"
|
|
1852
|
+
: selectedFiles.has(filePath)
|
|
1853
|
+
? "bg-sky-500/10 text-sky-100 hover:bg-sky-500/15"
|
|
1854
|
+
: "text-slate-300 hover:bg-slate-800/40"
|
|
1855
|
+
}`}
|
|
1856
|
+
onClick={() => setActiveFile(filePath)}
|
|
1857
|
+
style={{ paddingLeft }}
|
|
1858
|
+
>
|
|
1859
|
+
{(selectMode || selectedFiles.has(filePath)) && (
|
|
1860
|
+
<input
|
|
1861
|
+
type="checkbox"
|
|
1862
|
+
checked={selectedFiles.has(filePath)}
|
|
1863
|
+
onClick={(e) => e.stopPropagation()}
|
|
1864
|
+
onChange={() => toggleFileSelection(filePath)}
|
|
1865
|
+
className="h-3 w-3 shrink-0 accent-amber-400"
|
|
1866
|
+
title="Select file"
|
|
1867
|
+
/>
|
|
1868
|
+
)}
|
|
1869
|
+
<span className="truncate flex-1">{node.name}</span>
|
|
1870
|
+
<button
|
|
1871
|
+
onClick={(e) => {
|
|
1872
|
+
e.stopPropagation();
|
|
1873
|
+
setOpenFileMenu((current) =>
|
|
1874
|
+
current === filePath ? null : filePath,
|
|
1875
|
+
);
|
|
1876
|
+
setBulkMenuOpen(false);
|
|
1877
|
+
}}
|
|
1878
|
+
className="rounded px-1 text-slate-500 opacity-70 hover:bg-slate-800/70 hover:text-amber-200 group-hover:opacity-100"
|
|
1879
|
+
title="File actions"
|
|
1880
|
+
>
|
|
1881
|
+
⋯
|
|
1882
|
+
</button>
|
|
1883
|
+
{openFileMenu === filePath && (
|
|
1884
|
+
<div
|
|
1885
|
+
onClick={(e) => e.stopPropagation()}
|
|
1886
|
+
className="absolute right-1 top-6 z-40 w-40 overflow-hidden rounded-lg border border-slate-700 bg-slate-950 py-1 text-[11px] shadow-xl"
|
|
1887
|
+
>
|
|
1888
|
+
<button
|
|
1889
|
+
onClick={() => moveFile(filePath)}
|
|
1890
|
+
className="flex w-full items-center gap-2 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
1891
|
+
>
|
|
1892
|
+
<Pencil className="w-3 h-3 text-amber-300" />
|
|
1893
|
+
Move / rename…
|
|
1894
|
+
</button>
|
|
1895
|
+
<button
|
|
1896
|
+
onClick={() => copyFile(filePath)}
|
|
1897
|
+
className="flex w-full items-center gap-2 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
1898
|
+
>
|
|
1899
|
+
<Copy className="w-3 h-3 text-sky-300" />
|
|
1900
|
+
Copy to path…
|
|
1901
|
+
</button>
|
|
1902
|
+
<button
|
|
1903
|
+
onClick={() => {
|
|
1904
|
+
toggleFileSelection(filePath);
|
|
1905
|
+
setOpenFileMenu(null);
|
|
1906
|
+
}}
|
|
1907
|
+
className="flex w-full items-center gap-2 border-t border-slate-800 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
1908
|
+
>
|
|
1909
|
+
<ListChecks className="w-3 h-3 text-amber-300" />
|
|
1910
|
+
{selectedFiles.has(filePath) ? "Deselect" : "Select"}
|
|
1911
|
+
</button>
|
|
1912
|
+
{selectedFileList.length > 1 && selectedFiles.has(filePath) && (
|
|
1913
|
+
<>
|
|
1914
|
+
<button
|
|
1915
|
+
onClick={() => {
|
|
1916
|
+
moveFilesToFolder(selectedFileList);
|
|
1917
|
+
setOpenFileMenu(null);
|
|
1918
|
+
}}
|
|
1919
|
+
className="flex w-full items-center gap-2 border-t border-slate-800 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
1920
|
+
>
|
|
1921
|
+
Move selected…
|
|
1922
|
+
</button>
|
|
1923
|
+
<button
|
|
1924
|
+
onClick={() => {
|
|
1925
|
+
copyFilesToFolder(selectedFileList);
|
|
1926
|
+
setOpenFileMenu(null);
|
|
1927
|
+
}}
|
|
1928
|
+
className="flex w-full items-center gap-2 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
1929
|
+
>
|
|
1930
|
+
Copy selected…
|
|
1931
|
+
</button>
|
|
1932
|
+
</>
|
|
1933
|
+
)}
|
|
1934
|
+
<button
|
|
1935
|
+
onClick={() => deleteFile(filePath)}
|
|
1936
|
+
className="flex w-full items-center gap-2 border-t border-slate-800 px-2 py-1.5 text-left text-red-300 hover:bg-red-500/10"
|
|
1937
|
+
>
|
|
1938
|
+
<Trash2 className="w-3 h-3" />
|
|
1939
|
+
Delete
|
|
1940
|
+
</button>
|
|
1941
|
+
</div>
|
|
1942
|
+
)}
|
|
1943
|
+
</div>
|
|
1944
|
+
);
|
|
1945
|
+
};
|
|
1946
|
+
|
|
1709
1947
|
return (
|
|
1710
1948
|
<div className="fixed inset-0 z-40 bg-black/40">
|
|
1711
1949
|
<div
|
|
@@ -2021,155 +2259,7 @@ interface ImportMeta {
|
|
|
2021
2259
|
in workspace root • hold Option/Alt to copy
|
|
2022
2260
|
</div>
|
|
2023
2261
|
)}
|
|
2024
|
-
{
|
|
2025
|
-
const collapsed = collapsedFolders.has(folder);
|
|
2026
|
-
return (
|
|
2027
|
-
<div key={folder || "root"} className="mb-1">
|
|
2028
|
-
{folder && (
|
|
2029
|
-
<button
|
|
2030
|
-
onClick={() => toggleFolder(folder)}
|
|
2031
|
-
onDragOver={(e) => handleFolderDragOver(e, folder)}
|
|
2032
|
-
onDragLeave={() =>
|
|
2033
|
-
setDragOverFolder((current) =>
|
|
2034
|
-
current === folder ? null : current,
|
|
2035
|
-
)
|
|
2036
|
-
}
|
|
2037
|
-
onDrop={(e) => handleFolderDrop(e, folder)}
|
|
2038
|
-
className="flex items-center gap-1 w-full px-1 py-0.5 text-slate-400 hover:text-slate-200"
|
|
2039
|
-
title="Drop a file here to move it. Hold Option/Alt while dropping to copy."
|
|
2040
|
-
>
|
|
2041
|
-
{collapsed ? (
|
|
2042
|
-
<ChevronRight className="w-3 h-3" />
|
|
2043
|
-
) : (
|
|
2044
|
-
<ChevronDown className="w-3 h-3" />
|
|
2045
|
-
)}
|
|
2046
|
-
<Folder className="w-3 h-3" />
|
|
2047
|
-
<span
|
|
2048
|
-
className={`truncate rounded px-1 ${
|
|
2049
|
-
dragOverFolder === folder
|
|
2050
|
-
? "bg-amber-500/15 text-amber-200"
|
|
2051
|
-
: ""
|
|
2052
|
-
}`}
|
|
2053
|
-
>
|
|
2054
|
-
{folder}/
|
|
2055
|
-
</span>
|
|
2056
|
-
</button>
|
|
2057
|
-
)}
|
|
2058
|
-
{!collapsed &&
|
|
2059
|
-
files.map((filePath) => (
|
|
2060
|
-
<div
|
|
2061
|
-
key={filePath}
|
|
2062
|
-
data-selected={selectedFiles.has(filePath)}
|
|
2063
|
-
draggable
|
|
2064
|
-
onDragStart={(e) => handleFileDragStart(e, filePath)}
|
|
2065
|
-
onDragEnd={() => {
|
|
2066
|
-
setDraggingFile(null);
|
|
2067
|
-
setDragOverFolder(null);
|
|
2068
|
-
}}
|
|
2069
|
-
className={`group relative flex items-center gap-1 pl-${folder ? 5 : 1} pr-1 py-0.5 rounded cursor-pointer ${
|
|
2070
|
-
activeFile === filePath
|
|
2071
|
-
? "bg-amber-500/15 text-amber-200"
|
|
2072
|
-
: selectedFiles.has(filePath)
|
|
2073
|
-
? "bg-sky-500/10 text-sky-100 hover:bg-sky-500/15"
|
|
2074
|
-
: "text-slate-300 hover:bg-slate-800/40"
|
|
2075
|
-
}`}
|
|
2076
|
-
onClick={() => setActiveFile(filePath)}
|
|
2077
|
-
style={{ paddingLeft: folder ? 20 : 6 }}
|
|
2078
|
-
>
|
|
2079
|
-
{(selectMode || selectedFiles.has(filePath)) && (
|
|
2080
|
-
<input
|
|
2081
|
-
type="checkbox"
|
|
2082
|
-
checked={selectedFiles.has(filePath)}
|
|
2083
|
-
onClick={(e) => e.stopPropagation()}
|
|
2084
|
-
onChange={() => toggleFileSelection(filePath)}
|
|
2085
|
-
className="h-3 w-3 shrink-0 accent-amber-400"
|
|
2086
|
-
title="Select file"
|
|
2087
|
-
/>
|
|
2088
|
-
)}
|
|
2089
|
-
<span className="truncate flex-1">
|
|
2090
|
-
{baseName(filePath)}
|
|
2091
|
-
</span>
|
|
2092
|
-
<button
|
|
2093
|
-
onClick={(e) => {
|
|
2094
|
-
e.stopPropagation();
|
|
2095
|
-
setOpenFileMenu((current) =>
|
|
2096
|
-
current === filePath ? null : filePath,
|
|
2097
|
-
);
|
|
2098
|
-
setBulkMenuOpen(false);
|
|
2099
|
-
}}
|
|
2100
|
-
className="rounded px-1 text-slate-500 opacity-70 hover:bg-slate-800/70 hover:text-amber-200 group-hover:opacity-100"
|
|
2101
|
-
title="File actions"
|
|
2102
|
-
>
|
|
2103
|
-
⋯
|
|
2104
|
-
</button>
|
|
2105
|
-
{openFileMenu === filePath && (
|
|
2106
|
-
<div
|
|
2107
|
-
onClick={(e) => e.stopPropagation()}
|
|
2108
|
-
className="absolute right-1 top-6 z-40 w-40 overflow-hidden rounded-lg border border-slate-700 bg-slate-950 py-1 text-[11px] shadow-xl"
|
|
2109
|
-
>
|
|
2110
|
-
<button
|
|
2111
|
-
onClick={() => moveFile(filePath)}
|
|
2112
|
-
className="flex w-full items-center gap-2 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
2113
|
-
>
|
|
2114
|
-
<Pencil className="w-3 h-3 text-amber-300" />
|
|
2115
|
-
Move / rename…
|
|
2116
|
-
</button>
|
|
2117
|
-
<button
|
|
2118
|
-
onClick={() => copyFile(filePath)}
|
|
2119
|
-
className="flex w-full items-center gap-2 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
2120
|
-
>
|
|
2121
|
-
<Copy className="w-3 h-3 text-sky-300" />
|
|
2122
|
-
Copy to path…
|
|
2123
|
-
</button>
|
|
2124
|
-
<button
|
|
2125
|
-
onClick={() => {
|
|
2126
|
-
toggleFileSelection(filePath);
|
|
2127
|
-
setOpenFileMenu(null);
|
|
2128
|
-
}}
|
|
2129
|
-
className="flex w-full items-center gap-2 border-t border-slate-800 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
2130
|
-
>
|
|
2131
|
-
<ListChecks className="w-3 h-3 text-amber-300" />
|
|
2132
|
-
{selectedFiles.has(filePath)
|
|
2133
|
-
? "Deselect"
|
|
2134
|
-
: "Select"}
|
|
2135
|
-
</button>
|
|
2136
|
-
{selectedFileList.length > 1 &&
|
|
2137
|
-
selectedFiles.has(filePath) && (
|
|
2138
|
-
<>
|
|
2139
|
-
<button
|
|
2140
|
-
onClick={() => {
|
|
2141
|
-
moveFilesToFolder(selectedFileList);
|
|
2142
|
-
setOpenFileMenu(null);
|
|
2143
|
-
}}
|
|
2144
|
-
className="flex w-full items-center gap-2 border-t border-slate-800 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
2145
|
-
>
|
|
2146
|
-
Move selected…
|
|
2147
|
-
</button>
|
|
2148
|
-
<button
|
|
2149
|
-
onClick={() => {
|
|
2150
|
-
copyFilesToFolder(selectedFileList);
|
|
2151
|
-
setOpenFileMenu(null);
|
|
2152
|
-
}}
|
|
2153
|
-
className="flex w-full items-center gap-2 px-2 py-1.5 text-left text-slate-200 hover:bg-slate-800/70"
|
|
2154
|
-
>
|
|
2155
|
-
Copy selected…
|
|
2156
|
-
</button>
|
|
2157
|
-
</>
|
|
2158
|
-
)}
|
|
2159
|
-
<button
|
|
2160
|
-
onClick={() => deleteFile(filePath)}
|
|
2161
|
-
className="flex w-full items-center gap-2 border-t border-slate-800 px-2 py-1.5 text-left text-red-300 hover:bg-red-500/10"
|
|
2162
|
-
>
|
|
2163
|
-
<Trash2 className="w-3 h-3" />
|
|
2164
|
-
Delete
|
|
2165
|
-
</button>
|
|
2166
|
-
</div>
|
|
2167
|
-
)}
|
|
2168
|
-
</div>
|
|
2169
|
-
))}
|
|
2170
|
-
</div>
|
|
2171
|
-
);
|
|
2172
|
-
})}
|
|
2262
|
+
{fileTree.map((node) => renderFileTreeNode(node, 0))}
|
|
2173
2263
|
</div>
|
|
2174
2264
|
</div>
|
|
2175
2265
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
AWS_GOVERNANCE_GHA_LAB,
|
|
12
12
|
DEFAULT_GHA_LAB,
|
|
13
|
+
EMPTY_GITHUB_GHA_LAB,
|
|
13
14
|
GOVERNANCE_GHA_LAB,
|
|
14
15
|
parseGhaLabWorkspace,
|
|
15
16
|
REACT_VITE_TYPESCRIPT_GHA_LAB,
|
|
@@ -693,6 +694,12 @@ export default function LabsPanel() {
|
|
|
693
694
|
origin="github-actions"
|
|
694
695
|
emptyText="Save a GitHub lab to reopen it here"
|
|
695
696
|
newLabMenu={[
|
|
697
|
+
{
|
|
698
|
+
label: "Empty GitHub Template",
|
|
699
|
+
description:
|
|
700
|
+
"Minimal repo with blank .github/workflows/ci.yml and CODEOWNERS files",
|
|
701
|
+
onClick: () => openGhaLab(EMPTY_GITHUB_GHA_LAB),
|
|
702
|
+
},
|
|
696
703
|
{
|
|
697
704
|
label: "React Vite TypeScript Starter",
|
|
698
705
|
description:
|
|
@@ -513,6 +513,11 @@ li {
|
|
|
513
513
|
`,
|
|
514
514
|
};
|
|
515
515
|
|
|
516
|
+
const EMPTY_GITHUB_LAB_FILES: Record<string, string> = {
|
|
517
|
+
".github/workflows/ci.yml": "",
|
|
518
|
+
".github/CODEOWNERS": "",
|
|
519
|
+
};
|
|
520
|
+
|
|
516
521
|
// ─── Platform Governance Template ────────────────────────────────────────
|
|
517
522
|
//
|
|
518
523
|
// Mirrors a real-world "PLF-governance" mono-repo: one repo that owns
|
|
@@ -2435,6 +2440,15 @@ export const REACT_VITE_TYPESCRIPT_GHA_LAB: GithubActionsLabWorkspace = {
|
|
|
2435
2440
|
files: REACT_VITE_TYPESCRIPT_FILES,
|
|
2436
2441
|
};
|
|
2437
2442
|
|
|
2443
|
+
export const EMPTY_GITHUB_GHA_LAB: GithubActionsLabWorkspace = {
|
|
2444
|
+
version: 1,
|
|
2445
|
+
label: "Empty GitHub Lab Template",
|
|
2446
|
+
activeFile: ".github/workflows/ci.yml",
|
|
2447
|
+
defaultEvent: "push",
|
|
2448
|
+
defaultWorkflow: ".github/workflows/ci.yml",
|
|
2449
|
+
files: EMPTY_GITHUB_LAB_FILES,
|
|
2450
|
+
};
|
|
2451
|
+
|
|
2438
2452
|
// ─── Helpers (mirror infraLab.ts API surface) ────────────────────────────
|
|
2439
2453
|
|
|
2440
2454
|
function cloneGhaLabEnvironment(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/app.tsx","./src/api.ts","./src/browsersecuritytemplates.ts","./src/enterpriselocallab.ts","./src/ghaconcurrency.ts","./src/githubactionslab.ts","./src/infralab.ts","./src/main.tsx","./src/reactlab.ts","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/aisettingsmodal.tsx","./src/components/annotationdialog.tsx","./src/components/browsersecuritylabmodal.tsx","./src/components/canvaslabmodal.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/deploymentlabmodal.tsx","./src/components/diagramsmodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/ghaconcurrencypanel.tsx","./src/components/ghahistorypanel.tsx","./src/components/ghajobspanel.tsx","./src/components/gitdiffpanel.tsx","./src/components/gitdiffviewermodal.tsx","./src/components/githubactionslabmodal.tsx","./src/components/infralabmodal.tsx","./src/components/labspanel.tsx","./src/components/linkedconvospicker.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.tsx","./src/components/notesmodal.tsx","./src/components/plotembed.tsx","./src/components/sidebar.tsx","./src/components/textannotator.tsx","./src/components/vizcraftembed.tsx","./src/components/workspaceswitcher.tsx"],"version":"5.9.3"}
|
|
1
|
+
{"root":["./src/app.tsx","./src/api.ts","./src/awsgovernanceiamlab.ts","./src/browsersecuritytemplates.ts","./src/codeowners.ts","./src/enterpriselocallab.ts","./src/ghaconcurrency.ts","./src/githubactionslab.ts","./src/infralab.ts","./src/main.tsx","./src/reactlab.ts","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/aisettingsmodal.tsx","./src/components/annotationdialog.tsx","./src/components/browsersecuritylabmodal.tsx","./src/components/canvaslabmodal.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/deploymentlabmodal.tsx","./src/components/diagramsmodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/ghaconcurrencypanel.tsx","./src/components/ghahistorypanel.tsx","./src/components/ghajobspanel.tsx","./src/components/gitdiffpanel.tsx","./src/components/gitdiffviewermodal.tsx","./src/components/githubactionslabmodal.tsx","./src/components/infralabmodal.tsx","./src/components/labspanel.tsx","./src/components/linkedconvospicker.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.tsx","./src/components/notesmodal.tsx","./src/components/plotembed.tsx","./src/components/pullrequestpanel.tsx","./src/components/settingspanel.tsx","./src/components/sidebar.tsx","./src/components/textannotator.tsx","./src/components/vizcraftembed.tsx","./src/components/workspaceswitcher.tsx"],"version":"5.9.3"}
|
package/template/cockpit.json
CHANGED