@molecule/app-ide-react 3.0.3 → 3.1.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/README.md +166 -5
- package/dist/components/ChatPanel.d.ts +3 -1
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +299 -79
- package/dist/components/ChatPanel.js.map +1 -1
- package/dist/components/TestStatusBar.d.ts +3 -1
- package/dist/components/TestStatusBar.d.ts.map +1 -1
- package/dist/components/TestStatusBar.js +7 -4
- package/dist/components/TestStatusBar.js.map +1 -1
- package/dist/components/TestsCard.d.ts +12 -1
- package/dist/components/TestsCard.d.ts.map +1 -1
- package/dist/components/TestsCard.js +31 -3
- package/dist/components/TestsCard.js.map +1 -1
- package/dist/components/tests-card-utilities.d.ts +31 -2
- package/dist/components/tests-card-utilities.d.ts.map +1 -1
- package/dist/components/tests-card-utilities.js +136 -6
- package/dist/components/tests-card-utilities.js.map +1 -1
- package/dist/types.d.ts +40 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
|
@@ -51,7 +51,7 @@ import { ShareModal } from './ShareModal.js';
|
|
|
51
51
|
import { SkillsCard } from './SkillsCard.js';
|
|
52
52
|
import { StreamingIndicator } from './StreamingIndicator.js';
|
|
53
53
|
import { getTestsBarVisible, parseTestsBarArg, setTestsBarVisible, shouldShowTestsBar, } from './tests-bar-utilities.js';
|
|
54
|
-
import { applyTestRunEvent, buildTestFixMessage, EMPTY_RUN_STATE, failRun, markSkipUnavailable, parseTestCommand, requestSkip, } from './tests-card-utilities.js';
|
|
54
|
+
import { applyTestRunEvent, buildTestFixMessage, EMPTY_RUN_STATE, failRun, markSkipUnavailable, parseTestCommand, requestSkip, TestRunProjection, } from './tests-card-utilities.js';
|
|
55
55
|
import { TestsCard } from './TestsCard.js';
|
|
56
56
|
import { TestStatusBar } from './TestStatusBar.js';
|
|
57
57
|
import { TipCard } from './TipCard.js';
|
|
@@ -650,7 +650,7 @@ function CollapsibleUserMessage({ content, isLight, }) {
|
|
|
650
650
|
* @param props - Component props (see {@link MessageItemProps}).
|
|
651
651
|
* @returns The rendered verification badge element.
|
|
652
652
|
*/
|
|
653
|
-
function VerificationBadge({ status, output, categories, }) {
|
|
653
|
+
function VerificationBadge({ status, output, categories, renderCheckUnavailable, }) {
|
|
654
654
|
const cm = getClassMap();
|
|
655
655
|
const [expanded, setExpanded] = useState(false);
|
|
656
656
|
if (status === 'ok') {
|
|
@@ -660,7 +660,9 @@ function VerificationBadge({ status, output, categories, }) {
|
|
|
660
660
|
gap: '6px',
|
|
661
661
|
padding: '3px 0',
|
|
662
662
|
fontSize: '13px',
|
|
663
|
-
}, className: cm.textMuted, children: [_jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", style: { flexShrink: 0, alignSelf: 'center', position: 'relative', top: '-1px' }, children: _jsx("path", { d: "M2 5.5 L4.2 7.8 L8 3", fill: "none", stroke: "#3fb950", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
663
|
+
}, className: cm.textMuted, children: [_jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", style: { flexShrink: 0, alignSelf: 'center', position: 'relative', top: '-1px' }, children: _jsx("path", { d: "M2 5.5 L4.2 7.8 L8 3", fill: "none", stroke: "#3fb950", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }), _jsxs("span", { children: [t('ide.chat.verificationPassed', undefined, { defaultValue: 'Checks passed' }), renderCheckUnavailable && (_jsxs("span", { "data-mol-id": "render-check-unavailable", className: cm.textWarning, children: [' · ', t('ide.chat.renderCheckUnavailable', undefined, {
|
|
664
|
+
defaultValue: 'Render check unavailable',
|
|
665
|
+
})] }))] })] }));
|
|
664
666
|
}
|
|
665
667
|
// Count errors from the output text
|
|
666
668
|
const cats = categories ?? [];
|
|
@@ -1212,7 +1214,7 @@ const MessageItem = memo(function MessageItem(props) {
|
|
|
1212
1214
|
}
|
|
1213
1215
|
if (blockType === 'verification') {
|
|
1214
1216
|
const vBlock = block;
|
|
1215
|
-
return (_jsx(VerificationBadge, { status: vBlock.status, output: vBlock.output, workspaces: vBlock.workspaces, categories: vBlock.categories }, `verification-${bi}`));
|
|
1217
|
+
return (_jsx(VerificationBadge, { status: vBlock.status, output: vBlock.output, workspaces: vBlock.workspaces, categories: vBlock.categories, renderCheckUnavailable: vBlock.renderCheckUnavailable }, `verification-${bi}`));
|
|
1216
1218
|
}
|
|
1217
1219
|
if (blockType === 'resource_limit') {
|
|
1218
1220
|
const rlBlock = block;
|
|
@@ -1321,12 +1323,24 @@ const MessageItem = memo(function MessageItem(props) {
|
|
|
1321
1323
|
}, children: String.fromCharCode(65 + i) }), _jsx("span", { children: opt.label })] }, i)))] }));
|
|
1322
1324
|
})()] })) }));
|
|
1323
1325
|
});
|
|
1326
|
+
/** Only COMPLETED own ids are bounded; active own ids are pinned separately. */
|
|
1327
|
+
const COMPLETED_OWN_TEST_RUN_ID_LIMIT = 32;
|
|
1328
|
+
/** Remember a completed own run for its delayed feed copy. */
|
|
1329
|
+
function rememberCompletedOwnTestRunId(ids, runId) {
|
|
1330
|
+
ids.delete(runId);
|
|
1331
|
+
ids.add(runId);
|
|
1332
|
+
if (ids.size <= COMPLETED_OWN_TEST_RUN_ID_LIMIT)
|
|
1333
|
+
return;
|
|
1334
|
+
const oldest = ids.values().next();
|
|
1335
|
+
if (!oldest.done)
|
|
1336
|
+
ids.delete(oldest.value);
|
|
1337
|
+
}
|
|
1324
1338
|
/**
|
|
1325
1339
|
* Inner chat component that owns useChat state, message rendering, and input handling.
|
|
1326
1340
|
* @param props - Component props (see {@link MessageItemProps}).
|
|
1327
1341
|
* @returns The rendered chat inner component.
|
|
1328
1342
|
*/
|
|
1329
|
-
function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent, isPro, isAnonymous, canEdit, canSendTeamMessages = true, canShare, shareLinksAllowed, shareLinksRefreshSignal, shareModalContent, buildUpgradeCta, buildHelpUpgradeSection, activeFile, openTabs, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onFileChange, onFileDeleted, onCommit, onConversationId, onActivityClick, onRenderError, onProfileClick, currentUserId, onReadyToBuild, awaitingSandboxBoot, onClientAction, onTurnComplete, onLoadingChange, onNavigatePreview, onRegisterPushHandler, onRegisterHistoryReconcile, autoSubmitSignal, openSettingsSignal, onManageCustomModels, modelSelectionSignal, openReportSignal, openShareSignal, initialInputValue, pendingMessage, pendingMessageKey, pendingMessageSuppressUser, pendingMessageUserInitiated, userEditedFile, userEditedFileKey, gitStatusTick: externalGitStatusTick, discovery, userAvatar, agentName = DEFAULT_AGENT_NAME, productName = DEFAULT_PRODUCT_NAME, version, extraCommands, listTests, runTests, canRunTests, testsAvailable, skipToolCall,
|
|
1343
|
+
function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent, isPro, isAnonymous, canEdit, canSendTeamMessages = true, canShare, shareLinksAllowed, shareLinksRefreshSignal, shareModalContent, buildUpgradeCta, buildHelpUpgradeSection, activeFile, openTabs, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onFileChange, onFileDeleted, onCommit, onConversationId, onActivityClick, onRenderError, onProfileClick, currentUserId, onReadyToBuild, awaitingSandboxBoot, onClientAction, onTurnComplete, onLoadingChange, onNavigatePreview, onRegisterPushHandler, onRegisterHistoryReconcile, autoSubmitSignal, openSettingsSignal, onManageCustomModels, modelSelectionSignal, openReportSignal, openShareSignal, initialInputValue, pendingMessage, pendingMessageKey, pendingMessageSuppressUser, pendingMessageUserInitiated, userEditedFile, userEditedFileKey, gitStatusTick: externalGitStatusTick, discovery, userAvatar, agentName = DEFAULT_AGENT_NAME, productName = DEFAULT_PRODUCT_NAME, version, extraCommands, listTests, runTests, watchTestRuns, canRunTests, testsAvailable, skipToolCall,
|
|
1330
1344
|
// feedbackUrl: prop kept for back-compat (callers still pass it), but no longer
|
|
1331
1345
|
// consumed here — its only use was the command-menu footer link removed in P3-21.
|
|
1332
1346
|
}) {
|
|
@@ -2753,13 +2767,48 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2753
2767
|
const [testsList, setTestsList] = useState([]);
|
|
2754
2768
|
const [testsStatus, setTestsStatus] = useState('loading');
|
|
2755
2769
|
const [testsRun, setTestsRun] = useState(EMPTY_RUN_STATE);
|
|
2756
|
-
|
|
2770
|
+
// Each callback closes over its own folded run. Only this invocation owns
|
|
2771
|
+
// controls; stopped/completed invocations can still report trailing verdicts.
|
|
2772
|
+
const ownTestRunRef = useRef(null);
|
|
2757
2773
|
// Live "is a run in flight?" for the dispatchers, so `startTestsRun` keeps a
|
|
2758
2774
|
// stable identity — the command callbacks that hold it carry hand-written
|
|
2759
2775
|
// dependency lists, and a value that changes every run transition is one they
|
|
2760
2776
|
// would eventually capture stale.
|
|
2761
2777
|
const testsRunningRef = useRef(false);
|
|
2762
2778
|
testsRunningRef.current = testsRun.running;
|
|
2779
|
+
// One incremental projection: per-file verdicts plus bounded live/latest run state. Server ids
|
|
2780
|
+
// dedupe the direct stream and feed echo; legacy id-less custom hosts use the bounded id guard.
|
|
2781
|
+
const testRunProjectionRef = useRef(null);
|
|
2782
|
+
if (!testRunProjectionRef.current)
|
|
2783
|
+
testRunProjectionRef.current = new TestRunProjection();
|
|
2784
|
+
const activeOwnTestRunIdsRef = useRef(new Set());
|
|
2785
|
+
const completedOwnTestRunIdsRef = useRef(new Set());
|
|
2786
|
+
const projectTestsRun = useCallback(() => {
|
|
2787
|
+
const invocation = ownTestRunRef.current;
|
|
2788
|
+
if (invocation && invocation.state.runId == null) {
|
|
2789
|
+
setTestsRun(invocation.state);
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
const activeOwnRunId = invocation?.state.runId ?? null;
|
|
2793
|
+
const next = testRunProjectionRef.current.show(activeOwnRunId);
|
|
2794
|
+
setTestsRun(invocation && next.runId === invocation.state.runId
|
|
2795
|
+
? {
|
|
2796
|
+
...next,
|
|
2797
|
+
skipPending: invocation.state.skipPending,
|
|
2798
|
+
skipUnavailable: invocation.state.skipUnavailable,
|
|
2799
|
+
}
|
|
2800
|
+
: next);
|
|
2801
|
+
}, []);
|
|
2802
|
+
const recordOwnTestRunEvent = useCallback((event) => {
|
|
2803
|
+
const legacy = !event.eventId;
|
|
2804
|
+
if (legacy) {
|
|
2805
|
+
if (ownTestRunRef.current?.state.runId === event.runId)
|
|
2806
|
+
activeOwnTestRunIdsRef.current.add(event.runId);
|
|
2807
|
+
else
|
|
2808
|
+
rememberCompletedOwnTestRunId(completedOwnTestRunIdsRef.current, event.runId);
|
|
2809
|
+
}
|
|
2810
|
+
testRunProjectionRef.current.apply(event, ownTestRunRef.current?.state.runId ?? null);
|
|
2811
|
+
}, []);
|
|
2763
2812
|
// Whether a TURN is in flight — a fix is a real message, so it waits its turn.
|
|
2764
2813
|
const testsBusyRef = useRef(false);
|
|
2765
2814
|
// Live for the async callbacks, so a list resolving after an unmount, or a
|
|
@@ -2771,7 +2820,9 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2771
2820
|
testsMountedRef.current = true;
|
|
2772
2821
|
return () => {
|
|
2773
2822
|
testsMountedRef.current = false;
|
|
2774
|
-
|
|
2823
|
+
const own = ownTestRunRef.current;
|
|
2824
|
+
ownTestRunRef.current = null;
|
|
2825
|
+
own?.handle?.cancel();
|
|
2775
2826
|
};
|
|
2776
2827
|
}, []);
|
|
2777
2828
|
const refreshTests = useCallback(async () => {
|
|
@@ -2807,52 +2858,121 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2807
2858
|
return;
|
|
2808
2859
|
if (canRunTests === false || canEdit === false)
|
|
2809
2860
|
return;
|
|
2810
|
-
if (testsAvailable === false || testsRunningRef.current)
|
|
2861
|
+
if (testsAvailable === false || testsRunningRef.current || ownTestRunRef.current)
|
|
2811
2862
|
return;
|
|
2812
2863
|
if (selection.ids?.length === 0)
|
|
2813
2864
|
return;
|
|
2814
2865
|
// Seed a running state immediately: the host's `start` event may be a
|
|
2815
2866
|
// round trip away, and a Run button that appears to do nothing is the
|
|
2816
2867
|
// failure this feature exists to remove.
|
|
2817
|
-
|
|
2818
|
-
...
|
|
2868
|
+
const initial = {
|
|
2869
|
+
...EMPTY_RUN_STATE,
|
|
2819
2870
|
running: true,
|
|
2820
|
-
|
|
2821
|
-
error: null,
|
|
2822
|
-
output: [],
|
|
2823
|
-
currentId: null,
|
|
2824
|
-
queued: selection.ids ?? [],
|
|
2871
|
+
queued: [...(selection.ids ?? [])],
|
|
2825
2872
|
startedAt: Date.now(),
|
|
2826
|
-
|
|
2827
|
-
}
|
|
2873
|
+
external: false,
|
|
2874
|
+
};
|
|
2875
|
+
const invocation = {
|
|
2876
|
+
state: initial,
|
|
2877
|
+
handle: null,
|
|
2878
|
+
ended: null,
|
|
2879
|
+
};
|
|
2880
|
+
ownTestRunRef.current = invocation;
|
|
2881
|
+
setTestsRun((prev) => {
|
|
2882
|
+
const results = { ...prev.results };
|
|
2883
|
+
for (const id of initial.queued)
|
|
2884
|
+
delete results[id];
|
|
2885
|
+
return { ...initial, results };
|
|
2886
|
+
});
|
|
2828
2887
|
try {
|
|
2829
2888
|
const handle = runTests(selection, (event) => {
|
|
2830
2889
|
if (!testsMountedRef.current)
|
|
2831
2890
|
return;
|
|
2832
|
-
|
|
2891
|
+
const current = ownTestRunRef.current === invocation;
|
|
2892
|
+
const ended = invocation.ended;
|
|
2893
|
+
invocation.state = applyTestRunEvent(invocation.state, event);
|
|
2894
|
+
const runId = invocation.state.runId;
|
|
2895
|
+
// Completion owns cleanup even when no start ever supplied an id. Otherwise one failed
|
|
2896
|
+
// start leaves this invocation occupied and every later Run click returns early.
|
|
2833
2897
|
if (event.type === 'done') {
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2898
|
+
invocation.ended = event;
|
|
2899
|
+
if (current)
|
|
2900
|
+
ownTestRunRef.current = null;
|
|
2837
2901
|
void refreshTests();
|
|
2838
2902
|
}
|
|
2903
|
+
if (runId == null) {
|
|
2904
|
+
if (current)
|
|
2905
|
+
setTestsRun(invocation.state);
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
2908
|
+
if (event.type === 'start') {
|
|
2909
|
+
recordOwnTestRunEvent(event);
|
|
2910
|
+
if (ended) {
|
|
2911
|
+
// A start delayed past cancellation is history, never a live own run.
|
|
2912
|
+
invocation.state = applyTestRunEvent(invocation.state, ended);
|
|
2913
|
+
recordOwnTestRunEvent({ ...ended, runId });
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
else {
|
|
2917
|
+
recordOwnTestRunEvent({ ...event, runId });
|
|
2918
|
+
}
|
|
2919
|
+
if (event.type === 'done' && !event.eventId) {
|
|
2920
|
+
activeOwnTestRunIdsRef.current.delete(runId);
|
|
2921
|
+
rememberCompletedOwnTestRunId(completedOwnTestRunIdsRef.current, runId);
|
|
2922
|
+
}
|
|
2923
|
+
projectTestsRun();
|
|
2839
2924
|
});
|
|
2840
|
-
|
|
2925
|
+
// The host is allowed to finish synchronously, before returning its
|
|
2926
|
+
// handle. It no longer owns controls after handing the card onward.
|
|
2927
|
+
if (ownTestRunRef.current !== invocation)
|
|
2928
|
+
return;
|
|
2929
|
+
invocation.handle = handle;
|
|
2841
2930
|
// Whether a Skip control belongs on screen is decided HERE, by whether
|
|
2842
2931
|
// this host's handle serves one — not by the card guessing, and not by
|
|
2843
2932
|
// a button that would do nothing when clicked.
|
|
2844
2933
|
const skips = typeof handle.skipCurrent === 'function';
|
|
2934
|
+
invocation.state = {
|
|
2935
|
+
...invocation.state,
|
|
2936
|
+
skipPending: false,
|
|
2937
|
+
skipUnavailable: !skips,
|
|
2938
|
+
};
|
|
2845
2939
|
setTestsRun((prev) => ({ ...prev, skipPending: false, skipUnavailable: !skips }));
|
|
2846
2940
|
}
|
|
2847
2941
|
catch (error) {
|
|
2848
2942
|
logger.warn('Failed to start a test run', { error });
|
|
2849
|
-
|
|
2943
|
+
if (ownTestRunRef.current !== invocation)
|
|
2944
|
+
return;
|
|
2945
|
+
ownTestRunRef.current = null;
|
|
2946
|
+
const message = error instanceof Error
|
|
2850
2947
|
? error.message
|
|
2851
2948
|
: t('ide.tests.runError', undefined, {
|
|
2852
2949
|
defaultValue: 'The test run could not start.',
|
|
2853
|
-
})
|
|
2950
|
+
});
|
|
2951
|
+
const runId = invocation.state.runId;
|
|
2952
|
+
if (runId != null) {
|
|
2953
|
+
const done = { type: 'done', outcome: 'error', error: message };
|
|
2954
|
+
invocation.ended = done;
|
|
2955
|
+
invocation.state = applyTestRunEvent(invocation.state, done);
|
|
2956
|
+
activeOwnTestRunIdsRef.current.delete(runId);
|
|
2957
|
+
recordOwnTestRunEvent({ ...done, runId });
|
|
2958
|
+
projectTestsRun();
|
|
2959
|
+
}
|
|
2960
|
+
else if (testRunProjectionRef.current.show().running) {
|
|
2961
|
+
projectTestsRun();
|
|
2962
|
+
}
|
|
2963
|
+
else {
|
|
2964
|
+
setTestsRun((prev) => failRun(prev, message));
|
|
2965
|
+
}
|
|
2854
2966
|
}
|
|
2855
|
-
}, [
|
|
2967
|
+
}, [
|
|
2968
|
+
runTests,
|
|
2969
|
+
canRunTests,
|
|
2970
|
+
canEdit,
|
|
2971
|
+
testsAvailable,
|
|
2972
|
+
refreshTests,
|
|
2973
|
+
projectTestsRun,
|
|
2974
|
+
recordOwnTestRunEvent,
|
|
2975
|
+
]);
|
|
2856
2976
|
// Hand the failing tests to the agent as ONE ordinary user message — the same
|
|
2857
2977
|
// move as the editor’s "Fix with AI" and the auto-fix loop: compose the
|
|
2858
2978
|
// problem, send it through the normal path, let the turn answer. The overlay
|
|
@@ -2869,18 +2989,70 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2869
2989
|
sendMessageRef.current(message);
|
|
2870
2990
|
}, [canEdit]);
|
|
2871
2991
|
const cancelTestsRun = useCallback(() => {
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2992
|
+
// Only this panel's own run can be stopped from here; the controls are not
|
|
2993
|
+
// offered on a run it is only watching.
|
|
2994
|
+
const invocation = ownTestRunRef.current;
|
|
2995
|
+
if (!invocation)
|
|
2996
|
+
return;
|
|
2997
|
+
// Relinquish ownership before cancel(), which may synchronously call back.
|
|
2998
|
+
ownTestRunRef.current = null;
|
|
2999
|
+
const done = { type: 'done', outcome: 'cancelled' };
|
|
3000
|
+
invocation.ended = done;
|
|
3001
|
+
invocation.state = applyTestRunEvent(invocation.state, done);
|
|
3002
|
+
const runId = invocation.state.runId;
|
|
3003
|
+
if (runId != null) {
|
|
3004
|
+
activeOwnTestRunIdsRef.current.delete(runId);
|
|
3005
|
+
recordOwnTestRunEvent({ ...done, runId });
|
|
3006
|
+
projectTestsRun();
|
|
3007
|
+
}
|
|
3008
|
+
else {
|
|
3009
|
+
setTestsRun({ ...invocation.state, skipUnavailable: true });
|
|
3010
|
+
}
|
|
3011
|
+
invocation.handle?.cancel();
|
|
3012
|
+
}, [projectTestsRun, recordOwnTestRunEvent]);
|
|
3013
|
+
// Every run in the project, however it was started, reaches the same bar and
|
|
3014
|
+
// browser: the host replays the runs it still holds, then streams new ones.
|
|
3015
|
+
// The ref keeps the subscription from restarting (and replaying) each time
|
|
3016
|
+
// `refreshTests` changes identity.
|
|
3017
|
+
const refreshTestsRef = useRef(refreshTests);
|
|
3018
|
+
refreshTestsRef.current = refreshTests;
|
|
3019
|
+
useEffect(() => {
|
|
3020
|
+
if (!watchTestRuns)
|
|
3021
|
+
return;
|
|
3022
|
+
let relist = null;
|
|
3023
|
+
let unsubscribe = null;
|
|
3024
|
+
try {
|
|
3025
|
+
unsubscribe = watchTestRuns((event) => {
|
|
3026
|
+
if (!testsMountedRef.current)
|
|
3027
|
+
return;
|
|
3028
|
+
// Only id-less legacy hosts need run-id suppression. Ordered copies dedupe by event id.
|
|
3029
|
+
if (!event.eventId &&
|
|
3030
|
+
(activeOwnTestRunIdsRef.current.has(event.runId) ||
|
|
3031
|
+
completedOwnTestRunIdsRef.current.has(event.runId))) {
|
|
3032
|
+
return;
|
|
3033
|
+
}
|
|
3034
|
+
testRunProjectionRef.current.apply(event, ownTestRunRef.current?.state.runId ?? null);
|
|
3035
|
+
projectTestsRun();
|
|
3036
|
+
if (event.type === 'done') {
|
|
3037
|
+
if (relist)
|
|
3038
|
+
clearTimeout(relist);
|
|
3039
|
+
relist = setTimeout(() => {
|
|
3040
|
+
relist = null;
|
|
3041
|
+
void refreshTestsRef.current();
|
|
3042
|
+
}, 500);
|
|
3043
|
+
}
|
|
3044
|
+
});
|
|
3045
|
+
projectTestsRun();
|
|
3046
|
+
}
|
|
3047
|
+
catch (error) {
|
|
3048
|
+
logger.warn('Failed to watch the project test runs', { error });
|
|
3049
|
+
}
|
|
3050
|
+
return () => {
|
|
3051
|
+
if (relist)
|
|
3052
|
+
clearTimeout(relist);
|
|
3053
|
+
unsubscribe?.();
|
|
3054
|
+
};
|
|
3055
|
+
}, [watchTestRuns, projectTestsRun]);
|
|
2884
3056
|
// Skip the executor's in-flight tool call WITHOUT ending the turn — the
|
|
2885
3057
|
// model is told the command did not run and carries on. The card owns its own
|
|
2886
3058
|
// pressed state, so this just forwards the host's answer: `false` means the
|
|
@@ -2905,23 +3077,33 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2905
3077
|
const skipCurrentTest = useCallback(() => {
|
|
2906
3078
|
if (canEdit === false || canRunTests === false)
|
|
2907
3079
|
return;
|
|
2908
|
-
const
|
|
3080
|
+
const invocation = ownTestRunRef.current;
|
|
3081
|
+
if (!invocation)
|
|
3082
|
+
return;
|
|
3083
|
+
const skip = invocation.handle?.skipCurrent;
|
|
2909
3084
|
if (!skip) {
|
|
3085
|
+
invocation.state = markSkipUnavailable(invocation.state);
|
|
2910
3086
|
setTestsRun((prev) => markSkipUnavailable(prev));
|
|
2911
3087
|
return;
|
|
2912
3088
|
}
|
|
3089
|
+
invocation.state = requestSkip(invocation.state);
|
|
2913
3090
|
setTestsRun((prev) => requestSkip(prev));
|
|
2914
3091
|
const unavailable = () => {
|
|
2915
|
-
if (testsMountedRef.current)
|
|
3092
|
+
if (testsMountedRef.current && ownTestRunRef.current === invocation) {
|
|
3093
|
+
invocation.state = markSkipUnavailable(invocation.state);
|
|
2916
3094
|
setTestsRun((prev) => markSkipUnavailable(prev));
|
|
3095
|
+
}
|
|
2917
3096
|
};
|
|
2918
3097
|
try {
|
|
2919
|
-
const outcome = skip.call(
|
|
3098
|
+
const outcome = skip.call(invocation.handle);
|
|
2920
3099
|
if (outcome) {
|
|
2921
3100
|
void outcome.then((accepted) => {
|
|
2922
3101
|
if (accepted === false)
|
|
2923
3102
|
unavailable();
|
|
2924
|
-
},
|
|
3103
|
+
}, (error) => {
|
|
3104
|
+
logger.warn('Failed to skip the running test', { error });
|
|
3105
|
+
unavailable();
|
|
3106
|
+
});
|
|
2925
3107
|
}
|
|
2926
3108
|
}
|
|
2927
3109
|
catch (error) {
|
|
@@ -3329,11 +3511,31 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3329
3511
|
// ── Git status ─────────────────────────────────────────────────────────────
|
|
3330
3512
|
const [gitStatusTick, setGitStatusTick] = useState(0);
|
|
3331
3513
|
const refreshGitStatus = useCallback(() => setGitStatusTick((n) => n + 1), []);
|
|
3514
|
+
const gitScopeRef = useRef(0);
|
|
3515
|
+
useLayoutEffect(() => {
|
|
3516
|
+
gitScopeRef.current++;
|
|
3517
|
+
setPendingFiles(null);
|
|
3518
|
+
setCommitState(null);
|
|
3519
|
+
setCommitCards([]);
|
|
3520
|
+
setCommitBarExpanded(false);
|
|
3521
|
+
return () => {
|
|
3522
|
+
gitScopeRef.current++;
|
|
3523
|
+
};
|
|
3524
|
+
}, [projectId]);
|
|
3525
|
+
const gitStatusRequestRef = useRef(0);
|
|
3332
3526
|
const fetchPendingFiles = useCallback(() => {
|
|
3333
|
-
|
|
3527
|
+
const scope = gitScopeRef.current;
|
|
3528
|
+
const request = ++gitStatusRequestRef.current;
|
|
3529
|
+
return http
|
|
3334
3530
|
.get(`/projects/${projectId}/git-status`)
|
|
3335
|
-
.then((res) =>
|
|
3336
|
-
|
|
3531
|
+
.then((res) => {
|
|
3532
|
+
if (scope === gitScopeRef.current && request === gitStatusRequestRef.current)
|
|
3533
|
+
setPendingFiles(res.data.files.length > 0 ? res.data.files : null);
|
|
3534
|
+
})
|
|
3535
|
+
.catch(() => {
|
|
3536
|
+
if (scope === gitScopeRef.current && request === gitStatusRequestRef.current)
|
|
3537
|
+
setPendingFiles(null);
|
|
3538
|
+
});
|
|
3337
3539
|
}, [http, projectId]);
|
|
3338
3540
|
// Debounced version — coalesces rapid ticks (e.g. save → format, or an AI
|
|
3339
3541
|
// write burst mid-turn) into a single fetch after the last write settles.
|
|
@@ -3362,6 +3564,25 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3362
3564
|
return;
|
|
3363
3565
|
debouncedFetchPendingFiles();
|
|
3364
3566
|
}, [externalGitStatusTick, debouncedFetchPendingFiles]);
|
|
3567
|
+
// Startup repairs, another editor, or another client can commit without a
|
|
3568
|
+
// local file event. Reconcile on return and while the workspace is visible.
|
|
3569
|
+
useEffect(() => {
|
|
3570
|
+
const refresh = () => {
|
|
3571
|
+
if (document.visibilityState !== 'hidden')
|
|
3572
|
+
void fetchPendingFiles();
|
|
3573
|
+
};
|
|
3574
|
+
window.addEventListener('focus', refresh);
|
|
3575
|
+
document.addEventListener('visibilitychange', refresh);
|
|
3576
|
+
const interval = setInterval(refresh, 45_000);
|
|
3577
|
+
return () => {
|
|
3578
|
+
window.removeEventListener('focus', refresh);
|
|
3579
|
+
document.removeEventListener('visibilitychange', refresh);
|
|
3580
|
+
clearInterval(interval);
|
|
3581
|
+
if (gitFetchTimerRef.current)
|
|
3582
|
+
clearTimeout(gitFetchTimerRef.current);
|
|
3583
|
+
gitStatusRequestRef.current++;
|
|
3584
|
+
};
|
|
3585
|
+
}, [fetchPendingFiles]);
|
|
3365
3586
|
// A user-side file mutation (edit/rename/delete) also restarts the auto-commit
|
|
3366
3587
|
// countdown — the parent's externalGitStatusTick is the canonical "files moved"
|
|
3367
3588
|
// signal. Skipped on the initial 0 so opening a project doesn't arm anything.
|
|
@@ -3604,6 +3825,9 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3604
3825
|
}, [userEditedFile, userEditedFileKey, clearQueuedForFile]);
|
|
3605
3826
|
// ── Commit ─────────────────────────────────────────────────────────────────
|
|
3606
3827
|
const handleCommit = useCallback(async () => {
|
|
3828
|
+
const scope = gitScopeRef.current;
|
|
3829
|
+
// An older status response must not restore a pre-commit count.
|
|
3830
|
+
gitStatusRequestRef.current++;
|
|
3607
3831
|
setCommitState({ status: 'committing' });
|
|
3608
3832
|
const cardId = `commit-${Date.now()}`;
|
|
3609
3833
|
setCommitCards((prev) => [
|
|
@@ -3611,8 +3835,11 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3611
3835
|
{ id: cardId, message: '', files: [], timestamp: Date.now(), status: 'running' },
|
|
3612
3836
|
]);
|
|
3613
3837
|
try {
|
|
3614
|
-
const result = await http.post(`/projects/${projectId}/commit
|
|
3838
|
+
const result = await http.post(`/projects/${projectId}/commit`);
|
|
3839
|
+
if (scope !== gitScopeRef.current)
|
|
3840
|
+
return;
|
|
3615
3841
|
if (result.data.committed) {
|
|
3842
|
+
gitStatusRequestRef.current++;
|
|
3616
3843
|
const msg = result.data.message ?? t('ide.chat.committed');
|
|
3617
3844
|
setCommitState({ status: 'committed', message: msg });
|
|
3618
3845
|
setPendingFiles(null);
|
|
@@ -3627,14 +3854,28 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3627
3854
|
}
|
|
3628
3855
|
: c));
|
|
3629
3856
|
onCommit?.();
|
|
3630
|
-
setTimeout(() =>
|
|
3857
|
+
setTimeout(() => {
|
|
3858
|
+
if (scope === gitScopeRef.current)
|
|
3859
|
+
setCommitState(null);
|
|
3860
|
+
}, 3000);
|
|
3631
3861
|
}
|
|
3632
3862
|
else {
|
|
3633
3863
|
setCommitCards((prev) => prev.filter((c) => c.id !== cardId));
|
|
3634
3864
|
setCommitState(null);
|
|
3865
|
+
addSystemCard(result.data.message === 'No changes to commit'
|
|
3866
|
+
? t('ide.chat.commitNoChanges', undefined, { defaultValue: 'No changes to commit.' })
|
|
3867
|
+
: result.data.message === 'A build is in progress'
|
|
3868
|
+
? t('ide.chat.commitBuildInProgress', undefined, {
|
|
3869
|
+
defaultValue: 'Wait for the build to finish before creating a Git commit.',
|
|
3870
|
+
})
|
|
3871
|
+
: t('ide.chat.commitError', undefined, { defaultValue: 'Failed to commit changes.' }));
|
|
3872
|
+
void fetchPendingFiles();
|
|
3873
|
+
onCommit?.();
|
|
3635
3874
|
}
|
|
3636
3875
|
}
|
|
3637
3876
|
catch (error) {
|
|
3877
|
+
if (scope !== gitScopeRef.current)
|
|
3878
|
+
return;
|
|
3638
3879
|
logger.warn('Commit request failed', { error });
|
|
3639
3880
|
setCommitCards((prev) => prev.map((c) => c.id === cardId
|
|
3640
3881
|
? {
|
|
@@ -3644,9 +3885,12 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3644
3885
|
}
|
|
3645
3886
|
: c));
|
|
3646
3887
|
setCommitState({ status: 'error' });
|
|
3647
|
-
setTimeout(() =>
|
|
3888
|
+
setTimeout(() => {
|
|
3889
|
+
if (scope === gitScopeRef.current)
|
|
3890
|
+
setCommitState(null);
|
|
3891
|
+
}, 3000);
|
|
3648
3892
|
}
|
|
3649
|
-
}, [http, projectId, onCommit]);
|
|
3893
|
+
}, [http, projectId, onCommit, addSystemCard, fetchPendingFiles, t]);
|
|
3650
3894
|
// ── Revert commit ────────────────────────────────────────────────────────
|
|
3651
3895
|
/** Reverts a commit by hash. Returns the new revert commit's hash on success. */
|
|
3652
3896
|
const handleRevertCommit = useCallback(async (hash) => {
|
|
@@ -4503,32 +4747,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4503
4747
|
}
|
|
4504
4748
|
else if (id === 'commit') {
|
|
4505
4749
|
setInputValue('');
|
|
4506
|
-
|
|
4507
|
-
const status = await http.get(`/projects/${projectId}/git-status`);
|
|
4508
|
-
if (!status.data.files.length) {
|
|
4509
|
-
addSystemCard(t('ide.chat.commitNoChanges', undefined, { defaultValue: 'No changes to commit.' }));
|
|
4510
|
-
return;
|
|
4511
|
-
}
|
|
4512
|
-
const res = await http.post(`/projects/${projectId}/commit`);
|
|
4513
|
-
if (res.data.committed) {
|
|
4514
|
-
setCommitCards((prev) => [
|
|
4515
|
-
...prev,
|
|
4516
|
-
{
|
|
4517
|
-
id: crypto.randomUUID(),
|
|
4518
|
-
message: res.data.message ?? '',
|
|
4519
|
-
files: res.data.files ?? [],
|
|
4520
|
-
timestamp: Date.now(),
|
|
4521
|
-
status: 'done',
|
|
4522
|
-
hash: res.data.hash,
|
|
4523
|
-
},
|
|
4524
|
-
]);
|
|
4525
|
-
refreshGitStatus();
|
|
4526
|
-
}
|
|
4527
|
-
}
|
|
4528
|
-
catch (error) {
|
|
4529
|
-
logger.warn('Failed to commit changes via /commit command', { error });
|
|
4530
|
-
addSystemCard(t('ide.chat.commitError', undefined, { defaultValue: 'Failed to commit changes.' }));
|
|
4531
|
-
}
|
|
4750
|
+
await handleCommit();
|
|
4532
4751
|
}
|
|
4533
4752
|
else if (id === 'test') {
|
|
4534
4753
|
setInputValue('');
|
|
@@ -4653,6 +4872,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4653
4872
|
messages,
|
|
4654
4873
|
sendMessage,
|
|
4655
4874
|
refreshGitStatus,
|
|
4875
|
+
handleCommit,
|
|
4656
4876
|
openPanelOverlay,
|
|
4657
4877
|
extraCommandIds,
|
|
4658
4878
|
canEdit,
|
|
@@ -6174,7 +6394,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6174
6394
|
return (_jsx(ScriptsCard, { projectId: projectId, initialQuery: item.card.query ?? '', isLight: isLight, agentName: agentName }, item.card.id));
|
|
6175
6395
|
}
|
|
6176
6396
|
if (item.card.variant === 'tests') {
|
|
6177
|
-
return (_jsx(TestsCard, { tests: testsList, status: testsStatus, run: testsRun, initialQuery: item.card.query ?? '', canRun: (canRunTests ?? canEdit !== false) && canEdit !== false, onRun: startTestsRun, onCancel: cancelTestsRun, onSkipCurrent: skipCurrentTest, onFix: fixTests, fixDisabledReason: canEdit === false
|
|
6397
|
+
return (_jsx(TestsCard, { tests: testsList, status: testsStatus, run: testsRun, agentName: agentName, initialQuery: item.card.query ?? '', canRun: (canRunTests ?? canEdit !== false) && canEdit !== false, onRun: startTestsRun, onCancel: cancelTestsRun, onSkipCurrent: skipCurrentTest, onFix: fixTests, fixDisabledReason: canEdit === false
|
|
6178
6398
|
? t('ide.tests.viewerCannotRun', undefined, {
|
|
6179
6399
|
defaultValue: 'Only editors can run this project’s tests.',
|
|
6180
6400
|
})
|
|
@@ -7431,7 +7651,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
7431
7651
|
minHeight: 36,
|
|
7432
7652
|
}
|
|
7433
7653
|
: {}),
|
|
7434
|
-
}, children: '✕' })] }), _jsxs("div", { style: { overflowY: 'auto', flex: 1 }, children: [panelOverlay === 'settings' && (_jsx(SettingsCard, { settings: computeSettingsList(), onRunCommand: (commandId) => void executeCommand(commandId), onPrefillInput: (input) => setInputAndCursorEnd(`${input} `), isLight: isLight, agentName: agentName, embedded: true })), panelOverlay === 'skills' && (_jsx(SkillsCard, { projectId: projectId, initialQuery: panelOverlayQuery, onLoad: loadSkill, onCreate: createSkill, loadedSkillPaths: loadedSkillPaths, defaultSkillPaths: defaultSkillPaths, onToggleDefault: toggleDefaultSkill, onResetDefault: resetDefaultSkills, defaultsExplicit: defaultSkillsExplicitRef.current, isLight: isLight, embedded: true })), panelOverlay === 'scripts' && (_jsx(ScriptsCard, { projectId: projectId, initialQuery: panelOverlayQuery, isLight: isLight, agentName: agentName, embedded: true })), panelOverlay === 'tests' && (_jsx(TestsCard, { tests: testsList, status: testsStatus, run: testsRun, initialQuery: panelOverlayQuery, canRun: (canRunTests ?? canEdit !== false) && canEdit !== false, onRun: startTestsRun, onCancel: cancelTestsRun, onSkipCurrent: skipCurrentTest, onFix: fixTests, fixDisabledReason: canEdit === false
|
|
7654
|
+
}, children: '✕' })] }), _jsxs("div", { style: { overflowY: 'auto', flex: 1 }, children: [panelOverlay === 'settings' && (_jsx(SettingsCard, { settings: computeSettingsList(), onRunCommand: (commandId) => void executeCommand(commandId), onPrefillInput: (input) => setInputAndCursorEnd(`${input} `), isLight: isLight, agentName: agentName, embedded: true })), panelOverlay === 'skills' && (_jsx(SkillsCard, { projectId: projectId, initialQuery: panelOverlayQuery, onLoad: loadSkill, onCreate: createSkill, loadedSkillPaths: loadedSkillPaths, defaultSkillPaths: defaultSkillPaths, onToggleDefault: toggleDefaultSkill, onResetDefault: resetDefaultSkills, defaultsExplicit: defaultSkillsExplicitRef.current, isLight: isLight, embedded: true })), panelOverlay === 'scripts' && (_jsx(ScriptsCard, { projectId: projectId, initialQuery: panelOverlayQuery, isLight: isLight, agentName: agentName, embedded: true })), panelOverlay === 'tests' && (_jsx(TestsCard, { tests: testsList, status: testsStatus, run: testsRun, agentName: agentName, initialQuery: panelOverlayQuery, canRun: (canRunTests ?? canEdit !== false) && canEdit !== false, onRun: startTestsRun, onCancel: cancelTestsRun, onSkipCurrent: skipCurrentTest, onFix: fixTests, fixDisabledReason: canEdit === false
|
|
7435
7655
|
? t('ide.tests.viewerCannotRun', undefined, {
|
|
7436
7656
|
defaultValue: 'Only editors can run this project’s tests.',
|
|
7437
7657
|
})
|
|
@@ -7627,7 +7847,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
7627
7847
|
}, onMouseLeave: (e) => {
|
|
7628
7848
|
e.currentTarget.style.opacity = '0.7';
|
|
7629
7849
|
e.currentTarget.style.background = 'none';
|
|
7630
|
-
}, children: _jsx("svg", { width: "13", height: "13", viewBox: "0 0 12 12", style: { display: 'block' }, children: _jsx("path", { d: "M 2.5,6.5 L 6,3 L 9.5,6.5 M 6,3.5 L 6,10", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] }, qm.id))) })] })), workVisible && (_jsxs(WorkCard, { children: [_jsx(PlanCard, { todos: plan, live: planLive, awaitingUser: planAwaitingUser }), _jsx(BackgroundTasksStrip, { tasks: backgroundTasks }), showTests && (_jsx(TestStatusBar, { run: testsRun, tests: testsList, onOpen: () => openTestsBrowser(''), onStop: testsRun.running ? cancelTestsRun : undefined })), showFiles && pendingFiles != null && (_jsx(DockStrip, { molId: "chat-commit-bar", toggleMolId: "chat-commit-toggle", expanded: commitBarExpanded, onToggle: () => setCommitBarExpanded((v) => !v), hideLabel: true, label: t('ide.chat.showChangedFiles', undefined, {
|
|
7850
|
+
}, children: _jsx("svg", { width: "13", height: "13", viewBox: "0 0 12 12", style: { display: 'block' }, children: _jsx("path", { d: "M 2.5,6.5 L 6,3 L 9.5,6.5 M 6,3.5 L 6,10", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] }, qm.id))) })] })), workVisible && (_jsxs(WorkCard, { children: [_jsx(PlanCard, { todos: plan, live: planLive, awaitingUser: planAwaitingUser }), _jsx(BackgroundTasksStrip, { tasks: backgroundTasks }), showTests && (_jsx(TestStatusBar, { run: testsRun, agentName: agentName, tests: testsList, onOpen: () => openTestsBrowser(''), onStop: testsRun.running && !testsRun.external ? cancelTestsRun : undefined })), showFiles && pendingFiles != null && (_jsx(DockStrip, { molId: "chat-commit-bar", toggleMolId: "chat-commit-toggle", expanded: commitBarExpanded, onToggle: () => setCommitBarExpanded((v) => !v), hideLabel: true, label: t('ide.chat.showChangedFiles', undefined, {
|
|
7631
7851
|
defaultValue: 'Show changed files',
|
|
7632
7852
|
}), actions: _jsx(_Fragment, { children: isAutoCommitEnabled(autoCommit) &&
|
|
7633
7853
|
commitState?.status !== 'committing' &&
|
|
@@ -8077,7 +8297,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
8077
8297
|
* @param props - Component props (see {@link MessageItemProps}).
|
|
8078
8298
|
* @returns The rendered chat panel element.
|
|
8079
8299
|
*/
|
|
8080
|
-
export function ChatPanel({ projectId, endpoint, initialMessage, onInitialMessageSent, activeFile, openTabs, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onFileChange, onFileDeleted, onCommit, onActivityClick, onRenderError, onProfileClick, currentUserId, onReadyToBuild, awaitingSandboxBoot, onClientAction, onTurnComplete, onLoadingChange, onNavigatePreview, onRegisterPushHandler, onRegisterHistoryReconcile, autoSubmitSignal, initialInputValue, hideConversationMenu, renderConversationHeader = true, conversationId: controlledConversationId, chatKey: controlledChatKey, onConversationId: controlledOnConversationId, openShareSignal: controlledShareSignal, openReportSignal: controlledReportSignal, openSettingsSignal: controlledSettingsSignal, onManageCustomModels, gitStatusTick, pendingMessage, pendingMessageKey, pendingMessageSuppressUser, pendingMessageUserInitiated, userEditedFile, userEditedFileKey, isPro, isAnonymous, canEdit = true, canSendTeamMessages = true, canShare, shareLinksAllowed, shareLinksRefreshSignal, shareModalContent, buildUpgradeCta, buildHelpUpgradeSection, userAvatar, agentName, productName, version, extraCommands, feedbackUrl, listTests, runTests, canRunTests, testsAvailable, skipToolCall, className, }) {
|
|
8300
|
+
export function ChatPanel({ projectId, endpoint, initialMessage, onInitialMessageSent, activeFile, openTabs, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onFileChange, onFileDeleted, onCommit, onActivityClick, onRenderError, onProfileClick, currentUserId, onReadyToBuild, awaitingSandboxBoot, onClientAction, onTurnComplete, onLoadingChange, onNavigatePreview, onRegisterPushHandler, onRegisterHistoryReconcile, autoSubmitSignal, initialInputValue, hideConversationMenu, renderConversationHeader = true, conversationId: controlledConversationId, chatKey: controlledChatKey, onConversationId: controlledOnConversationId, openShareSignal: controlledShareSignal, openReportSignal: controlledReportSignal, openSettingsSignal: controlledSettingsSignal, onManageCustomModels, gitStatusTick, pendingMessage, pendingMessageKey, pendingMessageSuppressUser, pendingMessageUserInitiated, userEditedFile, userEditedFileKey, isPro, isAnonymous, canEdit = true, canSendTeamMessages = true, canShare, shareLinksAllowed, shareLinksRefreshSignal, shareModalContent, buildUpgradeCta, buildHelpUpgradeSection, userAvatar, agentName, productName, version, extraCommands, feedbackUrl, listTests, runTests, watchTestRuns, canRunTests, testsAvailable, skipToolCall, className, }) {
|
|
8081
8301
|
const cm = getClassMap();
|
|
8082
8302
|
// Share management may be gated ABOVE canEdit by the host (see
|
|
8083
8303
|
// ChatPanelProps.canShare) — gates the built-in header share button here and
|
|
@@ -8292,7 +8512,7 @@ export function ChatPanel({ projectId, endpoint, initialMessage, onInitialMessag
|
|
|
8292
8512
|
textOverflow: 'ellipsis',
|
|
8293
8513
|
whiteSpace: 'nowrap',
|
|
8294
8514
|
width: '100%',
|
|
8295
|
-
}, children: conv.preview ?? 'New conversation' }), _jsx("span", { className: cm.cn(cm.textMuted, cm.textSize('xs')), style: { opacity: 0.55 }, children: relativeTime(conv.updatedAt) })] }, conv.id)))] }))] })), _jsx(ChatInner, { projectId: projectId, endpoint: chatEndpoint, initialMessage: initialMessage, onInitialMessageSent: onInitialMessageSent, isPro: isPro, isAnonymous: isAnonymous, canEdit: canEdit, canSendTeamMessages: canSendTeamMessages, canShare: shareAllowed, shareLinksAllowed: shareLinksAllowed, shareLinksRefreshSignal: shareLinksRefreshSignal, shareModalContent: shareModalContent, buildUpgradeCta: buildUpgradeCta, buildHelpUpgradeSection: buildHelpUpgradeSection, activeFile: activeFile, openTabs: openTabs, onFileOpen: onFileOpen, onFileDoubleClick: onFileDoubleClick, onFileDiff: onFileDiff, onFileRevert: onFileRevert, onFileChange: onFileChange, onFileDeleted: onFileDeleted, onCommit: onCommit, onConversationId: reportConversationId, onActivityClick: onActivityClick, onRenderError: onRenderError, onProfileClick: onProfileClick, currentUserId: currentUserId, onReadyToBuild: onReadyToBuild, awaitingSandboxBoot: awaitingSandboxBoot, onClientAction: onClientAction, onTurnComplete: onTurnComplete, onLoadingChange: onLoadingChange, onNavigatePreview: onNavigatePreview, onRegisterPushHandler: onRegisterPushHandler, onRegisterHistoryReconcile: onRegisterHistoryReconcile, autoSubmitSignal: autoSubmitSignal, openSettingsSignal: effectiveSettingsSignal, onManageCustomModels: onManageCustomModels, openReportSignal: effectiveReportSignal, openShareSignal: effectiveShareSignal, initialInputValue: initialInputValue, pendingMessage: pendingMessage, pendingMessageKey: pendingMessageKey, pendingMessageSuppressUser: pendingMessageSuppressUser, pendingMessageUserInitiated: pendingMessageUserInitiated, userEditedFile: userEditedFile, userEditedFileKey: userEditedFileKey, gitStatusTick: gitStatusTick, discovery: hideConversationMenu, userAvatar: userAvatar, agentName: agentName, productName: productName, version: version, extraCommands: extraCommands, feedbackUrl: feedbackUrl, listTests: listTests, runTests: runTests, canRunTests: canRunTests, testsAvailable: testsAvailable, skipToolCall: skipToolCall }, chatKey)] }));
|
|
8515
|
+
}, children: conv.preview ?? 'New conversation' }), _jsx("span", { className: cm.cn(cm.textMuted, cm.textSize('xs')), style: { opacity: 0.55 }, children: relativeTime(conv.updatedAt) })] }, conv.id)))] }))] })), _jsx(ChatInner, { projectId: projectId, endpoint: chatEndpoint, initialMessage: initialMessage, onInitialMessageSent: onInitialMessageSent, isPro: isPro, isAnonymous: isAnonymous, canEdit: canEdit, canSendTeamMessages: canSendTeamMessages, canShare: shareAllowed, shareLinksAllowed: shareLinksAllowed, shareLinksRefreshSignal: shareLinksRefreshSignal, shareModalContent: shareModalContent, buildUpgradeCta: buildUpgradeCta, buildHelpUpgradeSection: buildHelpUpgradeSection, activeFile: activeFile, openTabs: openTabs, onFileOpen: onFileOpen, onFileDoubleClick: onFileDoubleClick, onFileDiff: onFileDiff, onFileRevert: onFileRevert, onFileChange: onFileChange, onFileDeleted: onFileDeleted, onCommit: onCommit, onConversationId: reportConversationId, onActivityClick: onActivityClick, onRenderError: onRenderError, onProfileClick: onProfileClick, currentUserId: currentUserId, onReadyToBuild: onReadyToBuild, awaitingSandboxBoot: awaitingSandboxBoot, onClientAction: onClientAction, onTurnComplete: onTurnComplete, onLoadingChange: onLoadingChange, onNavigatePreview: onNavigatePreview, onRegisterPushHandler: onRegisterPushHandler, onRegisterHistoryReconcile: onRegisterHistoryReconcile, autoSubmitSignal: autoSubmitSignal, openSettingsSignal: effectiveSettingsSignal, onManageCustomModels: onManageCustomModels, openReportSignal: effectiveReportSignal, openShareSignal: effectiveShareSignal, initialInputValue: initialInputValue, pendingMessage: pendingMessage, pendingMessageKey: pendingMessageKey, pendingMessageSuppressUser: pendingMessageSuppressUser, pendingMessageUserInitiated: pendingMessageUserInitiated, userEditedFile: userEditedFile, userEditedFileKey: userEditedFileKey, gitStatusTick: gitStatusTick, discovery: hideConversationMenu, userAvatar: userAvatar, agentName: agentName, productName: productName, version: version, extraCommands: extraCommands, feedbackUrl: feedbackUrl, listTests: listTests, runTests: runTests, watchTestRuns: watchTestRuns, canRunTests: canRunTests, testsAvailable: testsAvailable, skipToolCall: skipToolCall }, chatKey)] }));
|
|
8296
8516
|
}
|
|
8297
8517
|
ChatPanel.displayName = 'ChatPanel';
|
|
8298
8518
|
//# sourceMappingURL=ChatPanel.js.map
|