Skip to content

feat: add tablet layout with icons#221

Closed
ngoiyaeric wants to merge 3 commits into
mainfrom
fix-tablet-layout
Closed

feat: add tablet layout with icons#221
ngoiyaeric wants to merge 3 commits into
mainfrom
fix-tablet-layout

Conversation

@ngoiyaeric
Copy link
Copy Markdown
Collaborator

@ngoiyaeric ngoiyaeric commented Aug 14, 2025

PR Type

Enhancement


Description

  • Add responsive tablet layout with 50/50 split design

  • Implement device detection for mobile, tablet, and desktop

  • Create dedicated tablet icons bar with horizontal scrolling

  • Refactor layout logic for better responsive behavior


Diagram Walkthrough

flowchart LR
  A["Device Detection"] --> B["Mobile Layout"]
  A --> C["Tablet Layout"]
  A --> D["Desktop Layout"]
  C --> E["Map Section (50%)"]
  C --> F["Chat Section (50%)"]
  E --> G["Icons Bar"]
Loading

File Walkthrough

Relevant files
Enhancement
globals.css
Add tablet-specific CSS layout styles                                       

app/globals.css

  • Add tablet-specific media query for 769px-1024px screens
  • Create 50/50 split layout with map and chat sections
  • Implement horizontal scrolling icons bar with hidden scrollbars
  • Style tablet layout with proper borders and spacing
+50/-0   
chat.tsx
Implement tablet layout and device detection                         

components/chat.tsx

  • Add isTablet state for tablet device detection
  • Refactor device detection logic to handle mobile/tablet/desktop
  • Implement tablet layout rendering with split-screen design
  • Update breakpoints: mobile ≤768px, tablet 769-1024px
+33/-14 

Summary by CodeRabbit

  • New Features

    • Tablet-optimized split view (769–1024px): left side for map/icons, right side for chat.
  • Style

    • Tablet-specific styling: two-column layout, full-height map region, 60px horizontal icons bar with hidden scrollbars, and left border separating chat.
  • Improved Responsive Behavior

    • Enhanced device detection and layout switching on resize for mobile, tablet, and desktop.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Aug 14, 2025

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

Project Deployment Preview Comments Updated (UTC)
qcx Ready Preview Comment Aug 14, 2025 4:37pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 14, 2025

Walkthrough

Adds a tablet-specific breakpoint (769–1024px): CSS implements a two-column tablet layout; chat component gains isTablet detection, a tablet render path that composes map/settings + icons bar + chat, and integrates MapDataProvider/useMapData usage for that path.

Changes

Cohort / File(s) Summary of changes
Tablet responsive CSS
app/globals.css
Adds media query for 769–1024px implementing a two-column tablet layout: left map + fixed-height, horizontally scrollable icons bar; right chat column; 50/50 width split, hidden scrollbars, z-index for controls. (Tablet block duplicated in diff.)
Chat device handling & tablet UI
components/chat.tsx
Introduces isTablet state and checkDevice logic (mobile/tablet/desktop); updates resize listener; adds a tablet render path wrapped with MapDataProvider rendering map/settings, tablet-icons-bar, and chat column; preserves mobile/desktop paths.

Sequence Diagram(s)

sequenceDiagram
  participant Window
  participant Chat
  participant Layout

  Window->>Chat: load / resize event
  Chat->>Chat: checkDevice(width)\nset isMobile/isTablet/desktop
  alt isTablet
    Chat->>Layout: render tablet layout (map + icons bar + chat)
  else isMobile
    Chat->>Layout: render mobile layout
  else
    Chat->>Layout: render desktop layout
  end
Loading
sequenceDiagram
  participant Chat
  participant MapDataProvider
  participant Server as updateDrawingContext

  Chat->>MapDataProvider: useMapData()
  MapDataProvider-->>Chat: drawnFeatures
  Chat->>Chat: effect on drawnFeatures change
  Chat->>Server: updateDrawingContext(chatId, drawnFeatures)
  Server-->>Chat: response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

A rabbit taps a tablet bright,
Two columns bloom beneath soft light.
Maps on left and chat on right,
Icons scroll by, tucked in sight.
Hop — the interface fits just right. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f93151b and ac8178b.

📒 Files selected for processing (2)
  • app/globals.css (1 hunks)
  • components/chat.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/globals.css
  • components/chat.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-tablet-layout

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codiumai-pr-agent-free
Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Component Reuse

The PR uses MobileIconsBar in the tablet layout, but it might need tablet-specific styling or behavior. Verify if this component works correctly in the tablet context or if a dedicated TabletIconsBar would be more appropriate.

  <MobileIconsBar />
</div>
Missing Empty Screen

The tablet layout doesn't handle the showEmptyScreen state that's used in other layouts. This could lead to inconsistent behavior when there are no messages.

if (isTablet) {
  return (
    <MapDataProvider>
      <div className="tablet-layout-container">
        <div className="tablet-map-and-icons-section">
          <div className="tablet-map-section">
            {activeView ? <SettingsView /> : <Mapbox />}
          </div>
          <div className="tablet-icons-bar">
            <MobileIconsBar />
          </div>
        </div>
        <div className="tablet-chat-section">
          <ChatMessages messages={messages} />
          <ChatPanel messages={messages} input={input} setInput={setInput} />
        </div>
      </div>
    </MapDataProvider>
  );
}

@qodo-code-review
Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Resize Logic

The tablet/mobile detection relies solely on window width breakpoints. Validate that these thresholds align with design specs and that rapid resize/orientation changes correctly re-render layouts without flicker or stale state, especially around the 768/769 and 1024 boundaries.

  const checkDevice = () => {
    const width = window.innerWidth;
    setIsMobile(width <= 768);
    setIsTablet(width > 768 && width <= 1024);
  };

  checkDevice();
  window.addEventListener('resize', checkDevice);
  return () => window.removeEventListener('resize', checkDevice);
}, []);
Layout Consistency

In the tablet layout, the icons bar component used is MobileIconsBar. Confirm its visuals/spacing fit the new tablet styles and that it does not pull in mobile-only assumptions (sizes, touch targets) that could misalign with the CSS in the tablet icons bar.

  <div className="tablet-icons-bar">
    <MobileIconsBar />
  </div>
</div>
Scroll/Overflow

Hiding scrollbars on the tablet icons bar can impact discoverability and accessibility. Ensure keyboard/assistive navigation works and that horizontal scrolling does not interfere with map gestures or cause scroll chaining.

.tablet-icons-bar {
  height: 60px;
  background-color: hsl(var(--background));
  border-top: 1px solid hsl(var(--border));
  display: flex;
  align-items: center;
  padding: 0 10px;
  z-index: 20;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; /* Firefox */
}

.tablet-icons-bar::-webkit-scrollbar {
  display: none; /* Chrome, Safari, Edge */
}

@codiumai-pr-agent-free
Copy link
Copy Markdown
Contributor

codiumai-pr-agent-free Bot commented Aug 14, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Debounce resize event handler

Add a debounce mechanism to the resize event handler to prevent excessive state
updates during window resizing, which can cause performance issues and
unnecessary re-renders.

components/chat.tsx [36-40]

 const checkDevice = () => {
   const width = window.innerWidth;
   setIsMobile(width <= 768);
   setIsTablet(width > 768 && width <= 1024);
 };
 
+// Use debounced version for the event listener
+const debouncedCheckDevice = () => {
+  clearTimeout(window.resizeTimer);
+  window.resizeTimer = setTimeout(checkDevice, 250);
+};
+
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential performance issue by calling state setters on every resize event and proposes debouncing, which is a standard and effective optimization.

Medium
Improve scrolling experience

Add padding-bottom to ensure content at the bottom of the chat section isn't cut
off or hidden behind any fixed elements when scrolling, especially on iOS
devices.

app/globals.css [267-275]

 .tablet-chat-section {
   width: 50%;
   display: flex;
   flex-direction: column;
   height: 100vh;
   border-left: 1px solid hsl(var(--border));
   padding: 12px;
+  padding-bottom: 80px;
   overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that the last message in the chat might be obscured by the input panel and proposes adding padding-bottom as a valid fix to ensure all content is visible when scrolled.

Low
  • Update

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Aug 14, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Avoid width-based device detection

The UI switches layouts using raw window width thresholds, which is brittle and
can misclassify devices (e.g., desktop with narrow window, high-DPI tablets,
split-screen). Prefer capability- or container-based responsive design: rely on
CSS media queries and container queries to control layout, and expose layout
state via a single source of truth (e.g., CSS classes or a useMediaQuery hook)
to keep React logic minimal and avoid duplicated breakpoint logic between JS and
CSS.

Examples:

components/chat.tsx [36-45]
    const checkDevice = () => {
      const width = window.innerWidth;
      setIsMobile(width <= 768);
      setIsTablet(width > 768 && width <= 1024);
    };
    
    checkDevice();
    window.addEventListener('resize', checkDevice);
    return () => window.removeEventListener('resize', checkDevice);
  - relevant_file: "components/chat.tsx"

 ... (clipped 24 lines)

Solution Walkthrough:

Before:

// In component:
const [isTablet, setIsTablet] = useState(false);

useEffect(() => {
  const checkDevice = () => {
    const width = window.innerWidth;
    setIsTablet(width > 768 && width <= 1024);
  };
  window.addEventListener('resize', checkDevice);
  checkDevice();
  // ...
}, []);

if (isTablet) {
  return <TabletLayout />;
}
// ...

After:

// Using a custom hook that syncs with CSS media queries:
// const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');

// This removes hardcoded breakpoints from the component logic,
// making it the single source of truth.

// In component:
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');

if (isTablet) {
  return <TabletLayout />;
}
// ...
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical design flaw where breakpoint logic is duplicated and hardcoded in both CSS and JavaScript, leading to maintainability issues and brittle behavior.

High
General
Use dynamic viewport and safe areas

Account for mobile browser UI and safe areas to prevent content being obscured
by notches or dynamic toolbars. Use dynamic viewport units and safe-area insets
instead of fixed 100vh and add overflow containment for iOS.

app/globals.css [229-276]

 @media (min-width: 769px) and (max-width: 1024px) {
   .tablet-layout-container {
     display: flex;
-    height: 100vh;
+    height: 100dvh;
     width: 100%;
     background-color: hsl(var(--background));
+    padding-top: env(safe-area-inset-top);
+    padding-bottom: env(safe-area-inset-bottom);
+    padding-left: env(safe-area-inset-left);
+    padding-right: env(safe-area-inset-right);
+    overflow: hidden;
   }
-...
+
+  .tablet-map-and-icons-section {
+    width: 50%;
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    min-height: 0;
+  }
+
+  .tablet-map-section {
+    flex-grow: 1;
+    background-color: hsl(var(--secondary));
+    z-index: 10;
+    min-height: 0;
+  }
+
+  .tablet-icons-bar {
+    height: 60px;
+    background-color: hsl(var(--background));
+    border-top: 1px solid hsl(var(--border));
+    display: flex;
+    align-items: center;
+    padding: 0 10px;
+    z-index: 20;
+    overflow-x: auto;
+    -webkit-overflow-scrolling: touch;
+    scrollbar-width: none; /* Firefox */
+    flex-shrink: 0;
+  }
+
   .tablet-chat-section {
     width: 50%;
     display: flex;
     flex-direction: column;
-    height: 100vh;
+    height: 100%;
     border-left: 1px solid hsl(var(--border));
     padding: 12px;
     overflow-y: auto;
+    min-height: 0;
   }
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly addresses potential layout issues on mobile devices by using 100dvh and env(safe-area-inset-*), which prevents content from being obscured by browser UI or device notches.

Medium
Throttle resize-driven state updates

Throttle the resize handler to avoid excessive state updates and re-renders
during window resizing. This prevents performance issues and layout jank on
tablets and mobiles when users rotate or resize the viewport.

components/chat.tsx [35-45]

 useEffect(() => {
+  let frame = 0;
   const checkDevice = () => {
     const width = window.innerWidth;
     setIsMobile(width <= 768);
     setIsTablet(width > 768 && width <= 1024);
   };
-  
+  const onResize = () => {
+    if (frame) return;
+    frame = window.requestAnimationFrame(() => {
+      frame = 0;
+      checkDevice();
+    });
+  };
   checkDevice();
-  window.addEventListener('resize', checkDevice);
-  return () => window.removeEventListener('resize', checkDevice);
+  window.addEventListener('resize', onResize);
+  return () => {
+    if (frame) cancelAnimationFrame(frame);
+    window.removeEventListener('resize', onResize);
+  };
 }, []);
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a performance issue by throttling the resize event handler with requestAnimationFrame, which prevents excessive re-renders and improves UI responsiveness.

Medium
  • More

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Aug 14, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ ngoiyaeric
❌ google-labs-jules[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (1)
components/chat.tsx (1)

63-69: Do not import server actions into a client component — stop calling updateDrawingContext directly from components/chat.tsx

Verified: lib/actions/chat.ts exports updateDrawingContext and contains 'use server' (server action), so importing/calling it inside a 'use client' component will fail. The current effect also doesn't await/catch the Promise and skips clearing when features become empty.

Files to fix:

  • components/chat.tsx — current useEffect (lines ~63–69) calls updateDrawingContext from the client.
  • lib/actions/chat.ts — export async function updateDrawingContext(...) with 'use server' (lines ~165–169).

Suggested change (call a server API or move invocation to a Server Component; ensure await, error handling, debounce, and clear-on-empty):

-  useEffect(() => {
-    if (id && mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
-      console.log('Chat.tsx: drawnFeatures changed, calling updateDrawingContext', mapData.drawnFeatures);
-      updateDrawingContext(id, mapData.drawnFeatures);
-    }
-  }, [id, mapData.drawnFeatures]);
+  useEffect(() => {
+    if (!id || !mapData) return;
+    const features = mapData.drawnFeatures ?? [];
+
+    const handle = setTimeout(async () => {
+      try {
+        await fetch('/api/chat/drawing', {
+          method: 'POST',
+          headers: { 'Content-Type': 'application/json' },
+          body: JSON.stringify({ chatId: id, drawnFeatures: features }),
+        });
+      } catch (err) {
+        console.error('Failed to update drawing context', err);
+      }
+    }, 250);
+
+    return () => clearTimeout(handle);
+  }, [id, mapData?.drawnFeatures]);

Action items:

  • Implement a POST API route (e.g., /api/chat/drawing) that calls lib/actions/chat.updateDrawingContext server action, or
  • Move the update call into a Server Component and pass a server action/handler into the client.
  • Ensure empty feature arrays are sent to clear server state, debounce rapid updates, await the call, and handle errors.
🧹 Nitpick comments (4)
app/globals.css (2)

229-276: Use dynamic viewport units to avoid 100vh issues on mobile/tablet browsers.

100vh can cause content to be clipped under the browser chrome (especially iOS Safari). Prefer 100dvh where supported to ensure full-viewport height.

-  .tablet-layout-container {
-    display: flex;
-    height: 100vh;
+  .tablet-layout-container {
+    display: flex;
+    height: 100dvh; /* fallback in a cascaded rule below if needed */
     width: 100%;
     background-color: hsl(var(--background));
   }
 
-  .tablet-map-and-icons-section {
-    width: 50%;
-    display: flex;
-    flex-direction: column;
-    height: 100vh;
+  .tablet-map-and-icons-section {
+    width: 50%;
+    display: flex;
+    flex-direction: column;
+    height: 100dvh;
   }
 
-  .tablet-chat-section {
+  .tablet-chat-section {
     width: 50%;
     display: flex;
     flex-direction: column;
-    height: 100vh;
+    height: 100dvh;
     border-left: 1px solid hsl(var(--border));
     padding: 12px;
     overflow-y: auto;
   }

If you need broader fallback support, keep a preceding 100vh rule and override with 100dvh later.


229-276: Align breakpoints across CSS and TS logic (tablet: 769–1024; mobile: ≤768).

The tablet media query here is 769–1024, which matches the TS breakpoint logic. However, the mobile CSS block above uses max-width: 1024px (not part of this change), while the TS logic treats mobile as ≤768. This drift can confuse future maintenance. Consider updating the mobile CSS breakpoint to max-width: 768px to match the TS logic, or derive classes via Tailwind’s breakpoints to centralize breakpoints.

components/chat.tsx (2)

36-45: Prefer matchMedia over manual innerWidth checks and consider throttling resize.

matchMedia keeps JS detection aligned with CSS queries and avoids tight resize loops. Also, resize can fire frequently—consider throttling to reduce re-renders.

-  useEffect(() => {
-    const checkDevice = () => {
-      const width = window.innerWidth;
-      setIsMobile(width <= 768);
-      setIsTablet(width > 768 && width <= 1024);
-    };
-    checkDevice();
-    window.addEventListener('resize', checkDevice);
-    return () => window.removeEventListener('resize', checkDevice);
-  }, []);
+  useEffect(() => {
+    const mqMobile = window.matchMedia('(max-width: 768px)');
+    const mqTablet = window.matchMedia('(min-width: 769px) and (max-width: 1024px)');
+
+    const update = () => {
+      setIsMobile(mqMobile.matches);
+      setIsTablet(mqTablet.matches);
+    };
+    update();
+
+    // Prefer `change` events on MediaQueryList when available
+    mqMobile.addEventListener?.('change', update);
+    mqTablet.addEventListener?.('change', update);
+
+    // Fallback to resize for older browsers
+    const onResize = () => update();
+    window.addEventListener('resize', onResize, { passive: true });
+
+    return () => {
+      mqMobile.removeEventListener?.('change', update);
+      mqTablet.removeEventListener?.('change', update);
+      window.removeEventListener('resize', onResize);
+    };
+  }, []);

101-121: Minor: consider ChatPanel placement and scroll dynamics on tablet.

You render ChatMessages above ChatPanel for tablet, while on mobile ChatPanel is a fixed input area above messages. Ensure the tablet chat section has sufficient flex/overflow to keep the input visible and maintain expected UX parity, especially with the 60px icons bar.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 475b38c and fb159b0.

📒 Files selected for processing (2)
  • app/globals.css (1 hunks)
  • components/chat.tsx (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
components/chat.tsx (5)
components/map/map-data-context.tsx (2)
  • MapDataProvider (26-34)
  • MapData (7-17)
components/settings/settings-view.tsx (1)
  • SettingsView (8-35)
components/mobile-icons-bar.tsx (1)
  • MobileIconsBar (20-55)
components/chat-panel.tsx (1)
  • ChatPanel (21-187)
app/page.tsx (1)
  • Page (9-18)
🔇 Additional comments (1)
app/globals.css (1)

229-276: Tablet layout structure is coherent and maps cleanly to the JSX.

The 2-column split with a dedicated map + icons stack on the left and chat on the right is easy to reason about. Class naming is consistent with the JSX usage in chat.tsx.

Comment thread components/chat.tsx
Comment thread components/chat.tsx
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
components/chat.tsx (1)

61-69: Do not import and call a Server Action from a Client Component; pass it as a prop (and dedupe calls).

Calling updateDrawingContext directly from a 'use client' component is not supported and will fail the build in Next.js. Also, the effect will spam the server on every intermediate draw event and logs feature data to the console.

Action items:

  • Pass the server action down from a Server Component (e.g., app/page.tsx) as a prop to Chat and invoke it here.
  • Remove the console.log.
  • Dedupe the payload to avoid duplicate writes (optional but recommended).
  • Make the effect null-safe with optional chaining and include proper dependencies.

Apply this diff here:

-  const { mapData } = useMapData();
+  const { mapData } = useMapData();
+  const drawnFeatures = mapData?.drawnFeatures;

   // useEffect to call the server action when drawnFeatures changes
   useEffect(() => {
-    if (id && mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
-      console.log('Chat.tsx: drawnFeatures changed, calling updateDrawingContext', mapData.drawnFeatures);
-      updateDrawingContext(id, mapData.drawnFeatures);
-    }
-  }, [id, mapData.drawnFeatures]);
+    if (!id || !drawnFeatures?.length) return;
+    // Call a server action passed from a Server Component (see page.tsx) to avoid importing server-only code in a Client Component.
+    void onUpdateDrawingContext?.(id, drawnFeatures);
+  }, [id, drawnFeatures, onUpdateDrawingContext]);

Changes outside this hunk (to wire the action properly):

  • Update the props type and usage in this file:

    • Add to ChatProps: onUpdateDrawingContext?: (id: string, drawn: any[]) => Promise
    • Use it as shown in the effect.
  • In app/page.tsx (Server Component), pass the action:

// app/page.tsx (excerpt)
import { updateDrawingContext } from '@/lib/actions/chat'

export default async function Page() {
  const id = nanoid()
  return (
    <MapDataProvider>
      <Chat id={id} onUpdateDrawingContext={updateDrawingContext} />
    </MapDataProvider>
  )
}

Optional hardening:

  • Debounce and/or dedupe the payload to reduce DB churn (e.g., track last payload with a ref and skip if unchanged).
  • Add try/catch around the awaited call for error logging/telemetry.
♻️ Duplicate comments (3)
components/chat.tsx (3)

74-98: Remove nested MapDataProvider — it breaks context synchronization (duplicate of prior review).

Chat uses useMapData above, relying on the page-level provider. Wrapping the mobile subtree in a new MapDataProvider splits the context, so Mapbox/MobileIconsBar write to a different MapData than the effect observes. Tablet path is already fixed; apply the same fix here.

Apply this diff:

-      <MapDataProvider> {/* Add Provider */}
         <div className="mobile-layout-container">
           <div className="mobile-map-section">
             {activeView ? <SettingsView /> : <Mapbox />}
           </div>
           <div className="mobile-icons-bar">
             <MobileIconsBar />
           </div>
           <div className="mobile-chat-input-area">
             <ChatPanel messages={messages} input={input} setInput={setInput} />
           </div>
           <div className="mobile-chat-messages-area">
             {showEmptyScreen ? (
               <EmptyScreen
                 submitMessage={message => {
                   setInput(message)
                 }}
               />
             ) : (
               <ChatMessages messages={messages} />
             )}
           </div>
         </div>
-      </MapDataProvider>

123-139: Remove nested MapDataProvider in desktop branch — keep a single authoritative provider (duplicate of prior review).

Same issue as the mobile branch: nested providers desynchronize context from the effect above.

Apply this diff:

-  return (
-    <MapDataProvider> {/* Add Provider */}
+  return (
       <div className="flex justify-start items-start">
         {/* This is the new div for scrolling */}
         <div className="w-1/2 flex flex-col space-y-3 md:space-y-4 px-8 sm:px-12 pt-12 md:pt-14 pb-4 h-[calc(100vh-0.5in)] overflow-y-auto">
           {/* TODO: Add EmptyScreen for desktop if needed */}
           <ChatMessages messages={messages} />
           <ChatPanel messages={messages} input={input} setInput={setInput} />
         </div>
         <div
           className="w-1/2 p-4 fixed h:[calc(100vh-0.5in)] top-0 right-0 mt-[0.5in]"
           style={{ zIndex: 10 }} // Added z-index
         >
           {activeView ? <SettingsView /> : <Mapbox />}
         </div>
       </div>
-    </MapDataProvider>
-  );
+  );

36-45: Standardize breakpoints via a shared hook/constants; avoid drift with other components (duplicate of prior review).

This file uses ≤768 for mobile and 769–1024 for tablet, while ChatPanel uses ≤1024 as “mobile”. This will cause tablet to render mobile behavior inside ChatPanel. Extract a shared useDevice or breakpoint constants and use them across components.

Minimal drop-in improvement here (optional): use matchMedia listeners instead of resize to avoid layout thrash and simplify logic.

-    const checkDevice = () => {
-      const width = window.innerWidth;
-      setIsMobile(width <= 768);
-      setIsTablet(width > 768 && width <= 1024);
-    };
-
-    checkDevice();
-    window.addEventListener('resize', checkDevice);
-    return () => window.removeEventListener('resize', checkDevice);
+    const MOBILE_MAX = 768;
+    const TABLET_MAX = 1024;
+    const mMobile = window.matchMedia(`(max-width: ${MOBILE_MAX}px)`);
+    const mTablet = window.matchMedia(`(min-width: ${MOBILE_MAX + 1}px) and (max-width: ${TABLET_MAX}px)`);
+    const apply = () => {
+      setIsMobile(mMobile.matches);
+      setIsTablet(mTablet.matches);
+    };
+    apply();
+    mMobile.addEventListener('change', apply);
+    mTablet.addEventListener('change', apply);
+    return () => {
+      mMobile.removeEventListener('change', apply);
+      mTablet.removeEventListener('change', apply);
+    };

Repository-wide follow-up:

  • Create src/lib/breakpoints.ts (MOBILE_MAX = 768, TABLET_MAX = 1024) or hooks/useDevice.ts returning { isMobile, isTablet, isDesktop }.
  • Replace ad-hoc innerWidth checks with the shared source of truth (ChatPanel, mapbox-map, profile-toggle, etc.).

You can verify remaining hard-coded checks with:

#!/bin/bash
# Find width-based checks to unify breakpoints throughout the repo.
rg -n -A2 -i 'matchMedia|innerWidth|isMobile|mobile' components | sed -n '1,200p'
🧹 Nitpick comments (2)
components/chat.tsx (2)

25-26: Avoid initial layout flash on first render.

Initializing isMobile/isTablet to false renders the desktop branch before the effect runs, causing a visible flicker on mobile/tablet. Initialize to null and gate render until computed.

-  const [isMobile, setIsMobile] = useState(false);
-  const [isTablet, setIsTablet] = useState(false);
+  const [isMobile, setIsMobile] = useState<boolean | null>(null);
+  const [isTablet, setIsTablet] = useState<boolean | null>(null);

Then early-return until both are resolved (place this before the conditional branches):

if (isMobile === null || isTablet === null) return null;

101-119: Tablet path looks good; ensure ChatPanel behavior isn’t “mobile” on tablets.

Given ChatPanel currently treats ≤1024 as mobile, it may render the mobile UI in this tablet layout. After standardizing breakpoints, confirm ChatPanel renders the intended tablet-desktop behavior here. Also consider renaming MobileIconsBar to a neutral name if it’s shared on tablet.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fb159b0 and f93151b.

📒 Files selected for processing (1)
  • components/chat.tsx (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
components/chat.tsx (8)
components/settings/settings-view.tsx (1)
  • SettingsView (8-35)
components/map/mapbox-map.tsx (1)
  • Mapbox (17-614)
components/mobile-icons-bar.tsx (1)
  • MobileIconsBar (20-55)
components/chat-messages.tsx (1)
  • ChatMessages (11-70)
lib/db/schema.ts (1)
  • messages (26-37)
components/chat-panel.tsx (1)
  • ChatPanel (21-187)
app/page.tsx (1)
  • Page (9-18)
lib/actions/chat.ts (1)
  • updateDrawingContext (165-205)

The icons in the tablet layout were overflowing and not all visible. This change adjusts the CSS for the tablet icon bar to ensure all icons are visible and correctly aligned.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants