@molecule/app-ide-react 1.4.0 → 1.6.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 +100 -1
- package/dist/command-metadata.d.ts +39 -0
- package/dist/command-metadata.d.ts.map +1 -1
- package/dist/command-metadata.js +37 -1
- package/dist/command-metadata.js.map +1 -1
- package/dist/components/ChatPanel.d.ts +9 -1
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +199 -27
- package/dist/components/ChatPanel.js.map +1 -1
- package/dist/components/ShareLinkManager.d.ts +63 -0
- package/dist/components/ShareLinkManager.d.ts.map +1 -0
- package/dist/components/ShareLinkManager.js +162 -0
- package/dist/components/ShareLinkManager.js.map +1 -0
- package/dist/components/ShareModal.d.ts +7 -11
- package/dist/components/ShareModal.d.ts.map +1 -1
- package/dist/components/ShareModal.js +5 -99
- package/dist/components/ShareModal.js.map +1 -1
- package/dist/components/TipCard.d.ts +7 -2
- package/dist/components/TipCard.d.ts.map +1 -1
- package/dist/components/TipCard.js +8 -6
- package/dist/components/TipCard.js.map +1 -1
- package/dist/components/UserAvatar.d.ts +10 -1
- package/dist/components/UserAvatar.d.ts.map +1 -1
- package/dist/components/UserAvatar.js +9 -6
- package/dist/components/UserAvatar.js.map +1 -1
- package/dist/components/chat-share-utilities.d.ts +6 -0
- package/dist/components/chat-share-utilities.d.ts.map +1 -1
- package/dist/components/chat-share-utilities.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/types.d.ts +30 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
|
@@ -16,7 +16,7 @@ import { ActivityCard } from './ActivityCard.js';
|
|
|
16
16
|
import { AutoCommitBadge } from './AutoCommitBadge.js';
|
|
17
17
|
import { AUTO_COMMIT_DISABLED, autoCommitReducer, isAutoCommitArmed, isAutoCommitDue, isAutoCommitEnabled, parseAutoCommitCommand, resolveAutoCommitSeconds, } from './chat-autocommit-utilities.js';
|
|
18
18
|
import { CHAT_CARD_ICON_SIZE, chatCardBorder, chatCardStyle } from './chat-card-style.js';
|
|
19
|
-
import { COMMAND_CATEGORIES, COMMANDS } from './chat-commands.js';
|
|
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
22
|
import { effortOptionsForModel, nativeEffortName, parseEffortCommand, resolveEffortArg, } from './chat-effort-utilities.js';
|
|
@@ -928,7 +928,7 @@ export function CommitCardItem({ card, onRevert, }) {
|
|
|
928
928
|
* @returns The rendered message item.
|
|
929
929
|
*/
|
|
930
930
|
const MessageItem = memo(function MessageItem(props) {
|
|
931
|
-
const { msg, sendMessage, handleAskUserResponse, isLoading, streamingStatus, onNavigatePreview, undoneTcIds, handleUndoToggle, onFileOpen, onFileDoubleClick, onFileDiff, handleFileRevert, setInputAndCursorEnd, setModelPicker, userAvatar, onAvatarClick, discovery, buildUpgradeCta, } = props;
|
|
931
|
+
const { msg, sendMessage, handleAskUserResponse, isLoading, streamingStatus, onNavigatePreview, undoneTcIds, handleUndoToggle, onFileOpen, onFileDoubleClick, onFileDiff, handleFileRevert, setInputAndCursorEnd, setModelPicker, userAvatar, onAvatarClick, discovery, buildUpgradeCta, agentName, } = props;
|
|
932
932
|
const cm = getClassMap();
|
|
933
933
|
const themeMode = useThemeMode();
|
|
934
934
|
const isLight = themeMode === 'light';
|
|
@@ -936,6 +936,11 @@ const MessageItem = memo(function MessageItem(props) {
|
|
|
936
936
|
// A message sent automatically on the user's behalf (e.g. an auto-fix prompt):
|
|
937
937
|
// it has role 'user' but must NOT look like the user typed it (C2).
|
|
938
938
|
const isAutomatic = msg.role === 'user' && !!msg.automatic;
|
|
939
|
+
// A human-only team note (side channel, e.g. /teamsay): renders like a user
|
|
940
|
+
// message — author header, time, plain content — but with the gold team-only
|
|
941
|
+
// accent + badge, and never the user message's blue stripe. `role` is 'system'
|
|
942
|
+
// (the model never sees it), so this is checked before isUser.
|
|
943
|
+
const isTeamNote = !!msg.teamOnly;
|
|
939
944
|
// A real, user-typed message (the only one styled with the blue border + the
|
|
940
945
|
// user's own avatar).
|
|
941
946
|
const isUser = msg.role === 'user' && !isAutomatic;
|
|
@@ -949,7 +954,7 @@ const MessageItem = memo(function MessageItem(props) {
|
|
|
949
954
|
const wrapperSpacing = {
|
|
950
955
|
marginBottom: `${discovery ? TIMELINE_ITEM_GAP_DISCOVERY : TIMELINE_ITEM_GAP}px`,
|
|
951
956
|
};
|
|
952
|
-
return (_jsx("div", { style: wrapperSpacing, children: isUser || isAutomatic ? (_jsxs("div", { className:
|
|
957
|
+
return (_jsx("div", { style: wrapperSpacing, children: isUser || isAutomatic || isTeamNote ? (_jsxs("div", { className:
|
|
953
958
|
// Auto-sent card matches the info cards' `xs` body; a real user message keeps
|
|
954
959
|
// its slightly larger `sm` (it's a different kind of row, not an info card).
|
|
955
960
|
isAutomatic ? cm.textSize('xs') : cm.cn(cm.surfaceSecondary, cm.textSize('sm')), style: {
|
|
@@ -962,7 +967,9 @@ const MessageItem = memo(function MessageItem(props) {
|
|
|
962
967
|
// drawn by the `::before` injected via USER_ACCENT_STYLE + gated on the
|
|
963
968
|
// data-mol-id below, and the user's full-size avatar. An auto-sent message
|
|
964
969
|
// is instead a green (success) tinted card — same chrome as the info cards
|
|
965
|
-
// — so it's unmistakably agent-sent, not user-typed (C2).
|
|
970
|
+
// — so it's unmistakably agent-sent, not user-typed (C2). A team note keeps
|
|
971
|
+
// the user-message look but swaps the blue stripe (its data-mol-id differs,
|
|
972
|
+
// so USER_ACCENT_STYLE never applies) for the gold team-only border.
|
|
966
973
|
...(isAutomatic
|
|
967
974
|
? chatCardStyle(AUTO_SENT_ACCENT)
|
|
968
975
|
: {
|
|
@@ -971,14 +978,41 @@ const MessageItem = memo(function MessageItem(props) {
|
|
|
971
978
|
paddingTop: '10px',
|
|
972
979
|
paddingBottom: '10px',
|
|
973
980
|
paddingRight: '10px',
|
|
981
|
+
...(isTeamNote ? { border: `1px solid ${NOTICE_TONE.gold.accent}` } : {}),
|
|
974
982
|
}),
|
|
975
|
-
}, "data-mol-id":
|
|
983
|
+
}, "data-mol-id": isTeamNote
|
|
984
|
+
? 'chat-team-message'
|
|
985
|
+
: isAutomatic
|
|
986
|
+
? 'chat-automatic-message'
|
|
987
|
+
: 'chat-user-message', children: [isAutomatic ? (_jsx(Icon, { name: "sync", size: CHAT_CARD_ICON_SIZE, "aria-hidden": "true", style: { flexShrink: 0, marginTop: 1, color: AUTO_SENT_ACCENT } })) : (_jsx(UserAvatar
|
|
988
|
+
// An AUTHORED message shows that author's avatar — or their initial
|
|
989
|
+
// when they have none — NEVER the viewing user's picture (which made
|
|
990
|
+
// a teammate's avatar-less message wear the viewer's own face). Only
|
|
991
|
+
// an author-less message (the local optimistic echo, legacy rows) is
|
|
992
|
+
// the signed-in user's own and uses their avatar.
|
|
993
|
+
, {
|
|
994
|
+
// An AUTHORED message shows that author's avatar — or their initial
|
|
995
|
+
// when they have none — NEVER the viewing user's picture (which made
|
|
996
|
+
// a teammate's avatar-less message wear the viewer's own face). Only
|
|
997
|
+
// an author-less message (the local optimistic echo, legacy rows) is
|
|
998
|
+
// the signed-in user's own and uses their avatar.
|
|
999
|
+
userAvatar: msg.author ? msg.author.avatar : userAvatar, name: msg.author?.name ?? undefined, size: 36, onClick: onAvatarClick })), _jsxs("div", { style: { flex: 1, minWidth: 0, marginTop: 1 }, children: [isAutomatic ? null : (_jsxs("div", { style: {
|
|
976
1000
|
display: 'flex',
|
|
977
1001
|
alignItems: 'baseline',
|
|
978
1002
|
gap: 8,
|
|
979
1003
|
lineHeight: 1.3,
|
|
980
1004
|
marginBottom: 1,
|
|
981
|
-
}, children: [_jsx("span", { style: { fontWeight: 600 }, children: msg.author?.name ?? t('ide.chat.you', undefined, { defaultValue: 'You' }) }),
|
|
1005
|
+
}, children: [_jsx("span", { style: { fontWeight: 600 }, children: msg.author?.name ?? t('ide.chat.you', undefined, { defaultValue: 'You' }) }), isTeamNote && (_jsx("span", { title: t('ide.chat.teamOnly.badge', { agentName: agentName ?? 'the assistant' }, {
|
|
1006
|
+
defaultValue: 'Team only — visible to your team; {{agentName}} will ignore it',
|
|
1007
|
+
}), "aria-label": t('ide.chat.teamOnly.badge', { agentName: agentName ?? 'the assistant' }, {
|
|
1008
|
+
defaultValue: 'Team only — visible to your team; {{agentName}} will ignore it',
|
|
1009
|
+
}), style: { display: 'inline-flex', alignSelf: 'center', flexShrink: 0 }, "data-mol-id": "chat-team-only-badge", children: _jsx(Icon
|
|
1010
|
+
// Same size as the username text (textSize('sm') ≈ 14px) so the
|
|
1011
|
+
// badge reads as part of the header line, not a footnote.
|
|
1012
|
+
, {
|
|
1013
|
+
// Same size as the username text (textSize('sm') ≈ 14px) so the
|
|
1014
|
+
// badge reads as part of the header line, not a footnote.
|
|
1015
|
+
name: "people", size: 14, "aria-hidden": "true", style: { color: NOTICE_TONE.gold.accent } }) })), typeof msg.timestamp === 'number' && (_jsx("span", { className: cm.textMuted, style: { fontSize: 11 }, children: relativeTimeLong(msg.timestamp) }))] })), _jsx(CollapsibleUserMessage, { content: msg.content, isLight: isLight }), msg.attachments && msg.attachments.length > 0 && (_jsx("div", { style: { marginTop: 4, display: 'flex', gap: 4, flexWrap: 'wrap' }, children: msg.attachments.map((att, ai) => (_jsxs("span", { className: cm.cn(cm.textMuted, cm.textSize('xs')), style: { display: 'inline-flex', alignItems: 'center', gap: 2 }, children: [att.mediaType.startsWith('image/')
|
|
982
1016
|
? '\uD83D\uDDBC\uFE0F'
|
|
983
1017
|
: att.mediaType.startsWith('audio/')
|
|
984
1018
|
? '\uD83C\uDFB5'
|
|
@@ -1152,7 +1186,7 @@ const MessageItem = memo(function MessageItem(props) {
|
|
|
1152
1186
|
* @param props - Component props (see {@link MessageItemProps}).
|
|
1153
1187
|
* @returns The rendered chat inner component.
|
|
1154
1188
|
*/
|
|
1155
|
-
function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent, isPro, isAnonymous, buildUpgradeCta, buildHelpUpgradeSection, activeFile, openTabs, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onFileChange, onFileDeleted, onCommit, onConversationId, onActivityClick, onRenderError, onProfileClick, onReadyToBuild, awaitingSandboxBoot, onClientAction, onTurnComplete, onLoadingChange, onNavigatePreview, onRegisterPushHandler, autoSubmitSignal, openSettingsSignal, onManageCustomModels, openReportSignal, openShareSignal, initialInputValue, pendingMessage, pendingMessageKey, pendingMessageSuppressUser, pendingMessageUserInitiated, userEditedFile, userEditedFileKey, gitStatusTick: externalGitStatusTick, discovery, userAvatar, agentName = DEFAULT_AGENT_NAME, productName = DEFAULT_PRODUCT_NAME, version, extraCommands,
|
|
1189
|
+
function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent, isPro, isAnonymous, canEdit, 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,
|
|
1156
1190
|
// feedbackUrl: prop kept for back-compat (callers still pass it), but no longer
|
|
1157
1191
|
// consumed here — its only use was the command-menu footer link removed in P3-21.
|
|
1158
1192
|
}) {
|
|
@@ -1171,10 +1205,12 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
1171
1205
|
// visible; 420px bounds it on small tablets.
|
|
1172
1206
|
const popupMaxHeight = isNarrow ? 'min(50dvh, 420px)' : '70vh';
|
|
1173
1207
|
const http = useHttpClient();
|
|
1174
|
-
// Bind the host's profile-click callback to the
|
|
1175
|
-
//
|
|
1176
|
-
//
|
|
1177
|
-
//
|
|
1208
|
+
// Bind the host's profile-click callback to the SIGNED-IN user's identity once.
|
|
1209
|
+
// Hosts open the *own*-profile surface from this, so it is only ever attached to
|
|
1210
|
+
// the signed-in user's OWN messages (see the per-message gate at the MessageItem
|
|
1211
|
+
// call site — a teammate's avatar must never open the viewer's profile). Stable
|
|
1212
|
+
// so MessageItem's memo isn't broken; `undefined` when the host opts out, which
|
|
1213
|
+
// keeps every avatar non-interactive.
|
|
1178
1214
|
const onUserAvatarClick = useMemo(() => onProfileClick
|
|
1179
1215
|
? () => onProfileClick({ avatar: userAvatar })
|
|
1180
1216
|
: undefined, [onProfileClick, userAvatar]);
|
|
@@ -1235,6 +1271,10 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
1235
1271
|
// applied through handleStreamEvent) can append a card-message to the ONE message store.
|
|
1236
1272
|
// This client's OWN `card` events are appended internally by useChat's stream handler.
|
|
1237
1273
|
const appendCardMessageRef = useRef(() => { });
|
|
1274
|
+
// Ref to useChat's appendCompleteMessage — same contract as appendCardMessageRef, for a
|
|
1275
|
+
// teammate's broadcast `message` event (a complete non-streaming message, e.g. a
|
|
1276
|
+
// human-only team note). This client's OWN `message` events append internally in useChat.
|
|
1277
|
+
const appendCompleteMessageRef = useRef(() => { });
|
|
1238
1278
|
// Kept current each render so handleStreamEvent (memoized) always calls the latest.
|
|
1239
1279
|
const onReadyToBuildRef = useRef(onReadyToBuild);
|
|
1240
1280
|
onReadyToBuildRef.current = onReadyToBuild;
|
|
@@ -1281,6 +1321,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
1281
1321
|
if (event.type === 'card' && applyingPushedRef.current) {
|
|
1282
1322
|
appendCardMessageRef.current(event.id, event.timestamp, event.card);
|
|
1283
1323
|
}
|
|
1324
|
+
// A teammate's broadcast complete message (e.g. a human-only team note) — append it
|
|
1325
|
+
// to the local store so it shows live for the collaborator too (de-duped by id;
|
|
1326
|
+
// never re-persisted — the originating server already persisted it). This client's
|
|
1327
|
+
// OWN `message` stream events are appended internally by useChat.
|
|
1328
|
+
if (event.type === 'message' && applyingPushedRef.current && event.message) {
|
|
1329
|
+
appendCompleteMessageRef.current(event.message);
|
|
1330
|
+
}
|
|
1284
1331
|
// Captured outbound side effect (email/sms/push/webhook/channel) — push an
|
|
1285
1332
|
// inline activity card into the timeline. Non-text card, mirroring how
|
|
1286
1333
|
// system cards are appended.
|
|
@@ -1415,7 +1462,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
1415
1462
|
}
|
|
1416
1463
|
onFileChange?.(path, content);
|
|
1417
1464
|
}, [onFileChange, onFileOpen, autoFixCountdown]);
|
|
1418
|
-
const { messages, isLoading, isRemoteStreaming, noteRemoteStreamEvent, error, errorMeta, mode, fastMode, streamingStatus, setMode, setFastMode, sendMessage, abort, clearHistory, editQueuedMessage, deleteQueuedMessage, clearQueuedForFile, appendCardMessage, retryCountdown, cancelRetry, } = useChat({
|
|
1465
|
+
const { messages, isLoading, isRemoteStreaming, noteRemoteStreamEvent, error, errorMeta, mode, fastMode, streamingStatus, setMode, setFastMode, sendMessage, abort, clearHistory, editQueuedMessage, deleteQueuedMessage, clearQueuedForFile, appendCardMessage, appendCompleteMessage, retryCountdown, cancelRetry, } = useChat({
|
|
1419
1466
|
endpoint,
|
|
1420
1467
|
projectId,
|
|
1421
1468
|
agentName,
|
|
@@ -1441,6 +1488,8 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
1441
1488
|
// Keep the card-append ref current so handleStreamEvent (memoized) can append a teammate's
|
|
1442
1489
|
// broadcast `card` event to the message store.
|
|
1443
1490
|
appendCardMessageRef.current = appendCardMessage;
|
|
1491
|
+
// Same for a teammate's broadcast complete `message` event (e.g. a team note).
|
|
1492
|
+
appendCompleteMessageRef.current = appendCompleteMessage;
|
|
1444
1493
|
// User Stop. A stop is a user decision the platform must not overrule — so
|
|
1445
1494
|
// beyond killing the stream (useChat.abort also records the stop client-side
|
|
1446
1495
|
// and, via chat-abort's userInitiated flag, server-side), drop every pending
|
|
@@ -2303,6 +2352,31 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2303
2352
|
const text = t(`ide.chat.tip.${ENTRY_TIP.id}`, { agentName }, { defaultValue: ENTRY_TIP.text });
|
|
2304
2353
|
setTipCards((prev) => [...prev, { id: crypto.randomUUID(), text, timestamp: Date.now() }]);
|
|
2305
2354
|
}, [conversationId, messages.length, agentName, discovery]);
|
|
2355
|
+
// Viewer orientation tip: a read-only member's explainer — GOLD like the
|
|
2356
|
+
// team-only messages and led by the same `people` icon it explains, so the
|
|
2357
|
+
// badge on a team note is self-describing. A regular dismissable tip (never a
|
|
2358
|
+
// permanent composer note); shown once per panel mount, skipped in discovery
|
|
2359
|
+
// like the other tips (mvp B1). Gated on an EXPLICIT canEdit === false so
|
|
2360
|
+
// hosts that don't do roles never see it.
|
|
2361
|
+
const viewerTipShownRef = useRef(false);
|
|
2362
|
+
useEffect(() => {
|
|
2363
|
+
if (canEdit !== false || discovery || viewerTipShownRef.current)
|
|
2364
|
+
return;
|
|
2365
|
+
viewerTipShownRef.current = true;
|
|
2366
|
+
const text = t('ide.chat.tip.viewerTeamOnly', { agentName }, {
|
|
2367
|
+
defaultValue: 'View-only access — read along and /teamsay the team. This gold icon marks team-only messages ({{agentName}} ignores them). Running the assistant and changing the model or settings need editor access.',
|
|
2368
|
+
});
|
|
2369
|
+
setTipCards((prev) => [
|
|
2370
|
+
...prev,
|
|
2371
|
+
{
|
|
2372
|
+
id: crypto.randomUUID(),
|
|
2373
|
+
text,
|
|
2374
|
+
timestamp: Date.now(),
|
|
2375
|
+
accent: NOTICE_TONE.gold.accent,
|
|
2376
|
+
icon: 'people',
|
|
2377
|
+
},
|
|
2378
|
+
]);
|
|
2379
|
+
}, [canEdit, discovery, agentName]);
|
|
2306
2380
|
// Surface an occasional idle tip. This effect re-runs on every message change, so
|
|
2307
2381
|
// the idle timer is continually reset by activity; it only fires once the
|
|
2308
2382
|
// conversation has been quiet for TIP_IDLE_MS — and even then only when the
|
|
@@ -2521,6 +2595,31 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2521
2595
|
setAutoCommitLoaded(true);
|
|
2522
2596
|
});
|
|
2523
2597
|
}, [http, projectId]);
|
|
2598
|
+
// Re-read ONLY the persisted model fields when the host signals a model change
|
|
2599
|
+
// (the provider modal's "Use" button sets chatModel server-side). Targeted so
|
|
2600
|
+
// a model pick never re-hydrates the rest of the settings (auto-commit,
|
|
2601
|
+
// skills, effort). Skips the initial 0 value — mount already read them.
|
|
2602
|
+
useEffect(() => {
|
|
2603
|
+
if (!modelSelectionSignal)
|
|
2604
|
+
return;
|
|
2605
|
+
http
|
|
2606
|
+
.get(`/projects/${projectId}`)
|
|
2607
|
+
.then((res) => {
|
|
2608
|
+
const s = res.data.settings;
|
|
2609
|
+
if (typeof s?.chatModel === 'string') {
|
|
2610
|
+
setCurrentModel(s.chatModel);
|
|
2611
|
+
setSavedChatModel(s.chatModel);
|
|
2612
|
+
}
|
|
2613
|
+
if (typeof s?.planModel === 'string')
|
|
2614
|
+
setPlanModel(s.planModel);
|
|
2615
|
+
if (typeof s?.executeModel === 'string')
|
|
2616
|
+
setExecuteModel(s.executeModel);
|
|
2617
|
+
})
|
|
2618
|
+
.catch(() => {
|
|
2619
|
+
// Non-fatal: the model was persisted server-side; the picker indicator
|
|
2620
|
+
// just won't refresh until the next full settings read.
|
|
2621
|
+
});
|
|
2622
|
+
}, [modelSelectionSignal, http, projectId]);
|
|
2524
2623
|
// Persist the auto-commit cadence to project.settings (debounced) so it
|
|
2525
2624
|
// survives a reload/reconnect like every other setting. The reducer is the
|
|
2526
2625
|
// live source of truth; this mirrors intervalSeconds (0 = off) back to the
|
|
@@ -2566,7 +2665,15 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2566
2665
|
return;
|
|
2567
2666
|
removedModelNotifiedRef.current = currentModel;
|
|
2568
2667
|
const removedId = currentModel;
|
|
2569
|
-
|
|
2668
|
+
// Prefer a still-available model from the SAME custom provider before a
|
|
2669
|
+
// platform model: when a BYO provider's model is renamed (custom/<prov>/A →
|
|
2670
|
+
// custom/<prov>/B), keep the user on their own endpoint rather than bouncing
|
|
2671
|
+
// them to a platform free model.
|
|
2672
|
+
const sameProviderPrefix = removedId.match(/^(custom\/[^/]+\/)/)?.[1];
|
|
2673
|
+
const sameProviderModel = sameProviderPrefix
|
|
2674
|
+
? AVAILABLE_MODELS.find((m) => m.id.startsWith(sameProviderPrefix))?.id
|
|
2675
|
+
: undefined;
|
|
2676
|
+
const fallback = sameProviderModel || FREE_TIER_MODEL || AVAILABLE_MODELS[0]?.id;
|
|
2570
2677
|
addSystemCard(fallback
|
|
2571
2678
|
? t('ide.chat.modelRemoved', { removed: removedId, fallback }, {
|
|
2572
2679
|
defaultValue: 'Your selected model "{{removed}}" is no longer available. Switched to "{{fallback}}". Type /model to pick another.',
|
|
@@ -2755,10 +2862,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2755
2862
|
useEffect(() => {
|
|
2756
2863
|
if (initialMessage && !hasConversation && sentInitialRef.current !== initialMessage) {
|
|
2757
2864
|
sentInitialRef.current = initialMessage;
|
|
2758
|
-
|
|
2865
|
+
// Same rule as handleSubmit: a host side-channel command (e.g. /teamsay)
|
|
2866
|
+
// renders no optimistic echo — the server's `message` event is the message.
|
|
2867
|
+
const sideChannel = matchesSideChannelCommand(extraCommands?.length ? [...COMMANDS, ...extraCommands] : COMMANDS, initialMessage);
|
|
2868
|
+
sendMessage(initialMessage, undefined, sideChannel ? { suppressUserMessage: true } : undefined);
|
|
2759
2869
|
onInitialMessageSent?.();
|
|
2760
2870
|
}
|
|
2761
|
-
}, [initialMessage, hasConversation, sendMessage, onInitialMessageSent]);
|
|
2871
|
+
}, [initialMessage, hasConversation, sendMessage, onInitialMessageSent, extraCommands]);
|
|
2762
2872
|
// ── Auto-send pending message (e.g. "Fix with AI", preview errors) ──────
|
|
2763
2873
|
// Defers sending while the AI is streaming to avoid queueing up auto-fix
|
|
2764
2874
|
// messages during active work. Messages are sent once streaming ends.
|
|
@@ -3434,6 +3544,20 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3434
3544
|
setInputAndCursorEnd(`/${id} `);
|
|
3435
3545
|
return;
|
|
3436
3546
|
}
|
|
3547
|
+
// A read-only VIEWER may run only viewerSafe commands (read / view-only /
|
|
3548
|
+
// per-user preference). Everything else writes shared project state or
|
|
3549
|
+
// triggers a Synthase turn the server 403s, so surface a read-only note
|
|
3550
|
+
// instead of a dead control that silently fails. Default-deny: an
|
|
3551
|
+
// unflagged command is unavailable to viewers.
|
|
3552
|
+
if (!canEdit) {
|
|
3553
|
+
const def = allCommands.find((c) => c.id === id);
|
|
3554
|
+
if (def && !def.viewerSafe) {
|
|
3555
|
+
addSystemCard(t('ide.chat.viewerReadOnlyCommand', undefined, {
|
|
3556
|
+
defaultValue: 'You have view-only access, so this command is unavailable. Ask an editor to make changes.',
|
|
3557
|
+
}));
|
|
3558
|
+
return;
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3437
3561
|
if (id === 'clear') {
|
|
3438
3562
|
setInputValue('');
|
|
3439
3563
|
await clearHistory();
|
|
@@ -3779,6 +3903,9 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3779
3903
|
refreshGitStatus,
|
|
3780
3904
|
openPanelOverlay,
|
|
3781
3905
|
extraCommandIds,
|
|
3906
|
+
canEdit,
|
|
3907
|
+
allCommands,
|
|
3908
|
+
t,
|
|
3782
3909
|
]);
|
|
3783
3910
|
// When the auto-commit countdown reaches zero, fire the existing /commit path
|
|
3784
3911
|
// (no new backend) and pause until the next file change re-arms it. /commit
|
|
@@ -3814,6 +3941,26 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3814
3941
|
const trimmed = inputRef.current.trim();
|
|
3815
3942
|
if (!trimmed && attachedFiles.length === 0)
|
|
3816
3943
|
return;
|
|
3944
|
+
// Read-only VIEWER guard. A viewer cannot run the assistant (the server 403s
|
|
3945
|
+
// the chat route) or any project-mutating command, so block a plain message
|
|
3946
|
+
// or a write-command here with a read-only note instead of a silent 403 —
|
|
3947
|
+
// but let team side-channel messages (/teamsay) and viewerSafe commands
|
|
3948
|
+
// through so viewers can still talk to the team and read. (Menu-dispatched
|
|
3949
|
+
// commands are gated in executeCommand; this covers composer typing.)
|
|
3950
|
+
if (!canEdit) {
|
|
3951
|
+
const token = trimmed.match(/^\/(\S+)/)?.[1]?.toLowerCase();
|
|
3952
|
+
const def = token
|
|
3953
|
+
? allCommands.find((c) => c.id.toLowerCase() === token || c.aliases?.includes(token))
|
|
3954
|
+
: undefined;
|
|
3955
|
+
const allowed = matchesSideChannelCommand(allCommands, trimmed) || def?.viewerSafe === true;
|
|
3956
|
+
if (!allowed) {
|
|
3957
|
+
setInputValue('');
|
|
3958
|
+
addSystemCard(t('ide.chat.viewerReadOnly', undefined, {
|
|
3959
|
+
defaultValue: "You have view-only access, so you can't run the assistant here. You can still read along and use /teamsay to message the team.",
|
|
3960
|
+
}));
|
|
3961
|
+
return;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3817
3964
|
// Handle /autofix toggle locally
|
|
3818
3965
|
if (/^\/autofix$/i.test(trimmed)) {
|
|
3819
3966
|
void executeCommand('autofix');
|
|
@@ -4132,6 +4279,16 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4132
4279
|
sendMessage(prompt);
|
|
4133
4280
|
return;
|
|
4134
4281
|
}
|
|
4282
|
+
// A host side-channel command (CommandDef.sideChannel, matched by id or alias — e.g.
|
|
4283
|
+
// molecule.dev's /teamsay + /t): send the raw text to the host's server intercept but
|
|
4284
|
+
// suppress the optimistic user bubble. The server emits the canonical `message` stream
|
|
4285
|
+
// event (persisted + broadcast to every member), which IS the visible message — so the
|
|
4286
|
+
// transcript never shows the literal "/command" text, and never shows it twice.
|
|
4287
|
+
if (matchesSideChannelCommand(allCommands, trimmed)) {
|
|
4288
|
+
setInputValue('');
|
|
4289
|
+
sendMessage(trimmed, undefined, { suppressUserMessage: true });
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4135
4292
|
// Rewrite /explain with attachments into a proper prompt (attachments processed below)
|
|
4136
4293
|
let message = trimmed;
|
|
4137
4294
|
if (explainMatch) {
|
|
@@ -4178,7 +4335,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4178
4335
|
setAttachedFiles([]);
|
|
4179
4336
|
setAttachmentError(null);
|
|
4180
4337
|
sendMessage(message, chatAttachments.length > 0 ? chatAttachments : undefined);
|
|
4181
|
-
}, [attachedFiles, http, projectId, sendMessage, setInputValue, runSavedScript]);
|
|
4338
|
+
}, [attachedFiles, http, projectId, sendMessage, setInputValue, runSavedScript, allCommands]);
|
|
4182
4339
|
// External auto-submit. When the signal changes, submit the current input —
|
|
4183
4340
|
// used by the prompt → chat morph to send the prefilled prompt once the chat
|
|
4184
4341
|
// has docked into place (handleSubmit clears the input as it sends).
|
|
@@ -4234,7 +4391,12 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4234
4391
|
if (!q)
|
|
4235
4392
|
return AVAILABLE_MODELS;
|
|
4236
4393
|
return AVAILABLE_MODELS.filter((m) => m.id.toLowerCase().includes(q) || m.label.toLowerCase().includes(q));
|
|
4237
|
-
|
|
4394
|
+
// AVAILABLE_MODELS MUST be a dep: when a custom provider is edited (a model
|
|
4395
|
+
// renamed/added, a URL changed) the catalog refreshes in place, and without
|
|
4396
|
+
// this the picker keeps rendering the pre-edit snapshot — the new model
|
|
4397
|
+
// never appears and can't be selected. (The query is read from inputRef and
|
|
4398
|
+
// stays fresh because each keystroke re-sets modelPicker.)
|
|
4399
|
+
}, [modelPicker, AVAILABLE_MODELS]);
|
|
4238
4400
|
// ── Older models section ────────────────────────────────────────────────────
|
|
4239
4401
|
// Deprecated entries fold into a collapsed "Older models ⌄" section under the
|
|
4240
4402
|
// current models. The section auto-expands when the user's currentModel is in
|
|
@@ -4597,9 +4759,11 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4597
4759
|
case 'model': {
|
|
4598
4760
|
const label = cardEvent.label || cardEvent.model;
|
|
4599
4761
|
// Append the model's effective processing-region code — catalog
|
|
4600
|
-
// models only
|
|
4762
|
+
// models only. A custom/BYO endpoint has no meaningful region: its
|
|
4763
|
+
// catalog `regions` defaults to ['us'] as a placeholder, not a real
|
|
4764
|
+
// re-host choice, so never surface a region for it.
|
|
4601
4765
|
const def = AVAILABLE_MODELS.find((m) => m.id === cardEvent.model);
|
|
4602
|
-
const regionCode = def ? effectiveModelRegion(def).toUpperCase() : null;
|
|
4766
|
+
const regionCode = def && def.provider !== 'custom' ? effectiveModelRegion(def).toUpperCase() : null;
|
|
4603
4767
|
return {
|
|
4604
4768
|
id,
|
|
4605
4769
|
text: regionCode
|
|
@@ -4843,7 +5007,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4843
5007
|
if (item.kind === 'activity')
|
|
4844
5008
|
return (_jsx(ActivityCard, { activity: item.card.activity, onActivityClick: onActivityClick }, item.card.id));
|
|
4845
5009
|
if (item.kind === 'tip')
|
|
4846
|
-
return (_jsx(TipCard, { text: item.card.text, onDismiss: () => dismissTip(item.card.id) }, item.card.id));
|
|
5010
|
+
return (_jsx(TipCard, { text: item.card.text, accent: item.card.accent, icon: item.card.icon, onDismiss: () => dismissTip(item.card.id) }, item.card.id));
|
|
4847
5011
|
if (item.kind === 'system') {
|
|
4848
5012
|
if (item.card.variant === 'settings') {
|
|
4849
5013
|
// Legacy inline branch — kept so any 'settings' card persisted
|
|
@@ -4946,7 +5110,15 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4946
5110
|
hash,
|
|
4947
5111
|
}, onRevert: handleRevertCommit }, msg.id));
|
|
4948
5112
|
}
|
|
4949
|
-
return (_jsx(MessageItem, { msg: msg, sendMessage: sendMessage, handleAskUserResponse: handleAskUserResponse, isLoading: isLoading, streamingStatus: streamingStatus, onNavigatePreview: onNavigatePreview, undoneTcIds: undoneTcIds, handleUndoToggle: handleUndoToggle, onFileOpen: onFileOpen, onFileDoubleClick: onFileDoubleClick, onFileDiff: onFileDiff, handleFileRevert: handleFileRevert, setInputAndCursorEnd: setInputAndCursorEnd, setModelPicker: setModelPicker, userAvatar: userAvatar,
|
|
5113
|
+
return (_jsx(MessageItem, { msg: msg, sendMessage: sendMessage, handleAskUserResponse: handleAskUserResponse, isLoading: isLoading, streamingStatus: streamingStatus, onNavigatePreview: onNavigatePreview, undoneTcIds: undoneTcIds, handleUndoToggle: handleUndoToggle, onFileOpen: onFileOpen, onFileDoubleClick: onFileDoubleClick, onFileDiff: onFileDiff, handleFileRevert: handleFileRevert, setInputAndCursorEnd: setInputAndCursorEnd, setModelPicker: setModelPicker, userAvatar: userAvatar,
|
|
5114
|
+
// Avatar clicks open the signed-in user's OWN profile, so only
|
|
5115
|
+
// their own messages get the handler: author-less ones (the
|
|
5116
|
+
// local echo, legacy solo rows) or an author matching
|
|
5117
|
+
// currentUserId. A teammate's avatar stays non-interactive —
|
|
5118
|
+
// clicking Test's face must not open Luke's profile editor.
|
|
5119
|
+
onAvatarClick: !msg.author?.id || (currentUserId != null && msg.author.id === currentUserId)
|
|
5120
|
+
? onUserAvatarClick
|
|
5121
|
+
: undefined, discovery: discovery, buildUpgradeCta: buildUpgradeCta, agentName: agentName }, msg.id));
|
|
4950
5122
|
} }, item.kind === 'message' ? item.msg.id : item.card.id))), error &&
|
|
4951
5123
|
!isStaleAnonymousLimit &&
|
|
4952
5124
|
(errorMeta?.limitType ? (_jsx(ResourceLimitBanner, { message: error, action: buildUpgradeCta?.({ requiresSignup: errorMeta.requiresSignup }) })) : (_jsx("div", { className: cm.cn(cm.textSize('sm'), cm.sp('p', 2), cm.sp('mb', 2), cm.bgErrorSubtle, cm.textError), style: { borderRadius: '6px' }, children: error }))), (() => {
|
|
@@ -6331,7 +6503,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6331
6503
|
alignItems: 'center',
|
|
6332
6504
|
marginTop: '6px',
|
|
6333
6505
|
gap: '4px',
|
|
6334
|
-
}, children: [_jsx("button", { type: "button", onClick: () => {
|
|
6506
|
+
}, children: [_jsx("button", { type: "button", disabled: !canEdit, onClick: () => {
|
|
6335
6507
|
const newMode = mode === 'plan' ? 'execute' : 'plan';
|
|
6336
6508
|
setMode(newMode);
|
|
6337
6509
|
http
|
|
@@ -6365,9 +6537,9 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6365
6537
|
? `1px solid ${isLight ? 'rgba(217,119,6,0.55)' : 'rgba(234,179,8,0.5)'}`
|
|
6366
6538
|
: 'none',
|
|
6367
6539
|
borderRadius: '3px',
|
|
6368
|
-
cursor: 'pointer',
|
|
6540
|
+
cursor: canEdit ? 'pointer' : 'not-allowed',
|
|
6369
6541
|
color: mode === 'plan' ? (isLight ? '#d97706' : '#eab308') : 'inherit',
|
|
6370
|
-
opacity: mode === 'plan' ? 1 : 0.4,
|
|
6542
|
+
opacity: !canEdit ? 0.4 : mode === 'plan' ? 1 : 0.4,
|
|
6371
6543
|
padding: 0,
|
|
6372
6544
|
transition: 'opacity 100ms, color 100ms',
|
|
6373
6545
|
}, onMouseEnter: (e) => {
|
|
@@ -6376,7 +6548,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6376
6548
|
}, onMouseLeave: (e) => {
|
|
6377
6549
|
if (mode !== 'plan')
|
|
6378
6550
|
e.currentTarget.style.opacity = '0.4';
|
|
6379
|
-
}, children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", children: _jsx("path", { d: "M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z" }) }) }), fastModeAvailable && (_jsx("button", { type: "button", "data-mol-id": "chat-fast-mode-toggle", onClick: () => {
|
|
6551
|
+
}, children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", children: _jsx("path", { d: "M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z" }) }) }), fastModeAvailable && (_jsx("button", { type: "button", "data-mol-id": "chat-fast-mode-toggle", disabled: !canEdit, onClick: () => {
|
|
6380
6552
|
const next = !fastMode;
|
|
6381
6553
|
setFastMode(next);
|
|
6382
6554
|
http
|
|
@@ -6682,7 +6854,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
6682
6854
|
* @param props - Component props (see {@link MessageItemProps}).
|
|
6683
6855
|
* @returns The rendered chat panel element.
|
|
6684
6856
|
*/
|
|
6685
|
-
export function ChatPanel({ projectId, endpoint, initialMessage, onInitialMessageSent, activeFile, openTabs, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onFileChange, onFileDeleted, onCommit, onActivityClick, onRenderError, onProfileClick, 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, buildUpgradeCta, buildHelpUpgradeSection, userAvatar, agentName, productName, version, extraCommands, feedbackUrl, className, }) {
|
|
6857
|
+
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, buildUpgradeCta, buildHelpUpgradeSection, userAvatar, agentName, productName, version, extraCommands, feedbackUrl, className, }) {
|
|
6686
6858
|
const cm = getClassMap();
|
|
6687
6859
|
const isNarrow = useNarrowViewport();
|
|
6688
6860
|
const isCoarse = useCoarsePointer();
|
|
@@ -6891,7 +7063,7 @@ export function ChatPanel({ projectId, endpoint, initialMessage, onInitialMessag
|
|
|
6891
7063
|
textOverflow: 'ellipsis',
|
|
6892
7064
|
whiteSpace: 'nowrap',
|
|
6893
7065
|
width: '100%',
|
|
6894
|
-
}, 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, 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, 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)] }));
|
|
7066
|
+
}, 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, 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)] }));
|
|
6895
7067
|
}
|
|
6896
7068
|
ChatPanel.displayName = 'ChatPanel';
|
|
6897
7069
|
//# sourceMappingURL=ChatPanel.js.map
|