Skip to content

Conversation

@AndlerRL
Copy link
Member

@AndlerRL AndlerRL commented Nov 19, 2025

TODO:

  • Check if running.
  • Revise if missing any fn and/or variable.

Summary by Sourcery

Extract the workspace document management logic into a standalone hook and provider, streamline the main workspace context, and update components to consume the new documents hook.

Enhancements:

  • Extract useWorkspaceDocuments hook to centralize document fetching, updates, and streaming logic.
  • Refactor WorkspaceChatProvider to use document state and actions from useWorkspaceDocuments instead of local implementation.
  • Simplify useWorkspace context and WorkspaceProvider by removing direct document/structure state and delegating to the new documents hook under a feature flag.
  • Clean up legacy workspace structure code and mutation wrappers, consolidating TanStack queries in the documents hook.
  • Adjust workspace-tab-menu to only render the document-history submenu option.

@AndlerRL AndlerRL self-assigned this Nov 19, 2025
@vercel
Copy link

vercel bot commented Nov 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
masterbots-pro Error Error Nov 19, 2025 1:58am
1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
masterbots Skipped Skipped Nov 19, 2025 1:58am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 19, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/independent-workspace-document-hook

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 19, 2025

Reviewer's Guide

This PR extracts all document and streaming state into a standalone useWorkspaceDocuments hook/provider, refactors the chat and main workspace hooks to delegate document logic to the new module, removes legacy workspace state fields and mutations, and applies a minor menu id fix.

Sequence diagram for document update flow after refactor

sequenceDiagram
    participant WC as WorkspaceChatProvider
    participant WDP as WorkspaceDocumentsProvider
    participant DB as Database
    WC->>WDP: handleDocumentUpdate(aiResponse, selectionRange)
    WDP->>DB: updateDocumentContentConditional(project, document, newMarkdown)
    DB-->>WDP: Document updated
    WDP-->>WC: onSectionContentUpdate(sectionId, updatedContent)
Loading

Class diagram for refactored workspace document management

classDiagram
    class WorkspaceDocumentsProvider {
        +documents: WorkspaceDocumentMetadata[]
        +organizationData: OrganizationData[]
        +organizationList: string[]
        +departmentsByOrg: Record<string, string[]>
        +projectsByDept: Record<string, Record<string, string[]>>
        +documentList: Record<string, string[]>
        +textDocuments: Record<string, string[]>
        +imageDocuments: Record<string, string[]>
        +spreadsheetDocuments: Record<string, string[]>
        +templates: typeof workspaceDocTemplates
        +addOrganization(name, chatbots)
        +addDepartment(org, name)
        +addProject(org, dept, name)
        +addDocument(project, name, type, templateContent)
        +updateChatbotActiveState(orgName, chatbotId, isActive)
        +getOrganizationChatbots(orgName)
        +documentData: content, sections
        +isLoadingDocument: boolean
        +workspaceProcessingState: 'idle' | 'analyzing' | 'generating' | 'updating'
        +setWorkspaceProcessingState(state)
        +activeWorkspaceSection: string | null
        +setActiveWorkspaceSection(sectionId)
        +selectionRange: start, end
        +setSelectionRange(range)
        +handleDocumentUpdate(aiResponse, selectionRange)
        +onSectionContentUpdate(sectionId, content)
        +setOnSectionContentUpdate(callback)
        +onStreamingChunk()
        +setOnStreamingChunk(callback)
    }

    class WorkspaceChatProvider {
        -document and streaming state (removed)
        +delegates document logic to useWorkspaceDocuments()
    }

    class WorkspaceProvider {
        -document and streaming state fields (removed)
        +delegates document logic to useWorkspaceDocuments()
    }

    WorkspaceChatProvider --> WorkspaceDocumentsProvider : uses
    WorkspaceProvider --> WorkspaceDocumentsProvider : uses
Loading

Class diagram for removed legacy workspace state and mutations

classDiagram
    class WorkspaceContextType {
        -organizationList: string[]
        -departmentList: Record<string, string[]>
        -projectList: string[]
        -documentList: Record<string, string[]>
        -textDocuments: Record<string, string[]>
        -imageDocuments: Record<string, string[]>
        -spreadsheetDocuments: Record<string, string[]>
        -projectsByDept: Record<string, Record<string, string[]>>
        -addOrganization(name, chatbots)
        -addDepartment(org, name)
        -addProject(org, dept, name)
        -addDocument(project, name, type, templateContent)
        -updateChatbotActiveState(orgName, chatbotId, isActive)
        -getOrganizationChatbots(orgName)
    }

    class WorkspaceState {
        -departmentsByOrg: Record<string, string[]>
        -projectsByDept: Record<string, Record<string, string[]>>
        -textDocuments: Record<string, string[]>
        -imageDocuments: Record<string, string[]>
        -spreadsheetDocuments: Record<string, string[]>
    }
Loading

File-Level Changes

Change Details Files
Introduce independent useWorkspaceDocuments hook and provider
  • Create new WorkspaceDocumentsContext and WorkspaceDocumentsProvider
  • Centralize document fetching, mutations, and streaming refs in the hook
  • Implement content state and handleDocumentUpdate logic inside the new module
  • Mirror original use-workspace.tsx behavior via localStructure state
apps/pro-web/lib/hooks/use-workspace-documents.tsx
Refactor use-workspace-chat to consume the new hook
  • Remove local state, refs, and updateDocumentContentConditional logic
  • Destructure workspaceProcessingState, selectionRange, handlers from useWorkspaceDocuments
  • Simplify debouncedCleanup and streaming reset to call onStreamingChunk
  • Drop duplicated refs and hard-coded document update code
apps/pro-web/lib/hooks/use-workspace-chat.tsx
Clean up WorkspaceProvider in use-workspace.tsx
  • Remove legacy document lists, structure fields, and addX mutation wrappers
  • Comment out old useWorkspaceStructure query and derive data via new hook
  • Adjust contextValue to only expose navigation and setters, delegate documents to useWorkspaceDocuments
  • Swap legacyResult.userDocuments to useWorkspaceDocuments output
apps/pro-web/lib/hooks/use-workspace.tsx
Minor menu id adjustment in workspace-tab-menu
  • Change option.id check from 'history' to 'document-history'
  • Update inline comment to reflect separate media-history handling
apps/pro-web/components/routes/workspace/workspace-tab-menu.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors workspace document management by extracting it into a dedicated useWorkspaceDocuments hook and provider, separating concerns between workspace navigation state and document operations.

Key Changes

  • Extracted document structure management (organizations, departments, projects, documents) and content operations into a standalone WorkspaceDocumentsProvider and useWorkspaceDocuments hook
  • Streamlined WorkspaceProvider by removing document-related state and mutations, delegating to the new documents hook
  • Updated WorkspaceChatProvider to consume document state and actions from useWorkspaceDocuments instead of managing them locally
  • Removed large portions of streaming document update logic from use-workspace-chat.tsx, consolidating it in the documents hook

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
apps/pro-web/lib/hooks/use-workspace.tsx Removed document structure state/actions and mutations; now consumes from useWorkspaceDocuments hook; simplified to focus on workspace navigation state only
apps/pro-web/lib/hooks/use-workspace-documents.tsx New file providing standalone document management with structure data, mutations, content state, and streaming update logic
apps/pro-web/lib/hooks/use-workspace-documents.ts Old implementation deleted - replaced by new Provider-based approach
apps/pro-web/lib/hooks/use-workspace-chat.tsx Refactored to use document state/actions from useWorkspaceDocuments; removed local streaming refs and document update logic
apps/pro-web/components/routes/workspace/workspace-tab-menu.tsx Updated condition to only show history submenu for 'document-history' instead of generic 'history'
Comments suppressed due to low confidence (1)

apps/pro-web/lib/hooks/use-workspace-documents.tsx:16

  • Unused import useAutoSaveDocument.
import {
	makeDocumentKey,
	useAutoSaveDocument,
	useDocumentContent,
	useUpdateDocumentContent,
} from '@/lib/queries'

Comment on lines +497 to +500
const newMarkdown = `${preservedBeforeSelectionRef.current}${cleanedResponse}
${preservedAfterSelectionRef.current}`

Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Formatting inconsistency: The template string has an extra blank line between ${cleanedResponse} and the closing of the preserved after content. This creates double line breaks which may not be intentional. The same pattern appears on line 562-564.

Consider removing the extra blank line:

const newMarkdown = `${preservedBeforeSelectionRef.current}${cleanedResponse}
${preservedAfterSelectionRef.current}`
Suggested change
const newMarkdown = `${preservedBeforeSelectionRef.current}${cleanedResponse}
${preservedAfterSelectionRef.current}`
const newMarkdown = `${preservedBeforeSelectionRef.current}${cleanedResponse}${preservedAfterSelectionRef.current}`

Copilot uses AI. Check for mistakes.
start: number
end: number
} | null>(null)
const currentStreamingContentRef = useRef<string>('')
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentStreamingContentRef is defined and assigned (line 565) but never read or used anywhere in the code. This appears to be dead code that can be removed to improve maintainability.

Suggested change
const currentStreamingContentRef = useRef<string>('')

Copilot uses AI. Check for mistakes.
Comment on lines +422 to +431
const {
documents: legacyUserDocuments,
organizationList,
departmentsByOrg,
projectsByDept,
documentList,
textDocuments,
imageDocuments,
spreadsheetDocuments,
} = useWorkspaceDocuments()
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useWorkspaceDocuments() hook is being called but the WorkspaceDocumentsProvider is not added to the provider hierarchy. This will cause a runtime error because useWorkspaceDocuments() requires its context provider.

The WorkspaceDocumentsProvider should be added to the provider tree (likely in providers.tsx) wrapping or near the WorkspaceProvider, and it should receive the initialState prop with the appropriate document-related data.

Copilot uses AI. Check for mistakes.
Comment on lines +125 to +131
const {
activeOrganization,
activeDepartment,
activeProject,
activeDocument,
setActiveDocument,
} = useWorkspace()
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circular dependency issue: useWorkspaceDocuments (line 5) imports and uses useWorkspace (line 125-131), while useWorkspace imports and uses useWorkspaceDocuments (line 422-431). This creates a circular dependency that could lead to initialization issues or runtime errors.

Consider restructuring so that useWorkspaceDocuments doesn't depend on useWorkspace. You could pass the required values (activeOrganization, activeDepartment, activeProject, activeDocument, setActiveDocument) as parameters to the provider or extract shared state into a third, lower-level context that both can depend on.

Copilot uses AI. Check for mistakes.
useUpdateChatbotActiveState,
} from '@/lib/queries/use-workspace-mutations'
import {
type WorkspaceStructure,
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The imported WorkspaceStructure type is not used anywhere in this file. Consider removing this unused import.

Suggested change
type WorkspaceStructure,

Copilot uses AI. Check for mistakes.
const isLast = index === menuOptions.length - 1

return option.id === 'history' ? (
// Only document-history has history items for now. For media-history we will have a pop-up, different UI/UX for it...
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar issue in comment: "For media-history we will have a pop-up" should be "For media-history, we will have a pop-up" (missing comma after the introductory clause).

Suggested change
// Only document-history has history items for now. For media-history we will have a pop-up, different UI/UX for it...
// Only document-history has history items for now. For media-history, we will have a pop-up, different UI/UX for it...

Copilot uses AI. Check for mistakes.
Comment on lines +375 to +378
const initialSelectionRangeRef = useRef<{
start: number
end: number
} | null>(null)
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initialSelectionRangeRef is declared and assigned (lines 489, 554) but never read anywhere in the code. Consider removing this unused ref to improve code clarity.

Suggested change
const initialSelectionRangeRef = useRef<{
start: number
end: number
} | null>(null)

Copilot uses AI. Check for mistakes.
} from '@/lib/markdown-utils'
import {
makeDocumentKey,
useAutoSaveDocument,
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The imported useAutoSaveDocument is not used anywhere in this file. Consider removing this unused import.

Suggested change
useAutoSaveDocument,

Copilot uses AI. Check for mistakes.
start: number
end: number
} | null>(null)
const [onSectionContentUpdate, setOnSectionContentUpdate] = useState<
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using useState for storing callback functions is problematic. The state setter pattern setOnSectionContentUpdate expects a value, but when you try to set a function, React will treat it as a state updater function. You need to wrap the function in another function like: setOnSectionContentUpdate(() => callback) when setting it.

Consider using useRef instead of useState for storing callback references, or ensure all usages properly wrap the callback: setOnSectionContentUpdate(() => actualCallback).

Suggested change
const [onSectionContentUpdate, setOnSectionContentUpdate] = useState<
const onSectionContentUpdateRef = useRef<

Copilot uses AI. Check for mistakes.
} else if (
selectionRange &&
selectionRange.start === selectionRange.end &&
selectionRange.start > 0
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic issue: Lines 467-476 check if selectionRange exists and if start !== end, then if start === end with additional validation. However, there's a potential issue at line 473 where you check selectionRange.start > 0.

A selection at position 0 (start of section content) is valid, so this condition would incorrectly skip that case. The condition should likely be selectionRange.start >= 0 or the > 0 check should be removed entirely since cursor position at the beginning of content is a valid scenario.

Suggested change
selectionRange.start > 0
selectionRange.start >= 0

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants