Skip to content

Commit 7ea0271

Browse files
authored
fix(webapp): avoid mutating render inputs (#4716)
## Summary Keeps render inputs and shared regular expressions immutable. Grouped selects now compute each section's shortcut offset directly from preceding sections, which also makes numeric shortcuts follow the displayed item order reliably.
1 parent 176fb6d commit 7ea0271

3 files changed

Lines changed: 16 additions & 16 deletions

File tree

apps/webapp/app/components/code/CodeBlock.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -221,35 +221,35 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
221221
const [modalCopied, setModalCopied] = useState(false);
222222
const [isModalOpen, setIsModalOpen] = useState(false);
223223
const [isWrapped, setIsWrapped] = useState(wrap);
224+
const normalizedCode = code?.trim() ?? "";
224225

225226
const onCopied = useCallback(
226227
(event: React.MouseEvent<HTMLButtonElement>) => {
227228
event.preventDefault();
228229
event.stopPropagation();
229-
navigator.clipboard.writeText(code);
230+
navigator.clipboard.writeText(normalizedCode);
230231
setCopied(true);
231232
setTimeout(() => {
232233
setCopied(false);
233234
}, 1500);
234235
},
235-
[code]
236+
[normalizedCode]
236237
);
237238

238239
const onModalCopied = useCallback(
239240
(event: React.MouseEvent<HTMLButtonElement>) => {
240241
event.preventDefault();
241242
event.stopPropagation();
242-
navigator.clipboard.writeText(code);
243+
navigator.clipboard.writeText(normalizedCode);
243244
setModalCopied(true);
244245
setTimeout(() => {
245246
setModalCopied(false);
246247
}, 1500);
247248
},
248-
[code]
249+
[normalizedCode]
249250
);
250251

251-
code = code?.trim() ?? "";
252-
const lineCount = code.split("\n").length;
252+
const lineCount = normalizedCode.split("\n").length;
253253
const maxLineWidth = lineCount.toString().length;
254254
let maxHeight: string | undefined = undefined;
255255
if (maxLines && lineCount > maxLines) {
@@ -345,7 +345,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
345345
{shouldHighlight ? (
346346
<HighlightCode
347347
theme={theme}
348-
code={code}
348+
code={normalizedCode}
349349
language={language}
350350
showLineNumbers={showLineNumbers}
351351
highlightLines={highlightLines}
@@ -373,7 +373,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
373373
)}
374374
dir="ltr"
375375
>
376-
{highlightSearchText(code, searchTerm)}
376+
{highlightSearchText(normalizedCode, searchTerm)}
377377
</pre>
378378
</div>
379379
)}
@@ -400,7 +400,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
400400
{shouldHighlight ? (
401401
<HighlightCode
402402
theme={theme}
403-
code={code}
403+
code={normalizedCode}
404404
language={language}
405405
showLineNumbers={showLineNumbers}
406406
highlightLines={highlightLines}
@@ -415,7 +415,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
415415
className="overflow-auto px-3 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control"
416416
>
417417
<pre className="relative mr-2 p-2 font-mono text-base leading-relaxed" dir="ltr">
418-
{highlightSearchText(code, searchTerm)}
418+
{highlightSearchText(normalizedCode, searchTerm)}
419419
</pre>
420420
</div>
421421
)}

apps/webapp/app/components/dashboard-agent/report-sparkline.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export function ReportFindingLine({
187187
* entities mono, verdict phrases bright and medium, everything else dimmed.
188188
* Colour stays reserved for severity, so emphasis here is weight only.
189189
*/
190-
const QUANTITY_RE = /~?\d[\d,.]*\s?(?:%|×|\/min|ms\b|s\b|min\b|h\b)?/g;
190+
const QUANTITY_RE = /~?\d[\d,.]*\s?(?:%|×|\/min|ms\b|s\b|min\b|h\b)?/;
191191

192192
const VERDICT_PHRASES = [
193193
"not your code",
@@ -249,7 +249,6 @@ export function ReportProse({ text, entities }: { text: string; entities?: strin
249249
segments = splitBy(
250250
segments,
251251
(t) => {
252-
QUANTITY_RE.lastIndex = 0;
253252
const m = QUANTITY_RE.exec(t);
254253
return m && m[0].trim().length > 0 ? { start: m.index, end: m.index + m[0].length } : null;
255254
},

apps/webapp/app/components/primitives/Select.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,19 +413,20 @@ function SelectGroupedRenderer<TItem>({
413413
) => React.ReactNode;
414414
enableItemShortcuts: boolean;
415415
}) {
416-
let count = 0;
417416
return (
418417
<>
419418
{items.map((section, index) => {
420-
const previousItem = items.at(index - 1);
421-
count += previousItem ? previousItem.items.length : 0;
419+
const startIndex = items
420+
.slice(0, index)
421+
.reduce((count, previousSection) => count + previousSection.items.length, 0);
422+
422423
return (
423424
<Fragment key={index}>
424425
{children(section.items as ItemFromSection<TItem>[], {
425426
shortcutsEnabled: enableItemShortcuts,
426427
section: {
427428
title: section.title,
428-
startIndex: count - 1,
429+
startIndex,
429430
count: section.items.length,
430431
},
431432
})}

0 commit comments

Comments
 (0)