@nkzw/codiff-core 1.8.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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/SharedPlanApp.d.ts +32 -0
- package/dist/SharedWalkthroughApp.d.ts +40 -0
- package/dist/app/components/Avatar.d.ts +6 -0
- package/dist/app/components/Button.d.ts +12 -0
- package/dist/app/components/CommandBar.d.ts +8 -0
- package/dist/app/components/FileTree.d.ts +10 -0
- package/dist/app/components/KeyboardShortcutsHelp.d.ts +5 -0
- package/dist/app/components/MarkdownDocumentEditor.d.ts +26 -0
- package/dist/app/components/Panels.d.ts +71 -0
- package/dist/app/components/PlanEditorView.d.ts +18 -0
- package/dist/app/components/ReadOnlyMarkdownView.d.ts +16 -0
- package/dist/app/components/ReviewCodeView.d.ts +103 -0
- package/dist/app/components/Sidebar.d.ts +48 -0
- package/dist/app/components/WalkthroughFileError.d.ts +4 -0
- package/dist/app/components/merge-request/GeneralComments.d.ts +49 -0
- package/dist/app/components/useCopiedState.d.ts +1 -0
- package/dist/app/components/walkthrough/CommitView.d.ts +23 -0
- package/dist/app/components/walkthrough/NarrativeSidebar.d.ts +12 -0
- package/dist/app/components/walkthrough/NarrativeWalkthroughView.d.ts +44 -0
- package/dist/app/components/walkthrough/WalkthroughDiffSurface.d.ts +16 -0
- package/dist/app/components/walkthrough/WalkthroughProgress.d.ts +8 -0
- package/dist/app/components/walkthrough/parts.d.ts +15 -0
- package/dist/app/components/walkthrough/useNarrativeNavigation.d.ts +34 -0
- package/dist/app/hooks/useAppCommands.d.ts +22 -0
- package/dist/app/hooks/useAppKeyboardShortcuts.d.ts +18 -0
- package/dist/app/hooks/useAppReviewComments.d.ts +38 -0
- package/dist/app/hooks/useAppWalkthrough.d.ts +96 -0
- package/dist/app/hooks/useDiffSearch.d.ts +24 -0
- package/dist/app/hooks/useDocumentAppearance.d.ts +10 -0
- package/dist/app/hooks/useResizableSidebar.d.ts +12 -0
- package/dist/app/hooks/useReviewCommentDrafts.d.ts +23 -0
- package/dist/app/hooks/useReviewState.d.ts +22 -0
- package/dist/config/defaults.d.ts +4 -0
- package/dist/config/keymap.d.ts +3 -0
- package/dist/config/types.d.ts +44 -0
- package/dist/defaults.d.ts +2 -0
- package/dist/fonts/firacode/FiraCode-Regular.woff2 +0 -0
- package/dist/fonts/firacode/FiraCode-SemiBold.woff2 +0 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +12 -0
- package/dist/lib/app-constants.d.ts +7 -0
- package/dist/lib/app-types.d.ts +137 -0
- package/dist/lib/code-view-options.d.ts +26 -0
- package/dist/lib/command-registry.d.ts +14 -0
- package/dist/lib/diff-search.d.ts +3 -0
- package/dist/lib/diff.d.ts +35 -0
- package/dist/lib/files.d.ts +14 -0
- package/dist/lib/item-version.d.ts +1 -0
- package/dist/lib/keyboard.d.ts +2 -0
- package/dist/lib/markdown.d.ts +3 -0
- package/dist/lib/narrative-walkthrough-diff.d.ts +113 -0
- package/dist/lib/narrative-walkthrough-diff.mjs +257 -0
- package/dist/lib/narrative-walkthrough.d.ts +108 -0
- package/dist/lib/reload-selection.d.ts +23 -0
- package/dist/lib/review-command-target.d.ts +15 -0
- package/dist/lib/review-comments.d.ts +29 -0
- package/dist/lib/review-identity.d.ts +7 -0
- package/dist/lib/review-scroll.d.ts +5 -0
- package/dist/lib/search-highlights.d.ts +5 -0
- package/dist/lib/sidebar-width.d.ts +9 -0
- package/dist/lib/source.d.ts +18 -0
- package/dist/lib/viewed.d.ts +2 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.mjs +9355 -0
- package/dist/share.d.ts +301 -0
- package/dist/share.mjs +103 -0
- package/dist/style.css +9142 -0
- package/dist/styles.css.d.ts +2 -0
- package/dist/types.d.ts +702 -0
- package/package.json +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getItemVersion: (value: string) => number;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
declare const HUNK_HEADER: RegExp;
|
|
2
|
+
/** @param {string} path */
|
|
3
|
+
declare const isGeneratedWalkthroughPath: (path: string) => boolean;
|
|
4
|
+
/** @param {{generated?: boolean; path: string}} file */
|
|
5
|
+
declare const isGeneratedWalkthroughFile: (file: {
|
|
6
|
+
generated?: boolean;
|
|
7
|
+
path: string;
|
|
8
|
+
}) => boolean;
|
|
9
|
+
/** @param {string} line */
|
|
10
|
+
declare const parseHunkHeader: (line: string) => {
|
|
11
|
+
additionCount: number;
|
|
12
|
+
additionEnd: number;
|
|
13
|
+
additionStart: number;
|
|
14
|
+
deletionCount: number;
|
|
15
|
+
deletionEnd: number;
|
|
16
|
+
deletionStart: number;
|
|
17
|
+
} | null;
|
|
18
|
+
/** @param {string} patch */
|
|
19
|
+
declare const extractPatchHunks: (patch: string) => {
|
|
20
|
+
additionCount: number;
|
|
21
|
+
additionEnd: number;
|
|
22
|
+
additionStart: number;
|
|
23
|
+
deletionCount: number;
|
|
24
|
+
deletionEnd: number;
|
|
25
|
+
deletionStart: number;
|
|
26
|
+
added: number;
|
|
27
|
+
deleted: number;
|
|
28
|
+
header: string;
|
|
29
|
+
}[];
|
|
30
|
+
/** @param {ReadonlyArray<{added: number; deleted: number}>} hunks */
|
|
31
|
+
declare const sumHunkLineCounts: (hunks: ReadonlyArray<{
|
|
32
|
+
added: number;
|
|
33
|
+
deleted: number;
|
|
34
|
+
}>) => {
|
|
35
|
+
added: number;
|
|
36
|
+
deleted: number;
|
|
37
|
+
};
|
|
38
|
+
/** @param {{kind?: string} | null | undefined} hunk */
|
|
39
|
+
declare const isSyntheticWalkthroughHunk: (hunk: {
|
|
40
|
+
kind?: string;
|
|
41
|
+
} | null | undefined) => boolean;
|
|
42
|
+
/** @param {ReturnType<typeof extractPatchHunks>[number] & {kind?: string}} hunk */
|
|
43
|
+
declare const hunkDisplayStart: (hunk: ReturnType<typeof extractPatchHunks>[number] & {
|
|
44
|
+
kind?: string;
|
|
45
|
+
}) => number | undefined;
|
|
46
|
+
/** @param {ReturnType<typeof extractPatchHunks>[number] & {kind?: string}} hunk */
|
|
47
|
+
declare const hunkDisplayEnd: (hunk: ReturnType<typeof extractPatchHunks>[number] & {
|
|
48
|
+
kind?: string;
|
|
49
|
+
}) => number | undefined;
|
|
50
|
+
/** @param {string} path @param {ReadonlyArray<ReturnType<typeof extractPatchHunks>[number] & {kind?: string}>} hunks */
|
|
51
|
+
declare const buildAnchorDisplay: (path: string, hunks: ReadonlyArray<ReturnType<typeof extractPatchHunks>[number] & {
|
|
52
|
+
kind?: string;
|
|
53
|
+
}>) => string;
|
|
54
|
+
/**
|
|
55
|
+
* Codiff's walkthrough hunk ids identify the smallest reviewable diff unit.
|
|
56
|
+
* Most are textual patch hunks; non-text or metadata-only sections get one
|
|
57
|
+
* synthetic hunk so walkthroughs remain hunk-based for every visible change.
|
|
58
|
+
*
|
|
59
|
+
* @param {{generated?: boolean; oldPath?: string; path: string; status: string}} file
|
|
60
|
+
* @param {{binary?: boolean; id: string; kind: string; loadState?: string; patch?: string; summary?: {reason?: string}}} section
|
|
61
|
+
*/
|
|
62
|
+
declare const getSectionWalkthroughHunks: (file: {
|
|
63
|
+
generated?: boolean;
|
|
64
|
+
oldPath?: string;
|
|
65
|
+
path: string;
|
|
66
|
+
status: string;
|
|
67
|
+
}, section: {
|
|
68
|
+
binary?: boolean;
|
|
69
|
+
id: string;
|
|
70
|
+
kind: string;
|
|
71
|
+
loadState?: string;
|
|
72
|
+
patch?: string;
|
|
73
|
+
summary?: {
|
|
74
|
+
reason?: string;
|
|
75
|
+
};
|
|
76
|
+
}) => {
|
|
77
|
+
added: number;
|
|
78
|
+
deleted: number;
|
|
79
|
+
id: string;
|
|
80
|
+
index: number;
|
|
81
|
+
kind: string;
|
|
82
|
+
oldPath: string | undefined;
|
|
83
|
+
path: string;
|
|
84
|
+
sectionId: string;
|
|
85
|
+
sectionKind: string;
|
|
86
|
+
status: string;
|
|
87
|
+
summary: string | undefined;
|
|
88
|
+
}[] | {
|
|
89
|
+
additionCount: number;
|
|
90
|
+
additionEnd: number;
|
|
91
|
+
additionStart: number;
|
|
92
|
+
deletionCount: number;
|
|
93
|
+
deletionEnd: number;
|
|
94
|
+
deletionStart: number;
|
|
95
|
+
added: number;
|
|
96
|
+
deleted: number;
|
|
97
|
+
header: string;
|
|
98
|
+
id: string;
|
|
99
|
+
index: number;
|
|
100
|
+
kind: string;
|
|
101
|
+
oldPath: string | undefined;
|
|
102
|
+
path: string;
|
|
103
|
+
sectionId: string;
|
|
104
|
+
sectionKind: string;
|
|
105
|
+
status: string;
|
|
106
|
+
}[];
|
|
107
|
+
/**
|
|
108
|
+
* @param {string} patch
|
|
109
|
+
* @param {string} sectionId
|
|
110
|
+
* @param {ReadonlyArray<string>} hunkIds
|
|
111
|
+
*/
|
|
112
|
+
declare const filterPatchToHunkIds: (patch: string, sectionId: string, hunkIds: ReadonlyArray<string>) => string | null;
|
|
113
|
+
export { buildAnchorDisplay, extractPatchHunks, filterPatchToHunkIds, getSectionWalkthroughHunks, HUNK_HEADER, hunkDisplayEnd, hunkDisplayStart, isGeneratedWalkthroughFile, isGeneratedWalkthroughPath, isSyntheticWalkthroughHunk, parseHunkHeader, sumHunkLineCounts, };
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
//#region lib/narrative-walkthrough-diff.js
|
|
2
|
+
const HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
3
|
+
const GENERATED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([
|
|
4
|
+
"__generated__",
|
|
5
|
+
"__snapshots__",
|
|
6
|
+
".generated",
|
|
7
|
+
"codegen",
|
|
8
|
+
"gen",
|
|
9
|
+
"generated",
|
|
10
|
+
"generated-sources",
|
|
11
|
+
"generated-src"
|
|
12
|
+
]);
|
|
13
|
+
const GENERATED_BASENAMES = /* @__PURE__ */ new Set([
|
|
14
|
+
"bun.lock",
|
|
15
|
+
"bun.lockb",
|
|
16
|
+
"cargo.lock",
|
|
17
|
+
"gemfile.lock",
|
|
18
|
+
"npm-shrinkwrap.json",
|
|
19
|
+
"package-lock.json",
|
|
20
|
+
"pnpm-lock.yaml",
|
|
21
|
+
"poetry.lock",
|
|
22
|
+
"pubspec.lock",
|
|
23
|
+
"uv.lock",
|
|
24
|
+
"yarn.lock"
|
|
25
|
+
]);
|
|
26
|
+
const GENERATED_EXTENSIONS = [
|
|
27
|
+
".d.ts.map",
|
|
28
|
+
".g.dart",
|
|
29
|
+
".generated.cjs",
|
|
30
|
+
".generated.css",
|
|
31
|
+
".generated.js",
|
|
32
|
+
".generated.jsx",
|
|
33
|
+
".generated.mjs",
|
|
34
|
+
".generated.ts",
|
|
35
|
+
".generated.tsx",
|
|
36
|
+
".pb.go",
|
|
37
|
+
".pb.gw.go",
|
|
38
|
+
".snap",
|
|
39
|
+
".snapshot"
|
|
40
|
+
];
|
|
41
|
+
const GENERATED_SUFFIXES = [
|
|
42
|
+
"-generated.js",
|
|
43
|
+
"-generated.ts",
|
|
44
|
+
"-generated.tsx",
|
|
45
|
+
".min.cjs",
|
|
46
|
+
".min.css",
|
|
47
|
+
".min.js",
|
|
48
|
+
".min.mjs",
|
|
49
|
+
".pb.cc",
|
|
50
|
+
".pb.h",
|
|
51
|
+
".pb.rb",
|
|
52
|
+
"_generated.go",
|
|
53
|
+
"_generated.rs",
|
|
54
|
+
"_pb2.py",
|
|
55
|
+
"_pb2_grpc.py"
|
|
56
|
+
];
|
|
57
|
+
/** @param {string} path */
|
|
58
|
+
const getPathParts = (path) => path.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
59
|
+
/** @param {string} path */
|
|
60
|
+
const isGeneratedWalkthroughPath = (path) => {
|
|
61
|
+
const parts = getPathParts(path).map((part) => part.toLowerCase());
|
|
62
|
+
const basename = parts.at(-1) || "";
|
|
63
|
+
if (!basename) return false;
|
|
64
|
+
return parts.some((part) => GENERATED_DIRECTORY_NAMES.has(part)) || GENERATED_BASENAMES.has(basename) || GENERATED_EXTENSIONS.some((extension) => basename.endsWith(extension)) || GENERATED_SUFFIXES.some((suffix) => basename.endsWith(suffix)) || basename.endsWith(".map") && !basename.endsWith(".importmap");
|
|
65
|
+
};
|
|
66
|
+
/** @param {{generated?: boolean; path: string}} file */
|
|
67
|
+
const isGeneratedWalkthroughFile = (file) => file.generated ?? isGeneratedWalkthroughPath(file.path);
|
|
68
|
+
/** @param {string} path */
|
|
69
|
+
const getGeneratedWalkthroughSummary = (path) => {
|
|
70
|
+
const parts = getPathParts(path).map((part) => part.toLowerCase());
|
|
71
|
+
const basename = parts.at(-1) || "";
|
|
72
|
+
if (GENERATED_BASENAMES.has(basename)) return "Lockfile collapsed into one generated-file review unit.";
|
|
73
|
+
if (parts.includes("__snapshots__") || basename.endsWith(".snap") || basename.endsWith(".snapshot")) return "Snapshot collapsed into one generated-file review unit.";
|
|
74
|
+
return "Generated file collapsed into one review unit.";
|
|
75
|
+
};
|
|
76
|
+
/** @param {string} line */
|
|
77
|
+
const parseHunkHeader = (line) => {
|
|
78
|
+
const match = HUNK_HEADER.exec(line);
|
|
79
|
+
if (!match) return null;
|
|
80
|
+
const deletionStart = Number(match[1]);
|
|
81
|
+
const deletionCount = Number(match[2] ?? 1);
|
|
82
|
+
const additionStart = Number(match[3]);
|
|
83
|
+
const additionCount = Number(match[4] ?? 1);
|
|
84
|
+
return {
|
|
85
|
+
additionCount,
|
|
86
|
+
additionEnd: additionStart + Math.max(0, additionCount - 1),
|
|
87
|
+
additionStart,
|
|
88
|
+
deletionCount,
|
|
89
|
+
deletionEnd: deletionStart + Math.max(0, deletionCount - 1),
|
|
90
|
+
deletionStart
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/** @param {string} patch */
|
|
94
|
+
const extractPatchHunks = (patch) => {
|
|
95
|
+
const lines = typeof patch === "string" ? patch.split("\n") : [];
|
|
96
|
+
const hunks = [];
|
|
97
|
+
let index = 0;
|
|
98
|
+
while (index < lines.length) {
|
|
99
|
+
const header = lines[index] ?? "";
|
|
100
|
+
const parsed = parseHunkHeader(header);
|
|
101
|
+
if (!parsed) {
|
|
102
|
+
index += 1;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
let additions = 0;
|
|
106
|
+
let deletions = 0;
|
|
107
|
+
index += 1;
|
|
108
|
+
while (index < lines.length && !parseHunkHeader(lines[index] ?? "")) {
|
|
109
|
+
const line = lines[index] ?? "";
|
|
110
|
+
if (line.startsWith("+")) additions += 1;
|
|
111
|
+
else if (line.startsWith("-")) deletions += 1;
|
|
112
|
+
index += 1;
|
|
113
|
+
}
|
|
114
|
+
hunks.push({
|
|
115
|
+
...parsed,
|
|
116
|
+
added: additions,
|
|
117
|
+
deleted: deletions,
|
|
118
|
+
header
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return hunks;
|
|
122
|
+
};
|
|
123
|
+
/** @param {ReadonlyArray<{added: number; deleted: number}>} hunks */
|
|
124
|
+
const sumHunkLineCounts = (hunks) => hunks.reduce((totals, hunk) => ({
|
|
125
|
+
added: totals.added + hunk.added,
|
|
126
|
+
deleted: totals.deleted + hunk.deleted
|
|
127
|
+
}), {
|
|
128
|
+
added: 0,
|
|
129
|
+
deleted: 0
|
|
130
|
+
});
|
|
131
|
+
/** @param {{kind?: string} | null | undefined} hunk */
|
|
132
|
+
const isSyntheticWalkthroughHunk = (hunk) => hunk?.kind === "synthetic";
|
|
133
|
+
/** @param {ReturnType<typeof extractPatchHunks>[number] & {kind?: string}} hunk */
|
|
134
|
+
const hunkDisplayStart = (hunk) => {
|
|
135
|
+
if (isSyntheticWalkthroughHunk(hunk)) return;
|
|
136
|
+
return hunk.added > 0 ? hunk.additionStart : hunk.deletionStart;
|
|
137
|
+
};
|
|
138
|
+
/** @param {ReturnType<typeof extractPatchHunks>[number] & {kind?: string}} hunk */
|
|
139
|
+
const hunkDisplayEnd = (hunk) => {
|
|
140
|
+
if (isSyntheticWalkthroughHunk(hunk)) return;
|
|
141
|
+
return hunk.added > 0 ? hunk.additionEnd : hunk.deletionEnd;
|
|
142
|
+
};
|
|
143
|
+
/** @param {string} path @param {ReadonlyArray<ReturnType<typeof extractPatchHunks>[number] & {kind?: string}>} hunks */
|
|
144
|
+
const buildAnchorDisplay = (path, hunks) => {
|
|
145
|
+
if (hunks.length === 0) return path;
|
|
146
|
+
const first = hunks[0];
|
|
147
|
+
const last = hunks.at(-1);
|
|
148
|
+
if (!first || !last) return path;
|
|
149
|
+
if (isSyntheticWalkthroughHunk(first)) return path;
|
|
150
|
+
const startLine = hunkDisplayStart(first);
|
|
151
|
+
const endLine = hunkDisplayEnd(last);
|
|
152
|
+
return startLine && endLine && endLine !== startLine ? `${path}:${startLine}-${endLine}` : `${path}:${startLine || 1}`;
|
|
153
|
+
};
|
|
154
|
+
/** @param {{oldPath?: string; path: string; status?: string}} file */
|
|
155
|
+
const fileHasRenameMetadata = (file) => file.status === "renamed" && file.oldPath != null && file.oldPath !== file.path;
|
|
156
|
+
/**
|
|
157
|
+
* @param {{oldPath?: string; path: string; status?: string}} file
|
|
158
|
+
* @param {{binary?: boolean; loadState?: string; patch?: string; summary?: {reason?: string}}} section
|
|
159
|
+
*/
|
|
160
|
+
const shouldCreateSyntheticHunk = (file, section) => {
|
|
161
|
+
if (section.binary) return true;
|
|
162
|
+
if (section.loadState != null && section.loadState !== "ready") return true;
|
|
163
|
+
if (fileHasRenameMetadata(file)) return true;
|
|
164
|
+
return typeof section.patch === "string" && section.patch.trim().length > 0;
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* @param {{generated?: boolean; oldPath?: string; path: string; status: string}} file
|
|
168
|
+
* @param {{binary?: boolean; id: string; kind: string; loadState?: string; patch?: string; summary?: {reason?: string}}} section
|
|
169
|
+
*/
|
|
170
|
+
const createSyntheticSectionHunk = (file, section, lineCount = {
|
|
171
|
+
added: 0,
|
|
172
|
+
deleted: 0
|
|
173
|
+
}) => ({
|
|
174
|
+
added: lineCount.added,
|
|
175
|
+
deleted: lineCount.deleted,
|
|
176
|
+
id: `${section.id}:h1`,
|
|
177
|
+
index: 1,
|
|
178
|
+
kind: "synthetic",
|
|
179
|
+
oldPath: file.oldPath,
|
|
180
|
+
path: file.path,
|
|
181
|
+
sectionId: section.id,
|
|
182
|
+
sectionKind: section.kind,
|
|
183
|
+
status: file.status,
|
|
184
|
+
summary: section.summary?.reason ?? (isGeneratedWalkthroughFile(file) ? getGeneratedWalkthroughSummary(file.path) : void 0)
|
|
185
|
+
});
|
|
186
|
+
/**
|
|
187
|
+
* Codiff's walkthrough hunk ids identify the smallest reviewable diff unit.
|
|
188
|
+
* Most are textual patch hunks; non-text or metadata-only sections get one
|
|
189
|
+
* synthetic hunk so walkthroughs remain hunk-based for every visible change.
|
|
190
|
+
*
|
|
191
|
+
* @param {{generated?: boolean; oldPath?: string; path: string; status: string}} file
|
|
192
|
+
* @param {{binary?: boolean; id: string; kind: string; loadState?: string; patch?: string; summary?: {reason?: string}}} section
|
|
193
|
+
*/
|
|
194
|
+
const getSectionWalkthroughHunks = (file, section) => {
|
|
195
|
+
const patchHunks = extractPatchHunks(section.patch || "");
|
|
196
|
+
if (patchHunks.length > 0 && isGeneratedWalkthroughFile(file)) return [createSyntheticSectionHunk(file, section, sumHunkLineCounts(patchHunks))];
|
|
197
|
+
if (patchHunks.length > 0) return patchHunks.map((hunk, index) => ({
|
|
198
|
+
...hunk,
|
|
199
|
+
id: `${section.id}:h${index + 1}`,
|
|
200
|
+
index: index + 1,
|
|
201
|
+
kind: "patch",
|
|
202
|
+
oldPath: file.oldPath,
|
|
203
|
+
path: file.path,
|
|
204
|
+
sectionId: section.id,
|
|
205
|
+
sectionKind: section.kind,
|
|
206
|
+
status: file.status
|
|
207
|
+
}));
|
|
208
|
+
return shouldCreateSyntheticHunk(file, section) ? [createSyntheticSectionHunk(file, section)] : [];
|
|
209
|
+
};
|
|
210
|
+
/** @param {string} sectionId @param {string} hunkId */
|
|
211
|
+
const getHunkOrdinal = (sectionId, hunkId) => {
|
|
212
|
+
const prefix = `${sectionId}:h`;
|
|
213
|
+
if (!hunkId.startsWith(prefix)) return null;
|
|
214
|
+
const ordinal = Number(hunkId.slice(prefix.length));
|
|
215
|
+
return Number.isInteger(ordinal) && ordinal > 0 ? ordinal : null;
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* @param {string} patch
|
|
219
|
+
* @param {string} sectionId
|
|
220
|
+
* @param {ReadonlyArray<string>} hunkIds
|
|
221
|
+
*/
|
|
222
|
+
const filterPatchToHunkIds = (patch, sectionId, hunkIds) => {
|
|
223
|
+
if (typeof patch !== "string" || patch.trim().length === 0 || hunkIds.length === 0) return null;
|
|
224
|
+
const patchLines = patch.split("\n");
|
|
225
|
+
const headerLines = [];
|
|
226
|
+
const hunkLinesByOrdinal = /* @__PURE__ */ new Map();
|
|
227
|
+
let index = 0;
|
|
228
|
+
let foundHunk = false;
|
|
229
|
+
let ordinal = 0;
|
|
230
|
+
while (index < patchLines.length) {
|
|
231
|
+
if (parseHunkHeader(patchLines[index] ?? "")) {
|
|
232
|
+
foundHunk = true;
|
|
233
|
+
ordinal += 1;
|
|
234
|
+
const hunkStart = index;
|
|
235
|
+
index += 1;
|
|
236
|
+
while (index < patchLines.length && !parseHunkHeader(patchLines[index] ?? "")) index += 1;
|
|
237
|
+
hunkLinesByOrdinal.set(ordinal, patchLines.slice(hunkStart, index));
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (!foundHunk) headerLines.push(patchLines[index] ?? "");
|
|
241
|
+
index += 1;
|
|
242
|
+
}
|
|
243
|
+
if (!foundHunk) return null;
|
|
244
|
+
const selectedHunkLines = [];
|
|
245
|
+
for (const hunkId of hunkIds) {
|
|
246
|
+
const ordinal = getHunkOrdinal(sectionId, hunkId);
|
|
247
|
+
if (ordinal == null) return null;
|
|
248
|
+
const lines = hunkLinesByOrdinal.get(ordinal);
|
|
249
|
+
if (!lines) return null;
|
|
250
|
+
selectedHunkLines.push(...lines);
|
|
251
|
+
}
|
|
252
|
+
if (selectedHunkLines.length === 0) return null;
|
|
253
|
+
const focused = [...headerLines, ...selectedHunkLines].join("\n");
|
|
254
|
+
return patch.endsWith("\n") && !focused.endsWith("\n") ? `${focused}\n` : focused;
|
|
255
|
+
};
|
|
256
|
+
//#endregion
|
|
257
|
+
export { HUNK_HEADER, buildAnchorDisplay, extractPatchHunks, filterPatchToHunkIds, getSectionWalkthroughHunks, hunkDisplayEnd, hunkDisplayStart, isGeneratedWalkthroughFile, isGeneratedWalkthroughPath, isSyntheticWalkthroughHunk, parseHunkHeader, sumHunkLineCounts };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { ChangedFile, DiffSection, NarrativeWalkthrough, WalkthroughChangeType, WalkthroughChapter, WalkthroughHunk, WalkthroughHunkGroup, WalkthroughIcon, WalkthroughSupportGroup, WalkthroughStop } from '../types.ts';
|
|
2
|
+
/** A stop with a global position in the walkthrough. */
|
|
3
|
+
export type WalkthroughStopView = WalkthroughStop & {
|
|
4
|
+
chapterId: string;
|
|
5
|
+
index: number;
|
|
6
|
+
};
|
|
7
|
+
/** A chapter with indexed stops. */
|
|
8
|
+
type WalkthroughChapterView = Omit<WalkthroughChapter, 'stops'> & {
|
|
9
|
+
stops: ReadonlyArray<WalkthroughStopView>;
|
|
10
|
+
};
|
|
11
|
+
/** Support grouped by reason, preserving first-seen order. */
|
|
12
|
+
type WalkthroughSupportReason = {
|
|
13
|
+
files: ReadonlyArray<WalkthroughSupportGroup>;
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
16
|
+
/** Everything a narrative walkthrough needs to render. */
|
|
17
|
+
export type WalkthroughView = {
|
|
18
|
+
chapters: ReadonlyArray<WalkthroughChapterView>;
|
|
19
|
+
sequence: ReadonlyArray<WalkthroughStopView>;
|
|
20
|
+
support: ReadonlyArray<WalkthroughSupportGroup>;
|
|
21
|
+
supportByReason: ReadonlyArray<WalkthroughSupportReason>;
|
|
22
|
+
};
|
|
23
|
+
export type WalkthroughFileList = {
|
|
24
|
+
label: string;
|
|
25
|
+
title: string;
|
|
26
|
+
};
|
|
27
|
+
export type WalkthroughFileLineRow = {
|
|
28
|
+
added: number;
|
|
29
|
+
deleted: number;
|
|
30
|
+
label: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
title: string;
|
|
33
|
+
};
|
|
34
|
+
export declare const walkthroughFileName: (path: string) => string;
|
|
35
|
+
export declare const walkthroughItemPaths: (item: WalkthroughHunkGroup) => ReadonlyArray<string>;
|
|
36
|
+
export declare const walkthroughItemTitleFallback: (item: WalkthroughHunkGroup & {
|
|
37
|
+
prose?: string;
|
|
38
|
+
}) => string;
|
|
39
|
+
export declare const formatWalkthroughFileList: (paths: ReadonlyArray<string>, maxVisibleFiles?: number) => WalkthroughFileList;
|
|
40
|
+
export declare const formatWalkthroughFileLineRows: (items: ReadonlyArray<{
|
|
41
|
+
added: number;
|
|
42
|
+
deleted: number;
|
|
43
|
+
path: string;
|
|
44
|
+
}>, maxVisibleFiles?: number) => ReadonlyArray<WalkthroughFileLineRow>;
|
|
45
|
+
export declare const getUncoveredWalkthroughFiles: (files: ReadonlyArray<ChangedFile>, view: WalkthroughView, showWhitespace: boolean) => ReadonlyArray<ChangedFile>;
|
|
46
|
+
export declare const getUncoveredWalkthroughFileLineItems: (files: ReadonlyArray<ChangedFile>, view: WalkthroughView, showWhitespace: boolean) => ReadonlyArray<{
|
|
47
|
+
added: number;
|
|
48
|
+
deleted: number;
|
|
49
|
+
path: string;
|
|
50
|
+
}>;
|
|
51
|
+
export declare const isWalkthroughCommittable: (walkthrough: NarrativeWalkthrough) => boolean;
|
|
52
|
+
/** Build the walkthrough view-model with globally indexed stops. */
|
|
53
|
+
export declare const buildWalkthroughView: (walkthrough: NarrativeWalkthrough) => WalkthroughView | null;
|
|
54
|
+
/** The changed file + diff section a resolved hunk anchors into, if present in the diff. */
|
|
55
|
+
export type ResolvedWalkthroughHunkFile = {
|
|
56
|
+
file: ChangedFile;
|
|
57
|
+
section: DiffSection;
|
|
58
|
+
};
|
|
59
|
+
export type WalkthroughHunkRun = {
|
|
60
|
+
hunks: ReadonlyArray<WalkthroughHunk>;
|
|
61
|
+
key: string;
|
|
62
|
+
resolved: ResolvedWalkthroughHunkFile;
|
|
63
|
+
};
|
|
64
|
+
/** Resolve one normalized hunk to its exact live `ChangedFile` and `DiffSection`. */
|
|
65
|
+
export declare const resolveWalkthroughHunkFile: (hunk: WalkthroughHunk, files: ReadonlyArray<ChangedFile>) => ResolvedWalkthroughHunkFile | null;
|
|
66
|
+
/** Resolve item hunks in authored order, coalescing adjacent hunks from the same file section. */
|
|
67
|
+
export declare const resolveWalkthroughHunkRuns: (item: WalkthroughHunkGroup, files: ReadonlyArray<ChangedFile>) => ReadonlyArray<WalkthroughHunkRun>;
|
|
68
|
+
export declare const getWalkthroughRunNote: (item: WalkthroughHunkGroup, run: WalkthroughHunkRun) => string | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Return the changed-file view a walkthrough stop should render for one live diff
|
|
71
|
+
* section. The patch contains exactly the provided hunk ids, in that order.
|
|
72
|
+
*/
|
|
73
|
+
export declare const focusChangedFileForHunks: (file: ChangedFile, section: DiffSection, hunks: ReadonlyArray<WalkthroughHunk>) => ChangedFile | null;
|
|
74
|
+
/** One unique changed file in the commit composer, with summed line counts. */
|
|
75
|
+
export type CommitFile = {
|
|
76
|
+
added: number;
|
|
77
|
+
changeType?: WalkthroughChangeType;
|
|
78
|
+
deleted: number;
|
|
79
|
+
itemId: string;
|
|
80
|
+
name: string;
|
|
81
|
+
/** The note the generated body uses for this file. */
|
|
82
|
+
note?: string;
|
|
83
|
+
path: string;
|
|
84
|
+
};
|
|
85
|
+
/** A group of files in the composer — one per chapter, plus a final support group. */
|
|
86
|
+
export type CommitGroup = {
|
|
87
|
+
files: ReadonlyArray<CommitFile>;
|
|
88
|
+
icon: WalkthroughIcon;
|
|
89
|
+
id: string;
|
|
90
|
+
isSupport: boolean;
|
|
91
|
+
title: string;
|
|
92
|
+
};
|
|
93
|
+
export type CommitModel = {
|
|
94
|
+
/** Every unique file, in group order. */
|
|
95
|
+
files: ReadonlyArray<CommitFile>;
|
|
96
|
+
groups: ReadonlyArray<CommitGroup>;
|
|
97
|
+
};
|
|
98
|
+
export declare const changeTypeLabel: Record<WalkthroughChangeType, string>;
|
|
99
|
+
export declare const buildGenericCommitModel: (changedFiles: ReadonlyArray<ChangedFile>) => CommitModel;
|
|
100
|
+
/**
|
|
101
|
+
* Collapse the walkthrough view into unique changed files. Line counts are
|
|
102
|
+
* summed across every item that shares a path, and a path is placed in the first
|
|
103
|
+
* group that mentions it.
|
|
104
|
+
*/
|
|
105
|
+
export declare const buildCommitModel: (view: WalkthroughView, changedFiles?: ReadonlyArray<ChangedFile>) => CommitModel;
|
|
106
|
+
export declare const getCommitSelectionPaths: (view: WalkthroughView | null, changedFiles: ReadonlyArray<ChangedFile>) => ReadonlyArray<string>;
|
|
107
|
+
export declare const importanceLabel: Record<WalkthroughStop['importance'], string>;
|
|
108
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { GitFileStatus, RepositoryState, ReviewSource } from '../types.ts';
|
|
2
|
+
type ReloadSelectionFile = {
|
|
3
|
+
fingerprint: string;
|
|
4
|
+
path: string;
|
|
5
|
+
status: GitFileStatus;
|
|
6
|
+
};
|
|
7
|
+
export type ReloadMainMode = 'commit' | 'review';
|
|
8
|
+
type ReloadSelection = {
|
|
9
|
+
files: ReadonlyArray<ReloadSelectionFile>;
|
|
10
|
+
historySource?: ReviewSource | null;
|
|
11
|
+
mainMode?: ReloadMainMode;
|
|
12
|
+
root: string;
|
|
13
|
+
selectedPath: string | null;
|
|
14
|
+
source: ReviewSource;
|
|
15
|
+
};
|
|
16
|
+
export declare const consumeReloadSelection: () => ReloadSelection | null;
|
|
17
|
+
export declare const getReloadSelectionPath: (selection: ReloadSelection | null, state: RepositoryState) => string | null;
|
|
18
|
+
export declare const getChangedPaths: (previousFiles: ReadonlyArray<ReloadSelectionFile>, nextFiles: ReadonlyArray<ReloadSelectionFile>) => ReadonlySet<string>;
|
|
19
|
+
export declare const getReloadDeltaPaths: (selection: ReloadSelection | null, state: RepositoryState) => ReadonlySet<string>;
|
|
20
|
+
export declare const getReloadHistorySource: (selection: ReloadSelection | null, state: RepositoryState) => ReviewSource | null;
|
|
21
|
+
export declare const getReloadMainMode: (selection: ReloadSelection | null, state: RepositoryState) => ReloadMainMode | null;
|
|
22
|
+
export declare const writeReloadSelection: (state: RepositoryState | null, selectedPath: string | null, historySource?: ReviewSource | null, mainMode?: ReloadMainMode) => void;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ChangedFile, ReviewSource } from '../types.ts';
|
|
2
|
+
import type { ReviewIdentity } from './app-types.ts';
|
|
3
|
+
export type ReviewCommandTarget = {
|
|
4
|
+
file: ChangedFile;
|
|
5
|
+
reviewIdentity: ReviewIdentity;
|
|
6
|
+
sourceKey: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const createReviewCommandTarget: (source: ReviewSource, file: ChangedFile, reviewIdentity?: ReviewIdentity) => ReviewCommandTarget;
|
|
9
|
+
export declare const resolveReviewCommandTarget: ({ activeTarget, files, selectedPath, source, useActiveTarget, }: {
|
|
10
|
+
activeTarget: ReviewCommandTarget | null;
|
|
11
|
+
files: ReadonlyArray<ChangedFile>;
|
|
12
|
+
selectedPath: string | null;
|
|
13
|
+
source: ReviewSource;
|
|
14
|
+
useActiveTarget: boolean;
|
|
15
|
+
}) => ReviewCommandTarget | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ChangedFile, PullRequestReviewComment, RepositoryState } from '../types.ts';
|
|
2
|
+
import type { CodeViewInstance, ReviewComment } from './app-types.ts';
|
|
3
|
+
export declare const isInteractiveReviewEvent: (event: PointerEvent) => boolean;
|
|
4
|
+
export declare const hasActiveTextSelection: () => boolean;
|
|
5
|
+
export declare const isFileReviewComment: (comment: Pick<ReviewComment, 'anchor' | 'lineNumber' | 'side'>) => boolean;
|
|
6
|
+
export declare const isLineReviewComment: (comment: ReviewComment) => comment is ReviewComment & {
|
|
7
|
+
lineNumber: number;
|
|
8
|
+
side: 'additions' | 'deletions';
|
|
9
|
+
};
|
|
10
|
+
export declare function updateStickyHeaderState(viewer: CodeViewInstance): void;
|
|
11
|
+
export declare const getReviewCommentLineLabel: (comment: Pick<ReviewComment, 'anchor' | 'lineNumber' | 'side' | 'startLineNumber' | 'startSide'>) => string;
|
|
12
|
+
export declare const getReviewCommentRangeProps: (comment: Pick<ReviewComment, 'anchor' | 'lineNumber' | 'side' | 'startLineNumber' | 'startSide'>) => {
|
|
13
|
+
anchor: 'file';
|
|
14
|
+
} | {
|
|
15
|
+
anchor?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
anchor?: undefined;
|
|
18
|
+
startLineNumber: number;
|
|
19
|
+
startSide?: "additions" | "deletions" | undefined;
|
|
20
|
+
};
|
|
21
|
+
export declare const toPullRequestReviewComment: (comment: ReviewComment) => PullRequestReviewComment;
|
|
22
|
+
export declare const getPendingPullRequestReviewComments: (comments: ReadonlyArray<ReviewComment>, activeDraft?: Pick<ReviewComment, 'body' | 'id'> | null) => ReviewComment[];
|
|
23
|
+
export declare const findReusableReviewCommentDraft: (comments: ReadonlyArray<ReviewComment>, activeDraft?: Pick<ReviewComment, 'body' | 'id'> | null) => ReviewComment | undefined;
|
|
24
|
+
export declare const getCommentKey: (comment: Pick<ReviewComment, 'anchor' | 'lineNumber' | 'sectionId' | 'side' | 'startLineNumber' | 'startSide'>) => string;
|
|
25
|
+
export declare const getReviewCommentsDigest: (comments: ReadonlyArray<ReviewComment>) => string;
|
|
26
|
+
export declare const buildReviewCommentsMarkdown: (files: ReadonlyArray<ChangedFile>, comments: ReadonlyArray<ReviewComment>, showWhitespace: boolean, prefix?: string) => string;
|
|
27
|
+
export declare const getReviewCommentsFromState: (state: RepositoryState) => ReadonlyArray<ReviewComment>;
|
|
28
|
+
export declare const getVisibleReviewComments: (comments: ReadonlyArray<ReviewComment>, showOutdated: boolean) => ReadonlyArray<ReviewComment>;
|
|
29
|
+
export declare const shouldDiscardReviewCommentOnEscape: (body: string, confirmDiscard?: (message: string) => boolean) => boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ChangedFile } from '../types.ts';
|
|
2
|
+
import type { ReviewIdentity } from './app-types.ts';
|
|
3
|
+
export declare const getFileReviewIdentity: (file: ChangedFile) => ReviewIdentity;
|
|
4
|
+
export declare const getReviewIdentity: (file: ChangedFile, identityByPath?: ReadonlyMap<string, ReviewIdentity>) => ReviewIdentity;
|
|
5
|
+
export declare const isReviewIdentityViewed: (viewed: Readonly<Record<string, string>>, identity: ReviewIdentity) => boolean;
|
|
6
|
+
export declare const updateReviewIdentityViewed: (viewed: Readonly<Record<string, string>>, identity: ReviewIdentity, currentlyViewed: boolean) => Record<string, string>;
|
|
7
|
+
export declare const updateReviewIdentityCollapsed: (collapsed: ReadonlySet<string>, identity: ReviewIdentity, currentlyCollapsed: boolean) => Set<string>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ChangedFile } from '../types.ts';
|
|
2
|
+
import type { CodeViewInstance } from './app-types.ts';
|
|
3
|
+
type ReviewScrollViewer = Pick<CodeViewInstance, 'getScrollTop' | 'getTopForItem'>;
|
|
4
|
+
export declare const getSelectedPathFromScroll: (viewer: ReviewScrollViewer, files: ReadonlyArray<ChangedFile>, showWhitespace: boolean) => string | null;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const SIDEBAR_COLLAPSE_THRESHOLD = 80;
|
|
2
|
+
export declare const SIDEBAR_DEFAULT_WIDTH = 292;
|
|
3
|
+
export declare const SIDEBAR_MAX_WIDTH = 640;
|
|
4
|
+
export declare const SIDEBAR_MIN_WIDTH = 220;
|
|
5
|
+
type SidebarWidthStorage = Pick<Storage, 'getItem' | 'setItem'>;
|
|
6
|
+
export declare const clampSidebarWidth: (width: number) => number;
|
|
7
|
+
export declare const readSidebarWidth: (storage?: SidebarWidthStorage) => number;
|
|
8
|
+
export declare const writeSidebarWidth: (width: number, storage?: SidebarWidthStorage) => void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReviewSource } from '../types.ts';
|
|
2
|
+
import type { RepositoryLoadError } from './app-types.ts';
|
|
3
|
+
export declare const getSourceKey: (source: ReviewSource) => string;
|
|
4
|
+
export declare const getRepositoryLoadError: (error: unknown) => RepositoryLoadError;
|
|
5
|
+
export declare const getShortRef: (ref: string) => string;
|
|
6
|
+
export declare const getSourceLabel: (source: ReviewSource) => string;
|
|
7
|
+
export declare const getHistorySource: (source: ReviewSource) => ReviewSource | undefined;
|
|
8
|
+
export declare const getRefreshSource: (source: ReviewSource) => ReviewSource;
|
|
9
|
+
export declare const supportsLazyDiffContent: (source: ReviewSource) => boolean;
|
|
10
|
+
export declare const supportsDiffSearchContentPreload: (source: ReviewSource) => boolean;
|
|
11
|
+
export declare const shouldStartInHistoryWhenEmpty: (source: ReviewSource) => boolean;
|
|
12
|
+
export declare const usesViewedFileState: (source: ReviewSource) => boolean;
|
|
13
|
+
export declare const getEmptySourceTitle: (source: ReviewSource) => string;
|
|
14
|
+
export declare const getEmptySourceDetail: (source: ReviewSource, root: string) => {
|
|
15
|
+
kind: 'code' | 'text';
|
|
16
|
+
text: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
};
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { Button, buttonVariants, type ButtonProps } from './app/components/Button.tsx';
|
|
2
|
+
export { getPlanCommentAffordancePosition, getPlanCommentColorIndex, getPlanCommentIdentityColorIndex, getSharedPlanDownloadContent, PlanReviewSurface, type PlanReviewCommenting, } from './SharedPlanApp.tsx';
|
|
3
|
+
export { ReadOnlyGeneralCommentCard, ReviewSurface, type ReviewCommenting, type ReviewMode, type ReviewSurfaceProps, type ReviewWalkthroughStatus, } from './SharedWalkthroughApp.tsx';
|