gcsdk 1.0.7 → 1.0.9
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/dist/index.d.ts +19 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -9
- package/dist/lib/utils.d.ts +25 -10
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +283 -28
- package/dist/react/ConversationGraph.d.ts +7 -4
- package/dist/react/ConversationGraph.d.ts.map +1 -1
- package/dist/react/ConversationGraph.js +451 -45
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/native/index.node +0 -0
- package/native/package.json +2 -2
- package/native/src/lib.rs +222 -18
- package/package.json +8 -8
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
export interface HistoryNode {
|
|
6
|
-
id: string;
|
|
7
|
-
parentId: string | null;
|
|
8
|
-
role: "user" | "assistant" | "system";
|
|
9
|
-
content: string;
|
|
10
|
-
branchName: string;
|
|
11
|
-
}
|
|
12
|
-
export declare class GitChat {
|
|
1
|
+
import type { Message, HistoryNode, IGitChat } from "./types";
|
|
2
|
+
export type { Message, HistoryNode, IGitChat, TreeNode } from "./types";
|
|
3
|
+
export declare class GitChat implements IGitChat {
|
|
13
4
|
private currentBranch;
|
|
14
5
|
private lastCommitId;
|
|
15
6
|
reset(): void;
|
|
@@ -18,7 +9,22 @@ export declare class GitChat {
|
|
|
18
9
|
createBranch(branchName: string, baseBranchName: string): void;
|
|
19
10
|
getHistory(): HistoryNode[];
|
|
20
11
|
checkout(branchName: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Create a new branch pointing to a specific commit ID.
|
|
14
|
+
* Returns true if successful, false if commit doesn't exist.
|
|
15
|
+
*/
|
|
16
|
+
createBranchFromCommit(branchName: string, commitId: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Get the message history from root to a specific commit.
|
|
19
|
+
* Returns oldest-first array.
|
|
20
|
+
*/
|
|
21
|
+
getHistoryToCommit(commitId: string): HistoryNode[];
|
|
22
|
+
/**
|
|
23
|
+
* Create a branch from a specific commit and switch to it.
|
|
24
|
+
* Convenience method combining createBranchFromCommit + state update.
|
|
25
|
+
*/
|
|
26
|
+
branchFromCommit(branchName: string, commitId: string): boolean;
|
|
27
|
+
returnTreeAsJson(): Promise<string>;
|
|
21
28
|
renderTree(): Promise<any>;
|
|
22
29
|
}
|
|
23
|
-
export {};
|
|
24
30
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAY,MAAM,SAAS,CAAC;AACxE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAqBxE,qBAAa,OAAQ,YAAW,QAAQ;IACtC,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,YAAY,CAAuB;IAE3C,KAAK;IAMC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAY5D,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;IAMhD,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;IAKvD,UAAU,IAAI,WAAW,EAAE;IAI3B,QAAQ,CAAC,UAAU,EAAE,MAAM;IAM3B;;;OAGG;IACH,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIrE;;;OAGG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE;IAInD;;;OAGG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IASzD,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAInC,UAAU;CAGjB"}
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
1
|
+
import { createRequire } from "module";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
6
8
|
// Resolve the native module path
|
|
7
9
|
// When compiled, __dirname will be the dist/ directory
|
|
8
10
|
// So we need to go up one level to find native/
|
|
9
|
-
const nativeModulePath =
|
|
10
|
-
if (!
|
|
11
|
+
const nativeModulePath = join(__dirname, "../native/index.node");
|
|
12
|
+
if (!existsSync(nativeModulePath)) {
|
|
11
13
|
throw new Error(`Native module not found at ${nativeModulePath}. ` +
|
|
12
14
|
`This package requires a pre-built native module. ` +
|
|
13
15
|
`Please ensure you're using the published version from npm.`);
|
|
14
16
|
}
|
|
15
17
|
const native = require(nativeModulePath);
|
|
16
|
-
class GitChat {
|
|
18
|
+
export class GitChat {
|
|
17
19
|
constructor() {
|
|
18
20
|
this.currentBranch = "main";
|
|
19
21
|
this.lastCommitId = null;
|
|
@@ -50,8 +52,36 @@ class GitChat {
|
|
|
50
52
|
this.currentBranch = branchName;
|
|
51
53
|
this.lastCommitId = headId ?? null;
|
|
52
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a new branch pointing to a specific commit ID.
|
|
57
|
+
* Returns true if successful, false if commit doesn't exist.
|
|
58
|
+
*/
|
|
59
|
+
createBranchFromCommit(branchName, commitId) {
|
|
60
|
+
return native.createBranchFromCommit(branchName, commitId);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the message history from root to a specific commit.
|
|
64
|
+
* Returns oldest-first array.
|
|
65
|
+
*/
|
|
66
|
+
getHistoryToCommit(commitId) {
|
|
67
|
+
return native.getHistoryToCommit(commitId);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a branch from a specific commit and switch to it.
|
|
71
|
+
* Convenience method combining createBranchFromCommit + state update.
|
|
72
|
+
*/
|
|
73
|
+
branchFromCommit(branchName, commitId) {
|
|
74
|
+
const success = this.createBranchFromCommit(branchName, commitId);
|
|
75
|
+
if (success) {
|
|
76
|
+
this.currentBranch = branchName;
|
|
77
|
+
this.lastCommitId = commitId;
|
|
78
|
+
}
|
|
79
|
+
return success;
|
|
80
|
+
}
|
|
81
|
+
async returnTreeAsJson() {
|
|
82
|
+
return await native.returnTreeAsJson();
|
|
83
|
+
}
|
|
53
84
|
async renderTree() {
|
|
54
85
|
return await native.renderTree();
|
|
55
86
|
}
|
|
56
87
|
}
|
|
57
|
-
exports.GitChat = GitChat;
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -1,29 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
1
|
+
import type { HistoryNode, TreeNode } from "../types";
|
|
2
|
+
export interface LayoutNode extends HistoryNode {
|
|
3
|
+
lane: number;
|
|
4
|
+
row: number;
|
|
5
|
+
color: string;
|
|
6
|
+
isHead: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function flattenTree(tree: TreeNode[]): HistoryNode[];
|
|
9
|
+
export declare const BRANCH_COLORS: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Assigns lanes (horizontal positions) and rows (vertical positions) to nodes
|
|
12
|
+
* for canvas-based git graph rendering.
|
|
13
|
+
*/
|
|
14
|
+
export declare function assignLanes(history: HistoryNode[]): LayoutNode[];
|
|
15
|
+
export declare const transformToFlowData: (history: HistoryNode[]) => {
|
|
3
16
|
flowNodes: {
|
|
4
17
|
id: string;
|
|
18
|
+
type: string;
|
|
5
19
|
data: {
|
|
6
|
-
|
|
20
|
+
role: string;
|
|
21
|
+
content: string;
|
|
22
|
+
branchColor: string;
|
|
23
|
+
branch: string;
|
|
24
|
+
isHead: boolean;
|
|
25
|
+
branchLabel: string | undefined;
|
|
7
26
|
};
|
|
8
27
|
position: {
|
|
9
28
|
x: number;
|
|
10
29
|
y: number;
|
|
11
30
|
};
|
|
12
|
-
style: {
|
|
13
|
-
background: string;
|
|
14
|
-
color: string;
|
|
15
|
-
borderRadius: string;
|
|
16
|
-
padding: string;
|
|
17
|
-
};
|
|
18
31
|
}[];
|
|
19
32
|
flowEdges: {
|
|
20
33
|
id: string;
|
|
21
34
|
source: string;
|
|
22
35
|
target: string;
|
|
23
|
-
|
|
36
|
+
type: string;
|
|
24
37
|
style: {
|
|
25
38
|
stroke: string;
|
|
39
|
+
strokeWidth: number;
|
|
26
40
|
};
|
|
41
|
+
animated: boolean;
|
|
27
42
|
}[];
|
|
28
43
|
};
|
|
29
44
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/lib/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGtD,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,WAAW,EAAE,CAyE3D;AAGD,eAAO,MAAM,aAAa,UASzB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,UAAU,EAAE,CA4GhE;AAED,eAAO,MAAM,mBAAmB,GAAI,SAAS,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwIzD,CAAC"}
|
package/dist/lib/utils.js
CHANGED
|
@@ -1,33 +1,288 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
export function flattenTree(tree) {
|
|
2
|
+
const nodeBranchMap = new Map();
|
|
3
|
+
const seenInMark = new Set();
|
|
4
|
+
const seenInTraverse = new Set();
|
|
5
|
+
// First pass: find branch paths by traversing to HEADs and marking back
|
|
6
|
+
// Returns array of branch names found in this subtree
|
|
7
|
+
function markBranchPaths(node) {
|
|
8
|
+
// Cycle/dup guard (shouldn't happen in a DAG, but protects against corrupted input)
|
|
9
|
+
if (seenInMark.has(node.id)) {
|
|
10
|
+
return [nodeBranchMap.get(node.id) || node.branches[0] || "main"];
|
|
11
|
+
}
|
|
12
|
+
seenInMark.add(node.id);
|
|
13
|
+
// If this is a leaf (HEAD), use its branch name
|
|
14
|
+
if (node.children.length === 0) {
|
|
15
|
+
const branchName = node.branches[0] || "main";
|
|
16
|
+
nodeBranchMap.set(node.id, branchName);
|
|
17
|
+
return [branchName];
|
|
18
|
+
}
|
|
19
|
+
// Collect branches from all children
|
|
20
|
+
const childBranches = [];
|
|
21
|
+
for (const child of node.children) {
|
|
22
|
+
childBranches.push(...markBranchPaths(child));
|
|
23
|
+
}
|
|
24
|
+
// Determine this node's branch
|
|
25
|
+
const uniqueBranches = [...new Set(childBranches)];
|
|
26
|
+
if (uniqueBranches.length === 1) {
|
|
27
|
+
// Single path - inherit that branch
|
|
28
|
+
nodeBranchMap.set(node.id, uniqueBranches[0]);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Fork point - prefer "main" if present, otherwise first
|
|
32
|
+
nodeBranchMap.set(node.id, uniqueBranches.includes("main") ? "main" : uniqueBranches[0]);
|
|
33
|
+
}
|
|
34
|
+
return childBranches;
|
|
35
|
+
}
|
|
36
|
+
// Second pass: build flat array with correct branch names
|
|
37
|
+
function traverse(node) {
|
|
38
|
+
if (seenInTraverse.has(node.id))
|
|
39
|
+
return;
|
|
40
|
+
seenInTraverse.add(node.id);
|
|
41
|
+
result.push({
|
|
42
|
+
id: node.id,
|
|
43
|
+
parent_id: node.parent_id,
|
|
44
|
+
role: node.role,
|
|
45
|
+
content: node.content,
|
|
46
|
+
branch_name: nodeBranchMap.get(node.id) || "main",
|
|
47
|
+
});
|
|
48
|
+
for (const child of node.children) {
|
|
49
|
+
traverse(child);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Mark branch paths first
|
|
53
|
+
for (const root of tree) {
|
|
54
|
+
markBranchPaths(root);
|
|
55
|
+
}
|
|
56
|
+
// Then flatten
|
|
57
|
+
const result = [];
|
|
58
|
+
for (const root of tree) {
|
|
59
|
+
traverse(root);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
// Branch colors palette (git-style)
|
|
64
|
+
export const BRANCH_COLORS = [
|
|
65
|
+
"#3b82f6", // blue (main)
|
|
66
|
+
"#22c55e", // green
|
|
67
|
+
"#a855f7", // purple
|
|
68
|
+
"#f97316", // orange
|
|
69
|
+
"#ec4899", // pink
|
|
70
|
+
"#14b8a6", // teal
|
|
71
|
+
"#eab308", // yellow
|
|
72
|
+
"#ef4444", // red
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* Assigns lanes (horizontal positions) and rows (vertical positions) to nodes
|
|
76
|
+
* for canvas-based git graph rendering.
|
|
77
|
+
*/
|
|
78
|
+
export function assignLanes(history) {
|
|
79
|
+
if (history.length === 0) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
// Build lookup maps
|
|
83
|
+
const nodeMap = new Map();
|
|
84
|
+
history.forEach((node) => nodeMap.set(node.id, node));
|
|
85
|
+
const childrenMap = new Map();
|
|
86
|
+
history.forEach((node) => {
|
|
87
|
+
if (node.parent_id) {
|
|
88
|
+
if (!childrenMap.has(node.parent_id)) {
|
|
89
|
+
childrenMap.set(node.parent_id, []);
|
|
90
|
+
}
|
|
91
|
+
childrenMap.get(node.parent_id).push(node);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// Get unique branches and sort: main first, then by first appearance
|
|
95
|
+
const branchOrder = [];
|
|
96
|
+
const seenBranches = new Set();
|
|
97
|
+
history.forEach((node) => {
|
|
98
|
+
const branch = node.branch_name || "main";
|
|
99
|
+
if (!seenBranches.has(branch)) {
|
|
100
|
+
seenBranches.add(branch);
|
|
101
|
+
branchOrder.push(branch);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// Ensure main is first
|
|
105
|
+
const mainIndex = branchOrder.indexOf("main");
|
|
106
|
+
if (mainIndex > 0) {
|
|
107
|
+
branchOrder.splice(mainIndex, 1);
|
|
108
|
+
branchOrder.unshift("main");
|
|
109
|
+
}
|
|
110
|
+
else if (mainIndex === -1) {
|
|
111
|
+
branchOrder.unshift("main");
|
|
112
|
+
}
|
|
113
|
+
// Assign lanes to branches
|
|
114
|
+
const branchLanes = new Map();
|
|
115
|
+
branchOrder.forEach((branch, index) => {
|
|
116
|
+
branchLanes.set(branch, index);
|
|
117
|
+
});
|
|
118
|
+
// Assign colors to branches
|
|
119
|
+
const branchColors = new Map();
|
|
120
|
+
branchOrder.forEach((branch, index) => {
|
|
121
|
+
branchColors.set(branch, BRANCH_COLORS[index % BRANCH_COLORS.length]);
|
|
122
|
+
});
|
|
123
|
+
// Find HEAD nodes (nodes with no children on their branch)
|
|
124
|
+
const headNodes = new Set();
|
|
125
|
+
history.forEach((node) => {
|
|
126
|
+
const children = childrenMap.get(node.id) || [];
|
|
127
|
+
const sameBranchChildren = children.filter((c) => c.branch_name === node.branch_name);
|
|
128
|
+
if (sameBranchChildren.length === 0) {
|
|
129
|
+
headNodes.add(node.id);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
// Assign row positions using DFS
|
|
133
|
+
const rowPositions = new Map();
|
|
134
|
+
let currentRow = 0;
|
|
135
|
+
// Find root nodes
|
|
136
|
+
const roots = history.filter((node) => !node.parent_id || !nodeMap.has(node.parent_id));
|
|
137
|
+
function assignRows(node) {
|
|
138
|
+
if (rowPositions.has(node.id))
|
|
139
|
+
return;
|
|
140
|
+
rowPositions.set(node.id, currentRow);
|
|
141
|
+
currentRow++;
|
|
142
|
+
const children = childrenMap.get(node.id) || [];
|
|
143
|
+
// Sort children: main branch first, then by branch name
|
|
144
|
+
children.sort((a, b) => {
|
|
145
|
+
const aBranch = a.branch_name || "main";
|
|
146
|
+
const bBranch = b.branch_name || "main";
|
|
147
|
+
if (aBranch === "main" && bBranch !== "main")
|
|
148
|
+
return -1;
|
|
149
|
+
if (bBranch === "main" && aBranch !== "main")
|
|
150
|
+
return 1;
|
|
151
|
+
return aBranch.localeCompare(bBranch);
|
|
152
|
+
});
|
|
153
|
+
for (const child of children) {
|
|
154
|
+
assignRows(child);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const root of roots) {
|
|
158
|
+
assignRows(root);
|
|
159
|
+
}
|
|
160
|
+
// Build LayoutNode array
|
|
161
|
+
return history.map((node) => {
|
|
162
|
+
const branch = node.branch_name || "main";
|
|
163
|
+
return {
|
|
164
|
+
...node,
|
|
165
|
+
lane: branchLanes.get(branch) || 0,
|
|
166
|
+
row: rowPositions.get(node.id) || 0,
|
|
167
|
+
color: branchColors.get(branch) || BRANCH_COLORS[0],
|
|
168
|
+
isHead: headNodes.has(node.id),
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
export const transformToFlowData = (history) => {
|
|
173
|
+
if (history.length === 0) {
|
|
174
|
+
return { flowNodes: [], flowEdges: [] };
|
|
175
|
+
}
|
|
176
|
+
// Build a map for quick lookup
|
|
177
|
+
const nodeMap = new Map();
|
|
178
|
+
history.forEach((node) => nodeMap.set(node.id, node));
|
|
179
|
+
// Build children map for traversal
|
|
180
|
+
const childrenMap = new Map();
|
|
181
|
+
history.forEach((node) => {
|
|
182
|
+
if (node.parent_id) {
|
|
183
|
+
if (!childrenMap.has(node.parent_id)) {
|
|
184
|
+
childrenMap.set(node.parent_id, []);
|
|
185
|
+
}
|
|
186
|
+
childrenMap.get(node.parent_id).push(node);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
// Get unique branches and assign colors
|
|
190
|
+
const branchSet = new Set();
|
|
191
|
+
history.forEach((node) => branchSet.add(node.branch_name || "main"));
|
|
192
|
+
// Sort branches: main first, then others
|
|
193
|
+
const branches = Array.from(branchSet).sort((a, b) => {
|
|
194
|
+
if (a === "main")
|
|
195
|
+
return -1;
|
|
196
|
+
if (b === "main")
|
|
197
|
+
return 1;
|
|
198
|
+
return a.localeCompare(b);
|
|
199
|
+
});
|
|
200
|
+
// Assign colors to branches
|
|
201
|
+
const branchColors = new Map();
|
|
202
|
+
branches.forEach((branch, index) => {
|
|
203
|
+
branchColors.set(branch, BRANCH_COLORS[index % BRANCH_COLORS.length]);
|
|
204
|
+
});
|
|
205
|
+
// Assign X positions - main at 0, branches offset to the right
|
|
206
|
+
const branchXPos = new Map();
|
|
207
|
+
branches.forEach((branch, index) => {
|
|
208
|
+
branchXPos.set(branch, index * 300);
|
|
209
|
+
});
|
|
210
|
+
// Calculate Y positions using DFS to maintain parent-child order
|
|
211
|
+
const yPositions = new Map();
|
|
212
|
+
let currentY = 0;
|
|
213
|
+
// Find root nodes
|
|
214
|
+
const roots = history.filter((node) => !node.parent_id || !nodeMap.has(node.parent_id));
|
|
215
|
+
// DFS traversal - process main branch children first
|
|
216
|
+
function assignYPositions(node) {
|
|
217
|
+
if (yPositions.has(node.id))
|
|
218
|
+
return;
|
|
219
|
+
yPositions.set(node.id, currentY);
|
|
220
|
+
currentY += 80;
|
|
221
|
+
const children = childrenMap.get(node.id) || [];
|
|
222
|
+
// Sort children: main branch first, then others by branch name
|
|
223
|
+
children.sort((a, b) => {
|
|
224
|
+
const aBranch = a.branch_name || "main";
|
|
225
|
+
const bBranch = b.branch_name || "main";
|
|
226
|
+
if (aBranch === "main" && bBranch !== "main")
|
|
227
|
+
return -1;
|
|
228
|
+
if (bBranch === "main" && aBranch !== "main")
|
|
229
|
+
return 1;
|
|
230
|
+
return aBranch.localeCompare(bBranch);
|
|
231
|
+
});
|
|
232
|
+
for (const child of children) {
|
|
233
|
+
assignYPositions(child);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
for (const root of roots) {
|
|
237
|
+
assignYPositions(root);
|
|
238
|
+
}
|
|
239
|
+
// Find HEAD nodes (nodes with no children on their branch)
|
|
240
|
+
const headNodes = new Set();
|
|
241
|
+
history.forEach((node) => {
|
|
242
|
+
const children = childrenMap.get(node.id) || [];
|
|
243
|
+
const sameBranchChildren = children.filter((c) => c.branch_name === node.branch_name);
|
|
244
|
+
if (sameBranchChildren.length === 0) {
|
|
245
|
+
headNodes.add(node.id);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
const flowNodes = history.map((node) => {
|
|
249
|
+
const branch = node.branch_name || "main";
|
|
250
|
+
const branchColor = branchColors.get(branch) || BRANCH_COLORS[0];
|
|
251
|
+
const isHead = headNodes.has(node.id);
|
|
252
|
+
return {
|
|
253
|
+
id: node.id,
|
|
254
|
+
type: "commit",
|
|
255
|
+
data: {
|
|
256
|
+
role: node.role.toUpperCase(),
|
|
257
|
+
content: node.content.slice(0, 50),
|
|
258
|
+
branchColor: branchColor,
|
|
259
|
+
branch: branch,
|
|
260
|
+
isHead: isHead,
|
|
261
|
+
branchLabel: isHead ? branch : undefined,
|
|
262
|
+
},
|
|
263
|
+
position: {
|
|
264
|
+
x: branchXPos.get(branch) || 0,
|
|
265
|
+
y: yPositions.get(node.id) || 0,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
const flowEdges = history
|
|
270
|
+
.filter((node) => node.parent_id && nodeMap.has(node.parent_id))
|
|
271
|
+
.map((node) => {
|
|
272
|
+
// Use the child's branch color for the edge
|
|
273
|
+
const branch = node.branch_name || "main";
|
|
274
|
+
const branchColor = branchColors.get(branch) || BRANCH_COLORS[0];
|
|
275
|
+
return {
|
|
276
|
+
id: `e-${node.parent_id}-${node.id}`,
|
|
277
|
+
source: node.parent_id,
|
|
278
|
+
target: node.id,
|
|
279
|
+
type: "smoothstep",
|
|
280
|
+
style: { stroke: branchColor, strokeWidth: 2 },
|
|
281
|
+
animated: false,
|
|
282
|
+
};
|
|
283
|
+
});
|
|
28
284
|
return {
|
|
29
285
|
flowNodes,
|
|
30
286
|
flowEdges,
|
|
31
287
|
};
|
|
32
288
|
};
|
|
33
|
-
exports.transformToFlowData = transformToFlowData;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { LayoutNode } from "../lib/utils";
|
|
2
|
+
import type { TreeNode } from "../types";
|
|
3
|
+
export declare function ConversationGraph({ history, onNodeClick, selectedNodeId, onBranch, }: {
|
|
4
|
+
history: TreeNode[];
|
|
5
|
+
onNodeClick: (node: LayoutNode) => void;
|
|
6
|
+
selectedNodeId?: string;
|
|
7
|
+
onBranch?: (fromNodeId: string, content: string, branchName: string) => void;
|
|
5
8
|
}): import("react/jsx-runtime").JSX.Element;
|
|
6
9
|
//# sourceMappingURL=ConversationGraph.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationGraph.d.ts","sourceRoot":"","sources":["../../react/ConversationGraph.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConversationGraph.d.ts","sourceRoot":"","sources":["../../react/ConversationGraph.tsx"],"names":[],"mappings":"AAOA,OAAO,EAA2C,UAAU,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAgBzC,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,WAAW,EACX,cAAc,EACd,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,WAAW,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9E,2CA2qBA"}
|