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
This commit is contained in:
Waleed
2026-05-18 10:36:47 -07:00
committed by GitHub
parent 3979476432
commit 268fa0e97e
2 changed files with 27 additions and 3 deletions
@@ -1,6 +1,6 @@
'use client'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { Label, Switch } from '@/components/emcn'
import { isApiClientError } from '@/lib/api/client/errors'
import { requestJson } from '@/lib/api/client/request'
@@ -50,6 +50,8 @@ export function ChunkEditor({
onCreated,
}: ChunkEditorProps) {
const textareaRef = useRef<HTMLTextAreaElement>(null)
const tokenizedScrollRef = useRef<HTMLDivElement>(null)
const preservedScrollTopRef = useRef(0)
const { mutateAsync: updateChunk } = useUpdateChunk()
const { mutateAsync: createChunk } = useCreateChunk()
@@ -170,6 +172,24 @@ export function ChunkEditor({
[saveRef]
)
const hasToggledTokenizerRef = useRef(false)
const handleTokenizerChange = useCallback(
(value: boolean) => {
const source = tokenizerOn ? tokenizedScrollRef.current : textareaRef.current
preservedScrollTopRef.current = source?.scrollTop ?? 0
hasToggledTokenizerRef.current = true
setTokenizerOn(value)
},
[tokenizerOn]
)
useLayoutEffect(() => {
if (!hasToggledTokenizerRef.current) return
const target = tokenizerOn ? tokenizedScrollRef.current : textareaRef.current
if (target) target.scrollTop = preservedScrollTopRef.current
}, [tokenizerOn])
const tokenStrings = useMemo(() => {
if (!tokenizerOn || !editedContent) return []
return getTokenStrings(editedContent)
@@ -196,7 +216,10 @@ export function ChunkEditor({
}}
>
{tokenizerOn ? (
<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'>
<div
ref={tokenizedScrollRef}
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'
>
{tokenStrings.map((token, index) => (
<span
key={index}
@@ -232,7 +255,7 @@ export function ChunkEditor({
<div className='flex items-center justify-between border-[var(--border)] border-t px-6 py-2.5'>
<TokenizerToggle
checked={tokenizerOn}
onCheckedChange={setTokenizerOn}
onCheckedChange={handleTokenizerChange}
hoveredTokenIndex={tokenizerOn ? hoveredTokenIndex : null}
/>
<span className='text-[var(--text-secondary)] text-caption'>
+1
View File
@@ -9,6 +9,7 @@
},
"scripts": {
"dev": "next dev --port 3000",
"dev:clean": "rm -rf .next/dev/cache",
"dev:webpack": "next dev --webpack",
"load:workflow": "bun run load:workflow:baseline",
"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",