[codex] Polish workspace chat activity UI#601
Conversation
|
Warning Review limit reached
Next review available in: 45 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis PR adds tool activity icon derivation, working-state loaders, entry/layout animations for chat messages, source preview rendering with favicons for AI tool activities, richer running/completed tool receipt summaries, and a new workspace AI context outline feature (types, builder, prompt formatting, validation), plus CSS animation refactors and a minor skeleton style tweak. ChangesAI Chat UX and Tool Activity
Workspace AI Context Outline
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant AiChatThreadView
participant AiChatMessageList
participant AiChatToolActivityRow
participant ai-chat-tool-receipts
User->>AiChatThreadView: submit prompt
AiChatThreadView->>AiChatThreadView: generateId() for message
AiChatThreadView->>AiChatMessageList: pass sentMessageAnimationId
AiChatMessageList->>AiChatMessageList: getAiChatRowEntryAnimation(row)
AiChatMessageList-->>User: render "sent" animation for matching row
AiChatThreadView->>AiChatToolActivityRow: render tool activity (running/finished)
AiChatToolActivityRow->>ai-chat-tool-receipts: getRunningToolReceipt / getFinishedToolReceipt
ai-chat-tool-receipts-->>AiChatToolActivityRow: summary text
AiChatToolActivityRow->>AiChatToolActivityRow: getToolSourcePreviews(activity)
AiChatToolActivityRow-->>User: render icon, summary, favicons, status
sequenceDiagram
participant buildWorkspaceAiContextSnapshot
participant buildWorkspaceAiContextOutline
participant formatWorkspaceAiContextForPrompt
participant isWorkspaceAiContextSnapshot
buildWorkspaceAiContextSnapshot->>buildWorkspaceAiContextOutline: build outline for workspace scope
buildWorkspaceAiContextOutline-->>buildWorkspaceAiContextSnapshot: outline (included or summarized)
buildWorkspaceAiContextSnapshot->>formatWorkspaceAiContextForPrompt: snapshot with outline
formatWorkspaceAiContextForPrompt-->>formatWorkspaceAiContextForPrompt: format outline lines within char limits
isWorkspaceAiContextSnapshot->>isWorkspaceAiContextSnapshot: validate outline via isWorkspaceAiContextOutline
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
c69373c to
ac9bd13
Compare
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Greptile SummaryThis PR adds workspace structure to AI context and polishes the workspace chat activity UI. The main changes are:
Confidence Score: 4/5Close to mergeable after fixing one contained UI interaction bug. One issue affects expandable tool activity rows with source previews by nesting source links inside the collapsible trigger. The workspace outline, prompt formatting, and animation changes appear contained. src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx
What T-Rex did
Important Files Changed
Reviews (1): Last reviewed commit: "feat(ai-chat): render tool activity inli..." | Re-trigger Greptile |
| <CollapsibleTrigger className="group/collapsible w-fit max-w-full text-left"> | ||
| <ActivitySummary activity={activity} canExpand sourcePreviews={sourcePreviews} /> |
There was a problem hiding this comment.
Avoid nested links
ActivitySummary renders InlineSourceFavicons as <a> elements when sources have URLs, but this summary is now inside CollapsibleTrigger, which renders an interactive trigger. Source favicon clicks are then nested inside another interactive control, so the row can toggle instead of opening the source and the markup is invalid for keyboard and screen-reader users. Render the favicons outside the trigger or make them non-linking while inside the trigger.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac9bd13678
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <CollapsibleTrigger className="group/collapsible w-fit max-w-full text-left"> | ||
| <ActivitySummary activity={activity} canExpand sourcePreviews={sourcePreviews} /> |
There was a problem hiding this comment.
Move source favicons out of the trigger
When a web/research tool has sources, this ActivitySummary includes InlineSourceFavicons, which renders source <a> elements. With the default CollapsibleTrigger element, those links become nested interactive controls inside the trigger button, so clicking or focusing a favicon can toggle the row instead of opening the source and produces invalid HTML; keep the links outside the trigger or render the trigger as a non-button control.
Useful? React with 👍 / 👎.
| : !isBusy | ||
| ? null |
There was a problem hiding this comment.
Show pending state while submitted
When a send has just entered status === "submitted", awaitingFirstToken is true, but isBusy can still be false because it only reflects recovering/streaming/server-streaming flags. This branch returns null before checking awaitingFirstToken, so the transcript can show no Thinking row after a user submits until another streaming flag flips; check awaitingFirstToken before the !isBusy branch or include submitted in the busy condition.
Useful? React with 👍 / 👎.
ac9bd13 to
2759d1a
Compare
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
src/features/workspaces/components/ai-chat/ai-chat-tool-source-previews.ts (1)
83-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate primitive helpers and hostname-parsing logic across files.
asRecord/getArray/getStringhere duplicate the same private helpers already present inai-chat-tool-receipts.ts, andgetToolSourceHostnameduplicates the identicalnew URL(url).hostname.replace(/^www\./, "")logic used byformatUrlin that file. Consider extracting these into a shared module (e.g. a smallrecord-utils.ts) imported by both files to avoid divergence.Also applies to: 122-143
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/workspaces/components/ai-chat/ai-chat-tool-source-previews.ts` around lines 83 - 89, The primitive helpers and hostname parsing logic are duplicated between ai-chat-tool-source-previews and ai-chat-tool-receipts, so centralize them into a shared module and import them from both places. Extract asRecord/getArray/getString plus the hostname normalization used by getToolSourceHostname/formatUrl into a common utility (for example a shared record-utils helper), then update ai-chat-tool-source-previews and ai-chat-tool-receipts to use the shared functions so the logic stays consistent in one place.src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx (1)
92-116: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winDrop the nested
LazyMotionhere.AiChatMessageListalready wraps these rows in a sharedLazyMotion, so this provider is redundant on every tool-activity row.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx` around lines 92 - 116, The ToolActivityMotion component is creating a nested LazyMotion provider that is already supplied by AiChatMessageList, so remove the redundant LazyMotion wrapper and keep only the motion.div rendering path. Update ToolActivityMotion in AiChatToolActivityRow.tsx to rely on the existing shared motion context while preserving the disabled early return and the same animation props on m.div.src/features/workspaces/model/workspace-ai-context-outline.ts (3)
77-110: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueThree full linear passes over all items on every outline build.
summarizeWorkspaceAiContextOutlineRowsiteratesinput.rowsthree times even afteraddRowhas stopped adding (oncerows.length >= input.limit, subsequent calls are no-ops but the loops still run to completion). For very large workspaces this is wasted work on every chat message send; an earlybreakonce the limit is reached in the first two loops would avoid unnecessary iteration.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/workspaces/model/workspace-ai-context-outline.ts` around lines 77 - 110, The outline summarization in summarizeWorkspaceAiContextOutlineRows still scans input.rows after the limit has already been reached, which wastes work on large workspaces. Update the first two loops in summarizeWorkspaceAiContextOutlineRows to stop iterating once rows.length reaches input.limit, while keeping the existing addRow deduplication and ordering behavior intact. Use the existing addRow helper, selectedItemIds set, and rows collection to detect when to break early.
126-159: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winUnbounded recursion in
countDescendantshas no cycle guard.
countDescendantsrecurses throughchildrenByParentIdwith no visited-set protection. This function runs on everysendMessagecall viabuildWorkspaceAiContextSnapshot→buildWorkspaceAiContextOutline, so if the parent/child graph is ever malformed (e.g. a cycle introduced by a bug elsewhere, or a very deep folder chain), this will stack-overflow and break message sending workspace-wide rather than just the outline feature.♻️ Optional: add basic recursion-depth/cycle protection
const countDescendants = (itemId: string): number => { const cachedCount = descendantCountsByItemId.get(itemId); if (cachedCount !== undefined) { return cachedCount; } + descendantCountsByItemId.set(itemId, 0); // guard against cycles during recursion const count = (childrenByParentId.get(itemId) ?? []).reduce( (total, child) => total + 1 + countDescendants(child.id), 0, ); descendantCountsByItemId.set(itemId, count); return count; };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/workspaces/model/workspace-ai-context-outline.ts` around lines 126 - 159, `getWorkspaceAiContextOutlineDescendantCounts` and its nested `countDescendants` recursion need cycle/depth protection because malformed parent-child graphs can cause infinite recursion and stack overflow. Add a visited-set or recursion-stack guard inside `countDescendants` so repeated traversal of the same item id is detected and stopped, and ensure the guard is applied when `buildWorkspaceAiContextSnapshot` calls `buildWorkspaceAiContextOutline`. Keep the existing descendant-count caching, but make recursion safe for cycles and excessively deep folder chains.
1-160: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftNo test coverage for the bounded outline builder.
This file implements non-trivial bounded/priority logic (folder-first summarization, memoized descendant counts, item-limit enforcement) but ships without unit tests in this batch. Given the truncation/priority rules are easy to regress silently (e.g., off-by-one at exactly the 50-item boundary, folder-vs-root-item precedence), adding focused unit tests for
buildWorkspaceAiContextOutlineboundary cases would materially increase confidence.
Want me to draft unit tests covering the exactly-at-limit, folder-priority, and empty-workspace cases?🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/workspaces/model/workspace-ai-context-outline.ts` around lines 1 - 160, The bounded outline builder in buildWorkspaceAiContextOutline lacks unit coverage for its truncation and prioritization behavior, so add focused tests around the exact 50-item boundary, summarized vs included output, folder-first selection in summarizeWorkspaceAiContextOutlineRows, and empty-workspace handling. Use the public buildWorkspaceAiContextOutline entrypoint plus the helper ordering logic to verify childCount/descendantCount and omittedItems stay correct when limits are hit, and ensure the tests lock in root-item and folder precedence so regressions are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx`:
- Around line 276-306: The Favicon component currently fetches icons from Google
using the raw hostname, which leaks source hostnames and has no fallback if the
request fails. Update Favicon in AiChatToolActivityRow to avoid direct
google.com/s2/favicons calls by routing favicon loading through a first-party
proxy or equivalent internal source, and add load-failure handling so the
existing letter-avatar placeholder is shown when the image cannot load. Use a
state-based fallback in Favicon (with useState if needed) and keep the
hostname/title behavior intact.
In `@src/styles.css`:
- Around line 360-402: The .thinkex-thinking-mark rule has a stylelint
declaration-empty-line-before violation because overflow: visible follows the
custom property without the required blank line. Update the
.thinkex-thinking-mark block in the stylesheet so the overflow declaration is
separated by an empty line from --thinkex-thinking-cycle, matching the project’s
declaration spacing convention.
---
Nitpick comments:
In `@src/features/workspaces/components/ai-chat/ai-chat-tool-source-previews.ts`:
- Around line 83-89: The primitive helpers and hostname parsing logic are
duplicated between ai-chat-tool-source-previews and ai-chat-tool-receipts, so
centralize them into a shared module and import them from both places. Extract
asRecord/getArray/getString plus the hostname normalization used by
getToolSourceHostname/formatUrl into a common utility (for example a shared
record-utils helper), then update ai-chat-tool-source-previews and
ai-chat-tool-receipts to use the shared functions so the logic stays consistent
in one place.
In `@src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx`:
- Around line 92-116: The ToolActivityMotion component is creating a nested
LazyMotion provider that is already supplied by AiChatMessageList, so remove the
redundant LazyMotion wrapper and keep only the motion.div rendering path. Update
ToolActivityMotion in AiChatToolActivityRow.tsx to rely on the existing shared
motion context while preserving the disabled early return and the same animation
props on m.div.
In `@src/features/workspaces/model/workspace-ai-context-outline.ts`:
- Around line 77-110: The outline summarization in
summarizeWorkspaceAiContextOutlineRows still scans input.rows after the limit
has already been reached, which wastes work on large workspaces. Update the
first two loops in summarizeWorkspaceAiContextOutlineRows to stop iterating once
rows.length reaches input.limit, while keeping the existing addRow deduplication
and ordering behavior intact. Use the existing addRow helper, selectedItemIds
set, and rows collection to detect when to break early.
- Around line 126-159: `getWorkspaceAiContextOutlineDescendantCounts` and its
nested `countDescendants` recursion need cycle/depth protection because
malformed parent-child graphs can cause infinite recursion and stack overflow.
Add a visited-set or recursion-stack guard inside `countDescendants` so repeated
traversal of the same item id is detected and stopped, and ensure the guard is
applied when `buildWorkspaceAiContextSnapshot` calls
`buildWorkspaceAiContextOutline`. Keep the existing descendant-count caching,
but make recursion safe for cycles and excessively deep folder chains.
- Around line 1-160: The bounded outline builder in
buildWorkspaceAiContextOutline lacks unit coverage for its truncation and
prioritization behavior, so add focused tests around the exact 50-item boundary,
summarized vs included output, folder-first selection in
summarizeWorkspaceAiContextOutlineRows, and empty-workspace handling. Use the
public buildWorkspaceAiContextOutline entrypoint plus the helper ordering logic
to verify childCount/descendantCount and omittedItems stay correct when limits
are hit, and ensure the tests lock in root-item and folder precedence so
regressions are caught.
🪄 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: d6b48e46-e102-4933-82ce-b5a8cbda130e
📒 Files selected for processing (16)
src/features/workspaces/ai/ai-tool-presentation.tssrc/features/workspaces/components/WorkspaceCardSkeleton.tsxsrc/features/workspaces/components/ai-chat/AiChatAssistantPending.tsxsrc/features/workspaces/components/ai-chat/AiChatMessageList.tsxsrc/features/workspaces/components/ai-chat/AiChatThreadView.tsxsrc/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsxsrc/features/workspaces/components/ai-chat/ai-chat-display-state.tssrc/features/workspaces/components/ai-chat/ai-chat-tool-receipts.tssrc/features/workspaces/components/ai-chat/ai-chat-tool-source-previews.tssrc/features/workspaces/components/ai-chat/types.tssrc/features/workspaces/model/workspace-ai-context-outline.tssrc/features/workspaces/model/workspace-ai-context-prompt.tssrc/features/workspaces/model/workspace-ai-context-snapshot.tssrc/features/workspaces/model/workspace-ai-context-types.tssrc/features/workspaces/model/workspace-ai-context-validation.tssrc/styles.css
| function Favicon({ | ||
| className, | ||
| hostname, | ||
| title, | ||
| }: { | ||
| className?: string; | ||
| hostname: string | null; | ||
| title: string; | ||
| }) { | ||
| if (!hostname) { | ||
| return ( | ||
| <Marker role="status" aria-live="polite" className="w-fit max-w-full py-1 text-sm"> | ||
| <MarkerContent className="shimmer ai-status-shimmer">{activity.summary}</MarkerContent> | ||
| {canExpand ? <ChevronDown className="size-3 shrink-0" aria-hidden="true" /> : null} | ||
| </Marker> | ||
| <span | ||
| className={cn( | ||
| "grid size-3.5 shrink-0 place-items-center rounded-sm bg-muted text-[9px] text-muted-foreground", | ||
| className, | ||
| )} | ||
| > | ||
| {title.charAt(0).toUpperCase()} | ||
| </span> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <Marker className="w-fit max-w-full py-1 text-sm"> | ||
| <MarkerContent className="truncate text-muted-foreground/80"> | ||
| {activity.summary} | ||
| </MarkerContent> | ||
| {canExpand ? <ChevronDown className="size-3 shrink-0" aria-hidden="true" /> : null} | ||
| </Marker> | ||
| <img | ||
| src={`https://www.google.com/s2/favicons?domain=${encodeURIComponent(hostname)}&sz=32`} | ||
| alt="" | ||
| className={cn("size-3.5 shrink-0 rounded-sm", className)} | ||
| loading="lazy" | ||
| /> | ||
| ); | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Favicon fetch leaks source hostnames to Google, with no failure fallback.
Favicon sends every tool-activity source hostname to google.com/s2/favicons, exposing what the AI agent searched/read to a third party without user consent — a privacy concern for any workspace containing sensitive research topics. It also lacks an onError handler, so a blocked/failed request (common with ad-blockers/privacy extensions) leaves a permanently broken image instead of falling back to the existing letter-avatar placeholder.
🔒 Suggested fix: add image-load fallback (and consider a first-party favicon proxy)
function Favicon({
className,
hostname,
title,
}: {
className?: string;
hostname: string | null;
title: string;
}) {
- if (!hostname) {
+ const [failed, setFailed] = useState(false);
+
+ if (!hostname || failed) {
return (
<span
className={cn(
"grid size-3.5 shrink-0 place-items-center rounded-sm bg-muted text-[9px] text-muted-foreground",
className,
)}
>
{title.charAt(0).toUpperCase()}
</span>
);
}
return (
<img
src={`https://www.google.com/s2/favicons?domain=${encodeURIComponent(hostname)}&sz=32`}
alt=""
className={cn("size-3.5 shrink-0 rounded-sm", className)}
loading="lazy"
+ onError={() => setFailed(true)}
/>
);
}Remember to import useState from react. For the privacy leak itself, consider proxying favicon requests through your own backend rather than calling Google directly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx` around
lines 276 - 306, The Favicon component currently fetches icons from Google using
the raw hostname, which leaks source hostnames and has no fallback if the
request fails. Update Favicon in AiChatToolActivityRow to avoid direct
google.com/s2/favicons calls by routing favicon loading through a first-party
proxy or equivalent internal source, and add load-failure handling so the
existing letter-avatar placeholder is shown when the image cannot load. Use a
state-based fallback in Favicon (with useState if needed) and keep the
hostname/title behavior intact.
| .thinkex-thinking-mark { | ||
| --thinkex-thinking-cycle: 2.2s; | ||
| overflow: visible; | ||
| } | ||
|
|
||
| .thinkex-thinking-block { | ||
| animation: thinkex-thinking-block 2.3s ease-in-out infinite both; | ||
| animation: thinkex-thinking-block-loop var(--thinkex-thinking-cycle) ease-in-out infinite both; | ||
| opacity: 0; | ||
| will-change: opacity; | ||
| } | ||
|
|
||
| .thinkex-thinking-block--red-left { | ||
| animation-delay: 840ms; | ||
| } | ||
|
|
||
| .thinkex-thinking-block--top-left { | ||
| animation-delay: 0ms; | ||
| } | ||
|
|
||
| .thinkex-thinking-block--top-middle { | ||
| animation-delay: 120ms; | ||
| } | ||
|
|
||
| .thinkex-thinking-block--top-right { | ||
| animation-delay: 240ms; | ||
| } | ||
|
|
||
| .thinkex-thinking-block--right-middle { | ||
| animation-delay: 360ms; | ||
| } | ||
|
|
||
| .thinkex-thinking-block--bottom-right { | ||
| animation-delay: 480ms; | ||
| } | ||
|
|
||
| .thinkex-thinking-block--center { | ||
| animation-delay: 600ms; | ||
| } | ||
|
|
||
| .thinkex-thinking-block--bottom-left { | ||
| animation-delay: 720ms; | ||
| } | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix stylelint declaration-empty-line-before violation.
Static analysis flags Line 362 (overflow: visible;) as missing a required empty line before the declaration, since it follows the custom-property declaration on Line 361.
🎨 Proposed fix
.thinkex-thinking-mark {
--thinkex-thinking-cycle: 2.2s;
+
overflow: visible;
}📝 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.
| .thinkex-thinking-mark { | |
| --thinkex-thinking-cycle: 2.2s; | |
| overflow: visible; | |
| } | |
| .thinkex-thinking-block { | |
| animation: thinkex-thinking-block 2.3s ease-in-out infinite both; | |
| animation: thinkex-thinking-block-loop var(--thinkex-thinking-cycle) ease-in-out infinite both; | |
| opacity: 0; | |
| will-change: opacity; | |
| } | |
| .thinkex-thinking-block--red-left { | |
| animation-delay: 840ms; | |
| } | |
| .thinkex-thinking-block--top-left { | |
| animation-delay: 0ms; | |
| } | |
| .thinkex-thinking-block--top-middle { | |
| animation-delay: 120ms; | |
| } | |
| .thinkex-thinking-block--top-right { | |
| animation-delay: 240ms; | |
| } | |
| .thinkex-thinking-block--right-middle { | |
| animation-delay: 360ms; | |
| } | |
| .thinkex-thinking-block--bottom-right { | |
| animation-delay: 480ms; | |
| } | |
| .thinkex-thinking-block--center { | |
| animation-delay: 600ms; | |
| } | |
| .thinkex-thinking-block--bottom-left { | |
| animation-delay: 720ms; | |
| } | |
| .thinkex-thinking-mark { | |
| --thinkex-thinking-cycle: 2.2s; | |
| overflow: visible; | |
| } | |
| .thinkex-thinking-block { | |
| animation: thinkex-thinking-block-loop var(--thinkex-thinking-cycle) ease-in-out infinite both; | |
| opacity: 0; | |
| will-change: opacity; | |
| } | |
| .thinkex-thinking-block--red-left { | |
| animation-delay: 840ms; | |
| } | |
| .thinkex-thinking-block--top-left { | |
| animation-delay: 0ms; | |
| } | |
| .thinkex-thinking-block--top-middle { | |
| animation-delay: 120ms; | |
| } | |
| .thinkex-thinking-block--top-right { | |
| animation-delay: 240ms; | |
| } | |
| .thinkex-thinking-block--right-middle { | |
| animation-delay: 360ms; | |
| } | |
| .thinkex-thinking-block--bottom-right { | |
| animation-delay: 480ms; | |
| } | |
| .thinkex-thinking-block--center { | |
| animation-delay: 600ms; | |
| } | |
| .thinkex-thinking-block--bottom-left { | |
| animation-delay: 720ms; | |
| } |
🧰 Tools
🪛 Stylelint (17.14.0)
[error] 362-362: Expected empty line before declaration (declaration-empty-line-before)
(declaration-empty-line-before)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/styles.css` around lines 360 - 402, The .thinkex-thinking-mark rule has a
stylelint declaration-empty-line-before violation because overflow: visible
follows the custom property without the required blank line. Update the
.thinkex-thinking-mark block in the stylesheet so the overflow declaration is
separated by an empty line from --thinkex-thinking-cycle, matching the project’s
declaration spacing convention.
Source: Linters/SAST tools
Summary
Why
The workspace chat activity UI was too heavy for lay users: tool calls looked like cards, source details competed with the answer, and the pending indicator did not persist smoothly while work continued.
Changes
Testing
pnpm run checkNote: the command passes, with the existing local Node engine warning because this shell uses Node
v25.8.0while the repo declares>=24.11.0 <25.Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by CodeRabbit
New Features
Bug Fixes