@theia/ai-chat-ui 1.72.0-next.21 → 1.72.0-next.29

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.
Files changed (32) hide show
  1. package/lib/browser/chat-input-widget.js +1 -1
  2. package/lib/browser/chat-input-widget.js.map +1 -1
  3. package/lib/browser/chat-input-widget.spec.d.ts +2 -0
  4. package/lib/browser/chat-input-widget.spec.d.ts.map +1 -0
  5. package/lib/browser/chat-input-widget.spec.js +64 -0
  6. package/lib/browser/chat-input-widget.spec.js.map +1 -0
  7. package/lib/browser/chat-response-renderer/delegation-tool-renderer.d.ts +2 -1
  8. package/lib/browser/chat-response-renderer/delegation-tool-renderer.d.ts.map +1 -1
  9. package/lib/browser/chat-response-renderer/delegation-tool-renderer.js +14 -1
  10. package/lib/browser/chat-response-renderer/delegation-tool-renderer.js.map +1 -1
  11. package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts +10 -0
  12. package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts.map +1 -1
  13. package/lib/browser/chat-response-renderer/markdown-part-renderer.js +11 -6
  14. package/lib/browser/chat-response-renderer/markdown-part-renderer.js.map +1 -1
  15. package/lib/browser/chat-response-renderer/tool-confirmation.d.ts +6 -2
  16. package/lib/browser/chat-response-renderer/tool-confirmation.d.ts.map +1 -1
  17. package/lib/browser/chat-response-renderer/tool-confirmation.js +30 -8
  18. package/lib/browser/chat-response-renderer/tool-confirmation.js.map +1 -1
  19. package/lib/browser/chat-response-renderer/tool-confirmation.spec.js +60 -1
  20. package/lib/browser/chat-response-renderer/tool-confirmation.spec.js.map +1 -1
  21. package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts.map +1 -1
  22. package/lib/browser/chat-response-renderer/toolcall-part-renderer.js +5 -9
  23. package/lib/browser/chat-response-renderer/toolcall-part-renderer.js.map +1 -1
  24. package/package.json +11 -11
  25. package/src/browser/chat-input-widget.spec.ts +74 -0
  26. package/src/browser/chat-input-widget.tsx +1 -1
  27. package/src/browser/chat-response-renderer/delegation-tool-renderer.tsx +14 -7
  28. package/src/browser/chat-response-renderer/markdown-part-renderer.tsx +16 -7
  29. package/src/browser/chat-response-renderer/tool-confirmation.spec.ts +79 -3
  30. package/src/browser/chat-response-renderer/tool-confirmation.tsx +60 -10
  31. package/src/browser/chat-response-renderer/toolcall-part-renderer.tsx +8 -8
  32. package/src/browser/style/index.css +78 -68
@@ -26,7 +26,7 @@ import { ResponseNode } from '../chat-tree-view';
26
26
  import { SubChatWidgetFactory } from '../chat-tree-view/sub-chat-widget';
27
27
  import { withToolCallConfirmation } from './tool-confirmation';
28
28
  import { extractJsonStringField } from './toolcall-utils';
29
- import { CompositeTreeNode, ContextMenuRenderer } from '@theia/core/lib/browser';
29
+ import { CompositeTreeNode, ContextMenuRenderer, OpenerService } from '@theia/core/lib/browser';
30
30
  import { ContributionProvider, DisposableCollection, nls } from '@theia/core';
31
31
  import * as React from '@theia/core/shared/react';
32
32
 
@@ -51,6 +51,9 @@ export class DelegationToolRenderer implements ChatResponsePartRenderer<ToolCall
51
51
  @inject(ContextMenuRenderer)
52
52
  protected contextMenuRenderer: ContextMenuRenderer;
53
53
 
54
+ @inject(OpenerService)
55
+ protected openerService: OpenerService;
56
+
54
57
  @inject(ContributionProvider) @named(ChatResponsePartRenderer)
55
58
  protected chatResponsePartRenderers: ContributionProvider<ChatResponsePartRenderer<ChatResponseContent>>;
56
59
 
@@ -100,12 +103,16 @@ export class DelegationToolRenderer implements ChatResponsePartRenderer<ToolCall
100
103
  subChatWidgetFactory={this.subChatWidgetFactory}
101
104
  contextMenuRenderer={this.contextMenuRenderer}
102
105
  chatResponsePartRenderers={this.chatResponsePartRenderers}
103
- response={response}
104
- confirmationMode={confirmationMode}
105
- toolConfirmationManager={this.toolConfirmationManager}
106
- toolRequest={toolRequest}
107
- chatId={chatId}
108
- requestCanceled={parentNode.response.isCanceled}
106
+ toolConfirmation={{
107
+ response,
108
+ confirmationMode,
109
+ toolConfirmationManager: this.toolConfirmationManager,
110
+ toolRequest,
111
+ chatId,
112
+ requestCanceled: parentNode.response.isCanceled,
113
+ contextMenuRenderer: this.contextMenuRenderer,
114
+ openerService: this.openerService
115
+ }}
109
116
  />;
110
117
  }
111
118
  }
@@ -30,6 +30,21 @@ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
30
30
  import { OpenerService, open } from '@theia/core/lib/browser';
31
31
  import { URI } from '@theia/core';
32
32
 
33
+ export interface MarkdownRenderProps {
34
+ text: string | MarkdownString;
35
+ openerService: OpenerService;
36
+ className?: string;
37
+ }
38
+
39
+ /**
40
+ * Renders the given markdown via {@link useMarkdownRendering} into a `<div>`.
41
+ * Shared component for use across chat response renderers.
42
+ */
43
+ export const MarkdownRender: React.FC<MarkdownRenderProps> = ({ text, openerService, className }) => {
44
+ const ref = useMarkdownRendering(text, openerService);
45
+ return <div className={className} ref={ref}></div>;
46
+ };
47
+
33
48
  @injectable()
34
49
  export class MarkdownPartRenderer implements ChatResponsePartRenderer<MarkdownChatResponseContent | InformationalChatResponseContent> {
35
50
  @inject(OpenerService) protected readonly openerService: OpenerService;
@@ -51,16 +66,10 @@ export class MarkdownPartRenderer implements ChatResponsePartRenderer<MarkdownCh
51
66
  return null;
52
67
  }
53
68
 
54
- return <MarkdownRender response={response} openerService={this.openerService} />;
69
+ return <MarkdownRender text={response.content} openerService={this.openerService} />;
55
70
  }
56
71
  }
57
72
 
58
- const MarkdownRender = ({ response, openerService }: { response: MarkdownChatResponseContent | InformationalChatResponseContent; openerService: OpenerService }) => {
59
- const ref = useMarkdownRendering(response.content, openerService);
60
-
61
- return <div ref={ref}></div>;
62
- };
63
-
64
73
  export interface DeclaredEventsEventListenerObject extends EventListenerObject {
65
74
  handledEvents?: (keyof HTMLElementEventMap)[];
66
75
  }
@@ -14,12 +14,31 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
+ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
18
+
19
+ let disableJSDOM = enableJSDOM();
20
+
17
21
  import { expect } from 'chai';
18
- import { ContextMenuRenderer } from '@theia/core/lib/browser';
22
+ import { ContextMenuRenderer, OpenerService } from '@theia/core/lib/browser';
19
23
  import { ToolCallChatResponseContent } from '@theia/ai-chat/lib/common';
20
- import { ConfirmationScope, CountdownTimerProps, ToolConfirmationCallbacks, ToolConfirmationActionsProps, ToolConfirmationProps } from './tool-confirmation';
24
+ import { ToolConfirmationMode as ToolConfirmationPreferenceMode } from '@theia/ai-chat/lib/common/chat-tool-preferences';
25
+ import { ToolConfirmationManager } from '@theia/ai-chat/lib/browser/chat-tool-preference-bindings';
26
+ import { flushSync } from '@theia/core/shared/react-dom';
27
+ import { createRoot } from '@theia/core/shared/react-dom/client';
28
+ import * as React from '@theia/core/shared/react';
29
+ import {
30
+ ConfirmationScope,
31
+ CountdownTimerProps,
32
+ ToolConfirmationCallbacks,
33
+ ToolConfirmationActionsProps,
34
+ ToolConfirmationProps,
35
+ withToolCallConfirmation
36
+ } from './tool-confirmation';
21
37
 
22
38
  const mockContextMenuRenderer = {} as ContextMenuRenderer;
39
+ const mockOpenerService = {} as OpenerService;
40
+
41
+ disableJSDOM();
23
42
 
24
43
  describe('Tool Confirmation Types', () => {
25
44
  describe('ConfirmationScope', () => {
@@ -109,15 +128,72 @@ describe('Tool Confirmation Types', () => {
109
128
  });
110
129
  });
111
130
 
131
+ describe('withToolCallConfirmation', () => {
132
+ before(() => {
133
+ disableJSDOM = enableJSDOM();
134
+ });
135
+
136
+ after(() => {
137
+ disableJSDOM();
138
+ });
139
+
140
+ it('should pass opener service through to the wrapped component', () => {
141
+ const Wrapped: React.FC<{ openerService: OpenerService }> = ({ openerService }) =>
142
+ React.createElement('span', undefined, openerService === mockOpenerService ? 'passed' : 'missing');
143
+ const WrappedWithConfirmation = withToolCallConfirmation(Wrapped);
144
+ const container = document.createElement('div');
145
+ const root = createRoot(container);
146
+ const response = {
147
+ kind: 'toolCall',
148
+ id: 'test',
149
+ name: 'test',
150
+ finished: true,
151
+ confirmed: Promise.resolve(true),
152
+ needsUserConfirmation: Promise.resolve(),
153
+ confirm: () => { },
154
+ deny: () => { }
155
+ } as ToolCallChatResponseContent;
156
+
157
+ flushSync(() => root.render(React.createElement(WrappedWithConfirmation, {
158
+ openerService: mockOpenerService,
159
+ toolConfirmation: {
160
+ response,
161
+ confirmationMode: ToolConfirmationPreferenceMode.CONFIRM,
162
+ toolConfirmationManager: {} as ToolConfirmationManager,
163
+ chatId: 'test-chat',
164
+ requestCanceled: false,
165
+ contextMenuRenderer: mockContextMenuRenderer,
166
+ openerService: mockOpenerService
167
+ }
168
+ })));
169
+
170
+ expect(container.textContent).to.equal('passed');
171
+ root.unmount();
172
+ });
173
+ });
174
+
112
175
  describe('ToolConfirmationProps', () => {
113
176
  it('should pick toolRequest from ToolConfirmationCallbacks', () => {
114
177
  const props: ToolConfirmationProps = {
115
178
  response: { kind: 'toolCall', id: 'test', name: 'test' } as ToolConfirmationProps['response'],
116
179
  onAllow: () => { },
117
180
  onDeny: () => { },
118
- contextMenuRenderer: mockContextMenuRenderer
181
+ contextMenuRenderer: mockContextMenuRenderer,
182
+ openerService: mockOpenerService
119
183
  };
120
184
  expect(props.toolRequest).to.be.undefined;
121
185
  });
186
+
187
+ it('should accept response arguments with an opener service', () => {
188
+ const props: ToolConfirmationProps = {
189
+ response: { kind: 'toolCall', id: 'test', name: 'test', arguments: '{"foo":"bar"}' } as ToolConfirmationProps['response'],
190
+ onAllow: () => { },
191
+ onDeny: () => { },
192
+ contextMenuRenderer: mockContextMenuRenderer,
193
+ openerService: mockOpenerService
194
+ };
195
+ expect(props.response.arguments).to.equal('{"foo":"bar"}');
196
+ expect(props.openerService).to.equal(mockOpenerService);
197
+ });
122
198
  });
123
199
  });
@@ -16,13 +16,15 @@
16
16
 
17
17
  import * as React from '@theia/core/shared/react';
18
18
  import { nls } from '@theia/core/lib/common/nls';
19
- import { codicon, ContextMenuRenderer } from '@theia/core/lib/browser';
19
+ import { codicon, ContextMenuRenderer, OpenerService } from '@theia/core/lib/browser';
20
20
  import { ToolCallChatResponseContent } from '@theia/ai-chat/lib/common';
21
21
  import { ToolRequest } from '@theia/ai-core';
22
22
  import { CommandMenu, ContextExpressionMatcher, MenuPath } from '@theia/core/lib/common/menu';
23
23
  import { GroupImpl } from '@theia/core/lib/browser/menu/composite-menu-node';
24
24
  import { ToolConfirmationMode as ToolConfirmationPreferenceMode } from '@theia/ai-chat/lib/common/chat-tool-preferences';
25
25
  import { ToolConfirmationManager } from '@theia/ai-chat/lib/browser/chat-tool-preference-bindings';
26
+ import { MarkdownRender } from './markdown-part-renderer';
27
+ import { condenseArguments, formatArgsForTooltip } from './toolcall-utils';
26
28
 
27
29
  export interface CountdownTimerProps {
28
30
  response: ToolCallChatResponseContent;
@@ -430,9 +432,10 @@ export interface ToolConfirmationProps extends Pick<ToolConfirmationCallbacks, '
430
432
  onAllow: (scope?: ConfirmationScope) => void;
431
433
  onDeny: (scope?: ConfirmationScope, reason?: string) => void;
432
434
  contextMenuRenderer: ContextMenuRenderer;
435
+ openerService: OpenerService;
433
436
  }
434
437
 
435
- export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({ response, toolRequest, onAllow, onDeny, contextMenuRenderer }) => {
438
+ export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({ response, toolRequest, onAllow, onDeny, contextMenuRenderer, openerService }) => {
436
439
  const [state, setState] = React.useState<ToolConfirmationState>('waiting');
437
440
 
438
441
  const handleAllow = React.useCallback((scope: ConfirmationScope) => {
@@ -461,16 +464,30 @@ export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({ response, to
461
464
  );
462
465
  }
463
466
 
467
+ const toolNameContent = (
468
+ <>
469
+ <span className="label">{nls.localizeByDefault('Tool')}:</span>
470
+ <span className="value">{response.name}</span>
471
+ </>
472
+ );
473
+
464
474
  return (
465
475
  <div className="theia-tool-confirmation">
466
476
  <div className="theia-tool-confirmation-header">
467
477
  <span className={codicon('shield')}></span> {nls.localize('theia/ai/chat-ui/toolconfirmation/header', 'Confirm Tool Execution')}
468
478
  </div>
469
479
  <div className="theia-tool-confirmation-info">
470
- <div className="theia-tool-confirmation-name">
471
- <span className="label">{nls.localizeByDefault('Tool')}:</span>
472
- <span className="value">{response.name}</span>
473
- </div>
480
+ {toolRequest?.description ? (
481
+ <details className="theia-tool-confirmation-name">
482
+ <summary>{toolNameContent}</summary>
483
+ <div className="theia-tool-confirmation-description">
484
+ {toolRequest.description}
485
+ </div>
486
+ </details>
487
+ ) : (
488
+ <div className="theia-tool-confirmation-name">{toolNameContent}</div>
489
+ )}
490
+ <ToolArgsDisplay args={response.arguments} openerService={openerService} />
474
491
  </div>
475
492
  <ToolConfirmationActions
476
493
  toolName={response.name ?? 'unknown'}
@@ -484,6 +501,33 @@ export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({ response, to
484
501
  );
485
502
  };
486
503
 
504
+ interface ToolArgsDisplayProps {
505
+ args: string | undefined;
506
+ openerService: OpenerService;
507
+ }
508
+
509
+ const ToolArgsDisplay: React.FC<ToolArgsDisplayProps> = ({ args, openerService }) => {
510
+ const trimmedArgs = args?.trim();
511
+ if (!trimmedArgs || trimmedArgs === '{}') {
512
+ // eslint-disable-next-line no-null/no-null
513
+ return null;
514
+ }
515
+ const summaryLabel = condenseArguments(trimmedArgs) ?? '\u2026';
516
+ return (
517
+ <details className="theia-tool-confirmation-args">
518
+ <summary>
519
+ <span className="label">{nls.localizeByDefault('Arguments')}:</span>
520
+ <span className="theia-tool-confirmation-args-summary">{summaryLabel}</span>
521
+ </summary>
522
+ <MarkdownRender
523
+ text={formatArgsForTooltip(trimmedArgs)}
524
+ openerService={openerService}
525
+ className="theia-tool-confirmation-args-content"
526
+ />
527
+ </details>
528
+ );
529
+ };
530
+
487
531
  export interface WithToolCallConfirmationProps {
488
532
  response: ToolCallChatResponseContent;
489
533
  confirmationMode: ToolConfirmationPreferenceMode;
@@ -494,12 +538,17 @@ export interface WithToolCallConfirmationProps {
494
538
  showArgsTooltip?: (response: ToolCallChatResponseContent, target: HTMLElement | undefined) => void;
495
539
  requestCanceled: boolean;
496
540
  contextMenuRenderer: ContextMenuRenderer;
541
+ openerService: OpenerService;
497
542
  }
498
543
 
499
544
  export function withToolCallConfirmation<P extends object>(
500
545
  WrappedComponent: React.ComponentType<P>
501
- ): React.FC<P & WithToolCallConfirmationProps> {
502
- const WithConfirmation: React.FC<P & WithToolCallConfirmationProps> = props => {
546
+ ): React.FC<P & { toolConfirmation: WithToolCallConfirmationProps }> {
547
+ const WithConfirmation: React.FC<P & { toolConfirmation: WithToolCallConfirmationProps }> = props => {
548
+ const {
549
+ toolConfirmation,
550
+ ...componentProps
551
+ } = props;
503
552
  const {
504
553
  response,
505
554
  confirmationMode,
@@ -510,8 +559,8 @@ export function withToolCallConfirmation<P extends object>(
510
559
  showArgsTooltip,
511
560
  requestCanceled,
512
561
  contextMenuRenderer,
513
- ...componentProps
514
- } = props;
562
+ openerService
563
+ } = toolConfirmation;
515
564
 
516
565
  const { confirmationState } = useToolConfirmationState(response, confirmationMode);
517
566
  const pendingRef = React.useRef<HTMLElement | undefined>(undefined);
@@ -572,6 +621,7 @@ export function withToolCallConfirmation<P extends object>(
572
621
  onAllow={handleAllow}
573
622
  onDeny={handleDeny}
574
623
  contextMenuRenderer={contextMenuRenderer}
624
+ openerService={openerService}
575
625
  />
576
626
  );
577
627
  }
@@ -24,7 +24,7 @@ import * as React from '@theia/core/shared/react';
24
24
  import { createConfirmationHandlers, ToolConfirmation, useToolConfirmationState } from './tool-confirmation';
25
25
  import { ToolConfirmationMode } from '@theia/ai-chat/lib/common/chat-tool-preferences';
26
26
  import { ResponseNode } from '../chat-tree-view';
27
- import { useMarkdownRendering } from './markdown-part-renderer';
27
+ import { MarkdownRender } from './markdown-part-renderer';
28
28
  import { ToolCallResult, ToolInvocationRegistry, ToolRequest } from '@theia/ai-core';
29
29
  import { ToolConfirmationManager } from '@theia/ai-chat/lib/browser/chat-tool-preference-bindings';
30
30
  import { condenseArguments, formatArgsForTooltip } from './toolcall-utils';
@@ -67,6 +67,7 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
67
67
  onAllow={handleAllow}
68
68
  onDeny={handleDeny}
69
69
  contextMenuRenderer={this.contextMenuRenderer}
70
+ openerService={this.openerService}
70
71
  />;
71
72
  }
72
73
 
@@ -84,7 +85,8 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
84
85
  showArgsTooltip={this.showArgsTooltip.bind(this)}
85
86
  responseRenderer={this.renderResult.bind(this)}
86
87
  requestCanceled={parentNode.response.isCanceled}
87
- contextMenuRenderer={this.contextMenuRenderer} />;
88
+ contextMenuRenderer={this.contextMenuRenderer}
89
+ openerService={this.openerService} />;
88
90
  }
89
91
 
90
92
  protected renderResult(response: ToolCallChatResponseContent): ReactNode {
@@ -185,6 +187,7 @@ interface ToolCallContentProps {
185
187
  responseRenderer: (response: ToolCallChatResponseContent) => ReactNode | undefined;
186
188
  requestCanceled: boolean;
187
189
  contextMenuRenderer: ContextMenuRenderer;
190
+ openerService: OpenerService;
188
191
  }
189
192
 
190
193
  /**
@@ -200,7 +203,8 @@ const ToolCallContent: React.FC<ToolCallContentProps> = ({
200
203
  getArgumentsLabel,
201
204
  requestCanceled,
202
205
  showArgsTooltip,
203
- contextMenuRenderer
206
+ contextMenuRenderer,
207
+ openerService
204
208
  }) => {
205
209
  const { confirmationState, rejectionReason } = useToolConfirmationState(response, confirmationMode);
206
210
  const summaryRef = React.useRef<HTMLElement | undefined>(undefined);
@@ -290,14 +294,10 @@ const ToolCallContent: React.FC<ToolCallContentProps> = ({
290
294
  onAllow={handleAllow}
291
295
  onDeny={handleDeny}
292
296
  contextMenuRenderer={contextMenuRenderer}
297
+ openerService={openerService}
293
298
  />
294
299
  </span>
295
300
  )}
296
301
  </div>
297
302
  );
298
303
  };
299
-
300
- const MarkdownRender = ({ text, openerService }: { text: string; openerService: OpenerService }) => {
301
- const ref = useMarkdownRendering(text, openerService);
302
- return <div ref={ref}></div>;
303
- };
@@ -50,6 +50,7 @@ div:last-child>.theia-ChatNode {
50
50
 
51
51
  .theia-ChatNodeHeader .theia-ChatContentInProgress {
52
52
  color: var(--theia-disabledForeground);
53
+ white-space: nowrap;
53
54
  }
54
55
 
55
56
  .theia-ChatNodeHeader .theia-ChatContentInProgress-Cancel {
@@ -82,6 +83,9 @@ div:last-child>.theia-ChatNode {
82
83
  .theia-ChatNodeHeader .theia-ChatContentInProgress::after {
83
84
  content: "";
84
85
  animation: dots 1s steps(1, end) infinite;
86
+ display: inline-block;
87
+ width: 3ch;
88
+ text-align: left;
85
89
  }
86
90
 
87
91
  .theia-ChatNode .codicon {
@@ -984,6 +988,7 @@ div:last-child>.theia-ChatNode {
984
988
  border-radius: 4px;
985
989
  background-color: var(--theia-editorWidget-background);
986
990
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.14);
991
+ cursor: default;
987
992
  }
988
993
 
989
994
  .theia-tool-confirmation-header {
@@ -1010,19 +1015,65 @@ div:last-child>.theia-ChatNode {
1010
1015
  margin-right: 6px;
1011
1016
  }
1012
1017
 
1013
- .theia-tool-confirmation-args pre.value {
1014
- margin: 6px 0;
1015
- padding: 8px;
1016
- background-color: var(--theia-editor-background);
1017
- border-radius: 3px;
1018
- max-height: 150px;
1018
+ .theia-tool-confirmation-description {
1019
+ margin: 6px 0 8px 0;
1020
+ color: var(--theia-descriptionForeground);
1021
+ font-weight: normal;
1022
+ max-height: 200px;
1023
+ overflow: auto;
1024
+ }
1025
+
1026
+ details.theia-tool-confirmation-name>summary,
1027
+ details.theia-tool-confirmation-args>summary {
1028
+ cursor: pointer;
1029
+ user-select: none;
1030
+ }
1031
+
1032
+ details.theia-tool-confirmation-name>summary::marker,
1033
+ details.theia-tool-confirmation-args>summary::marker {
1034
+ color: var(--theia-button-background);
1035
+ }
1036
+
1037
+ .theia-tool-confirmation-args-summary {
1038
+ color: var(--theia-descriptionForeground);
1039
+ }
1040
+
1041
+ .theia-tool-confirmation-args-content {
1042
+ margin: var(--theia-ui-padding) 0;
1043
+ padding: var(--theia-ui-padding) calc(var(--theia-ui-padding) * 2);
1044
+ max-height: 200px;
1019
1045
  overflow: auto;
1020
1046
  }
1021
1047
 
1048
+ .theia-tool-confirmation-args-content p {
1049
+ margin: var(--theia-ui-padding) 0;
1050
+ }
1051
+
1052
+ .theia-tool-confirmation-args-content pre {
1053
+ margin: var(--theia-ui-padding) 0;
1054
+ padding: var(--theia-ui-padding) calc(var(--theia-ui-padding) * 2);
1055
+ background-color: var(--theia-textCodeBlock-background);
1056
+ border-radius: 3px;
1057
+ white-space: pre-wrap;
1058
+ word-break: break-word;
1059
+ font-weight: normal;
1060
+ }
1061
+
1062
+ .theia-tool-confirmation-args-content code {
1063
+ word-break: break-word;
1064
+ font-weight: normal;
1065
+ }
1066
+
1067
+ .theia-tool-confirmation-args-content hr {
1068
+ border-top: 1px solid var(--theia-editorHoverWidgetInternalBorder);
1069
+ border-bottom: 0;
1070
+ margin: var(--theia-ui-padding) 0;
1071
+ }
1072
+
1022
1073
  .theia-tool-confirmation-actions {
1023
1074
  display: flex;
1024
1075
  justify-content: flex-end;
1025
- gap: 0;
1076
+ gap: 8px;
1026
1077
  }
1027
1078
 
1028
1079
  .theia-tool-confirmation-split-button {
@@ -1080,69 +1131,24 @@ div:last-child>.theia-ChatNode {
1080
1131
 
1081
1132
  .theia-tool-confirmation-status {
1082
1133
  margin: 10px 0;
1083
- padding: 12px;
1134
+ padding: 8px;
1084
1135
  border: 1px solid var(--theia-dropdown-border);
1085
1136
  border-radius: 4px;
1086
1137
  background-color: var(--theia-editorWidget-background);
1087
1138
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.14);
1088
- }
1089
-
1090
- .theia-tool-confirmation-header {
1091
- font-weight: bold;
1092
- margin-bottom: 8px;
1093
- color: var(--theia-foreground);
1094
- display: flex;
1095
- align-items: center;
1096
- gap: 6px;
1097
- }
1098
-
1099
- .theia-tool-confirmation-info {
1100
- margin-bottom: 12px;
1101
- }
1102
-
1103
- .theia-tool-confirmation-name,
1104
- .theia-tool-confirmation-args {
1105
- margin-bottom: 4px;
1106
- }
1107
-
1108
- .theia-tool-confirmation-name .label,
1109
- .theia-tool-confirmation-args .label {
1110
- font-weight: bold;
1111
- margin-right: 6px;
1112
- }
1113
-
1114
- .theia-tool-confirmation-args pre.value {
1115
- margin: 6px 0;
1116
- padding: 8px;
1117
- background-color: var(--theia-editor-background);
1118
- border-radius: 3px;
1119
- max-height: 150px;
1120
- overflow: auto;
1121
- }
1122
-
1123
- .theia-tool-confirmation-actions {
1124
- display: flex;
1125
- justify-content: flex-end;
1126
- gap: 8px;
1127
- }
1128
-
1129
- .theia-tool-confirmation-status {
1130
- padding: 8px;
1131
- margin: 10px 0;
1132
- border-radius: 4px;
1133
1139
  display: flex;
1134
1140
  align-items: center;
1135
1141
  gap: 6px;
1136
1142
  }
1137
1143
 
1138
- .theia-tool-confirmation-status.approved {
1139
- background-color: var(--theia-successBackground);
1140
- color: var(--theia-successForeground);
1144
+ .theia-tool-confirmation-status.allowed {
1145
+ border-color: var(--theia-successBackground);
1146
+ color: var(--theia-successBackground);
1141
1147
  }
1142
1148
 
1143
1149
  .theia-tool-confirmation-status.denied {
1144
- background-color: var(--theia-errorBackground);
1145
- color: var(--theia-errorForeground);
1150
+ border-color: var(--theia-errorBackground);
1151
+ color: var(--theia-errorBackground);
1146
1152
  }
1147
1153
 
1148
1154
  .theia-toolCall-denied,
@@ -1593,38 +1599,42 @@ div:last-child>.theia-ChatNode {
1593
1599
  }
1594
1600
 
1595
1601
  /* Scrollable hover for large tool call arguments */
1596
- .theia-hover.toolcall-args-hover div.rendered-markdown {
1602
+ .theia-hover.toolcall-args-hover>div {
1597
1603
  max-height: 400px;
1598
1604
  max-width: 600px;
1599
1605
  overflow-y: auto;
1600
1606
  overflow-x: hidden;
1601
1607
  }
1602
1608
 
1603
- /* Code blocks inside tool call hovers: constrain and scroll.
1604
- Monaco renders fenced code blocks as div[data-code] > span > div.monaco-tokenized-source,
1605
- not as <pre> elements, so we target both structures. */
1606
- .theia-hover.toolcall-args-hover div.rendered-markdown pre,
1607
- .theia-hover.toolcall-args-hover div.rendered-markdown div[data-code] {
1609
+ /* Code blocks inside tool call hovers: constrain and scroll. */
1610
+ .theia-hover.toolcall-args-hover pre {
1608
1611
  max-height: 200px;
1609
1612
  overflow: auto;
1610
- white-space: pre;
1613
+ white-space: pre-wrap;
1614
+ word-break: break-word;
1611
1615
  margin: var(--theia-ui-padding) 0;
1612
1616
  padding: var(--theia-ui-padding) calc(var(--theia-ui-padding) * 2);
1613
1617
  border-radius: 3px;
1614
1618
  background-color: var(--theia-textCodeBlock-background);
1615
1619
  }
1616
1620
 
1621
+ /* Reset code background inside pre to avoid double semi-transparent layers
1622
+ (.theia-hover code in hover-service.css sets the same background) */
1623
+ .theia-hover.toolcall-args-hover pre code {
1624
+ background-color: transparent;
1625
+ }
1626
+
1617
1627
  /* Inline code styling */
1618
- .theia-hover.toolcall-args-hover div.rendered-markdown code:not(pre code) {
1628
+ .theia-hover.toolcall-args-hover code:not(pre code) {
1619
1629
  padding: 1px var(--theia-ui-padding);
1620
1630
  border-radius: 3px;
1621
1631
  background-color: var(--theia-textCodeBlock-background);
1622
1632
  word-break: break-all;
1623
- font-family: var(--theia-monospace-font-family);
1633
+ font-family: var(--theia-code-font-family);
1624
1634
  }
1625
1635
 
1626
1636
  /* Spacing between arg sections - uses internal border like hover-service.css */
1627
- .theia-hover.toolcall-args-hover div.rendered-markdown hr {
1637
+ .theia-hover.toolcall-args-hover hr {
1628
1638
  border-top: 1px solid var(--theia-editorHoverWidgetInternalBorder);
1629
1639
  border-bottom: 0;
1630
1640
  margin: var(--theia-ui-padding) 0;