@molecule/app-ide-react 1.8.0 → 1.9.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/README.md +14 -1
- package/dist/command-metadata.js +2 -2
- package/dist/command-metadata.js.map +1 -1
- package/dist/components/ChatPanel.d.ts +3 -1
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +355 -44
- package/dist/components/ChatPanel.js.map +1 -1
- package/dist/components/PreviewPanel.d.ts.map +1 -1
- package/dist/components/PreviewPanel.js +25 -1
- package/dist/components/PreviewPanel.js.map +1 -1
- package/dist/components/ShareLinkManager.d.ts +7 -1
- package/dist/components/ShareLinkManager.d.ts.map +1 -1
- package/dist/components/ShareLinkManager.js +2 -2
- package/dist/components/ShareLinkManager.js.map +1 -1
- package/dist/components/chat-effort-utilities.d.ts +11 -3
- package/dist/components/chat-effort-utilities.d.ts.map +1 -1
- package/dist/components/chat-effort-utilities.js +5 -3
- package/dist/components/chat-effort-utilities.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
|
@@ -19,7 +19,7 @@ import { CHAT_CARD_ICON_SIZE, chatCardBorder, chatCardStyle } from './chat-card-
|
|
|
19
19
|
import { COMMAND_CATEGORIES, COMMANDS, matchesSideChannelCommand, } from './chat-commands.js';
|
|
20
20
|
import { stripCommitCoauthorTrailer } from './chat-commit-utilities.js';
|
|
21
21
|
import { cachedPromptTokens, formatTokenTotal } from './chat-cost-utilities.js';
|
|
22
|
-
import { effortOptionsForModel, nativeEffortName, parseEffortCommand, resolveEffortArg, } from './chat-effort-utilities.js';
|
|
22
|
+
import { defaultEffortForModel, effortOptionsForModel, nativeEffortName, parseEffortCommand, resolveEffortArg, } from './chat-effort-utilities.js';
|
|
23
23
|
import { buildHelpText } from './chat-help-utilities.js';
|
|
24
24
|
import { effectiveModeModelId, freeTierLockReason, freeTierUsableMode, isModeModelLocked, modeSettingKey, parseModelModeCommand, resolveModeModel, } from './chat-model-mode-utilities.js';
|
|
25
25
|
import { modelHasPeakPricing, modelPeakMultiplier, modelPeakWindowLabels, modelUsageRate, sortModels, } from './chat-models-utilities.js';
|
|
@@ -1218,7 +1218,7 @@ const MessageItem = memo(function MessageItem(props) {
|
|
|
1218
1218
|
* @param props - Component props (see {@link MessageItemProps}).
|
|
1219
1219
|
* @returns The rendered chat inner component.
|
|
1220
1220
|
*/
|
|
1221
|
-
function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent, isPro, isAnonymous, canEdit, canShare, buildUpgradeCta, buildHelpUpgradeSection, activeFile, openTabs, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onFileChange, onFileDeleted, onCommit, onConversationId, onActivityClick, onRenderError, onProfileClick, currentUserId, onReadyToBuild, awaitingSandboxBoot, onClientAction, onTurnComplete, onLoadingChange, onNavigatePreview, onRegisterPushHandler, 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,
|
|
1221
|
+
function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent, isPro, isAnonymous, canEdit, canShare, 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,
|
|
1222
1222
|
// feedbackUrl: prop kept for back-compat (callers still pass it), but no longer
|
|
1223
1223
|
// consumed here — its only use was the command-menu footer link removed in P3-21.
|
|
1224
1224
|
}) {
|
|
@@ -1265,19 +1265,39 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
1265
1265
|
const soundsConfigRef = useRef({ ...DEFAULT_SOUNDS_CONFIG });
|
|
1266
1266
|
// ── Context usage tracking (ring indicator) ─────────────────────────────
|
|
1267
1267
|
const [contextUsage, setContextUsage] = useState(null);
|
|
1268
|
-
// Restore context usage from the history endpoint on mount
|
|
1268
|
+
// Restore context usage from the history endpoint on mount. Retries a few
|
|
1269
|
+
// times on failure: this used to be one-shot, so a single transient error at
|
|
1270
|
+
// mount (an API deploy restart, a rate-limit blip) silently cost the context
|
|
1271
|
+
// ring for the entire session — the state only ever refills from the next
|
|
1272
|
+
// turn's `done` event (observed live 2026-08-29: ring gone, server data fine,
|
|
1273
|
+
// reload fixed it).
|
|
1269
1274
|
useEffect(() => {
|
|
1270
1275
|
if (!hasConversation)
|
|
1271
1276
|
return;
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1277
|
+
let cancelled = false;
|
|
1278
|
+
let attempt = 0;
|
|
1279
|
+
const load = () => {
|
|
1280
|
+
http
|
|
1281
|
+
.get(endpoint)
|
|
1282
|
+
.then((res) => {
|
|
1283
|
+
if (cancelled)
|
|
1284
|
+
return;
|
|
1285
|
+
if (res.data.contextUsage)
|
|
1286
|
+
setContextUsage(res.data.contextUsage);
|
|
1287
|
+
})
|
|
1288
|
+
.catch((_error) => {
|
|
1289
|
+
// Transient failure — retry with backoff (bounded); after the budget,
|
|
1290
|
+
// the next turn's done event repopulates the ring.
|
|
1291
|
+
if (cancelled || attempt >= 3)
|
|
1292
|
+
return;
|
|
1293
|
+
attempt++;
|
|
1294
|
+
setTimeout(load, attempt * 4000);
|
|
1295
|
+
});
|
|
1296
|
+
};
|
|
1297
|
+
load();
|
|
1298
|
+
return () => {
|
|
1299
|
+
cancelled = true;
|
|
1300
|
+
};
|
|
1281
1301
|
}, [endpoint, hasConversation, http]);
|
|
1282
1302
|
// ── Auto-fix countdown state ──────────────────────────────────────────────
|
|
1283
1303
|
// After the AI finishes, if verification found errors, show a countdown
|
|
@@ -1491,7 +1511,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
1491
1511
|
}
|
|
1492
1512
|
onFileChange?.(path, content);
|
|
1493
1513
|
}, [onFileChange, onFileOpen, autoFixCountdown]);
|
|
1494
|
-
const { messages, isLoading, isRemoteStreaming, noteRemoteStreamEvent, error, errorMeta, mode, fastMode, streamingStatus, setMode, setFastMode, sendMessage, abort, clearHistory, editQueuedMessage, deleteQueuedMessage, clearQueuedForFile, applyRemoteEvent, retryCountdown, cancelRetry, } = useChat({
|
|
1514
|
+
const { messages, isLoading, isRemoteStreaming, noteRemoteStreamEvent, error, errorMeta, mode, fastMode, streamingStatus, setMode, setFastMode, sendMessage, abort, clearHistory, editQueuedMessage, deleteQueuedMessage, clearQueuedForFile, applyRemoteEvent, reconcileHistory, retryCountdown, cancelRetry, } = useChat({
|
|
1495
1515
|
endpoint,
|
|
1496
1516
|
projectId,
|
|
1497
1517
|
agentName,
|
|
@@ -1919,6 +1939,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
1919
1939
|
setIsListening(false);
|
|
1920
1940
|
setCommandMenu(null);
|
|
1921
1941
|
setModelPicker(null);
|
|
1942
|
+
setEffortPicker(null);
|
|
1922
1943
|
setSoundsPicker(null);
|
|
1923
1944
|
setFilePicker(null);
|
|
1924
1945
|
setPanelOverlay(null);
|
|
@@ -2115,6 +2136,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2115
2136
|
// Popups are one-at-a-time — close any sibling before opening the picker
|
|
2116
2137
|
setCommandMenu(null);
|
|
2117
2138
|
setModelPicker(null);
|
|
2139
|
+
setEffortPicker(null);
|
|
2118
2140
|
setSoundsPicker(null);
|
|
2119
2141
|
setFilePicker(null);
|
|
2120
2142
|
setPanelOverlay(null);
|
|
@@ -2155,6 +2177,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2155
2177
|
const [commandMenu, setCommandMenu] = useState(null);
|
|
2156
2178
|
// ── Model picker (shown when typing /model <filter>) ──────────────────────
|
|
2157
2179
|
const [modelPicker, setModelPicker] = useState(null);
|
|
2180
|
+
const [effortPicker, setEffortPicker] = useState(null);
|
|
2158
2181
|
// ── System cards (persistent inline notifications in chat history) ────────
|
|
2159
2182
|
const [systemCards, setSystemCards] = useState([]);
|
|
2160
2183
|
// Bug-report modal — `{ title }` (seed) when open, null when closed. Opened by
|
|
@@ -2258,6 +2281,15 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2258
2281
|
onRegisterPushHandler?.(applyPushedStreamEvent);
|
|
2259
2282
|
return () => onRegisterPushHandler?.(null);
|
|
2260
2283
|
}, [onRegisterPushHandler, applyPushedStreamEvent]);
|
|
2284
|
+
// Register the history reconcile with the parent so it can converge this
|
|
2285
|
+
// panel on the persisted transcript whenever its push channel (re)connects —
|
|
2286
|
+
// a broadcast sent while that socket was down (a teammate's team note against
|
|
2287
|
+
// a backgrounded tab or a slept laptop) is otherwise lost until a page
|
|
2288
|
+
// lifecycle event happens to fire.
|
|
2289
|
+
useEffect(() => {
|
|
2290
|
+
onRegisterHistoryReconcile?.(reconcileHistory);
|
|
2291
|
+
return () => onRegisterHistoryReconcile?.(null);
|
|
2292
|
+
}, [onRegisterHistoryReconcile, reconcileHistory]);
|
|
2261
2293
|
// Inject the user-message accent-stripe styles once (the gradient `::before` + its
|
|
2262
2294
|
// keyframe can't live inline; gated on the row's data-mol-id, so no other row is
|
|
2263
2295
|
// touched). Guarded by id so it injects a single time across the app.
|
|
@@ -2510,6 +2542,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2510
2542
|
// Close any sibling popup so overlays never stack (matches how the model
|
|
2511
2543
|
// picker / sounds picker each own this region exclusively).
|
|
2512
2544
|
setModelPicker(null);
|
|
2545
|
+
setEffortPicker(null);
|
|
2513
2546
|
setSoundsPicker(null);
|
|
2514
2547
|
setCommandMenu(null);
|
|
2515
2548
|
setPanelOverlayQuery(query);
|
|
@@ -2685,10 +2718,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2685
2718
|
setAutoCommitLoaded(true);
|
|
2686
2719
|
});
|
|
2687
2720
|
}, [http, projectId]);
|
|
2688
|
-
// Re-read
|
|
2689
|
-
//
|
|
2690
|
-
//
|
|
2691
|
-
//
|
|
2721
|
+
// Re-read the persisted agent-behavior settings when the host signals a
|
|
2722
|
+
// settings change: the provider modal's "Use" button, or a resource_change
|
|
2723
|
+
// broadcast saying ANOTHER member changed the project (model/effort/regions/
|
|
2724
|
+
// max-loops/auto-fix/auto-approve) — so this browser's pickers and toggles
|
|
2725
|
+
// match the change instead of rendering stale values under the shared setting
|
|
2726
|
+
// card. Deliberately does NOT touch auto-commit or skills (they carry local
|
|
2727
|
+
// in-session state the mount effect owns). Skips the initial 0 value.
|
|
2692
2728
|
useEffect(() => {
|
|
2693
2729
|
if (!modelSelectionSignal)
|
|
2694
2730
|
return;
|
|
@@ -2704,10 +2740,44 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2704
2740
|
setPlanModel(s.planModel);
|
|
2705
2741
|
if (typeof s?.executeModel === 'string')
|
|
2706
2742
|
setExecuteModel(s.executeModel);
|
|
2743
|
+
if (typeof s?.commitModel === 'string')
|
|
2744
|
+
setCommitModel(s.commitModel);
|
|
2745
|
+
if (typeof s?.compactModel === 'string')
|
|
2746
|
+
setCompactModel(s.compactModel);
|
|
2747
|
+
if (typeof s?.effortLevel === 'string' && s.effortLevel)
|
|
2748
|
+
setEffortLevel(s.effortLevel);
|
|
2749
|
+
if (s?.effortByMode &&
|
|
2750
|
+
typeof s.effortByMode === 'object' &&
|
|
2751
|
+
!Array.isArray(s.effortByMode)) {
|
|
2752
|
+
const next = {};
|
|
2753
|
+
for (const m of ['plan', 'execute']) {
|
|
2754
|
+
const raw = s.effortByMode[m];
|
|
2755
|
+
if (typeof raw === 'string' && raw)
|
|
2756
|
+
next[m] = raw;
|
|
2757
|
+
}
|
|
2758
|
+
setEffortByMode(next);
|
|
2759
|
+
}
|
|
2760
|
+
if (s?.modelRegions &&
|
|
2761
|
+
typeof s.modelRegions === 'object' &&
|
|
2762
|
+
!Array.isArray(s.modelRegions)) {
|
|
2763
|
+
const nextRegions = {};
|
|
2764
|
+
for (const [id, raw] of Object.entries(s.modelRegions)) {
|
|
2765
|
+
if (typeof raw === 'string' && raw in MODEL_REGION_META)
|
|
2766
|
+
nextRegions[id] = raw;
|
|
2767
|
+
}
|
|
2768
|
+
setModelRegions(nextRegions);
|
|
2769
|
+
}
|
|
2770
|
+
if (typeof s?.maxToolLoops === 'number')
|
|
2771
|
+
setCurrentMaxLoops(s.maxToolLoops);
|
|
2772
|
+
if (typeof s?.autoFix === 'boolean')
|
|
2773
|
+
setAutoFixEnabled(s.autoFix);
|
|
2774
|
+
if (typeof s?.autoApproveCommands === 'boolean') {
|
|
2775
|
+
setAutoApproveCommandsEnabled(s.autoApproveCommands);
|
|
2776
|
+
}
|
|
2707
2777
|
})
|
|
2708
2778
|
.catch(() => {
|
|
2709
|
-
// Non-fatal: the
|
|
2710
|
-
//
|
|
2779
|
+
// Non-fatal: the change was persisted server-side; this view just won't
|
|
2780
|
+
// refresh until the next signal or full settings read.
|
|
2711
2781
|
});
|
|
2712
2782
|
}, [modelSelectionSignal, http, projectId]);
|
|
2713
2783
|
// Persist the auto-commit cadence to project.settings (debounced) so it
|
|
@@ -3545,6 +3615,23 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3545
3615
|
return;
|
|
3546
3616
|
}
|
|
3547
3617
|
setModelPicker(null);
|
|
3618
|
+
// Show the effort picker while typing "/effort ..." — same live behavior
|
|
3619
|
+
// as /model. Text after the command (minus the --plan/--execute flag)
|
|
3620
|
+
// filters the level list; "?" keeps the textual status path instead. A
|
|
3621
|
+
// dropdown-chosen mode survives further keystrokes unless a flag names one.
|
|
3622
|
+
if (/^\/effort\s/i.test(val)) {
|
|
3623
|
+
const typed = parseEffortCommand(val);
|
|
3624
|
+
if (typed && (typed.kind === 'menu' || (typed.kind === 'set' && typed.arg !== '?'))) {
|
|
3625
|
+
const typedMode = typed.mode;
|
|
3626
|
+
setEffortPicker((p) => ({
|
|
3627
|
+
selectedIdx: -1,
|
|
3628
|
+
mode: typedMode ?? p?.mode ?? liveModelMode,
|
|
3629
|
+
}));
|
|
3630
|
+
setCommandMenu(null);
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
setEffortPicker(null);
|
|
3548
3635
|
if (val.startsWith('/') && !val.includes(' ')) {
|
|
3549
3636
|
setCommandMenu({ selectedIdx: -1 });
|
|
3550
3637
|
}
|
|
@@ -3640,6 +3727,29 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3640
3727
|
logger.warn('Failed to update model processing region', { error });
|
|
3641
3728
|
}
|
|
3642
3729
|
}, [http, projectId, modelRegions]);
|
|
3730
|
+
// Persist a mode's reasoning effort (used by both the /effort <level> typed
|
|
3731
|
+
// path and the effort picker). Shallow-merge on the server means we send the
|
|
3732
|
+
// WHOLE effortByMode map, not just the changed entry.
|
|
3733
|
+
const applyEffortLevel = useCallback(async (targetMode, level, targetModel) => {
|
|
3734
|
+
setEffortPicker(null);
|
|
3735
|
+
setInputValue('');
|
|
3736
|
+
try {
|
|
3737
|
+
const nextByMode = { ...effortByMode, [targetMode]: level };
|
|
3738
|
+
await http.patch(settingsPatchUrl(), { settings: { effortByMode: nextByMode } });
|
|
3739
|
+
setEffortByMode(nextByMode);
|
|
3740
|
+
addSystemCard(t('ide.chat.effort.setMode', {
|
|
3741
|
+
mode: targetMode,
|
|
3742
|
+
level: nativeEffortName(targetModel, level) ?? level,
|
|
3743
|
+
model: targetModel?.label ?? '?',
|
|
3744
|
+
}, { defaultValue: 'Reasoning effort for {{mode}} set to {{level}} ({{model}}).' }));
|
|
3745
|
+
}
|
|
3746
|
+
catch (error) {
|
|
3747
|
+
logger.warn('Failed to update reasoning effort level', { error });
|
|
3748
|
+
addSystemCard(t('ide.chat.effort.error', undefined, {
|
|
3749
|
+
defaultValue: 'Failed to update reasoning effort.',
|
|
3750
|
+
}));
|
|
3751
|
+
}
|
|
3752
|
+
}, [http, effortByMode, settingsPatchUrl, addSystemCard]);
|
|
3643
3753
|
// The shared command registry UNION any host-provided commands (e.g.
|
|
3644
3754
|
// molecule.dev's /deploy, /push, /invite, /teamsay), so the menu, grouping,
|
|
3645
3755
|
// and dispatch all see one list and host commands never go missing. Declared
|
|
@@ -3648,11 +3758,12 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3648
3758
|
const extraCommandIds = useMemo(() => new Set((extraCommands ?? []).map((c) => c.id)), [extraCommands]);
|
|
3649
3759
|
const executeCommand = useCallback(async (id) => {
|
|
3650
3760
|
setCommandMenu(null);
|
|
3651
|
-
// Any command closes every sibling popup (panel overlay, model/
|
|
3652
|
-
// pickers) so popups are strictly one-at-a-time — the branches
|
|
3653
|
-
// re-open their own.
|
|
3761
|
+
// Any command closes every sibling popup (panel overlay, model/effort/
|
|
3762
|
+
// sounds/mic pickers) so popups are strictly one-at-a-time — the branches
|
|
3763
|
+
// below re-open their own.
|
|
3654
3764
|
setPanelOverlay(null);
|
|
3655
3765
|
setModelPicker(null);
|
|
3766
|
+
setEffortPicker(null);
|
|
3656
3767
|
setSoundsPicker(null);
|
|
3657
3768
|
setMicPicker(null);
|
|
3658
3769
|
// Host-provided commands (e.g. molecule.dev's /deploy, /push, /invite,
|
|
@@ -3697,8 +3808,10 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3697
3808
|
setInputAndCursorEnd('/maxloops ');
|
|
3698
3809
|
}
|
|
3699
3810
|
else if (id === 'effort') {
|
|
3700
|
-
//
|
|
3701
|
-
|
|
3811
|
+
// Open the selectable level picker (mirrors /model) scoped to the live
|
|
3812
|
+
// conversation mode; its mode dropdown re-scopes in place.
|
|
3813
|
+
setInputValue('');
|
|
3814
|
+
setEffortPicker({ selectedIdx: -1, mode: liveModelMode });
|
|
3702
3815
|
}
|
|
3703
3816
|
else if (id === 'autocommit') {
|
|
3704
3817
|
// Prefill so the user types the cadence (seconds); 0 cancels.
|
|
@@ -4209,6 +4322,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4209
4322
|
setPanelOverlay(null);
|
|
4210
4323
|
setCommandMenu(null);
|
|
4211
4324
|
setModelPicker(null);
|
|
4325
|
+
setEffortPicker(null);
|
|
4212
4326
|
setSoundsPicker(null);
|
|
4213
4327
|
setMicPicker({ autoStart: false });
|
|
4214
4328
|
return;
|
|
@@ -4258,6 +4372,18 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4258
4372
|
const effortCmd = parseEffortCommand(trimmed);
|
|
4259
4373
|
if (effortCmd) {
|
|
4260
4374
|
setInputValue('');
|
|
4375
|
+
// Bare /effort (or a bare --plan/--execute flag) opens the selectable
|
|
4376
|
+
// level picker — same interaction as bare /model. The picker lists the
|
|
4377
|
+
// target mode's model's own levels; its mode dropdown re-scopes in place.
|
|
4378
|
+
if (effortCmd.kind === 'menu') {
|
|
4379
|
+
setPanelOverlay(null);
|
|
4380
|
+
setCommandMenu(null);
|
|
4381
|
+
setModelPicker(null);
|
|
4382
|
+
setSoundsPicker(null);
|
|
4383
|
+
setMicPicker(null);
|
|
4384
|
+
setEffortPicker({ selectedIdx: -1, mode: effortCmd.mode ?? liveModelMode });
|
|
4385
|
+
return;
|
|
4386
|
+
}
|
|
4261
4387
|
// Resolve the model a given mode will actually use — mirrors the /model
|
|
4262
4388
|
// picker + slash-suffix resolveModeModel logic (P2-10: each mode's
|
|
4263
4389
|
// options come from ITS model's reasoning capabilities).
|
|
@@ -4315,22 +4441,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4315
4441
|
}, { defaultValue: "{{level}} isn't available for {{model}}. Available: {{levels}}" }));
|
|
4316
4442
|
return;
|
|
4317
4443
|
}
|
|
4318
|
-
|
|
4319
|
-
const nextByMode = { ...effortByMode, [targetMode]: level };
|
|
4320
|
-
await http.patch(settingsPatchUrl(), { settings: { effortByMode: nextByMode } });
|
|
4321
|
-
setEffortByMode(nextByMode);
|
|
4322
|
-
addSystemCard(t('ide.chat.effort.setMode', {
|
|
4323
|
-
mode: targetMode,
|
|
4324
|
-
level: nativeEffortName(targetModel, level) ?? effortCmd.arg,
|
|
4325
|
-
model: targetModelLabel,
|
|
4326
|
-
}, { defaultValue: 'Reasoning effort for {{mode}} set to {{level}} ({{model}}).' }));
|
|
4327
|
-
}
|
|
4328
|
-
catch (error) {
|
|
4329
|
-
logger.warn('Failed to update reasoning effort level', { error });
|
|
4330
|
-
addSystemCard(t('ide.chat.effort.error', undefined, {
|
|
4331
|
-
defaultValue: 'Failed to update reasoning effort.',
|
|
4332
|
-
}));
|
|
4333
|
-
}
|
|
4444
|
+
await applyEffortLevel(targetMode, level, targetModel);
|
|
4334
4445
|
}
|
|
4335
4446
|
return;
|
|
4336
4447
|
}
|
|
@@ -4406,6 +4517,20 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4406
4517
|
// event (persisted + broadcast to every member), which IS the visible message — so the
|
|
4407
4518
|
// transcript never shows the literal "/command" text, and never shows it twice.
|
|
4408
4519
|
if (matchesSideChannelCommand(allCommands, trimmed)) {
|
|
4520
|
+
// The sent note must not survive as a draft: the keystroke-debounced
|
|
4521
|
+
// persistDraft already holds the full "/teamsay …" text, and the viewer
|
|
4522
|
+
// re-prefill below is non-empty so setInputValue never clears it — a
|
|
4523
|
+
// reload then reopened the already-SENT message in the composer. Cancel
|
|
4524
|
+
// any pending draft write and drop the stored draft; the mount prefill
|
|
4525
|
+
// recreates the bare prefix.
|
|
4526
|
+
if (draftTimerRef.current)
|
|
4527
|
+
clearTimeout(draftTimerRef.current);
|
|
4528
|
+
try {
|
|
4529
|
+
sessionStorage.removeItem(draftKey);
|
|
4530
|
+
}
|
|
4531
|
+
catch (_error) {
|
|
4532
|
+
/* sessionStorage unavailable — draft persistence is best-effort */
|
|
4533
|
+
}
|
|
4409
4534
|
// A viewer's composer is the team-chat box — re-fill the /teamsay prefix
|
|
4410
4535
|
// after each sent team message so the next one is one keystroke away.
|
|
4411
4536
|
if (canEdit === false)
|
|
@@ -4470,6 +4595,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4470
4595
|
runSavedScript,
|
|
4471
4596
|
allCommands,
|
|
4472
4597
|
selectModel,
|
|
4598
|
+
applyEffortLevel,
|
|
4473
4599
|
liveModelMode,
|
|
4474
4600
|
]);
|
|
4475
4601
|
// External auto-submit. When the signal changes, submit the current input —
|
|
@@ -4686,6 +4812,43 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4686
4812
|
? (resolveModeModel({ planModel, executeModel, commitModel, compactModel, chatModel: savedChatModel }, modelPicker.mode) ?? serverModelDefaults?.[modelPicker.mode])
|
|
4687
4813
|
: savedChatModel || undefined
|
|
4688
4814
|
: undefined;
|
|
4815
|
+
// ── Effort picker derived state ─────────────────────────────────────────────
|
|
4816
|
+
// The model whose levels the open /effort picker lists — the model the
|
|
4817
|
+
// selected mode will actually run — plus its selectable options and the level
|
|
4818
|
+
// the "current" pill marks (the persisted per-mode value resolved per-model,
|
|
4819
|
+
// so an unset mode correctly pills the model's own default). Cheap to
|
|
4820
|
+
// recompute per render, mirroring pickerModeOptions above.
|
|
4821
|
+
const effortPickerModel = effortPicker
|
|
4822
|
+
? AVAILABLE_MODELS.find((m) => m.id === effectiveModelForMode(effortPicker.mode))
|
|
4823
|
+
: undefined;
|
|
4824
|
+
const effortPickerOptions = effortOptionsForModel(effortPickerModel);
|
|
4825
|
+
// Typed filter, mirroring filteredModels: the text after "/effort" (minus a
|
|
4826
|
+
// mode flag) narrows the rows, so "/effort xh" + Enter selects xhigh. Read
|
|
4827
|
+
// from inputRef — it stays fresh because each keystroke re-sets effortPicker.
|
|
4828
|
+
const effortPickerVisibleOptions = (() => {
|
|
4829
|
+
if (!effortPicker)
|
|
4830
|
+
return [];
|
|
4831
|
+
const typed = parseEffortCommand(inputRef.current);
|
|
4832
|
+
const q = typed?.kind === 'set' ? typed.arg.toLowerCase() : '';
|
|
4833
|
+
if (!q)
|
|
4834
|
+
return effortPickerOptions;
|
|
4835
|
+
return effortPickerOptions.filter((o) => o.value.toLowerCase().includes(q));
|
|
4836
|
+
})();
|
|
4837
|
+
const effortPickerCurrent = effortPicker
|
|
4838
|
+
? nativeEffortName(effortPickerModel, effortByMode[effortPicker.mode] ?? (effortLevel || undefined))
|
|
4839
|
+
: null;
|
|
4840
|
+
// Mode dropdown rows ("Plan · <model>") — effort exists only for the two
|
|
4841
|
+
// conversation modes, each scoped to ITS model's native levels.
|
|
4842
|
+
const effortPickerModeOptions = ['plan', 'execute'].map((m) => ({
|
|
4843
|
+
value: m,
|
|
4844
|
+
label: t('ide.chat.modelModeOption', {
|
|
4845
|
+
mode: m === 'plan'
|
|
4846
|
+
? t('ide.chat.settings.modePlan', undefined, { defaultValue: 'Plan' })
|
|
4847
|
+
: t('ide.chat.settings.modeExecute', undefined, { defaultValue: 'Execute' }),
|
|
4848
|
+
model: AVAILABLE_MODELS.find((x) => x.id === effectiveModelForMode(m))?.label ??
|
|
4849
|
+
effectiveModelForMode(m),
|
|
4850
|
+
}, { defaultValue: '{{mode}} · {{model}}' }),
|
|
4851
|
+
}));
|
|
4689
4852
|
const filteredEntries = useMemo(() => {
|
|
4690
4853
|
if (!filePicker)
|
|
4691
4854
|
return [];
|
|
@@ -4747,6 +4910,10 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4747
4910
|
setMicPicker(null);
|
|
4748
4911
|
return;
|
|
4749
4912
|
}
|
|
4913
|
+
if (effortPicker) {
|
|
4914
|
+
setEffortPicker(null);
|
|
4915
|
+
return;
|
|
4916
|
+
}
|
|
4750
4917
|
if (modelPicker) {
|
|
4751
4918
|
setModelPicker(null);
|
|
4752
4919
|
return;
|
|
@@ -4791,6 +4958,43 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4791
4958
|
return;
|
|
4792
4959
|
}
|
|
4793
4960
|
}
|
|
4961
|
+
// Effort picker: one row per (typed-filter-matching) level of the selected
|
|
4962
|
+
// mode's model. A fixed-reasoning model or an unmatched filter lists
|
|
4963
|
+
// nothing — arrows/Enter fall through (Escape closes; Enter submits the
|
|
4964
|
+
// typed text, so an unknown level still gets the "isn't available" card).
|
|
4965
|
+
if (effortPicker && effortPickerVisibleOptions.length > 0) {
|
|
4966
|
+
if (e.key === 'ArrowDown') {
|
|
4967
|
+
e.preventDefault();
|
|
4968
|
+
setEffortPicker((p) => p
|
|
4969
|
+
? { ...p, selectedIdx: wrapIdx(p.selectedIdx, 1, effortPickerVisibleOptions.length) }
|
|
4970
|
+
: null);
|
|
4971
|
+
return;
|
|
4972
|
+
}
|
|
4973
|
+
if (e.key === 'ArrowUp') {
|
|
4974
|
+
e.preventDefault();
|
|
4975
|
+
setEffortPicker((p) => p
|
|
4976
|
+
? { ...p, selectedIdx: wrapIdx(p.selectedIdx, -1, effortPickerVisibleOptions.length) }
|
|
4977
|
+
: null);
|
|
4978
|
+
return;
|
|
4979
|
+
}
|
|
4980
|
+
if (e.key === 'Enter' || e.key === 'Tab') {
|
|
4981
|
+
e.preventDefault();
|
|
4982
|
+
// Highlighted row wins; otherwise an exactly-typed level beats the
|
|
4983
|
+
// first substring match ("/effort high" must never apply xhigh's
|
|
4984
|
+
// neighbor), then default to the first visible row (mirrors /model).
|
|
4985
|
+
let option = effortPickerVisibleOptions[effortPicker.selectedIdx];
|
|
4986
|
+
if (!option) {
|
|
4987
|
+
const typed = parseEffortCommand(inputRef.current);
|
|
4988
|
+
const q = typed?.kind === 'set' ? typed.arg.toLowerCase() : '';
|
|
4989
|
+
option =
|
|
4990
|
+
(q ? effortPickerVisibleOptions.find((o) => o.value.toLowerCase() === q) : undefined) ??
|
|
4991
|
+
effortPickerVisibleOptions[0];
|
|
4992
|
+
}
|
|
4993
|
+
if (option)
|
|
4994
|
+
void applyEffortLevel(effortPicker.mode, option.value, effortPickerModel);
|
|
4995
|
+
return;
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4794
4998
|
// The optional "manage your own models" row sits after the model rows and
|
|
4795
4999
|
// participates in arrow/Enter navigation as the last index.
|
|
4796
5000
|
const modelRowCount = visibleModels.length + (onManageCustomModels ? 1 : 0);
|
|
@@ -6200,7 +6404,108 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6200
6404
|
: 'color-mix(in srgb, var(--mol-color-primary, #6366f1) 12%, transparent)',
|
|
6201
6405
|
}, children: t('ide.chat.manageCustomModels', undefined, {
|
|
6202
6406
|
defaultValue: 'Add or manage your own models…',
|
|
6203
|
-
}) }))] })),
|
|
6407
|
+
}) }))] })), effortPicker && (_jsxs("div", { className: cm.cn(cm.surface, cm.borderAll), style: {
|
|
6408
|
+
position: 'absolute',
|
|
6409
|
+
bottom: '100%',
|
|
6410
|
+
left: 0,
|
|
6411
|
+
right: 0,
|
|
6412
|
+
marginBottom: 0,
|
|
6413
|
+
borderRadius: '6px 6px 0 0',
|
|
6414
|
+
zIndex: 100,
|
|
6415
|
+
boxShadow: '0 -4px 16px rgba(0,0,0,0.25)',
|
|
6416
|
+
display: 'flex',
|
|
6417
|
+
flexDirection: 'column',
|
|
6418
|
+
maxHeight: popupMaxHeight,
|
|
6419
|
+
}, children: [_jsxs("div", { className: cm.cn(cm.textSize('xs'), cm.textMuted), style: {
|
|
6420
|
+
padding: '6px 12px',
|
|
6421
|
+
borderBottom: '1px solid rgba(128,128,128,0.12)',
|
|
6422
|
+
display: 'flex',
|
|
6423
|
+
alignItems: 'center',
|
|
6424
|
+
gap: 10,
|
|
6425
|
+
flexShrink: 0,
|
|
6426
|
+
flexWrap: 'wrap',
|
|
6427
|
+
}, children: [_jsx("span", { style: { flexShrink: 0 }, children: t('ide.chat.settings.effort.label', undefined, {
|
|
6428
|
+
defaultValue: 'Reasoning effort',
|
|
6429
|
+
}) }), _jsxs("div", { style: {
|
|
6430
|
+
display: 'flex',
|
|
6431
|
+
alignItems: 'center',
|
|
6432
|
+
gap: 6,
|
|
6433
|
+
flex: '1 1 190px',
|
|
6434
|
+
minWidth: 0,
|
|
6435
|
+
}, children: [_jsx("span", { style: { flexShrink: 0 }, children: t('ide.chat.modelModeLabel', undefined, { defaultValue: 'Mode' }) }), _jsx("select", { "data-mol-id": "chat-effort-mode-select", "aria-label": t('ide.chat.modelModeLabel', undefined, { defaultValue: 'Mode' }), value: effortPicker.mode, onChange: (e) => {
|
|
6436
|
+
const v = e.target.value;
|
|
6437
|
+
// Reset the highlighted row — the level list changes with
|
|
6438
|
+
// the mode's model.
|
|
6439
|
+
setEffortPicker((p) => (p ? { mode: v, selectedIdx: -1 } : p));
|
|
6440
|
+
}, className: cm.cn(cm.surfaceSecondary, cm.borderAll, cm.textSize('xs')), style: {
|
|
6441
|
+
borderRadius: 4,
|
|
6442
|
+
// Extra right padding clears the native dropdown arrow so
|
|
6443
|
+
// the selected label never runs underneath it.
|
|
6444
|
+
padding: '2px 18px 2px 6px',
|
|
6445
|
+
color: 'inherit',
|
|
6446
|
+
cursor: 'pointer',
|
|
6447
|
+
height: 24,
|
|
6448
|
+
boxSizing: 'border-box',
|
|
6449
|
+
flex: 1,
|
|
6450
|
+
minWidth: 0,
|
|
6451
|
+
overflow: 'hidden',
|
|
6452
|
+
whiteSpace: 'nowrap',
|
|
6453
|
+
textOverflow: 'ellipsis',
|
|
6454
|
+
}, children: effortPickerModeOptions.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value))) })] }), _jsx("button", { type: "button", "data-mol-id": "chat-effort-picker-close", onClick: () => setEffortPicker(null), style: {
|
|
6455
|
+
background: 'none',
|
|
6456
|
+
border: 'none',
|
|
6457
|
+
cursor: 'pointer',
|
|
6458
|
+
color: 'inherit',
|
|
6459
|
+
padding: '0 2px',
|
|
6460
|
+
fontSize: '14px',
|
|
6461
|
+
lineHeight: 1,
|
|
6462
|
+
opacity: 0.6,
|
|
6463
|
+
// Touch floor for this secondary header ✕ (36px, like the
|
|
6464
|
+
// notice-card actions); fine pointers keep the slim header.
|
|
6465
|
+
...(isCoarse
|
|
6466
|
+
? {
|
|
6467
|
+
display: 'inline-flex',
|
|
6468
|
+
alignItems: 'center',
|
|
6469
|
+
justifyContent: 'center',
|
|
6470
|
+
minWidth: 36,
|
|
6471
|
+
minHeight: 36,
|
|
6472
|
+
}
|
|
6473
|
+
: {}),
|
|
6474
|
+
}, children: '✕' })] }), _jsx("div", { style: { overflowY: 'auto', flex: 1 }, children: effortPickerOptions.length === 0 ? (_jsx("div", { className: cm.cn(cm.textSize('sm'), cm.textMuted), style: { padding: 12 }, children: t('ide.chat.effort.fixedForModel', { mode: effortPicker.mode, model: effortPickerModel?.label ?? '?' }, {
|
|
6475
|
+
defaultValue: 'Reasoning effort is fixed on {{model}} ({{mode}} mode) — nothing to set.',
|
|
6476
|
+
}) })) : (effortPickerVisibleOptions.map((option, idx) => (_jsxs("button", { type: "button", "data-mol-id": `chat-effort-level-${option.value}`, onClick: () => void applyEffortLevel(effortPicker.mode, option.value, effortPickerModel), onMouseEnter: (e) => {
|
|
6477
|
+
;
|
|
6478
|
+
e.currentTarget.style.background = 'rgba(128,128,128,0.15)';
|
|
6479
|
+
}, onMouseLeave: (e) => {
|
|
6480
|
+
;
|
|
6481
|
+
e.currentTarget.style.background =
|
|
6482
|
+
idx === effortPicker.selectedIdx ? 'rgba(128,128,128,0.1)' : 'transparent';
|
|
6483
|
+
}, className: cm.w('full'), style: {
|
|
6484
|
+
display: 'flex',
|
|
6485
|
+
alignItems: 'center',
|
|
6486
|
+
gap: '6px',
|
|
6487
|
+
width: '100%',
|
|
6488
|
+
minHeight: '40px',
|
|
6489
|
+
padding: '8px 12px',
|
|
6490
|
+
border: 'none',
|
|
6491
|
+
borderTop: idx === 0 ? 'none' : '1px solid rgba(128,128,128,0.12)',
|
|
6492
|
+
cursor: 'pointer',
|
|
6493
|
+
color: 'inherit',
|
|
6494
|
+
textAlign: 'left',
|
|
6495
|
+
fontSize: '13px',
|
|
6496
|
+
background: idx === effortPicker.selectedIdx ? 'rgba(128,128,128,0.1)' : 'transparent',
|
|
6497
|
+
}, children: [_jsx("span", { className: cm.fontWeight('medium'), children: option.value }), defaultEffortForModel(effortPickerModel) === option.value && (_jsx("span", { className: cm.textMuted, style: { fontSize: '10px' }, children: t('ide.chat.modelMode.default', undefined, { defaultValue: 'Default' }) })), effortPickerCurrent === option.value && (_jsx("span", { "data-mol-id": `effort-current-${option.value}`, className: cm.fontWeight('medium'), style: {
|
|
6498
|
+
// Right-aligned + primary-tinted, matching the model
|
|
6499
|
+
// picker's "current" pill (hex is only the var()
|
|
6500
|
+
// fallback; the theme token wins).
|
|
6501
|
+
marginLeft: 'auto',
|
|
6502
|
+
fontSize: '10px',
|
|
6503
|
+
color: 'var(--mol-color-primary, #6366f1)',
|
|
6504
|
+
background: 'color-mix(in srgb, var(--mol-color-primary, #6366f1) 16%, transparent)',
|
|
6505
|
+
border: '1px solid color-mix(in srgb, var(--mol-color-primary, #6366f1) 42%, transparent)',
|
|
6506
|
+
padding: '1px 7px',
|
|
6507
|
+
borderRadius: '999px',
|
|
6508
|
+
}, children: t('ide.chat.currentBadge', undefined, { defaultValue: 'current' }) }))] }, option.value)))) })] })), soundsPicker && (_jsxs("div", { className: cm.cn(cm.surface, cm.borderAll), style: {
|
|
6204
6509
|
position: 'absolute',
|
|
6205
6510
|
bottom: '100%',
|
|
6206
6511
|
left: 0,
|
|
@@ -6441,7 +6746,11 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6441
6746
|
? t('ide.chat.activeFile', undefined, { defaultValue: 'active' })
|
|
6442
6747
|
: t('ide.chat.openTab', undefined, { defaultValue: 'open' }) }))] }, entry.name));
|
|
6443
6748
|
}) }));
|
|
6444
|
-
})(), queuedMessages.length > 0 &&
|
|
6749
|
+
})(), queuedMessages.length > 0 &&
|
|
6750
|
+
!commandMenu &&
|
|
6751
|
+
!modelPicker &&
|
|
6752
|
+
!effortPicker &&
|
|
6753
|
+
!panelOverlay && (_jsxs("div", { style: {
|
|
6445
6754
|
borderTop: '1px solid rgba(128,128,128,0.15)',
|
|
6446
6755
|
padding: '6px 8px 6px 10px',
|
|
6447
6756
|
}, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 3 }, children: [_jsx(Icon, { name: "clock", size: 11, "aria-hidden": "true", style: { opacity: 0.5, flexShrink: 0 } }), _jsx("span", { className: cm.cn(cm.textMuted, cm.textSize('xs')), style: { fontStyle: 'italic' }, children: t('ide.chat.queuedCount', { count: queuedMessages.length }, { defaultValue: '{{count}} queued' }) })] }), _jsx("div", { style: {
|
|
@@ -6577,6 +6886,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6577
6886
|
pendingFiles.length > 0 &&
|
|
6578
6887
|
!commandMenu &&
|
|
6579
6888
|
!modelPicker &&
|
|
6889
|
+
!effortPicker &&
|
|
6580
6890
|
!panelOverlay && (_jsxs("div", { style: {
|
|
6581
6891
|
borderTop: '1px solid rgba(128,128,128,0.15)',
|
|
6582
6892
|
// Equal top/right/bottom (8px) so the commit button — flush to the
|
|
@@ -6942,6 +7252,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6942
7252
|
// Popups are one-at-a-time — close siblings first.
|
|
6943
7253
|
setMicPicker(null);
|
|
6944
7254
|
setModelPicker(null);
|
|
7255
|
+
setEffortPicker(null);
|
|
6945
7256
|
setSoundsPicker(null);
|
|
6946
7257
|
const cur = inputRef.current;
|
|
6947
7258
|
if (!cur)
|
|
@@ -7124,7 +7435,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
7124
7435
|
* @param props - Component props (see {@link MessageItemProps}).
|
|
7125
7436
|
* @returns The rendered chat panel element.
|
|
7126
7437
|
*/
|
|
7127
|
-
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, 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, canShare, buildUpgradeCta, buildHelpUpgradeSection, userAvatar, agentName, productName, version, extraCommands, feedbackUrl, className, }) {
|
|
7438
|
+
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, canShare, buildUpgradeCta, buildHelpUpgradeSection, userAvatar, agentName, productName, version, extraCommands, feedbackUrl, className, }) {
|
|
7128
7439
|
const cm = getClassMap();
|
|
7129
7440
|
// Share management may be gated ABOVE canEdit by the host (see
|
|
7130
7441
|
// ChatPanelProps.canShare) — gates the built-in header share button here and
|
|
@@ -7337,7 +7648,7 @@ export function ChatPanel({ projectId, endpoint, initialMessage, onInitialMessag
|
|
|
7337
7648
|
textOverflow: 'ellipsis',
|
|
7338
7649
|
whiteSpace: 'nowrap',
|
|
7339
7650
|
width: '100%',
|
|
7340
|
-
}, 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, canShare: shareAllowed, 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, 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 }, chatKey)] }));
|
|
7651
|
+
}, 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, canShare: shareAllowed, 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 }, chatKey)] }));
|
|
7341
7652
|
}
|
|
7342
7653
|
ChatPanel.displayName = 'ChatPanel';
|
|
7343
7654
|
//# sourceMappingURL=ChatPanel.js.map
|