-
Notifications
You must be signed in to change notification settings - Fork 1
Pro web/bugfix create structured markdown | Centralize doc types in thread.types, streaming chat, update UI/docs #573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
There was a problem hiding this 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
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughCentralizes workspace document types into Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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> |
Copilot
AI
Oct 13, 2025
There was a problem hiding this comment.
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'.
// 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> |
Copilot
AI
Oct 13, 2025
There was a problem hiding this comment.
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.
// 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> |
Copilot
AI
Oct 13, 2025
There was a problem hiding this comment.
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.
// 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> |
Copilot
AI
Oct 13, 2025
There was a problem hiding this comment.
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.
// 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> |
Copilot
AI
Oct 13, 2025
There was a problem hiding this comment.
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.
// 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.
There was a problem hiding this 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 strippingSection 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 labelsEach 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()) continueapps/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 truthTypes 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 → WorkspaceDocumentTypeCasting 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 guardAvoid // @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 frameCreating 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 setWhen 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 stateWhen 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 simplificationCurrent 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
📒 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 logicStore 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 sinceactiveDocumentType
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 centralizedWorkspaceDocumentType
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 importedWorkspaceDocumentType
, 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
andWorkspaceDocumentTypeFilter
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 importedWorkspaceDocumentType
, 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 headEnsure the
beforeInteractive
scripts inapps/web/app/layout.tsx
(andapps/pro-web/app/layout.tsx
) actually load before hydration. If they don’t, switch toafterInteractive
orlazyOnload
, 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>
withstrategy="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 toparseMarkdownSections
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 indexingGood 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 goodSwitched to WorkspaceDocumentTypeFilter cleanly; behavior preserved.
Also applies to: 21-22
apps/pro-web/lib/workspace-state.ts (1)
3-5
: Consistent shared types adoptionUsing WorkspaceDocumentType and WorkspaceStatePayload here is correct; upsertDocumentDraft signature aligns.
Also applies to: 83-84
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, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
overrides?.content ?? | ||
(documentContent?.[`${activeProject}:${activeDocument}`] || ''), | ||
type: | ||
(overrides?.type ?? activeDocumentType === 'all') | ||
? 'text' | ||
: activeDocumentType, | ||
activeOrganization, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
There was a problem hiding this 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 unusedviewMode
fromhandleRollback
dependencies
viewMode
isn’t referenced inside thehandleRollback
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
📒 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 logicStore 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
, andsections
with proper dependency tracking improves performance and aligns with the PR objective to use cacheddocumentSections
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
andhandleSectionUpdate
include proper validation, clear logging, and correct integration with the memoized sections. The use ofrequestAnimationFrame
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.
There was a problem hiding this 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
: Addaria-disabled
to convey disabled state to assistive technology.The disabled state is only visual (CSS
pointer-events-none
andcursor-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
andonKeyDown
handlers to checkaria-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
withrole="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 explanationBoth 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
📒 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 logicStore 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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
// 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.
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, | ||
}, | ||
)() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
There was a problem hiding this 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
callssaveDocumentContent
on every keystroke (line 247), which invokessetDocumentContent
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 immediatesetEditableContent
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
📒 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 logicStore 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)
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]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
Summary by CodeRabbit