neural-loom 0.2.57 → 0.2.58
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/src/app/api/git/route.ts +79 -0
- package/src/app/components/IdeLayout.tsx +328 -0
package/package.json
CHANGED
package/src/app/api/git/route.ts
CHANGED
|
@@ -96,6 +96,57 @@ export async function GET(request: Request) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
if (action === "branches") {
|
|
100
|
+
const { stdout } = await execPromise("git branch --format=\"%(refname:short)|%(HEAD)\"", { cwd: targetRoot });
|
|
101
|
+
const branches = stdout.split(/\r?\n/).filter(Boolean).map(line => {
|
|
102
|
+
const [name, active] = line.split("|");
|
|
103
|
+
return {
|
|
104
|
+
name: name.trim(),
|
|
105
|
+
isActive: active === "*"
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
return NextResponse.json({ success: true, branches });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (action === "log") {
|
|
112
|
+
try {
|
|
113
|
+
const { stdout } = await execPromise("git log --graph --abbrev-commit --decorate --format=format:\"%h||%p||%s||%an||%ar\" --all -n 100", { cwd: targetRoot });
|
|
114
|
+
const lines = stdout.split(/\r?\n/);
|
|
115
|
+
const logItems = lines.map(line => {
|
|
116
|
+
const parts = line.split("||");
|
|
117
|
+
if (parts.length >= 5) {
|
|
118
|
+
const firstPart = parts[0];
|
|
119
|
+
const hashMatch = firstPart.match(/([0-9a-f]{7,40})$/);
|
|
120
|
+
const hash = hashMatch ? hashMatch[1] : "";
|
|
121
|
+
const graph = firstPart.substring(0, firstPart.length - hash.length);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
isCommit: true,
|
|
125
|
+
graph,
|
|
126
|
+
hash,
|
|
127
|
+
parents: parts[1].trim().split(/\s+/).filter(Boolean),
|
|
128
|
+
subject: parts[2],
|
|
129
|
+
author: parts[3],
|
|
130
|
+
date: parts[4]
|
|
131
|
+
};
|
|
132
|
+
} else {
|
|
133
|
+
return {
|
|
134
|
+
isCommit: false,
|
|
135
|
+
graph: line,
|
|
136
|
+
hash: "",
|
|
137
|
+
parents: [],
|
|
138
|
+
subject: "",
|
|
139
|
+
author: "",
|
|
140
|
+
date: ""
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return NextResponse.json({ success: true, log: logItems });
|
|
145
|
+
} catch (err) {
|
|
146
|
+
return NextResponse.json({ success: true, log: [] });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
99
150
|
return NextResponse.json({ error: "Invalid action" }, { status: 400 });
|
|
100
151
|
} catch (error) {
|
|
101
152
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -189,6 +240,34 @@ export async function POST(request: Request) {
|
|
|
189
240
|
return NextResponse.json({ success: true, output: stdout + stderr });
|
|
190
241
|
}
|
|
191
242
|
|
|
243
|
+
if (action === "checkout") {
|
|
244
|
+
const { branchName } = body;
|
|
245
|
+
if (!branchName) {
|
|
246
|
+
return NextResponse.json({ error: "Missing branchName parameter" }, { status: 400 });
|
|
247
|
+
}
|
|
248
|
+
const { stdout, stderr } = await execPromise(`git checkout ${JSON.stringify(branchName)}`, { cwd: targetRoot });
|
|
249
|
+
return NextResponse.json({ success: true, output: stdout + stderr });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (action === "create_branch") {
|
|
253
|
+
const { branchName } = body;
|
|
254
|
+
if (!branchName) {
|
|
255
|
+
return NextResponse.json({ error: "Missing branchName parameter" }, { status: 400 });
|
|
256
|
+
}
|
|
257
|
+
const { stdout, stderr } = await execPromise(`git checkout -b ${JSON.stringify(branchName)}`, { cwd: targetRoot });
|
|
258
|
+
return NextResponse.json({ success: true, output: stdout + stderr });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (action === "delete_branch") {
|
|
262
|
+
const { branchName, force } = body;
|
|
263
|
+
if (!branchName) {
|
|
264
|
+
return NextResponse.json({ error: "Missing branchName parameter" }, { status: 400 });
|
|
265
|
+
}
|
|
266
|
+
const flag = force ? "-D" : "-d";
|
|
267
|
+
const { stdout, stderr } = await execPromise(`git branch ${flag} ${JSON.stringify(branchName)}`, { cwd: targetRoot });
|
|
268
|
+
return NextResponse.json({ success: true, output: stdout + stderr });
|
|
269
|
+
}
|
|
270
|
+
|
|
192
271
|
return NextResponse.json({ error: "Invalid action" }, { status: 400 });
|
|
193
272
|
} catch (error) {
|
|
194
273
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -115,6 +115,16 @@ interface GitFile {
|
|
|
115
115
|
worktreeStatus?: string;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
interface GitLogItem {
|
|
119
|
+
isCommit: boolean;
|
|
120
|
+
graph: string;
|
|
121
|
+
hash: string;
|
|
122
|
+
parents: string[];
|
|
123
|
+
subject: string;
|
|
124
|
+
author: string;
|
|
125
|
+
date: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
118
128
|
interface SessionStatus {
|
|
119
129
|
type?: string;
|
|
120
130
|
status?: string;
|
|
@@ -310,6 +320,12 @@ export default function IdeLayout({ sessionId, workspaceRoot, scopedDirs, onClos
|
|
|
310
320
|
const [gitBranch, setGitBranch] = useState("");
|
|
311
321
|
const [gitSync, setGitSync] = useState({ ahead: 0, behind: 0 });
|
|
312
322
|
const [commitMessage, setCommitMessage] = useState("");
|
|
323
|
+
const [gitBranches, setGitBranches] = useState<{ name: string; isActive: boolean }[]>([]);
|
|
324
|
+
const [gitLog, setGitLog] = useState<GitLogItem[]>([]);
|
|
325
|
+
const [isBranchesExpanded, setIsBranchesExpanded] = useState(false);
|
|
326
|
+
const [isHistoryExpanded, setIsHistoryExpanded] = useState(false);
|
|
327
|
+
const [newBranchName, setNewBranchName] = useState("");
|
|
328
|
+
const [branchActionLoading, setBranchActionLoading] = useState(false);
|
|
313
329
|
|
|
314
330
|
// Status & Telemetry
|
|
315
331
|
const [sessionStatus, setSessionStatus] = useState<SessionStatus | null>(null);
|
|
@@ -340,6 +356,34 @@ export default function IdeLayout({ sessionId, workspaceRoot, scopedDirs, onClos
|
|
|
340
356
|
setGitFiles(data.files || []);
|
|
341
357
|
setGitBranch(data.branch || "");
|
|
342
358
|
setGitSync({ ahead: data.ahead || 0, behind: data.behind || 0 });
|
|
359
|
+
|
|
360
|
+
if (data.isGit) {
|
|
361
|
+
// Fetch branches
|
|
362
|
+
try {
|
|
363
|
+
const branchesRes = await fetch(`/api/git?action=branches&root=${encodeURIComponent(workspaceRoot)}`);
|
|
364
|
+
if (branchesRes.ok) {
|
|
365
|
+
const branchesData = await branchesRes.json();
|
|
366
|
+
if (branchesData.success) {
|
|
367
|
+
setGitBranches(branchesData.branches || []);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
} catch (err) {
|
|
371
|
+
console.error("Failed to load git branches:", err);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Fetch log
|
|
375
|
+
try {
|
|
376
|
+
const logRes = await fetch(`/api/git?action=log&root=${encodeURIComponent(workspaceRoot)}`);
|
|
377
|
+
if (logRes.ok) {
|
|
378
|
+
const logData = await logRes.json();
|
|
379
|
+
if (logData.success) {
|
|
380
|
+
setGitLog(logData.log || []);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
} catch (err) {
|
|
384
|
+
console.error("Failed to load git log:", err);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
343
387
|
}
|
|
344
388
|
}
|
|
345
389
|
} catch (e) {
|
|
@@ -510,6 +554,102 @@ export default function IdeLayout({ sessionId, workspaceRoot, scopedDirs, onClos
|
|
|
510
554
|
}
|
|
511
555
|
};
|
|
512
556
|
|
|
557
|
+
const handleGitCheckout = async (branchName: string) => {
|
|
558
|
+
setBranchActionLoading(true);
|
|
559
|
+
try {
|
|
560
|
+
const res = await fetch("/api/git", {
|
|
561
|
+
method: "POST",
|
|
562
|
+
headers: { "Content-Type": "application/json" },
|
|
563
|
+
body: JSON.stringify({
|
|
564
|
+
action: "checkout",
|
|
565
|
+
branchName,
|
|
566
|
+
rootPath: workspaceRoot,
|
|
567
|
+
}),
|
|
568
|
+
});
|
|
569
|
+
const data = await res.json();
|
|
570
|
+
if (res.ok && data.success) {
|
|
571
|
+
await loadGitStatus();
|
|
572
|
+
} else {
|
|
573
|
+
alert(data.error || `Failed to checkout branch ${branchName}`);
|
|
574
|
+
}
|
|
575
|
+
} catch (e) {
|
|
576
|
+
console.error(e);
|
|
577
|
+
alert("Failed to checkout branch due to a network error.");
|
|
578
|
+
} finally {
|
|
579
|
+
setBranchActionLoading(false);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const handleGitCreateBranch = async (e?: React.FormEvent) => {
|
|
584
|
+
if (e) e.preventDefault();
|
|
585
|
+
if (!newBranchName.trim()) return;
|
|
586
|
+
setBranchActionLoading(true);
|
|
587
|
+
try {
|
|
588
|
+
const res = await fetch("/api/git", {
|
|
589
|
+
method: "POST",
|
|
590
|
+
headers: { "Content-Type": "application/json" },
|
|
591
|
+
body: JSON.stringify({
|
|
592
|
+
action: "create_branch",
|
|
593
|
+
branchName: newBranchName.trim(),
|
|
594
|
+
rootPath: workspaceRoot,
|
|
595
|
+
}),
|
|
596
|
+
});
|
|
597
|
+
const data = await res.json();
|
|
598
|
+
if (res.ok && data.success) {
|
|
599
|
+
setNewBranchName("");
|
|
600
|
+
await loadGitStatus();
|
|
601
|
+
} else {
|
|
602
|
+
alert(data.error || `Failed to create branch ${newBranchName}`);
|
|
603
|
+
}
|
|
604
|
+
} catch (e) {
|
|
605
|
+
console.error(e);
|
|
606
|
+
alert("Failed to create branch due to a network error.");
|
|
607
|
+
} finally {
|
|
608
|
+
setBranchActionLoading(false);
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
const handleGitDeleteBranch = async (branchName: string, force = false) => {
|
|
613
|
+
if (branchName === gitBranch) {
|
|
614
|
+
alert("Cannot delete the currently active branch.");
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
const message = force
|
|
618
|
+
? `Are you sure you want to FORCE delete the branch "${branchName}"? All unmerged changes will be lost.`
|
|
619
|
+
: `Are you sure you want to delete the branch "${branchName}"?`;
|
|
620
|
+
if (!confirm(message)) return;
|
|
621
|
+
|
|
622
|
+
setBranchActionLoading(true);
|
|
623
|
+
try {
|
|
624
|
+
const res = await fetch("/api/git", {
|
|
625
|
+
method: "POST",
|
|
626
|
+
headers: { "Content-Type": "application/json" },
|
|
627
|
+
body: JSON.stringify({
|
|
628
|
+
action: "delete_branch",
|
|
629
|
+
branchName,
|
|
630
|
+
force,
|
|
631
|
+
rootPath: workspaceRoot,
|
|
632
|
+
}),
|
|
633
|
+
});
|
|
634
|
+
const data = await res.json();
|
|
635
|
+
if (res.ok && data.success) {
|
|
636
|
+
await loadGitStatus();
|
|
637
|
+
} else {
|
|
638
|
+
// If it failed because it's not merged, offer force delete
|
|
639
|
+
if (!force && data.error && (data.error.includes("not fully merged") || data.error.includes("not merged"))) {
|
|
640
|
+
handleGitDeleteBranch(branchName, true);
|
|
641
|
+
} else {
|
|
642
|
+
alert(data.error || `Failed to delete branch ${branchName}`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
} catch (e) {
|
|
646
|
+
console.error(e);
|
|
647
|
+
alert("Failed to delete branch due to a network error.");
|
|
648
|
+
} finally {
|
|
649
|
+
setBranchActionLoading(false);
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
513
653
|
const handleSelectGitFile = async (file: GitFile) => {
|
|
514
654
|
try {
|
|
515
655
|
const res = await fetch(`/api/git?action=diff&file=${encodeURIComponent(file.relativePath)}&root=${encodeURIComponent(workspaceRoot)}`);
|
|
@@ -1390,6 +1530,194 @@ export default function IdeLayout({ sessionId, workspaceRoot, scopedDirs, onClos
|
|
|
1390
1530
|
</div>
|
|
1391
1531
|
)}
|
|
1392
1532
|
</div>
|
|
1533
|
+
|
|
1534
|
+
{/* Branches Section */}
|
|
1535
|
+
<div style={{ borderTop: "1px solid var(--border)", paddingTop: "0.75rem" }}>
|
|
1536
|
+
<div
|
|
1537
|
+
onClick={() => setIsBranchesExpanded(!isBranchesExpanded)}
|
|
1538
|
+
style={{
|
|
1539
|
+
display: "flex",
|
|
1540
|
+
justifyContent: "space-between",
|
|
1541
|
+
alignItems: "center",
|
|
1542
|
+
cursor: "pointer",
|
|
1543
|
+
marginBottom: "0.5rem",
|
|
1544
|
+
userSelect: "none"
|
|
1545
|
+
}}
|
|
1546
|
+
>
|
|
1547
|
+
<span style={{ fontSize: "0.7rem", fontWeight: 700, textTransform: "uppercase", color: "var(--muted)", display: "flex", alignItems: "center", gap: "0.25rem" }}>
|
|
1548
|
+
{isBranchesExpanded ? "▼" : "▶"} Branches ({gitBranches.length})
|
|
1549
|
+
</span>
|
|
1550
|
+
</div>
|
|
1551
|
+
|
|
1552
|
+
{isBranchesExpanded && (
|
|
1553
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
|
|
1554
|
+
{/* Create Branch Input */}
|
|
1555
|
+
<form onSubmit={handleGitCreateBranch} style={{ display: "flex", gap: "0.25rem" }}>
|
|
1556
|
+
<input
|
|
1557
|
+
type="text"
|
|
1558
|
+
placeholder="New branch name..."
|
|
1559
|
+
value={newBranchName}
|
|
1560
|
+
onChange={(e) => setNewBranchName(e.target.value)}
|
|
1561
|
+
disabled={branchActionLoading}
|
|
1562
|
+
style={{
|
|
1563
|
+
flex: 1,
|
|
1564
|
+
backgroundColor: "#05070a",
|
|
1565
|
+
border: "1px solid var(--border)",
|
|
1566
|
+
borderRadius: "4px",
|
|
1567
|
+
padding: "0.25rem 0.5rem",
|
|
1568
|
+
color: "#ffffff",
|
|
1569
|
+
fontSize: "0.7rem",
|
|
1570
|
+
outline: "none"
|
|
1571
|
+
}}
|
|
1572
|
+
/>
|
|
1573
|
+
<button
|
|
1574
|
+
type="submit"
|
|
1575
|
+
disabled={branchActionLoading || !newBranchName.trim()}
|
|
1576
|
+
style={{
|
|
1577
|
+
backgroundColor: "rgba(59, 130, 246, 0.15)",
|
|
1578
|
+
border: "1px solid #3b82f6",
|
|
1579
|
+
color: "#3b82f6",
|
|
1580
|
+
padding: "0.25rem 0.5rem",
|
|
1581
|
+
borderRadius: "4px",
|
|
1582
|
+
fontSize: "0.7rem",
|
|
1583
|
+
fontWeight: 600,
|
|
1584
|
+
cursor: "pointer",
|
|
1585
|
+
opacity: (!newBranchName.trim() || branchActionLoading) ? 0.5 : 1
|
|
1586
|
+
}}
|
|
1587
|
+
>
|
|
1588
|
+
+ Create
|
|
1589
|
+
</button>
|
|
1590
|
+
</form>
|
|
1591
|
+
|
|
1592
|
+
{/* Branch List */}
|
|
1593
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.25rem", maxHeight: "150px", overflowY: "auto" }}>
|
|
1594
|
+
{gitBranches.map((br) => (
|
|
1595
|
+
<div
|
|
1596
|
+
key={br.name}
|
|
1597
|
+
style={{
|
|
1598
|
+
display: "flex",
|
|
1599
|
+
alignItems: "center",
|
|
1600
|
+
justifyContent: "space-between",
|
|
1601
|
+
padding: "0.35rem 0.5rem",
|
|
1602
|
+
borderRadius: "4px",
|
|
1603
|
+
backgroundColor: br.isActive ? "rgba(59, 130, 246, 0.08)" : "transparent",
|
|
1604
|
+
border: br.isActive ? "1px solid rgba(59, 130, 246, 0.2)" : "1px solid transparent",
|
|
1605
|
+
fontSize: "0.75rem",
|
|
1606
|
+
transition: "all 0.15s ease",
|
|
1607
|
+
}}
|
|
1608
|
+
>
|
|
1609
|
+
<div style={{ display: "flex", alignItems: "center", gap: "0.35rem", fontWeight: br.isActive ? 700 : 500, color: br.isActive ? "var(--primary)" : "#cbd5e1", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1 }}>
|
|
1610
|
+
<span style={{ fontSize: "0.7rem" }}>{br.isActive ? "●" : "○"}</span>
|
|
1611
|
+
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={br.name}>{br.name}</span>
|
|
1612
|
+
</div>
|
|
1613
|
+
<div style={{ display: "flex", gap: "0.25rem" }}>
|
|
1614
|
+
{!br.isActive && (
|
|
1615
|
+
<>
|
|
1616
|
+
<button
|
|
1617
|
+
type="button"
|
|
1618
|
+
onClick={() => handleGitCheckout(br.name)}
|
|
1619
|
+
disabled={branchActionLoading}
|
|
1620
|
+
title={`Switch to ${br.name}`}
|
|
1621
|
+
style={{
|
|
1622
|
+
background: "none",
|
|
1623
|
+
border: "none",
|
|
1624
|
+
cursor: "pointer",
|
|
1625
|
+
fontSize: "0.7rem",
|
|
1626
|
+
padding: "2px 4px",
|
|
1627
|
+
color: "var(--muted)"
|
|
1628
|
+
}}
|
|
1629
|
+
>
|
|
1630
|
+
➔
|
|
1631
|
+
</button>
|
|
1632
|
+
<button
|
|
1633
|
+
type="button"
|
|
1634
|
+
onClick={() => handleGitDeleteBranch(br.name)}
|
|
1635
|
+
disabled={branchActionLoading}
|
|
1636
|
+
title={`Delete ${br.name}`}
|
|
1637
|
+
style={{
|
|
1638
|
+
background: "none",
|
|
1639
|
+
border: "none",
|
|
1640
|
+
cursor: "pointer",
|
|
1641
|
+
fontSize: "0.7rem",
|
|
1642
|
+
padding: "2px 4px",
|
|
1643
|
+
color: "var(--muted)"
|
|
1644
|
+
}}
|
|
1645
|
+
>
|
|
1646
|
+
✖
|
|
1647
|
+
</button>
|
|
1648
|
+
</>
|
|
1649
|
+
)}
|
|
1650
|
+
</div>
|
|
1651
|
+
</div>
|
|
1652
|
+
))}
|
|
1653
|
+
</div>
|
|
1654
|
+
</div>
|
|
1655
|
+
)}
|
|
1656
|
+
</div>
|
|
1657
|
+
|
|
1658
|
+
{/* Commit History Section */}
|
|
1659
|
+
<div style={{ borderTop: "1px solid var(--border)", paddingTop: "0.75rem" }}>
|
|
1660
|
+
<div
|
|
1661
|
+
onClick={() => setIsHistoryExpanded(!isHistoryExpanded)}
|
|
1662
|
+
style={{
|
|
1663
|
+
display: "flex",
|
|
1664
|
+
justifyContent: "space-between",
|
|
1665
|
+
alignItems: "center",
|
|
1666
|
+
cursor: "pointer",
|
|
1667
|
+
marginBottom: "0.5rem",
|
|
1668
|
+
userSelect: "none"
|
|
1669
|
+
}}
|
|
1670
|
+
>
|
|
1671
|
+
<span style={{ fontSize: "0.7rem", fontWeight: 700, textTransform: "uppercase", color: "var(--muted)", display: "flex", alignItems: "center", gap: "0.25rem" }}>
|
|
1672
|
+
{isHistoryExpanded ? "▼" : "▶"} Commit History
|
|
1673
|
+
</span>
|
|
1674
|
+
</div>
|
|
1675
|
+
|
|
1676
|
+
{isHistoryExpanded && (
|
|
1677
|
+
<div style={{
|
|
1678
|
+
backgroundColor: "#05070a",
|
|
1679
|
+
border: "1px solid var(--border)",
|
|
1680
|
+
borderRadius: "6px",
|
|
1681
|
+
padding: "0.5rem",
|
|
1682
|
+
maxHeight: "220px",
|
|
1683
|
+
overflowY: "auto",
|
|
1684
|
+
overflowX: "auto"
|
|
1685
|
+
}}>
|
|
1686
|
+
{gitLog.length === 0 ? (
|
|
1687
|
+
<div style={{ color: "var(--muted)", fontStyle: "italic", fontSize: "0.7rem", textAlign: "center", padding: "0.5rem" }}>
|
|
1688
|
+
No commit history found.
|
|
1689
|
+
</div>
|
|
1690
|
+
) : (
|
|
1691
|
+
<div style={{ fontFamily: "var(--font-mono)", fontSize: "0.65rem", lineHeight: "1.4", whiteSpace: "pre" }}>
|
|
1692
|
+
{gitLog.map((item, idx) => (
|
|
1693
|
+
<div
|
|
1694
|
+
key={idx}
|
|
1695
|
+
style={{
|
|
1696
|
+
display: "flex",
|
|
1697
|
+
padding: "2px 0",
|
|
1698
|
+
borderBottom: "1px solid rgba(255,255,255,0.01)",
|
|
1699
|
+
alignItems: "center"
|
|
1700
|
+
}}
|
|
1701
|
+
>
|
|
1702
|
+
<span style={{ color: "#3b82f6", flexShrink: 0, fontWeight: 700 }}>{item.graph}</span>
|
|
1703
|
+
{item.isCommit && (
|
|
1704
|
+
<>
|
|
1705
|
+
<span style={{ color: "#eab308", marginRight: "6px", flexShrink: 0 }} title="Commit Hash">{item.hash}</span>
|
|
1706
|
+
<span style={{ flex: 1, textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap", color: "#cbd5e1" }} title={item.subject}>
|
|
1707
|
+
{item.subject}
|
|
1708
|
+
</span>
|
|
1709
|
+
<span style={{ color: "var(--muted)", fontSize: "0.55rem", marginLeft: "6px", flexShrink: 0 }} title={`${item.author} (${item.date})`}>
|
|
1710
|
+
{item.date}
|
|
1711
|
+
</span>
|
|
1712
|
+
</>
|
|
1713
|
+
)}
|
|
1714
|
+
</div>
|
|
1715
|
+
))}
|
|
1716
|
+
</div>
|
|
1717
|
+
)}
|
|
1718
|
+
</div>
|
|
1719
|
+
)}
|
|
1720
|
+
</div>
|
|
1393
1721
|
</div>
|
|
1394
1722
|
) : (
|
|
1395
1723
|
<div style={{ padding: "1rem", color: "var(--muted)", fontSize: "0.75rem", fontStyle: "italic" }}>
|