Add usage limits UI for copilot#1540
Conversation
📝 WalkthroughWalkthroughIntroduces a new Changes
Sequence Diagram(s)sequenceDiagram
participant UI as AIChat Component
participant RPCClient as AiPanelRpcClient
participant Handler as RPC Handler
participant Mgr as AiPanelRpcManager
participant API as Backend API
UI->>RPCClient: getUsage()
RPCClient->>Handler: request getUsage
Handler->>Mgr: rpcManager.getUsage()
Mgr->>API: fetchWithAuth(BACKEND_URL + LLM_API_BASE_PATH + "/usage")
API-->>Mgr: UsageResponse { remainingUsagePercentage, resetsIn }
Mgr-->>Handler: UsageResponse | undefined
Handler-->>RPCClient: UsageResponse | undefined
RPCClient-->>UI: UsageResponse | undefined
UI->>UI: update usage state, compute isUsageExceeded
UI->>UI: render remaining usage and reset timer, disable input/footer if exceeded
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/StyledInput.tsx (1)
396-409:⚠️ Potential issue | 🟡 MinorMissing
aria-disabledwhendisabled=truebreaks screen reader semanticsThe element has
role="textbox"andaria-multiline="true", but whendisabled=true, no ARIA state communicates the non-editable condition to assistive technologies. A screen reader will still announce this as an interactive text field.♿ Proposed fix
<StyledInput ref={divRef} contentEditable={!disabled} spellCheck="true" onInput={handleInput} onKeyDown={handleKeyDown} onPaste={handlePaste} onBlur={onBlur} suppressContentEditableWarning={true} role="textbox" aria-multiline="true" + aria-disabled={disabled || undefined} data-placeholder={placeholder} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/StyledInput.tsx` around lines 396 - 409, The StyledInput element currently uses role="textbox" and aria-multiline="true" but does not expose the disabled state to AT; update the StyledInput rendered in AIChatInput to include an ARIA state reflecting the prop by adding aria-disabled={disabled} (and ensure it aligns with the existing contentEditable={!disabled} logic) so screen readers know when the textbox is not interactive.workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/index.tsx (1)
465-469:⚠️ Potential issue | 🟡 Minor
handleKeyDowncan triggerhandleSendwhiledisabled=true
contentEditable={false}prevents text input but does not prevent keyboard events. A user who clicks the disabled input area (giving it focus) and presses Enter will callhandleSend()ifinputValue.textis non-empty at the timedisabledbecomestrue.🐛 Proposed fix
- } else if (event.key === "Enter" && !event.shiftKey && !isLoading) { + } else if (event.key === "Enter" && !event.shiftKey && !isLoading && !disabled) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/index.tsx` around lines 465 - 469, The keydown handler (handleKeyDown) currently calls handleSend when Enter is pressed even if the component is disabled; update handleKeyDown to first check the disabled flag (and isLoading) before calling handleSend — e.g., if disabled is true return early (or skip calling handleSend) so Enter presses on a focused, contentEditable={false} element won't trigger send; reference handleKeyDown, disabled, isLoading, handleSend, and inputValue.text when locating the logic to change.
🧹 Nitpick comments (4)
workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/index.tsx (1)
575-594: Attach and command buttons remain interactive whendisabled=trueThe "Attach Context" (
handleAttachClick) and "/" (insertTextAtCursor) buttons have no guard ondisabled. When usage is exceeded, users can still open the file picker and accumulate attachments even though no message can be sent.♻️ Proposed fix
<ActionButton title="Chat with Command" - disabled={inputValue.text !== ""} + disabled={inputValue.text !== "" || disabled} onClick={() => { inputRef.current?.insertTextAtCursor({ text: "/" }); }} > / </ActionButton> ... - <ActionButton title="Attach Context" onClick={handleAttachClick}> + <ActionButton title="Attach Context" onClick={handleAttachClick} disabled={disabled}>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/index.tsx` around lines 575 - 594, The attach and command ActionButton handlers lack checks for the component's disabled state, letting users open the file picker or insert "/" even when actions are disabled; update the ActionButton props and handlers so they respect the same disabled flag used elsewhere (e.g., the send/usage-disabled boolean): add disabled={disabled} to both ActionButton instances, and add early-return guards in handleAttachClick (avoid fileInputRef.current?.click()) and in the command onClick (avoid inputRef.current?.insertTextAtCursor({ text: "/" })) when disabled is true; also add a guard at the start of onAttachmentSelection to ignore file changes if disabled to prevent accumulating attachments.workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx (3)
232-250:formatResetsInandformatResetsInExactare pure functions that should live at module scope.Both helpers have no dependency on component state or props, yet they are re-created on every render. Moving them outside the component is zero-risk and avoids unnecessary allocations.
♻️ Proposed fix
+const formatResetsIn = (seconds: number): string => { + const days = Math.floor(seconds / 86400); + if (days >= 1) return `${days} day${days > 1 ? 's' : ''}`; + const hours = Math.floor(seconds / 3600); + if (hours >= 1) return `${hours} hour${hours > 1 ? 's' : ''}`; + const mins = Math.floor(seconds / 60); + return `${mins} min${mins > 1 ? 's' : ''}`; +}; + +const formatResetsInExact = (seconds: number): string => { + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + const mins = Math.floor((seconds % 3600) / 60); + const parts: string[] = []; + if (days > 0) parts.push(`${days} day${days > 1 ? 's' : ''}`); + if (hours > 0) parts.push(`${hours} hour${hours > 1 ? 's' : ''}`); + if (mins > 0) parts.push(`${mins} minute${mins > 1 ? 's' : ''}`); + return parts.length > 0 ? parts.join(', ') : 'less than a minute'; +}; const AIChat: React.FC = () => { ... - const formatResetsIn = ... - const formatResetsInExact = ...🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx` around lines 232 - 250, formatResetsIn and formatResetsInExact are pure helpers currently declared inside the component causing them to be re-created every render; move both function declarations to module scope (outside the component function) so they are defined once and can be reused, update any references inside the component to call the relocated functions (formatResetsIn, formatResetsInExact), and ensure their TypeScript signatures and exports (if needed) remain unchanged.
159-160:usagestate type duplicatesUsageResponse— import the interface directly.The inline type
{ remainingUsagePercentage: number; resetsIn: number }is identical to theUsageResponseinterface already exported from@wso2/ballerina-core. Any future change to the interface won't be caught here.♻️ Proposed fix
Add to the
@wso2/ballerina-coreimport block (e.g. alongsideApprovalOverlayState):+ UsageResponse,Then update the state declaration:
- const [usage, setUsage] = useState<{ remainingUsagePercentage: number; resetsIn: number } | null>(null); + const [usage, setUsage] = useState<UsageResponse | null>(null);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx` around lines 159 - 160, The inline type used for the usage state duplicates the exported UsageResponse interface; update the import block that currently brings in ApprovalOverlayState from "@wso2/ballerina-core" to also import UsageResponse, then change the state declaration const [usage, setUsage] = useState<...> to use useState<UsageResponse | null>(null) so the component uses the shared interface (referencing usage, setUsage and UsageResponse).
267-267:useEffectmissingrpcClientdependency — inconsistent with the rest of this file.Every other effect in this component that calls into
rpcClientincludes it in the dependency array (e.g., lines 311, 325, 334, 343, 363). The empty[]here capturesfetchUsageat mount time; if the RPC context ever changes the effect will silently skip the refresh.♻️ Proposed fix
- useEffect(() => { fetchUsage(); }, []); + useEffect(() => { fetchUsage(); }, [rpcClient]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx` at line 267, The useEffect that calls fetchUsage in the AIChat component currently has an empty dependency array and therefore captures the mounted fetchUsage closure; change it to include rpcClient (and any other external values used by fetchUsage) so the effect reruns when the RPC context changes — locate the useEffect invocation that calls fetchUsage and update its dependency array to include rpcClient (and ensure fetchUsage either is stable or recreated from rpcClient), matching the pattern used by the other effects in this file.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/index.tsworkspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/interfaces.tsworkspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/rpc-type.tsworkspaces/ballerina/ballerina-extension/src/features/ai/utils/ai-client.tsworkspaces/ballerina/ballerina-extension/src/features/ai/utils/ai-utils.tsworkspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-handler.tsworkspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.tsworkspaces/ballerina/ballerina-extension/src/views/ai-panel/aiMachine.tsworkspaces/ballerina/ballerina-rpc-client/src/rpc-clients/ai-panel/rpc-client.tsworkspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/Footer/index.tsxworkspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsxworkspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/StyledInput.tsxworkspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/index.tsx
💤 Files with no reviewable changes (1)
- workspaces/ballerina/ballerina-extension/src/views/ai-panel/aiMachine.ts
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts`:
- Around line 706-723: In getUsage(), improve error visibility and runtime
validation: when fetchWithAuth returns a non-ok response (response &&
!response.ok) log a warning including response.status and response.statusText
(and optionally await response.text() for more detail) instead of silently
returning undefined; after parsing JSON, validate the payload fields expected by
UsageResponse (e.g., remainingUsagePercentage and resetsIn) by checking they
exist and are numbers (coerce/parseInt/parseFloat as needed) and return
undefined or a safe default if validation fails to avoid NaN in the UI;
reference the getUsage function, fetchWithAuth call, and
UsageResponse/remainingUsagePercentage/resetsIn symbols when making these edits.
In
`@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx`:
- Around line 252-265: In fetchUsage, ensure RPC failures don't leave stale
state: in the catch block reset usage state by calling setUsage(null) and
setIsUsageExceeded(false) so a transient getUsage error won't permanently block
the user; also replace the magic literal 3 used in the comparison
(remainingUsagePercentage < 3) with a clearly named constant (e.g.
USAGE_EXCEEDED_THRESHOLD_PERCENT) defined near the top of the module and use
that constant in the setIsUsageExceeded check to make the threshold explicit;
reference function fetchUsage and the setters setUsage / setIsUsageExceeded plus
remainingUsagePercentage when making the changes.
---
Outside diff comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/index.tsx`:
- Around line 465-469: The keydown handler (handleKeyDown) currently calls
handleSend when Enter is pressed even if the component is disabled; update
handleKeyDown to first check the disabled flag (and isLoading) before calling
handleSend — e.g., if disabled is true return early (or skip calling handleSend)
so Enter presses on a focused, contentEditable={false} element won't trigger
send; reference handleKeyDown, disabled, isLoading, handleSend, and
inputValue.text when locating the logic to change.
In
`@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/StyledInput.tsx`:
- Around line 396-409: The StyledInput element currently uses role="textbox" and
aria-multiline="true" but does not expose the disabled state to AT; update the
StyledInput rendered in AIChatInput to include an ARIA state reflecting the prop
by adding aria-disabled={disabled} (and ensure it aligns with the existing
contentEditable={!disabled} logic) so screen readers know when the textbox is
not interactive.
---
Nitpick comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx`:
- Around line 232-250: formatResetsIn and formatResetsInExact are pure helpers
currently declared inside the component causing them to be re-created every
render; move both function declarations to module scope (outside the component
function) so they are defined once and can be reused, update any references
inside the component to call the relocated functions (formatResetsIn,
formatResetsInExact), and ensure their TypeScript signatures and exports (if
needed) remain unchanged.
- Around line 159-160: The inline type used for the usage state duplicates the
exported UsageResponse interface; update the import block that currently brings
in ApprovalOverlayState from "@wso2/ballerina-core" to also import
UsageResponse, then change the state declaration const [usage, setUsage] =
useState<...> to use useState<UsageResponse | null>(null) so the component uses
the shared interface (referencing usage, setUsage and UsageResponse).
- Line 267: The useEffect that calls fetchUsage in the AIChat component
currently has an empty dependency array and therefore captures the mounted
fetchUsage closure; change it to include rpcClient (and any other external
values used by fetchUsage) so the effect reruns when the RPC context changes —
locate the useEffect invocation that calls fetchUsage and update its dependency
array to include rpcClient (and ensure fetchUsage either is stable or recreated
from rpcClient), matching the pattern used by the other effects in this file.
In
`@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChatInput/index.tsx`:
- Around line 575-594: The attach and command ActionButton handlers lack checks
for the component's disabled state, letting users open the file picker or insert
"/" even when actions are disabled; update the ActionButton props and handlers
so they respect the same disabled flag used elsewhere (e.g., the
send/usage-disabled boolean): add disabled={disabled} to both ActionButton
instances, and add early-return guards in handleAttachClick (avoid
fileInputRef.current?.click()) and in the command onClick (avoid
inputRef.current?.insertTextAtCursor({ text: "/" })) when disabled is true; also
add a guard at the start of onAttachmentSelection to ignore file changes if
disabled to prevent accumulating attachments.
workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts
Show resolved
Hide resolved
workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx (1)
807-849:⚠️ Potential issue | 🟡 MinorAdd
fetchUsage()call to the error handler to refresh usage state on 429 rate-limit responses.The stop handler refreshes usage (line 774), but the error handler does not. If the backend emits an error event without a subsequent stop event—particularly for rate-limit (429) responses—
isUsageExceededremains stale, the footer stays enabled, and users can continue submitting requests that fail with 429.Proposed fix
} else if (type === "error") { // ... existing error handling ... setIsCodeLoading(false); setIsLoading(false); isErrorChunkReceivedRef.current = true; + fetchUsage(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx` around lines 807 - 849, The error handler for type === "error" must refresh usage state like the stop handler does: call fetchUsage() after setting isErrorChunkReceivedRef.current = true (and/or before calling setIsLoading(false)/setIsCodeLoading(false)) to ensure isUsageExceeded is updated on 429 responses; update the block inside the setMessages callback (where newMessages[...] gets errorTemplate) to invoke fetchUsage() (reference fetchUsage, setMessages, setIsCodeLoading, setIsLoading, isErrorChunkReceivedRef, and SYSTEM_ERROR_SECRET) so the UI footer/usage state is refreshed when an error event arrives.
♻️ Duplicate comments (1)
workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx (1)
92-93: Previous concerns fully addressed — LGTM.
USAGE_EXCEEDED_THRESHOLD_PERCENTconstant is in place and thecatchblock infetchUsagenow resets bothusageandisUsageExceededto prevent permanent lockout on transient RPC failures.Also applies to: 254-270
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx` around lines 92 - 93, The review confirms USAGE_EXCEEDED_THRESHOLD_PERCENT and the updated fetchUsage catch block that resets both usage and isUsageExceeded are correct; apply the same fix to the duplicate code at lines 254-270 by ensuring any other fetchUsage (or similarly named usage-fetching) implementation uses the USAGE_EXCEEDED_THRESHOLD_PERCENT constant and that its catch handler resets both usage and isUsageExceeded to safe defaults to avoid permanent lockout on transient RPC failures.
🧹 Nitpick comments (2)
workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx (2)
234-252: MoveformatResetsIn/formatResetsInExactto module scope.Neither function captures any component state or props, yet they are redefined on every render. Moving them outside
AIChatcosts nothing and makes them easier to unit-test.♻️ Proposed refactor
+const formatResetsIn = (seconds: number): string => { + const days = Math.floor(seconds / 86400); + if (days >= 1) return `${days} day${days > 1 ? 's' : ''}`; + const hours = Math.floor(seconds / 3600); + if (hours >= 1) return `${hours} hour${hours > 1 ? 's' : ''}`; + const mins = Math.floor(seconds / 60); + return `${mins} min${mins > 1 ? 's' : ''}`; +}; + +const formatResetsInExact = (seconds: number): string => { + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + const mins = Math.floor((seconds % 3600) / 60); + const parts: string[] = []; + if (days > 0) parts.push(`${days} day${days > 1 ? 's' : ''}`); + if (hours > 0) parts.push(`${hours} hour${hours > 1 ? 's' : ''}`); + if (mins > 0) parts.push(`${mins} minute${mins > 1 ? 's' : ''}`); + return parts.length > 0 ? parts.join(', ') : 'less than a minute'; +}; + const AIChat: React.FC = () => { ... - const formatResetsIn = (seconds: number): string => { ... }; - const formatResetsInExact = (seconds: number): string => { ... };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx` around lines 234 - 252, The helper functions formatResetsIn and formatResetsInExact are defined inside the AIChat component and get re-created on every render; move both functions to module scope (outside the AIChat component) so they are defined once, remain pure (no component state/props), and become easier to unit-test—update any internal references to call the module-level formatResetsIn and formatResetsInExact and ensure their signatures remain (seconds: number) => string.
161-163: UseUsageResponsetype and deriveisUsageExceededto eliminate redundant state.The inline type
{ remainingUsagePercentage: number; resetsIn: number }should be replaced withUsageResponsefrom@wso2/ballerina-core— they are identical in structure. More importantly,isUsageExceededis purely derived fromusageand only updated together infetchUsage. Keeping it as separate state adds unnecessary bookkeeping.♻️ Proposed refactor
-import { ..., UsageResponse } from "@wso2/ballerina-core"; +import { + ..., + UsageResponse, +} from "@wso2/ballerina-core"; ... -const [usage, setUsage] = useState<{ remainingUsagePercentage: number; resetsIn: number } | null>(null); -const [isUsageExceeded, setIsUsageExceeded] = useState(false); +const [usage, setUsage] = useState<UsageResponse | null>(null); + +const isUsageExceeded = usage !== null + && usage.resetsIn !== -1 + && usage.remainingUsagePercentage < USAGE_EXCEEDED_THRESHOLD_PERCENT;Remove all
setIsUsageExceeded(...)calls fromfetchUsage.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx` around lines 161 - 163, Replace the inline usage type with the shared UsageResponse from `@wso2/ballerina-core` by changing the usage state declaration to use UsageResponse | null, remove the separate isUsageExceeded state entirely, and compute the boolean where needed (e.g., const isUsageExceeded = usage ? usage.remainingUsagePercentage <= 0 : false) instead of storing it; update any places that call setIsUsageExceeded (notably inside fetchUsage) to only call setUsage and remove those setIsUsageExceeded calls so the derived value is calculated from usage at render/usage-read sites.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx`:
- Around line 807-849: The error handler for type === "error" must refresh usage
state like the stop handler does: call fetchUsage() after setting
isErrorChunkReceivedRef.current = true (and/or before calling
setIsLoading(false)/setIsCodeLoading(false)) to ensure isUsageExceeded is
updated on 429 responses; update the block inside the setMessages callback
(where newMessages[...] gets errorTemplate) to invoke fetchUsage() (reference
fetchUsage, setMessages, setIsCodeLoading, setIsLoading,
isErrorChunkReceivedRef, and SYSTEM_ERROR_SECRET) so the UI footer/usage state
is refreshed when an error event arrives.
---
Duplicate comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx`:
- Around line 92-93: The review confirms USAGE_EXCEEDED_THRESHOLD_PERCENT and
the updated fetchUsage catch block that resets both usage and isUsageExceeded
are correct; apply the same fix to the duplicate code at lines 254-270 by
ensuring any other fetchUsage (or similarly named usage-fetching) implementation
uses the USAGE_EXCEEDED_THRESHOLD_PERCENT constant and that its catch handler
resets both usage and isUsageExceeded to safe defaults to avoid permanent
lockout on transient RPC failures.
---
Nitpick comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx`:
- Around line 234-252: The helper functions formatResetsIn and
formatResetsInExact are defined inside the AIChat component and get re-created
on every render; move both functions to module scope (outside the AIChat
component) so they are defined once, remain pure (no component state/props), and
become easier to unit-test—update any internal references to call the
module-level formatResetsIn and formatResetsInExact and ensure their signatures
remain (seconds: number) => string.
- Around line 161-163: Replace the inline usage type with the shared
UsageResponse from `@wso2/ballerina-core` by changing the usage state declaration
to use UsageResponse | null, remove the separate isUsageExceeded state entirely,
and compute the boolean where needed (e.g., const isUsageExceeded = usage ?
usage.remainingUsagePercentage <= 0 : false) instead of storing it; update any
places that call setIsUsageExceeded (notably inside fetchUsage) to only call
setUsage and remove those setIsUsageExceeded calls so the derived value is
calculated from usage at render/usage-read sites.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.tsworkspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts
Purpose
$Subject
Goals
Approach
UI Component Development
npm run storybookfrom the root directory to view current components.Manage Icons
User stories
Release note
Documentation
Training
Certification
Marketing
Automation tests
Security checks
Samples
Related PRs
Migrations (if applicable)
Test environment
Learning
Summary by CodeRabbit
New Features
Bug Fixes