Skip to content

Conversation

AndlerRL
Copy link
Member

@AndlerRL AndlerRL commented Oct 13, 2025

Summary by CodeRabbit

  • New Features
    • Workspace preserves document sections, adds streaming-driven incremental AI updates for smoother real-time editing, and improves autosave/save flows.
  • Refactor
    • Standardized document-type handling across the app.
    • Translate and analytics scripts now load in the page head for more reliable initialization.
    • Thread popup toggles cleanly between Workspace and Chat; automated "create document from first reply" flow removed.
  • Documentation
    • Added a step-by-step foundational tech planning guide; removed two obsolete transition/operational guides.

@AndlerRL AndlerRL requested a review from Copilot October 13, 2025 20:52
@AndlerRL AndlerRL self-assigned this Oct 13, 2025
Copy link

vercel bot commented Oct 13, 2025

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

Project Deployment Preview Comments Updated (UTC)
masterbots-pro Ready Ready Preview Comment Oct 15, 2025 2:22am
1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
masterbots Skipped Skipped Oct 15, 2025 2:22am

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Sorry @AndlerRL, your pull request is larger than the review limit of 150000 diff characters

Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Centralizes workspace document types into types/thread.types.ts, adds documentSections, refactors streaming-driven workspace chat/update flows and editor APIs, removes the create-from-first-assistant-message flow, moves Translate/Twitter scripts into <head>, and replaces two large planning docs while adding one.

Changes

Cohort / File(s) Summary
Core types added
apps/pro-web/types/thread.types.ts
Adds WorkspaceDocumentType, WorkspaceDocumentTypeFilter, WorkspaceStatePayload; extends WorkspaceDocumentMetadata and WorkspaceDocumentVersion.
API / lib type migration
apps/pro-web/app/actions/thread.actions.ts, apps/pro-web/app/api/workspace/state/route.ts, apps/pro-web/lib/api/documents.ts, apps/pro-web/lib/constants/prompts.ts, apps/pro-web/lib/workspace-state.ts
Replace local literal unions with WorkspaceDocumentType and import WorkspaceStatePayload; update signatures and usages to the new shared types.
Workspace provider & state
apps/pro-web/lib/hooks/use-workspace.tsx, apps/pro-web/lib/workspace-state.ts
Add documentSections state, switch activeDocumentType to WorkspaceDocumentTypeFilter, make addDocument accept WorkspaceDocumentType, add updateWorkspaceStateData, and parse/store sections via parseMarkdownSections.
Workspace content & editor hooks
apps/pro-web/lib/hooks/use-workspace-content-hook.ts, apps/pro-web/lib/hooks/use-workspace-chat.tsx, apps/pro-web/components/routes/workspace/workspace-content.tsx, apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx, apps/pro-web/components/routes/workspace/workspace-content-header.tsx, apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
Streaming-driven refactor: remove setSections and finish-callback model, introduce onStreamingChunk/setOnStreamingChunk, derive sections from documentSections, simplify save/debounce flows, and switch many prop types to WorkspaceDocumentType.
Chat / pro panel integration
apps/pro-web/components/routes/pro/chat-panel-pro.tsx, apps/pro-web/components/routes/chat/chat-message-actions.tsx, apps/pro-web/components/routes/chat/chat-options.tsx
Consume documentSections for meta prompts and cast/accept WorkspaceDocumentType when creating/uploading documents.
Thread UI & popup changes
apps/pro-web/components/routes/thread/thread-popup.tsx, apps/pro-web/components/routes/thread/thread-list.tsx
Align addDocument types to WorkspaceDocumentType; remove automatic create-from-first-assistant-message path; consolidate AnimatePresence rendering between WorkspaceContent and ChatList.
Header & document-type UI
apps/pro-web/components/layout/header/create-document-alert.tsx, apps/pro-web/components/layout/header/crumb-header.tsx, apps/pro-web/components/layout/header/document-type-filter.tsx, apps/pro-web/components/layout/header/header.tsx
Replace local literal unions with WorkspaceDocumentType / WorkspaceDocumentTypeFilter in props and state; update setter/callback types.
Hooks & chat helpers
apps/pro-web/lib/hooks/use-mb-chat.tsx
Use WorkspaceDocumentType for new-document type assertions.
Markdown utils minor refactor
apps/pro-web/lib/markdown-utils.ts
Minor local-variable and JSDoc tweaks in markdown helpers; no behavior change.
Layout script relocation
apps/pro-web/app/layout.tsx, apps/web/app/layout.tsx
Move Google Translate init/script and conditional Twitter pixel into the document <head>.
Workspace section UI
apps/pro-web/components/routes/workspace/workspace-section-item.tsx
Replace <button> with div role="button" to control interactivity via classes/aria; add keyboard handling and disabled styling.
Docs added/removed
apps/pro-web/document-pre-ai-streaming-update-sequence-response.md, apps/pro-web/document-update-ai-streaming-sequence-response.md (deleted), apps/pro-web/document-update-final-result.md (deleted)
Adds a new planning doc; removes two large governance/transition/checklist documents.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as UI (Editor / Chat)
  participant WSC as Workspace Chat
  participant WSP as Workspace Provider
  participant S as Server API

  rect rgba(200,230,255,0.18)
  note over UI,WSC: Streaming-driven update flow
  UI->>WSC: startStreaming(prompt)
  WSC->>S: request AI stream
  loop for each chunk
    S-->>WSC: chunk
    WSC->>WSC: update currentContent / activeSection
    WSC-->>UI: invoke onStreamingChunk (if set)
    WSC->>WSP: setDocumentContent(documentKey, updatedContent)
    WSP->>WSP: parseMarkdownSections(updatedContent)
    WSP->>WSP: update documentSections[documentKey]
  end
  end

  rect rgba(220,255,220,0.18)
  note over UI,WSP: Persist workspace state
  UI->>WSP: updateWorkspaceStateData({ content, type })
  WSP->>S: getWorkspaceState()/upsertDocumentDraft/postWorkspaceState
  S-->>WSP: ack
  WSP-->>UI: resolved
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

bug, frontend

Suggested reviewers

  • max12-max

Poem

A rabbit taps keys with a whiskery grin,
Types hop from strings to a tidy bin.
Sections now sprout where documents grow,
Streams hum like clover in a breezy flow.
Head scripts tucked, I thump—away I go. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The provided title is overly long and attempts to enumerate several distinct changes rather than summarizing the primary intent in a single, clear sentence; it uses a slash and pipe to list multiple feature areas, which reduces readability and obscures the main focus of the pull request. Please shorten the title to a concise sentence that highlights the main change—such as “Centralize workspace document types and refactor structured markdown parsing”—and consider splitting unrelated updates into separate pull requests if needed.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pro-web/bugfix-create-structured-markdown

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.

Copy link

@Copilot 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 focuses on type safety improvements and bugfix optimizations for workspace functionality in the pro-web application. The changes consolidate type definitions, improve code structure, and address issues with document streaming and structured markdown creation.

Key changes:

  • Centralized workspace document type definitions in thread.types.ts
  • Fixed HTML structure issues in layout files
  • Optimized markdown parsing and document state management
  • Streamlined workspace chat functionality and removed redundant code

Reviewed Changes

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

Show a summary per file
File Description
apps/web/app/layout.tsx Fixed improper nested <head> elements in HTML structure
apps/pro-web/types/thread.types.ts Added centralized type definitions for workspace document types and state
apps/pro-web/lib/workspace-state.ts Moved WorkspaceStatePayload type to centralized location and updated imports
apps/pro-web/lib/markdown-utils.ts Minor optimization moving condition check for better performance
apps/pro-web/lib/hooks/use-workspace.tsx Enhanced with document sections caching and improved type safety
apps/pro-web/lib/hooks/use-workspace-content-hook.ts Major refactoring removing redundant state management and optimizing section parsing
apps/pro-web/lib/hooks/use-workspace-chat.tsx Streamlined chat handling with improved streaming logic and removed deprecated functions
Multiple component files Updated to use centralized WorkspaceDocumentType instead of inline string literals
API and action files Updated type imports to use centralized definitions

if (isUserTypingRef.current || userEditInProgressRef.current) {
return
}
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

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

The biome-ignore comment lacks a meaningful explanation. Please provide a specific reason for why exhaustive dependencies are not needed here, such as 'Intentionally omitting fullMarkdown to prevent infinite re-creation of debounced function'.

Suggested change
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
// biome-ignore lint/correctness/useExhaustiveDependencies: Intentionally omitting fullMarkdown to prevent infinite re-creation of debounced function

Copilot uses AI. Check for mistakes.

[setGlobalSelectionRange],
)

// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

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

The biome-ignore comment lacks a meaningful explanation. Please provide a specific reason for why exhaustive dependencies are not needed here.

Suggested change
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
// biome-ignore lint/correctness/useExhaustiveDependencies: Only 'sections' is included because other referenced setters and refs (setActiveSection, setEditableContent, onActiveSectionChange, sectionTextareaRef, setGlobalSelectionRange) are stable and do not change between renders.

Copilot uses AI. Check for mistakes.

[projectName, documentName, sections, fullMarkdown, setDocumentContent],
)

// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

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

The biome-ignore comment lacks a meaningful explanation. Please provide a specific reason for why exhaustive dependencies are not needed here.

Suggested change
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
// biome-ignore lint/correctness/useExhaustiveDependencies: The dependency array intentionally omits setViewMode and setEditableContent because they are stable from React's useState; all other dependencies are included.

Copilot uses AI. Check for mistakes.

updateIndexedItem,
])

// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

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

The biome-ignore comment lacks a meaningful explanation. Please provide a specific reason for why exhaustive dependencies are not needed here.

Suggested change
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
// biome-ignore lint/correctness/useExhaustiveDependencies: All relevant dependencies are included; versions and viewMode are stable and managed outside this scope.

Copilot uses AI. Check for mistakes.

}>({ lineStart: 0, lineCount: 1, lineHeight: 20, paddingTop: 0 })

// Auto-scroll effect (wrap-aware)
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

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

The biome-ignore comment lacks a meaningful explanation. Please provide a specific reason for why exhaustive dependencies are not needed here.

Suggested change
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
// biome-ignore lint/correctness/useExhaustiveDependencies: The dependencies are intentionally limited to avoid unnecessary rerenders; all referenced values are stable or managed outside this effect.

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai bot changed the title Pro web/bugfix create structured markdown | @coderabbitai Pro web/bugfix create structured markdown | Centralize doc types in thread.types, streaming chat, update UI/docs Oct 13, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/pro-web/components/routes/workspace/workspace-text-editor.tsx (1)

114-135: Use section offsets (contentStart/headingStart) instead of indexOf(title)

Relying on title index is brittle with duplicate headings; use the MarkdownSection offsets for correct cursor and scroll behavior.

Apply:

- const calculateAbsolutePosition = useCallback(() => {
-   if (!activeSection || !persistedSelection) return 0
-   const activeSectionData = sections.find((s) => s.id === activeSection)
-   if (!activeSectionData) return 0
-   const sectionStartIndex = fullMarkdown.indexOf(activeSectionData.title)
-   if (sectionStartIndex === -1) return 0
-   const titleEndIndex = fullMarkdown.indexOf('\n', sectionStartIndex)
-   const sectionBodyStart =
-     titleEndIndex !== -1 ? titleEndIndex + 1 : sectionStartIndex
-   const selectionOffset = persistedSelection.start || 0
-   return sectionBodyStart + selectionOffset
- }, [activeSection, persistedSelection, sections, fullMarkdown])
+ const calculateAbsolutePosition = useCallback(() => {
+   if (!activeSection || !persistedSelection) return 0
+   const s = sections.find((x) => x.id === activeSection)
+   if (!s) return 0
+   const selectionOffset = persistedSelection.start || 0
+   return Math.min(s.contentStart + selectionOffset, fullMarkdown.length)
+ }, [activeSection, persistedSelection, sections, fullMarkdown])

And in the auto-scroll effect:

- const activeSectionData = sections.find((s) => s.id === activeSection)
+ const activeSectionData = sections.find((s) => s.id === activeSection)
  if (!activeSectionData) return
- const titleIdx = fullMarkdown.indexOf(activeSectionData.title)
- if (titleIdx === -1) return
- const titleEndIdx = fullMarkdown.indexOf('\n', titleIdx)
- const bodyStart = titleEndIdx === -1 ? titleIdx : titleEndIdx + 1
+ const headingStart = activeSectionData.headingStart ?? 0
+ const bodyStart = activeSectionData.contentStart ?? headingStart
  const selOffset = lastSelectionStartRef.current || 0
  const absolutePosition = Math.min(
    bodyStart + selOffset,
    textarea.value.length,
  )
  const styles = window.getComputedStyle(textarea)
  const lineHeight = Number.parseInt(styles.lineHeight) || 20
  const paddingTop = Number.parseInt(styles.paddingTop) || 0
- const contentBeforeHeading = fullMarkdown.slice(0, titleIdx)
+ const contentBeforeHeading = fullMarkdown.slice(0, headingStart)

Optionally remove or guard the console.log later in this block to avoid prod noise.

Also applies to: 192-231

apps/pro-web/lib/hooks/use-workspace-chat.tsx (1)

260-268: Escape section titles in regex to avoid accidental stripping

Section titles may contain regex metacharacters, causing over/under-matching. Escape before building the pattern.

Apply this diff:

-                const sectionTitleRegex = new RegExp(
-                  `^#{1,6}\\s*${currentSection.title}\\s*\\n?`,
-                  'i',
-                )
+                const escapeRegExp = (s: string) =>
+                  s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+                const sectionTitleRegex = new RegExp(
+                  `^#{1,6}\\s*${escapeRegExp(currentSection.title)}\\s*\\n?`,
+                  'i',
+                )
🧹 Nitpick comments (13)
apps/pro-web/document-pre-ai-streaming-update-sequence-response.md (1)

5-95: Simplify heading labels

Each heading already encodes its level via Markdown syntax, so embedding H1:/H2: in the text is redundant and leaks into generated tables of contents and anchor slugs. Suggest trimming those prefixes so the headings read ## 1. Clarify the idea..., ## 2. Map the customer..., etc.

apps/pro-web/lib/markdown-utils.ts (1)

248-250: Optional: Inline the trim check for brevity.

The intermediate variable partContent is only used for the emptiness check and not referenced elsewhere in the loop iteration. Consider keeping the previous inline approach for simplicity:

-		const partContent = part.trim()
-
-		if (!partContent) continue
+		if (!part.trim()) continue
apps/pro-web/components/routes/pro/chat-panel-pro.tsx (1)

210-226: Remove commented-out debug code.

The commented debug logging code should be removed before merging. If debugging is needed in the future, it can be retrieved from version control.

Apply this diff:

-	// // Add debug logging to track ChatPanelPro re-renders
-	// useEffect(() => {
-	// 	console.log('🔍 ChatPanelPro re-render detected:', {
-	// 		isWorkspaceActive,
-	// 		activeProject,
-	// 		activeDocument,
-	// 		activeDocumentType,
-	// 		hasDocumentContent: !!documentContent,
-	// 		timestamp: Date.now(),
-	// 	})
-	// }, [
-	// 	isWorkspaceActive,
-	// 	activeProject,
-	// 	activeDocument,
-	// 	activeDocumentType,
-	// 	documentContent,
-	// ])
apps/pro-web/lib/api/documents.ts (1)

3-6: Unify allowed document types to a single source of truth

Types are centralized, but runtime validation still hardcodes literals. Prefer an exported const for allowed types to avoid drift.

Add a shared constant and use it here:

- if (!['text', 'image', 'spreadsheet'].includes(document.type)) {
+ import { WORKSPACE_DOCUMENT_TYPES } from '@/types/thread.types'
+ if (!WORKSPACE_DOCUMENT_TYPES.includes(document.type)) {
   return {
     data: null,
     error: 'Document type must be text, image, or spreadsheet.',
   }
 }

Outside this file, in apps/pro-web/types/thread.types.ts:

export const WORKSPACE_DOCUMENT_TYPES = ['text', 'image', 'spreadsheet'] as const
export type WorkspaceDocumentType = (typeof WORKSPACE_DOCUMENT_TYPES)[number]

Also applies to: 12-12, 49-55

apps/pro-web/components/routes/chat/chat-options.tsx (1)

29-29: Avoid casting; use a narrow helper to map filter → WorkspaceDocumentType

Casting can hide type drift. Introduce a mapper to keep this safe and reusable.

Add a helper (e.g., in thread.types.ts):

export function toWorkspaceDocumentType(
  t: WorkspaceDocumentType | 'all',
): WorkspaceDocumentType {
  return t === 'all' ? 'text' : t
}

Then here:

- const docType = activeDocumentType === 'all' ? 'text' : activeDocumentType
- addDocument(activeProject, docTitle, docType as WorkspaceDocumentType)
+ addDocument(
+   activeProject,
+   docTitle,
+   toWorkspaceDocumentType(activeDocumentType),
+ )

Also applies to: 125-127

apps/pro-web/components/routes/thread/thread-popup.tsx (1)

21-21: Replace type assertion with a mapper; rendering refactor looks good

  • Use a toWorkspaceDocumentType helper instead of as WorkspaceDocumentType (same rationale as chat-options).
  • Consolidated AnimatePresence flow and keyed workspace pane look good.

Proposed change for mapping:

- const docType = activeDocumentType === 'all' ? 'text' : activeDocumentType
- addDocument(activeProject, docTitle, docType as WorkspaceDocumentType)
+ addDocument(
+   activeProject,
+   docTitle,
+   toWorkspaceDocumentType(activeDocumentType),
+ )

Also applies to: 132-134

apps/pro-web/lib/hooks/use-workspace-content-hook.ts (1)

134-147: Harden base64 decode and DRY fresh-sections logic

  • Wrap atob in a try/catch to avoid rare decode errors on malformed data URLs.
  • Extract a small getFreshSections() helper to reuse the same logic instead of repeating documentSections?.[key] ?? parseMarkdownSections(fullMarkdown).

Example:

- const baseContent = savedContent?.startsWith('data:text/markdown;base64')
-   ? atob(savedContent.split(',')[1])
-   : (savedContent ?? initialContent)
+ const baseContent =
+   savedContent?.startsWith('data:text/markdown;base64') ?
+     (() => { try { return atob(savedContent.split(',')[1]) } catch { return '' } })() :
+     (savedContent ?? initialContent)

And:

function getFreshSections(
  docSections: Record<string, MarkdownSection[]> | undefined,
  key: string,
  markdown: string,
) {
  return docSections?.[key] ?? parseMarkdownSections(markdown)
}

Then replace occurrences like:

- const cachedSections = documentSections?.[documentKey]
- return cachedSections ?? parseMarkdownSections(fullMarkdown)
+ return getFreshSections(documentSections, documentKey, fullMarkdown)

and similarly in effects where you compute fresh/parsed sections.

Also applies to: 191-198, 507-510

apps/pro-web/lib/hooks/use-workspace-chat.tsx (3)

145-158: Remove ts-ignore; normalize message IDs with a type guard

Avoid // @ts-ignore and unify id/messageId handling to detect the latest assistant message safely.

Apply this diff:

-  const newAssistantMessage = React.useMemo(() => {
-    const lastAssistantMessage = messages
-      ?.filter((msg) => msg.role === 'assistant')
-      .pop()
-
-    return !activeThread?.messages?.find(
-      (msg) =>
-        msg.messageId ===
-        // @ts-ignore
-        (lastAssistantMessage?.id || lastAssistantMessage?.messageId),
-    )
-      ? lastAssistantMessage
-      : undefined
-  }, [messages, activeThread])
+  const newAssistantMessage = React.useMemo(() => {
+    const lastAssistant = [...(messages ?? [])]
+      .reverse()
+      .find((m) => m.role === 'assistant')
+    if (!lastAssistant) return undefined
+    const lastId = getMessageId(lastAssistant)
+    const existsInThread = lastId
+      ? !!activeThread?.messages?.some((m) => m.messageId === lastId)
+      : false
+    return existsInThread ? undefined : lastAssistant
+  }, [messages, activeThread])

Add this helper near the top of the file (outside the component):

function getMessageId(m: Message | UIMessage): string | undefined {
  if (m && typeof (m as any).id === 'string') return (m as any).id
  if (m && typeof (m as any).messageId === 'string') return (m as any).messageId
  return undefined
}

160-185: Hoist debounced workspace-state updater; avoid recreating per frame

Creating debounce inside RAF on each chunk allocates timers repeatedly. Hoist it to a ref/memo and call it, so it’s shared and cancellable.

For example:

// define once
const scheduleWorkspaceStateUpdate = React.useMemo(
  () =>
    debounce(async () => {
      await updateWorkspaceStateData()
      shouldUpdateWorkspaceStateOnStream.current = 0
    }, 5000, { leading: true }),
  [updateWorkspaceStateData],
)

// inside requestAnimationFrame:
shouldUpdateWorkspaceStateOnStream.current = 1
if (shouldUpdateWorkspaceStateOnStream.current && workspaceProcessingState === 'idle') {
  scheduleWorkspaceStateUpdate()
}

392-396: Guard noisy warn when no active section is set

When activeWorkspaceSection is null, this warning triggers every chunk. Consider logging only when an id is set but not found.

-      console.warn(`⚠️ Section with ID "${activeWorkspaceSection}" not found`)
+      if (activeWorkspaceSection) {
+        console.warn(`⚠️ Section with ID "${activeWorkspaceSection}" not found`)
+      }
apps/pro-web/lib/hooks/use-workspace.tsx (3)

321-337: Avoid writing empty project/document keys to server state

When activeProject/activeDocument are null, you pass empty strings, creating keys like ':' server-side. Prefer early return.

-    const nextState = upsertDocumentDraft(serverState, {
-      project: activeProject || '',
-      document: activeDocument || '',
+    if (!activeProject || !activeDocument) {
+      return
+    }
+    const nextState = upsertDocumentDraft(serverState, {
+      project: activeProject,
+      document: activeDocument,

Confirm callers always have both set (e.g., streaming path). If not, guard there as well.


223-226: Eagerly maintain sections; minor setter simplification

Current approach is fine. Optionally simplify by deriving “changed” inside the setter and avoid the outer mutable flag.

-  let shouldUpdateSections = false
   setDocumentContentState((prev) => {
-    if (prev[documentKey] === content) {
-      return prev
-    }
-    shouldUpdateSections = true
-    return { ...prev, [documentKey]: content }
+    if (prev[documentKey] === content) return prev
+    // also update sections in the same pass to keep state in sync
+    setDocumentSections((sPrev) => ({
+      ...sPrev,
+      [documentKey]: parseMarkdownSections(content),
+    }))
+    return { ...prev, [documentKey]: content }
   })
-
-  if (shouldUpdateSections) {
-    setDocumentSections((prev) => ({
-      ...prev,
-      [documentKey]: parseMarkdownSections(content),
-    }))
-  }

Also applies to: 257-279


597-612: Hydration: precompute documentSections from documentContent (optional)

After adopting remote/local state, documentSections remains empty until first edit. Precomputing improves initial UX and avoids on-the-fly parsing on first operation.

// after setDocumentContentState(data.documentContent)
setDocumentSections(
  Object.fromEntries(
    Object.entries(data.documentContent || {}).map(([k, v]) => [k, parseMarkdownSections(v ?? '')]),
  ),
)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51d0853 and f84dec2.

📒 Files selected for processing (29)
  • apps/pro-web/app/actions/thread.actions.ts (2 hunks)
  • apps/pro-web/app/api/workspace/state/route.ts (1 hunks)
  • apps/pro-web/app/layout.tsx (2 hunks)
  • apps/pro-web/components/layout/header/create-document-alert.tsx (2 hunks)
  • apps/pro-web/components/layout/header/crumb-header.tsx (2 hunks)
  • apps/pro-web/components/layout/header/document-type-filter.tsx (2 hunks)
  • apps/pro-web/components/layout/header/header.tsx (2 hunks)
  • apps/pro-web/components/routes/chat/chat-message-actions.tsx (2 hunks)
  • apps/pro-web/components/routes/chat/chat-options.tsx (2 hunks)
  • apps/pro-web/components/routes/pro/chat-panel-pro.tsx (2 hunks)
  • apps/pro-web/components/routes/thread/thread-list.tsx (2 hunks)
  • apps/pro-web/components/routes/thread/thread-popup.tsx (3 hunks)
  • apps/pro-web/components/routes/workspace/workspace-content-header.tsx (1 hunks)
  • apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx (1 hunks)
  • apps/pro-web/components/routes/workspace/workspace-content.tsx (2 hunks)
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx (3 hunks)
  • apps/pro-web/document-pre-ai-streaming-update-sequence-response.md (1 hunks)
  • apps/pro-web/document-update-ai-streaming-sequence-response.md (0 hunks)
  • apps/pro-web/document-update-final-result.md (0 hunks)
  • apps/pro-web/lib/api/documents.ts (1 hunks)
  • apps/pro-web/lib/constants/prompts.ts (2 hunks)
  • apps/pro-web/lib/hooks/use-mb-chat.tsx (2 hunks)
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx (13 hunks)
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts (20 hunks)
  • apps/pro-web/lib/hooks/use-workspace.tsx (10 hunks)
  • apps/pro-web/lib/markdown-utils.ts (1 hunks)
  • apps/pro-web/lib/workspace-state.ts (2 hunks)
  • apps/pro-web/types/thread.types.ts (3 hunks)
  • apps/web/app/layout.tsx (2 hunks)
💤 Files with no reviewable changes (2)
  • apps/pro-web/document-update-final-result.md
  • apps/pro-web/document-update-ai-streaming-sequence-response.md
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Use meaningful variable names; prefix booleans with is/has/does/should
Use interface for objects/classes; type for unions/tuples/aliases; enum for fixed sets; const for literals
Avoid any; prefer specific types
Leverage type inference where clear; annotate function params and return types
Prefer type assertions with 'as' over angle-bracket syntax
Use type guards to narrow types in conditionals

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Use modern JavaScript features with TypeScript across the codebase
Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
Receive an object and return an object (RORO) for functions interfacing with external services
Type definitions: use interface for objects/classes; type for unions/tuples/aliases; const for literals; enum for fixed sets
Avoid any; prefer specific types
Leverage type inference; omit explicit annotations when obvious
Add explicit type annotations for function parameters and return values
Prefer type assertions with as over angle-bracket syntax
Use type guards to narrow types within conditionals
Favor composition over inheritance in code structure

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Prefer RORO (Receive an object, return an object) for function signatures, especially for external service interactions
Use interface for object shapes/classes; type for unions/tuples/aliases; const for literals; enum for fixed enumerations
Avoid using any; prefer precise types
Leverage type inference when clear; omit redundant annotations
Explicitly annotate function parameters and return types
Prefer type assertions with as over angle-bracket syntax
Use type guards to narrow types safely
Use meaningful variable names; prefix booleans with is/has/does/should
Favor composition over inheritance
Use lowercase dash-separated names for files and folders; use extensions like .config.ts, .test.ts, .context.tsx, .typ...

Files:

  • apps/pro-web/lib/markdown-utils.ts
  • apps/pro-web/app/api/workspace/state/route.ts
  • apps/pro-web/lib/api/documents.ts
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
  • apps/pro-web/lib/workspace-state.ts
  • apps/pro-web/lib/constants/prompts.ts
  • apps/pro-web/components/routes/workspace/workspace-content-header.tsx
  • apps/pro-web/app/actions/thread.actions.ts
  • apps/pro-web/components/routes/pro/chat-panel-pro.tsx
  • apps/pro-web/components/routes/workspace/workspace-content.tsx
  • apps/pro-web/components/routes/chat/chat-options.tsx
  • apps/pro-web/components/layout/header/document-type-filter.tsx
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace.tsx
  • apps/pro-web/components/layout/header/header.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx
  • apps/web/app/layout.tsx
  • apps/pro-web/components/layout/header/crumb-header.tsx
  • apps/pro-web/components/routes/thread/thread-list.tsx
  • apps/pro-web/lib/hooks/use-mb-chat.tsx
  • apps/pro-web/components/layout/header/create-document-alert.tsx
  • apps/pro-web/components/routes/chat/chat-message-actions.tsx
  • apps/pro-web/components/routes/thread/thread-popup.tsx
  • apps/pro-web/app/layout.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/types/thread.types.ts
apps/{web,pro-web}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/**/*.{ts,tsx}: Use lowercase kebab-case for directories and filenames (components can omit extension)
Verify DOM operations and consider timing (RAF/delays) when manipulating the DOM in React
Use Tailwind CSS utility classes for styling React components

apps/{web,pro-web}/**/*.{ts,tsx}: Use TypeScript to define React props types (interfaces for props)
Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
Separate presentation from business logic and compose small components
Use hasura.service.ts for all GraphQL operations from UI layers

apps/{web,pro-web}/**/*.{ts,tsx}: Prefer Server Components; use the use client directive only when client-side features are required
Implement Nested Layouts using the App Router
Use Streaming and Suspense in Next.js 14/15 where appropriate
Follow Next.js file conventions as per documentation
Use IndexedDB for local caching to enable immediate UI updates

Files:

  • apps/pro-web/lib/markdown-utils.ts
  • apps/pro-web/app/api/workspace/state/route.ts
  • apps/pro-web/lib/api/documents.ts
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
  • apps/pro-web/lib/workspace-state.ts
  • apps/pro-web/lib/constants/prompts.ts
  • apps/pro-web/components/routes/workspace/workspace-content-header.tsx
  • apps/pro-web/app/actions/thread.actions.ts
  • apps/pro-web/components/routes/pro/chat-panel-pro.tsx
  • apps/pro-web/components/routes/workspace/workspace-content.tsx
  • apps/pro-web/components/routes/chat/chat-options.tsx
  • apps/pro-web/components/layout/header/document-type-filter.tsx
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace.tsx
  • apps/pro-web/components/layout/header/header.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx
  • apps/web/app/layout.tsx
  • apps/pro-web/components/layout/header/crumb-header.tsx
  • apps/pro-web/components/routes/thread/thread-list.tsx
  • apps/pro-web/lib/hooks/use-mb-chat.tsx
  • apps/pro-web/components/layout/header/create-document-alert.tsx
  • apps/pro-web/components/routes/chat/chat-message-actions.tsx
  • apps/pro-web/components/routes/thread/thread-popup.tsx
  • apps/pro-web/app/layout.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/types/thread.types.ts
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

**/*: Use lowercase with dash-separated directory and file names; use specific extensions: .config.ts, .test.ts, .context.tsx, .type.ts, .hook.ts; components may omit extension
Use Bun for package management and scripts instead of npm/yarn
Use Biome for formatting and linting via bun format-and-lint:fix; avoid Prettier/ESLint configs

Files:

  • apps/pro-web/lib/markdown-utils.ts
  • apps/pro-web/app/api/workspace/state/route.ts
  • apps/pro-web/lib/api/documents.ts
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
  • apps/pro-web/lib/workspace-state.ts
  • apps/pro-web/lib/constants/prompts.ts
  • apps/pro-web/components/routes/workspace/workspace-content-header.tsx
  • apps/pro-web/app/actions/thread.actions.ts
  • apps/pro-web/components/routes/pro/chat-panel-pro.tsx
  • apps/pro-web/components/routes/workspace/workspace-content.tsx
  • apps/pro-web/components/routes/chat/chat-options.tsx
  • apps/pro-web/components/layout/header/document-type-filter.tsx
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace.tsx
  • apps/pro-web/components/layout/header/header.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx
  • apps/web/app/layout.tsx
  • apps/pro-web/components/layout/header/crumb-header.tsx
  • apps/pro-web/components/routes/thread/thread-list.tsx
  • apps/pro-web/lib/hooks/use-mb-chat.tsx
  • apps/pro-web/components/layout/header/create-document-alert.tsx
  • apps/pro-web/components/routes/chat/chat-message-actions.tsx
  • apps/pro-web/components/routes/thread/thread-popup.tsx
  • apps/pro-web/document-pre-ai-streaming-update-sequence-response.md
  • apps/pro-web/app/layout.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/types/thread.types.ts
apps/{web,pro-web}/app/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/app/**: Prefer Next.js Server Components; use 'use client' only when necessary
Leverage Streaming and Suspense for responsiveness in Next.js 15
Follow Next.js file conventions in the App Router

apps/{web,pro-web}/app/**: Leverage Server Components by default; use the use client directive only when client-side features are needed
Implement Nested Layouts using the App Router
Use Streaming and Suspense features in Next.js 14/15 where appropriate
Follow Next.js file conventions for routing and layout in the App Router
Optimize data fetching: fetch on the server, fetch where needed, and use preload patterns to avoid waterfalls

Files:

  • apps/pro-web/app/api/workspace/state/route.ts
  • apps/pro-web/app/actions/thread.actions.ts
  • apps/web/app/layout.tsx
  • apps/pro-web/app/layout.tsx
apps/{web,pro-web}/components/routes/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place page-specific components under components/routes/[feature]/

Organize page-specific components under components/routes/

Files:

  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-header.tsx
  • apps/pro-web/components/routes/pro/chat-panel-pro.tsx
  • apps/pro-web/components/routes/workspace/workspace-content.tsx
  • apps/pro-web/components/routes/chat/chat-options.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx
  • apps/pro-web/components/routes/thread/thread-list.tsx
  • apps/pro-web/components/routes/chat/chat-message-actions.tsx
  • apps/pro-web/components/routes/thread/thread-popup.tsx
apps/{web,pro-web}/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/**/*.tsx: Declare React components with the function keyword
Name React components using PascalCase
Order component files: imports → constants → component → styled components → types/interfaces
Use functional components and hooks; type props with a TypeScript interface
Use regular function references (not inline arrows) for React event handlers to avoid extra re-renders

apps/{web,pro-web}/**/*.tsx: Use regular function references for event handlers instead of inline arrow functions in JSX
Declare React components with the function keyword (function ComponentName() {})
Use PascalCase for React component names
Order React component files: imports, constants, component, styled components, types/interfaces
Style components with Tailwind CSS utility classes

Files:

  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-header.tsx
  • apps/pro-web/components/routes/pro/chat-panel-pro.tsx
  • apps/pro-web/components/routes/workspace/workspace-content.tsx
  • apps/pro-web/components/routes/chat/chat-options.tsx
  • apps/pro-web/components/layout/header/document-type-filter.tsx
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace.tsx
  • apps/pro-web/components/layout/header/header.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx
  • apps/web/app/layout.tsx
  • apps/pro-web/components/layout/header/crumb-header.tsx
  • apps/pro-web/components/routes/thread/thread-list.tsx
  • apps/pro-web/lib/hooks/use-mb-chat.tsx
  • apps/pro-web/components/layout/header/create-document-alert.tsx
  • apps/pro-web/components/routes/chat/chat-message-actions.tsx
  • apps/pro-web/components/routes/thread/thread-popup.tsx
  • apps/pro-web/app/layout.tsx
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/*.tsx: Avoid inline arrow functions in JSX props; use stable function references for event handlers
Declare React components with the function keyword (functional components)
Use PascalCase for React component names
Use functional components and hooks instead of class components
Type component props with TypeScript interfaces

Files:

  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-header.tsx
  • apps/pro-web/components/routes/pro/chat-panel-pro.tsx
  • apps/pro-web/components/routes/workspace/workspace-content.tsx
  • apps/pro-web/components/routes/chat/chat-options.tsx
  • apps/pro-web/components/layout/header/document-type-filter.tsx
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace.tsx
  • apps/pro-web/components/layout/header/header.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx
  • apps/web/app/layout.tsx
  • apps/pro-web/components/layout/header/crumb-header.tsx
  • apps/pro-web/components/routes/thread/thread-list.tsx
  • apps/pro-web/lib/hooks/use-mb-chat.tsx
  • apps/pro-web/components/layout/header/create-document-alert.tsx
  • apps/pro-web/components/routes/chat/chat-message-actions.tsx
  • apps/pro-web/components/routes/thread/thread-popup.tsx
  • apps/pro-web/app/layout.tsx
apps/{web,pro-web}/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Order component files: imports → constants → component → styled components → types/interfaces

Files:

  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-header.tsx
  • apps/pro-web/components/routes/pro/chat-panel-pro.tsx
  • apps/pro-web/components/routes/workspace/workspace-content.tsx
  • apps/pro-web/components/routes/chat/chat-options.tsx
  • apps/pro-web/components/layout/header/document-type-filter.tsx
  • apps/pro-web/components/layout/header/header.tsx
  • apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx
  • apps/pro-web/components/layout/header/crumb-header.tsx
  • apps/pro-web/components/routes/thread/thread-list.tsx
  • apps/pro-web/components/layout/header/create-document-alert.tsx
  • apps/pro-web/components/routes/chat/chat-message-actions.tsx
  • apps/pro-web/components/routes/thread/thread-popup.tsx
apps/{web,pro-web}/lib/hooks/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/lib/hooks/**: Place custom React hooks under lib/hooks/
Encapsulate complex state in custom hooks and separate presentation from business logic

Store custom React hooks under lib/hooks/

Files:

  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace.tsx
  • apps/pro-web/lib/hooks/use-mb-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
apps/{web,pro-web}/lib/hooks/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Place custom React hooks in lib/hooks/

Files:

  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace.tsx
  • apps/pro-web/lib/hooks/use-mb-chat.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
apps/{web,pro-web}/app/**/layout.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Implement Nested Layouts via App Router layouts

Files:

  • apps/web/app/layout.tsx
  • apps/pro-web/app/layout.tsx
apps/{web,pro-web}/types/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared TypeScript definitions under types/

Keep TypeScript type definitions under types/

Files:

  • apps/pro-web/types/thread.types.ts
apps/{web,pro-web}/types/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Place shared TypeScript types under types/

Files:

  • apps/pro-web/types/thread.types.ts
🧠 Learnings (1)
📚 Learning: 2025-09-30T22:09:37.969Z
Learnt from: CR
PR: bitcashorg/masterbots#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-30T22:09:37.969Z
Learning: Pro workspace editors: controlled/uncontrolled patterns, Markdown utilities, section state sync, and timed DOM operations

Applied to files:

  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
  • apps/pro-web/lib/hooks/use-workspace.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
🧬 Code graph analysis (22)
apps/pro-web/lib/api/documents.ts (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/lib/workspace-state.ts (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/lib/constants/prompts.ts (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/routes/workspace/workspace-content-header.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/app/actions/thread.actions.ts (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/routes/pro/chat-panel-pro.tsx (3)
apps/pro-web/lib/hooks/use-workspace.tsx (1)
  • useWorkspace (773-779)
apps/pro-web/lib/markdown-utils.ts (1)
  • parseMarkdownSections (57-134)
apps/pro-web/lib/constants/prompts.ts (1)
  • createWorkspaceMetaPrompt (282-300)
apps/pro-web/components/routes/workspace/workspace-content.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/routes/chat/chat-options.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/layout/header/document-type-filter.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentTypeFilter (47-47)
apps/pro-web/lib/hooks/use-workspace-chat.tsx (1)
apps/pro-web/lib/markdown-utils.ts (2)
  • parseMarkdownSections (57-134)
  • replaceSectionContent (156-202)
apps/pro-web/lib/hooks/use-workspace.tsx (3)
apps/pro-web/lib/markdown-utils.ts (2)
  • MarkdownSection (5-16)
  • parseMarkdownSections (57-134)
apps/pro-web/types/thread.types.ts (3)
  • WorkspaceDocumentTypeFilter (47-47)
  • WorkspaceDocumentType (46-46)
  • WorkspaceStatePayload (3-18)
apps/pro-web/lib/workspace-state.ts (3)
  • getWorkspaceState (7-17)
  • upsertDocumentDraft (77-109)
  • postWorkspaceState (59-75)
apps/pro-web/components/layout/header/header.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/web/app/layout.tsx (1)
packages/mb-env/src/config.env.ts (1)
  • appConfig (2-62)
apps/pro-web/components/layout/header/crumb-header.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/routes/thread/thread-list.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/lib/hooks/use-mb-chat.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/layout/header/create-document-alert.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/routes/chat/chat-message-actions.tsx (1)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/routes/thread/thread-popup.tsx (3)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/components/routes/workspace/workspace-content.tsx (1)
  • WorkspaceContent (176-193)
apps/pro-web/components/routes/chat/chat-list/index.tsx (2)
  • ChatList (31-46)
  • ChatList (48-173)
apps/pro-web/app/layout.tsx (1)
packages/mb-env/src/config.env.ts (1)
  • appConfig (2-62)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (3)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/lib/hooks/use-workspace.tsx (1)
  • useWorkspace (773-779)
apps/pro-web/lib/markdown-utils.ts (3)
  • parseMarkdownSections (57-134)
  • replaceSectionContent (156-202)
  • replaceSectionHeading (207-220)
🔇 Additional comments (19)
apps/pro-web/app/api/workspace/state/route.ts (1)

3-3: LGTM! Type centralization improves maintainability.

Importing WorkspaceStatePayload from the centralized type definition file improves consistency and reduces duplication across the codebase.

apps/pro-web/components/routes/chat/chat-message-actions.tsx (1)

20-20: LGTM! Type usage aligns with centralized definition.

The import and usage of WorkspaceDocumentType correctly replaces the inline union type, maintaining consistency with the codebase-wide type refactor. The cast on line 119 is appropriate since activeDocumentType is already mapped from 'all' to 'text' before the cast.

Also applies to: 119-119

apps/pro-web/lib/constants/prompts.ts (1)

8-8: LGTM! Type centralization improves consistency.

The WorkspaceMetaPromptProps.documentType property now uses the centralized WorkspaceDocumentType instead of the inline union, ensuring type consistency across workspace-related operations.

Also applies to: 547-547

apps/pro-web/components/routes/thread/thread-list.tsx (1)

32-32: LGTM! Local type aligns with centralized definition.

The DocMeta type now uses the imported WorkspaceDocumentType, ensuring consistency with the centralized type system and reducing the risk of type drift.

Also applies to: 62-66

apps/pro-web/types/thread.types.ts (2)

3-18: LGTM! Well-structured workspace state payload.

The WorkspaceStatePayload interface provides a comprehensive structure for managing workspace state, including organization hierarchy, document collections by type, and active selections.


46-47: LGTM! Centralized document type definitions.

The WorkspaceDocumentType and WorkspaceDocumentTypeFilter types provide a single source of truth for document types across the codebase. The filter type appropriately extends the base type with an 'all' option for UI filtering scenarios.

apps/pro-web/components/routes/workspace/workspace-content-wrapper.tsx (1)

5-5: LGTM! Props type aligns with centralized definition.

The WorkspaceContentWrapperProps now uses the imported WorkspaceDocumentType, maintaining type consistency while preserving the existing runtime behavior of mapping 'all' to 'text'.

Also applies to: 8-15

apps/web/app/layout.tsx (1)

23-50: Verify Script strategy and placement in App Router layout head

Ensure the beforeInteractive scripts in apps/web/app/layout.tsx (and apps/pro-web/app/layout.tsx) actually load before hydration. If they don’t, switch to afterInteractive or lazyOnload, or inline the <script> directly in <head>.

apps/pro-web/app/actions/thread.actions.ts (1)

8-10: LGTM! Type centralization improves maintainability.

The migration from an inline union type to the centralized WorkspaceDocumentType is a good refactor that improves type consistency across the codebase without changing runtime behavior.

Also applies to: 310-310

apps/pro-web/components/layout/header/crumb-header.tsx (1)

9-9: LGTM! Consistent type usage.

The prop type change aligns with the broader type centralization effort, maintaining the same constraint while improving code maintainability.

Also applies to: 109-109

apps/pro-web/components/layout/header/header.tsx (1)

23-26: LGTM! Type centralization applied correctly.

The state type change from an inline union to WorkspaceDocumentType maintains the same constraints while improving type consistency across the codebase.

Also applies to: 158-159

apps/pro-web/lib/hooks/use-mb-chat.tsx (1)

50-50: LGTM! Type assertion updated consistently.

The type assertion now uses the centralized WorkspaceDocumentType, maintaining the same runtime behavior while improving type consistency.

Also applies to: 403-405

apps/pro-web/app/layout.tsx (1)

20-47: LGTM! Script placement optimized.

Moving the Google Translate and Twitter pixel scripts into the <head> with strategy="beforeInteractive" ensures they load before the page becomes interactive, which is appropriate for these third-party integrations.

apps/pro-web/components/routes/workspace/workspace-content-header.tsx (1)

4-4: LGTM! Type-only import and consistent prop typing.

Using a type-only import for WorkspaceDocumentType is a good practice that helps with tree-shaking, and the prop type change aligns with the type centralization effort.

Also applies to: 9-9

apps/pro-web/components/routes/pro/chat-panel-pro.tsx (1)

202-202: LGTM! Safe fallback for documentSections.

Adding documentSections from the workspace context with a nullish coalescing fallback to parseMarkdownSections is a good defensive pattern. This allows the component to use pre-parsed sections when available while maintaining functionality if they're not yet populated.

Also applies to: 349-358

apps/pro-web/components/routes/workspace/workspace-content.tsx (1)

6-6: LGTM! Consistent type usage with type-only import.

The type-only import and prop type change align with the type centralization effort, improving maintainability without affecting runtime behavior.

Also applies to: 203-203

apps/pro-web/components/layout/header/create-document-alert.tsx (1)

35-35: Props aligned to centralized type; verify templates indexing

Good switch to WorkspaceDocumentType for props. Ensure useWorkspace().templates is typed with keys of WorkspaceDocumentType so templates[documentType] remains type-safe.

Also applies to: 57-57, 59-59

apps/pro-web/components/layout/header/document-type-filter.tsx (1)

10-10: Type unification looks good

Switched to WorkspaceDocumentTypeFilter cleanly; behavior preserved.

Also applies to: 21-22

apps/pro-web/lib/workspace-state.ts (1)

3-5: Consistent shared types adoption

Using WorkspaceDocumentType and WorkspaceStatePayload here is correct; upsertDocumentDraft signature aligns.

Also applies to: 83-84

Comment on lines +207 to 214
const currentContent = documentContent?.[documentKey] ?? ''
console.log('📝 Working with content:', {
usingStreamingRef: !!currentStreamingContentRef.current,
contentLength: workingContent.length,
originalLength: originalSnapshot.length,
isOGDifferent: currentStreamingContentRef.current !== currentContent,
contentLength: currentContent.length,
originalLength: currentContent.length,
streamingChunkLength: aiResponse.length,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix stale-content during streaming: use and update the streaming ref consistently

State reads can lag under rapid chunks. Use currentStreamingContentRef when present, and update it after every content write (not only append). This prevents drift/duplication and ensures offsets align with the working buffer.

Apply these diffs:

@@
-      const currentContent = documentContent?.[documentKey] ?? ''
+      const currentContent =
+        currentStreamingContentRef.current ||
+        documentContent?.[documentKey] ||
+        ''
       console.log('📝 Working with content:', {
-        usingStreamingRef: !!currentStreamingContentRef.current,
-        isOGDifferent: currentStreamingContentRef.current !== currentContent,
-        contentLength: currentContent.length,
-        originalLength: currentContent.length,
+        usingStreamingRef: !!currentStreamingContentRef.current,
+        isOGDifferent: currentStreamingContentRef.current !== currentContent,
+        contentLength: currentContent.length,
+        originalLength: operationOriginalContentRef.current.length,
         streamingChunkLength: aiResponse.length,
       })
@@
       if (activeProject && activeDocument)
         setDocumentContent(activeProject, activeDocument, newMarkdown)
+      // Keep streaming buffer in sync for subsequent chunks
+      currentStreamingContentRef.current = newMarkdown
@@
         if (activeProject && activeDocument) {
           setDocumentContent(
             activeProject,
             activeDocument,
             newContentMarkdown,
           )
         }
+        currentStreamingContentRef.current = newContentMarkdown
@@
         if (activeProject && activeDocument) {
           setDocumentContent(
             activeProject,
             activeDocument,
             newContentMarkdown,
           )
         }
+        currentStreamingContentRef.current = newContentMarkdown

Also applies to: 384-387, 424-431, 462-468

Comment on lines +343 to +349
overrides?.content ??
(documentContent?.[`${activeProject}:${activeDocument}`] || ''),
type:
(overrides?.type ?? activeDocumentType === 'all')
? 'text'
: activeDocumentType,
activeOrganization,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: wrong precedence sets type to 'text' whenever overrides.type exists

The test expression mixes ?? and === without parentheses. If overrides.type is provided, it becomes the test value (a non-empty string), so the ternary always picks 'text'. This corrupts the document type on upsert.

Apply this diff:

-      type:
-        (overrides?.type ?? activeDocumentType === 'all')
-          ? 'text'
-          : activeDocumentType,
+      type:
+        overrides?.type ??
+        (activeDocumentType === 'all'
+          ? 'text'
+          : (activeDocumentType as WorkspaceDocumentType)),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
overrides?.content ??
(documentContent?.[`${activeProject}:${activeDocument}`] || ''),
type:
(overrides?.type ?? activeDocumentType === 'all')
? 'text'
: activeDocumentType,
activeOrganization,
overrides?.content ??
(documentContent?.[`${activeProject}:${activeDocument}`] || ''),
type:
overrides?.type ??
(activeDocumentType === 'all'
? 'text'
: (activeDocumentType as WorkspaceDocumentType)),
activeOrganization,
🤖 Prompt for AI Agents
In apps/pro-web/lib/hooks/use-workspace.tsx around lines 343 to 349, the ternary
and nullish coalescing are grouped incorrectly so any non-empty overrides.type
makes the whole expression truthy and always yields 'text'; change the
expression to first prefer overrides.type and only fall back to resolving
activeDocumentType (i.e. set type to overrides?.type ?? (activeDocumentType ===
'all' ? 'text' : activeDocumentType)) or equivalently use an explicit
conditional: if overrides.type use it, else if activeDocumentType === 'all' use
'text', otherwise use activeDocumentType.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (2)

230-230: Improve biome-ignore comment explanation.

The biome-ignore comment should provide specific reasoning for omitting dependencies. Based on past review feedback and the effect's logic, consider this more detailed explanation:

Apply this diff:

-	// biome-ignore lint/correctness/useExhaustiveDependencies: Need to respond to section changes
+	// biome-ignore lint/correctness/useExhaustiveDependencies: Only sections triggers re-sync. Setters (setEditableContent, setGlobalSelectionRange, setActiveSection) and refs (sectionTextareaRef, onActiveSectionChange) are stable and don't require inclusion per React guarantees.

811-811: Improve biome-ignore comment explanation.

The biome-ignore comment should provide specific reasoning for dependency exclusions. Based on past review feedback and the learnings about React setState stability:

Apply this diff:

-	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
+	// biome-ignore lint/correctness/useExhaustiveDependencies: customSonner and setDocumentContent are stable setState functions that don't require inclusion per React guarantees.
🧹 Nitpick comments (1)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (1)

868-877: Remove unused viewMode from handleRollback dependencies
viewMode isn’t referenced inside the handleRollback callback, so removing it from the dependency array will prevent unnecessary re-creations of the function.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f84dec2 and a7d60e9.

📒 Files selected for processing (1)
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts (19 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
apps/{web,pro-web}/lib/hooks/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/lib/hooks/**: Place custom React hooks under lib/hooks/
Encapsulate complex state in custom hooks and separate presentation from business logic

Store custom React hooks under lib/hooks/

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Use meaningful variable names; prefix booleans with is/has/does/should
Use interface for objects/classes; type for unions/tuples/aliases; enum for fixed sets; const for literals
Avoid any; prefer specific types
Leverage type inference where clear; annotate function params and return types
Prefer type assertions with 'as' over angle-bracket syntax
Use type guards to narrow types in conditionals

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Use modern JavaScript features with TypeScript across the codebase
Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
Receive an object and return an object (RORO) for functions interfacing with external services
Type definitions: use interface for objects/classes; type for unions/tuples/aliases; const for literals; enum for fixed sets
Avoid any; prefer specific types
Leverage type inference; omit explicit annotations when obvious
Add explicit type annotations for function parameters and return values
Prefer type assertions with as over angle-bracket syntax
Use type guards to narrow types within conditionals
Favor composition over inheritance in code structure

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Prefer RORO (Receive an object, return an object) for function signatures, especially for external service interactions
Use interface for object shapes/classes; type for unions/tuples/aliases; const for literals; enum for fixed enumerations
Avoid using any; prefer precise types
Leverage type inference when clear; omit redundant annotations
Explicitly annotate function parameters and return types
Prefer type assertions with as over angle-bracket syntax
Use type guards to narrow types safely
Use meaningful variable names; prefix booleans with is/has/does/should
Favor composition over inheritance
Use lowercase dash-separated names for files and folders; use extensions like .config.ts, .test.ts, .context.tsx, .typ...

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
apps/{web,pro-web}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/**/*.{ts,tsx}: Use lowercase kebab-case for directories and filenames (components can omit extension)
Verify DOM operations and consider timing (RAF/delays) when manipulating the DOM in React
Use Tailwind CSS utility classes for styling React components

apps/{web,pro-web}/**/*.{ts,tsx}: Use TypeScript to define React props types (interfaces for props)
Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
Separate presentation from business logic and compose small components
Use hasura.service.ts for all GraphQL operations from UI layers

apps/{web,pro-web}/**/*.{ts,tsx}: Prefer Server Components; use the use client directive only when client-side features are required
Implement Nested Layouts using the App Router
Use Streaming and Suspense in Next.js 14/15 where appropriate
Follow Next.js file conventions as per documentation
Use IndexedDB for local caching to enable immediate UI updates

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

**/*: Use lowercase with dash-separated directory and file names; use specific extensions: .config.ts, .test.ts, .context.tsx, .type.ts, .hook.ts; components may omit extension
Use Bun for package management and scripts instead of npm/yarn
Use Biome for formatting and linting via bun format-and-lint:fix; avoid Prettier/ESLint configs

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
apps/{web,pro-web}/lib/hooks/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Place custom React hooks in lib/hooks/

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
🧠 Learnings (4)
📚 Learning: 2025-01-14T06:20:03.578Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#337
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:195-205
Timestamp: 2025-01-14T06:20:03.578Z
Learning: React's useState setters (setState functions) maintain referential stability between renders and don't need to be included in useEffect dependency arrays. Include only the state values that the effect actually needs to track.

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2024-11-20T04:00:45.413Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#300
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:120-123
Timestamp: 2024-11-20T04:00:45.413Z
Learning: In React code, when reviewing `useEffect` dependencies, it's not always necessary to add functions like `handleThreadsChange` to the dependency array, as this may lead to unnecessary re-renders and performance issues. Only include dependencies that are essential and may change.

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2025-01-16T20:48:20.108Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#340
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:191-195
Timestamp: 2025-01-16T20:48:20.108Z
Learning: In React's useEffect, it's acceptable to exclude function dependencies from the dependency array when:
1. The function only uses stable references (like setState functions)
2. Including the function could lead to unnecessary re-renders and performance issues

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2025-09-30T22:09:37.969Z
Learnt from: CR
PR: bitcashorg/masterbots#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-30T22:09:37.969Z
Learning: Pro workspace editors: controlled/uncontrolled patterns, Markdown utilities, section state sync, and timed DOM operations

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
🧬 Code graph analysis (1)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (3)
apps/pro-web/types/thread.types.ts (1)
  • WorkspaceDocumentType (46-46)
apps/pro-web/lib/hooks/use-workspace.tsx (1)
  • useWorkspace (773-779)
apps/pro-web/lib/markdown-utils.ts (3)
  • parseMarkdownSections (57-134)
  • replaceSectionContent (156-202)
  • replaceSectionHeading (207-220)
🔇 Additional comments (5)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (5)

134-147: LGTM! Effective memoization strategy.

The memoized computation of savedContent, fullMarkdown, and sections with proper dependency tracking improves performance and aligns with the PR objective to use cached documentSections when available.


295-321: LGTM! Smart debounce strategy with stable ref.

The stable ref pattern prevents stale closures in the debounced function, and the adaptive debounce (0ms during streaming, 300ms with leading execution otherwise) provides good UX balance between responsiveness and performance.


330-346: LGTM! Clean content change handler.

The handler correctly marks user typing, updates content and cursor position, applies section-level updates via replaceSectionContent, and triggers debounced save. The integration with memoized sections is appropriate.


362-409: LGTM! Robust section management handlers.

Both handleSectionClick and handleSectionUpdate include proper validation, clear logging, and correct integration with the memoized sections. The use of requestAnimationFrame for DOM focus operations aligns with coding guidelines.


411-507: LGTM! Comprehensive view mode toggle logic.

The refactored handleViewSourceToggle correctly handles bidirectional transitions with proper state synchronization:

  • Saves section edits before switching to source view
  • Re-parses and loads section content when returning to section view
  • Includes defensive fallbacks for missing sections

The use of saveDocumentContentRef.current prevents stale closures, and the detailed logging aids debugging.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/pro-web/components/routes/workspace/workspace-section-item.tsx (1)

108-138: Add aria-disabled to convey disabled state to assistive technology.

The disabled state is only visual (CSS pointer-events-none and cursor-not-allowed) and is not announced by screen readers. This creates an accessibility barrier for users with assistive technology who won't know when the element is disabled.

Apply this diff to add semantic disabled state:

 		<div
 			// biome-ignore lint/a11y/useSemanticElements: <explanation>
 			role="button"
 			tabIndex={0}
 			className={cn(
 				'w-full group flex items-center gap-1 text-left px-1.5 py-2 rounded-md text-sm transition-colors cursor-pointer',
 				'border-l-2 rounded-l-sm border-transparent',
 				isActiveSectionClassNames,
 				fontLevelClassNames,
 				{
 					'pointer-events-none cursor-not-allowed':
 						isLoading || workspaceProcessingState !== 'idle',
 				},
 			)}
 			style={{
 				marginLeft: `${indentPx}px`,
 				maxWidth: `calc(100% - ${indentPx}px)`,
 			}}
 			onClick={handleSectionClick}
 			onKeyDown={(e) => {
 				if (e.key === 'Enter' || e.key === ' ') {
 					e.preventDefault()
 					handleSectionClick()
 				}
 			}}
 			aria-pressed={isActive}
+			aria-disabled={isLoading || workspaceProcessingState !== 'idle'}
 			onMouseEnter={() => setIsHovered(true)}
 			onMouseLeave={() => setIsHovered(false)}
 			onFocus={() => setIsHovered(true)}
 			onBlur={() => setIsHovered(false)}
 		>

Additionally, update the onClick and onKeyDown handlers to check aria-disabled and prevent interaction:

 			onClick={handleSectionClick}
 			onKeyDown={(e) => {
+				if (isLoading || workspaceProcessingState !== 'idle') return
 				if (e.key === 'Enter' || e.key === ' ') {
 					e.preventDefault()
 					handleSectionClick()
 				}
 			}}
 	const handleSectionClick = () => {
+		if (isLoading || workspaceProcessingState !== 'idle') return
 		onSectionClick(section.id)
 	}
🧹 Nitpick comments (2)
apps/pro-web/components/routes/workspace/workspace-section-item.tsx (1)

108-208: Consider restructuring to maintain native button semantics while avoiding nested interactive elements.

The current implementation uses a div with role="button" to work around the invalid HTML of nested buttons. However, there are better patterns that maintain accessibility:

Option 1: Event delegation pattern
Use a single container with data attributes and event delegation:

<div 
  className="relative group"
  style={{ marginLeft: `${indentPx}px` }}
>
  <button
    onClick={handleSectionClick}
    className={cn(...)}
    disabled={isLoading || workspaceProcessingState !== 'idle'}
    aria-pressed={isActive}
  >
    <span className="w-full flex items-center text-left gap-2 whitespace-nowrap">
      {section.title}
    </span>
  </button>
  
  {/* Position action buttons absolutely outside the button flow */}
  <div className="absolute right-1 top-1/2 -translate-y-1/2 flex gap-1">
    <Button onClick={handleExpandClick} ... />
    <Button onClick={handleRewriteClick} ... />
    <Button onClick={handleEditClick} ... />
  </div>
</div>

Option 2: Side-by-side layout
Place the main button and action buttons as siblings:

<div className="flex items-center gap-2">
  <button
    onClick={handleSectionClick}
    className="flex-1"
    disabled={isLoading || workspaceProcessingState !== 'idle'}
    aria-pressed={isActive}
  >
    {section.title}
  </button>
  <div className="flex gap-1">
    <Button onClick={handleExpandClick} ... />
    {/* other actions */}
  </div>
</div>

Both approaches maintain native button semantics, keyboard navigation, and proper disabled state handling without nested interactive elements.

apps/pro-web/components/routes/workspace/workspace-text-editor.tsx (1)

67-167: Replace placeholder biome-ignore explanation

Both biome-ignore directives (e.g., Line 67 and Line 166) still read // biome-ignore …: <explanation>, which defeats the requirement to document why dependencies are intentionally omitted. Please spell out the real reason (e.g., “sectionTextareaRef and setGlobalSelectionRange are stable refs/setters”) so future maintainers and Biome have the necessary context.

-	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
+	// biome-ignore lint/correctness/useExhaustiveDependencies: sectionTextareaRef and setGlobalSelectionRange are stable; rerunning on every render causes focus loops.

...
-	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
+	// biome-ignore lint/correctness/useExhaustiveDependencies: countDisplayLines is memoized and the referenced refs/setters are stable; expanding deps introduces scroll jitter.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7d60e9 and 6740ef6.

📒 Files selected for processing (5)
  • apps/pro-web/components/routes/workspace/workspace-content.tsx (4 hunks)
  • apps/pro-web/components/routes/workspace/workspace-section-item.tsx (2 hunks)
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx (5 hunks)
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx (16 hunks)
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts (18 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/pro-web/components/routes/workspace/workspace-content.tsx
🧰 Additional context used
📓 Path-based instructions (9)
apps/{web,pro-web}/components/routes/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place page-specific components under components/routes/[feature]/

Organize page-specific components under components/routes/

Files:

  • apps/pro-web/components/routes/workspace/workspace-section-item.tsx
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
apps/{web,pro-web}/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/**/*.tsx: Declare React components with the function keyword
Name React components using PascalCase
Order component files: imports → constants → component → styled components → types/interfaces
Use functional components and hooks; type props with a TypeScript interface
Use regular function references (not inline arrows) for React event handlers to avoid extra re-renders

apps/{web,pro-web}/**/*.tsx: Use regular function references for event handlers instead of inline arrow functions in JSX
Declare React components with the function keyword (function ComponentName() {})
Use PascalCase for React component names
Order React component files: imports, constants, component, styled components, types/interfaces
Style components with Tailwind CSS utility classes

Files:

  • apps/pro-web/components/routes/workspace/workspace-section-item.tsx
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Use meaningful variable names; prefix booleans with is/has/does/should
Use interface for objects/classes; type for unions/tuples/aliases; enum for fixed sets; const for literals
Avoid any; prefer specific types
Leverage type inference where clear; annotate function params and return types
Prefer type assertions with 'as' over angle-bracket syntax
Use type guards to narrow types in conditionals

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Use modern JavaScript features with TypeScript across the codebase
Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
Receive an object and return an object (RORO) for functions interfacing with external services
Type definitions: use interface for objects/classes; type for unions/tuples/aliases; const for literals; enum for fixed sets
Avoid any; prefer specific types
Leverage type inference; omit explicit annotations when obvious
Add explicit type annotations for function parameters and return values
Prefer type assertions with as over angle-bracket syntax
Use type guards to narrow types within conditionals
Favor composition over inheritance in code structure

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Prefer RORO (Receive an object, return an object) for function signatures, especially for external service interactions
Use interface for object shapes/classes; type for unions/tuples/aliases; const for literals; enum for fixed enumerations
Avoid using any; prefer precise types
Leverage type inference when clear; omit redundant annotations
Explicitly annotate function parameters and return types
Prefer type assertions with as over angle-bracket syntax
Use type guards to narrow types safely
Use meaningful variable names; prefix booleans with is/has/does/should
Favor composition over inheritance
Use lowercase dash-separated names for files and folders; use extensions like .config.ts, .test.ts, .context.tsx, .typ...

Files:

  • apps/pro-web/components/routes/workspace/workspace-section-item.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
apps/{web,pro-web}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/**/*.{ts,tsx}: Use lowercase kebab-case for directories and filenames (components can omit extension)
Verify DOM operations and consider timing (RAF/delays) when manipulating the DOM in React
Use Tailwind CSS utility classes for styling React components

apps/{web,pro-web}/**/*.{ts,tsx}: Use TypeScript to define React props types (interfaces for props)
Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
Separate presentation from business logic and compose small components
Use hasura.service.ts for all GraphQL operations from UI layers

apps/{web,pro-web}/**/*.{ts,tsx}: Prefer Server Components; use the use client directive only when client-side features are required
Implement Nested Layouts using the App Router
Use Streaming and Suspense in Next.js 14/15 where appropriate
Follow Next.js file conventions as per documentation
Use IndexedDB for local caching to enable immediate UI updates

Files:

  • apps/pro-web/components/routes/workspace/workspace-section-item.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

**/*: Use lowercase with dash-separated directory and file names; use specific extensions: .config.ts, .test.ts, .context.tsx, .type.ts, .hook.ts; components may omit extension
Use Bun for package management and scripts instead of npm/yarn
Use Biome for formatting and linting via bun format-and-lint:fix; avoid Prettier/ESLint configs

Files:

  • apps/pro-web/components/routes/workspace/workspace-section-item.tsx
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/*.tsx: Avoid inline arrow functions in JSX props; use stable function references for event handlers
Declare React components with the function keyword (functional components)
Use PascalCase for React component names
Use functional components and hooks instead of class components
Type component props with TypeScript interfaces

Files:

  • apps/pro-web/components/routes/workspace/workspace-section-item.tsx
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
apps/{web,pro-web}/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Order component files: imports → constants → component → styled components → types/interfaces

Files:

  • apps/pro-web/components/routes/workspace/workspace-section-item.tsx
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
apps/{web,pro-web}/lib/hooks/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/lib/hooks/**: Place custom React hooks under lib/hooks/
Encapsulate complex state in custom hooks and separate presentation from business logic

Store custom React hooks under lib/hooks/

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
apps/{web,pro-web}/lib/hooks/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Place custom React hooks in lib/hooks/

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/lib/hooks/use-workspace-chat.tsx
🧠 Learnings (4)
📚 Learning: 2025-01-14T06:20:03.578Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#337
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:195-205
Timestamp: 2025-01-14T06:20:03.578Z
Learning: React's useState setters (setState functions) maintain referential stability between renders and don't need to be included in useEffect dependency arrays. Include only the state values that the effect actually needs to track.

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2024-11-20T04:00:45.413Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#300
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:120-123
Timestamp: 2024-11-20T04:00:45.413Z
Learning: In React code, when reviewing `useEffect` dependencies, it's not always necessary to add functions like `handleThreadsChange` to the dependency array, as this may lead to unnecessary re-renders and performance issues. Only include dependencies that are essential and may change.

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2025-01-16T20:48:20.108Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#340
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:191-195
Timestamp: 2025-01-16T20:48:20.108Z
Learning: In React's useEffect, it's acceptable to exclude function dependencies from the dependency array when:
1. The function only uses stable references (like setState functions)
2. Including the function could lead to unnecessary re-renders and performance issues

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2025-09-30T22:09:37.969Z
Learnt from: CR
PR: bitcashorg/masterbots#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-30T22:09:37.969Z
Learning: Pro workspace editors: controlled/uncontrolled patterns, Markdown utilities, section state sync, and timed DOM operations

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
  • apps/pro-web/components/routes/workspace/workspace-text-editor.tsx
🧬 Code graph analysis (4)
apps/pro-web/components/routes/workspace/workspace-section-item.tsx (1)
apps/pro-web/lib/utils.ts (1)
  • cn (9-11)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (3)
apps/pro-web/lib/hooks/use-workspace.tsx (1)
  • useWorkspace (773-779)
apps/pro-web/lib/markdown-utils.ts (4)
  • parseMarkdownSections (57-134)
  • replaceSectionContent (156-202)
  • replaceSectionHeading (207-220)
  • MarkdownSection (5-16)
apps/pro-web/types/thread.types.ts (2)
  • WorkspaceDocumentType (46-46)
  • WorkspaceDocumentVersion (20-27)
apps/pro-web/lib/hooks/use-workspace-chat.tsx (1)
apps/pro-web/lib/markdown-utils.ts (2)
  • parseMarkdownSections (57-134)
  • replaceSectionContent (156-202)
apps/pro-web/components/routes/workspace/workspace-text-editor.tsx (1)
apps/pro-web/lib/markdown-utils.ts (1)
  • MarkdownSection (5-16)

<button
type="button"
<div
// biome-ignore lint/a11y/useSemanticElements: <explanation>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace placeholder text in the biome-ignore comment with actual explanation.

The comment contains placeholder text <explanation> instead of explaining why semantic elements are not being used. This reduces code maintainability and makes it unclear why the rule was suppressed.

Apply this diff to add a meaningful explanation:

-			// biome-ignore lint/a11y/useSemanticElements: <explanation>
+			// biome-ignore lint/a11y/useSemanticElements: Using div to avoid nested button elements (action buttons inside)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// biome-ignore lint/a11y/useSemanticElements: <explanation>
// biome-ignore lint/a11y/useSemanticElements: Using div to avoid nested button elements (action buttons inside)
🤖 Prompt for AI Agents
In apps/pro-web/components/routes/workspace/workspace-section-item.tsx around
line 109, the biome-ignore lint/a11y/useSemanticElements comment uses a
placeholder "<explanation>"—replace it with a concise, actionable rationale for
why a semantic element is not used (e.g., "using non-semantic div because
element requires complex nested interactive controls and custom keyboard
handling; appropriate ARIA roles and keyboard handlers are provided" or "wrapped
by parent landmark that supplies semantics"), and ensure the comment mentions
any accessibility compensations (roles/aria attributes/keyboard handlers) so the
suppression is clear and maintainable.

Comment on lines +116 to +137
const shouldUpdateWorkspaceStateOnStream = React.useRef<0 | 1>(1)
const _updateDocumentSectionStreaming = useAsync(async () => {
console.log('<-- Streaming! newAssistantMessage -->', newAssistantMessage)
if (!newAssistantMessage) return
requestAnimationFrame(async () => {
shouldUpdateWorkspaceStateOnStream.current = 1
if (
shouldUpdateWorkspaceStateOnStream.current &&
workspaceProcessingState === 'idle'
) {
debounce(
async () => {
// Updating the workspace state from streaming every 5 seconds
await updateWorkspaceStateData()
shouldUpdateWorkspaceStateOnStream.current = 0
},
5000,
{
leading: true,
},
)()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix streaming throttle: reuse the debounced callback

Creating the debounced function inside the callback and invoking it immediately (debounce(...)( )) defeats the whole purpose—each chunk builds a brand-new debouncer, so updateWorkspaceStateData() still fires on every chunk. Hoist a single debounced callback (via useRef/useMemo) and call that instead.

-			if (
-				shouldUpdateWorkspaceStateOnStream.current &&
-				workspaceProcessingState === 'idle'
-			) {
-				debounce(
-					async () => {
-						// Updating the workspace state from streaming every 5 seconds
-						await updateWorkspaceStateData()
-						shouldUpdateWorkspaceStateOnStream.current = 0
-					},
-					5000,
-					{
-						leading: true,
-					},
-				)()
-			}
+			if (
+				shouldUpdateWorkspaceStateOnStream.current &&
+				workspaceProcessingState === 'idle'
+			) {
+				scheduleWorkspaceStateRefreshRef.current()
+			}

Add once, near the refs:

+	const scheduleWorkspaceStateRefreshRef = React.useRef(
+		debounce(
+			async () => {
+				await updateWorkspaceStateData()
+				shouldUpdateWorkspaceStateOnStream.current = 0
+			},
+			5000,
+			{ leading: true },
+		),
+	)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const shouldUpdateWorkspaceStateOnStream = React.useRef<0 | 1>(1)
const _updateDocumentSectionStreaming = useAsync(async () => {
console.log('<-- Streaming! newAssistantMessage -->', newAssistantMessage)
if (!newAssistantMessage) return
requestAnimationFrame(async () => {
shouldUpdateWorkspaceStateOnStream.current = 1
if (
shouldUpdateWorkspaceStateOnStream.current &&
workspaceProcessingState === 'idle'
) {
debounce(
async () => {
// Updating the workspace state from streaming every 5 seconds
await updateWorkspaceStateData()
shouldUpdateWorkspaceStateOnStream.current = 0
},
5000,
{
leading: true,
},
)()
}
// Before: hoist refs at the top of the hook
const shouldUpdateWorkspaceStateOnStream = React.useRef<0 | 1>(1)
const scheduleWorkspaceStateRefreshRef = React.useRef(
debounce(
async () => {
await updateWorkspaceStateData()
shouldUpdateWorkspaceStateOnStream.current = 0
},
5000,
{ leading: true },
),
)
// …
const _updateDocumentSectionStreaming = useAsync(async () => {
console.log('<-- Streaming! newAssistantMessage -->', newAssistantMessage)
if (!newAssistantMessage) return
requestAnimationFrame(async () => {
shouldUpdateWorkspaceStateOnStream.current = 1
if (
shouldUpdateWorkspaceStateOnStream.current &&
workspaceProcessingState === 'idle'
) {
- debounce(
- async () => {
- // Updating the workspace state from streaming every 5 seconds
- await updateWorkspaceStateData()
- shouldUpdateWorkspaceStateOnStream.current = 0
- },
- 5000,
- {
- leading: true,
- },
scheduleWorkspaceStateRefreshRef.current()
}
})
})
🤖 Prompt for AI Agents
In apps/pro-web/lib/hooks/use-workspace-chat.tsx around lines 116 to 137, the
code creates a new debounced function on every stream chunk by calling
debounce(... )() inline, so throttling is ineffective; hoist a single debounced
callback out of the streaming handler (use useRef or useMemo to create it once
with the same options, e.g. leading: true, and call that ref'd function from
inside the requestAnimationFrame handler), replace the inline debounce(... )()
call with invoking the persisted debounced function, and ensure you clear/cancel
the debouncer on unmount to avoid leaks.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (5)

148-148: Provide meaningful biome-ignore explanation.

The biome-ignore comment needs a specific reason for omitting exhaustive dependencies, such as explaining that setDocumentContent is stable and intentionally omitted, or that certain dependencies would cause unwanted re-runs.

Based on past review feedback requesting this explanation.


316-316: Provide meaningful biome-ignore explanation.

The biome-ignore comment lacks a specific reason. Per past review feedback, explain why certain dependencies are intentionally omitted, such as stable refs or setters.


367-367: Provide meaningful biome-ignore explanation.

The biome-ignore comment lacks a specific reason for why exhaustive dependencies are not needed. Explain which dependencies are intentionally omitted and why (e.g., stable setters, avoid infinite loops).


394-394: Provide meaningful biome-ignore explanation.

The biome-ignore comment lacks a specific reason for why exhaustive dependencies are not needed. Explain which dependencies are intentionally omitted and why (e.g., stable setters, avoid infinite loops).


651-651: Provide meaningful biome-ignore explanation.

The biome-ignore comment lacks a specific reason for why exhaustive dependencies are not needed. Explain which dependencies are intentionally omitted and why (e.g., stable setters, avoid infinite loops).

🧹 Nitpick comments (2)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (2)

315-315: Resolve or remove TODO comment.

The TODO states "toggle is messing the source along with another useEffect that is triggering section updates as full markdowns." If this issue is resolved, remove the TODO. Otherwise, track it in an issue and provide a clearer explanation of what needs fixing.

Would you like me to generate a script to search for other effects that might be triggering the problematic section updates mentioned in the TODO?


233-250: Consider debouncing document saves.

handleContentChange calls saveDocumentContent on every keystroke (line 247), which invokes setDocumentContent for each character typed. This could cause performance issues with large documents or frequent re-renders in consuming components.

Consider adding debouncing to saveDocumentContent or only saving on blur/explicit save actions while keeping the immediate setEditableContent update for responsive UI.

Verify performance with large documents (>100KB) by monitoring render frequency and save call counts during typing.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6740ef6 and d3575f9.

📒 Files selected for processing (2)
  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts (18 hunks)
  • apps/pro-web/lib/markdown-utils.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/pro-web/lib/markdown-utils.ts
🧰 Additional context used
📓 Path-based instructions (5)
apps/{web,pro-web}/lib/hooks/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/lib/hooks/**: Place custom React hooks under lib/hooks/
Encapsulate complex state in custom hooks and separate presentation from business logic

Store custom React hooks under lib/hooks/

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Use meaningful variable names; prefix booleans with is/has/does/should
Use interface for objects/classes; type for unions/tuples/aliases; enum for fixed sets; const for literals
Avoid any; prefer specific types
Leverage type inference where clear; annotate function params and return types
Prefer type assertions with 'as' over angle-bracket syntax
Use type guards to narrow types in conditionals

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Use modern JavaScript features with TypeScript across the codebase
Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
Receive an object and return an object (RORO) for functions interfacing with external services
Type definitions: use interface for objects/classes; type for unions/tuples/aliases; const for literals; enum for fixed sets
Avoid any; prefer specific types
Leverage type inference; omit explicit annotations when obvious
Add explicit type annotations for function parameters and return values
Prefer type assertions with as over angle-bracket syntax
Use type guards to narrow types within conditionals
Favor composition over inheritance in code structure

**/*.{ts,tsx}: Avoid default exports; prefer named exports
Prefer RORO (Receive an object, return an object) for function signatures, especially for external service interactions
Use interface for object shapes/classes; type for unions/tuples/aliases; const for literals; enum for fixed enumerations
Avoid using any; prefer precise types
Leverage type inference when clear; omit redundant annotations
Explicitly annotate function parameters and return types
Prefer type assertions with as over angle-bracket syntax
Use type guards to narrow types safely
Use meaningful variable names; prefix booleans with is/has/does/should
Favor composition over inheritance
Use lowercase dash-separated names for files and folders; use extensions like .config.ts, .test.ts, .context.tsx, .typ...

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
apps/{web,pro-web}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

apps/{web,pro-web}/**/*.{ts,tsx}: Use lowercase kebab-case for directories and filenames (components can omit extension)
Verify DOM operations and consider timing (RAF/delays) when manipulating the DOM in React
Use Tailwind CSS utility classes for styling React components

apps/{web,pro-web}/**/*.{ts,tsx}: Use TypeScript to define React props types (interfaces for props)
Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
Separate presentation from business logic and compose small components
Use hasura.service.ts for all GraphQL operations from UI layers

apps/{web,pro-web}/**/*.{ts,tsx}: Prefer Server Components; use the use client directive only when client-side features are required
Implement Nested Layouts using the App Router
Use Streaming and Suspense in Next.js 14/15 where appropriate
Follow Next.js file conventions as per documentation
Use IndexedDB for local caching to enable immediate UI updates

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

**/*: Use lowercase with dash-separated directory and file names; use specific extensions: .config.ts, .test.ts, .context.tsx, .type.ts, .hook.ts; components may omit extension
Use Bun for package management and scripts instead of npm/yarn
Use Biome for formatting and linting via bun format-and-lint:fix; avoid Prettier/ESLint configs

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
apps/{web,pro-web}/lib/hooks/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Place custom React hooks in lib/hooks/

Files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
🧠 Learnings (4)
📚 Learning: 2025-01-14T06:20:03.578Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#337
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:195-205
Timestamp: 2025-01-14T06:20:03.578Z
Learning: React's useState setters (setState functions) maintain referential stability between renders and don't need to be included in useEffect dependency arrays. Include only the state values that the effect actually needs to track.

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2024-11-20T04:00:45.413Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#300
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:120-123
Timestamp: 2024-11-20T04:00:45.413Z
Learning: In React code, when reviewing `useEffect` dependencies, it's not always necessary to add functions like `handleThreadsChange` to the dependency array, as this may lead to unnecessary re-renders and performance issues. Only include dependencies that are essential and may change.

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2025-01-16T20:48:20.108Z
Learnt from: AndlerRL
PR: bitcashorg/masterbots#340
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:191-195
Timestamp: 2025-01-16T20:48:20.108Z
Learning: In React's useEffect, it's acceptable to exclude function dependencies from the dependency array when:
1. The function only uses stable references (like setState functions)
2. Including the function could lead to unnecessary re-renders and performance issues

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
📚 Learning: 2025-09-30T22:09:37.969Z
Learnt from: CR
PR: bitcashorg/masterbots#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-30T22:09:37.969Z
Learning: Pro workspace editors: controlled/uncontrolled patterns, Markdown utilities, section state sync, and timed DOM operations

Applied to files:

  • apps/pro-web/lib/hooks/use-workspace-content-hook.ts
🧬 Code graph analysis (1)
apps/pro-web/lib/hooks/use-workspace-content-hook.ts (3)
apps/pro-web/lib/hooks/use-workspace.tsx (1)
  • useWorkspace (773-779)
apps/pro-web/lib/markdown-utils.ts (4)
  • parseMarkdownSections (57-134)
  • replaceSectionContent (158-204)
  • replaceSectionHeading (209-222)
  • MarkdownSection (5-16)
apps/pro-web/types/thread.types.ts (2)
  • WorkspaceDocumentType (46-46)
  • WorkspaceDocumentVersion (20-27)

Comment on lines 227 to +231
useEffect(() => {
return () => {
debouncedStopTyping.cancel()
debouncedSaveDocumentContent.cancel()
debouncedParseMarkdown.cancel()
}
}, [
debouncedStopTyping,
debouncedSaveDocumentContent,
debouncedParseMarkdown,
])

const handleContentChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
markUserTyping()
userEditInProgressRef.current = true
const value = e.target.value
setEditableContent(value)
const position = e.target.selectionStart || 0
const end = e.target.selectionEnd || position
setGlobalSelectionRange({ start: position, end })
}, [debouncedStopTyping])
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate cleanup effect.

Lines 201-205 already handle cleanup for debouncedStopTyping. This duplicate effect serves no purpose.

Apply this diff:

-	useEffect(() => {
-		return () => {
-			debouncedStopTyping.cancel()
-		}
-	}, [debouncedStopTyping])
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
return () => {
debouncedStopTyping.cancel()
debouncedSaveDocumentContent.cancel()
debouncedParseMarkdown.cancel()
}
}, [
debouncedStopTyping,
debouncedSaveDocumentContent,
debouncedParseMarkdown,
])
const handleContentChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
markUserTyping()
userEditInProgressRef.current = true
const value = e.target.value
setEditableContent(value)
const position = e.target.selectionStart || 0
const end = e.target.selectionEnd || position
setGlobalSelectionRange({ start: position, end })
}, [debouncedStopTyping])
- useEffect(() => {
- return () => {
- debouncedStopTyping.cancel()
- }
- }, [debouncedStopTyping])
🤖 Prompt for AI Agents
In apps/pro-web/lib/hooks/use-workspace-content-hook.ts around lines 227 to 231,
there is a duplicate useEffect that only calls debouncedStopTyping.cancel() on
cleanup; lines 201–205 already perform the same cleanup. Remove the redundant
useEffect block (lines 227–231) so only the original cleanup remains, ensuring
no other references to debouncedStopTyping are left unhandled and the hook's
dependency array remains correct.

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.

1 participant