@adecore/agents-react 0.22.1 → 0.23.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/dist/chat/ui/EditDiff.d.ts +3 -1
- package/dist/chat/ui/EditDiff.js +25 -18
- package/dist/chat/ui/UnifiedDiff.d.ts +2 -11
- package/dist/chat/ui/UnifiedDiff.js +31 -58
- package/dist/chat/ui/diff/ApprovalReview.d.ts +5 -0
- package/dist/chat/ui/diff/ApprovalReview.js +29 -0
- package/dist/chat/ui/diff/DiffFallbackBoundary.d.ts +17 -0
- package/dist/chat/ui/diff/DiffFallbackBoundary.js +17 -0
- package/dist/chat/ui/diff/DiffOmitted.d.ts +3 -0
- package/dist/chat/ui/diff/DiffOmitted.js +8 -0
- package/dist/chat/ui/diff/DiffStack.d.ts +9 -0
- package/dist/chat/ui/diff/DiffStack.js +15 -0
- package/dist/chat/ui/diff/DiffUnavailable.d.ts +3 -0
- package/dist/chat/ui/diff/DiffUnavailable.js +8 -0
- package/dist/chat/ui/diff/EditorEditDiff.d.ts +23 -0
- package/dist/chat/ui/diff/EditorEditDiff.js +67 -0
- package/dist/chat/ui/diff/EditorPatchDiff.d.ts +16 -0
- package/dist/chat/ui/diff/EditorPatchDiff.js +20 -0
- package/dist/chat/ui/diff/LegacyDiffPool.d.ts +4 -0
- package/dist/chat/ui/diff/LegacyDiffPool.js +12 -0
- package/dist/chat/ui/diff/LegacyEditDiff.d.ts +5 -0
- package/dist/chat/ui/diff/LegacyEditDiff.js +23 -0
- package/dist/chat/ui/diff/LegacyUnifiedDiff.d.ts +2 -0
- package/dist/chat/ui/diff/LegacyUnifiedDiff.js +67 -0
- package/dist/chat/ui/diff/RawDiff.d.ts +6 -0
- package/dist/chat/ui/diff/RawDiff.js +7 -0
- package/dist/chat/ui/diff/ReviewDiff.d.ts +5 -0
- package/dist/chat/ui/diff/ReviewDiff.js +10 -0
- package/dist/chat/ui/diff/diff-props.d.ts +12 -0
- package/dist/chat/ui/diff/diff-props.js +1 -0
- package/dist/chat/ui/diff/diff-source-budget.d.ts +10 -0
- package/dist/chat/ui/diff/diff-source-budget.js +47 -0
- package/dist/chat/ui/diff/editor-edit-model.d.ts +8 -0
- package/dist/chat/ui/diff/editor-edit-model.js +19 -0
- package/dist/chat/ui/diff/review-plan.d.ts +28 -0
- package/dist/chat/ui/diff/review-plan.js +48 -0
- package/dist/chat/ui/diff/review-settle.d.ts +5 -0
- package/dist/chat/ui/diff/review-settle.js +12 -0
- package/dist/chat/ui/diff/syntax-runtime.d.ts +18 -0
- package/dist/chat/ui/diff/syntax-runtime.js +184 -0
- package/dist/chat/ui/diff/use-review-stack.d.ts +8 -0
- package/dist/chat/ui/diff/use-review-stack.js +30 -0
- package/dist/chat/ui/diff-syntax.d.ts +19 -0
- package/dist/chat/ui/diff-syntax.js +63 -0
- package/dist/chat/ui/rows/WorkRows.js +16 -5
- package/dist/chat/ui/use-diff-source-tokens.d.ts +4 -0
- package/dist/chat/ui/use-diff-source-tokens.js +40 -0
- package/dist/host.d.ts +6 -1
- package/dist/lazy.d.ts +3 -1
- package/dist/lazy.js +5 -3
- package/dist/locales/en/agent-chat.json +3 -0
- package/dist/locales/en/agent-prompts.json +1 -0
- package/dist/locales/nl/agent-chat.json +3 -0
- package/dist/locales/nl/agent-prompts.json +1 -0
- package/dist/prompts/ui/ApprovalBody.js +7 -5
- package/package.json +18 -5
- package/src/chat/ui/EditDiff.tsx +37 -22
- package/src/chat/ui/UnifiedDiff.tsx +55 -88
- package/src/chat/ui/diff/ApprovalReview.tsx +53 -0
- package/src/chat/ui/diff/DiffFallbackBoundary.tsx +32 -0
- package/src/chat/ui/diff/DiffOmitted.tsx +13 -0
- package/src/chat/ui/diff/DiffStack.tsx +45 -0
- package/src/chat/ui/diff/DiffUnavailable.tsx +13 -0
- package/src/chat/ui/diff/EditorEditDiff.tsx +145 -0
- package/src/chat/ui/diff/EditorPatchDiff.tsx +60 -0
- package/src/chat/ui/diff/LegacyDiffPool.tsx +13 -0
- package/src/chat/ui/diff/LegacyEditDiff.tsx +33 -0
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +93 -0
- package/src/chat/ui/diff/RawDiff.tsx +19 -0
- package/src/chat/ui/diff/ReviewDiff.tsx +22 -0
- package/src/chat/ui/diff/diff-props.ts +16 -0
- package/src/chat/ui/diff/diff-source-budget.ts +56 -0
- package/src/chat/ui/diff/editor-edit-model.ts +21 -0
- package/src/chat/ui/diff/review-plan.ts +69 -0
- package/src/chat/ui/diff/review-settle.ts +14 -0
- package/src/chat/ui/diff/syntax-runtime.ts +222 -0
- package/src/chat/ui/diff/use-review-stack.ts +38 -0
- package/src/chat/ui/diff-syntax.ts +82 -0
- package/src/chat/ui/rows/WorkRows.tsx +42 -33
- package/src/chat/ui/use-diff-source-tokens.ts +43 -0
- package/src/host.ts +10 -1
- package/src/lazy.tsx +6 -3
- package/src/locales/en/agent-chat.json +3 -0
- package/src/locales/en/agent-prompts.json +1 -0
- package/src/locales/nl/agent-chat.json +3 -0
- package/src/locales/nl/agent-prompts.json +1 -0
- package/src/prompts/ui/ApprovalBody.tsx +8 -14
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { FileChange } from '../logic/tools.js';
|
|
2
|
-
export default function EditDiff({ change }: {
|
|
2
|
+
export default function EditDiff({ change, allowEditor, maxHeight }: {
|
|
3
3
|
change: FileChange;
|
|
4
|
+
allowEditor?: boolean;
|
|
5
|
+
maxHeight?: number;
|
|
4
6
|
}): import("react").JSX.Element;
|
package/dist/chat/ui/EditDiff.js
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
import {
|
|
2
|
+
import { Suspense, useMemo } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { lazyNamed } from '../../lazy.js';
|
|
5
|
+
import { diffSourceBudget, legacyDiffBudget } from './diff/diff-source-budget.js';
|
|
6
|
+
import { DiffOmitted } from './diff/DiffOmitted.js';
|
|
7
|
+
import { DiffUnavailable } from './diff/DiffUnavailable.js';
|
|
8
|
+
import { DiffFallbackBoundary } from './diff/DiffFallbackBoundary.js';
|
|
4
9
|
import { chatHost } from '../../host.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export default function EditDiff({ change }) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
const LegacyEditDiff = lazyNamed(() => import('./diff/LegacyEditDiff.js'), 'default', { prefetch: false });
|
|
11
|
+
const EditorEditDiff = lazyNamed(() => import('./diff/EditorEditDiff.js'), 'default');
|
|
12
|
+
export default function EditDiff({ change, allowEditor = true, maxHeight }) {
|
|
13
|
+
const { t } = useTranslation('agent-chat');
|
|
14
|
+
const loadEngine = chatHost().code.editor;
|
|
15
|
+
const sourceAllowed = useMemo(() => diffSourceBudget(change.before, change.after), [change.before, change.after]);
|
|
16
|
+
const legacyMode = !allowEditor || loadEngine === undefined;
|
|
17
|
+
const legacyAllowed = useMemo(() => legacyMode && legacyDiffBudget(change.before, change.after), [legacyMode, change.before, change.after]);
|
|
18
|
+
const omitted = _jsx(DiffOmitted, { path: change.path });
|
|
19
|
+
const fallback = legacyAllowed ? (_jsx(DiffFallbackBoundary, { fallback: omitted, resetKeys: [change.path, change.before, change.after], children: _jsx(Suspense, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: _jsx(LegacyEditDiff, { change: change, maxHeight: maxHeight }) }) })) : (omitted);
|
|
20
|
+
if (!sourceAllowed) {
|
|
21
|
+
return omitted;
|
|
22
|
+
}
|
|
23
|
+
if (legacyMode) {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
const unavailable = _jsx(DiffUnavailable, { path: change.path });
|
|
27
|
+
return (_jsx(DiffFallbackBoundary, { fallback: unavailable, resetKeys: [change.path, change.before, change.after, loadEngine], children: _jsx(Suspense, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: _jsx(EditorEditDiff, { change: change, loadEngine: loadEngine, fallback: unavailable, maxHeight: maxHeight, sourceChecked: true }) }) }));
|
|
21
28
|
}
|
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
interface UnifiedDiffProps {
|
|
4
|
-
change: ChatFileChange;
|
|
5
|
-
overflow?: 'wrap' | 'scroll';
|
|
6
|
-
diffStyle?: 'unified' | 'split';
|
|
7
|
-
fill?: boolean;
|
|
8
|
-
contents?: DiffContents;
|
|
9
|
-
}
|
|
10
|
-
export default function UnifiedDiff({ change, overflow, diffStyle, fill, contents }: UnifiedDiffProps): import("react").JSX.Element;
|
|
11
|
-
export {};
|
|
1
|
+
import type { UnifiedDiffProps } from './diff/diff-props.js';
|
|
2
|
+
export default function UnifiedDiff(props: UnifiedDiffProps): import("react").JSX.Element;
|
|
@@ -1,64 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
2
|
+
import { Suspense, useMemo } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { lazyNamed } from '../../lazy.js';
|
|
5
|
+
import { diffSourceBudget, legacyDiffBudget, patchSourceBudget } from './diff/diff-source-budget.js';
|
|
6
|
+
import { DiffOmitted } from './diff/DiffOmitted.js';
|
|
7
|
+
import { DiffUnavailable } from './diff/DiffUnavailable.js';
|
|
8
|
+
import { RawDiff } from './diff/RawDiff.js';
|
|
9
|
+
import { DiffFallbackBoundary } from './diff/DiffFallbackBoundary.js';
|
|
5
10
|
import { chatHost } from '../../host.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const LegacyUnifiedDiff = lazyNamed(() => import('./diff/LegacyUnifiedDiff.js'), 'default', { prefetch: false });
|
|
12
|
+
const EditorPatchDiff = lazyNamed(() => import('./diff/EditorPatchDiff.js'), 'default');
|
|
13
|
+
export default function UnifiedDiff(props) {
|
|
14
|
+
const { t } = useTranslation('agent-chat');
|
|
15
|
+
const { change, contents, allowEditor = true, ignoreWhitespace = false, overflow = 'wrap', diffStyle = 'unified', fill = false, maxHeight } = props;
|
|
16
|
+
const loadEngine = chatHost().code.editor;
|
|
17
|
+
const oldText = contents?.old ?? '';
|
|
18
|
+
const newText = contents?.new ?? '';
|
|
19
|
+
const sourceAllowed = useMemo(() => diffSourceBudget(oldText, newText) && patchSourceBudget(change.diff), [oldText, newText, change.diff]);
|
|
20
|
+
const legacyMode = !allowEditor || loadEngine === undefined;
|
|
21
|
+
const hasHunks = /^@@/m.test(change.diff);
|
|
22
|
+
const legacyAllowed = useMemo(() => legacyMode && legacyDiffBudget(oldText, newText, change.diff), [legacyMode, oldText, newText, change.diff]);
|
|
23
|
+
const rawAllowed = useMemo(() => hasHunks || legacyDiffBudget('', '', change.diff), [hasHunks, change.diff]);
|
|
24
|
+
const omitted = _jsx(DiffOmitted, { path: change.path });
|
|
25
|
+
const fallback = legacyAllowed ? (_jsx(DiffFallbackBoundary, { fallback: omitted, resetKeys: [change.path, change.diff, oldText, newText, diffStyle], children: _jsx(Suspense, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: _jsx(LegacyUnifiedDiff, { ...props }) }) })) : (omitted);
|
|
26
|
+
if (!sourceAllowed) {
|
|
27
|
+
return omitted;
|
|
15
28
|
}
|
|
16
|
-
if (
|
|
17
|
-
return change.diff;
|
|
29
|
+
if (!hasHunks) {
|
|
30
|
+
return rawAllowed ? _jsx(RawDiff, { text: change.diff, overflow: overflow, fill: fill, maxHeight: maxHeight }) : omitted;
|
|
18
31
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return `--- ${from}\n+++ ${to}\n${change.diff}`;
|
|
22
|
-
}
|
|
23
|
-
/* Lets a diff with a view of its own run to its bottom, so a short one's scrollbar sits there and not under its last line. */
|
|
24
|
-
const FILL_CSS = `
|
|
25
|
-
:host { display: flex; flex-direction: column; flex-grow: 1; }
|
|
26
|
-
pre { flex-grow: 1; align-content: start; }
|
|
27
|
-
pre[data-diff-type="single"] { display: flex; flex-direction: column; }
|
|
28
|
-
pre[data-diff-type="split"][data-overflow="scroll"]:not([data-dehydrated]) { align-content: stretch; }
|
|
29
|
-
pre > [data-code] { flex-grow: 1; align-self: stretch; align-content: start; }
|
|
30
|
-
`;
|
|
31
|
-
function lineClass(line) {
|
|
32
|
-
if (line.startsWith('+')) {
|
|
33
|
-
return 'text-chat-added';
|
|
34
|
-
}
|
|
35
|
-
if (line.startsWith('-')) {
|
|
36
|
-
return 'text-chat-removed';
|
|
37
|
-
}
|
|
38
|
-
return 'text-text-muted';
|
|
39
|
-
}
|
|
40
|
-
export default function UnifiedDiff({ change, overflow = 'wrap', diffStyle = 'unified', fill = false, contents }) {
|
|
41
|
-
const resolved = chatHost().code.useMode();
|
|
42
|
-
const theme = useDiffTheme();
|
|
43
|
-
const patch = useMemo(() => asPatch(change), [change]);
|
|
44
|
-
const oldText = contents?.old;
|
|
45
|
-
const newText = contents?.new;
|
|
46
|
-
const full = useMemo(() => (patch === null || oldText === undefined || newText === undefined ? null : fullFileDiff(patch, change.path, { old: oldText, new: newText })), [patch, change.path, oldText, newText]);
|
|
47
|
-
const unfolds = full !== null;
|
|
48
|
-
const options = useMemo(() => ({
|
|
49
|
-
theme,
|
|
50
|
-
themeType: resolved,
|
|
51
|
-
disableFileHeader: true,
|
|
52
|
-
diffStyle,
|
|
53
|
-
overflow,
|
|
54
|
-
hunkSeparators: unfolds ? 'line-info' : 'simple',
|
|
55
|
-
unsafeCSS: fill ? FILL_CSS : undefined
|
|
56
|
-
}), [diffStyle, overflow, theme, resolved, fill, unfolds]);
|
|
57
|
-
if (patch === null) {
|
|
58
|
-
return (_jsx("pre", { className: "max-h-64 overflow-auto px-3 py-2 font-mono text-code select-text", children: change.diff
|
|
59
|
-
.replace(/\n$/, '')
|
|
60
|
-
.split('\n')
|
|
61
|
-
.map((line, index) => (_jsx("div", { className: clsx(lineClass(line), 'whitespace-pre-wrap'), children: line }, index))) }));
|
|
32
|
+
if (legacyMode) {
|
|
33
|
+
return fallback;
|
|
62
34
|
}
|
|
63
|
-
|
|
35
|
+
const unavailable = _jsx(DiffUnavailable, { path: change.path });
|
|
36
|
+
return (_jsx(DiffFallbackBoundary, { fallback: unavailable, resetKeys: [change.path, change.diff, oldText, newText, ignoreWhitespace, diffStyle, loadEngine], children: _jsx(Suspense, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: _jsx(EditorPatchDiff, { change: change, sourceChecked: true, contents: contents, ignoreWhitespace: ignoreWhitespace, wrap: overflow === 'wrap', fill: fill, maxHeight: maxHeight, layout: diffStyle, loadEngine: loadEngine, fallback: unavailable }) }) }));
|
|
64
37
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Button } from '@adecore/ui';
|
|
5
|
+
import { formatNumber } from '@adecore/ui/format';
|
|
6
|
+
import { ReviewDiff } from './ReviewDiff.js';
|
|
7
|
+
export function ApprovalReview({ entries, maxHeight }) {
|
|
8
|
+
const { t } = useTranslation('agent-prompts');
|
|
9
|
+
const [selected, setSelected] = useState(null);
|
|
10
|
+
const active = entries.find((entry) => entry.key === selected) ?? entries[0];
|
|
11
|
+
const labels = useMemo(() => {
|
|
12
|
+
const totals = new Map();
|
|
13
|
+
const counts = new Map();
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
totals.set(entry.path, (totals.get(entry.path) ?? 0) + 1);
|
|
16
|
+
}
|
|
17
|
+
return entries.map((entry) => {
|
|
18
|
+
const count = (counts.get(entry.path) ?? 0) + 1;
|
|
19
|
+
counts.set(entry.path, count);
|
|
20
|
+
const total = totals.get(entry.path);
|
|
21
|
+
const suffix = total > 1 ? ` (${formatNumber(count)}/${formatNumber(total)})` : '';
|
|
22
|
+
return { short: `${entry.path.split(/[\\/]/).at(-1)}${suffix}`, full: `${entry.path}${suffix}` };
|
|
23
|
+
});
|
|
24
|
+
}, [entries]);
|
|
25
|
+
if (active === undefined) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return (_jsxs("div", { children: [entries.length > 1 && (_jsxs(_Fragment, { children: [_jsx("div", { role: "toolbar", "aria-label": t('approval.reviewChanges'), className: "flex flex-wrap gap-1 border-b border-border p-1", children: entries.map((entry, index) => (_jsx(Button, { size: "sm", variant: active.key === entry.key ? 'secondary' : 'ghost', "aria-label": labels[index].full, "aria-pressed": active.key === entry.key, onClick: () => setSelected(entry.key), children: labels[index].short }, entry.key))) }), _jsx("p", { className: "break-all px-3 py-2 font-mono text-xs text-text-muted", children: active.path })] })), _jsx(ReviewDiff, { entry: active, maxHeight: maxHeight }, active.key)] }));
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Component, type ReactNode } from 'react';
|
|
2
|
+
interface Props {
|
|
3
|
+
fallback: ReactNode;
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
resetKeys: readonly unknown[];
|
|
6
|
+
}
|
|
7
|
+
interface State {
|
|
8
|
+
failed: boolean;
|
|
9
|
+
resetKeys: readonly unknown[];
|
|
10
|
+
}
|
|
11
|
+
export declare class DiffFallbackBoundary extends Component<Props, State> {
|
|
12
|
+
state: State;
|
|
13
|
+
static getDerivedStateFromError(): Partial<State>;
|
|
14
|
+
static getDerivedStateFromProps(props: Props, state: State): State | null;
|
|
15
|
+
render(): ReactNode;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
/* A failed view or lazy chunk must leave the containing review card usable. */
|
|
3
|
+
export class DiffFallbackBoundary extends Component {
|
|
4
|
+
state = { failed: false, resetKeys: this.props.resetKeys };
|
|
5
|
+
static getDerivedStateFromError() {
|
|
6
|
+
return { failed: true };
|
|
7
|
+
}
|
|
8
|
+
static getDerivedStateFromProps(props, state) {
|
|
9
|
+
if (state.resetKeys.length !== props.resetKeys.length || state.resetKeys.some((value, index) => !Object.is(value, props.resetKeys[index]))) {
|
|
10
|
+
return { failed: false, resetKeys: props.resetKeys };
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
render() {
|
|
15
|
+
return this.state.failed ? this.props.fallback : this.props.children;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useReviewSettled } from './review-settle.js';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
export function DiffOmitted({ path }) {
|
|
5
|
+
useReviewSettled();
|
|
6
|
+
const { t } = useTranslation('agent-chat');
|
|
7
|
+
return (_jsxs("div", { role: "status", "data-diff-omitted": true, className: "px-3 py-2 text-xs text-text-muted", children: [_jsx("p", { className: "truncate font-mono", children: path }), _jsx("p", { children: t('work.omitted.tooLarge') })] }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ReviewEntry } from './review-plan.js';
|
|
2
|
+
import { type ReviewStack } from './use-review-stack.js';
|
|
3
|
+
export declare function ReviewStackView({ entries, stack }: {
|
|
4
|
+
entries: readonly ReviewEntry[];
|
|
5
|
+
stack: ReviewStack;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
export declare function DiffStack({ entries }: {
|
|
8
|
+
entries: readonly ReviewEntry[];
|
|
9
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button } from '@adecore/ui';
|
|
4
|
+
import { ReviewSettleContext } from './review-settle.js';
|
|
5
|
+
import { ReviewDiff } from './ReviewDiff.js';
|
|
6
|
+
import { reviewSize } from './review-plan.js';
|
|
7
|
+
import { useReviewStack } from './use-review-stack.js';
|
|
8
|
+
export function ReviewStackView({ entries, stack }) {
|
|
9
|
+
const { t } = useTranslation('agent-chat');
|
|
10
|
+
return (_jsx("div", { "data-review-stack": true, children: entries.map((entry) => (_jsxs("div", { className: "border-t border-border first:border-t-0", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [_jsx("p", { className: "min-w-0 grow break-all font-mono text-xs text-text-muted", children: entry.path }), stack.slots.get(entry.key) === 'deferred' && (_jsx(Button, { size: "sm", "data-review-show": entry.key, onClick: () => stack.promote(entry.key), children: t('work.showDiff') }))] }), stack.ready.has(entry.key) ? (_jsx(ReviewSettleContext, { value: { key: entry.key, settle: stack.settle }, children: _jsx(ReviewDiff, { entry: entry }) })) : stack.slots.get(entry.key) === 'editor' ? (_jsx("div", { "data-review-pending": entry.key, role: "status", className: "px-3 py-2 text-xs text-text-faint", style: { height: Math.max(28, Math.min(reviewSize(entry).rows * 20, 480)) }, children: t('work.loadingDiff') })) : null] }, entry.key))) }));
|
|
11
|
+
}
|
|
12
|
+
export function DiffStack({ entries }) {
|
|
13
|
+
const stack = useReviewStack(entries);
|
|
14
|
+
return _jsx(ReviewStackView, { entries: entries, stack: stack });
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { useReviewSettled } from './review-settle.js';
|
|
4
|
+
export function DiffUnavailable({ path }) {
|
|
5
|
+
useReviewSettled();
|
|
6
|
+
const { t } = useTranslation('agent-chat');
|
|
7
|
+
return (_jsxs("div", { role: "status", "data-diff-unavailable": true, className: "px-3 py-2 text-xs text-text-muted", children: [_jsx("p", { className: "truncate font-mono", children: path }), _jsx("p", { children: t('work.diffUnavailable') })] }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { EditorEngine } from '@adecore/editor';
|
|
3
|
+
import { type DiffDocument } from '@adecore/merge';
|
|
4
|
+
import type { FileChange } from '../../logic/tools.js';
|
|
5
|
+
type EngineLoader = () => Promise<EditorEngine>;
|
|
6
|
+
export declare function EditorDocumentDiff({ document, path, loadEngine, fallback, wrap, fill, layout, maxHeight }: {
|
|
7
|
+
document: DiffDocument;
|
|
8
|
+
path: string;
|
|
9
|
+
loadEngine: EngineLoader;
|
|
10
|
+
fallback: ReactNode;
|
|
11
|
+
wrap?: boolean;
|
|
12
|
+
fill?: boolean;
|
|
13
|
+
layout?: 'unified' | 'split';
|
|
14
|
+
maxHeight?: number;
|
|
15
|
+
}): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
|
|
16
|
+
export default function EditorEditDiff({ change, loadEngine, fallback, maxHeight, sourceChecked }: {
|
|
17
|
+
change: FileChange;
|
|
18
|
+
sourceChecked?: boolean;
|
|
19
|
+
loadEngine: EngineLoader;
|
|
20
|
+
fallback: ReactNode;
|
|
21
|
+
maxHeight?: number;
|
|
22
|
+
}): import("react").JSX.Element;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useReviewSettled } from './review-settle.js';
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { getFiletypeFromFileName } from '@pierre/diffs';
|
|
6
|
+
import { DiffView } from '@adecore/editor-react';
|
|
7
|
+
import { diffDocument, patchOf } from '@adecore/merge';
|
|
8
|
+
import { chatHost } from '../../../host.js';
|
|
9
|
+
import { useDiffSourceTokens } from '../use-diff-source-tokens.js';
|
|
10
|
+
import { diffSourceBudget } from './diff-source-budget.js';
|
|
11
|
+
import { DiffOmitted } from './DiffOmitted.js';
|
|
12
|
+
import { canRenderEditorDiff, EDITOR_DIFF_LIMITS, EDITOR_DIFF_WORK } from './editor-edit-model.js';
|
|
13
|
+
// Virtualized rows remount; retain the engine so their height does not return to the loading placeholder.
|
|
14
|
+
const engines = new WeakMap();
|
|
15
|
+
function ReadyDiff({ document, path, engine, wrap, fill, layout, maxHeight }) {
|
|
16
|
+
useReviewSettled();
|
|
17
|
+
const { t } = useTranslation('agent-chat');
|
|
18
|
+
const mode = chatHost().code.useMode();
|
|
19
|
+
const theme = chatHost().code.useThemes()[mode];
|
|
20
|
+
const language = getFiletypeFromFileName(path);
|
|
21
|
+
const tokens = useDiffSourceTokens(document, language, theme);
|
|
22
|
+
return (_jsx(DiffView, { engine: engine, document: document, tokens: tokens, theme: theme, maxHeight: maxHeight, unfoldLimits: EDITOR_DIFF_LIMITS, label: t('work.diffLabel', { path }), layout: layout, wrap: wrap, fill: fill, className: "chat-diff select-text" }));
|
|
23
|
+
}
|
|
24
|
+
export function EditorDocumentDiff({ document, path, loadEngine, fallback, wrap = true, fill = false, layout = 'unified', maxHeight = 480 }) {
|
|
25
|
+
const { t } = useTranslation('agent-chat');
|
|
26
|
+
const withinLimits = useMemo(() => canRenderEditorDiff(document), [document]);
|
|
27
|
+
const [loaded, setLoaded] = useState();
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!withinLimits || engines.has(loadEngine)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let active = true;
|
|
33
|
+
void Promise.resolve()
|
|
34
|
+
.then(loadEngine)
|
|
35
|
+
.then((engine) => {
|
|
36
|
+
engines.set(loadEngine, engine);
|
|
37
|
+
if (active) {
|
|
38
|
+
setLoaded({ load: loadEngine, engine });
|
|
39
|
+
}
|
|
40
|
+
}, () => {
|
|
41
|
+
engines.set(loadEngine, null);
|
|
42
|
+
if (active) {
|
|
43
|
+
setLoaded({ load: loadEngine, engine: null });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return () => {
|
|
47
|
+
active = false;
|
|
48
|
+
};
|
|
49
|
+
}, [loadEngine, withinLimits]);
|
|
50
|
+
const engine = loaded?.load === loadEngine ? loaded.engine : engines.get(loadEngine);
|
|
51
|
+
if (!withinLimits) {
|
|
52
|
+
return _jsx(DiffOmitted, { path: path });
|
|
53
|
+
}
|
|
54
|
+
if (engine === undefined) {
|
|
55
|
+
return _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') });
|
|
56
|
+
}
|
|
57
|
+
return engine === null ? (fallback) : (_jsx(ReadyDiff, { document: document, path: path, engine: engine, wrap: wrap, fill: fill, layout: layout, maxHeight: maxHeight }));
|
|
58
|
+
}
|
|
59
|
+
export default function EditorEditDiff({ change, loadEngine, fallback, maxHeight, sourceChecked = false }) {
|
|
60
|
+
const document = useMemo(() => sourceChecked || diffSourceBudget(change.before, change.after)
|
|
61
|
+
? diffDocument(patchOf(change.before, change.after, 3, { maxWork: EDITOR_DIFF_WORK }), { old: change.before, new: change.after })
|
|
62
|
+
: undefined, [change.before, change.after, sourceChecked]);
|
|
63
|
+
if (document === undefined) {
|
|
64
|
+
return _jsx(DiffOmitted, { path: change.path });
|
|
65
|
+
}
|
|
66
|
+
return _jsx(EditorDocumentDiff, { document: document, path: change.path, loadEngine: loadEngine, fallback: fallback, maxHeight: maxHeight });
|
|
67
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { ChatFileChange } from '@adecore/agent-contracts';
|
|
3
|
+
import type { EditorEngine } from '@adecore/editor';
|
|
4
|
+
import type { DiffContents } from '../full-diff.js';
|
|
5
|
+
export default function EditorPatchDiff({ change, sourceChecked, contents, ignoreWhitespace, wrap, fill, maxHeight, layout, loadEngine, fallback }: {
|
|
6
|
+
change: ChatFileChange;
|
|
7
|
+
sourceChecked?: boolean;
|
|
8
|
+
contents?: DiffContents;
|
|
9
|
+
ignoreWhitespace: boolean;
|
|
10
|
+
wrap: boolean;
|
|
11
|
+
fill: boolean;
|
|
12
|
+
layout?: 'unified' | 'split';
|
|
13
|
+
maxHeight?: number;
|
|
14
|
+
loadEngine: () => Promise<EditorEngine>;
|
|
15
|
+
fallback: ReactNode;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { diffDocument, parsePatch } from '@adecore/merge';
|
|
4
|
+
import { diffSourceBudget, patchSourceBudget } from './diff-source-budget.js';
|
|
5
|
+
import { DiffOmitted } from './DiffOmitted.js';
|
|
6
|
+
import { EditorDocumentDiff } from './EditorEditDiff.js';
|
|
7
|
+
export default function EditorPatchDiff({ change, sourceChecked = false, contents, ignoreWhitespace, wrap, fill, maxHeight, layout = 'unified', loadEngine, fallback }) {
|
|
8
|
+
const oldText = contents?.old;
|
|
9
|
+
const newText = contents?.new;
|
|
10
|
+
const document = useMemo(() => {
|
|
11
|
+
if (!sourceChecked && (!diffSourceBudget(oldText ?? '', newText ?? '') || !patchSourceBudget(change.diff))) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return diffDocument(parsePatch(change.diff), oldText === undefined || newText === undefined ? undefined : { old: oldText, new: newText }, ignoreWhitespace);
|
|
15
|
+
}, [change.diff, oldText, newText, ignoreWhitespace, sourceChecked]);
|
|
16
|
+
if (document === undefined) {
|
|
17
|
+
return _jsx(DiffOmitted, { path: change.path });
|
|
18
|
+
}
|
|
19
|
+
return (_jsx(EditorDocumentDiff, { document: document, path: change.path, loadEngine: loadEngine, fallback: fallback, wrap: wrap, fill: fill, layout: layout, maxHeight: maxHeight }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export declare function LegacyDiffPool({ children }: {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
}): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useWorkerPool } from '@pierre/diffs/react';
|
|
3
|
+
import { chatHost } from '../../../host.js';
|
|
4
|
+
import DiffPool from '../DiffPool.js';
|
|
5
|
+
export function LegacyDiffPool({ children }) {
|
|
6
|
+
const outer = useWorkerPool();
|
|
7
|
+
const workerFactory = chatHost().code.legacyDiffWorker;
|
|
8
|
+
if (outer !== undefined || workerFactory === undefined) {
|
|
9
|
+
return children;
|
|
10
|
+
}
|
|
11
|
+
return _jsx(DiffPool, { workerFactory: workerFactory, children: children });
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { LegacyDiffPool } from './LegacyDiffPool.js';
|
|
3
|
+
import { useReviewSettled } from './review-settle.js';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { MultiFileDiff } from '@pierre/diffs/react';
|
|
6
|
+
import { chatHost } from '../../../host.js';
|
|
7
|
+
import { useDiffTheme } from '../diff-theme.js';
|
|
8
|
+
export default function LegacyEditDiff({ change, maxHeight }) {
|
|
9
|
+
useReviewSettled();
|
|
10
|
+
const resolved = chatHost().code.useMode();
|
|
11
|
+
const theme = useDiffTheme();
|
|
12
|
+
const oldFile = useMemo(() => ({ name: change.path, contents: change.before }), [change.path, change.before]);
|
|
13
|
+
const newFile = useMemo(() => ({ name: change.path, contents: change.after }), [change.path, change.after]);
|
|
14
|
+
const options = useMemo(() => ({
|
|
15
|
+
diffStyle: 'unified',
|
|
16
|
+
theme,
|
|
17
|
+
themeType: resolved,
|
|
18
|
+
disableFileHeader: true,
|
|
19
|
+
overflow: 'wrap',
|
|
20
|
+
hunkSeparators: 'simple'
|
|
21
|
+
}), [theme, resolved]);
|
|
22
|
+
return (_jsx("div", { className: "chat-diff select-text", style: maxHeight === undefined ? undefined : { maxHeight, overflow: 'auto' }, children: _jsx(LegacyDiffPool, { children: _jsx(MultiFileDiff, { oldFile: oldFile, newFile: newFile, options: options }) }) }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { LegacyDiffPool } from './LegacyDiffPool.js';
|
|
3
|
+
import { useReviewSettled } from './review-settle.js';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
import { FileDiff, PatchDiff } from '@pierre/diffs/react';
|
|
7
|
+
import { chatHost } from '../../../host.js';
|
|
8
|
+
import { useDiffTheme } from '../diff-theme.js';
|
|
9
|
+
import { fullFileDiff } from '../full-diff.js';
|
|
10
|
+
/*
|
|
11
|
+
* A patch the CLI reported itself. The renderer needs a file header, which a CLI that only sends hunks
|
|
12
|
+
* gets from the path we know; a diff without hunks is shown as its own lines, so nothing is swallowed.
|
|
13
|
+
*/
|
|
14
|
+
function asPatch(change) {
|
|
15
|
+
if (!/^@@/m.test(change.diff)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (/^\+\+\+ /m.test(change.diff)) {
|
|
19
|
+
return change.diff;
|
|
20
|
+
}
|
|
21
|
+
const from = change.kind === 'add' ? '/dev/null' : `a/${change.path}`;
|
|
22
|
+
const to = change.kind === 'delete' ? '/dev/null' : `b/${change.path}`;
|
|
23
|
+
return `--- ${from}\n+++ ${to}\n${change.diff}`;
|
|
24
|
+
}
|
|
25
|
+
/* Lets a diff with a view of its own run to its bottom, so a short one's scrollbar sits there and not under its last line. */
|
|
26
|
+
const FILL_CSS = `
|
|
27
|
+
:host { display: flex; flex-direction: column; flex-grow: 1; }
|
|
28
|
+
pre { flex-grow: 1; align-content: start; }
|
|
29
|
+
pre[data-diff-type="single"] { display: flex; flex-direction: column; }
|
|
30
|
+
pre[data-diff-type="split"][data-overflow="scroll"]:not([data-dehydrated]) { align-content: stretch; }
|
|
31
|
+
pre > [data-code] { flex-grow: 1; align-self: stretch; align-content: start; }
|
|
32
|
+
`;
|
|
33
|
+
function lineClass(line) {
|
|
34
|
+
if (line.startsWith('+')) {
|
|
35
|
+
return 'text-chat-added';
|
|
36
|
+
}
|
|
37
|
+
if (line.startsWith('-')) {
|
|
38
|
+
return 'text-chat-removed';
|
|
39
|
+
}
|
|
40
|
+
return 'text-text-muted';
|
|
41
|
+
}
|
|
42
|
+
export default function LegacyUnifiedDiff({ change, overflow = 'wrap', diffStyle = 'unified', fill = false, contents, maxHeight }) {
|
|
43
|
+
useReviewSettled();
|
|
44
|
+
const resolved = chatHost().code.useMode();
|
|
45
|
+
const theme = useDiffTheme();
|
|
46
|
+
const patch = useMemo(() => asPatch(change), [change]);
|
|
47
|
+
const oldText = contents?.old;
|
|
48
|
+
const newText = contents?.new;
|
|
49
|
+
const full = useMemo(() => (patch === null || oldText === undefined || newText === undefined ? null : fullFileDiff(patch, change.path, { old: oldText, new: newText })), [patch, change.path, oldText, newText]);
|
|
50
|
+
const unfolds = full !== null;
|
|
51
|
+
const options = useMemo(() => ({
|
|
52
|
+
theme,
|
|
53
|
+
themeType: resolved,
|
|
54
|
+
disableFileHeader: true,
|
|
55
|
+
diffStyle,
|
|
56
|
+
overflow,
|
|
57
|
+
hunkSeparators: unfolds ? 'line-info' : 'simple',
|
|
58
|
+
unsafeCSS: fill ? FILL_CSS : undefined
|
|
59
|
+
}), [diffStyle, overflow, theme, resolved, fill, unfolds]);
|
|
60
|
+
if (patch === null) {
|
|
61
|
+
return (_jsx("pre", { className: "max-h-64 overflow-auto px-3 py-2 font-mono text-code select-text", style: fill ? undefined : { maxHeight }, children: change.diff
|
|
62
|
+
.replace(/\n$/, '')
|
|
63
|
+
.split('\n')
|
|
64
|
+
.map((line, index) => (_jsx("div", { className: clsx(lineClass(line), 'whitespace-pre-wrap'), children: line }, index))) }));
|
|
65
|
+
}
|
|
66
|
+
return (_jsx("div", { className: clsx('chat-diff select-text', fill && 'flex grow flex-col'), style: fill || maxHeight === undefined ? undefined : { maxHeight, overflow: 'auto' }, children: _jsx(LegacyDiffPool, { children: full === null ? _jsx(PatchDiff, { patch: patch, options: options }) : _jsx(FileDiff, { fileDiff: full, options: options }) }) }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { useReviewSettled } from './review-settle.js';
|
|
4
|
+
export function RawDiff({ text, overflow, fill, maxHeight }) {
|
|
5
|
+
useReviewSettled();
|
|
6
|
+
return (_jsx("pre", { "data-diff-raw": true, className: clsx('overflow-auto px-3 py-2 font-mono text-code text-text-muted select-text', fill && 'h-full min-h-0', overflow === 'wrap' ? 'whitespace-pre-wrap' : 'whitespace-pre'), style: fill ? undefined : { maxHeight: maxHeight ?? 256 }, children: text.replace(/\n$/, '') }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { lazyNamed } from '../../../lazy.js';
|
|
5
|
+
const EditDiff = lazyNamed(() => import('../EditDiff.js'), 'default');
|
|
6
|
+
const UnifiedDiff = lazyNamed(() => import('../UnifiedDiff.js'), 'default');
|
|
7
|
+
export function ReviewDiff({ entry, maxHeight = 480 }) {
|
|
8
|
+
const { t } = useTranslation('agent-chat');
|
|
9
|
+
return (_jsx("div", { "data-review-entry": entry.key, children: _jsx(Suspense, { fallback: _jsx("p", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: entry.change.kind === 'edit' ? (_jsx(EditDiff, { change: entry.change.edit, maxHeight: maxHeight })) : (_jsx(UnifiedDiff, { change: entry.change.patch, contents: entry.change.contents, maxHeight: maxHeight })) }) }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChatFileChange } from '@adecore/agent-contracts';
|
|
2
|
+
import type { DiffContents } from '../full-diff.js';
|
|
3
|
+
export interface UnifiedDiffProps {
|
|
4
|
+
allowEditor?: boolean;
|
|
5
|
+
maxHeight?: number;
|
|
6
|
+
ignoreWhitespace?: boolean;
|
|
7
|
+
change: ChatFileChange;
|
|
8
|
+
overflow?: 'wrap' | 'scroll';
|
|
9
|
+
diffStyle?: 'unified' | 'split';
|
|
10
|
+
fill?: boolean;
|
|
11
|
+
contents?: DiffContents;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface TextBudget {
|
|
2
|
+
readonly rows: number;
|
|
3
|
+
readonly characters: number;
|
|
4
|
+
readonly lineLength: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function textFitsBudget(text: string, budget: TextBudget): boolean;
|
|
7
|
+
export declare function diffSourceBudget(before: string, after: string): boolean;
|
|
8
|
+
export declare function patchSourceBudget(patch: string): boolean;
|
|
9
|
+
export declare function legacyDiffBudget(before: string, after: string, patch?: string): boolean;
|
|
10
|
+
export {};
|