Skip to content

Conversation

sawka
Copy link
Member

@sawka sawka commented Oct 15, 2025

Documentation Updates (removing AI Widget Information / deprecation)
Hover effect on tool calls shows which widget is effected
Remove AI Widget from sidebar unless there is customized presets
Backend now provides blockid (if available) to frontend for tool calls

Copy link
Contributor

coderabbitai bot commented Oct 15, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Documentation: removes AI Presets content and redirects AI configuration references to a new Wave AI docs page; adds a Wave AI card and updates keybindings. Frontend: introduces a BlockModel singleton and BlockHighlightType for per-block highlight atoms; AIToolUse emits hover-driven highlights (via new blockid on tooluse), BlockFrame renders highlight overlay, workspace filters the AI widget based on hasCustomAIPresetsAtom, and a new hasCustomAIPresets atom is exported. Types: WaveUIDataTypes.tooluse gains optional blockid. Backend: UIMessageDataToolUse gains BlockId; WaveChatOpts gains TabId and TabStateGenerator now returns tabId; tool-use creation and several aiusechat handlers now resolve block IDs via new wcore.ResolveBlockIdFromPrefix; an older local resolver was removed.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The current title “Updates to prepare for v0.12 launch” is too generic and does not highlight the primary changes such as deprecating AI presets, adding hover effects, conditional widget display, or backend block ID support. Consider revising the title to more specifically reflect the main changes, for example referencing AI presets deprecation, block highlight hover effects, conditional widget removal, and backend block ID support.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed The description accurately summarizes the key changes, including documentation deprecation, hover effect for tool calls, conditional sidebar behavior, and backend block ID support, and aligns with the actual changes in the pull request.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 404c6c9 and 98939fa.

📒 Files selected for processing (1)
  • frontend/app/aipanel/aimessage.tsx (4 hunks)

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
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/docs/presets.mdx (1)

20-20: Remove outdated presets/ai.json reference.

Line 20 still points readers to presets/ai.json, but the rest of the page (and the PR) removes AI presets entirely. Keeping that path is misleading—folks will go hunting for a file we just deprecated. Please drop the AI example (or replace it with another background-focused one).

- All presets are aggregated regardless of which file they're in, so you can use the `presets` directory to organize them (e.g., `presets/bg.json`, `presets/ai.json`).
+ All presets are aggregated regardless of which file they're in, so you can use the `presets` directory to organize them (e.g., `presets/bg.json`).
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2f1d4f and 591e423.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • docs/docs/ai-presets.mdx (1 hunks)
  • docs/docs/customization.mdx (0 hunks)
  • docs/docs/faq.mdx (0 hunks)
  • docs/docs/gettingstarted.mdx (2 hunks)
  • docs/docs/index.mdx (1 hunks)
  • docs/docs/keybindings.mdx (1 hunks)
  • docs/docs/presets.mdx (3 hunks)
  • frontend/app/aipanel/aimessage.tsx (2 hunks)
  • frontend/app/aipanel/aitypes.ts (1 hunks)
  • frontend/app/block/block-model.ts (1 hunks)
  • frontend/app/block/blockframe.tsx (5 hunks)
  • frontend/app/store/global.ts (2 hunks)
  • frontend/app/workspace/widgets.tsx (2 hunks)
  • frontend/types/custom.d.ts (1 hunks)
  • pkg/aiusechat/openai/openai-backend.go (4 hunks)
  • pkg/aiusechat/tools.go (0 hunks)
  • pkg/aiusechat/tools_screenshot.go (2 hunks)
  • pkg/aiusechat/tools_term.go (2 hunks)
  • pkg/aiusechat/tools_web.go (1 hunks)
  • pkg/aiusechat/uctypes/usechat-types.go (2 hunks)
  • pkg/aiusechat/usechat.go (2 hunks)
  • pkg/wcore/wcore.go (2 hunks)
💤 Files with no reviewable changes (3)
  • docs/docs/customization.mdx
  • pkg/aiusechat/tools.go
  • docs/docs/faq.mdx
🧰 Additional context used
🧬 Code graph analysis (8)
pkg/aiusechat/tools_term.go (1)
pkg/wcore/wcore.go (1)
  • ResolveBlockIdFromPrefix (114-131)
frontend/app/block/blockframe.tsx (1)
frontend/app/block/block-model.ts (1)
  • BlockModel (12-51)
pkg/aiusechat/tools_web.go (1)
pkg/wcore/wcore.go (1)
  • ResolveBlockIdFromPrefix (114-131)
pkg/aiusechat/usechat.go (2)
pkg/aiusechat/uctypes/usechat-types.go (1)
  • ToolDefinition (78-90)
pkg/aiusechat/tools.go (1)
  • GenerateTabStateAndTools (88-140)
frontend/app/aipanel/aimessage.tsx (1)
frontend/app/block/block-model.ts (1)
  • BlockModel (12-51)
pkg/aiusechat/openai/openai-backend.go (2)
pkg/aiusechat/uctypes/usechat-types.go (3)
  • ToolDefinition (78-90)
  • WaveChatOpts (420-435)
  • UIMessageDataToolUse (136-144)
pkg/wcore/wcore.go (1)
  • ResolveBlockIdFromPrefix (114-131)
pkg/wcore/wcore.go (2)
pkg/wstore/wstore_dbops.go (1)
  • DBMustGet (138-149)
pkg/waveobj/wtype.go (2)
  • Tab (183-190)
  • Tab (192-194)
pkg/aiusechat/tools_screenshot.go (1)
pkg/wcore/wcore.go (1)
  • ResolveBlockIdFromPrefix (114-131)
🪛 LanguageTool
docs/docs/keybindings.mdx

[grammar] ~84-~84: There might be a mistake here.
Context: ... | Function | | ----------------------- | ------------...

(QB_NEW_EN)


[grammar] ~85-~85: There might be a mistake here.
Context: ...------------ | ----------------------- | | | Toggle WaveA...

(QB_NEW_EN)


[grammar] ~86-~86: There might be a mistake here.
Context: ...:Shift:a"/> | Toggle WaveAI panel | | | Focus WaveAI...

(QB_NEW_EN)


[grammar] ~87-~87: There might be a mistake here.
Context: ...l:Shift:0"/> | Focus WaveAI input | | | Clear AI Cha...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Build Docsite
  • GitHub Check: Build for TestDriver.ai
  • GitHub Check: merge-gatekeeper
🔇 Additional comments (26)
frontend/app/aipanel/aitypes.ts (1)

20-20: LGTM!

The optional blockid field addition correctly extends the tooluse data shape to support block highlighting in the UI.

frontend/app/block/block-model.ts (1)

12-51: LGTM on the singleton pattern and caching strategy.

The singleton pattern is correctly implemented with proper getInstance/resetInstance methods. The atom caching in getBlockHighlightAtom efficiently creates derived atoms per block ID.

frontend/types/custom.d.ts (1)

17-17: LGTM!

The type declaration for hasCustomAIPresetsAtom is correct and consistent with other atom declarations in the interface.

frontend/app/workspace/widgets.tsx (2)

72-72: LGTM!

The hasCustomAIPresetsAtom is correctly consumed to control AI widget visibility.


97-101: LGTM on the conditional filtering logic.

The implementation correctly removes the AI widget (defwidget@ai) when no custom AI presets exist, aligning with the PR objective to hide the AI widget from the sidebar unless customized.

pkg/aiusechat/tools_web.go (1)

89-92: LGTM on the centralized block ID resolution.

The refactoring to use wcore.ResolveBlockIdFromPrefix centralizes the resolution logic and simplifies error handling. The implementation is consistent with the new pattern.

frontend/app/store/global.ts (2)

107-118: LGTM on the derived atom implementation.

The hasCustomAIPresetsAtom correctly derives the presence of custom AI presets by checking for keys starting with "ai@" while excluding the default "ai@global" and "ai@wave" presets.


175-175: LGTM!

The atom is correctly exported in the global atoms object, making it available throughout the application.

frontend/app/aipanel/aimessage.tsx (3)

4-4: LGTM!

The BlockModel import is correctly added to support the block highlighting feature.


242-255: LGTM on the mouse event handlers.

The handlers correctly manage block highlighting:

  • handleMouseEnter sets the highlight when a blockid exists
  • handleMouseLeave clears the highlight
  • Both properly check for blockid presence before acting

258-264: LGTM on the UI updates.

The container correctly:

  • Applies cursor-pointer style when blockid exists to indicate interactivity
  • Wires up the mouse event handlers
  • Maintains existing styling and classes
pkg/aiusechat/tools_screenshot.go (2)

12-12: LGTM!

The import is correctly updated to use the centralized wcore package.


33-36: LGTM on the centralized block ID resolution.

The refactoring to use wcore.ResolveBlockIdFromPrefix is consistent with the broader PR pattern of centralizing block ID resolution logic.

pkg/aiusechat/tools_term.go (1)

14-14: LGTM! Centralized block ID resolution.

The refactor to use wcore.ResolveBlockIdFromPrefix consolidates the block ID resolution logic, reducing code duplication across tool files while maintaining proper error handling.

Also applies to: 123-126

pkg/wcore/wcore.go (1)

114-131: LGTM! Centralized block ID resolution with proper validation.

The new ResolveBlockIdFromPrefix function consolidates block ID resolution logic with appropriate validation (8-character prefix) and clear error messages. The linear search through tab.BlockIds is acceptable for typical tab sizes.

pkg/aiusechat/uctypes/usechat-types.go (4)

143-143: LGTM! BlockId field enables block highlighting.

The BlockId field addition to UIMessageDataToolUse enables the frontend to identify and highlight specific blocks associated with tool usage, supporting the new hover-to-highlight feature.


426-426: LGTM! TabStateGenerator signature extended for tab context propagation.

The updated signature returning four values (including tabId) enables proper propagation of tab context through the chat workflow, supporting block ID resolution in tool usage.


431-431: LGTM! Comment typo corrected.

Fixed: "emphemeral" → "ephemeral".


434-434: LGTM! TabId field enables tab-scoped operations.

The TabId field in WaveChatOpts allows tab-scoped tool operations and block ID resolution, completing the tab context propagation chain.

pkg/aiusechat/openai/openai-backend.go (1)

23-23: LGTM! BlockId resolution integrated into tool usage flow.

The createToolUseData signature extension and new resolution logic appropriately populate BlockId when both TabId and widget_id are available. The 2-second timeout for wcore.ResolveBlockIdFromPrefix is reasonable, and graceful error handling (silently skipping BlockId assignment on error) prevents disruption of the tool usage flow.

Also applies to: 817-817, 845-886

frontend/app/block/blockframe.tsx (4)

4-4: LGTM! BlockModel integration for highlight state.

The imports and singleton access pattern properly integrate block highlighting into the frame rendering logic.

Also applies to: 30-30, 488-488


513-515: LGTM! Conditional highlight border application.

The logic correctly applies a default highlight border (rgb(59, 130, 246)) only when blockHighlight exists and no explicit border color is already set, preventing override of user-configured borders.


525-533: LGTM! Block highlight rendering with appropriate priority.

The conditional rendering correctly prioritizes layout mode overlay over block highlight, and the highlight icon rendering (48px, 50% opacity) provides clear visual feedback without being intrusive.


536-536: LGTM! Background highlight styling.

The bg-blue-500/10 class provides subtle visual feedback when block highlighting is active, complementing the border and icon indicators.

pkg/aiusechat/usechat.go (2)

365-370: LGTM! Tab context propagation in chat flow.

The TabStateGenerator call and assignment logic correctly handle the extended signature, propagating tabId through the chat workflow only when generation succeeds (no tabErr).


625-628: LGTM! TabStateGenerator lambda updated for 4-tuple return.

The lambda correctly calls GenerateTabStateAndTools and returns the 4-tuple including req.TabId, maintaining consistency with the updated TabStateGenerator signature.

private static instance: BlockModel | null = null;
private blockHighlightAtomCache = new Map<string, jotai.Atom<BlockHighlightType | null>>();

blockHighlightAtom: jotai.PrimitiveAtom<BlockHighlightType> = jotai.atom(null) as jotai.PrimitiveAtom<BlockHighlightType>;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the type declaration to match the nullable initialization.

The atom is initialized with null but typed as PrimitiveAtom<BlockHighlightType> (non-nullable). This type mismatch could lead to type safety issues.

Apply this diff to fix the type:

-    blockHighlightAtom: jotai.PrimitiveAtom<BlockHighlightType> = jotai.atom(null) as jotai.PrimitiveAtom<BlockHighlightType>;
+    blockHighlightAtom: jotai.PrimitiveAtom<BlockHighlightType | null> = jotai.atom<BlockHighlightType | null>(null);
📝 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
blockHighlightAtom: jotai.PrimitiveAtom<BlockHighlightType> = jotai.atom(null) as jotai.PrimitiveAtom<BlockHighlightType>;
blockHighlightAtom: jotai.PrimitiveAtom<BlockHighlightType | null> = jotai.atom<BlockHighlightType | null>(null);
🤖 Prompt for AI Agents
In frontend/app/block/block-model.ts around line 16, the atom is initialized
with null but typed as jotai.PrimitiveAtom<BlockHighlightType> (non-nullable);
change the type to include null (jotai.PrimitiveAtom<BlockHighlightType | null>)
and/or annotate the atom generic as jotai.atom<BlockHighlightType | null>(null)
so the declared type matches the nullable initialization.

Copy link
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 (3)
docs/docs/presets.mdx (3)

9-12: Tighten intro (single-item list → sentence).

Reads cleaner as a sentence since only one use-case is listed.

-Wave's preset system allows you to save and apply multiple configuration settings at once. Presets are used for:
-
-- Tab backgrounds: Apply visual styles to your tabs
+Wave's preset system allows you to save and apply multiple configuration settings at once. Currently, presets are used for tab backgrounds (apply visual styles to your tabs).

20-23: Minor MDX hygiene around admonition start.

Ensure exactly one blank line before :::info and no trailing double-spaces at the end of Line 20 to avoid odd rendering. The content is fine.


25-28: Consider adding directory-creation hint for presets folder.

Some users may not have ~/.config/waveterm/presets/ yet.

 wsh editconfig presets.json        # Edit main presets file
 wsh editconfig presets/bg.json     # Edit background presets
+# If the presets directory doesn't exist yet:
+# mkdir -p ~/.config/waveterm/presets
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 591e423 and 404c6c9.

📒 Files selected for processing (2)
  • docs/docs/presets.mdx (3 hunks)
  • frontend/app/aipanel/aimessage.tsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/app/aipanel/aimessage.tsx (1)
frontend/app/block/block-model.ts (1)
  • BlockModel (12-51)
🪛 LanguageTool
docs/docs/presets.mdx

[grammar] ~22-~22: There might be a mistake here.
Context: ...them (e.g., presets/bg.json). :::info You can easily edit your presets using t...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build for TestDriver.ai
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
frontend/app/aipanel/aimessage.tsx (1)

205-206: Well-structured hover highlight implementation.

The use of refs to manage the timeout and track highlighted state is appropriate for this side-effect scenario. The cleanup effect properly prevents memory leaks, and the conditional cursor styling based on blockid presence is good UX.

Also applies to: 228-234, 252-293

Comment on lines +59 to 60
When a preset is applied, it overrides the default configuration values for that tab or block. Using `bg:*` will clear any previously overridden values, setting them back to defaults. It's recommended to include this key in your presets to ensure a clean slate.
:::
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Clarify scope: “tab or block” vs. tab-only bg presets.

The “preset-type” section lists only bg (tab menu). If block-level application isn’t supported, remove “or block”; if it is, mention that in the preset-type list.

-When a preset is applied, it overrides the default configuration values for that tab or block. Using `bg:*` will clear any previously overridden values, setting them back to defaults. It's recommended to include this key in your presets to ensure a clean slate.
+When a preset is applied, it overrides the default configuration values for that tab. Using `bg:*` will clear any previously overridden values, setting them back to defaults. It's recommended to include this key in your presets to ensure a clean slate.

Alternatively (if blocks are supported), add a second bullet under “The preset-type determines...” to document block usage.


🏁 Script executed:

#!/bin/bash
rg -n -C2 'preset-type' docs/docs/presets.mdx
rg -n 'block' docs/docs/presets.mdx

Length of output: 861


Add block-level details to preset-type docs
The bg preset-type also configures block-level backgrounds (e.g., bg:bordercolor, bg:activebordercolor). Under “The preset-type determines…,” add a second bullet noting block usage:

  • bg: Appears in the “Backgrounds” submenu when right-clicking a tab, and applies to blocks via keys like bg:bordercolor and bg:activebordercolor.
🤖 Prompt for AI Agents
In docs/docs/presets.mdx around lines 59-60, add a second bullet under “The
`preset-type` determines…” explaining that the `bg` preset-type not only appears
in the “Backgrounds” submenu when right-clicking a tab but also configures
block-level backgrounds via keys like `bg:bordercolor` and
`bg:activebordercolor`; update that section to include this concise bullet so
readers know `bg` applies to both tabs and blocks with those example keys.

Comment on lines +252 to +285
const handleMouseEnter = () => {
if (!toolData.blockid) return;

if (highlightTimeoutRef.current) {
clearTimeout(highlightTimeoutRef.current);
}

highlightedBlockIdRef.current = toolData.blockid;
BlockModel.getInstance().setBlockHighlight({
blockId: toolData.blockid,
icon: "sparkles",
});

highlightTimeoutRef.current = setTimeout(() => {
if (highlightedBlockIdRef.current === toolData.blockid) {
BlockModel.getInstance().setBlockHighlight(null);
highlightedBlockIdRef.current = null;
}
}, 2000);
};

const handleMouseLeave = () => {
if (!toolData.blockid) return;

if (highlightTimeoutRef.current) {
clearTimeout(highlightTimeoutRef.current);
highlightTimeoutRef.current = null;
}

if (highlightedBlockIdRef.current === toolData.blockid) {
BlockModel.getInstance().setBlockHighlight(null);
highlightedBlockIdRef.current = null;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Race condition: timeout can clear another component's active highlight.

When multiple AIToolUse components are hovered in quick succession, the timeout from an earlier component can incorrectly clear the highlight set by a later component. The check highlightedBlockIdRef.current === toolData.blockid only validates the local ref, not whether the global highlight still corresponds to this component's blockid.

Scenario:

  1. Hover over Tool A (block1) → sets highlight to block1, schedules 2s timeout.
  2. Hover over Tool B (block2) → sets highlight to block2, schedules 2s timeout (A's timeout still pending).
  3. A's timeout fires → checks highlightedBlockIdRef.current === toolData.blockid (true) → clears global highlight.
  4. Result: B's highlight vanishes prematurely even though the user is still interacting with B.

Apply this diff to verify the global state before clearing:

 highlightTimeoutRef.current = setTimeout(() => {
-    if (highlightedBlockIdRef.current === toolData.blockid) {
+    const currentHighlight = globalStore.get(BlockModel.getInstance().blockHighlightAtom);
+    if (highlightedBlockIdRef.current === toolData.blockid && currentHighlight?.blockId === toolData.blockid) {
         BlockModel.getInstance().setBlockHighlight(null);
         highlightedBlockIdRef.current = null;
     }
 }, 2000);

Also update handleMouseLeave for consistency:

 const handleMouseLeave = () => {
     if (!toolData.blockid) return;

     if (highlightTimeoutRef.current) {
         clearTimeout(highlightTimeoutRef.current);
         highlightTimeoutRef.current = null;
     }

-    if (highlightedBlockIdRef.current === toolData.blockid) {
+    const currentHighlight = globalStore.get(BlockModel.getInstance().blockHighlightAtom);
+    if (highlightedBlockIdRef.current === toolData.blockid && currentHighlight?.blockId === toolData.blockid) {
         BlockModel.getInstance().setBlockHighlight(null);
         highlightedBlockIdRef.current = null;
     }
 };

You'll need to import globalStore:

+import { globalStore } from "@/app/store/global";
📝 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
const handleMouseEnter = () => {
if (!toolData.blockid) return;
if (highlightTimeoutRef.current) {
clearTimeout(highlightTimeoutRef.current);
}
highlightedBlockIdRef.current = toolData.blockid;
BlockModel.getInstance().setBlockHighlight({
blockId: toolData.blockid,
icon: "sparkles",
});
highlightTimeoutRef.current = setTimeout(() => {
if (highlightedBlockIdRef.current === toolData.blockid) {
BlockModel.getInstance().setBlockHighlight(null);
highlightedBlockIdRef.current = null;
}
}, 2000);
};
const handleMouseLeave = () => {
if (!toolData.blockid) return;
if (highlightTimeoutRef.current) {
clearTimeout(highlightTimeoutRef.current);
highlightTimeoutRef.current = null;
}
if (highlightedBlockIdRef.current === toolData.blockid) {
BlockModel.getInstance().setBlockHighlight(null);
highlightedBlockIdRef.current = null;
}
};
// at the top of the file, alongside your other imports
import { globalStore } from "@/app/store/global";
const handleMouseEnter = () => {
if (!toolData.blockid) return;
if (highlightTimeoutRef.current) {
clearTimeout(highlightTimeoutRef.current);
}
highlightedBlockIdRef.current = toolData.blockid;
BlockModel.getInstance().setBlockHighlight({
blockId: toolData.blockid,
icon: "sparkles",
});
highlightTimeoutRef.current = setTimeout(() => {
const currentHighlight = globalStore.get(
BlockModel.getInstance().blockHighlightAtom
);
if (
highlightedBlockIdRef.current === toolData.blockid &&
currentHighlight?.blockId === toolData.blockid
) {
BlockModel.getInstance().setBlockHighlight(null);
highlightedBlockIdRef.current = null;
}
}, 2000);
};
const handleMouseLeave = () => {
if (!toolData.blockid) return;
if (highlightTimeoutRef.current) {
clearTimeout(highlightTimeoutRef.current);
highlightTimeoutRef.current = null;
}
const currentHighlight = globalStore.get(
BlockModel.getInstance().blockHighlightAtom
);
if (
highlightedBlockIdRef.current === toolData.blockid &&
currentHighlight?.blockId === toolData.blockid
) {
BlockModel.getInstance().setBlockHighlight(null);
highlightedBlockIdRef.current = null;
}
};
🤖 Prompt for AI Agents
In frontend/app/aipanel/aimessage.tsx around lines 252 to 285, the timeout
handler and mouse-leave logic can clear another component's global highlight
because they only check the component-local ref; update both handlers to verify
the global store's highlighted block id matches this component's
toolData.blockid before clearing the global highlight, and only clear if it
matches (i.e., read globalStore.getHighlightedBlockId() or equivalent and
compare to toolData.blockid), keep clearing and nulling the local timeout/ref as
before, and import globalStore at the top of the file.

Comment on lines 288 to 293
<div
className={cn("flex items-start gap-2 p-2 rounded bg-gray-800 border border-gray-700", statusColor, {
"cursor-pointer": toolData.blockid,
})}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add keyboard navigation and ARIA attributes for accessibility.

The div becomes interactive when toolData.blockid is present (cursor-pointer), but lacks keyboard support and ARIA attributes, preventing keyboard-only users from triggering the highlight.

Consider adding keyboard support:

 <div
     className={cn("flex items-start gap-2 p-2 rounded bg-gray-800 border border-gray-700", statusColor, {
         "cursor-pointer": toolData.blockid,
     })}
     onMouseEnter={handleMouseEnter}
     onMouseLeave={handleMouseLeave}
+    tabIndex={toolData.blockid ? 0 : undefined}
+    onFocus={handleMouseEnter}
+    onBlur={handleMouseLeave}
+    role={toolData.blockid ? "button" : undefined}
+    aria-label={toolData.blockid ? `Highlight block ${toolData.blockid}` : undefined}
 >
📝 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
<div
className={cn("flex items-start gap-2 p-2 rounded bg-gray-800 border border-gray-700", statusColor, {
"cursor-pointer": toolData.blockid,
})}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
<div
className={cn(
"flex items-start gap-2 p-2 rounded bg-gray-800 border border-gray-700",
statusColor,
{ "cursor-pointer": toolData.blockid },
)}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
tabIndex={toolData.blockid ? 0 : undefined}
onFocus={handleMouseEnter}
onBlur={handleMouseLeave}
role={toolData.blockid ? "button" : undefined}
aria-label={toolData.blockid ? `Highlight block ${toolData.blockid}` : undefined}
>
🤖 Prompt for AI Agents
In frontend/app/aipanel/aimessage.tsx around lines 288-293, the interactive div
that is clickable when toolData.blockid exists lacks keyboard support and ARIA
attributes; update it so that when toolData.blockid is present it receives
role="button" and tabIndex={0}, add an onKeyDown handler that triggers the same
highlight/interaction on Enter and Space, include an appropriate aria-label or
aria-pressed (depending on semantics) that describes the action, and ensure
focus-visible styling is applied via className so keyboard users can see focus;
keep these additions conditional on toolData.blockid to avoid making
non-interactive elements tabbable.

@sawka sawka merged commit 0e8eb83 into main Oct 15, 2025
6 of 7 checks passed
@sawka sawka deleted the sawka/tool-block-link branch October 15, 2025 23:10
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