Skip to content

fix(frontend): show 'device deleted' status for tasks with deleted de…#857

Open
FicoHu wants to merge 7 commits intowecode-ai:mainfrom
FicoHu:fix/deleted-device-display-inconsistency
Open

fix(frontend): show 'device deleted' status for tasks with deleted de…#857
FicoHu wants to merge 7 commits intowecode-ai:mainfrom
FicoHu:fix/deleted-device-display-inconsistency

Conversation

@FicoHu
Copy link
Copy Markdown
Collaborator

@FicoHu FicoHu commented Mar 26, 2026

…vices

When a device is deleted, historical chat sessions associated with that device now display a clear "Device Deleted" status instead of incorrectly showing "Public Mode". This provides better UX consistency:

  • DeviceSelectorTab: Shows red "Device Deleted" badge when task's device is deleted
  • Device chat page: Top device selector shows deletion status
  • Chat input is disabled with appropriate hint message

Summary by CodeRabbit

  • New Features

    • Chats detect when the associated device was deleted, show a "Device Deleted" state with an overlay and a primary action to start a new chat.
    • Top/device selector UI hides for deleted-task devices; read-only or unknown-device displays are shown instead.
    • Disabled/offline hints now prioritize deleted-device messaging when applicable.
  • Localization

    • Added English and Chinese translations for deleted-device messaging and "Start New Chat".

…vices

When a device is deleted, historical chat sessions associated with that device
now display a clear "Device Deleted" status instead of incorrectly showing
"Public Mode". This provides better UX consistency:

- DeviceSelectorTab: Shows red "Device Deleted" badge when task's device is deleted
- Device chat page: Top device selector shows deletion status
- Chat input is disabled with appropriate hint message

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

Detects when a task's associated device ID is missing from the loaded devices and surfaces that across chat UIs: hide the device selector, prefer a deleted-device hint for disabling input, show an overlay with a link to start a new chat, and add i18n keys.

Changes

Cohort / File(s) Summary
Task Chat Pages
frontend/src/app/(tasks)/devices/chat/page.tsx, frontend/src/app/(tasks)/chat/ChatPageDesktop.tsx, frontend/src/app/(tasks)/chat/ChatPageMobile.tsx
Derive taskDevice from selectedTaskDetail.device_id; compute isTaskDeviceDeleted / isTaskDeviceOffline; hide device control when deleted and prefer task-device deletion/offline state when computing disabledReason.
Chat UI / Input
frontend/src/features/tasks/components/chat/ChatArea.tsx, frontend/src/features/tasks/components/input/ChatInputCard.tsx
Propagate isTaskDeviceDeleted into input layer; add isTaskDeviceDeleted?: boolean prop and render an absolute overlay with "device deleted" text and a primary button linking to start a new chat when true.
Device Selector
frontend/src/features/tasks/components/input/DeviceSelectorTab.tsx
Add memoized isTaskDeviceDeleted; in read-only mode return null when deleted; render an alternative tooltip/trigger for unresolved taskDeviceId instead of falling back to cloud mode.
Internationalization
frontend/src/i18n/locales/en/devices.json, frontend/src/i18n/locales/zh-CN/devices.json
Add translation keys: device_deleted, device_deleted_hint, and start_new_chat in both English and Chinese locales.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Chat UI
    participant Task as SelectedTaskDetail
    participant Devices as DeviceContext
    participant Router as Router/Paths

    UI->>Task: read selectedTaskDetail.device_id
    UI->>Devices: read current devices list
    Devices-->>UI: devices[]
    alt task device id missing
        UI->>UI: set isTaskDeviceDeleted = true
        UI->>UI: hide device selector
        UI->>UI: show deleted-device overlay / disabled hint
        UI->>Router: link to start new chat (paths.chat.getHref)
    else task device present
        UI->>UI: render device selector / status pill
        UI->>UI: compute disabledReason from device status
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I checked the task, the device has fled,
The selector hides, a gentle message spread.
A button to start fresh — hop, click, and then,
A new chat begins, we’ve bounced back again. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: displaying 'device deleted' status for tasks with deleted devices, which is the primary objective across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


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

❤️ Share

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

Copy link
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/app/`(tasks)/devices/chat/page.tsx:
- Around line 129-133: isTaskDeviceDeleted currently treats an empty devices
array as "deleted" during initial load; update its logic in the useMemo that
defines isTaskDeviceDeleted to also check the DeviceContext loading flag
(isLoading) and return false while devices are still loading. Specifically,
modify the useMemo that references selectedTaskDetail?.device_id and devices so
it references the isLoading value from DeviceContext (or the provider hook) and
short-circuits to false when isLoading is true, then proceed to the existing
devices.some(...) check to determine real deletion status.

In `@frontend/src/features/tasks/components/input/DeviceSelectorTab.tsx`:
- Around line 331-336: The isTaskDeviceDeleted memo in DeviceSelectorTab.tsx can
return a false positive while devices are still loading; update the useMemo
(isTaskDeviceDeleted) to also guard against the devices loading state from
useDevices by returning false when isLoading is true (in addition to the
existing hasMessages/taskDeviceId checks), so the memo only treats a device as
deleted once loading has finished and the devices list is definitive.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 244a7f74-85a0-4f71-becf-e09a3551b1aa

📥 Commits

Reviewing files that changed from the base of the PR and between e308b38 and 3c7b349.

📒 Files selected for processing (4)
  • frontend/src/app/(tasks)/devices/chat/page.tsx
  • frontend/src/features/tasks/components/input/DeviceSelectorTab.tsx
  • frontend/src/i18n/locales/en/devices.json
  • frontend/src/i18n/locales/zh-CN/devices.json

Comment on lines +129 to +133
// Check if task was associated with a device that has been deleted
const isTaskDeviceDeleted = useMemo(() => {
if (!selectedTaskDetail?.device_id) return false
return !devices.some(d => d.device_id === selectedTaskDetail.device_id)
}, [selectedTaskDetail?.device_id, devices])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Race condition: false positive during initial device loading.

The isTaskDeviceDeleted check doesn't account for the loading state of the devices list. According to DeviceContext, devices initializes as an empty array while isLoading is true. This means any task with a device_id will incorrectly show "Device Deleted" status during the initial load before the devices fetch completes.

🐛 Proposed fix: Check `isLoading` before determining deletion status
+  const { devices, selectedDeviceId, setSelectedDeviceId, isLoading: isDevicesLoading } = useDevices()

   // Check if task was associated with a device that has been deleted
   const isTaskDeviceDeleted = useMemo(() => {
+    // Don't report as deleted while devices are still loading
+    if (isDevicesLoading) return false
     if (!selectedTaskDetail?.device_id) return false
     return !devices.some(d => d.device_id === selectedTaskDetail.device_id)
-  }, [selectedTaskDetail?.device_id, devices])
+  }, [selectedTaskDetail?.device_id, devices, isDevicesLoading])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/`(tasks)/devices/chat/page.tsx around lines 129 - 133,
isTaskDeviceDeleted currently treats an empty devices array as "deleted" during
initial load; update its logic in the useMemo that defines isTaskDeviceDeleted
to also check the DeviceContext loading flag (isLoading) and return false while
devices are still loading. Specifically, modify the useMemo that references
selectedTaskDetail?.device_id and devices so it references the isLoading value
from DeviceContext (or the provider hook) and short-circuits to false when
isLoading is true, then proceed to the existing devices.some(...) check to
determine real deletion status.

Comment on lines +331 to +336
// Check if the task was associated with a device that has been deleted
const isTaskDeviceDeleted = useMemo(() => {
if (!hasMessages || !taskDeviceId) return false
// If taskDeviceId exists but device not found in devices list, it means the device was deleted
return !devices.some(device => device.device_id === taskDeviceId)
}, [hasMessages, taskDeviceId, devices])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same race condition as in page.tsx: false positive during device loading.

This memo has the same issue as the one in page.tsx. When devices is still loading (empty array), tasks with a device_id will incorrectly appear as having a deleted device. The isLoading state from useDevices should be checked.

🐛 Proposed fix: Guard against loading state
-  const { devices, selectedDeviceId, setSelectedDeviceId, isLoading } = useDevices()
+  const { devices, selectedDeviceId, setSelectedDeviceId, isLoading: isDevicesLoading } = useDevices()

   // Check if the task was associated with a device that has been deleted
   const isTaskDeviceDeleted = useMemo(() => {
+    // Don't report as deleted while devices are still loading
+    if (isDevicesLoading) return false
     if (!hasMessages || !taskDeviceId) return false
     // If taskDeviceId exists but device not found in devices list, it means the device was deleted
     return !devices.some(device => device.device_id === taskDeviceId)
-  }, [hasMessages, taskDeviceId, devices])
+  }, [hasMessages, taskDeviceId, devices, isDevicesLoading])

Note: isLoading is already destructured on line 230, so you can use it directly instead of renaming:

   const isTaskDeviceDeleted = useMemo(() => {
+    if (isLoading) return false
     if (!hasMessages || !taskDeviceId) return false
     return !devices.some(device => device.device_id === taskDeviceId)
-  }, [hasMessages, taskDeviceId, devices])
+  }, [hasMessages, taskDeviceId, devices, isLoading])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/components/input/DeviceSelectorTab.tsx` around
lines 331 - 336, The isTaskDeviceDeleted memo in DeviceSelectorTab.tsx can
return a false positive while devices are still loading; update the useMemo
(isTaskDeviceDeleted) to also guard against the devices loading state from
useDevices by returning false when isLoading is true (in addition to the
existing hasMessages/taskDeviceId checks), so the memo only treats a device as
deleted once loading has finished and the devices list is definitive.

yunpeng7 and others added 5 commits March 26, 2026 18:53
- Hide the device selector in top navigation when task's device is deleted
- Add "Start New Chat" link next to "Device Deleted" badge in input area
- Users can now easily create a new conversation when viewing deleted device tasks

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Remove device deleted badge from DeviceSelectorTab (return null when deleted)
- Add clickable overlay in ChatInputCard when task's device is deleted
- Show centered "Device deleted" message with "Start New Chat" button
- Cleaner UI that follows Calm UI design principles

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- For existing tasks with device_id, show the device name and status
  even if the device is offline (read-only display)
- Only show device selector dropdown for new tasks
- Correctly handle disabledReason when task's device is offline

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Check task's device_id when computing disabledReason, not just selectedDeviceId
- Show "device deleted" hint when task's device no longer exists
- Show "device offline" hint when task's device is offline
- Applies to both ChatPageDesktop and ChatPageMobile

Co-Authored-By: Claude Opus 4.5 <[email protected]>
When a task has a device_id but the device is not in the current devices
list (loading or deleted), show a placeholder with offline indicator
instead of misleading "Public Mode" text.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
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: 3

🧹 Nitpick comments (1)
frontend/src/features/tasks/components/input/ChatInputCard.tsx (1)

269-281: Consider accessibility for the overlay.

The overlay blocks interaction with the underlying input but doesn't trap focus or announce itself to screen readers. Users relying on assistive technology may not understand why the input is inaccessible.

Consider adding role="alertdialog" and aria-modal="true" to the overlay container, and ensure the "Start New Chat" link receives focus when the overlay appears.

♿ Suggested accessibility improvement
         {/* Device Deleted Overlay - shows clickable prompt when task's device is deleted */}
         {isTaskDeviceDeleted && (
-          <div className="absolute inset-0 z-40 rounded-3xl bg-base/95 backdrop-blur-sm flex flex-col items-center justify-center transition-all">
+          <div
+            role="alertdialog"
+            aria-modal="true"
+            aria-label={t('devices:device_deleted_hint')}
+            className="absolute inset-0 z-40 rounded-3xl bg-base/95 backdrop-blur-sm flex flex-col items-center justify-center transition-all"
+          >
             <p className="text-sm text-text-muted mb-3">{t('devices:device_deleted_hint')}</p>
             <Link
               href={paths.chat.getHref()}
               className="flex items-center gap-1.5 px-4 py-2 rounded-full bg-primary text-white text-sm font-medium hover:bg-primary/90 transition-colors"
             >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/components/input/ChatInputCard.tsx` around lines
269 - 281, The overlay rendered when isTaskDeviceDeleted in ChatInputCard isn't
accessible: add role="alertdialog" and aria-modal="true" to the overlay
container element and ensure the "Start New Chat" Link receives focus when the
overlay appears; implement a ref for the Link (or a wrapper focusable element)
and use an effect that runs when isTaskDeviceDeleted becomes true to call
focus(), and consider adding simple focus trapping within the overlay while it's
visible so keyboard users cannot tab into the underlying input.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/app/`(tasks)/chat/ChatPageDesktop.tsx:
- Around line 62-69: The isTaskDeviceDeleted check can return a false positive
while devices are loading; update the logic to extract the loading flag from
useDevices() (e.g., isLoading or similar) and only compute isTaskDeviceDeleted
when loading is false and selectedTaskDetail?.device_id exists, otherwise treat
it as not deleted; keep taskDevice and isTaskDeviceOffline computations the same
but ensure any UI that shows "Device Deleted" depends on the guarded
isTaskDeviceDeleted.

In `@frontend/src/app/`(tasks)/chat/ChatPageMobile.tsx:
- Around line 51-58: The isTaskDeviceDeleted computation can return a false
positive while devices are still loading; update ChatPageMobile to extract the
isLoading flag from useDevices() (same hook used to get devices) and guard the
deletion check so that isTaskDeviceDeleted is false while isLoading is true;
specifically modify the logic around selectedTaskDetail, devices and the
isTaskDeviceDeleted variable to include !isLoading &&
selectedTaskDetail?.device_id && !devices.some(...), matching the pattern used
in ChatPageDesktop.

In `@frontend/src/features/tasks/components/chat/ChatArea.tsx`:
- Around line 116-123: The isTaskDeviceDeleted calculation can return a false
positive while devices are still loading; update the useDevices() call to also
extract isLoading (e.g., const { devices, isLoading } = useDevices()) and modify
the useMemo for isTaskDeviceDeleted to return false when isLoading is true (or
when devices is undefined/null), keeping the existing checks on
selectedTaskDetail?.device_id and the devices.some(...) logic otherwise; this
ensures the useMemo (isTaskDeviceDeleted) only reports deletion after devices
have finished loading.

---

Nitpick comments:
In `@frontend/src/features/tasks/components/input/ChatInputCard.tsx`:
- Around line 269-281: The overlay rendered when isTaskDeviceDeleted in
ChatInputCard isn't accessible: add role="alertdialog" and aria-modal="true" to
the overlay container element and ensure the "Start New Chat" Link receives
focus when the overlay appears; implement a ref for the Link (or a wrapper
focusable element) and use an effect that runs when isTaskDeviceDeleted becomes
true to call focus(), and consider adding simple focus trapping within the
overlay while it's visible so keyboard users cannot tab into the underlying
input.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ab60539-8c74-46c4-867c-498f609732f4

📥 Commits

Reviewing files that changed from the base of the PR and between 3c7b349 and 80df098.

📒 Files selected for processing (8)
  • frontend/src/app/(tasks)/chat/ChatPageDesktop.tsx
  • frontend/src/app/(tasks)/chat/ChatPageMobile.tsx
  • frontend/src/app/(tasks)/devices/chat/page.tsx
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/input/ChatInputCard.tsx
  • frontend/src/features/tasks/components/input/DeviceSelectorTab.tsx
  • frontend/src/i18n/locales/en/devices.json
  • frontend/src/i18n/locales/zh-CN/devices.json
✅ Files skipped from review due to trivial changes (2)
  • frontend/src/i18n/locales/zh-CN/devices.json
  • frontend/src/i18n/locales/en/devices.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/features/tasks/components/input/DeviceSelectorTab.tsx

Comment on lines +62 to +69
// For existing tasks, also check the task's device_id
const taskDevice = selectedTaskDetail?.device_id
? devices.find(d => d.device_id === selectedTaskDetail.device_id)
: null
const isTaskDeviceDeleted =
selectedTaskDetail?.device_id &&
!devices.some(d => d.device_id === selectedTaskDetail.device_id)
const isTaskDeviceOffline = taskDevice?.status === 'offline'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Race condition: false positive during initial device loading.

The isTaskDeviceDeleted check doesn't account for the devices loading state. When devices initializes as an empty array while fetching, any task with a device_id will incorrectly show "Device Deleted" status until the fetch completes.

This is the same issue identified in frontend/src/app/(tasks)/devices/chat/page.tsx. Consider extracting isLoading from useDevices() and guarding against it.

🐛 Proposed fix: Check loading state before determining deletion status
   // Device context - when a device is selected, switch to 'task' mode
-  const { selectedDeviceId, devices } = useDevices()
+  const { selectedDeviceId, devices, isLoading: isDevicesLoading } = useDevices()
   const selectedDevice = devices.find(d => d.device_id === selectedDeviceId)

   // For existing tasks, also check the task's device_id
   const taskDevice = selectedTaskDetail?.device_id
     ? devices.find(d => d.device_id === selectedTaskDetail.device_id)
     : null
   const isTaskDeviceDeleted =
+    !isDevicesLoading &&
     selectedTaskDetail?.device_id &&
     !devices.some(d => d.device_id === selectedTaskDetail.device_id)
   const isTaskDeviceOffline = taskDevice?.status === 'offline'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/`(tasks)/chat/ChatPageDesktop.tsx around lines 62 - 69, The
isTaskDeviceDeleted check can return a false positive while devices are loading;
update the logic to extract the loading flag from useDevices() (e.g., isLoading
or similar) and only compute isTaskDeviceDeleted when loading is false and
selectedTaskDetail?.device_id exists, otherwise treat it as not deleted; keep
taskDevice and isTaskDeviceOffline computations the same but ensure any UI that
shows "Device Deleted" depends on the guarded isTaskDeviceDeleted.

Comment on lines +51 to +58
// For existing tasks, also check the task's device_id
const taskDevice = selectedTaskDetail?.device_id
? devices.find(d => d.device_id === selectedTaskDetail.device_id)
: null
const isTaskDeviceDeleted =
selectedTaskDetail?.device_id &&
!devices.some(d => d.device_id === selectedTaskDetail.device_id)
const isTaskDeviceOffline = taskDevice?.status === 'offline'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Race condition: false positive during initial device loading.

Same issue as ChatPageDesktop.tsx: the isTaskDeviceDeleted check doesn't account for the devices loading state. Extract isLoading from useDevices() and guard against it.

🐛 Proposed fix: Check loading state before determining deletion status
   // Device context - when a device is selected, switch to 'task' mode
-  const { selectedDeviceId, devices } = useDevices()
+  const { selectedDeviceId, devices, isLoading: isDevicesLoading } = useDevices()
   const selectedDevice = devices.find(d => d.device_id === selectedDeviceId)

   // For existing tasks, also check the task's device_id
   const taskDevice = selectedTaskDetail?.device_id
     ? devices.find(d => d.device_id === selectedTaskDetail.device_id)
     : null
   const isTaskDeviceDeleted =
+    !isDevicesLoading &&
     selectedTaskDetail?.device_id &&
     !devices.some(d => d.device_id === selectedTaskDetail.device_id)
   const isTaskDeviceOffline = taskDevice?.status === 'offline'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/`(tasks)/chat/ChatPageMobile.tsx around lines 51 - 58, The
isTaskDeviceDeleted computation can return a false positive while devices are
still loading; update ChatPageMobile to extract the isLoading flag from
useDevices() (same hook used to get devices) and guard the deletion check so
that isTaskDeviceDeleted is false while isLoading is true; specifically modify
the logic around selectedTaskDetail, devices and the isTaskDeviceDeleted
variable to include !isLoading && selectedTaskDetail?.device_id &&
!devices.some(...), matching the pattern used in ChatPageDesktop.

Comment on lines +116 to +123
// Device context - for detecting deleted device
const { devices } = useDevices()

// Check if task was associated with a device that has been deleted
const isTaskDeviceDeleted = useMemo(() => {
if (!selectedTaskDetail?.device_id) return false
return !devices.some(d => d.device_id === selectedTaskDetail.device_id)
}, [selectedTaskDetail?.device_id, devices])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Race condition: false positive during initial device loading.

Same issue as in the page components: when devices is still loading (empty array), any task with a device_id will incorrectly report as deleted. Extract isLoading from useDevices() and check it in the useMemo.

🐛 Proposed fix: Guard against loading state
   // Device context - for detecting deleted device
-  const { devices } = useDevices()
+  const { devices, isLoading: isDevicesLoading } = useDevices()

   // Check if task was associated with a device that has been deleted
   const isTaskDeviceDeleted = useMemo(() => {
+    // Don't report as deleted while devices are still loading
+    if (isDevicesLoading) return false
     if (!selectedTaskDetail?.device_id) return false
     return !devices.some(d => d.device_id === selectedTaskDetail.device_id)
-  }, [selectedTaskDetail?.device_id, devices])
+  }, [selectedTaskDetail?.device_id, devices, isDevicesLoading])
📝 Committable suggestion

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

Suggested change
// Device context - for detecting deleted device
const { devices } = useDevices()
// Check if task was associated with a device that has been deleted
const isTaskDeviceDeleted = useMemo(() => {
if (!selectedTaskDetail?.device_id) return false
return !devices.some(d => d.device_id === selectedTaskDetail.device_id)
}, [selectedTaskDetail?.device_id, devices])
// Device context - for detecting deleted device
const { devices, isLoading: isDevicesLoading } = useDevices()
// Check if task was associated with a device that has been deleted
const isTaskDeviceDeleted = useMemo(() => {
// Don't report as deleted while devices are still loading
if (isDevicesLoading) return false
if (!selectedTaskDetail?.device_id) return false
return !devices.some(d => d.device_id === selectedTaskDetail.device_id)
}, [selectedTaskDetail?.device_id, devices, isDevicesLoading])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/components/chat/ChatArea.tsx` around lines 116 -
123, The isTaskDeviceDeleted calculation can return a false positive while
devices are still loading; update the useDevices() call to also extract
isLoading (e.g., const { devices, isLoading } = useDevices()) and modify the
useMemo for isTaskDeviceDeleted to return false when isLoading is true (or when
devices is undefined/null), keeping the existing checks on
selectedTaskDetail?.device_id and the devices.some(...) logic otherwise; this
ensures the useMemo (isTaskDeviceDeleted) only reports deletion after devices
have finished loading.

When viewing a historical task that was created with cloud mode
(device_id=null) on the /devices/chat page, the input should not be
disabled. Previously, if no device was selected globally, it would
show "device offline" error even for cloud mode tasks.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
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.

🧹 Nitpick comments (2)
frontend/src/app/(tasks)/devices/chat/page.tsx (2)

206-225: Consider adding accessibility attributes to the device selector.

The <select> element lacks an id attribute and associated <label> for screen reader users. Adding these would improve accessibility.

♿ Proposed accessibility improvement
+              <label htmlFor="device-selector" className="sr-only">
+                {t('select_device')}
+              </label>
               <select
+                id="device-selector"
                 value={selectedDeviceId || ''}
                 onChange={e => handleDeviceSelect(e.target.value)}
                 className="bg-surface border border-border rounded-md px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-primary/20"
+                aria-label={t('select_device')}
               >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/`(tasks)/devices/chat/page.tsx around lines 206 - 225, The
device <select> lacks an accessible label; add an <label> element tied to the
select via an id and htmlFor (or add a stable id on the select and use
aria-labelledby/aria-label) so screen readers announce the control; update the
select (currently using selectedDeviceId and handleDeviceSelect and rendering
devices) to include an id (e.g., device-select) and add a visible or
visually-hidden <label> with the translated text (t('select_device')) to
associate with it.

130-131: Comment doesn't match the implementation.

The comment states "non-empty title and was created" but the code only checks for selectedTaskDetail.id. Consider updating the comment to accurately reflect the logic.

📝 Suggested comment fix
-  // Task is considered to have messages if it has a non-empty title and was created
+  // Task is considered to have messages if it was created (has an id)
   const hasMessages = !!(selectedTaskDetail && selectedTaskDetail.id)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/`(tasks)/devices/chat/page.tsx around lines 130 - 131, The
comment above the hasMessages computation is misleading: it says "non-empty
title and was created" but the code only checks selectedTaskDetail &&
selectedTaskDetail.id; update the comment to accurately describe the logic
(e.g., "Task is considered to have messages if a selected task exists (has an
id)") or change the condition if you intended to check title; references:
hasMessages and selectedTaskDetail/selectedTaskDetail.id.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/src/app/`(tasks)/devices/chat/page.tsx:
- Around line 206-225: The device <select> lacks an accessible label; add an
<label> element tied to the select via an id and htmlFor (or add a stable id on
the select and use aria-labelledby/aria-label) so screen readers announce the
control; update the select (currently using selectedDeviceId and
handleDeviceSelect and rendering devices) to include an id (e.g., device-select)
and add a visible or visually-hidden <label> with the translated text
(t('select_device')) to associate with it.
- Around line 130-131: The comment above the hasMessages computation is
misleading: it says "non-empty title and was created" but the code only checks
selectedTaskDetail && selectedTaskDetail.id; update the comment to accurately
describe the logic (e.g., "Task is considered to have messages if a selected
task exists (has an id)") or change the condition if you intended to check
title; references: hasMessages and selectedTaskDetail/selectedTaskDetail.id.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 87461b4b-f2e6-4512-a8e5-8eb61b658c33

📥 Commits

Reviewing files that changed from the base of the PR and between 80df098 and 99fdd95.

📒 Files selected for processing (1)
  • frontend/src/app/(tasks)/devices/chat/page.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant