@molecule/app-ide-react 1.15.0 → 1.17.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 +193 -7
- package/dist/components/ChatPanel.d.ts +10 -1
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +136 -14
- package/dist/components/ChatPanel.js.map +1 -1
- package/dist/components/MarkdownContent.d.ts.map +1 -1
- package/dist/components/MarkdownContent.js +27 -7
- package/dist/components/MarkdownContent.js.map +1 -1
- package/dist/components/TestStatusBar.d.ts +56 -0
- package/dist/components/TestStatusBar.d.ts.map +1 -0
- package/dist/components/TestStatusBar.js +105 -0
- package/dist/components/TestStatusBar.js.map +1 -0
- package/dist/components/TestsCard.d.ts +15 -2
- package/dist/components/TestsCard.d.ts.map +1 -1
- package/dist/components/TestsCard.js +42 -9
- package/dist/components/TestsCard.js.map +1 -1
- package/dist/components/ToolCallCard.d.ts +7 -1
- package/dist/components/ToolCallCard.d.ts.map +1 -1
- package/dist/components/ToolCallCard.js +516 -161
- package/dist/components/ToolCallCard.js.map +1 -1
- package/dist/components/tests-bar-utilities.d.ts +78 -103
- package/dist/components/tests-bar-utilities.d.ts.map +1 -1
- package/dist/components/tests-bar-utilities.js +130 -201
- package/dist/components/tests-bar-utilities.js.map +1 -1
- package/dist/components/tests-card-utilities.d.ts +78 -2
- package/dist/components/tests-card-utilities.d.ts.map +1 -1
- package/dist/components/tests-card-utilities.js +134 -6
- package/dist/components/tests-card-utilities.js.map +1 -1
- package/dist/components/tool-call-utilities.d.ts +81 -7
- package/dist/components/tool-call-utilities.d.ts.map +1 -1
- package/dist/components/tool-call-utilities.js +146 -6
- package/dist/components/tool-call-utilities.js.map +1 -1
- package/dist/hooks/useTestsBarVisible.d.ts +16 -0
- package/dist/hooks/useTestsBarVisible.d.ts.map +1 -0
- package/dist/hooks/useTestsBarVisible.js +39 -0
- package/dist/hooks/useTestsBarVisible.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +60 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -5,7 +5,7 @@ import { useThemeMode } from '@molecule/app-react';
|
|
|
5
5
|
import { getClassMap } from '@molecule/app-ui';
|
|
6
6
|
import { useCoarsePointer, useNarrowViewport } from '../hooks/useViewport.js';
|
|
7
7
|
import { MarkdownContent } from './MarkdownContent.js';
|
|
8
|
-
import { basename, extractFilePath, fileDiffStats, moleculeDocPath, normalizeAskUserInput, num, str, toolLabel, toolSummary, } from './tool-call-utilities.js';
|
|
8
|
+
import { basename, extractFilePath, fileDiffStats, isSkippedByUser, moleculeDocPath, normalizeAskUserInput, normalizeTaskListInput, num, parseJudgeVerdict, str, toolLabel, toolSummary, } from './tool-call-utilities.js';
|
|
9
9
|
/**
|
|
10
10
|
* Normalize a model-authored string for display: models sometimes double-escape
|
|
11
11
|
* tool arguments, so a literal "\n"/"\t"/"\r" (backslash + letter) ends up in
|
|
@@ -305,7 +305,7 @@ function renderOut(name, output) {
|
|
|
305
305
|
* @param props - Component props.
|
|
306
306
|
* @returns The rendered tool call card element.
|
|
307
307
|
*/
|
|
308
|
-
export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output, status, fileDiff, isUndone: isUndoneProp, onUndoToggle, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onAskUserResponse, className, }) {
|
|
308
|
+
export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output, status, fileDiff, isUndone: isUndoneProp, onUndoToggle, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onAskUserResponse, onSkip, skipDisabledReason, className, }) {
|
|
309
309
|
const cm = getClassMap();
|
|
310
310
|
const isLight = useThemeMode() === 'light';
|
|
311
311
|
// Touch/phone branches (called unconditionally, before the per-tool early
|
|
@@ -319,7 +319,14 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
319
319
|
const [isUndoneLocal, setIsUndoneLocal] = useState(false);
|
|
320
320
|
const isUndone = isUndoneProp ?? isUndoneLocal;
|
|
321
321
|
const [isReverting, setIsReverting] = useState(false);
|
|
322
|
+
const [skipRequested, setSkipRequested] = useState(false);
|
|
323
|
+
// The person skipped this call. It is neither a success nor a failure, and
|
|
324
|
+
// the row has to say so — a green dot on a command that never ran is exactly
|
|
325
|
+
// the kind of quiet lie the honesty rule exists to stop.
|
|
326
|
+
const wasSkipped = isSkippedByUser(output);
|
|
322
327
|
const hasError = (() => {
|
|
328
|
+
if (wasSkipped)
|
|
329
|
+
return false;
|
|
323
330
|
if (status === 'error')
|
|
324
331
|
return true;
|
|
325
332
|
if (typeof output !== 'object' || output === null)
|
|
@@ -337,8 +344,9 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
337
344
|
}
|
|
338
345
|
return false;
|
|
339
346
|
})();
|
|
340
|
-
// gray → orange → green or red
|
|
341
|
-
|
|
347
|
+
// gray → orange → green or red; a skipped call stays gray, because it did
|
|
348
|
+
// not happen.
|
|
349
|
+
const dotColor = status === 'pending' || wasSkipped
|
|
342
350
|
? '#888888'
|
|
343
351
|
: status === 'running'
|
|
344
352
|
? '#e8a000'
|
|
@@ -382,6 +390,27 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
382
390
|
setIsReverting(false);
|
|
383
391
|
}
|
|
384
392
|
}, [canRevert, isReverting, isUndone, fileDiff, filePath, onFileRevert, onUndoToggle, id]);
|
|
393
|
+
// Skip this call while it is still running — the turn keeps going, the model
|
|
394
|
+
// is told the command did not run. A host that answers `false` means the call
|
|
395
|
+
// had already finished (the stale-button race), which is not an error: the
|
|
396
|
+
// button simply returns to its resting state.
|
|
397
|
+
const canSkip = status === 'running' && onSkip != null;
|
|
398
|
+
const handleSkip = useCallback(async () => {
|
|
399
|
+
if (!onSkip || skipRequested || skipDisabledReason)
|
|
400
|
+
return;
|
|
401
|
+
setSkipRequested(true);
|
|
402
|
+
try {
|
|
403
|
+
const accepted = await onSkip(id);
|
|
404
|
+
if (accepted === false)
|
|
405
|
+
setSkipRequested(false);
|
|
406
|
+
}
|
|
407
|
+
catch (_error) {
|
|
408
|
+
// Deliberate noop: the host already logs whatever went wrong on its side,
|
|
409
|
+
// and there is nothing for the PERSON to do here — the call is still
|
|
410
|
+
// running, so the honest UI is the button back at rest to try again.
|
|
411
|
+
setSkipRequested(false);
|
|
412
|
+
}
|
|
413
|
+
}, [onSkip, skipRequested, skipDisabledReason, id]);
|
|
385
414
|
// Tools that expand to show details inline.
|
|
386
415
|
const EXPANDABLE = new Set([
|
|
387
416
|
'exec_command',
|
|
@@ -413,6 +442,145 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
413
442
|
setExpanded((e) => !e);
|
|
414
443
|
}
|
|
415
444
|
: undefined;
|
|
445
|
+
// ── update_task_list: the working checklist the user watches ───────────────
|
|
446
|
+
if (name === 'update_task_list') {
|
|
447
|
+
const todos = normalizeTaskListInput(input);
|
|
448
|
+
const completed = todos.filter((todo) => todo.status === 'completed').length;
|
|
449
|
+
const borderClr = isLight ? '#d0d7de' : '#3d444d';
|
|
450
|
+
const statusGlyph = (status) => status === 'completed' ? '✓' : status === 'in_progress' ? '◐' : '○';
|
|
451
|
+
const statusColor = (status) => status === 'completed'
|
|
452
|
+
? '#3fb950'
|
|
453
|
+
: status === 'in_progress'
|
|
454
|
+
? isLight
|
|
455
|
+
? '#2563eb'
|
|
456
|
+
: '#60a5fa'
|
|
457
|
+
: isLight
|
|
458
|
+
? '#848d97'
|
|
459
|
+
: '#6e7681';
|
|
460
|
+
return (_jsxs("div", { className: className, "data-mol-id": "task-list-card", style: {
|
|
461
|
+
marginBottom: '8px',
|
|
462
|
+
marginTop: '8px',
|
|
463
|
+
borderRadius: '8px',
|
|
464
|
+
border: `1px solid ${borderClr}`,
|
|
465
|
+
background: isLight ? '#f6f8fa' : 'rgba(255,255,255,0.04)',
|
|
466
|
+
overflow: 'hidden',
|
|
467
|
+
}, children: [_jsxs("div", { style: {
|
|
468
|
+
display: 'flex',
|
|
469
|
+
alignItems: 'center',
|
|
470
|
+
justifyContent: 'space-between',
|
|
471
|
+
gap: '8px',
|
|
472
|
+
padding: '6px 12px',
|
|
473
|
+
borderBottom: todos.length > 0 ? `1px solid ${borderClr}` : 'none',
|
|
474
|
+
fontSize: '12px',
|
|
475
|
+
fontWeight: 600,
|
|
476
|
+
}, children: [_jsx("span", { children: t('ide.chat.taskListTitle', undefined, { defaultValue: 'Working plan' }) }), todos.length > 0 && (_jsx("span", { style: { fontWeight: 400, opacity: 0.65 }, children: t('ide.chat.taskListProgress', { done: completed, total: todos.length }, {
|
|
477
|
+
defaultValue: '{{done}} of {{total}} done',
|
|
478
|
+
}) }))] }), todos.map((todo, i) => (_jsxs("div", { style: {
|
|
479
|
+
display: 'flex',
|
|
480
|
+
alignItems: 'flex-start',
|
|
481
|
+
gap: '8px',
|
|
482
|
+
padding: '4px 12px',
|
|
483
|
+
borderTop: i > 0 ? `1px solid ${isLight ? '#eaeef2' : 'rgba(255,255,255,0.06)'}` : 'none',
|
|
484
|
+
fontSize: '12.5px',
|
|
485
|
+
...(todo.status === 'completed'
|
|
486
|
+
? { opacity: 0.55, textDecoration: 'line-through' }
|
|
487
|
+
: {}),
|
|
488
|
+
}, children: [_jsx("span", { "aria-hidden": true, style: {
|
|
489
|
+
flexShrink: 0,
|
|
490
|
+
width: 16,
|
|
491
|
+
fontSize: '12px',
|
|
492
|
+
lineHeight: '17px',
|
|
493
|
+
color: statusColor(todo.status),
|
|
494
|
+
fontWeight: 600,
|
|
495
|
+
}, children: statusGlyph(todo.status) }), _jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [todo.priority === 'high' && todo.status !== 'completed' && (_jsx("span", { style: {
|
|
496
|
+
color: isLight ? '#d63a2f' : '#f87171',
|
|
497
|
+
fontWeight: 600,
|
|
498
|
+
marginRight: '4px',
|
|
499
|
+
}, children: "!" })), todo.content] })] }, i)))] }));
|
|
500
|
+
}
|
|
501
|
+
// ── spawn_agent: subagent card — explore report / judge verdict ─────────────
|
|
502
|
+
if (name === 'spawn_agent') {
|
|
503
|
+
const inp = (input ?? {});
|
|
504
|
+
const kind = inp.type === 'judge' ? 'judge' : 'explore';
|
|
505
|
+
const out = (output ?? {});
|
|
506
|
+
const errorText = typeof out.error === 'string' ? out.error : '';
|
|
507
|
+
const report = typeof out.report === 'string' ? out.report : '';
|
|
508
|
+
const verdict = kind === 'judge' ? parseJudgeVerdict(report) : null;
|
|
509
|
+
const [expanded, setExpanded] = useState(false);
|
|
510
|
+
const borderClr = isLight ? '#d0d7de' : '#3d444d';
|
|
511
|
+
const verdictTint = isLight ? 'rgba(63,185,80,0.08)' : 'rgba(63,185,80,0.12)';
|
|
512
|
+
const failTint = isLight ? 'rgba(248,81,73,0.08)' : 'rgba(248,81,73,0.14)';
|
|
513
|
+
const verdictColor = verdict?.verdict === 'PASS' ? '#3fb950' : '#f8554f';
|
|
514
|
+
const bodyText = verdict ? verdict.rest : report;
|
|
515
|
+
return (_jsxs("div", { className: className, "data-mol-id": "subagent-card", style: {
|
|
516
|
+
marginBottom: '8px',
|
|
517
|
+
marginTop: '8px',
|
|
518
|
+
borderRadius: '8px',
|
|
519
|
+
border: `1px solid ${borderClr}`,
|
|
520
|
+
background: isLight ? '#f6f8fa' : 'rgba(255,255,255,0.04)',
|
|
521
|
+
overflow: 'hidden',
|
|
522
|
+
}, children: [_jsxs("div", { style: {
|
|
523
|
+
display: 'flex',
|
|
524
|
+
alignItems: 'center',
|
|
525
|
+
gap: '8px',
|
|
526
|
+
padding: '6px 12px',
|
|
527
|
+
fontSize: '12px',
|
|
528
|
+
fontWeight: 600,
|
|
529
|
+
borderBottom: report || errorText ? `1px solid ${borderClr}` : 'none',
|
|
530
|
+
}, children: [_jsx("span", { "aria-hidden": true, style: {
|
|
531
|
+
width: 8,
|
|
532
|
+
height: 8,
|
|
533
|
+
borderRadius: '50%',
|
|
534
|
+
flexShrink: 0,
|
|
535
|
+
background: status === 'error' || errorText
|
|
536
|
+
? '#f04040'
|
|
537
|
+
: status === 'done'
|
|
538
|
+
? '#3fb950'
|
|
539
|
+
: status === 'running'
|
|
540
|
+
? '#e8a000'
|
|
541
|
+
: '#888888',
|
|
542
|
+
} }), _jsx("span", { children: kind === 'judge'
|
|
543
|
+
? t('ide.chat.subagent.judge', undefined, { defaultValue: 'Acceptance judge' })
|
|
544
|
+
: t('ide.chat.subagent.explore', undefined, { defaultValue: 'Research subagent' }) }), status === 'running' && (_jsx("span", { style: { fontWeight: 400, opacity: 0.6 }, children: t('ide.chat.subagent.working', undefined, { defaultValue: 'working…' }) }))] }), errorText && (_jsx("div", { style: {
|
|
545
|
+
padding: '8px 12px',
|
|
546
|
+
fontSize: '12px',
|
|
547
|
+
color: '#f8554f',
|
|
548
|
+
borderBottom: `1px solid ${borderClr}`,
|
|
549
|
+
}, children: errorText })), verdict && (_jsxs("div", { "data-mol-id": "subagent-verdict", style: {
|
|
550
|
+
padding: '8px 12px',
|
|
551
|
+
background: verdict.verdict === 'PASS' ? verdictTint : failTint,
|
|
552
|
+
borderBottom: `1px solid ${borderClr}`,
|
|
553
|
+
}, children: [_jsxs("div", { style: {
|
|
554
|
+
display: 'flex',
|
|
555
|
+
alignItems: 'center',
|
|
556
|
+
gap: '6px',
|
|
557
|
+
fontSize: '13px',
|
|
558
|
+
fontWeight: 700,
|
|
559
|
+
color: verdictColor,
|
|
560
|
+
}, children: [verdict.verdict === 'PASS' ? '✓' : '✕', verdict.verdict === 'PASS'
|
|
561
|
+
? t('ide.chat.subagent.verdictPass', undefined, { defaultValue: 'PASS' })
|
|
562
|
+
: t('ide.chat.subagent.verdictFail', undefined, { defaultValue: 'FAIL' })] }), verdict.issues.length > 0 && (_jsx("ul", { style: {
|
|
563
|
+
margin: '6px 0 0',
|
|
564
|
+
paddingLeft: '18px',
|
|
565
|
+
fontSize: '12px',
|
|
566
|
+
display: 'flex',
|
|
567
|
+
flexDirection: 'column',
|
|
568
|
+
gap: '2px',
|
|
569
|
+
opacity: 0.9,
|
|
570
|
+
}, children: verdict.issues.map((issue, i) => (_jsx("li", { children: issue }, i))) }))] })), bodyText && (_jsxs("div", { style: { padding: '8px 12px' }, children: [_jsx("div", { style: {
|
|
571
|
+
fontSize: '12px',
|
|
572
|
+
whiteSpace: 'pre-wrap',
|
|
573
|
+
maxHeight: expanded ? undefined : 150,
|
|
574
|
+
overflow: 'hidden',
|
|
575
|
+
opacity: 0.9,
|
|
576
|
+
}, children: bodyText }), bodyText.length > 600 && (_jsx("button", { type: "button", "data-mol-id": "subagent-report-toggle", className: cm.button({ variant: 'link', color: 'primary', size: 'xs' }), onClick: () => setExpanded((e) => !e), style: { marginTop: '4px' }, children: expanded
|
|
577
|
+
? t('ide.chat.subagent.hideReport', undefined, {
|
|
578
|
+
defaultValue: 'Show less',
|
|
579
|
+
})
|
|
580
|
+
: t('ide.chat.subagent.showReport', undefined, {
|
|
581
|
+
defaultValue: 'Show full report',
|
|
582
|
+
}) }))] }))] }));
|
|
583
|
+
}
|
|
416
584
|
// ── save_plan: clickable row that opens the plan file in the editor ──────────
|
|
417
585
|
if (name === 'save_plan') {
|
|
418
586
|
const planOutput = (output ?? {});
|
|
@@ -452,10 +620,11 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
452
620
|
}
|
|
453
621
|
// ── ask_user: render interactive option list instead of a normal tool card ──
|
|
454
622
|
if (name === 'ask_user') {
|
|
455
|
-
// NEVER render raw tool input. `options` is declared
|
|
456
|
-
// schema, but the schema is a request to a language model, not
|
|
457
|
-
// weaker models send `[{ label: 'Recipe box' }]`,
|
|
458
|
-
//
|
|
623
|
+
// NEVER render raw tool input. `options` is declared as strings-or-objects
|
|
624
|
+
// in the tool schema, but the schema is a request to a language model, not
|
|
625
|
+
// a guarantee — weaker models send `[{ label: 'Recipe box' }]`, nested
|
|
626
|
+
// arrays, or quote-escaped pseudo-JSON, and an object reaching JSX throws
|
|
627
|
+
// React error #31 during render, which takes down the entire IDE.
|
|
459
628
|
const askInput = normalizeAskUserInput(input);
|
|
460
629
|
const askOutput = output;
|
|
461
630
|
const serverAwaiting = typeof askOutput === 'object' && askOutput?.status === 'awaiting_response';
|
|
@@ -465,6 +634,12 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
465
634
|
const selectedAnswer = isResponded ? askOutput : localAnswer;
|
|
466
635
|
const [freeText, setFreeText] = useState('');
|
|
467
636
|
const [hoveredIdx, setHoveredIdx] = useState(null);
|
|
637
|
+
// Multi-select picks in progress (labels); sent as one string joined with
|
|
638
|
+
// '; ' on confirm — the same shape an answered multi-select persists as.
|
|
639
|
+
const [multiPicks, setMultiPicks] = useState([]);
|
|
640
|
+
// Narrow screens have no room for the side-by-side preview pane; there a
|
|
641
|
+
// preview expands inline under its option row instead.
|
|
642
|
+
const [expandedPreviewIdx, setExpandedPreviewIdx] = useState(null);
|
|
468
643
|
// Don't render until the question has streamed in. `tool_use_start` surfaces
|
|
469
644
|
// the card the instant the call begins — before its input arrives — which
|
|
470
645
|
// would otherwise flash an empty question + option list. Once the full input
|
|
@@ -474,6 +649,34 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
474
649
|
return null;
|
|
475
650
|
const borderClr = isLight ? '#d0d7de' : '#3d444d';
|
|
476
651
|
const labelChar = (i) => String.fromCharCode(65 + i); // A, B, C, …
|
|
652
|
+
const hasPreviews = askInput.options.some((option) => option.preview);
|
|
653
|
+
const multi = askInput.multiSelect && askInput.options.length > 0;
|
|
654
|
+
// The plan-approval card renders its two decisions as prominent design-
|
|
655
|
+
// system CTAs (approve = primary solid, request changes = outline) instead
|
|
656
|
+
// of letter-badged rows — this is the product's single most consequential
|
|
657
|
+
// click, and it should look like it.
|
|
658
|
+
const reviewCtas = !!askInput.planReview && !multi && askInput.options.length === 2 && !hasPreviews;
|
|
659
|
+
const answeredLabels = multi && typeof selectedAnswer === 'string'
|
|
660
|
+
? selectedAnswer
|
|
661
|
+
.split('; ')
|
|
662
|
+
.map((part) => part.trim())
|
|
663
|
+
.filter(Boolean)
|
|
664
|
+
: [];
|
|
665
|
+
const selectedSet = new Set([...multiPicks, ...answeredLabels]);
|
|
666
|
+
// Which option's preview the pane shows: the hovered/focused one, else the
|
|
667
|
+
// picked one, else the first option that has a preview at all.
|
|
668
|
+
const previewIdx = (() => {
|
|
669
|
+
if (!hasPreviews)
|
|
670
|
+
return null;
|
|
671
|
+
const candidate = hoveredIdx ?? expandedPreviewIdx;
|
|
672
|
+
if (candidate != null && askInput.options[candidate]?.preview)
|
|
673
|
+
return candidate;
|
|
674
|
+
const pickedIdx = askInput.options.findIndex((option) => selectedSet.has(option.label));
|
|
675
|
+
if (pickedIdx >= 0 && askInput.options[pickedIdx].preview)
|
|
676
|
+
return pickedIdx;
|
|
677
|
+
return askInput.options.findIndex((option) => option.preview);
|
|
678
|
+
})();
|
|
679
|
+
const previewOption = previewIdx != null ? askInput.options[previewIdx] : undefined;
|
|
477
680
|
return (_jsxs("div", { className: className, style: {
|
|
478
681
|
marginBottom: '8px',
|
|
479
682
|
marginTop: '8px',
|
|
@@ -481,71 +684,213 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
481
684
|
border: `1px solid ${borderClr}`,
|
|
482
685
|
background: isLight ? '#f6f8fa' : 'rgba(255,255,255,0.04)',
|
|
483
686
|
overflow: 'hidden',
|
|
484
|
-
}, children: [
|
|
687
|
+
}, children: [askInput.planReview ? (_jsxs("div", { "data-mol-id": "plan-review-card", style: {
|
|
688
|
+
padding: '10px 12px 10px',
|
|
689
|
+
fontSize: '13px',
|
|
690
|
+
borderBottom: `1px solid ${borderClr}`,
|
|
691
|
+
}, children: [_jsx("div", { style: {
|
|
692
|
+
fontSize: '11px',
|
|
693
|
+
fontWeight: 600,
|
|
694
|
+
letterSpacing: '0.03em',
|
|
695
|
+
textTransform: 'uppercase',
|
|
696
|
+
opacity: 0.6,
|
|
697
|
+
marginBottom: '6px',
|
|
698
|
+
}, children: t('ide.chat.planReview.badge', undefined, {
|
|
699
|
+
defaultValue: 'Plan ready for review',
|
|
700
|
+
}) }), _jsx("div", { style: { fontSize: '14px', fontWeight: 600, marginBottom: '4px' }, children: askInput.planReview.name ||
|
|
701
|
+
(askInput.planReview.path ? basename(askInput.planReview.path) : '') ||
|
|
702
|
+
t('ide.chat.planReview.fallbackTitle', undefined, {
|
|
703
|
+
defaultValue: 'Implementation plan',
|
|
704
|
+
}) }), _jsxs("div", { style: {
|
|
705
|
+
display: 'flex',
|
|
706
|
+
alignItems: 'center',
|
|
707
|
+
gap: '10px',
|
|
708
|
+
fontSize: '12px',
|
|
709
|
+
opacity: 0.75,
|
|
710
|
+
marginBottom: askInput.planReview.preview.length > 0 ? '8px' : 0,
|
|
711
|
+
}, children: [_jsx("span", { children: t('ide.chat.planReview.steps', { count: askInput.planReview.steps }, {
|
|
712
|
+
defaultValue: '{{count}} steps',
|
|
713
|
+
}) }), askInput.planReview.path && onFileOpen && (_jsx("button", { type: "button", "data-mol-id": "plan-review-open", className: cm.button({ variant: 'link', color: 'primary', size: 'xs' }), onClick: () => onFileOpen(askInput.planReview.path), children: t('ide.chat.planReview.openPlan', undefined, { defaultValue: 'Open plan' }) }))] }), askInput.planReview.preview.length > 0 && (_jsx("ul", { style: {
|
|
714
|
+
margin: 0,
|
|
715
|
+
paddingLeft: '18px',
|
|
716
|
+
fontSize: '12px',
|
|
717
|
+
opacity: 0.8,
|
|
718
|
+
display: 'flex',
|
|
719
|
+
flexDirection: 'column',
|
|
720
|
+
gap: '2px',
|
|
721
|
+
}, children: askInput.planReview.preview.map((step, i) => (_jsx("li", { children: step }, i))) })), askInput.planReview.steps > askInput.planReview.preview.length && (_jsx("div", { style: { fontSize: '11px', opacity: 0.55, marginTop: '4px' }, children: t('ide.chat.planReview.more', { count: askInput.planReview.steps - askInput.planReview.preview.length }, { defaultValue: '+ {{count}} more steps' }) }))] })) : (_jsxs("div", { style: {
|
|
485
722
|
padding: '4px 12px 10px',
|
|
486
723
|
fontSize: '13px',
|
|
487
724
|
borderBottom: `1px solid ${borderClr}`,
|
|
488
|
-
}, children: _jsx(MarkdownContent, { text: unescapeLiterals(askInput.question), isStreaming: false }) }
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
725
|
+
}, children: [_jsx(MarkdownContent, { text: unescapeLiterals(askInput.question), isStreaming: false }), multi && isAwaiting && (_jsx("div", { style: { fontSize: '11px', fontStyle: 'italic', opacity: 0.55, marginTop: '4px' }, children: t('ide.chat.askUserMultiHint', undefined, {
|
|
726
|
+
defaultValue: 'You can choose more than one.',
|
|
727
|
+
}) }))] })), _jsxs("div", { style: { display: 'flex', alignItems: 'stretch' }, children: [_jsxs("div", { style: {
|
|
728
|
+
flex: hasPreviews && !isNarrow ? '1 1 55%' : '1 1 100%',
|
|
729
|
+
minWidth: 0,
|
|
730
|
+
display: 'flex',
|
|
731
|
+
flexDirection: 'column',
|
|
732
|
+
}, children: [reviewCtas ? (_jsx("div", { style: {
|
|
733
|
+
display: 'flex',
|
|
734
|
+
flexWrap: 'wrap',
|
|
735
|
+
gap: '8px',
|
|
736
|
+
padding: '10px 12px',
|
|
737
|
+
}, children: askInput.options.map((option, i) => {
|
|
738
|
+
const isPicked = selectedAnswer === option.label;
|
|
739
|
+
return (_jsx("button", { type: "button", "data-mol-id": `plan-review-cta-${i}`, disabled: !isAwaiting || onAskUserResponse == null, className: cm.cn(cm.button(i === 0
|
|
740
|
+
? { variant: 'solid', color: 'primary', size: 'sm' }
|
|
741
|
+
: { variant: 'outline', color: 'secondary', size: 'sm' }), cm.touchTargetCompact), style: {
|
|
742
|
+
flex: isNarrow ? '1 1 100%' : '1 1 auto',
|
|
743
|
+
...(isPicked ? { outline: '2px solid #3fb950', outlineOffset: '1px' } : {}),
|
|
744
|
+
}, onClick: () => {
|
|
745
|
+
if (onAskUserResponse == null)
|
|
746
|
+
return;
|
|
747
|
+
setLocalAnswer(option.label);
|
|
748
|
+
onAskUserResponse(option.label);
|
|
749
|
+
}, children: isPicked ? `✓ ${option.label}` : option.label }, i));
|
|
750
|
+
}) })) : (askInput.options.map((option, i) => {
|
|
751
|
+
const isSelected = selectedSet.has(option.label);
|
|
752
|
+
const isFaded = !isAwaiting && !isSelected;
|
|
753
|
+
const isHover = isAwaiting && hoveredIdx === i;
|
|
754
|
+
const isPreviewExpanded = expandedPreviewIdx === i;
|
|
755
|
+
return (_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'stretch' }, children: [_jsxs("button", { type: "button", "data-mol-id": `ask-user-option-${i}`, disabled: !isAwaiting || onAskUserResponse == null, onClick: () => {
|
|
756
|
+
// No handler = read-only (a project viewer): answering is editor work.
|
|
757
|
+
if (onAskUserResponse == null)
|
|
758
|
+
return;
|
|
759
|
+
if (multi) {
|
|
760
|
+
// Multi-select toggles a pick; a Confirm row submits them.
|
|
761
|
+
setMultiPicks((picks) => picks.includes(option.label)
|
|
762
|
+
? picks.filter((p) => p !== option.label)
|
|
763
|
+
: [...picks, option.label]);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
setLocalAnswer(option.label);
|
|
767
|
+
onAskUserResponse(option.label);
|
|
768
|
+
}, onFocus: () => {
|
|
769
|
+
if (isAwaiting)
|
|
770
|
+
setHoveredIdx(i);
|
|
771
|
+
}, onMouseEnter: () => {
|
|
772
|
+
if (isAwaiting)
|
|
773
|
+
setHoveredIdx(i);
|
|
774
|
+
}, onMouseLeave: () => setHoveredIdx(null), style: {
|
|
775
|
+
display: 'flex',
|
|
776
|
+
alignItems: 'center',
|
|
777
|
+
gap: '10px',
|
|
778
|
+
flex: 1,
|
|
779
|
+
minWidth: 0,
|
|
780
|
+
padding: option.description ? '7px 12px' : '8px 12px',
|
|
781
|
+
// Touch: full 44px rows — these are the PRIMARY discovery answers,
|
|
782
|
+
// so they get the standalone-control floor, not the dense-row 32.
|
|
783
|
+
...(isCoarse ? { minHeight: 44 } : {}),
|
|
784
|
+
border: 'none',
|
|
785
|
+
borderTop: i > 0 ? `1px solid ${borderClr}` : 'none',
|
|
786
|
+
background: isSelected
|
|
787
|
+
? isLight
|
|
788
|
+
? '#dbeafe'
|
|
789
|
+
: 'rgba(59,130,246,0.2)'
|
|
790
|
+
: isHover
|
|
791
|
+
? isLight
|
|
792
|
+
? '#eaeef2'
|
|
793
|
+
: 'rgba(255,255,255,0.06)'
|
|
794
|
+
: 'transparent',
|
|
795
|
+
color: 'inherit',
|
|
796
|
+
cursor: isAwaiting ? 'pointer' : 'default',
|
|
797
|
+
textAlign: 'left',
|
|
798
|
+
fontSize: '13px',
|
|
799
|
+
opacity: isFaded ? 0.4 : 1,
|
|
800
|
+
transition: 'background 80ms, opacity 80ms',
|
|
801
|
+
}, children: [_jsx("span", { style: {
|
|
802
|
+
display: 'inline-flex',
|
|
803
|
+
alignItems: 'center',
|
|
804
|
+
justifyContent: 'center',
|
|
805
|
+
width: 22,
|
|
806
|
+
height: 22,
|
|
807
|
+
borderRadius: multi ? '6px' : '5px',
|
|
808
|
+
border: `1px solid ${isSelected ? (isLight ? '#93c5fd' : '#3b82f6') : borderClr}`,
|
|
809
|
+
background: isSelected
|
|
810
|
+
? isLight
|
|
811
|
+
? '#3b82f6'
|
|
812
|
+
: '#2563eb'
|
|
813
|
+
: isLight
|
|
814
|
+
? '#fff'
|
|
815
|
+
: 'rgba(255,255,255,0.08)',
|
|
816
|
+
color: isSelected ? '#fff' : isLight ? '#57606a' : '#848d97',
|
|
817
|
+
fontSize: '11px',
|
|
818
|
+
fontWeight: 600,
|
|
819
|
+
flexShrink: 0,
|
|
820
|
+
fontFamily: '"SF Mono", Menlo, Consolas, "Courier New", monospace',
|
|
821
|
+
}, children: multi && isSelected ? '✓' : labelChar(i) }), _jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [_jsx("span", { style: { display: 'block' }, children: option.label }), option.description && (_jsx("span", { style: {
|
|
822
|
+
display: 'block',
|
|
823
|
+
fontSize: '11px',
|
|
824
|
+
opacity: 0.65,
|
|
825
|
+
marginTop: '1px',
|
|
826
|
+
}, children: option.description }))] }), !multi && isSelected && (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "14", height: "14", fill: isLight ? '#2563eb' : '#60a5fa', children: _jsx("path", { d: "M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z" }) }))] }), hasPreviews && isNarrow && option.preview && (_jsx("button", { type: "button", "data-mol-id": `ask-user-option-preview-${i}`, "aria-label": t('ide.chat.askUserPreview', undefined, {
|
|
827
|
+
defaultValue: 'Preview',
|
|
828
|
+
}), disabled: !isAwaiting, onClick: () => setExpandedPreviewIdx(isPreviewExpanded ? null : i), style: {
|
|
829
|
+
flexShrink: 0,
|
|
830
|
+
width: 36,
|
|
831
|
+
border: 'none',
|
|
832
|
+
borderTop: i > 0 ? `1px solid ${borderClr}` : 'none',
|
|
833
|
+
borderLeft: `1px solid ${borderClr}`,
|
|
834
|
+
background: isPreviewExpanded
|
|
835
|
+
? isLight
|
|
836
|
+
? '#eaeef2'
|
|
837
|
+
: 'rgba(255,255,255,0.06)'
|
|
838
|
+
: 'transparent',
|
|
839
|
+
color: 'inherit',
|
|
840
|
+
cursor: isAwaiting ? 'pointer' : 'default',
|
|
841
|
+
fontSize: '14px',
|
|
842
|
+
opacity: 0.7,
|
|
843
|
+
transform: isPreviewExpanded ? 'rotate(90deg)' : 'none',
|
|
844
|
+
transition: 'transform 80ms',
|
|
845
|
+
}, children: "\u203A" }))] }), hasPreviews && isNarrow && option.preview && isPreviewExpanded && (_jsx("div", { style: {
|
|
846
|
+
borderTop: `1px solid ${borderClr}`,
|
|
847
|
+
background: isLight ? '#fff' : 'rgba(255,255,255,0.03)',
|
|
848
|
+
padding: '8px 12px',
|
|
849
|
+
maxHeight: 240,
|
|
850
|
+
overflowY: 'auto',
|
|
851
|
+
fontSize: '12px',
|
|
852
|
+
}, children: _jsx(MarkdownContent, { text: option.preview, isStreaming: false }) }))] }, i));
|
|
853
|
+
})), multi && isAwaiting && onAskUserResponse != null && (_jsxs("div", { style: {
|
|
854
|
+
display: 'flex',
|
|
855
|
+
justifyContent: 'flex-end',
|
|
856
|
+
gap: '8px',
|
|
857
|
+
alignItems: 'center',
|
|
858
|
+
padding: '8px 12px',
|
|
859
|
+
borderTop: `1px solid ${borderClr}`,
|
|
860
|
+
}, children: [_jsx("span", { style: { fontSize: '11px', opacity: 0.6 }, children: t('ide.chat.askUserSelectedCount', undefined, {
|
|
861
|
+
defaultValue: '{{count}} selected',
|
|
862
|
+
count: multiPicks.length,
|
|
863
|
+
}) }), _jsx("button", { type: "button", "data-mol-id": "ask-user-multiselect-confirm", disabled: multiPicks.length === 0, className: cm.cn(cm.button({ variant: 'solid', color: 'primary', size: 'xs' }), cm.touchTargetCompact), onClick: () => {
|
|
864
|
+
if (multiPicks.length === 0)
|
|
865
|
+
return;
|
|
866
|
+
setLocalAnswer(multiPicks.join('; '));
|
|
867
|
+
onAskUserResponse(multiPicks.join('; '));
|
|
868
|
+
}, children: t('ide.chat.askUserMultiConfirm', undefined, {
|
|
869
|
+
defaultValue: 'Confirm choice',
|
|
870
|
+
}) })] }))] }), hasPreviews && !isNarrow && (_jsxs("div", { style: {
|
|
871
|
+
flex: '1 1 45%',
|
|
872
|
+
minWidth: 0,
|
|
873
|
+
borderLeft: `1px solid ${borderClr}`,
|
|
874
|
+
display: 'flex',
|
|
875
|
+
flexDirection: 'column',
|
|
876
|
+
background: isLight ? '#fff' : 'rgba(255,255,255,0.03)',
|
|
877
|
+
}, children: [_jsx("div", { style: {
|
|
878
|
+
padding: '6px 12px',
|
|
879
|
+
fontSize: '11px',
|
|
880
|
+
fontWeight: 600,
|
|
881
|
+
opacity: 0.6,
|
|
882
|
+
borderBottom: `1px solid ${borderClr}`,
|
|
883
|
+
whiteSpace: 'nowrap',
|
|
884
|
+
overflow: 'hidden',
|
|
885
|
+
textOverflow: 'ellipsis',
|
|
886
|
+
}, children: previewOption
|
|
887
|
+
? previewOption.label
|
|
888
|
+
: t('ide.chat.askUserPreview', undefined, { defaultValue: 'Preview' }) }), _jsx("div", { style: {
|
|
889
|
+
padding: '8px 12px',
|
|
890
|
+
maxHeight: 360,
|
|
891
|
+
overflowY: 'auto',
|
|
892
|
+
fontSize: '12px',
|
|
893
|
+
}, children: previewOption?.preview && (_jsx(MarkdownContent, { text: previewOption.preview, isStreaming: false })) })] }))] }), isAwaiting && askInput.allowFreeText !== false && onAskUserResponse != null && (_jsxs("div", { style: {
|
|
549
894
|
display: 'flex',
|
|
550
895
|
gap: '4px',
|
|
551
896
|
padding: '8px 12px',
|
|
@@ -610,7 +955,7 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
610
955
|
fontWeight: 500,
|
|
611
956
|
opacity: freeText.trim() ? 1 : 0.3,
|
|
612
957
|
transition: 'background 100ms, border-color 100ms, color 100ms',
|
|
613
|
-
}, children: t('ide.chat.askUserSubmit', undefined, { defaultValue: 'Send' }) })] })), selectedAnswer && !askInput.options.
|
|
958
|
+
}, children: t('ide.chat.askUserSubmit', undefined, { defaultValue: 'Send' }) })] })), selectedAnswer && !askInput.options.some((option) => option.label === selectedAnswer) && (_jsx("div", { style: {
|
|
614
959
|
padding: '8px 12px',
|
|
615
960
|
borderTop: `1px solid ${borderClr}`,
|
|
616
961
|
fontSize: '12px',
|
|
@@ -623,97 +968,107 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
623
968
|
opacity: 0.5,
|
|
624
969
|
}, children: askInput.hint }))] }));
|
|
625
970
|
}
|
|
626
|
-
return (_jsxs("div", { className: className, style: { marginBottom: '4px' }, children: [_jsxs("button", { type: "button", onClick: handleClick, onDoubleClick: isNewFile && filePath && onFileDoubleClick
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
display: 'flex',
|
|
632
|
-
// Coarse: the undo icon's 32px touch box inflates the label row, so
|
|
633
|
-
// top-alignment would leave the diff stats/chevron riding high —
|
|
634
|
-
// center everything instead. Desktop keeps flex-start + px nudges.
|
|
635
|
-
alignItems: isCoarse ? 'center' : 'flex-start',
|
|
636
|
-
gap: '6px',
|
|
637
|
-
background: 'none',
|
|
638
|
-
border: 'none',
|
|
639
|
-
cursor: handleClick ? 'pointer' : 'default',
|
|
640
|
-
color: 'inherit',
|
|
641
|
-
textAlign: 'left',
|
|
642
|
-
padding: '2px 0',
|
|
643
|
-
// Touch: ~20px rows are untappable — 32px is the dense-row floor.
|
|
644
|
-
...(isCoarse ? { minHeight: 32 } : {}),
|
|
645
|
-
width: '100%',
|
|
646
|
-
}, children: [_jsx("span", { style: { flex: 1, minWidth: 0 }, children: _jsxs("span", { style: { fontSize: '13px', display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsxs("svg", { width: "10", height: "10", viewBox: "0 0 10 10", style: { flexShrink: 0 }, children: [_jsx("circle", { cx: "5", cy: "5", r: "3", fill: dotColor, opacity: "0.35" }), _jsx("circle", { cx: "5", cy: "5", r: "3", fill: "none", stroke: dotColor, strokeWidth: "2" })] }), _jsx("span", { style: {
|
|
647
|
-
flex: 1,
|
|
648
|
-
minWidth: 0,
|
|
649
|
-
overflow: 'hidden',
|
|
650
|
-
textOverflow: 'ellipsis',
|
|
651
|
-
whiteSpace: 'nowrap',
|
|
652
|
-
}, children: renderLabel(name, input, filePath, onFileOpen, onFileDoubleClick) }), summary && (_jsx("span", { className: cm.cn(cm.textMuted, cm.textSize('xs')), style: { flexShrink: 0, marginLeft: '8px', whiteSpace: 'nowrap' }, children: summary })), canRevert && status !== 'running' && (_jsx("span", { role: "button", tabIndex: 0, title: isUndone
|
|
653
|
-
? t('ide.chat.redoChange', undefined, { defaultValue: 'Re-apply this change' })
|
|
654
|
-
: t('ide.chat.undoChange', undefined, { defaultValue: 'Undo this change' }), onClick: handleRevert, onKeyDown: (e) => {
|
|
655
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
656
|
-
e.preventDefault();
|
|
657
|
-
handleRevert(e);
|
|
658
|
-
}
|
|
659
|
-
}, onMouseEnter: (e) => {
|
|
660
|
-
e.currentTarget.style.background = 'rgba(128,128,128,0.2)';
|
|
661
|
-
e.currentTarget.style.opacity = '1';
|
|
662
|
-
}, onMouseLeave: (e) => {
|
|
663
|
-
e.currentTarget.style.background = 'transparent';
|
|
664
|
-
e.currentTarget.style.opacity = '';
|
|
665
|
-
}, style: {
|
|
666
|
-
display: 'inline-flex',
|
|
667
|
-
alignItems: 'center',
|
|
668
|
-
justifyContent: 'center',
|
|
669
|
-
// Touch: hover can't reveal it, so it rests visible at 0.6 and
|
|
670
|
-
// gets a 32px hit box (the floor for these dense inline rows).
|
|
671
|
-
width: isCoarse ? 32 : 20,
|
|
672
|
-
height: isCoarse ? 32 : 20,
|
|
673
|
-
borderRadius: 4,
|
|
674
|
-
flexShrink: 0,
|
|
675
|
-
// Nudge up 1px: the 13px glyph sat slightly below the text's
|
|
676
|
-
// optical center of the compact 20px box (moot at 32px).
|
|
677
|
-
position: 'relative',
|
|
678
|
-
top: isCoarse ? 0 : '-1px',
|
|
679
|
-
cursor: isReverting ? 'wait' : 'pointer',
|
|
680
|
-
opacity: isReverting ? 0.3 : isHovered || isCoarse ? 0.6 : 0,
|
|
681
|
-
transition: 'opacity 100ms, background 100ms',
|
|
682
|
-
}, children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "13", height: "13", fill: "currentColor", children: isUndone ? (_jsx("path", { d: "M14.78 6.28a.749.749 0 0 0 0-1.06l-3.5-3.5a.749.749 0 1 0-1.06 1.06L12.439 5H5.251l-.001.007L5.251 5a.8.8 0 0 0-.171.019A4.501 4.501 0 0 0 5.5 14h1.704a.75.75 0 0 0 0-1.5H5.5a3 3 0 1 1 0-6h6.939L10.22 8.72a.749.749 0 1 0 1.06 1.06l3.5-3.5Z" })) : (_jsx("path", { d: "M1.22 6.28a.749.749 0 0 1 0-1.06l3.5-3.5a.749.749 0 1 1 1.06 1.06L3.561 5h7.188l.001.007L10.749 5c.058 0 .116.007.171.019A4.501 4.501 0 0 1 10.5 14H8.796a.75.75 0 0 1 0-1.5H10.5a3 3 0 1 0 0-6H3.561L5.78 8.72a.749.749 0 1 1-1.06 1.06l-3.5-3.5Z" })) }) }))] }) }), diffStats && (_jsxs("span", { style: {
|
|
971
|
+
return (_jsxs("div", { className: className, style: { marginBottom: '4px' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }, children: [_jsxs("button", { type: "button", onClick: handleClick, onDoubleClick: isNewFile && filePath && onFileDoubleClick
|
|
972
|
+
? () => {
|
|
973
|
+
onFileDoubleClick(filePath);
|
|
974
|
+
}
|
|
975
|
+
: undefined, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), style: {
|
|
683
976
|
display: 'flex',
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
977
|
+
// Coarse: the undo icon's 32px touch box inflates the label row, so
|
|
978
|
+
// top-alignment would leave the diff stats/chevron riding high —
|
|
979
|
+
// center everything instead. Desktop keeps flex-start + px nudges.
|
|
980
|
+
alignItems: isCoarse ? 'center' : 'flex-start',
|
|
981
|
+
gap: '6px',
|
|
982
|
+
background: 'none',
|
|
983
|
+
border: 'none',
|
|
984
|
+
cursor: handleClick ? 'pointer' : 'default',
|
|
985
|
+
color: 'inherit',
|
|
986
|
+
textAlign: 'left',
|
|
987
|
+
padding: '2px 0',
|
|
988
|
+
// Touch: ~20px rows are untappable — 32px is the dense-row floor.
|
|
989
|
+
...(isCoarse ? { minHeight: 32 } : {}),
|
|
990
|
+
width: '100%',
|
|
991
|
+
// The row shares its line with the Skip button: without this it
|
|
992
|
+
// keeps its intrinsic min-width and pushes the Skip off a 390px
|
|
993
|
+
// screen instead of letting the label ellipsise.
|
|
994
|
+
minWidth: 0,
|
|
995
|
+
}, children: [_jsx("span", { style: { flex: 1, minWidth: 0 }, children: _jsxs("span", { style: { fontSize: '13px', display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsxs("svg", { width: "10", height: "10", viewBox: "0 0 10 10", style: { flexShrink: 0 }, children: [_jsx("circle", { cx: "5", cy: "5", r: "3", fill: dotColor, opacity: "0.35" }), _jsx("circle", { cx: "5", cy: "5", r: "3", fill: "none", stroke: dotColor, strokeWidth: "2" })] }), _jsx("span", { style: {
|
|
996
|
+
flex: 1,
|
|
997
|
+
minWidth: 0,
|
|
998
|
+
overflow: 'hidden',
|
|
999
|
+
textOverflow: 'ellipsis',
|
|
1000
|
+
whiteSpace: 'nowrap',
|
|
1001
|
+
}, children: renderLabel(name, input, filePath, onFileOpen, onFileDoubleClick) }), summary && (_jsx("span", { className: cm.cn(cm.textMuted, cm.textSize('xs')), style: { flexShrink: 0, marginLeft: '8px', whiteSpace: 'nowrap' }, children: summary })), canRevert && status !== 'running' && (_jsx("span", { role: "button", tabIndex: 0, title: isUndone
|
|
1002
|
+
? t('ide.chat.redoChange', undefined, {
|
|
1003
|
+
defaultValue: 'Re-apply this change',
|
|
1004
|
+
})
|
|
1005
|
+
: t('ide.chat.undoChange', undefined, { defaultValue: 'Undo this change' }), onClick: handleRevert, onKeyDown: (e) => {
|
|
1006
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
1007
|
+
e.preventDefault();
|
|
1008
|
+
handleRevert(e);
|
|
1009
|
+
}
|
|
1010
|
+
}, onMouseEnter: (e) => {
|
|
1011
|
+
e.currentTarget.style.background = 'rgba(128,128,128,0.2)';
|
|
1012
|
+
e.currentTarget.style.opacity = '1';
|
|
1013
|
+
}, onMouseLeave: (e) => {
|
|
1014
|
+
e.currentTarget.style.background = 'transparent';
|
|
1015
|
+
e.currentTarget.style.opacity = '';
|
|
1016
|
+
}, style: {
|
|
1017
|
+
display: 'inline-flex',
|
|
1018
|
+
alignItems: 'center',
|
|
1019
|
+
justifyContent: 'center',
|
|
1020
|
+
// Touch: hover can't reveal it, so it rests visible at 0.6 and
|
|
1021
|
+
// gets a 32px hit box (the floor for these dense inline rows).
|
|
1022
|
+
width: isCoarse ? 32 : 20,
|
|
1023
|
+
height: isCoarse ? 32 : 20,
|
|
1024
|
+
borderRadius: 4,
|
|
1025
|
+
flexShrink: 0,
|
|
1026
|
+
// Nudge up 1px: the 13px glyph sat slightly below the text's
|
|
1027
|
+
// optical center of the compact 20px box (moot at 32px).
|
|
1028
|
+
position: 'relative',
|
|
1029
|
+
top: isCoarse ? 0 : '-1px',
|
|
1030
|
+
cursor: isReverting ? 'wait' : 'pointer',
|
|
1031
|
+
opacity: isReverting ? 0.3 : isHovered || isCoarse ? 0.6 : 0,
|
|
1032
|
+
transition: 'opacity 100ms, background 100ms',
|
|
1033
|
+
}, children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "13", height: "13", fill: "currentColor", children: isUndone ? (_jsx("path", { d: "M14.78 6.28a.749.749 0 0 0 0-1.06l-3.5-3.5a.749.749 0 1 0-1.06 1.06L12.439 5H5.251l-.001.007L5.251 5a.8.8 0 0 0-.171.019A4.501 4.501 0 0 0 5.5 14h1.704a.75.75 0 0 0 0-1.5H5.5a3 3 0 1 1 0-6h6.939L10.22 8.72a.749.749 0 1 0 1.06 1.06l3.5-3.5Z" })) : (_jsx("path", { d: "M1.22 6.28a.749.749 0 0 1 0-1.06l3.5-3.5a.749.749 0 1 1 1.06 1.06L3.561 5h7.188l.001.007L10.749 5c.058 0 .116.007.171.019A4.501 4.501 0 0 1 10.5 14H8.796a.75.75 0 0 1 0-1.5H10.5a3 3 0 1 0 0-6H3.561L5.78 8.72a.749.749 0 1 1-1.06 1.06l-3.5-3.5Z" })) }) }))] }) }), diffStats && (_jsxs("span", { style: {
|
|
1034
|
+
display: 'flex',
|
|
1035
|
+
gap: '4px',
|
|
1036
|
+
flexShrink: 0,
|
|
1037
|
+
marginTop: isCoarse ? 0 : '2px',
|
|
1038
|
+
fontSize: '11px',
|
|
1039
|
+
fontFamily: '"SF Mono", Menlo, Consolas, "Courier New", monospace',
|
|
1040
|
+
opacity: isHovered ? 1 : 0.6,
|
|
1041
|
+
transition: 'opacity 100ms',
|
|
1042
|
+
}, children: [diffStats.added > 0 && (_jsxs("span", { style: {
|
|
1043
|
+
color: isUndone
|
|
1044
|
+
? isLight
|
|
1045
|
+
? '#cf222e'
|
|
1046
|
+
: '#f47067'
|
|
1047
|
+
: isLight
|
|
1048
|
+
? '#1a7f37'
|
|
1049
|
+
: '#57ab5a',
|
|
1050
|
+
textDecoration: isUndone ? 'line-through' : undefined,
|
|
1051
|
+
}, children: ["+", diffStats.added] })), diffStats.removed > 0 && (_jsxs("span", { style: {
|
|
1052
|
+
color: isUndone
|
|
1053
|
+
? isLight
|
|
1054
|
+
? '#1a7f37'
|
|
1055
|
+
: '#57ab5a'
|
|
1056
|
+
: isLight
|
|
1057
|
+
? '#cf222e'
|
|
1058
|
+
: '#f47067',
|
|
1059
|
+
textDecoration: isUndone ? 'line-through' : undefined,
|
|
1060
|
+
}, children: ["-", diffStats.removed] }))] })), (hasDetails || isFileDiff || isDocOpen || (isNewFile && filePath && onFileOpen)) && (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "14", height: "14", style: {
|
|
1061
|
+
display: 'block',
|
|
1062
|
+
flexShrink: 0,
|
|
1063
|
+
marginTop: isCoarse ? 0 : '3px',
|
|
1064
|
+
transform: expanded ? 'rotate(90deg)' : 'rotate(0deg)',
|
|
1065
|
+
transition: 'transform 150ms, opacity 100ms',
|
|
1066
|
+
opacity: isHovered ? 0.85 : 0.35,
|
|
1067
|
+
}, children: _jsx("polyline", { points: "6,4 10,8 6,12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }))] }), canSkip && (_jsx("button", { type: "button", "data-mol-id": `tool-call-skip-${id}`, onClick: () => {
|
|
1068
|
+
void handleSkip();
|
|
1069
|
+
}, disabled: skipRequested || skipDisabledReason != null, title: skipDisabledReason ?? undefined, className: cm.cn(cm.button({ variant: 'solid', color: 'primary', size: 'xs' })), style: { flexShrink: 0 }, children: skipRequested
|
|
1070
|
+
? t('ide.chat.skippingToolCall', undefined, { defaultValue: 'Skipping…' })
|
|
1071
|
+
: t('ide.chat.skipToolCall', undefined, { defaultValue: 'Skip' }) }))] }), expanded && hasDetails && (_jsx("div", { className: cm.surfaceSecondary, style: {
|
|
717
1072
|
marginLeft: '14px',
|
|
718
1073
|
marginTop: '4px',
|
|
719
1074
|
marginBottom: '4px',
|