@adecore/agents-react 0.22.0 → 0.23.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.
- 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/intelligent-ui/UiBlockFrame.js +3 -3
- package/dist/chat/ui/intelligent-ui/UiFallbackPart.js +1 -1
- package/dist/chat/ui/intelligent-ui/chart-data.d.ts +22 -2
- package/dist/chat/ui/intelligent-ui/chart-data.js +83 -19
- package/dist/chat/ui/intelligent-ui/renderers/chart.d.ts +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/chart.js +99 -35
- package/dist/chat/ui/intelligent-ui/renderers/choices.d.ts +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/choices.js +8 -2
- package/dist/chat/ui/intelligent-ui/renderers/content.js +5 -3
- package/dist/chat/ui/intelligent-ui/renderers/inputs.js +9 -4
- package/dist/chat/ui/intelligent-ui/renderers/links.js +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/text.js +5 -2
- package/dist/chat/ui/intelligent-ui/table-data.js +4 -4
- package/dist/chat/ui/intelligent-ui/use-truncated.d.ts +1 -0
- package/dist/chat/ui/intelligent-ui/use-truncated.js +28 -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 +12 -3
- package/dist/locales/en/agent-prompts.json +1 -0
- package/dist/locales/nl/agent-chat.json +12 -3
- package/dist/locales/nl/agent-prompts.json +1 -0
- package/dist/prompts/ui/ApprovalBody.js +7 -5
- package/package.json +23 -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/intelligent-ui/UiBlockFrame.tsx +5 -5
- package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +6 -1
- package/src/chat/ui/intelligent-ui/chart-data.ts +106 -20
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +237 -72
- package/src/chat/ui/intelligent-ui/renderers/choices.tsx +9 -4
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +31 -15
- package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +16 -5
- package/src/chat/ui/intelligent-ui/renderers/links.tsx +3 -3
- package/src/chat/ui/intelligent-ui/renderers/text.tsx +18 -8
- package/src/chat/ui/intelligent-ui/table-data.ts +4 -4
- package/src/chat/ui/intelligent-ui/use-truncated.ts +33 -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 +12 -3
- package/src/locales/en/agent-prompts.json +1 -0
- package/src/locales/nl/agent-chat.json +12 -3
- package/src/locales/nl/agent-prompts.json +1 -0
- package/src/prompts/ui/ApprovalBody.tsx +8 -14
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Diff preparation is synchronous; cap source work before creating lines or hunks.
|
|
2
|
+
const SOURCE_BUDGET = { rows: 20000, characters: 2000000, lineLength: 1000000 };
|
|
3
|
+
const PATCH_BUDGET = { rows: 50000, characters: 4000000, lineLength: 1000001 };
|
|
4
|
+
const LEGACY_BUDGET = { rows: 400, characters: 100000, lineLength: 10000 };
|
|
5
|
+
export function textFitsBudget(text, budget) {
|
|
6
|
+
if (text.length > budget.characters) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
let rows = text.length === 0 ? 0 : 1;
|
|
10
|
+
let length = 0;
|
|
11
|
+
for (let index = 0; index < text.length; index++) {
|
|
12
|
+
if (text.charCodeAt(index) === 10) {
|
|
13
|
+
if (index < text.length - 1 && ++rows > budget.rows) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
length = 0;
|
|
17
|
+
}
|
|
18
|
+
else if (++length > budget.lineLength) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return rows <= budget.rows;
|
|
23
|
+
}
|
|
24
|
+
export function diffSourceBudget(before, after) {
|
|
25
|
+
return textFitsBudget(before, SOURCE_BUDGET) && textFitsBudget(after, SOURCE_BUDGET);
|
|
26
|
+
}
|
|
27
|
+
export function patchSourceBudget(patch) {
|
|
28
|
+
return textFitsBudget(patch, PATCH_BUDGET);
|
|
29
|
+
}
|
|
30
|
+
export function legacyDiffBudget(before, after, patch = '') {
|
|
31
|
+
// Combined sources and patch must fit one small legacy render, including trailing blank lines.
|
|
32
|
+
if (before.length + after.length + patch.length > LEGACY_BUDGET.characters) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
let rows = 2;
|
|
36
|
+
for (const text of [before, after, patch]) {
|
|
37
|
+
if (!textFitsBudget(text, LEGACY_BUDGET)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
for (let index = 0; index < text.length; index++) {
|
|
41
|
+
if (text.charCodeAt(index) === 10 && ++rows > LEGACY_BUDGET.rows) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type DiffDocument } from '@adecore/merge';
|
|
2
|
+
export declare const EDITOR_DIFF_WORK = 4000000;
|
|
3
|
+
export declare const EDITOR_DIFF_LIMITS: {
|
|
4
|
+
rows: number;
|
|
5
|
+
characters: number;
|
|
6
|
+
lineLength: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function canRenderEditorDiff(document: DiffDocument): boolean;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { diffRows } from '@adecore/merge';
|
|
2
|
+
// Larger rendering tiers need native production measurements before this gate expands.
|
|
3
|
+
export const EDITOR_DIFF_WORK = 4_000_000;
|
|
4
|
+
export const EDITOR_DIFF_LIMITS = { rows: 2000, characters: 2000000, lineLength: 10000 };
|
|
5
|
+
export function canRenderEditorDiff(document) {
|
|
6
|
+
let rows = 0;
|
|
7
|
+
let characters = 0;
|
|
8
|
+
for (const row of diffRows(document, 'unified')) {
|
|
9
|
+
if (row.kind === 'gap') {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
const text = row.new === undefined ? document.sides.old[row.old.index] : document.sides.new[row.new.index];
|
|
13
|
+
characters += text.length + (rows > 0 ? 1 : 0);
|
|
14
|
+
if (++rows > EDITOR_DIFF_LIMITS.rows || characters > EDITOR_DIFF_LIMITS.characters || text.length > EDITOR_DIFF_LIMITS.lineLength) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ChatFileChange } from '@adecore/agent-contracts';
|
|
2
|
+
import type { FileChange } from '../../logic/tools.js';
|
|
3
|
+
import type { DiffContents } from '../full-diff.js';
|
|
4
|
+
export interface ReviewEntry {
|
|
5
|
+
readonly key: string;
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly change: {
|
|
8
|
+
readonly kind: 'edit';
|
|
9
|
+
readonly edit: FileChange;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: 'patch';
|
|
12
|
+
readonly patch: ChatFileChange;
|
|
13
|
+
readonly contents?: DiffContents;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface ReviewBudget {
|
|
17
|
+
readonly editors: number;
|
|
18
|
+
readonly rows: number;
|
|
19
|
+
readonly characters: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const REVIEW_BUDGET: ReviewBudget;
|
|
22
|
+
export type ReviewSlot = 'editor' | 'deferred';
|
|
23
|
+
export declare function reviewEntries(patches: readonly ChatFileChange[], edits: readonly FileChange[]): readonly ReviewEntry[];
|
|
24
|
+
export declare function reviewSize(entry: ReviewEntry, budget?: ReviewBudget): {
|
|
25
|
+
rows: number;
|
|
26
|
+
characters: number;
|
|
27
|
+
};
|
|
28
|
+
export declare function planReview(entries: readonly ReviewEntry[], promoted: string | null, budget?: ReviewBudget): ReadonlyMap<string, ReviewSlot>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const REVIEW_BUDGET = { editors: 3, rows: 2000, characters: 2000000 };
|
|
2
|
+
export function reviewEntries(patches, edits) {
|
|
3
|
+
return [
|
|
4
|
+
...patches.map((patch, index) => ({ key: `patch:${index}:${patch.path}`, path: patch.path, change: { kind: 'patch', patch } })),
|
|
5
|
+
...edits.map((edit, index) => ({ key: `edit:${index}:${edit.path}`, path: edit.path, change: { kind: 'edit', edit } }))
|
|
6
|
+
];
|
|
7
|
+
}
|
|
8
|
+
export function reviewSize(entry, budget = REVIEW_BUDGET) {
|
|
9
|
+
const texts = entry.change.kind === 'edit' ? [entry.change.edit.before, entry.change.edit.after] : [entry.change.patch.diff];
|
|
10
|
+
const characters = texts.reduce((sum, text) => sum + text.length, 0);
|
|
11
|
+
if (characters > budget.characters) {
|
|
12
|
+
return { rows: budget.rows + 1, characters };
|
|
13
|
+
}
|
|
14
|
+
let rows = 0;
|
|
15
|
+
for (const text of texts) {
|
|
16
|
+
if (text.length > 0) {
|
|
17
|
+
rows++;
|
|
18
|
+
}
|
|
19
|
+
for (let index = text.indexOf('\n'); index >= 0; index = text.indexOf('\n', index + 1)) {
|
|
20
|
+
if (++rows > budget.rows) {
|
|
21
|
+
return { rows, characters };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { rows, characters };
|
|
26
|
+
}
|
|
27
|
+
export function planReview(entries, promoted, budget = REVIEW_BUDGET) {
|
|
28
|
+
const priority = entries.find((entry) => entry.key === promoted);
|
|
29
|
+
const ordered = priority === undefined ? entries : [priority, ...entries.filter((entry) => entry !== priority)];
|
|
30
|
+
const slots = new Map();
|
|
31
|
+
let editors = 0;
|
|
32
|
+
let rows = 0;
|
|
33
|
+
let characters = 0;
|
|
34
|
+
for (const entry of ordered) {
|
|
35
|
+
const size = reviewSize(entry, budget);
|
|
36
|
+
// The first review keeps its own omission and error handling, even when the group estimate exceeds the budget.
|
|
37
|
+
if (editors === 0 || (editors < budget.editors && rows + size.rows <= budget.rows && characters + size.characters <= budget.characters)) {
|
|
38
|
+
slots.set(entry.key, 'editor');
|
|
39
|
+
editors++;
|
|
40
|
+
rows += size.rows;
|
|
41
|
+
characters += size.characters;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
slots.set(entry.key, 'deferred');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return slots;
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect } from 'react';
|
|
2
|
+
export const ReviewSettleContext = createContext(null);
|
|
3
|
+
export function useReviewSettled() {
|
|
4
|
+
const scope = useContext(ReviewSettleContext);
|
|
5
|
+
const key = scope?.key;
|
|
6
|
+
const settle = scope?.settle;
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (key !== undefined) {
|
|
9
|
+
settle?.(key);
|
|
10
|
+
}
|
|
11
|
+
}, [key, settle]);
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Highlighter, ThemeRegistration } from 'shiki';
|
|
2
|
+
import { type DiffSyntaxDocument, type DiffSyntaxTokens } from '@adecore/editor/shiki-worker';
|
|
3
|
+
export interface DiffSyntaxSession {
|
|
4
|
+
request(index: number): Promise<DiffSyntaxTokens | null>;
|
|
5
|
+
cancel(): void;
|
|
6
|
+
dispose(): void;
|
|
7
|
+
}
|
|
8
|
+
export interface DiffSyntaxRuntime {
|
|
9
|
+
open(document: DiffSyntaxDocument): DiffSyntaxSession;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
12
|
+
export interface DiffSyntaxRuntimeOptions {
|
|
13
|
+
workerFactory?: () => Worker;
|
|
14
|
+
themes: readonly ThemeRegistration[];
|
|
15
|
+
highlighter?: () => Promise<Highlighter>;
|
|
16
|
+
}
|
|
17
|
+
export declare function diffSyntaxRuntime(): DiffSyntaxRuntime;
|
|
18
|
+
export declare function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): DiffSyntaxRuntime;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { createSyntaxService } from '@adecore/editor/shiki-worker';
|
|
2
|
+
import { chatHost } from '../../../host.js';
|
|
3
|
+
let nextGeneration = 0;
|
|
4
|
+
let shared;
|
|
5
|
+
export function diffSyntaxRuntime() {
|
|
6
|
+
const code = chatHost().code;
|
|
7
|
+
return (shared ??= createDiffSyntaxRuntime({ workerFactory: code.syntaxWorker, themes: code.custom, highlighter: code.highlighter }));
|
|
8
|
+
}
|
|
9
|
+
export function createDiffSyntaxRuntime(options) {
|
|
10
|
+
const sessions = new Map();
|
|
11
|
+
let worker;
|
|
12
|
+
let fallback;
|
|
13
|
+
let highlighter;
|
|
14
|
+
let started = false;
|
|
15
|
+
let stopped = false;
|
|
16
|
+
const settle = (session) => {
|
|
17
|
+
for (const pending of session.pending.values()) {
|
|
18
|
+
pending.resolve(null);
|
|
19
|
+
}
|
|
20
|
+
session.pending.clear();
|
|
21
|
+
};
|
|
22
|
+
const stopWorker = () => {
|
|
23
|
+
worker?.removeEventListener('message', onMessage);
|
|
24
|
+
worker?.removeEventListener('error', onError);
|
|
25
|
+
worker?.removeEventListener('messageerror', onError);
|
|
26
|
+
worker?.terminate();
|
|
27
|
+
worker = undefined;
|
|
28
|
+
};
|
|
29
|
+
const loadHighlighter = () => {
|
|
30
|
+
highlighter ??= import('shiki').then(({ createHighlighter }) => createHighlighter({ langs: [], themes: [] }));
|
|
31
|
+
return highlighter;
|
|
32
|
+
};
|
|
33
|
+
const startFallback = () => {
|
|
34
|
+
stopWorker();
|
|
35
|
+
if (fallback !== undefined || stopped) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
fallback = createSyntaxService({ highlighter: options.highlighter ?? loadHighlighter, send: receive });
|
|
39
|
+
dispatch({ type: 'start', themes: options.themes, maxLines: 100000 });
|
|
40
|
+
for (const [generation, session] of sessions) {
|
|
41
|
+
session.sent = false;
|
|
42
|
+
for (const index of session.pending.keys()) {
|
|
43
|
+
request(generation, session, index);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const onError = (event) => {
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
startFallback();
|
|
50
|
+
};
|
|
51
|
+
const onMessage = (event) => {
|
|
52
|
+
receive(event.data);
|
|
53
|
+
};
|
|
54
|
+
const initialize = () => {
|
|
55
|
+
if (started || stopped) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
started = true;
|
|
59
|
+
if (options.workerFactory !== undefined) {
|
|
60
|
+
try {
|
|
61
|
+
worker = options.workerFactory();
|
|
62
|
+
worker.addEventListener('message', onMessage);
|
|
63
|
+
worker.addEventListener('error', onError);
|
|
64
|
+
worker.addEventListener('messageerror', onError);
|
|
65
|
+
dispatch({ type: 'start', themes: options.themes, maxLines: 100000 });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
startFallback();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
startFallback();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const dispatch = (message) => {
|
|
77
|
+
if (stopped) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (worker !== undefined) {
|
|
81
|
+
try {
|
|
82
|
+
worker.postMessage(message);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
startFallback();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (fallback !== undefined) {
|
|
89
|
+
void fallback.handle(message).catch(() => {
|
|
90
|
+
for (const session of sessions.values()) {
|
|
91
|
+
settle(session);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const request = (generation, session, index, resend = false) => {
|
|
97
|
+
const document = !session.sent || resend ? session.definition : undefined;
|
|
98
|
+
session.sent = true;
|
|
99
|
+
dispatch({ type: 'request', generation, index, ...(document ? { document } : {}) });
|
|
100
|
+
};
|
|
101
|
+
const receive = (message) => {
|
|
102
|
+
if (stopped || message.type === 'ready') {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (message.type === 'error') {
|
|
106
|
+
if (worker !== undefined) {
|
|
107
|
+
startFallback();
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
for (const session of sessions.values()) {
|
|
111
|
+
settle(session);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const session = sessions.get(message.generation);
|
|
117
|
+
const pending = session?.pending.get(message.index);
|
|
118
|
+
if (session === undefined || pending === undefined) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (message.type === 'failed' && message.reason === 'missing' && !pending.resent) {
|
|
122
|
+
pending.resent = true;
|
|
123
|
+
request(message.generation, session, message.index, true);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
session.pending.delete(message.index);
|
|
127
|
+
pending.resolve(message.type === 'tokens' ? message : null);
|
|
128
|
+
};
|
|
129
|
+
return {
|
|
130
|
+
open(document) {
|
|
131
|
+
const generation = ++nextGeneration;
|
|
132
|
+
const session = { definition: document, pending: new Map(), sent: false };
|
|
133
|
+
sessions.set(generation, session);
|
|
134
|
+
return {
|
|
135
|
+
request(index) {
|
|
136
|
+
if (stopped || !sessions.has(generation)) {
|
|
137
|
+
return Promise.resolve(null);
|
|
138
|
+
}
|
|
139
|
+
const existing = session.pending.get(index);
|
|
140
|
+
if (existing !== undefined) {
|
|
141
|
+
return existing.promise;
|
|
142
|
+
}
|
|
143
|
+
let resolve;
|
|
144
|
+
const promise = new Promise((done) => {
|
|
145
|
+
resolve = done;
|
|
146
|
+
});
|
|
147
|
+
session.pending.set(index, { promise, resolve, resent: false });
|
|
148
|
+
const initialized = started;
|
|
149
|
+
initialize();
|
|
150
|
+
// Fallback initialization replays pending requests, including this first one.
|
|
151
|
+
if (initialized || worker !== undefined) {
|
|
152
|
+
request(generation, session, index);
|
|
153
|
+
}
|
|
154
|
+
return promise;
|
|
155
|
+
},
|
|
156
|
+
cancel() {
|
|
157
|
+
settle(session);
|
|
158
|
+
if (started) {
|
|
159
|
+
dispatch({ type: 'cancel', generation });
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
dispose() {
|
|
163
|
+
settle(session);
|
|
164
|
+
sessions.delete(generation);
|
|
165
|
+
if (started) {
|
|
166
|
+
dispatch({ type: 'dispose', generation });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
dispose() {
|
|
172
|
+
stopped = true;
|
|
173
|
+
for (const session of sessions.values()) {
|
|
174
|
+
settle(session);
|
|
175
|
+
}
|
|
176
|
+
sessions.clear();
|
|
177
|
+
stopWorker();
|
|
178
|
+
fallback?.dispose();
|
|
179
|
+
if (options.highlighter === undefined) {
|
|
180
|
+
void highlighter?.then((loaded) => loaded.dispose(), () => undefined);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ReviewEntry, type ReviewSlot } from './review-plan.js';
|
|
2
|
+
export interface ReviewStack {
|
|
3
|
+
readonly slots: ReadonlyMap<string, ReviewSlot>;
|
|
4
|
+
readonly ready: ReadonlySet<string>;
|
|
5
|
+
promote(key: string): void;
|
|
6
|
+
settle(key: string): void;
|
|
7
|
+
}
|
|
8
|
+
export declare function useReviewStack(entries: readonly ReviewEntry[]): ReviewStack;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { planReview } from './review-plan.js';
|
|
3
|
+
export function useReviewStack(entries) {
|
|
4
|
+
const [promoted, setPromoted] = useState(null);
|
|
5
|
+
const slots = useMemo(() => planReview(entries, promoted), [entries, promoted]);
|
|
6
|
+
const selected = [...slots].filter(([, slot]) => slot === 'editor').map(([key]) => key);
|
|
7
|
+
const [admitted, setAdmitted] = useState(() => selected.slice(0, 1));
|
|
8
|
+
const retained = admitted.filter((key) => selected.includes(key));
|
|
9
|
+
const live = retained.length === 0 ? selected.slice(0, 1) : retained;
|
|
10
|
+
if (live.length !== admitted.length || live.some((key, index) => key !== admitted[index])) {
|
|
11
|
+
setAdmitted(live);
|
|
12
|
+
}
|
|
13
|
+
const [settled, setSettled] = useState(() => new Set());
|
|
14
|
+
if ([...settled].some((key) => !selected.includes(key))) {
|
|
15
|
+
setSettled(new Set([...settled].filter((key) => selected.includes(key))));
|
|
16
|
+
}
|
|
17
|
+
const settle = useCallback((key) => {
|
|
18
|
+
setSettled((previous) => (previous.has(key) ? previous : new Set([...previous, key])));
|
|
19
|
+
}, []);
|
|
20
|
+
const allSettled = live.every((key) => settled.has(key));
|
|
21
|
+
const next = selected.find((key) => !live.includes(key));
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (next === undefined || !allSettled) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const frame = window.requestAnimationFrame(() => setAdmitted((previous) => [...previous, next]));
|
|
27
|
+
return () => window.cancelAnimationFrame(frame);
|
|
28
|
+
}, [next, allSettled]);
|
|
29
|
+
return { slots, ready: new Set(live), promote: setPromoted, settle };
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LineToken } from '@adecore/editor';
|
|
2
|
+
import { type DiffSyntaxDocument } from '@adecore/editor/shiki-worker';
|
|
3
|
+
import type { DiffDocument } from '@adecore/merge';
|
|
4
|
+
import { type DiffSyntaxRuntime } from './diff/syntax-runtime.js';
|
|
5
|
+
export { createDiffSyntaxRuntime, diffSyntaxRuntime } from './diff/syntax-runtime.js';
|
|
6
|
+
export type { DiffSyntaxRuntime, DiffSyntaxRuntimeOptions, DiffSyntaxSession } from './diff/syntax-runtime.js';
|
|
7
|
+
export interface DiffSyntaxMapping {
|
|
8
|
+
readonly documents: Readonly<Record<'old' | 'new', DiffSyntaxDocument>>;
|
|
9
|
+
locate(side: 'old' | 'new', index: number): {
|
|
10
|
+
readonly block: number;
|
|
11
|
+
readonly line: number;
|
|
12
|
+
} | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface DiffSourceTokenSession {
|
|
15
|
+
get(side: 'old' | 'new', index: number): readonly LineToken[] | null | undefined;
|
|
16
|
+
subscribe(listener: () => void): () => void;
|
|
17
|
+
dispose(): void;
|
|
18
|
+
}
|
|
19
|
+
export declare function diffSourceTokens(document: DiffDocument, mapping: DiffSyntaxMapping, runtime?: DiffSyntaxRuntime): DiffSourceTokenSession;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { lineTokensOf } from '@adecore/editor/shiki-worker';
|
|
2
|
+
import { diffSyntaxRuntime } from './diff/syntax-runtime.js';
|
|
3
|
+
export { createDiffSyntaxRuntime, diffSyntaxRuntime } from './diff/syntax-runtime.js';
|
|
4
|
+
/* Structurally accepts the editor's source mapping without depending on its React renderer. */
|
|
5
|
+
export function diffSourceTokens(document, mapping, runtime = diffSyntaxRuntime()) {
|
|
6
|
+
const sides = { old: runtime.open(mapping.documents.old), new: runtime.open(mapping.documents.new) };
|
|
7
|
+
const cache = { old: new Map(), new: new Map() };
|
|
8
|
+
const pending = { old: new Set(), new: new Set() };
|
|
9
|
+
const listeners = new Set();
|
|
10
|
+
let disposed = false;
|
|
11
|
+
return {
|
|
12
|
+
get(side, index) {
|
|
13
|
+
const location = mapping.locate(side, index);
|
|
14
|
+
if (disposed || location === undefined || document.sides[side][index] === undefined) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const { block, line } = location;
|
|
18
|
+
const known = cache[side].get(block);
|
|
19
|
+
if (known !== undefined) {
|
|
20
|
+
return known[line] ?? null;
|
|
21
|
+
}
|
|
22
|
+
if (!pending[side].has(block)) {
|
|
23
|
+
pending[side].add(block);
|
|
24
|
+
const text = mapping.documents[side].blocks[block].split('\n');
|
|
25
|
+
const publish = (lines) => {
|
|
26
|
+
if (disposed) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
pending[side].delete(block);
|
|
30
|
+
cache[side].set(block, lines);
|
|
31
|
+
for (const listener of [...listeners]) {
|
|
32
|
+
listener();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const count = text.length;
|
|
36
|
+
void sides[side].request(block).then((packet) => {
|
|
37
|
+
if (!disposed) {
|
|
38
|
+
publish(text.map((text, line) => (packet === null || packet.lines.length !== count + 1 ? null : lineTokensOf(packet, line, text))));
|
|
39
|
+
}
|
|
40
|
+
}, () => publish(text.map(() => null)));
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
},
|
|
44
|
+
subscribe(listener) {
|
|
45
|
+
if (!disposed) {
|
|
46
|
+
listeners.add(listener);
|
|
47
|
+
}
|
|
48
|
+
return () => {
|
|
49
|
+
listeners.delete(listener);
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
dispose() {
|
|
53
|
+
disposed = true;
|
|
54
|
+
listeners.clear();
|
|
55
|
+
sides.old.dispose();
|
|
56
|
+
sides.new.dispose();
|
|
57
|
+
cache.old.clear();
|
|
58
|
+
cache.new.clear();
|
|
59
|
+
pending.old.clear();
|
|
60
|
+
pending.new.clear();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useId, useLayoutEffect, useRef, useState } from 'react';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import { Lock, Radio, TriangleAlert
|
|
5
|
+
import { Info, Lock, Radio, TriangleAlert } from 'lucide-react';
|
|
6
6
|
import { Icon, Skeleton, Spinner, Tooltip, useTickingText } from '@adecore/ui';
|
|
7
7
|
import { formatAgo, formatMoment } from '@adecore/ui/format';
|
|
8
8
|
import { uiBlockHead, uiReasonText } from './node-text.js';
|
|
@@ -36,7 +36,7 @@ function LivePart({ live }) {
|
|
|
36
36
|
return hint === null ? body : _jsx(Tooltip, { label: hint, children: body });
|
|
37
37
|
}
|
|
38
38
|
/*
|
|
39
|
-
* The line under a block, only when it has something to say: live, answered,
|
|
39
|
+
* The line under a block, only when it has something to say: live, answered, noted or drawn as
|
|
40
40
|
* text, in that order. A hairline of the soft border runs the full width of the card over it.
|
|
41
41
|
*/
|
|
42
42
|
function UiBlockFooter({ live = null, answered = null, fixes = NO_FIXES, shownAsText = null }) {
|
|
@@ -46,7 +46,7 @@ function UiBlockFooter({ live = null, answered = null, fixes = NO_FIXES, shownAs
|
|
|
46
46
|
if (live === null && answered === null && fixes.length === 0 && shownAsText === null) {
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
|
-
return (_jsxs("div", { className: "-mx-2.25 mt-2.25 -mb-2.25 flex flex-col gap-1.5 border-t border-border-soft px-4.25 py-2 text-xs text-text-faint", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1", children: [live !== null && _jsx(LivePart, { live: live }), answered !== null && (_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { children: t('blocks.answered', { label: answered.label }) }), _jsx("span", { "aria-hidden": true, children: "\u00B7" }), _jsx("time", { dateTime: new Date(answered.at).toISOString(), children: formatMoment(answered.at) })] })), fixes.length > 0 && (_jsxs("button", { type: "button", "aria-expanded": fixesOpen, "aria-controls": listId, className: "flex items-center gap-1.5 rounded-sm hover:text-text-muted", onClick: () => setFixesOpen(!fixesOpen), children: [_jsx(Icon, { icon:
|
|
49
|
+
return (_jsxs("div", { className: "-mx-2.25 mt-2.25 -mb-2.25 flex flex-col gap-1.5 border-t border-border-soft px-4.25 py-2 text-xs text-text-faint", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1", children: [live !== null && _jsx(LivePart, { live: live }), answered !== null && (_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { children: t('blocks.answered', { label: answered.label }) }), _jsx("span", { "aria-hidden": true, children: "\u00B7" }), _jsx("time", { dateTime: new Date(answered.at).toISOString(), children: formatMoment(answered.at) })] })), fixes.length > 0 && (_jsxs("button", { type: "button", "aria-expanded": fixesOpen, "aria-controls": listId, className: "flex items-center gap-1.5 rounded-sm hover:text-text-muted", onClick: () => setFixesOpen(!fixesOpen), children: [_jsx(Icon, { icon: Info, size: 12 }), t('blocks.notes', { count: fixes.length })] })), shownAsText !== null && (_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { children: t('blocks.shownAsText') }), _jsx("span", { "aria-hidden": true, children: "\u00B7" }), _jsx("span", { children: shownAsText })] }))] }), fixes.length > 0 && (_jsx("ul", { id: listId, hidden: !fixesOpen, className: "flex flex-col gap-0.5 text-text-muted", children: fixes.map((fix, index) => (_jsxs("li", { className: "flex gap-2", children: [_jsx("span", { className: "shrink-0 font-mono", children: fix.code }), _jsx("span", { className: "min-w-0", children: fix.message })] }, index))) }))] }));
|
|
50
50
|
}
|
|
51
51
|
const RELEASE_MS = 200;
|
|
52
52
|
/*
|
|
@@ -7,5 +7,5 @@ import { Markdown } from '../Markdown.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export function UiFallbackPart({ fallback, problem }) {
|
|
9
9
|
const { t } = useTranslation('agent-chat');
|
|
10
|
-
return (_jsxs("div", { className: "flex flex-col gap-1 rounded-md bg-surface-hover p-2", children: [_jsx("span", { className: "text-xs text-text-faint", children: problem.kind === 'unknown' ? t('blocks.unknownComponent', { component: problem.component }) : t('blocks.renderFailed') }), fallback !== '' && _jsx(Markdown, { text: fallback, fileLinks: false })] }));
|
|
10
|
+
return (_jsxs("div", { className: "flex flex-col gap-1 rounded-md bg-surface-hover p-2", children: [_jsx("span", { className: "text-xs text-text-faint", children: problem.kind === 'unknown' ? t('blocks.unknownComponent', { component: problem.component }) : t('blocks.renderFailed') }), fallback !== '' && (_jsx("div", { className: "min-w-0 wrap-anywhere", children: _jsx(Markdown, { text: fallback, fileLinks: false }) }))] }));
|
|
11
11
|
}
|
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
import type { UiProps } from '@adecore/intelligent-ui';
|
|
2
2
|
export declare const UI_CHART_SERIES = 6;
|
|
3
3
|
export declare const UI_CHART_ROWS = 60;
|
|
4
|
+
type ChartKind = UiProps<'Chart'>['kind'];
|
|
4
5
|
export interface UiChartSeries {
|
|
5
6
|
key: string;
|
|
6
7
|
values: (number | null)[];
|
|
7
8
|
}
|
|
8
|
-
export interface
|
|
9
|
+
export interface UiChartValues {
|
|
9
10
|
labels: string[];
|
|
10
11
|
series: UiChartSeries[];
|
|
12
|
+
}
|
|
13
|
+
export interface UiChartData extends UiChartValues {
|
|
14
|
+
kind: ChartKind;
|
|
11
15
|
min: number;
|
|
12
16
|
max: number;
|
|
13
17
|
truncated: boolean;
|
|
18
|
+
total: number;
|
|
19
|
+
omitted: string[];
|
|
14
20
|
}
|
|
15
|
-
export declare function
|
|
21
|
+
export declare function uiChartValues(data: UiProps<'Chart'>['data']): UiChartValues;
|
|
22
|
+
export declare function uiChartRange(min: number, max: number): {
|
|
23
|
+
low: number;
|
|
24
|
+
high: number;
|
|
25
|
+
} | null;
|
|
26
|
+
export declare function uiChartData(data: UiProps<'Chart'>['data'], kind: ChartKind): UiChartData | null;
|
|
27
|
+
export declare const UI_CHART_CHARACTER = 7.5;
|
|
28
|
+
export declare function uiChartCharacter(rootFontSize: number): number;
|
|
29
|
+
export declare function uiChartTextWidth(text: string, character?: number): number;
|
|
30
|
+
export declare function uiChartText(text: string, room: number, character?: number): string;
|
|
31
|
+
export declare function uiChartAxis(ticks: readonly string[], width: number, character?: number): {
|
|
32
|
+
axisWidth: number;
|
|
33
|
+
tickTexts: string[];
|
|
34
|
+
};
|
|
16
35
|
export declare function niceCeiling(value: number): number;
|
|
36
|
+
export {};
|