feat(mobile): live chat, the shared composer and a responsive app shell - #5872
feat(mobile): live chat, the shared composer and a responsive app shell#5872ardaerzin wants to merge 4 commits into
Conversation
|
@coderabbitai review |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe mobile app adopts shared workspace packages and design tokens, adds project home and agent overview routes, replaces local session-list management, and introduces live agent conversations with shared composer, approval, turn, and status components. ChangesMobile application experience
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant MobileRoute
participant ChatScreen
participant useAgentEntity
participant LiveConversation
participant Composer
participant ApprovalDock
MobileRoute->>ChatScreen: Provide session and project identifiers
ChatScreen->>useAgentEntity: Resolve agent and workflow revision
useAgentEntity-->>ChatScreen: Return agentId and entityId
ChatScreen->>LiveConversation: Render live agent conversation
LiveConversation->>Composer: Submit message or stop streaming
LiveConversation->>ApprovalDock: Render and submit approval actions
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 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 |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (5)
web/mobile/src/features/sessions/SessionListScreen.tsx (2)
19-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueShorten the block comment.
The coding guidelines limit in-code comments to one short line. Longer comments are reserved for surprising constraints such as bugs, races, or ordering requirements. This comment describes design intent, so reduce it to one line.
♻️ Proposed shortening
-/** - * The sessions page — the SAME shared body and filters panel the desktop page renders - * (`@agenta/sessions-ui`): one organisation (groups, pins, filters, paging), mobile's shell. - * Touch keeps row actions always visible (no hover); rows open the mobile chat route. - */ +// Shared sessions body and filters panel from `@agenta/sessions-ui`, inside the mobile shell.As per coding guidelines: "Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements."
Source: Coding guidelines
45-46: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueMemoize
openRow.
openRowis recreated on every render and passed toSessionsListView. If that component or its rows are memoized, the new identity defeats the memoization. Wrap it inuseCallback.♻️ Proposed change
- const openRow = (vm: SessionRowVm) => - void router.push(`/w/${workspaceId}/p/${projectId}/sessions/${vm.id}`) + const openRow = useCallback( + (vm: SessionRowVm) => + void router.push(`/w/${workspaceId}/p/${projectId}/sessions/${vm.id}`), + [router, workspaceId, projectId], + )Update the import on line 1:
-import {useMemo} from "react" +import {useCallback, useMemo} from "react"As per coding guidelines: "Minimize React re-renders with
useMemo,useCallback, andReact.memowhere appropriate; avoid unstable inline functions and objects, especially in lists."Source: Coding guidelines
web/mobile/src/features/home/HomeScreen.tsx (2)
53-55: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winStabilize the session-row callback.
Each
openRowfunction is recreated on every parent render and is passed toSessionCardList. UseuseCallbackwithbaseandrouterdependencies. Verify whetherSessionCardListmemoizes callback inputs before merge.
web/mobile/src/features/home/HomeScreen.tsx#L53-L55: MemoizeopenRowbefore passing it to both session lists.web/mobile/src/features/agents/AgentOverviewScreen.tsx#L37-L50: MemoizeopenRowbefore passing it to both agent-scoped session lists.As per coding guidelines, “Minimize React re-renders with
useMemo,useCallback, andReact.memowhere appropriate; avoid unstable inline functions and objects, especially in lists.”Source: Coding guidelines
45-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueShorten nonessential code comments.
These comments restate layout or implementation details. They do not document bugs, races, or ordering constraints. Remove them or reduce each to one short line.
web/mobile/src/features/home/HomeScreen.tsx#L45-L49: Reduce the component description to one short line.web/mobile/src/features/home/HomeScreen.tsx#L79-L84: Remove or shorten the layout and shared-list comments.web/mobile/src/features/home/HomeScreen.tsx#L112-L113: Remove or shorten the trigger-section comment.web/mobile/src/features/home/AgentListRow.tsx#L6-L9: Reduce the component description to one short line.web/mobile/src/features/home/HomeSessionRow.tsx#L4-L7: Reduce the component description to one short line.web/mobile/src/features/home/states/HomeStates.tsx#L11-L12: Reduce the empty-state comment to one short line.web/mobile/src/features/agents/AgentOverviewScreen.tsx#L21-L26: Reduce the component description to one short line.As per coding guidelines, “Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements.”
Source: Coding guidelines
web/mobile/src/features/chat/LiveConversation.tsx (1)
104-109: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReuse the shared empty-state component instead of duplicating the copy.
This paragraph repeats the markup and most of the text of
ChatEmptyinweb/mobile/src/features/chat/states/ChatStates.tsx. The two strings will drift. ExtendChatEmptywith an optional suffix, or add a sibling export, and render it here.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 7aca63c7-aff9-47a1-82e8-549cc060fbab
⛔ Files ignored due to path filters (1)
web/mobile/src/styles/theme.generated.cssis excluded by!**/*.generated.*
📒 Files selected for processing (39)
web/mobile/next.config.tsweb/mobile/package.jsonweb/mobile/scripts/generate-shadcn-tokens.tsweb/mobile/src/components/ScreenScaffold.tsxweb/mobile/src/features/agents/AgentOverviewScreen.tsxweb/mobile/src/features/agents/AgentOverviewSection.tsxweb/mobile/src/features/chat/ApprovalDock.tsxweb/mobile/src/features/chat/ChatHeader.tsxweb/mobile/src/features/chat/ChatScreen.tsxweb/mobile/src/features/chat/Composer.tsxweb/mobile/src/features/chat/LiveConversation.tsxweb/mobile/src/features/chat/StopButton.tsxweb/mobile/src/features/chat/TurnRow.tsxweb/mobile/src/features/chat/TurnStatusLine.tsxweb/mobile/src/features/chat/states/ChatStates.tsxweb/mobile/src/features/chat/useAgentEntity.tsweb/mobile/src/features/home/AgentListRow.tsxweb/mobile/src/features/home/HomeScreen.tsxweb/mobile/src/features/home/HomeSessionRow.tsxweb/mobile/src/features/home/states/HomeStates.tsxweb/mobile/src/features/sessions/SessionListScreen.tsxweb/mobile/src/features/sessions/SessionRow.tsxweb/mobile/src/features/sessions/SessionSearchBar.tsxweb/mobile/src/features/sessions/mergeSessionRows.tsweb/mobile/src/features/sessions/pageFailure.tsweb/mobile/src/features/sessions/pendingFilter.tsweb/mobile/src/features/sessions/states/SessionListStates.tsxweb/mobile/src/features/sessions/useSessionListHead.tsweb/mobile/src/features/sessions/useSessionListScrollRestore.tsweb/mobile/src/features/sessions/useSessionsInfinite.tsweb/mobile/src/pages/w/[workspace_id]/p/[project_id]/agents/[agent_id].tsxweb/mobile/src/pages/w/[workspace_id]/p/[project_id]/apps/index.tsxweb/mobile/src/styles/globals.cssweb/mobile/tests/unit/authConfig.test.tsweb/mobile/tests/unit/authDiscover.test.tsweb/mobile/tests/unit/mergeSessionRows.test.tsweb/mobile/tests/unit/otpMachine.test.tsweb/mobile/tests/unit/pageFailure.test.tsweb/mobile/tests/unit/pendingFilter.test.ts
💤 Files with no reviewable changes (15)
- web/mobile/src/features/sessions/useSessionListHead.ts
- web/mobile/tests/unit/pageFailure.test.ts
- web/mobile/src/features/sessions/pendingFilter.ts
- web/mobile/src/features/sessions/useSessionListScrollRestore.ts
- web/mobile/src/features/sessions/useSessionsInfinite.ts
- web/mobile/src/features/sessions/mergeSessionRows.ts
- web/mobile/tests/unit/authConfig.test.ts
- web/mobile/src/features/sessions/states/SessionListStates.tsx
- web/mobile/tests/unit/authDiscover.test.ts
- web/mobile/src/features/sessions/SessionSearchBar.tsx
- web/mobile/src/features/sessions/SessionRow.tsx
- web/mobile/tests/unit/pendingFilter.test.ts
- web/mobile/tests/unit/otpMachine.test.ts
- web/mobile/src/features/sessions/pageFailure.ts
- web/mobile/tests/unit/mergeSessionRows.test.ts
| "@agenta/auth", | ||
| "@agenta/auth-ui", | ||
| "@agenta/sdk", | ||
| "@agentaai/api-client", | ||
| "@agenta/shared", | ||
| "@agenta/ui", | ||
| "@agenta/entities", | ||
| "@agenta/entity-ui", | ||
| "@agenta/navigation", | ||
| "@agenta/navigation-ui", | ||
| "@agenta/sessions", |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 '"`@agenta/sessions-ui`"|transpilePackages' \
web/mobile/package.json web/mobile/next.config.ts
rg -nP -C 3 \
'(?:from\s+["'\'']`@agenta/sessions-ui`(?:/[^"'\'']*)?["'\'']|require\(\s*["'\'']`@agenta/sessions-ui`)' \
web/mobile/srcRepository: Agenta-AI/agenta
Length of output: 3556
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== next config =="
cat -n web/mobile/next.config.ts
echo
echo "== agenta-sessions-ui package files =="
fd -a . web/packages/agenta-sessions-ui | sed 's#^\./##' | head -80
echo
echo "== agenta-sessions-ui package manifests =="
for f in web/packages/agenta-sessions-ui/package.json web/packages/agenta-sessions-ui/tsconfig*.json web/packages/agenta-sessions-ui/next.config.* web/packages/agenta-sessions-ui/babel.config.* web/packages/agenta-sessions-ui/vite.config.* web/packages/agenta-sessions-ui/rollup.config.* web/packages/agenta-sessions-ui/tsup.config.*; do
[ -f "$f" ] && { echo "--- $f"; cat -n "$f"; }
done
echo
echo "== imports from sessions-ui =="
rg -n "'`@agenta/sessions-ui`|\"`@agenta/sessions-ui`" web/mobile/src || true
echo
echo "== package scripts/package manager =="
cat -n web/package.json | sed -n '1,80p'Repository: Agenta-AI/agenta
Length of output: 11557
🌐 Web query:
Next.js transpilePackages packages from node_modules source TypeScript packages
💡 Result:
In Next.js, the transpilePackages configuration option is used to instruct the compiler to transpile and bundle dependencies from node_modules or local monorepo workspaces that are not automatically processed [1][2]. By default, Next.js does not compile code inside node_modules [1]. If a dependency ships raw TypeScript (.ts/.tsx), JSX, or modern JavaScript syntax that requires transformation, it will cause errors (such as a SyntaxError or an unexpected token error) when imported [1][3]. You should add a package to transpilePackages when: * The dependency ships raw TypeScript or JSX source code directly [1][3]. * The dependency uses features that require the Next.js transformation pipeline, such as CSS Modules imports, server-only guards, or React Server Component directives ('use client'/'use server') [3]. * You are using the Pages Router and wish to bundle a server-side dependency into the route instead of loading it via Node.js require at runtime [1][4]. To use it, add the package name (including scope, if applicable) to your next.config.js file: // next.config.js / @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ['@scope/package-name', 'another-package'], } module.exports = nextConfig Key points to remember: * Values must be package names; file paths and glob patterns are not supported [1][2]. * Turbopack and Webpack handle local monorepo workspace packages differently, but explicitly listing them in transpilePackages is considered the safest and most predictable approach [1][3]. * A package cannot be listed in both transpilePackages and serverExternalPackages [1][4]. * Next.js may automatically include some internal packages in the transpilation process [1]. Checking the default-transpiled-packages.json file in the Next.js repository can show you which packages are handled automatically [1][4].
Citations:
- 1: https://nextjs.org/docs/app/api-reference/config/next-config-js/transpilePackages
- 2: https://nextjs.org/docs/15/app/api-reference/config/next-config-js/transpilePackages
- 3: Clarify when `transpilePackages` is required for local workspace packages vercel/next.js#93542
- 4: https://nextjs.org/docs/pages/api-reference/config/next-config-js/transpilePackages
Add @agenta/sessions-ui to transpilePackages.
web/mobile imports @agenta/sessions-ui, and that package exposes raw .ts/.tsx through main/exports without a compiled dist. Add @agenta/sessions-ui to web/mobile/next.config.ts so mobile bundles it in production.
| "@agenta/auth": "workspace:../packages/agenta-auth", | ||
| "@agenta/auth-ui": "workspace:../packages/agenta-auth-ui", | ||
| "@agenta/chat": "workspace:../packages/agenta-chat", | ||
| "@agenta/entities": "workspace:../packages/agenta-entities", | ||
| "@agenta/entity-ui": "workspace:../packages/agenta-entity-ui", | ||
| "@agenta/navigation": "workspace:../packages/agenta-navigation", | ||
| "@agenta/navigation-ui": "workspace:../packages/agenta-navigation-ui", | ||
| "@agenta/sdk": "workspace:../packages/agenta-sdk", | ||
| "@agenta/sessions": "workspace:../packages/agenta-sessions", | ||
| "@agenta/sessions-ui": "workspace:../packages/agenta-sessions-ui", | ||
| "@agenta/shared": "workspace:../packages/agenta-shared", | ||
| "@agenta/ui": "workspace:../packages/agenta-ui", |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Regenerate and commit pnpm-lock.yaml.
These dependency changes make pnpm install --frozen-lockfile fail. Regenerate the lockfile and commit it with this manifest update.
Source: Pipeline failures
| const {entityId} = useAgentEntity(sessionId, projectId) | ||
| const liveness = useLivenessPoll(projectId) | ||
| const running = Boolean( | ||
| liveness.data?.find((s) => s.session_id === sessionId)?.flags?.is_running, | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use resolving to avoid the incorrect read-only message during agent resolution.
useAgentEntity returns resolving, but ChatScreen ignores it. While the agent query is in flight, entityId is null, so ReplayScreen mounts and ChatHeader shows "Read-only — this session has no agent to message yet.". That statement is false for an agent-backed session. When resolution completes, the screen swaps components and the replay transcript state is discarded.
Gate the replay subtitle (or render ChatLoading) on resolving.
🔧 Proposed fix
- const {entityId} = useAgentEntity(sessionId, projectId)
+ const {entityId, resolving} = useAgentEntity(sessionId, projectId)
const liveness = useLivenessPoll(projectId)
const running = Boolean(
liveness.data?.find((s) => s.session_id === sessionId)?.flags?.is_running,
) return (
<ReplayScreen
sessionId={sessionId}
projectId={projectId}
workspaceId={workspaceId}
running={running}
+ resolving={resolving}
/>
)Then pass subtitle={resolving ? undefined : "Read-only — …"} inside ReplayScreen.
| const submit = async (text: string) => { | ||
| const staged = attachments.files | ||
| const parts = staged.length > 0 ? stagedFilesToParts(staged, sessionId) : undefined | ||
| await onSend({text, parts}) | ||
| attachments.clearAttachments(staged.map((file) => file.uid)) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Locate ChatComposer and inspect its onSubmit invocation and error handling.
fd -t f 'ChatComposer' | xargs -r ast-grep outline --items all
rg -nP -C 6 'onSubmit\s*[(?.]' --iglob '*ChatComposer*'Repository: Agenta-AI/agenta
Length of output: 1005
🏁 Script executed:
#!/bin/bash
set -u
echo "## relevant ChatComposer file"
wc -l web/packages/agenta-chat/src/components/ChatComposer.tsx
cat -n web/packages/agenta-chat/src/components/ChatComposer.tsx | sed -n '1,240p'
echo
echo "## references to ChatComposer onSubmit prop/usages"
rg -n -C 4 'ChatComposer|\.onSubmit\(|onSubmit\s*=' web/packages/agenta-chat web/mobile --iglob '*.{ts,tsx}'
echo
echo "## Composer.tsx relevant file"
fd -t f 'Composer.tsx' web/mobile | xargs -r -I{} sh -c 'echo "--- {}"; wc -l "{}"; cat -n "{}" | sed -n "1,140p"'Repository: Agenta-AI/agenta
Length of output: 17601
🏁 Script executed:
#!/bin/bash
set -u
echo "## RichChatInput files"
fd -t f 'rich.*chat.*input|chat.*input|RichChatInput' web --iglob '*.{ts,tsx}'
echo
echo "## RichChatInput implementations"
for f in $(fd -t f 'RichChatInput|rich-chat-input|chat-input' web/packages web/mobile --iglob '*.{ts,tsx}'); do
echo "--- $f"
wc -l "$f"
cat -n "$f" | sed -n '1,260p'
done
echo
echo "## onSubmit consumers across web/mobile"
rg -n -C 5 'onSubmit\s*=|\.onSubmit\(' web/packages web/mobile --iglob '*.{ts,tsx}'Repository: Agenta-AI/agenta
Length of output: 18353
🏁 Script executed:
#!/bin/bash
set -u
echo "## RichChatInput SendButton/SubmitPlugin definitions"
rg -n -C 6 'SubmitPlugin|onSubmit|forceEnabled|sendForceEnabled' web/packages/agenta-ui/src/RichChatInput --glob '*.{ts,tsx}'
echo
echo "## RichChatInput component outline"
ast-grep outline web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx --match RichChatInput --view compact || true
echo
echo "## RichChatInput section"
cat -n web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx | sed -n '1,360p'
echo
echo "## lexical submit plugin/package maybe in node_modules or source?"
rg -n -C 5 'SubmitPlugin|return lexicon?|onSubmit.*LexicalNode|LexicalSubmitPlugin' web --glob '*.{ts,tsx}'Repository: Agenta-AI/agenta
Length of output: 50374
Handle failed sends before clearing staged attachments.
submit awaits onSend({text, parts}) and only clears attachments afterward. A rejected send makes the promise passed through RichChatInput/SubmitPlugin.addSubmitHandler unresolved to the UI, so the user gets no feedback and the attachments remain queued. Catch the rejection and clear only after the send succeeds, or surface the error in the way ChatComposer expects.
| const visibleTurns = conversation.turns.filter((turn) => !turn.hidden) | ||
| const autoScroll = useTranscriptAutoScroll(visibleTurns) |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect useTranscriptAutoScroll to see whether the turns argument is an effect dependency.
fd -t f 'useTranscriptAutoScroll' | xargs -r cat -nRepository: Agenta-AI/agenta
Length of output: 1122
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Files:\n'
git ls-files | rg '(^|/)LiveConversation\.tsx$|(^|/)ChatScreen\.tsx$|useTranscriptAutoScroll' || true
printf '\nLiveConversation relevant section:\n'
sed -n '1,150p' web/mobile/src/features/chat/LiveConversation.tsx 2>/dev/null | cat -n
printf '\nChatScreen useTranscriptAutoScroll usages:\n'
rg -n "useTranscriptAutoScroll|turns|visibleTurns" web/mobile/src/features/chat/ChatScreen.tsx web/mobile/src/features/chat/LiveConversation.tsx -C 2Repository: Agenta-AI/agenta
Length of output: 11867
Memoize visibleTurns before passing it to useTranscriptAutoScroll.
useTranscriptAutoScroll includes content in its useLayoutEffect dependency list, so a new array on each render re-runs the pin effect even when only unrelated state such as watch.connected or steerActions.phase changes.
Proposed fix
- const visibleTurns = conversation.turns.filter((turn) => !turn.hidden)
+ const visibleTurns = useMemo(
+ () => conversation.turns.filter((turn) => !turn.hidden),
+ [conversation.turns],
+ )
const autoScroll = useTranscriptAutoScroll(visibleTurns)📝 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 visibleTurns = conversation.turns.filter((turn) => !turn.hidden) | |
| const autoScroll = useTranscriptAutoScroll(visibleTurns) | |
| const visibleTurns = useMemo( | |
| () => conversation.turns.filter((turn) => !turn.hidden), | |
| [conversation.turns], | |
| ) | |
| const autoScroll = useTranscriptAutoScroll(visibleTurns) |
Source: Coding guidelines
| <Link href={viewAllHref} className="text-muted-foreground text-xs no-underline"> | ||
| View all → | ||
| </Link> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Add semantic interaction states to the new navigation links.
These links define static styling only. Add visible hover, active, and focus-visible states with semantic Tailwind colors. Verify the states in both light and dark themes.
web/mobile/src/features/home/HomeScreen.tsx#L36-L38: Add interaction-state utilities to the Sessions “View all” link.web/mobile/src/features/home/AgentListRow.tsx#L14-L14: Add interaction-state utilities to the agent row link.web/mobile/src/features/home/HomeSessionRow.tsx#L9-L9: Add interaction-state utilities to the session row link.web/mobile/src/features/agents/AgentOverviewScreen.tsx#L60-L66: Add interaction-state utilities to the back link.web/mobile/src/features/agents/AgentOverviewSection.tsx#L17-L19: Add interaction-state utilities to the “View all” link.
As per coding guidelines, “Implement light and dark appearance and interaction states for every added or changed UI element, and verify both themes.”
📍 Affects 5 files
web/mobile/src/features/home/HomeScreen.tsx#L36-L38(this comment)web/mobile/src/features/home/AgentListRow.tsx#L14-L14web/mobile/src/features/home/HomeSessionRow.tsx#L9-L9web/mobile/src/features/agents/AgentOverviewScreen.tsx#L60-L66web/mobile/src/features/agents/AgentOverviewSection.tsx#L17-L19
Source: Coding guidelines
| <span | ||
| aria-label={vm.status.label} | ||
| className={`size-2 shrink-0 rounded-full ${vm.status.dotClassName} ${ | ||
| vm.status.pulse ? "motion-safe:animate-pulse" : "" | ||
| }`} | ||
| /> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file exists and around target =="
if [ -f "web/mobile/src/features/home/HomeSessionRow.tsx" ]; then
nl -ba web/mobile/src/features/home/HomeSessionRow.tsx | sed -n '1,220p'
else
fd -a HomeSessionRow.tsx .
fi
echo
echo "== search HomeSessionRow and chipLabel/status =="
rg -n "HomeSessionRow|chipLabel|status:|dotClassName|aria-label" web/mobile/src/features/home -S || true
echo
echo "== package / lint / a11y config clues =="
fd -a "package.json|eslint|prettier|biome|tsconfig|app.json|app.config" web/mobile web -t f | sed -n '1,120p'Repository: Agenta-AI/agenta
Length of output: 230
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate file and show with cat -n =="
if [ -f "web/mobile/src/features/home/HomeSessionRow.tsx" ]; then
cat -n web/mobile/src/features/home/HomeSessionRow.tsx | sed -n '1,240p'
else
fd HomeSessionRow.tsx . -t f -x echo 'FILE:{}' -x sh -c 'sed -n "1,240p" "$1"' sh {}
fi
echo
echo "== search related identifiers =="
rg -n "HomeSessionRow|chipLabel|status:|dotClassName|aria-label|role=" web/mobile/src/features/home web/mobile/src -S || true
echo
echo "== lint/dependency config clues =="
fd package.json|head -n 20|xargs -r -n1 sh -c 'echo "--- $0"; sed -n "1,220p" "$0"'
fd -i "eslint|prettier|biome|tsconfig|app.json|app.config|metro|babel|tailwind" -t f web/mobile web | sed -n '1,160p'Repository: Agenta-AI/agenta
Length of output: 45065
🌐 Web query:
WCAG ARIA name computation generic span no exposed role nameFrom attribute aria-label
💡 Result:
According to the WAI-ARIA and Accessible Name and Description Computation (AccName) specifications, using an aria-label attribute on a generic element (such as a or
Citations:
- 1: https://rocketvalidator.com/accessibility-validation/accesslint/0.18/aria/aria-prohibited-attr
- 2: https://www.w3.org/TR/html-aria/
- 3: https://github.com/w3c/aria/blob/main/accname/index.html
- 4: https://benmyers.dev/blog/dont-use-aria-label-on-static-text-elements/
- 5: https://www.w3.org/TR/accname-1.2/
- 6: https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/
- 7: https://stackoverflow.com/questions/72568204/what-can-aria-label-be-used-on
- 8: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label
🌐 Web query:
MDN ARIA img role span aria-label nameFrom attribute
💡 Result:
When using an element like a span with role="img", the accessible name is determined by the Accessible Name and Description Computation (AccName) specification [1][2]. For elements with an explicit ARIA role of img, the nameFrom attribute is "author" [1][2]. This means the element derives its accessible name from author-provided markup, such as the aria-label or aria-labelledby attributes [1][2]. Key points regarding this computation: - Name Calculation: Elements with role="img" are generally treated as a single entity by assistive technologies [3][4]. Because role="img" does not support "name from content" (i.e., it does not automatically extract text from its children), you must explicitly provide an accessible name [3][4][5]. - Attributes: - aria-label: Provides a string value to name the element [6]. It is used when there is no visible text on the page to serve as a label [6][7]. - aria-labelledby: References other elements on the page via their IDs to provide the accessible name [7]. This is preferred over aria-label if a visible label exists [3][6][7]. - Precedence: If both aria-labelledby and aria-label are provided on the same element, aria-labelledby takes precedence [7]. While role="img" is primarily designed to use author-provided names, the broader W3C Accessible Name Computation algorithm accounts for various scenarios where other host-language mechanisms (like the alt or title attributes on native HTML elements) might contribute to the accessible name if the element is not a simple span [1][2]. However, for a span specifically, aria-label or aria-labelledby are the standard and expected methods to ensure the element has a meaningful accessible name [3][4].
Citations:
- 1: https://w3c.github.io/accname/
- 2: https://www.w3.org/TR/2025/WD-accname-1.2-20250301/
- 3: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/img_role
- 4: https://github.com/mdn/content/blob/main/files/en-us/web/accessibility/aria/reference/roles/img_role/index.md
- 5: role=img should allow accName other than aria-label or aria-labelledby w3c/aria#1398
- 6: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label
- 7: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby
Expose the status indicator with a permitted semantic role.
The generic span does not support aria-label, so screen readers may skip this status label. Add role="img" or render visible/visually hidden text instead when chipLabel is absent.
| --text-field-md--line-height: 1.6666666666666667; | ||
| --text-field-lg: 14px; | ||
| --text-field-lg--line-height: 1.5714285714285714; | ||
| --shadow-switch-handle: 0 2px 4px 0 rgba(0, 35, 11, 0.2); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Move the switch shadow color into the palette-backed token layer.
rgba(0, 35, 11, 0.2) bypasses semantic theme tokens and applies the same shadow color in both themes. Add a light/dark palette token, emit it through generate-shadcn-tokens.ts, and reference that variable here.
As per coding guidelines, “Consume theme colors through Ant Design semantic tokens, Tailwind color utilities, or supported var(--ag-color*) variables.”
Source: Coding guidelines
The app imports it from Home, the session list and the agent overview, but Next was never told to transpile it. The workspace packages ship raw TS (main: ./src/index.ts) with no dist, so a production build cannot parse it — every other package the app uses is already on this list.
…r claims read-only Three chat-screen correctness fixes. The composer awaited onSend and let the rejection escape: nothing consumes the promise RichChatInput hands back, so a failed send left the user with no message, no error, and no idea it had failed — and `stagedFilesToParts` throws outright on an unsettled upload, which Enter reaches (only the send BUTTON is gated on `attachmentsSettled`). It now catches, keeps the attachments staged, puts the text back in the editor, and reports through the same inline rejections strip the desktop composer uses. The screen router ignored `resolving`, so while the session's agent was still being looked up it mounted the replay and told the user 'Read-only — this session has no agent to message yet' about a perfectly live agent, then threw the replay transcript away when the answer landed. It now waits on the loading state until the lookup settles. `visibleTurns` was a fresh array every render, and the auto-scroll effect lists it as a dependency — so the transcript re-pinned on renders that changed nothing about it (a watch reconnect, a steer phase change). Memoized.
… a themed switch shadow The links and rows added for Home and the agent overview rendered as static text: no hover, no active, no focus-visible treatment. One module now holds the three variants (row, inline, icon) so all of them read the same instead of drifting into five near-identical treatments; colours come from the semantic tokens, which already resolve per theme, so there is no dark-mode override. (The focus ring spells out `outline-solid`: Tailwind v4's `outline-none` sets --tw-outline-style: none, which `outline-2` reads back, so the pair alone draws nothing.) The session row's status dot carried an aria-label on a bare span, which is not reliably exposed — it is a named image now. The switch-handle shadow was a literal rgba() in globals.css, painting the same near-black green in both themes. It joins the palette as a light/dark pair and rides the generator like every other token. NOTE: src/styles/theme.generated.css still needs regenerating for the new --switch-handle-shadow var. It is deliberately NOT regenerated here: the committed file on this branch is already ahead of this branch's generator (13 vars per block that a later lane's role map adds, several of them referenced by globals.css), so a regeneration now would delete them.
92cc9cf to
04e5416
Compare
3ec8109 to
baa1f8e
Compare
/mgets live chat on the same@agenta/chatengine the desktop now uses, the shared composer,and a responsive app shell — rather than a mobile-only reimplementation that would drift.
Not run in a browser — static gates only (
pnpm lint-fix24/24,tsc --noEmitcleanfor
@agenta/shared,ui,entities,entity-ui,settings-ui,oss,ee,mobile).Stacked on
oss/chat-on-shared-engine; review only this lane's diff.