@theia/ai-chat-ui 1.76.0-next.7 → 1.76.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.
Files changed (134) hide show
  1. package/lib/browser/ai-chat-ui-frontend-module.d.ts.map +1 -1
  2. package/lib/browser/ai-chat-ui-frontend-module.js +6 -0
  3. package/lib/browser/ai-chat-ui-frontend-module.js.map +1 -1
  4. package/lib/browser/chat-find/chat-find-contribution.d.ts +18 -0
  5. package/lib/browser/chat-find/chat-find-contribution.d.ts.map +1 -0
  6. package/lib/browser/chat-find/chat-find-contribution.js +76 -0
  7. package/lib/browser/chat-find/chat-find-contribution.js.map +1 -0
  8. package/lib/browser/chat-find/chat-find-highlighter.d.ts +114 -0
  9. package/lib/browser/chat-find/chat-find-highlighter.d.ts.map +1 -0
  10. package/lib/browser/chat-find/chat-find-highlighter.js +353 -0
  11. package/lib/browser/chat-find/chat-find-highlighter.js.map +1 -0
  12. package/lib/browser/chat-find/chat-find-highlighter.spec.d.ts +2 -0
  13. package/lib/browser/chat-find/chat-find-highlighter.spec.d.ts.map +1 -0
  14. package/lib/browser/chat-find/chat-find-highlighter.spec.js +283 -0
  15. package/lib/browser/chat-find/chat-find-highlighter.spec.js.map +1 -0
  16. package/lib/browser/chat-find/chat-find-matcher.d.ts +72 -0
  17. package/lib/browser/chat-find/chat-find-matcher.d.ts.map +1 -0
  18. package/lib/browser/chat-find/chat-find-matcher.js +168 -0
  19. package/lib/browser/chat-find/chat-find-matcher.js.map +1 -0
  20. package/lib/browser/chat-find/chat-find-matcher.spec.d.ts +2 -0
  21. package/lib/browser/chat-find/chat-find-matcher.spec.d.ts.map +1 -0
  22. package/lib/browser/chat-find/chat-find-matcher.spec.js +145 -0
  23. package/lib/browser/chat-find/chat-find-matcher.spec.js.map +1 -0
  24. package/lib/browser/chat-find/chat-find-text.d.ts +20 -0
  25. package/lib/browser/chat-find/chat-find-text.d.ts.map +1 -0
  26. package/lib/browser/chat-find/chat-find-text.js +56 -0
  27. package/lib/browser/chat-find/chat-find-text.js.map +1 -0
  28. package/lib/browser/chat-find/chat-find-widget.d.ts +96 -0
  29. package/lib/browser/chat-find/chat-find-widget.d.ts.map +1 -0
  30. package/lib/browser/chat-find/chat-find-widget.js +332 -0
  31. package/lib/browser/chat-find/chat-find-widget.js.map +1 -0
  32. package/lib/browser/chat-find/chat-find-widget.spec.d.ts +2 -0
  33. package/lib/browser/chat-find/chat-find-widget.spec.d.ts.map +1 -0
  34. package/lib/browser/chat-find/chat-find-widget.spec.js +236 -0
  35. package/lib/browser/chat-find/chat-find-widget.spec.js.map +1 -0
  36. package/lib/browser/chat-input-scroll-util.d.ts +7 -0
  37. package/lib/browser/chat-input-scroll-util.d.ts.map +1 -0
  38. package/lib/browser/chat-input-scroll-util.js +33 -0
  39. package/lib/browser/chat-input-scroll-util.js.map +1 -0
  40. package/lib/browser/chat-input-scroll-util.spec.d.ts +2 -0
  41. package/lib/browser/chat-input-scroll-util.spec.d.ts.map +1 -0
  42. package/lib/browser/chat-input-scroll-util.spec.js +38 -0
  43. package/lib/browser/chat-input-scroll-util.spec.js.map +1 -0
  44. package/lib/browser/chat-input-widget.d.ts +21 -3
  45. package/lib/browser/chat-input-widget.d.ts.map +1 -1
  46. package/lib/browser/chat-input-widget.js +109 -50
  47. package/lib/browser/chat-input-widget.js.map +1 -1
  48. package/lib/browser/chat-input-widget.spec.js +21 -0
  49. package/lib/browser/chat-input-widget.spec.js.map +1 -1
  50. package/lib/browser/chat-response-renderer/code-part-renderer.d.ts.map +1 -1
  51. package/lib/browser/chat-response-renderer/code-part-renderer.js +9 -1
  52. package/lib/browser/chat-response-renderer/code-part-renderer.js.map +1 -1
  53. package/lib/browser/chat-response-renderer/code-wrapper-editors.d.ts +13 -0
  54. package/lib/browser/chat-response-renderer/code-wrapper-editors.d.ts.map +1 -0
  55. package/lib/browser/chat-response-renderer/code-wrapper-editors.js +41 -0
  56. package/lib/browser/chat-response-renderer/code-wrapper-editors.js.map +1 -0
  57. package/lib/browser/chat-response-renderer/delegation-tool-renderer.js +74 -43
  58. package/lib/browser/chat-response-renderer/delegation-tool-renderer.js.map +1 -1
  59. package/lib/browser/chat-response-renderer/index.d.ts +1 -0
  60. package/lib/browser/chat-response-renderer/index.d.ts.map +1 -1
  61. package/lib/browser/chat-response-renderer/index.js +1 -0
  62. package/lib/browser/chat-response-renderer/index.js.map +1 -1
  63. package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts +9 -0
  64. package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts.map +1 -1
  65. package/lib/browser/chat-response-renderer/markdown-part-renderer.js +34 -16
  66. package/lib/browser/chat-response-renderer/markdown-part-renderer.js.map +1 -1
  67. package/lib/browser/chat-response-renderer/server-toolcall-part-renderer.d.ts.map +1 -1
  68. package/lib/browser/chat-response-renderer/server-toolcall-part-renderer.js +2 -2
  69. package/lib/browser/chat-response-renderer/server-toolcall-part-renderer.js.map +1 -1
  70. package/lib/browser/chat-response-renderer/spinner.d.ts +3 -0
  71. package/lib/browser/chat-response-renderer/spinner.d.ts.map +1 -0
  72. package/lib/browser/chat-response-renderer/spinner.js +24 -0
  73. package/lib/browser/chat-response-renderer/spinner.js.map +1 -0
  74. package/lib/browser/chat-response-renderer/thinking-part-renderer.d.ts +9 -1
  75. package/lib/browser/chat-response-renderer/thinking-part-renderer.d.ts.map +1 -1
  76. package/lib/browser/chat-response-renderer/thinking-part-renderer.js +93 -2
  77. package/lib/browser/chat-response-renderer/thinking-part-renderer.js.map +1 -1
  78. package/lib/browser/chat-response-renderer/thinking-part-renderer.spec.d.ts +2 -0
  79. package/lib/browser/chat-response-renderer/thinking-part-renderer.spec.d.ts.map +1 -0
  80. package/lib/browser/chat-response-renderer/thinking-part-renderer.spec.js +84 -0
  81. package/lib/browser/chat-response-renderer/thinking-part-renderer.spec.js.map +1 -0
  82. package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts.map +1 -1
  83. package/lib/browser/chat-response-renderer/toolcall-part-renderer.js +2 -2
  84. package/lib/browser/chat-response-renderer/toolcall-part-renderer.js.map +1 -1
  85. package/lib/browser/chat-response-renderer/toolcall-part-renderer.spec.js +16 -0
  86. package/lib/browser/chat-response-renderer/toolcall-part-renderer.spec.js.map +1 -1
  87. package/lib/browser/chat-response-renderer/toolcall-utils.d.ts.map +1 -1
  88. package/lib/browser/chat-response-renderer/toolcall-utils.js +7 -2
  89. package/lib/browser/chat-response-renderer/toolcall-utils.js.map +1 -1
  90. package/lib/browser/chat-tree-view/chat-view-tree-container.d.ts.map +1 -1
  91. package/lib/browser/chat-tree-view/chat-view-tree-container.js +4 -0
  92. package/lib/browser/chat-tree-view/chat-view-tree-container.js.map +1 -1
  93. package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts +20 -1
  94. package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts.map +1 -1
  95. package/lib/browser/chat-tree-view/chat-view-tree-widget.js +88 -7
  96. package/lib/browser/chat-tree-view/chat-view-tree-widget.js.map +1 -1
  97. package/lib/common/toolcall-utils.d.ts +11 -1
  98. package/lib/common/toolcall-utils.d.ts.map +1 -1
  99. package/lib/common/toolcall-utils.js +191 -9
  100. package/lib/common/toolcall-utils.js.map +1 -1
  101. package/lib/common/toolcall-utils.spec.js +92 -4
  102. package/lib/common/toolcall-utils.spec.js.map +1 -1
  103. package/package.json +13 -13
  104. package/src/browser/ai-chat-ui-frontend-module.ts +7 -0
  105. package/src/browser/chat-find/chat-find-contribution.ts +74 -0
  106. package/src/browser/chat-find/chat-find-highlighter.spec.ts +313 -0
  107. package/src/browser/chat-find/chat-find-highlighter.ts +385 -0
  108. package/src/browser/chat-find/chat-find-matcher.spec.ts +173 -0
  109. package/src/browser/chat-find/chat-find-matcher.ts +197 -0
  110. package/src/browser/chat-find/chat-find-text.ts +65 -0
  111. package/src/browser/chat-find/chat-find-widget.spec.tsx +261 -0
  112. package/src/browser/chat-find/chat-find-widget.tsx +418 -0
  113. package/src/browser/chat-input-scroll-util.spec.ts +42 -0
  114. package/src/browser/chat-input-scroll-util.ts +30 -0
  115. package/src/browser/chat-input-widget.spec.ts +29 -0
  116. package/src/browser/chat-input-widget.tsx +118 -56
  117. package/src/browser/chat-response-renderer/code-part-renderer.tsx +9 -1
  118. package/src/browser/chat-response-renderer/code-wrapper-editors.ts +41 -0
  119. package/src/browser/chat-response-renderer/delegation-tool-renderer.tsx +88 -62
  120. package/src/browser/chat-response-renderer/index.ts +1 -0
  121. package/src/browser/chat-response-renderer/markdown-part-renderer.tsx +34 -17
  122. package/src/browser/chat-response-renderer/server-toolcall-part-renderer.tsx +2 -5
  123. package/src/browser/chat-response-renderer/spinner.tsx +22 -0
  124. package/src/browser/chat-response-renderer/thinking-part-renderer.spec.tsx +104 -0
  125. package/src/browser/chat-response-renderer/thinking-part-renderer.tsx +129 -9
  126. package/src/browser/chat-response-renderer/toolcall-part-renderer.spec.ts +16 -0
  127. package/src/browser/chat-response-renderer/toolcall-part-renderer.tsx +1 -4
  128. package/src/browser/chat-response-renderer/toolcall-utils.ts +5 -0
  129. package/src/browser/chat-tree-view/chat-view-tree-container.ts +4 -0
  130. package/src/browser/chat-tree-view/chat-view-tree-widget.tsx +99 -6
  131. package/src/browser/style/chat-find.css +107 -0
  132. package/src/browser/style/index.css +75 -8
  133. package/src/common/toolcall-utils.spec.ts +113 -4
  134. package/src/common/toolcall-utils.ts +207 -9
@@ -87,15 +87,103 @@ describe('extractJsonStringField', () => {
87
87
  const result = (0, toolcall_utils_1.extractJsonStringField)('{"pat', 'path');
88
88
  (0, chai_1.expect)(result).to.be.undefined;
89
89
  });
90
- it('should handle escaped quotes - regex stops at first unescaped quote', () => {
90
+ it('should handle escaped quotes in complete JSON', () => {
91
91
  // With complete JSON, JSON.parse handles escapes correctly
92
92
  const result = (0, toolcall_utils_1.extractJsonStringField)('{"cmd": "echo \\"hello\\""}', 'cmd');
93
93
  (0, chai_1.expect)(result).to.equal('echo "hello"');
94
94
  });
95
- it('should handle escaped quotes in partial JSON - regex captures up to first quote', () => {
96
- // With incomplete JSON, regex stops at the first unescaped quote character
95
+ it('should unescape escaped quotes in partial JSON', () => {
97
96
  const result = (0, toolcall_utils_1.extractJsonStringField)('{"cmd": "echo \\"hello', 'cmd');
98
- (0, chai_1.expect)(result).to.equal('echo \\');
97
+ (0, chai_1.expect)(result).to.equal('echo "hello');
98
+ });
99
+ });
100
+ describe('parse/scan boundary', () => {
101
+ it('should unescape via JSON.parse when complete JSON has trailing whitespace', () => {
102
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"cmd": "echo \\"hello\\""}\n', 'cmd');
103
+ (0, chai_1.expect)(result).to.equal('echo "hello"');
104
+ });
105
+ it('should extract by scanning when JSON ends with a brace but is still incomplete', () => {
106
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"path": "src/index.ts", "meta": {"x": "y"}', 'path');
107
+ (0, chai_1.expect)(result).to.equal('src/index.ts');
108
+ });
109
+ it('should return undefined for a complete array without the field', () => {
110
+ const result = (0, toolcall_utils_1.extractJsonStringField)('[{"path": "src/index.ts"}]', 'other');
111
+ (0, chai_1.expect)(result).to.be.undefined;
112
+ });
113
+ it('should return undefined for an incomplete array, which has no top-level field', () => {
114
+ const result = (0, toolcall_utils_1.extractJsonStringField)('[{"path": "src/index.ts"', 'path');
115
+ (0, chai_1.expect)(result).to.be.undefined;
116
+ });
117
+ });
118
+ describe('top-level fields only (streaming)', () => {
119
+ it('should ignore a matching key inside a nested object', () => {
120
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"meta": {"path": "inner.ts"}, "path": "outer.ts', 'path');
121
+ (0, chai_1.expect)(result).to.equal('outer.ts');
122
+ });
123
+ it('should ignore a matching key inside a nested array of objects', () => {
124
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"edits": [{"path": "inner.ts"}], "path": "outer.ts', 'path');
125
+ (0, chai_1.expect)(result).to.equal('outer.ts');
126
+ });
127
+ it('should ignore a matching key inside a string value', () => {
128
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"content": "{\\"path\\": \\"inner.ts\\"}", "path": "outer.ts', 'path');
129
+ (0, chai_1.expect)(result).to.equal('outer.ts');
130
+ });
131
+ it('should return undefined when the field only occurs nested', () => {
132
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"meta": {"path": "inner.ts"}, "content": "abc', 'path');
133
+ (0, chai_1.expect)(result).to.be.undefined;
134
+ });
135
+ it('should return undefined while a nested object is still streaming', () => {
136
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"meta": {"path": "inner.ts', 'path');
137
+ (0, chai_1.expect)(result).to.be.undefined;
138
+ });
139
+ it('should return undefined when the top-level field is not a string', () => {
140
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"path": 42, "content": "abc', 'path');
141
+ (0, chai_1.expect)(result).to.be.undefined;
142
+ });
143
+ it('should extract a field that follows a non-string top-level value', () => {
144
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"line": 42, "path": "src/index.ts', 'path');
145
+ (0, chai_1.expect)(result).to.equal('src/index.ts');
146
+ });
147
+ it('should extract a field that follows a nested object', () => {
148
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"meta": {"a": {"b": "c"}}, "path": "src/index.ts', 'path');
149
+ (0, chai_1.expect)(result).to.equal('src/index.ts');
150
+ });
151
+ it('should not match a key whose name merely ends with the field name', () => {
152
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"filepath": "wrong.ts", "path": "right.ts', 'path');
153
+ (0, chai_1.expect)(result).to.equal('right.ts');
154
+ });
155
+ });
156
+ describe('escapes in streaming values', () => {
157
+ it('should not end the value at an escaped quote', () => {
158
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"path": "a\\"b", "other": "x', 'path');
159
+ (0, chai_1.expect)(result).to.equal('a"b');
160
+ });
161
+ it('should unescape backslashes in a streaming Windows path', () => {
162
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"path": "C:\\\\Users\\\\me', 'path');
163
+ (0, chai_1.expect)(result).to.equal('C:\\Users\\me');
164
+ });
165
+ it('should drop a trailing incomplete escape', () => {
166
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"path": "a\\', 'path');
167
+ (0, chai_1.expect)(result).to.equal('a');
168
+ });
169
+ it('should drop a trailing incomplete unicode escape', () => {
170
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"path": "a\\u00', 'path');
171
+ (0, chai_1.expect)(result).to.equal('a');
172
+ });
173
+ it('should keep a completed escaped backslash at the end of a streaming value', () => {
174
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"path": "a\\\\', 'path');
175
+ (0, chai_1.expect)(result).to.equal('a\\');
176
+ });
177
+ it('should keep literal text that looks like an escape', () => {
178
+ const result = (0, toolcall_utils_1.extractJsonStringField)('{"path": "a\\\\u0041', 'path');
179
+ (0, chai_1.expect)(result).to.equal('a\\u0041');
180
+ });
181
+ });
182
+ describe('large streaming payloads', () => {
183
+ it('should extract a field that follows a very large content value', () => {
184
+ const content = 'x'.repeat(1_000_000);
185
+ const result = (0, toolcall_utils_1.extractJsonStringField)(`{"content": "${content}", "path": "src/index.ts`, 'path');
186
+ (0, chai_1.expect)(result).to.equal('src/index.ts');
99
187
  });
100
188
  });
101
189
  });
@@ -1 +1 @@
1
- {"version":3,"file":"toolcall-utils.spec.js","sourceRoot":"","sources":["../../src/common/toolcall-utils.spec.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;AAEhF,+BAA8B;AAC9B,qDAA0D;AAE1D,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACpC,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACrD,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,8CAA8C,EAAE,MAAM,CAAC,CAAC;YAC9F,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YACxD,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,+CAA+C,EAAE,QAAQ,CAAC,CAAC;YACjG,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;YACvE,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;YAClE,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAChE,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,eAAe,EAAE,OAAO,CAAC,CAAC;YAChE,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;QACnC,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACzC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC5D,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,yBAAyB,EAAE,MAAM,CAAC,CAAC;YACzE,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;YAC1E,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;YACxE,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YAC/C,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,qBAAqB,EAAE,SAAS,CAAC,CAAC;YACxE,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;YAClF,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAC5D,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;YACvE,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;YAC3E,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,0CAA0C,EAAE,MAAM,CAAC,CAAC;YAC1F,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YACzD,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,2CAA2C,EAAE,QAAQ,CAAC,CAAC;YAC7F,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACnD,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACzD,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAClD,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;YACzE,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;YAClE,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;YAC9D,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YAC/D,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACpD,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACvD,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;YAC3E,2DAA2D;YAC3D,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;YAC5E,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;YACvF,2EAA2E;YAC3E,MAAM,MAAM,GAAG,IAAA,uCAAsB,EAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;YACvE,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"toolcall-utils.spec.js","sourceRoot":"","sources":["../../src/common/toolcall-utils.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@theia/ai-chat-ui",
3
- "version": "1.76.0-next.7+7c303b183",
3
+ "version": "1.76.0",
4
4
  "description": "Theia - AI Chat UI Extension",
5
5
  "dependencies": {
6
- "@theia/ai-chat": "1.76.0-next.7+7c303b183",
7
- "@theia/ai-core": "1.76.0-next.7+7c303b183",
8
- "@theia/core": "1.76.0-next.7+7c303b183",
9
- "@theia/editor": "1.76.0-next.7+7c303b183",
10
- "@theia/editor-preview": "1.76.0-next.7+7c303b183",
11
- "@theia/filesystem": "1.76.0-next.7+7c303b183",
12
- "@theia/monaco": "1.76.0-next.7+7c303b183",
6
+ "@theia/ai-chat": "1.76.0",
7
+ "@theia/ai-core": "1.76.0",
8
+ "@theia/core": "1.76.0",
9
+ "@theia/editor": "1.76.0",
10
+ "@theia/editor-preview": "1.76.0",
11
+ "@theia/filesystem": "1.76.0",
12
+ "@theia/monaco": "1.76.0",
13
13
  "@theia/monaco-editor-core": "1.108.201",
14
- "@theia/preferences": "1.76.0-next.7+7c303b183",
15
- "@theia/workspace": "1.76.0-next.7+7c303b183",
14
+ "@theia/preferences": "1.76.0",
15
+ "@theia/workspace": "1.76.0",
16
16
  "date-fns": "^4.4.0",
17
- "mermaid": "^11.16.1",
17
+ "mermaid": "^11.17.2",
18
18
  "tslib": "^2.8.1"
19
19
  },
20
20
  "publishConfig": {
@@ -51,10 +51,10 @@
51
51
  "watch": "theiaext watch"
52
52
  },
53
53
  "devDependencies": {
54
- "@theia/ext-scripts": "1.75.0"
54
+ "@theia/ext-scripts": "1.76.0"
55
55
  },
56
56
  "nyc": {
57
57
  "extends": "../../configs/nyc.json"
58
58
  },
59
- "gitHead": "7c303b1836421183d7013f0bd679a9a30ff0b5dc"
59
+ "gitHead": "b9b54c500b46515512e2b44371bc0c7122947bed"
60
60
  }
@@ -54,6 +54,8 @@ import {
54
54
  } from './chat-response-renderer/ai-selection-resolver';
55
55
  import { QuestionPartRenderer } from './chat-response-renderer/question-part-renderer';
56
56
  import { ExternalResourceAllowlistContribution } from './chat-response-renderer/external-resource-allowlist-contribution';
57
+ import { ChatFindContribution } from './chat-find/chat-find-contribution';
58
+ import { ChatFindMatcher } from './chat-find/chat-find-matcher';
57
59
  import { createChatViewTreeWidget, ChatWelcomeMessageProvider } from './chat-tree-view';
58
60
  import { ChatViewTreeWidget } from './chat-tree-view/chat-view-tree-widget';
59
61
  import { ChatViewMenuContribution } from './chat-view-contribution';
@@ -106,6 +108,10 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
106
108
  bind(CommandContribution).toService(ChatFocusContribution);
107
109
  bind(KeybindingContribution).toService(ChatFocusContribution);
108
110
 
111
+ bind(ChatFindContribution).toSelf().inSingletonScope();
112
+ bind(CommandContribution).toService(ChatFindContribution);
113
+ bind(KeybindingContribution).toService(ChatFindContribution);
114
+
109
115
  bind(ChatCapabilitiesServiceImpl).toSelf().inSingletonScope();
110
116
  bind(ChatCapabilitiesService).toService(ChatCapabilitiesServiceImpl);
111
117
 
@@ -144,6 +150,7 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
144
150
  createWidget: () => container.get(AIChatInputWidget)
145
151
  })).inSingletonScope();
146
152
 
153
+ bind(ChatFindMatcher).toSelf().inSingletonScope();
147
154
  bind(ChatViewTreeWidget).toDynamicValue(ctx =>
148
155
  createChatViewTreeWidget(ctx.container)
149
156
  );
@@ -0,0 +1,74 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Safi Seid-Ahmad, K2view and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { Command, CommandContribution, CommandRegistry } from '@theia/core';
18
+ import { ApplicationShell, KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser';
19
+ import { inject, injectable } from '@theia/core/shared/inversify';
20
+ import { ChatCommands } from '../chat-view-commands';
21
+ import { ChatViewWidget } from '../chat-view-widget';
22
+
23
+ export const CHAT_FIND_COMMAND = Command.toLocalizedCommand({
24
+ id: 'ai-chat.find',
25
+ category: ChatCommands.CHAT_CATEGORY,
26
+ label: 'Find in Chat'
27
+ }, 'theia/ai/chat-ui/find', ChatCommands.CHAT_CATEGORY_KEY);
28
+
29
+ export const CHAT_FIND_HIDE_COMMAND = Command.toLocalizedCommand({
30
+ id: 'ai-chat.find.hide',
31
+ category: ChatCommands.CHAT_CATEGORY,
32
+ label: 'Hide Find in Chat'
33
+ }, 'theia/ai/chat-ui/findHide', ChatCommands.CHAT_CATEGORY_KEY);
34
+
35
+ /**
36
+ * Ctrl+F in the chat response tree opens the find bar over it; in the chat input, Ctrl+F keeps its default
37
+ * behavior. The binding is only enabled while the session has content to search. It takes precedence over core's
38
+ * `CommonCommands.FIND` and Monaco's `actions.find` because it is registered later within the default scope,
39
+ * the same mechanism `ChatInputPasteContribution` relies on for Ctrl+V.
40
+ */
41
+ @injectable()
42
+ export class ChatFindContribution implements CommandContribution, KeybindingContribution {
43
+
44
+ @inject(ApplicationShell)
45
+ protected readonly shell: ApplicationShell;
46
+
47
+ registerCommands(commands: CommandRegistry): void {
48
+ commands.registerCommand(CHAT_FIND_COMMAND, {
49
+ isEnabled: () => this.findActiveChatViewWidget()?.treeWidget.canFind === true,
50
+ execute: () => this.findActiveChatViewWidget()?.treeWidget.showFind()
51
+ });
52
+ commands.registerCommand(CHAT_FIND_HIDE_COMMAND, {
53
+ isEnabled: () => this.findActiveChatViewWidget()?.treeWidget.isFindVisible === true,
54
+ execute: () => this.findActiveChatViewWidget()?.treeWidget.hideFind()
55
+ });
56
+ }
57
+
58
+ registerKeybindings(keybindings: KeybindingRegistry): void {
59
+ keybindings.registerKeybinding({
60
+ command: CHAT_FIND_COMMAND.id,
61
+ keybinding: 'ctrlcmd+f',
62
+ when: 'chatResponseFocus'
63
+ });
64
+ keybindings.registerKeybinding({
65
+ command: CHAT_FIND_HIDE_COMMAND.id,
66
+ keybinding: 'esc',
67
+ when: 'chatFindVisible && chatResponseFocus'
68
+ });
69
+ }
70
+
71
+ protected findActiveChatViewWidget(): ChatViewWidget | undefined {
72
+ return ChatViewWidget.findActive(this.shell);
73
+ }
74
+ }
@@ -0,0 +1,313 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Safi Seid-Ahmad, K2view and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
18
+ let disableJSDOM = enableJSDOM();
19
+ import { expect } from 'chai';
20
+ import { Container, injectable } from '@theia/core/shared/inversify';
21
+ import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
22
+ import { CodeWrapperEditors } from '../chat-response-renderer/code-wrapper-editors';
23
+ import { ChatFindHighlighter, ChatFindHighlightRegistry } from './chat-find-highlighter';
24
+ import { ChatFindMatch, ChatFindMatcher } from './chat-find-matcher';
25
+ disableJSDOM();
26
+
27
+ class FakeRegistry implements ChatFindHighlightRegistry {
28
+ readonly highlights = new Map<string, Range[]>();
29
+ set(name: string, ranges: Range[]): void {
30
+ this.highlights.set(name, ranges);
31
+ }
32
+ delete(name: string): void {
33
+ this.highlights.delete(name);
34
+ }
35
+ texts(name: string): string[] {
36
+ return (this.highlights.get(name) ?? []).map(range => range.toString());
37
+ }
38
+ }
39
+
40
+ @injectable()
41
+ class TestHighlighter extends ChatFindHighlighter {
42
+ static registry: ChatFindHighlightRegistry | undefined;
43
+ protected override createRegistry(): ChatFindHighlightRegistry | undefined {
44
+ return TestHighlighter.registry;
45
+ }
46
+ }
47
+
48
+ function createHighlighter(registry: ChatFindHighlightRegistry | undefined): ChatFindHighlighter {
49
+ TestHighlighter.registry = registry;
50
+ const container = new Container();
51
+ container.bind(ChatFindMatcher).toSelf().inSingletonScope();
52
+ container.bind(TestHighlighter).toSelf();
53
+ return container.get(TestHighlighter);
54
+ }
55
+
56
+ interface FakeDecoration { start: number, end: number, className: string }
57
+
58
+ /** A stand-in for the editor of a code part: offsets are used as columns of a single line. */
59
+ function fakeEditor(): { editor: SimpleMonacoEditor, decorations: () => FakeDecoration[], setCount: () => number } {
60
+ let decorations: FakeDecoration[] = [];
61
+ let sets = 0;
62
+ const collection = {
63
+ set: (items: { range: { startColumn: number, endColumn: number }, options: { className: string } }[]) => {
64
+ sets++;
65
+ decorations = items.map(item => ({ start: item.range.startColumn - 1, end: item.range.endColumn - 1, className: item.options.className }));
66
+ },
67
+ clear: () => { decorations = []; }
68
+ };
69
+ const editor = {
70
+ document: { textEditorModel: { getPositionAt: (offset: number) => ({ lineNumber: 1, column: offset + 1 }), getVersionId: () => 1 } },
71
+ getControl: () => ({ createDecorationsCollection: () => collection })
72
+ } as unknown as SimpleMonacoEditor;
73
+ return { editor, decorations: () => decorations, setCount: () => sets };
74
+ }
75
+
76
+ /** Gives an element the layout jsdom does not compute. */
77
+ function layout(element: Element, rect: { top: number, height: number }): void {
78
+ element.getBoundingClientRect = () => ({
79
+ top: rect.top, bottom: rect.top + rect.height, height: rect.height,
80
+ left: 0, right: 0, width: 0, x: 0, y: rect.top, toJSON: () => ({})
81
+ }) as DOMRect;
82
+ }
83
+
84
+ function scrollable(element: HTMLElement, scrollHeight: number, clientHeight: number): void {
85
+ element.style.overflowY = 'auto';
86
+ Object.defineProperty(element, 'scrollHeight', { value: scrollHeight, configurable: true });
87
+ Object.defineProperty(element, 'clientHeight', { value: clientHeight, configurable: true });
88
+ }
89
+
90
+ describe('ChatFindHighlighter', () => {
91
+ let root: HTMLElement;
92
+ let registry: FakeRegistry;
93
+ let highlighter: ChatFindHighlighter;
94
+
95
+ const requestMatch = (occurrence: number): ChatFindMatch => ({ nodeId: 'r1', kind: ChatFindMatch.REQUEST_KIND, occurrence, start: 0, end: 7 });
96
+ // Offsets of 'find me and find me', the text of content 1.
97
+ const contentMatch = (contentIndex: number, occurrence: number, kind = 'markdownContent'): ChatFindMatch =>
98
+ ({ nodeId: 'r1-response', kind, contentIndex, occurrence, start: occurrence * 12, end: occurrence * 12 + 7 });
99
+
100
+ before(() => disableJSDOM = enableJSDOM());
101
+ after(() => disableJSDOM());
102
+
103
+ beforeEach(() => {
104
+ root = document.createElement('div');
105
+ document.body.appendChild(root);
106
+ root.innerHTML = `
107
+ <div data-node-id="r1"><div class="theia-RequestNode"><p><span>find </span><span>me</span></p></div></div>
108
+ <div data-node-id="r1-response"><div class="theia-ResponseNode">
109
+ <div class="theia-ResponseNode-Content"><p>a tool call that would find me too</p></div>
110
+ <div class="theia-ResponseNode-Content"><p>find <strong>me</strong> and find me</p></div>
111
+ </div></div>`;
112
+ registry = new FakeRegistry();
113
+ highlighter = createHighlighter(registry);
114
+ });
115
+
116
+ afterEach(() => {
117
+ highlighter.dispose();
118
+ document.body.removeChild(root);
119
+ });
120
+
121
+ it('highlights only the content the matcher searched, never unsearched parts', () => {
122
+ // The matcher skips tool calls, reasoning and delegated sub-chats, so content 0 has no matches.
123
+ highlighter.update(root, /find me/gi, [contentMatch(1, 0), contentMatch(1, 1)], undefined);
124
+ const ranges = registry.highlights.get(ChatFindHighlighter.MATCH_HIGHLIGHT)!;
125
+ const contents = root.querySelectorAll('.theia-ResponseNode-Content');
126
+ expect(ranges).to.have.length(2);
127
+ expect(ranges.every(r => contents[1].contains(r.startContainer))).to.be.true;
128
+ expect(ranges.some(r => contents[0].contains(r.startContainer))).to.be.false;
129
+ });
130
+
131
+ it('resolves content parts among the row\'s own nodes, not those of a delegated sub-chat inside a part', () => {
132
+ // A delegation renders the sub-chat's response node inline (collapsed) inside the outer content element.
133
+ root.querySelectorAll('.theia-ResponseNode-Content')[0].innerHTML = '<details><div class="theia-ResponseNode">' +
134
+ '<div class="theia-ResponseNode-Content"><p>find me in the sub-chat</p></div></div></details>';
135
+ const match = contentMatch(1, 0);
136
+ highlighter.update(root, /find me/gi, [match], match);
137
+ const inSubChat = (range: Range): boolean => !!range.startContainer.parentElement!.closest('details');
138
+ const ranges = registry.highlights.get(ChatFindHighlighter.MATCH_HIGHLIGHT)!;
139
+ expect(ranges).to.have.length(2);
140
+ expect(ranges.some(inSubChat)).to.be.false;
141
+ const current = registry.highlights.get(ChatFindHighlighter.CURRENT_HIGHLIGHT)!;
142
+ expect(current.map(range => range.toString())).to.deep.equal(['find me']);
143
+ expect(current.some(inSubChat)).to.be.false;
144
+ });
145
+
146
+ it('does not highlight text inside <style> or <script>, e.g. a diagram stylesheet', () => {
147
+ root.querySelectorAll('.theia-ResponseNode-Content')[1].innerHTML =
148
+ '<svg><style>.find-me-node{fill:#fff}</style>' +
149
+ '<foreignObject><div><span>find me</span></div></foreignObject></svg>' +
150
+ '<script>const findMe = "find me";</script>';
151
+ highlighter.update(root, /find me/gi, [contentMatch(1, 0)], undefined);
152
+ const ranges = registry.highlights.get(ChatFindHighlighter.MATCH_HIGHLIGHT)!;
153
+ expect(ranges).to.have.length(1);
154
+ expect(ranges[0].startContainer.parentElement!.nodeName.toLowerCase()).to.equal('span');
155
+ });
156
+
157
+ it('highlights request text when the match belongs to a request row', () => {
158
+ highlighter.update(root, /find me/gi, [requestMatch(0)], undefined);
159
+ expect(registry.texts(ChatFindHighlighter.MATCH_HIGHLIGHT)).to.deep.equal(['find me']);
160
+ });
161
+
162
+ it('marks the current match by content index and occurrence', () => {
163
+ highlighter.update(root, /find me/gi, [contentMatch(1, 0), contentMatch(1, 1)], contentMatch(1, 1));
164
+ const current = registry.highlights.get(ChatFindHighlighter.CURRENT_HIGHLIGHT)!;
165
+ expect(current).to.have.length(1);
166
+ expect(current[0].toString()).to.equal('find me');
167
+ // the second occurrence lives in the trailing text node ' and find me'
168
+ expect(current[0].startOffset).to.equal(5);
169
+ });
170
+
171
+ it('marks the current match by its offsets, not by counting occurrences in the DOM', () => {
172
+ // The DOM holds an occurrence the match list does not (e.g. still being streamed): offsets still pick the right one.
173
+ root.querySelectorAll('.theia-ResponseNode-Content')[1].innerHTML = '<p>find me <em>and</em> find me</p>';
174
+ const second: ChatFindMatch = { ...contentMatch(1, 0), start: 12, end: 19 };
175
+ highlighter.update(root, /find me/gi, [second], second);
176
+ const current = registry.highlights.get(ChatFindHighlighter.CURRENT_HIGHLIGHT)!;
177
+ expect(current).to.have.length(1);
178
+ expect(current[0].startContainer.textContent).to.equal(' find me');
179
+ });
180
+
181
+ it('marks nothing as current when the DOM has no such occurrence', () => {
182
+ highlighter.update(root, /find me/gi, [contentMatch(1, 5)], contentMatch(1, 5));
183
+ expect(registry.highlights.get(ChatFindHighlighter.CURRENT_HIGHLIGHT)).to.deep.equal([]);
184
+ });
185
+
186
+ it('searches only the message of an error part', () => {
187
+ root.querySelectorAll('.theia-ResponseNode-Content')[1].innerHTML = '<div class="theia-ChatPart-Error">' +
188
+ '<div class="theia-ChatPart-Error-headline"><div class="theia-ChatPart-Error-prefix">Error find me:</div>' +
189
+ '<div class="theia-ChatPart-Error-message">find me</div></div><details><pre>find me</pre></details></div>';
190
+ const match = contentMatch(1, 0, 'error');
191
+ highlighter.update(root, /find me/gi, [match], match);
192
+ const ranges = registry.highlights.get(ChatFindHighlighter.MATCH_HIGHLIGHT)!;
193
+ expect(ranges).to.have.length(1);
194
+ expect(ranges[0].startContainer.parentElement!.className).to.equal('theia-ChatPart-Error-message');
195
+ });
196
+
197
+ it('skips blocked-resource placeholders and treats non-breaking spaces as spaces', () => {
198
+ root.querySelectorAll('.theia-ResponseNode-Content')[1].innerHTML =
199
+ '<p><span class="theia-blocked-resource">find me</span>find\u00a0me</p>';
200
+ highlighter.update(root, /find me/gi, [contentMatch(1, 0)], contentMatch(1, 0));
201
+ const ranges = registry.highlights.get(ChatFindHighlighter.MATCH_HIGHLIGHT)!;
202
+ expect(ranges).to.have.length(1);
203
+ expect(ranges[0].startContainer.parentElement!.nodeName.toLowerCase()).to.equal('p');
204
+ expect(registry.highlights.get(ChatFindHighlighter.CURRENT_HIGHLIGHT)).to.have.length(1);
205
+ });
206
+
207
+ describe('code parts', () => {
208
+ let wrapper: HTMLElement;
209
+ let fake: ReturnType<typeof fakeEditor>;
210
+
211
+ beforeEach(() => {
212
+ root.querySelectorAll('.theia-ResponseNode-Content')[1].innerHTML =
213
+ `<div class="theia-CodePartRenderer-root"><div class="${CodeWrapperEditors.CLASS}"><span>find\u00a0me</span></div></div>`;
214
+ wrapper = root.querySelector(`.${CodeWrapperEditors.CLASS}`)!;
215
+ fake = fakeEditor();
216
+ CodeWrapperEditors.set(wrapper, fake.editor);
217
+ });
218
+
219
+ it('decorates matches in the editor instead of highlighting its DOM', () => {
220
+ const first = contentMatch(1, 0, 'code');
221
+ const second = contentMatch(1, 1, 'code');
222
+ highlighter.update(root, /find me/gi, [first, second], second);
223
+ expect(fake.decorations()).to.deep.equal([
224
+ { start: 0, end: 7, className: 'findMatch' },
225
+ { start: 12, end: 19, className: 'currentFindMatch' }
226
+ ]);
227
+ expect(registry.highlights.get(ChatFindHighlighter.MATCH_HIGHLIGHT)).to.deep.equal([]);
228
+ expect(registry.highlights.get(ChatFindHighlighter.CURRENT_HIGHLIGHT)).to.deep.equal([]);
229
+ });
230
+
231
+ it('does not set unchanged decorations again, so decorating does not loop through the mutation observer', () => {
232
+ const match = contentMatch(1, 0, 'code');
233
+ highlighter.update(root, /find me/gi, [match], match);
234
+ highlighter.refresh();
235
+ expect(fake.setCount()).to.equal(1);
236
+ });
237
+
238
+ it('clears the decorations when the find is cleared', () => {
239
+ highlighter.update(root, /find me/gi, [contentMatch(1, 0, 'code')], undefined);
240
+ highlighter.update(root, undefined, [], undefined);
241
+ expect(fake.decorations()).to.deep.equal([]);
242
+ });
243
+ });
244
+
245
+ it('clears both highlights when the regexp is removed', () => {
246
+ highlighter.update(root, /find/gi, [contentMatch(1, 0)], undefined);
247
+ highlighter.update(root, undefined, [], undefined);
248
+ expect(registry.highlights.has(ChatFindHighlighter.MATCH_HIGHLIGHT)).to.be.false;
249
+ expect(registry.highlights.has(ChatFindHighlighter.CURRENT_HIGHLIGHT)).to.be.false;
250
+ });
251
+
252
+ it('does nothing without a registry', () => {
253
+ const noop = createHighlighter(undefined);
254
+ noop.update(root, /find/gi, [contentMatch(1, 0)], undefined);
255
+ noop.dispose();
256
+ expect(registry.highlights.size).to.equal(0);
257
+ });
258
+
259
+ it('re-applies after DOM mutations', async () => {
260
+ highlighter.update(root, /find/gi, [contentMatch(1, 0), contentMatch(1, 1)], undefined);
261
+ const before = registry.texts(ChatFindHighlighter.MATCH_HIGHLIGHT).length;
262
+ root.querySelectorAll('.theia-ResponseNode-Content')[1].insertAdjacentHTML('beforeend', '<p>find again</p>');
263
+ await new Promise(resolve => setTimeout(resolve, 50));
264
+ expect(registry.texts(ChatFindHighlighter.MATCH_HIGHLIGHT).length).to.equal(before + 1);
265
+ });
266
+
267
+ describe('scrollCurrentIntoView', () => {
268
+ let outer: HTMLElement;
269
+ let scroller: HTMLElement;
270
+ let scrollIntoViewCalls: number;
271
+
272
+ beforeEach(() => {
273
+ scrollIntoViewCalls = 0;
274
+ Element.prototype.scrollIntoView = function (): void { scrollIntoViewCalls++; };
275
+ // An element outside the root that must never be scrolled (stands in for the Theia shell).
276
+ outer = document.createElement('div');
277
+ scrollable(outer, 2000, 400);
278
+ document.body.appendChild(outer);
279
+ outer.appendChild(root);
280
+ // The tree's own scroll container, inside the root.
281
+ scroller = document.createElement('div');
282
+ scrollable(scroller, 1000, 200);
283
+ layout(scroller, { top: 0, height: 200 });
284
+ root.insertBefore(scroller, root.firstChild);
285
+ scroller.innerHTML = '<div data-node-id="r9"><div class="theia-ResponseNode">' +
286
+ '<div class="theia-ResponseNode-Content"><p>find me</p></div></div></div>';
287
+ });
288
+
289
+ afterEach(() => {
290
+ document.body.removeChild(outer);
291
+ document.body.appendChild(root);
292
+ });
293
+
294
+ const match = (): ChatFindMatch => ({ nodeId: 'r9', kind: 'markdownContent', contentIndex: 0, occurrence: 0, start: 0, end: 7 });
295
+
296
+ it('scrolls the tree scroll container and never an ancestor outside the root', () => {
297
+ layout(scroller.querySelector('p')!, { top: 500, height: 20 });
298
+ highlighter.update(root, /find me/gi, [match()], match());
299
+ highlighter.scrollCurrentIntoView(root);
300
+ expect(scroller.scrollTop).to.equal(410); // 500 - (200 - 20) / 2
301
+ expect(outer.scrollTop).to.equal(0);
302
+ expect(scrollIntoViewCalls).to.equal(0);
303
+ });
304
+
305
+ it('leaves the scroll position alone when the match is already visible', () => {
306
+ layout(scroller.querySelector('p')!, { top: 50, height: 20 });
307
+ scroller.scrollTop = 120;
308
+ highlighter.update(root, /find me/gi, [match()], match());
309
+ highlighter.scrollCurrentIntoView(root);
310
+ expect(scroller.scrollTop).to.equal(120);
311
+ });
312
+ });
313
+ });