@nkzw/codiff-core 1.11.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -26,6 +26,7 @@ declare const extractPatchHunks: (patch: string) => {
|
|
|
26
26
|
added: number;
|
|
27
27
|
deleted: number;
|
|
28
28
|
header: string;
|
|
29
|
+
patch: string;
|
|
29
30
|
}[];
|
|
30
31
|
/** @param {ReadonlyArray<{added: number; deleted: number}>} hunks */
|
|
31
32
|
declare const sumHunkLineCounts: (hunks: ReadonlyArray<{
|
|
@@ -95,6 +96,7 @@ declare const getSectionWalkthroughHunks: (file: {
|
|
|
95
96
|
added: number;
|
|
96
97
|
deleted: number;
|
|
97
98
|
header: string;
|
|
99
|
+
patch: string;
|
|
98
100
|
id: string;
|
|
99
101
|
index: number;
|
|
100
102
|
kind: string;
|
|
@@ -102,6 +102,7 @@ const extractPatchHunks = (patch) => {
|
|
|
102
102
|
index += 1;
|
|
103
103
|
continue;
|
|
104
104
|
}
|
|
105
|
+
const hunkStart = index;
|
|
105
106
|
let additions = 0;
|
|
106
107
|
let deletions = 0;
|
|
107
108
|
index += 1;
|
|
@@ -115,7 +116,8 @@ const extractPatchHunks = (patch) => {
|
|
|
115
116
|
...parsed,
|
|
116
117
|
added: additions,
|
|
117
118
|
deleted: deletions,
|
|
118
|
-
header
|
|
119
|
+
header,
|
|
120
|
+
patch: lines.slice(hunkStart, index).join("\n")
|
|
119
121
|
});
|
|
120
122
|
}
|
|
121
123
|
return hunks;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ChangedFile, PullRequestExistingReviewComment, PullRequestReviewComment, RepositoryState } from '../types.ts';
|
|
2
2
|
import type { CodeViewInstance, ReviewComment } from './app-types.ts';
|
|
3
3
|
export declare const isInteractiveReviewEvent: (event: PointerEvent) => boolean;
|
|
4
|
-
export declare const hasActiveTextSelection: () => boolean;
|
|
4
|
+
export declare const hasActiveTextSelection: (target?: Node | null) => boolean;
|
|
5
5
|
export declare const isFileReviewComment: (comment: Pick<ReviewComment, 'anchor' | 'lineNumber' | 'side'>) => boolean;
|
|
6
6
|
export declare const isLineReviewComment: (comment: ReviewComment) => comment is ReviewComment & {
|
|
7
7
|
lineNumber: number;
|
package/dist/react.mjs
CHANGED
|
@@ -44,7 +44,6 @@ import { ArrowRightIcon } from "@phosphor-icons/react/ArrowRight";
|
|
|
44
44
|
import { CaretLeftIcon } from "@phosphor-icons/react/CaretLeft";
|
|
45
45
|
import { CaretRightIcon } from "@phosphor-icons/react/CaretRight";
|
|
46
46
|
import { ArrowsClockwiseIcon } from "@phosphor-icons/react/ArrowsClockwise";
|
|
47
|
-
import { Terminal, init } from "ghostty-web";
|
|
48
47
|
//#region app/components/Button.tsx
|
|
49
48
|
const buttonVariants = cva("codiff-button", {
|
|
50
49
|
defaultVariants: {
|
|
@@ -2380,9 +2379,31 @@ function MergeRequestCommentsView({ canComment, commenting, draft, editDraft, ed
|
|
|
2380
2379
|
//#endregion
|
|
2381
2380
|
//#region lib/review-comments.ts
|
|
2382
2381
|
const isInteractiveReviewEvent = (event) => event.composedPath().some((target) => target instanceof HTMLElement && (target.closest("button, textarea, input, select, a") || target.closest(".review-comment-thread")));
|
|
2383
|
-
const
|
|
2382
|
+
const rangeIntersectsTarget = (range, target) => {
|
|
2383
|
+
const targetRoot = target.getRootNode();
|
|
2384
|
+
if (range.startContainer.getRootNode() !== targetRoot || range.endContainer.getRootNode() !== targetRoot) return false;
|
|
2385
|
+
const selectionRange = (target.ownerDocument ?? document).createRange();
|
|
2386
|
+
selectionRange.setStart(range.startContainer, range.startOffset);
|
|
2387
|
+
selectionRange.setEnd(range.endContainer, range.endOffset);
|
|
2388
|
+
return selectionRange.intersectsNode(target);
|
|
2389
|
+
};
|
|
2390
|
+
const isExpandedSelection = (selection, target) => {
|
|
2391
|
+
if (selection == null || selection.rangeCount === 0) return false;
|
|
2392
|
+
const range = selection.getRangeAt(0);
|
|
2393
|
+
return (!selection.isCollapsed || selection.toString().length > 0 || !range.collapsed) && (!target || rangeIntersectsTarget(range, target));
|
|
2394
|
+
};
|
|
2395
|
+
const isExpandedRange = (range, target) => range != null && !range.collapsed && (!target || rangeIntersectsTarget(range, target));
|
|
2396
|
+
const hasActiveTextSelection = (target) => {
|
|
2384
2397
|
const selection = window.getSelection();
|
|
2385
|
-
|
|
2398
|
+
const root = target?.getRootNode();
|
|
2399
|
+
const shadowRoot = root && "host" in root ? root : null;
|
|
2400
|
+
if (shadowRoot) {
|
|
2401
|
+
const composedRange = selection?.getComposedRanges?.({ shadowRoots: [shadowRoot] })[0];
|
|
2402
|
+
if (isExpandedRange(composedRange, target)) return true;
|
|
2403
|
+
const shadowSelection = shadowRoot.getSelection?.();
|
|
2404
|
+
if (isExpandedSelection(shadowSelection, target)) return true;
|
|
2405
|
+
}
|
|
2406
|
+
return isExpandedSelection(selection, target);
|
|
2386
2407
|
};
|
|
2387
2408
|
const isFileReviewComment = (comment) => comment.anchor === "file" || comment.lineNumber == null || comment.side == null;
|
|
2388
2409
|
const isLineReviewComment = (comment) => !isFileReviewComment(comment);
|
|
@@ -5957,8 +5978,9 @@ function ReviewCommentEditor({ agentId, agentLabel, comment, displayName, focusC
|
|
|
5957
5978
|
onSaveCommentEdit
|
|
5958
5979
|
]);
|
|
5959
5980
|
const handleBlur = useCallback(() => {
|
|
5960
|
-
onCommentBlur(
|
|
5981
|
+
onCommentBlur(comment, draft, flushDraft);
|
|
5961
5982
|
}, [
|
|
5983
|
+
comment,
|
|
5962
5984
|
draft,
|
|
5963
5985
|
flushDraft,
|
|
5964
5986
|
onCommentBlur
|
|
@@ -6590,6 +6612,10 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
6590
6612
|
const handledScrollRequestRef = useRef(null);
|
|
6591
6613
|
const handledHunkNavRef = useRef(hunkNavigation?.request ?? null);
|
|
6592
6614
|
const emptyCommentDeleteTimersRef = useRef(/* @__PURE__ */ new Map());
|
|
6615
|
+
const activePointerIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
6616
|
+
const pendingCommentBlursRef = useRef(/* @__PURE__ */ new Map());
|
|
6617
|
+
const pendingCommentBlurTimerRef = useRef(null);
|
|
6618
|
+
const focusedCommentEditorIdRef = useRef(null);
|
|
6593
6619
|
const definitionHighlightFrameRef = useRef(null);
|
|
6594
6620
|
const highlightFrameRef = useRef(null);
|
|
6595
6621
|
const ignoreNextLineSelectionEndRef = useRef(false);
|
|
@@ -6939,6 +6965,66 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
6939
6965
|
}
|
|
6940
6966
|
emptyCommentDeleteTimersRef.current.clear();
|
|
6941
6967
|
}, []);
|
|
6968
|
+
const flushPendingCommentBlurs = useCallback(() => {
|
|
6969
|
+
if (activePointerIdsRef.current.size > 0) return;
|
|
6970
|
+
const callbacks = [...pendingCommentBlursRef.current.values()];
|
|
6971
|
+
pendingCommentBlursRef.current.clear();
|
|
6972
|
+
for (const callback of callbacks) callback();
|
|
6973
|
+
}, []);
|
|
6974
|
+
const schedulePendingCommentBlurFlush = useCallback(() => {
|
|
6975
|
+
if (activePointerIdsRef.current.size > 0 || pendingCommentBlursRef.current.size === 0 || pendingCommentBlurTimerRef.current != null) return;
|
|
6976
|
+
const timer = window.setTimeout(() => {
|
|
6977
|
+
deferredTimersRef.current.delete(timer);
|
|
6978
|
+
pendingCommentBlurTimerRef.current = null;
|
|
6979
|
+
flushPendingCommentBlurs();
|
|
6980
|
+
}, 0);
|
|
6981
|
+
pendingCommentBlurTimerRef.current = timer;
|
|
6982
|
+
deferredTimersRef.current.add(timer);
|
|
6983
|
+
}, [flushPendingCommentBlurs]);
|
|
6984
|
+
useEffect(() => {
|
|
6985
|
+
const activePointerIds = activePointerIdsRef.current;
|
|
6986
|
+
const deferredTimers = deferredTimersRef.current;
|
|
6987
|
+
const pendingCommentBlurs = pendingCommentBlursRef.current;
|
|
6988
|
+
const handlePointerDown = (event) => {
|
|
6989
|
+
const timer = pendingCommentBlurTimerRef.current;
|
|
6990
|
+
if (timer != null) {
|
|
6991
|
+
window.clearTimeout(timer);
|
|
6992
|
+
deferredTimers.delete(timer);
|
|
6993
|
+
pendingCommentBlurTimerRef.current = null;
|
|
6994
|
+
}
|
|
6995
|
+
activePointerIds.add(event.pointerId);
|
|
6996
|
+
};
|
|
6997
|
+
const handlePointerEnd = (event) => {
|
|
6998
|
+
activePointerIds.delete(event.pointerId);
|
|
6999
|
+
schedulePendingCommentBlurFlush();
|
|
7000
|
+
};
|
|
7001
|
+
const handleWindowBlur = () => {
|
|
7002
|
+
activePointerIds.clear();
|
|
7003
|
+
schedulePendingCommentBlurFlush();
|
|
7004
|
+
};
|
|
7005
|
+
window.addEventListener("pointerdown", handlePointerDown, true);
|
|
7006
|
+
window.addEventListener("pointercancel", handlePointerEnd, true);
|
|
7007
|
+
window.addEventListener("pointerup", handlePointerEnd, true);
|
|
7008
|
+
window.addEventListener("blur", handleWindowBlur);
|
|
7009
|
+
return () => {
|
|
7010
|
+
window.removeEventListener("pointerdown", handlePointerDown, true);
|
|
7011
|
+
window.removeEventListener("pointercancel", handlePointerEnd, true);
|
|
7012
|
+
window.removeEventListener("pointerup", handlePointerEnd, true);
|
|
7013
|
+
window.removeEventListener("blur", handleWindowBlur);
|
|
7014
|
+
activePointerIds.clear();
|
|
7015
|
+
pendingCommentBlurs.clear();
|
|
7016
|
+
const timer = pendingCommentBlurTimerRef.current;
|
|
7017
|
+
if (timer != null) {
|
|
7018
|
+
window.clearTimeout(timer);
|
|
7019
|
+
deferredTimers.delete(timer);
|
|
7020
|
+
pendingCommentBlurTimerRef.current = null;
|
|
7021
|
+
}
|
|
7022
|
+
};
|
|
7023
|
+
}, [schedulePendingCommentBlurFlush]);
|
|
7024
|
+
const deferCommentBlurUntilPointerEnd = useCallback((commentId, callback) => {
|
|
7025
|
+
if (activePointerIdsRef.current.size > 0 || pendingCommentBlurTimerRef.current != null) pendingCommentBlursRef.current.set(commentId, callback);
|
|
7026
|
+
else callback();
|
|
7027
|
+
}, []);
|
|
6942
7028
|
const scrollFileItemToTop = useCallback((itemId) => {
|
|
6943
7029
|
const handle = codeViewRef.current;
|
|
6944
7030
|
const viewer = handle?.getInstance();
|
|
@@ -7034,10 +7120,10 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7034
7120
|
createCommentForRange(range, context);
|
|
7035
7121
|
},
|
|
7036
7122
|
onLineClick: (line, context) => {
|
|
7123
|
+
const lineElement = "lineElement" in line ? line.lineElement : null;
|
|
7037
7124
|
if (isPrimaryModifier(line.event) && onFindDefinitions) {
|
|
7038
7125
|
line.event.preventDefault();
|
|
7039
7126
|
line.event.stopPropagation();
|
|
7040
|
-
const lineElement = "lineElement" in line ? line.lineElement : null;
|
|
7041
7127
|
const identifier = lineElement ? getIdentifierFromPointerEvent(line.event, lineElement) : null;
|
|
7042
7128
|
const meta = itemMetadata.get(context.item.id);
|
|
7043
7129
|
const side = "annotationSide" in line ? line.annotationSide : null;
|
|
@@ -7086,7 +7172,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7086
7172
|
onLoadSection(meta.file, meta.section);
|
|
7087
7173
|
return;
|
|
7088
7174
|
}
|
|
7089
|
-
if (hasActiveTextSelection()) return;
|
|
7175
|
+
if (hasActiveTextSelection(lineElement)) return;
|
|
7090
7176
|
const side = "annotationSide" in line ? line.annotationSide : null;
|
|
7091
7177
|
if (!side) return;
|
|
7092
7178
|
cancelPendingEmptyCommentDeletes();
|
|
@@ -7147,6 +7233,8 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7147
7233
|
wordWrap
|
|
7148
7234
|
]);
|
|
7149
7235
|
const focusComment = useCallback((comment) => {
|
|
7236
|
+
focusedCommentEditorIdRef.current = comment.id;
|
|
7237
|
+
pendingCommentBlursRef.current.delete(comment.id);
|
|
7150
7238
|
onCommentDraftChange?.({
|
|
7151
7239
|
body: comment.body,
|
|
7152
7240
|
id: comment.id
|
|
@@ -7157,25 +7245,32 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7157
7245
|
deferredTimersRef.current.delete(timer);
|
|
7158
7246
|
emptyCommentDeleteTimersRef.current.delete(comment.id);
|
|
7159
7247
|
}, [onCommentDraftChange]);
|
|
7160
|
-
const blurComment = useCallback((comment, body) => {
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
if (existingTimer != null) {
|
|
7166
|
-
window.clearTimeout(existingTimer);
|
|
7167
|
-
deferredTimersRef.current.delete(existingTimer);
|
|
7248
|
+
const blurComment = useCallback((comment, body, flushDraft) => {
|
|
7249
|
+
deferCommentBlurUntilPointerEnd(comment.id, () => {
|
|
7250
|
+
if (focusedCommentEditorIdRef.current === comment.id) {
|
|
7251
|
+
focusedCommentEditorIdRef.current = null;
|
|
7252
|
+
onCommentDraftChange?.(null);
|
|
7168
7253
|
}
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7254
|
+
flushDraft();
|
|
7255
|
+
clearCommentLineHighlight();
|
|
7256
|
+
if (!comment.isReadOnly && body.trim().length === 0) {
|
|
7257
|
+
const existingTimer = emptyCommentDeleteTimersRef.current.get(comment.id);
|
|
7258
|
+
if (existingTimer != null) {
|
|
7259
|
+
window.clearTimeout(existingTimer);
|
|
7260
|
+
deferredTimersRef.current.delete(existingTimer);
|
|
7261
|
+
}
|
|
7262
|
+
const timer = window.setTimeout(() => {
|
|
7263
|
+
deferredTimersRef.current.delete(timer);
|
|
7264
|
+
emptyCommentDeleteTimersRef.current.delete(comment.id);
|
|
7265
|
+
onDeleteComment(comment.id);
|
|
7266
|
+
}, 120);
|
|
7267
|
+
deferredTimersRef.current.add(timer);
|
|
7268
|
+
emptyCommentDeleteTimersRef.current.set(comment.id, timer);
|
|
7269
|
+
}
|
|
7270
|
+
});
|
|
7177
7271
|
}, [
|
|
7178
7272
|
clearCommentLineHighlight,
|
|
7273
|
+
deferCommentBlurUntilPointerEnd,
|
|
7179
7274
|
onCommentDraftChange,
|
|
7180
7275
|
onDeleteComment
|
|
7181
7276
|
]);
|
|
@@ -7883,6 +7978,12 @@ function resolveBackdrop(element) {
|
|
|
7883
7978
|
}
|
|
7884
7979
|
return "#00000000";
|
|
7885
7980
|
}
|
|
7981
|
+
let ghosttyModule = null;
|
|
7982
|
+
const loadGhostty = async () => {
|
|
7983
|
+
const ghostty = await (ghosttyModule ??= import("ghostty-web"));
|
|
7984
|
+
await ghostty.init();
|
|
7985
|
+
return ghostty;
|
|
7986
|
+
};
|
|
7886
7987
|
/**
|
|
7887
7988
|
* Read-only ghostty-web terminal that replays the streamed commit output, so
|
|
7888
7989
|
* ANSI colors and cursor movement from pre-commit hooks render as they would
|
|
@@ -7897,7 +7998,7 @@ function CommitLogTerminal({ output }) {
|
|
|
7897
7998
|
if (!container) return;
|
|
7898
7999
|
let disposed = false;
|
|
7899
8000
|
let instance = null;
|
|
7900
|
-
|
|
8001
|
+
loadGhostty().then(({ Terminal }) => {
|
|
7901
8002
|
if (disposed) return;
|
|
7902
8003
|
const style = getComputedStyle(container);
|
|
7903
8004
|
instance = new Terminal({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nkzw/codiff-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "A fast diff viewer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@nkzw/mdx-editor": "^0.
|
|
54
|
+
"@nkzw/mdx-editor": "^1.0.1",
|
|
55
55
|
"@nkzw/use-relative-time": "^1.2.3",
|
|
56
56
|
"@phosphor-icons/react": "^2.1.10",
|
|
57
57
|
"@pierre/diffs": "1.3.6",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@radix-ui/react-slot": "^1.3.3",
|
|
60
60
|
"class-variance-authority": "^0.7.1",
|
|
61
61
|
"ghostty-web": "^0.4.0",
|
|
62
|
-
"lucide-react": "^1.
|
|
62
|
+
"lucide-react": "^1.39.0",
|
|
63
63
|
"valibot": "^1.4.2"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|