mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-13 20:14:16 +00:00
Chunk nav fixed
This commit is contained in:
1 parent
f4331cd3a3
commit
b12f5b6c81
9 files changed
+87
-25
No files matched your search
@@ -507,6 +507,8 @@ describe('Chunks', () => {
|
||||
await act(async () => buttonByText('modals.chunk.save')!.click());
|
||||
expect(drawer()).toBeNull();
|
||||
expect(container.querySelector('h2')?.textContent).toBe('Rewritten');
|
||||
expect(container.textContent).toContain('chunkPositionUnplaced');
|
||||
expect(buttonByLabel('settings.sources.nextChunk')!.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('deleting the only chunk on the last page lands on the page before', async () => {
|
||||
@@ -789,7 +791,7 @@ describe('Chunks', () => {
|
||||
expect(container.querySelector('h1')?.textContent).toBe('Second');
|
||||
});
|
||||
|
||||
it('an edited chunk the store moved off the probed positions keeps what is shown', async () => {
|
||||
it('an edited chunk the store moved off the probed positions keeps its text but not its place', async () => {
|
||||
serveStore(1);
|
||||
await render({ embedded: true });
|
||||
const tiles = container.querySelectorAll<HTMLButtonElement>(
|
||||
@@ -799,7 +801,14 @@ describe('Chunks', () => {
|
||||
service.getDocumentChunks.mockClear();
|
||||
await editOpenChunk('# First edited');
|
||||
expect(container.querySelector('h1')?.textContent).toBe('First edited');
|
||||
expect(position()).toEqual([1, 3]);
|
||||
// Its position is unknown: no number is claimed and paging is off, so
|
||||
// previous / next can't step from a place the chunk no longer holds.
|
||||
expect(container.textContent).not.toMatch(/chunkPosition \{/);
|
||||
expect(container.textContent).toContain('chunkPositionUnplaced');
|
||||
expect(buttonByLabel('settings.sources.previousChunk')!.disabled).toBe(
|
||||
true,
|
||||
);
|
||||
expect(buttonByLabel('settings.sources.nextChunk')!.disabled).toBe(true);
|
||||
// Only the open and last positions are probed: the whole filtered list
|
||||
// (one page of `total`) is never fetched.
|
||||
const sizes = service.getDocumentChunks.mock.calls.map(
|
||||
|
||||
@@ -111,6 +111,9 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
const [openChunk, setOpenChunk] = useState<ChunkType | null>(null);
|
||||
// The open chunk's position in the whole filtered list (1-based).
|
||||
const [openPosition, setOpenPosition] = useState(0);
|
||||
// Set when a save leaves the open chunk off every probed position: its
|
||||
// place in the list is unknown, so position labels and paging are off.
|
||||
const [positionLost, setPositionLost] = useState(false);
|
||||
// A previous / next fetch in flight.
|
||||
const [stepping, setStepping] = useState(false);
|
||||
const stepRef = useRef(0);
|
||||
@@ -197,6 +200,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
if (chunk) {
|
||||
setOpenChunk(chunk);
|
||||
setOpenPosition(position);
|
||||
setPositionLost(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -214,7 +218,10 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
* position (the chunk stayed), then the last one (a store that re-adds the
|
||||
* copy at the end). Each probe matches by id, so no store ordering is
|
||||
* assumed. When neither holds it (moved elsewhere, or a search it no longer
|
||||
* matches), what is shown stays: the whole filtered list is never fetched.
|
||||
* matches), the saved text stays but its position is unknown, so the
|
||||
* position labels drop the number and previous / next are off until the
|
||||
* reader closes; the whole filtered list is never fetched. A failed or
|
||||
* superseded probe leaves what is shown.
|
||||
*
|
||||
* @param chunk The chunk just saved, with its new id.
|
||||
*/
|
||||
@@ -237,6 +244,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
setTotalChunks(total);
|
||||
setOpenChunk(stored);
|
||||
setOpenPosition(at);
|
||||
setPositionLost(false);
|
||||
};
|
||||
try {
|
||||
const here = await fetchPage(openPosition, 1);
|
||||
@@ -248,12 +256,17 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
show(stored, openPosition, total);
|
||||
return;
|
||||
}
|
||||
if (total < 1) return;
|
||||
if (total === openPosition) return;
|
||||
const last = await fetchPage(total, 1);
|
||||
if (!last) return;
|
||||
const lastChunk: ChunkType | undefined = last.chunks?.[0];
|
||||
if (lastChunk?.doc_id === chunk.doc_id) show(lastChunk, total, total);
|
||||
if (total >= 1 && total !== openPosition) {
|
||||
const last = await fetchPage(total, 1);
|
||||
if (!last) return;
|
||||
const lastChunk: ChunkType | undefined = last.chunks?.[0];
|
||||
if (lastChunk?.doc_id === chunk.doc_id) {
|
||||
show(lastChunk, total, total);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setTotalChunks(total);
|
||||
setPositionLost(true);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -296,6 +309,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
const showChunk = (chunk: ChunkType, position: number) => {
|
||||
setOpenChunk(chunk);
|
||||
setOpenPosition(position);
|
||||
setPositionLost(false);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -459,8 +473,10 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
setPage(1);
|
||||
}, [path]);
|
||||
|
||||
const canGoPrevious = !!openChunk && !stepping && openPosition > 1;
|
||||
const canGoNext = !!openChunk && !stepping && openPosition < totalChunks;
|
||||
const canGoPrevious =
|
||||
!!openChunk && !stepping && !positionLost && openPosition > 1;
|
||||
const canGoNext =
|
||||
!!openChunk && !stepping && !positionLost && openPosition < totalChunks;
|
||||
|
||||
// ← / → walk the chunks while nothing else owns the keys.
|
||||
const goToChunkRef = useRef(goToChunk);
|
||||
@@ -490,7 +506,13 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
onSelect: openChunk ? () => closeChunk() : undefined,
|
||||
},
|
||||
...(openChunk
|
||||
? [{ label: t('settings.sources.chunkCrumb', { n: openPosition }) }]
|
||||
? [
|
||||
{
|
||||
label: positionLost
|
||||
? t('settings.sources.chunkCrumbUnplaced')
|
||||
: t('settings.sources.chunkCrumb', { n: openPosition }),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
byline={
|
||||
@@ -609,11 +631,15 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
<ReaderPanel
|
||||
meta={
|
||||
<span>
|
||||
{t('settings.sources.chunkPosition', {
|
||||
n: openPosition,
|
||||
total: totalChunks,
|
||||
tokens: formatChunkTokens(chunk.metadata),
|
||||
})}
|
||||
{positionLost
|
||||
? t('settings.sources.chunkPositionUnplaced', {
|
||||
tokens: formatChunkTokens(chunk.metadata),
|
||||
})
|
||||
: t('settings.sources.chunkPosition', {
|
||||
n: openPosition,
|
||||
total: totalChunks,
|
||||
tokens: formatChunkTokens(chunk.metadata),
|
||||
})}
|
||||
</span>
|
||||
}
|
||||
actions={
|
||||
@@ -723,14 +749,20 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
: t('settings.sources.addChunk')
|
||||
}
|
||||
description={
|
||||
editing
|
||||
? t('settings.sources.editChunkDescription', {
|
||||
file: fileLabel,
|
||||
n: openPosition,
|
||||
tokens: formatChunkTokens(openChunk?.metadata),
|
||||
interpolation: { escapeValue: false },
|
||||
})
|
||||
: fileLabel || undefined
|
||||
!editing
|
||||
? fileLabel || undefined
|
||||
: positionLost
|
||||
? t('settings.sources.editChunkDescriptionUnplaced', {
|
||||
file: fileLabel,
|
||||
tokens: formatChunkTokens(openChunk?.metadata),
|
||||
interpolation: { escapeValue: false },
|
||||
})
|
||||
: t('settings.sources.editChunkDescription', {
|
||||
file: fileLabel,
|
||||
n: openPosition,
|
||||
tokens: formatChunkTokens(openChunk?.metadata),
|
||||
interpolation: { escapeValue: false },
|
||||
})
|
||||
}
|
||||
value={draft}
|
||||
onChange={setDraft}
|
||||
|
||||
@@ -411,6 +411,9 @@
|
||||
},
|
||||
"chunkPosition": "Chunk {{n}} von {{total}} · {{tokens}} Tokens",
|
||||
"chunkCrumb": "Chunk {{n}}",
|
||||
"chunkCrumbUnplaced": "Bearbeiteter Chunk",
|
||||
"chunkPositionUnplaced": "Bearbeiteter Chunk · {{tokens}} Tokens",
|
||||
"editChunkDescriptionUnplaced": "{{file}} · bearbeiteter Chunk · {{tokens}} Tokens",
|
||||
"chunkTileMeta": "#{{n}} · {{tokens}} Tokens",
|
||||
"copyText": "Text kopieren",
|
||||
"chunkTitleHint": "Der Name, unter dem Antworten diesen Chunk zitieren.",
|
||||
|
||||
@@ -416,6 +416,9 @@
|
||||
},
|
||||
"chunkPosition": "Chunk {{n}} of {{total}} · {{tokens}} tokens",
|
||||
"chunkCrumb": "Chunk {{n}}",
|
||||
"chunkCrumbUnplaced": "Edited chunk",
|
||||
"chunkPositionUnplaced": "Edited chunk · {{tokens}} tokens",
|
||||
"editChunkDescriptionUnplaced": "{{file}} · edited chunk · {{tokens}} tokens",
|
||||
"chunkTileMeta": "#{{n}} · {{tokens}} tokens",
|
||||
"copyText": "Copy text",
|
||||
"chunkTitleHint": "The name answers cite this chunk by.",
|
||||
|
||||
@@ -411,6 +411,9 @@
|
||||
},
|
||||
"chunkPosition": "Fragmento {{n}} de {{total}} · {{tokens}} tokens",
|
||||
"chunkCrumb": "Fragmento {{n}}",
|
||||
"chunkCrumbUnplaced": "Fragmento editado",
|
||||
"chunkPositionUnplaced": "Fragmento editado · {{tokens}} tokens",
|
||||
"editChunkDescriptionUnplaced": "{{file}} · fragmento editado · {{tokens}} tokens",
|
||||
"chunkTileMeta": "#{{n}} · {{tokens}} tokens",
|
||||
"copyText": "Copiar texto",
|
||||
"chunkTitleHint": "El nombre con el que las respuestas citan este fragmento.",
|
||||
|
||||
@@ -404,6 +404,9 @@
|
||||
},
|
||||
"chunkPosition": "チャンク {{n}} / {{total}} · {{tokens}} トークン",
|
||||
"chunkCrumb": "チャンク {{n}}",
|
||||
"chunkCrumbUnplaced": "編集済みチャンク",
|
||||
"chunkPositionUnplaced": "編集済みチャンク · {{tokens}} トークン",
|
||||
"editChunkDescriptionUnplaced": "{{file}} · 編集済みチャンク · {{tokens}} トークン",
|
||||
"chunkTileMeta": "#{{n}} · {{tokens}} トークン",
|
||||
"copyText": "テキストをコピー",
|
||||
"chunkTitleHint": "回答がこのチャンクを引用するときの名前です。",
|
||||
|
||||
@@ -435,6 +435,9 @@
|
||||
},
|
||||
"chunkPosition": "Фрагмент {{n}} из {{total}} · токенов: {{tokens}}",
|
||||
"chunkCrumb": "Фрагмент {{n}}",
|
||||
"chunkCrumbUnplaced": "Изменённый фрагмент",
|
||||
"chunkPositionUnplaced": "Изменённый фрагмент · токенов: {{tokens}}",
|
||||
"editChunkDescriptionUnplaced": "{{file}} · изменённый фрагмент · токенов: {{tokens}}",
|
||||
"chunkTileMeta": "#{{n}} · токенов: {{tokens}}",
|
||||
"copyText": "Копировать текст",
|
||||
"chunkTitleHint": "Название, под которым ответы ссылаются на этот фрагмент.",
|
||||
|
||||
@@ -404,6 +404,9 @@
|
||||
},
|
||||
"chunkPosition": "第 {{n}} 個文本塊,共 {{total}} 個 · {{tokens}} Token",
|
||||
"chunkCrumb": "第 {{n}} 個文本塊",
|
||||
"chunkCrumbUnplaced": "已編輯的文本塊",
|
||||
"chunkPositionUnplaced": "已編輯的文本塊 · {{tokens}} Token",
|
||||
"editChunkDescriptionUnplaced": "{{file}} · 已編輯的文本塊 · {{tokens}} Token",
|
||||
"chunkTileMeta": "#{{n}} · {{tokens}} Token",
|
||||
"copyText": "複製文字",
|
||||
"chunkTitleHint": "回答引用此文本塊時使用的名稱。",
|
||||
|
||||
@@ -404,6 +404,9 @@
|
||||
},
|
||||
"chunkPosition": "第 {{n}} 个文本块,共 {{total}} 个 · {{tokens}} 个令牌",
|
||||
"chunkCrumb": "第 {{n}} 个文本块",
|
||||
"chunkCrumbUnplaced": "已编辑的文本块",
|
||||
"chunkPositionUnplaced": "已编辑的文本块 · {{tokens}} 个令牌",
|
||||
"editChunkDescriptionUnplaced": "{{file}} · 已编辑的文本块 · {{tokens}} 个令牌",
|
||||
"chunkTileMeta": "#{{n}} · {{tokens}} 个令牌",
|
||||
"copyText": "复制文本",
|
||||
"chunkTitleHint": "回答引用此文本块时使用的名称。",
|
||||
|
||||
Reference in new issue
Block a user