Skip to content

Commit 268fa0e

Browse files
authored
fix(knowledge): preserve scroll position when toggling tokenizer in chunk viewer (#4643)
* fix(knowledge): preserve scroll position when toggling tokenizer in chunk viewer * fix(knowledge): skip scroll restore on initial mount of chunk editor * chore(dev): add dev:clean script to purge Turbopack cache
1 parent 3979476 commit 268fa0e

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/chunk-editor/chunk-editor.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
44
import { Label, Switch } from '@/components/emcn'
55
import { isApiClientError } from '@/lib/api/client/errors'
66
import { requestJson } from '@/lib/api/client/request'
@@ -50,6 +50,8 @@ export function ChunkEditor({
5050
onCreated,
5151
}: ChunkEditorProps) {
5252
const textareaRef = useRef<HTMLTextAreaElement>(null)
53+
const tokenizedScrollRef = useRef<HTMLDivElement>(null)
54+
const preservedScrollTopRef = useRef(0)
5355
const { mutateAsync: updateChunk } = useUpdateChunk()
5456
const { mutateAsync: createChunk } = useCreateChunk()
5557

@@ -170,6 +172,24 @@ export function ChunkEditor({
170172
[saveRef]
171173
)
172174

175+
const hasToggledTokenizerRef = useRef(false)
176+
177+
const handleTokenizerChange = useCallback(
178+
(value: boolean) => {
179+
const source = tokenizerOn ? tokenizedScrollRef.current : textareaRef.current
180+
preservedScrollTopRef.current = source?.scrollTop ?? 0
181+
hasToggledTokenizerRef.current = true
182+
setTokenizerOn(value)
183+
},
184+
[tokenizerOn]
185+
)
186+
187+
useLayoutEffect(() => {
188+
if (!hasToggledTokenizerRef.current) return
189+
const target = tokenizerOn ? tokenizedScrollRef.current : textareaRef.current
190+
if (target) target.scrollTop = preservedScrollTopRef.current
191+
}, [tokenizerOn])
192+
173193
const tokenStrings = useMemo(() => {
174194
if (!tokenizerOn || !editedContent) return []
175195
return getTokenStrings(editedContent)
@@ -196,7 +216,10 @@ export function ChunkEditor({
196216
}}
197217
>
198218
{tokenizerOn ? (
199-
<div className='h-full w-full cursor-default overflow-y-auto whitespace-pre-wrap break-words p-6 font-sans text-[var(--text-body)] text-sm'>
219+
<div
220+
ref={tokenizedScrollRef}
221+
className='h-full w-full cursor-default overflow-y-auto whitespace-pre-wrap break-words p-6 font-sans text-[var(--text-body)] text-sm'
222+
>
200223
{tokenStrings.map((token, index) => (
201224
<span
202225
key={index}
@@ -232,7 +255,7 @@ export function ChunkEditor({
232255
<div className='flex items-center justify-between border-[var(--border)] border-t px-6 py-2.5'>
233256
<TokenizerToggle
234257
checked={tokenizerOn}
235-
onCheckedChange={setTokenizerOn}
258+
onCheckedChange={handleTokenizerChange}
236259
hoveredTokenIndex={tokenizerOn ? hoveredTokenIndex : null}
237260
/>
238261
<span className='text-[var(--text-secondary)] text-caption'>

apps/sim/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
},
1010
"scripts": {
1111
"dev": "next dev --port 3000",
12+
"dev:clean": "rm -rf .next/dev/cache",
1213
"dev:webpack": "next dev --webpack",
1314
"load:workflow": "bun run load:workflow:baseline",
1415
"load:workflow:baseline": "BASE_URL=${BASE_URL:-http://localhost:3000} WARMUP_DURATION=${WARMUP_DURATION:-10} WARMUP_RATE=${WARMUP_RATE:-2} PEAK_RATE=${PEAK_RATE:-8} HOLD_DURATION=${HOLD_DURATION:-20} bunx artillery run scripts/load/workflow-concurrency.yml",

0 commit comments

Comments
 (0)