Conversation
WalkthroughAPI key display components are refactored to address truncation issues by replacing Input-based displays with native code blocks that properly handle full-length keys. Copy-to-clipboard functionality and visibility toggles are enhanced with improved UX patterns including tooltips and adjusted button positioning. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 (1)
web/src/components/ApiKeyAuthorize.standalone.vue (1)
162-172:⚠️ Potential issue | 🟠 MajorAdd accessible names to icon-only key action controls.
Line 162 and Line 170 render icon-only controls without an accessible name, which makes these actions ambiguous for screen readers.
♿ Suggested fix
<button type="button" + :aria-label="showKey ? 'Hide API key' : 'Show API key'" + :title="showKey ? 'Hide API key' : 'Show API key'" class="text-muted-foreground hover:text-foreground absolute top-2 right-2 flex items-center px-1" `@click`="toggleShowKey" > <component :is="showKey ? EyeSlashIcon : EyeIcon" class="h-4 w-4" /> </button> </div> - <Button variant="outline" size="icon" `@click`="copyApiKey"> + <Button + variant="outline" + size="icon" + aria-label="Copy API key to clipboard" + title="Copy API key to clipboard" + `@click`="copyApiKey" + > <ClipboardDocumentIcon class="h-4 w-4" /> </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ApiKeyAuthorize.standalone.vue` around lines 162 - 172, The icon-only controls for toggling visibility and copying the API key lack accessible names; update the toggle button (the element using `@click`="toggleShowKey" and <component :is="showKey ? EyeSlashIcon : EyeIcon">) and the copy Button (the Button invoking `@click`="copyApiKey" with <ClipboardDocumentIcon>) to include descriptive accessible attributes (e.g., aria-label or aria-labelledby, and optional title) that reflect their actions like "Show API key"/"Hide API key" for the toggle (use showKey to pick the correct label) and "Copy API key" for the clipboard button so screen readers can identify the controls.
🧹 Nitpick comments (1)
web/src/components/ApiKey/ApiKeyManager.vue (1)
280-280: Track copy feedback per key row instead of globally.Line 280 uses shared
copied, so a tooltip in a different row can briefly show “Copied!” after another key was copied. Consider trackingcopiedKeyIdfor row-specific feedback.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ApiKey/ApiKeyManager.vue` at line 280, The component currently uses a single shared reactive flag copied which causes any row's tooltip to show “Copied!” when any key is copied; change to a row-scoped identifier copiedKeyId (or similar) instead: replace the copied boolean with copiedKeyId in the component state (null when none), update the copy handler (e.g., the method that copies a key) to set copiedKeyId = key.id and start/clear the timeout to reset it to null, and update the template expression that currently reads "copied ? 'Copied!' : 'Copy to clipboard...'" to use "copiedKeyId === key.id ? 'Copied!' : 'Copy to clipboard...'". Ensure all references to copied are updated to the new variable (including any imports, watchers, or timeouts).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/components/ApiKey/ApiKeyManager.vue`:
- Around line 270-277: The icon-only Button elements in ApiKeyManager.vue (the
copy button that calls copyKeyValue(key.key) and the visibility button that
toggles key visibility) lack accessible names; add clear accessible labels by
adding aria-label attributes (or visually hidden text) to those Button
components (e.g., aria-label="Copy API key" for the ClipboardDocumentIcon button
and aria-label="Show API key" / "Hide API key" for the visibility toggle) and
ensure the toggle updates its label based on state so screen readers get the
correct action.
- Around line 267-283: The TooltipTrigger wrapping the Button should include the
as-child prop to pass semantics to the wrapped interactive element; update the
TooltipTrigger surrounding the Button (the one that calls copyKeyValue(key.key)
and references copied) to use as-child so the Button remains the real focusable
element and keyboard/focus behavior is preserved.
---
Outside diff comments:
In `@web/src/components/ApiKeyAuthorize.standalone.vue`:
- Around line 162-172: The icon-only controls for toggling visibility and
copying the API key lack accessible names; update the toggle button (the element
using `@click`="toggleShowKey" and <component :is="showKey ? EyeSlashIcon :
EyeIcon">) and the copy Button (the Button invoking `@click`="copyApiKey" with
<ClipboardDocumentIcon>) to include descriptive accessible attributes (e.g.,
aria-label or aria-labelledby, and optional title) that reflect their actions
like "Show API key"/"Hide API key" for the toggle (use showKey to pick the
correct label) and "Copy API key" for the clipboard button so screen readers can
identify the controls.
---
Nitpick comments:
In `@web/src/components/ApiKey/ApiKeyManager.vue`:
- Line 280: The component currently uses a single shared reactive flag copied
which causes any row's tooltip to show “Copied!” when any key is copied; change
to a row-scoped identifier copiedKeyId (or similar) instead: replace the copied
boolean with copiedKeyId in the component state (null when none), update the
copy handler (e.g., the method that copies a key) to set copiedKeyId = key.id
and start/clear the timeout to reset it to null, and update the template
expression that currently reads "copied ? 'Copied!' : 'Copy to clipboard...'" to
use "copiedKeyId === key.id ? 'Copied!' : 'Copy to clipboard...'". Ensure all
references to copied are updated to the new variable (including any imports,
watchers, or timeouts).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 8d34c8c3-e848-471f-99c1-43636a11fef0
📒 Files selected for processing (2)
web/src/components/ApiKey/ApiKeyManager.vueweb/src/components/ApiKeyAuthorize.standalone.vue
| <TooltipProvider> | ||
| <Tooltip :delay-duration="0"> | ||
| <TooltipTrigger> | ||
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| class="h-8 w-8" | ||
| @click="copyKeyValue(key.key)" | ||
| > | ||
| <ClipboardDocumentIcon class="h-4 w-4" /> | ||
| </Button> | ||
| </TooltipTrigger> | ||
| <TooltipContent> | ||
| <p>{{ copied ? 'Copied!' : 'Copy to clipboard...' }}</p> | ||
| </TooltipContent> | ||
| </Tooltip> | ||
| </TooltipProvider> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, verify the file exists and check the actual code at the specified lines
fd "ApiKeyManager.vue" webRepository: unraid/api
Length of output: 97
🏁 Script executed:
# Read the file to confirm the code snippet
cat -n web/src/components/ApiKey/ApiKeyManager.vue | sed -n '260,290p'Repository: unraid/api
Length of output: 1789
🏁 Script executed:
# Search for TooltipTrigger imports and usage in the file
rg -n "TooltipTrigger|import.*Tooltip" web/src/components/ApiKey/ApiKeyManager.vueRepository: unraid/api
Length of output: 257
🏁 Script executed:
# Search for TooltipTrigger component definition or package info
rg -n "TooltipTrigger" web/src/components --type=vue -A2 | head -50Repository: unraid/api
Length of output: 81
🏁 Script executed:
# Check if as-child is used elsewhere with TooltipTrigger in the codebase
rg "TooltipTrigger.*as-child" web --type=vueRepository: unraid/api
Length of output: 81
🏁 Script executed:
# Read around line 332 to see the context where as-child IS used
cat -n web/src/components/ApiKey/ApiKeyManager.vue | sed -n '325,345p'Repository: unraid/api
Length of output: 1204
🏁 Script executed:
# Search for TooltipTrigger patterns without type flag
rg -n "TooltipTrigger" web/src/components/ApiKey/ApiKeyManager.vue -C2Repository: unraid/api
Length of output: 1278
🏁 Script executed:
# Check for TooltipTrigger usage across the entire codebase
rg "TooltipTrigger(?!.*as-child)" web/src --multiline-dotall 2>/dev/null | head -40Repository: unraid/api
Length of output: 36
🏁 Script executed:
# Find where TooltipTrigger component is defined or imported from
rg "from.*TooltipTrigger|import.*TooltipTrigger" web/srcRepository: unraid/api
Length of output: 190
🏁 Script executed:
# Find `@unraid/ui` location - is it in the repo or external?
fd -t f "@unraid/ui" . 2>/dev/null || find . -name "*unraid*" -type d 2>/dev/null | grep -v node_modules | head -20Repository: unraid/api
Length of output: 36
🏁 Script executed:
# Search for all TooltipTrigger usages in the codebase
rg "TooltipTrigger" web/src --no-heading | sort | uniq -cRepository: unraid/api
Length of output: 1567
🏁 Script executed:
# Get all TooltipTrigger context across the codebase
rg -B1 -A4 "TooltipTrigger" web/src 2>/dev/nullRepository: unraid/api
Length of output: 8458
🏁 Script executed:
# Check package.json for `@unraid/ui` info
fd "package.json" web -exec cat {} \; | grep -A5 -B5 "@unraid/ui" | head -20Repository: unraid/api
Length of output: 200
Add as-child to TooltipTrigger for proper semantics.
Line 269 wraps a <Button> inside TooltipTrigger without the as-child prop. The rest of the codebase consistently uses as-child when wrapping interactive elements—see line 332 in the same file for the correct pattern. Without as-child, nested button elements can break focus behavior and keyboard accessibility.
🛠️ Suggested fix
- <TooltipTrigger>
+ <TooltipTrigger as-child>
<Button
variant="ghost"
size="icon"
class="h-8 w-8"
`@click`="copyKeyValue(key.key)"
>
<ClipboardDocumentIcon class="h-4 w-4" />
</Button>
- </TooltipTrigger>
+ </TooltipTrigger>📝 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.
| <TooltipProvider> | |
| <Tooltip :delay-duration="0"> | |
| <TooltipTrigger> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| class="h-8 w-8" | |
| @click="copyKeyValue(key.key)" | |
| > | |
| <ClipboardDocumentIcon class="h-4 w-4" /> | |
| </Button> | |
| </TooltipTrigger> | |
| <TooltipContent> | |
| <p>{{ copied ? 'Copied!' : 'Copy to clipboard...' }}</p> | |
| </TooltipContent> | |
| </Tooltip> | |
| </TooltipProvider> | |
| <TooltipProvider> | |
| <Tooltip :delay-duration="0"> | |
| <TooltipTrigger as-child> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| class="h-8 w-8" | |
| `@click`="copyKeyValue(key.key)" | |
| > | |
| <ClipboardDocumentIcon class="h-4 w-4" /> | |
| </Button> | |
| </TooltipTrigger> | |
| <TooltipContent> | |
| <p>{{ copied ? 'Copied!' : 'Copy to clipboard...' }}</p> | |
| </TooltipContent> | |
| </Tooltip> | |
| </TooltipProvider> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ApiKey/ApiKeyManager.vue` around lines 267 - 283, The
TooltipTrigger wrapping the Button should include the as-child prop to pass
semantics to the wrapped interactive element; update the TooltipTrigger
surrounding the Button (the one that calls copyKeyValue(key.key) and references
copied) to use as-child so the Button remains the real focusable element and
keyboard/focus behavior is preserved.
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| class="h-8 w-8" | ||
| @click="copyKeyValue(key.key)" | ||
| > | ||
| <ClipboardDocumentIcon class="h-4 w-4" /> | ||
| </Button> |
There was a problem hiding this comment.
Add accessible names to icon-only copy and visibility buttons.
Line 270 and Line 291 use icon-only buttons without accessible names, reducing usability for assistive technologies.
♿ Suggested fix
<Button
variant="ghost"
size="icon"
class="h-8 w-8"
+ aria-label="Copy API key to clipboard"
+ title="Copy API key to clipboard"
`@click`="copyKeyValue(key.key)"
>
<ClipboardDocumentIcon class="h-4 w-4" />
</Button>
@@
<Button
variant="ghost"
size="icon"
class="absolute top-2 right-2 h-auto w-auto px-1 text-gray-500 hover:text-gray-700"
+ :aria-label="showKey[key.id] ? 'Hide API key' : 'Show API key'"
+ :title="showKey[key.id] ? 'Hide API key' : 'Show API key'"
`@click`="toggleShowKey(key.id)"
>Also applies to: 291-298
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ApiKey/ApiKeyManager.vue` around lines 270 - 277, The
icon-only Button elements in ApiKeyManager.vue (the copy button that calls
copyKeyValue(key.key) and the visibility button that toggles key visibility)
lack accessible names; add clear accessible labels by adding aria-label
attributes (or visually hidden text) to those Button components (e.g.,
aria-label="Copy API key" for the ClipboardDocumentIcon button and
aria-label="Show API key" / "Hide API key" for the visibility toggle) and ensure
the toggle updates its label based on state so screen readers get the correct
action.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1890 +/- ##
==========================================
+ Coverage 46.40% 48.65% +2.24%
==========================================
Files 954 1013 +59
Lines 59791 67635 +7844
Branches 5538 6944 +1406
==========================================
+ Hits 27749 32908 +5159
- Misses 31923 34606 +2683
- Partials 119 121 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Summary
Validation
Fixes #1889
Summary by CodeRabbit