diff --git a/web/features/new-rag/documents/detail/__tests__/page.spec.tsx b/web/features/new-rag/documents/detail/__tests__/page.spec.tsx
index 968f628b367..f53d265bf75 100644
--- a/web/features/new-rag/documents/detail/__tests__/page.spec.tsx
+++ b/web/features/new-rag/documents/detail/__tests__/page.spec.tsx
@@ -16,6 +16,7 @@ import { act, fireEvent, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import copy from 'copy-to-clipboard'
import { createStore, Provider } from 'jotai'
+import { NuqsTestingAdapter } from 'nuqs/adapters/testing'
import { renderWithNuqs } from '@/test/nuqs-testing'
import { DocumentDetailPage } from '../page'
@@ -2555,6 +2556,39 @@ describe('DocumentDetailPage', () => {
getBoundingClientRect.mockRestore()
})
+ it('isolates document route state while client navigation renders two route instances', () => {
+ chunksQuery.data = {
+ pages: [
+ {
+ items: [
+ chunk({ id: 'first', text: 'First chunk' }),
+ chunk({ id: 'target', ordinal: 2, text: 'Target chunk' }),
+ ],
+ },
+ ],
+ }
+
+ render(
+ <>
+
+
+
+
+
+
+ >,
+ )
+
+ const firstChunkRows = screen.getAllByRole('treeitem', { name: 'First chunk' })
+ const targetChunkRows = screen.getAllByRole('treeitem', { name: 'Target chunk' })
+ expect(firstChunkRows).toHaveLength(2)
+ expect(targetChunkRows).toHaveLength(2)
+ expect(firstChunkRows[0]).toHaveAttribute('aria-selected', 'true')
+ expect(targetChunkRows[0]).toHaveAttribute('aria-selected', 'false')
+ expect(firstChunkRows[1]).toHaveAttribute('aria-selected', 'false')
+ expect(targetChunkRows[1]).toHaveAttribute('aria-selected', 'true')
+ })
+
it('distinguishes missing, restricted, and retryable document failures', async () => {
const user = userEvent.setup()
documentQuery.data = undefined
diff --git a/web/features/new-rag/documents/detail/state/boundary.tsx b/web/features/new-rag/documents/detail/state/boundary.tsx
index c949999c91f..867380163dd 100644
--- a/web/features/new-rag/documents/detail/state/boundary.tsx
+++ b/web/features/new-rag/documents/detail/state/boundary.tsx
@@ -1,6 +1,7 @@
'use client'
import type { ReactNode } from 'react'
+import { ScopeProvider } from 'jotai-scope'
import { useHydrateAtoms } from 'jotai/utils'
import { useQueryStates } from 'nuqs'
import {
@@ -10,6 +11,27 @@ import {
documentDetailRequestedRevisionAtom,
} from './inputs'
import { documentDetailChunkParser, documentDetailRevisionParser } from './location'
+import { documentWorkflowScopedAtoms } from './workflow'
+
+function DocumentDetailLocationBridge({
+ children,
+ chunkId,
+ revision,
+}: {
+ children: ReactNode
+ chunkId: string | null
+ revision: number | null
+}) {
+ useHydrateAtoms(
+ [
+ [documentDetailRequestedChunkIdAtom, chunkId],
+ [documentDetailRequestedRevisionAtom, revision],
+ ],
+ { dangerouslyForceHydrate: true },
+ )
+
+ return children
+}
export function DocumentDetailStateBoundary({
children,
@@ -25,15 +47,24 @@ export function DocumentDetailStateBoundary({
revision: documentDetailRevisionParser,
})
- useHydrateAtoms(
- [
- [documentDetailDocumentIdAtom, documentId],
- [documentDetailKnowledgeSpaceIdAtom, knowledgeSpaceId],
- [documentDetailRequestedChunkIdAtom, documentLocation.chunk],
- [documentDetailRequestedRevisionAtom, documentLocation.revision],
- ],
- { dangerouslyForceHydrate: true },
+ return (
+
+
+ {children}
+
+
)
-
- return children
}
diff --git a/web/features/new-rag/documents/detail/state/workflow.ts b/web/features/new-rag/documents/detail/state/workflow.ts
index 9f838f24bd5..dd3d29bdf01 100644
--- a/web/features/new-rag/documents/detail/state/workflow.ts
+++ b/web/features/new-rag/documents/detail/state/workflow.ts
@@ -67,6 +67,11 @@ function initialWorkflowState(identity: string): DocumentWorkflowState {
const documentWorkflowStateAtom = atom(initialWorkflowState(''))
+export const documentWorkflowScopedAtoms = [
+ documentHasEditPermissionAtom,
+ documentWorkflowStateAtom,
+] as const
+
function workflowState(get: Getter) {
const identity = workflowIdentity(get)
const state = get(documentWorkflowStateAtom)