@molecule/app-ide-react 1.16.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 +7 -1
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +57 -1
- 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/ToolCallCard.d.ts.map +1 -1
- package/dist/components/ToolCallCard.js +384 -68
- 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/tool-call-utilities.d.ts +68 -7
- package/dist/components/tool-call-utilities.d.ts.map +1 -1
- package/dist/components/tool-call-utilities.js +124 -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/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, isSkippedByUser, 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
|
|
@@ -442,6 +442,145 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
442
442
|
setExpanded((e) => !e);
|
|
443
443
|
}
|
|
444
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
|
+
}
|
|
445
584
|
// ── save_plan: clickable row that opens the plan file in the editor ──────────
|
|
446
585
|
if (name === 'save_plan') {
|
|
447
586
|
const planOutput = (output ?? {});
|
|
@@ -481,10 +620,11 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
481
620
|
}
|
|
482
621
|
// ── ask_user: render interactive option list instead of a normal tool card ──
|
|
483
622
|
if (name === 'ask_user') {
|
|
484
|
-
// NEVER render raw tool input. `options` is declared
|
|
485
|
-
// schema, but the schema is a request to a language model, not
|
|
486
|
-
// weaker models send `[{ label: 'Recipe box' }]`,
|
|
487
|
-
//
|
|
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.
|
|
488
628
|
const askInput = normalizeAskUserInput(input);
|
|
489
629
|
const askOutput = output;
|
|
490
630
|
const serverAwaiting = typeof askOutput === 'object' && askOutput?.status === 'awaiting_response';
|
|
@@ -494,6 +634,12 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
494
634
|
const selectedAnswer = isResponded ? askOutput : localAnswer;
|
|
495
635
|
const [freeText, setFreeText] = useState('');
|
|
496
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);
|
|
497
643
|
// Don't render until the question has streamed in. `tool_use_start` surfaces
|
|
498
644
|
// the card the instant the call begins — before its input arrives — which
|
|
499
645
|
// would otherwise flash an empty question + option list. Once the full input
|
|
@@ -503,6 +649,34 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
503
649
|
return null;
|
|
504
650
|
const borderClr = isLight ? '#d0d7de' : '#3d444d';
|
|
505
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;
|
|
506
680
|
return (_jsxs("div", { className: className, style: {
|
|
507
681
|
marginBottom: '8px',
|
|
508
682
|
marginTop: '8px',
|
|
@@ -510,71 +684,213 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
510
684
|
border: `1px solid ${borderClr}`,
|
|
511
685
|
background: isLight ? '#f6f8fa' : 'rgba(255,255,255,0.04)',
|
|
512
686
|
overflow: 'hidden',
|
|
513
|
-
}, 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: {
|
|
514
722
|
padding: '4px 12px 10px',
|
|
515
723
|
fontSize: '13px',
|
|
516
724
|
borderBottom: `1px solid ${borderClr}`,
|
|
517
|
-
}, children: _jsx(MarkdownContent, { text: unescapeLiterals(askInput.question), isStreaming: false }) }
|
|
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
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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: {
|
|
578
894
|
display: 'flex',
|
|
579
895
|
gap: '4px',
|
|
580
896
|
padding: '8px 12px',
|
|
@@ -639,7 +955,7 @@ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output
|
|
|
639
955
|
fontWeight: 500,
|
|
640
956
|
opacity: freeText.trim() ? 1 : 0.3,
|
|
641
957
|
transition: 'background 100ms, border-color 100ms, color 100ms',
|
|
642
|
-
}, 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: {
|
|
643
959
|
padding: '8px 12px',
|
|
644
960
|
borderTop: `1px solid ${borderClr}`,
|
|
645
961
|
fontSize: '12px',
|