feat: demo features script deloyer - #466
Conversation
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
✅ Deploy Preview for docsccc ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for apiccc ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for liveccc ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
|
Warning Review limit reached
Next review available in: 35 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: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe PR adds a Deploy Script tool with file upload, Type ID cell scanning, deployment, update, burn, pagination, transaction confirmation, and result reporting. It also adds configurable ChangesDeploy Script workflow
Message rendering behavior
Repository ignore rules
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant DeployScript
participant useDeployScript
participant runDeploy
participant CKBNetwork
DeployScript->>useDeployScript: scan and select Type ID cells
useDeployScript-->>DeployScript: cell list and selection state
DeployScript->>runDeploy: file, immutable flag, and selected cell
runDeploy->>CKBNetwork: complete and submit transaction
CKBNetwork-->>runDeploy: transaction hash
runDeploy-->>DeployScript: deployment result hashes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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 |
✅ Deploy Preview for appccc ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (9)
packages/demo/src/app/connected/(tools)/DeployScript/page.tsx (2)
100-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueLog the swallowed error.
The catch block discards the error. The UI then shows "Unavailable" with no way to learn the cause. Add a
console.erroror route it to the existingerrorsender.🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/page.tsx around lines 100 - 108, Update the catch block in the DeployScript page to capture the thrown error and log it with console.error or the existing error sender before preserving the current !cancelled setNewCellOccupiedSizes behavior.
213-233: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract the nested ternaries into helper functions.
toOccupynests four conditional levels across three distinct states (null,undefined, number) anddeployButtonLabelnests four more. Both are hard to read and hard to extend.Move each into a small named function or an early-return block above the JSX. The behavior stays identical.
Note: the distinction between
foundCell.cellOutput.occupiedSizeat line 200 andfoundCell.occupiedSizeindeployComponents.tsxis correct but subtle. A short comment on line 200 stating that the output-only size excludes the data would help future readers.🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/page.tsx around lines 213 - 233, Extract the nested conditional logic for toOccupy and deployButtonLabel into small named helpers or early-return blocks above the JSX, preserving their existing outputs for null, undefined, numeric, operation, file, and typeIdArgs states. Add a brief comment near the foundCell.cellOutput.occupiedSize usage clarifying that this output-only size excludes the cell data.packages/demo/src/app/connected/(tools)/DeployScript/useDeployScript.ts (3)
227-227: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueImport
normalizeTypeIdArgsdirectly instead of re-exporting it through the hook.
normalizeTypeIdArgsis a pure helper with no hook state.page.tsxcan import it from./helpersin the same way this file does. Returning it from the hook adds indirection to the hook contract without benefit.🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/useDeployScript.ts at line 227, Remove normalizeTypeIdArgs from the hook’s returned contract and import it directly from ./helpers in page.tsx, matching the existing import in useDeployScript.ts. Update page.tsx to call the direct helper and keep the hook focused on hook state and behavior.
164-178: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueUse a ref for the in-flight guard.
isLoadingMoreCellsis React state. The guard reads a value from the render closure, andsetIsLoadingMoreCells(true)does not apply until the next render. TwoloadMoreTypeIdCellscalls issued before that render both pass the guard and both consume from the same sharedcellIteratorRef.currentgenerator. That appends overlapping or missing cells at line 187.The disabled "Load More" button makes this hard to reach today. A ref removes the race entirely and protects against future callers.
♻️ Proposed change
+ const isLoadingMoreRef = useRef(false);if ( !signer || !cellIteratorRef.current || !bufferedTypeIdCell || !hasMoreTypeIdCells || - isLoadingMoreCells + isLoadingMoreRef.current ) { return; } + isLoadingMoreRef.current = true; setIsLoadingMoreCells(true);Clear
isLoadingMoreRef.current = falsein thefinallyblock, and dropisLoadingMoreCellsfrom the dependency array.🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/useDeployScript.ts around lines 164 - 178, Update loadMoreTypeIdCells to use an in-flight ref for the concurrency guard instead of the isLoadingMoreCells state captured by the callback; set the ref before starting the load and clear it in the finally block. Keep the state updates for UI rendering, and remove isLoadingMoreCells from the callback dependency array.
105-144: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueA refresh collapses already-loaded pages back to the first page.
refreshTypeIdCellsre-runs this effect with an unchanged signer, so thesignerChangedreset is skipped, but line 142 still replacestypeIdCellswith only the first page. A user who pressed "Load More" several times loses that state after every deploy, update, or burn, becauserefreshCellsAfterTransactioninpage.tsxfires three refreshes.The behavior is safe and recovers with another "Load More" click. Consider re-fetching as many pages as were previously loaded, or documenting the reset in the UI.
🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/useDeployScript.ts around lines 105 - 144, Update refreshTypeIdCells so refreshes preserve the number of previously loaded type-ID pages instead of always replacing typeIdCells with the first page. Re-fetch enough cells for the existing loaded-page count, populate the visible cells and buffered cell consistently, and retain the current cancellation/generation guards.packages/demo/src/app/utils/(tools)/FileUpload/page.tsx (2)
190-196: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd
type="button"to the clear button.Every other button in this component sets
type="button". This one defaults tosubmit. It submits an enclosing form if the component is ever nested in one.♻️ Proposed change
<button + type="button" onClick={handleClearFile}🤖 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 `@packages/demo/src/app/utils/`(tools)/FileUpload/page.tsx around lines 190 - 196, Update the clear-file button that invokes handleClearFile to explicitly set its type to button, matching the other buttons in the component and preventing unintended form submission when nested in a form.
7-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider
file.arrayBuffer()instead ofFileReader.
Blob.prototype.arrayBuffer()is supported in all current browsers. It removes the manual promise wrapper.♻️ Proposed simplification
export async function readFileAsBytes(file: File): Promise<Uint8Array> { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = (e) => { - if (e.target?.result instanceof ArrayBuffer) { - resolve(new Uint8Array(e.target.result)); - } else { - reject(new Error("Failed to read file")); - } - }; - reader.onerror = () => reject(new Error("Failed to read file")); - reader.readAsArrayBuffer(file); - }); + return new Uint8Array(await file.arrayBuffer()); }🤖 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 `@packages/demo/src/app/utils/`(tools)/FileUpload/page.tsx around lines 7 - 20, Update readFileAsBytes to use the file’s native arrayBuffer() method and return a Uint8Array from the resulting buffer. Remove the manual Promise, FileReader setup, event handlers, and associated error handling while preserving the function’s Promise-based async result.packages/demo/src/app/connected/(tools)/DeployScript/deployComponents.tsx (1)
118-123: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winMemoize the data hash.
ccc.hashCkb(foundCell.outputData)runs synchronously on every render.CellFoundSectionre-renders whenever the parent page re-renders, and the parent re-renders on file selection, refresh timers, and scan state changes. Deployed script binaries can reach hundreds of kilobytes, so the hash cost repeats on the main thread without need.♻️ Proposed change
+ const dataHash = useMemo( + () => ccc.hashCkb(foundCell.outputData ?? "0x"), + [foundCell.outputData], + );<p> <span className="font-medium">Data Hash:</span>{" "} - <span className="font-mono break-all"> - {ccc.hashCkb(foundCell.outputData ?? "0x")} - </span> + <span className="font-mono break-all">{dataHash}</span> </p>Add the
useMemoimport fromreact.🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/deployComponents.tsx around lines 118 - 123, Memoize the computed data hash in CellFoundSection instead of invoking ccc.hashCkb during every render. Add the React useMemo import and derive the hash from foundCell.outputData with the existing "0x" fallback, using that memoized value in the Data Hash display.packages/demo/src/app/connected/(tools)/DeployScript/deployLogic.ts (1)
88-104: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueNarrow the return type to
Promise<string>.
runBurnalways returnstxHashor throws. It never returnsnull. The wider type forces the redundantif (!txHash) return;guard atpackages/demo/src/app/connected/(tools)/DeployScript/page.tsxline 122, which silently skips the refresh and theclearSelectioncall if it ever triggers.🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/deployLogic.ts around lines 88 - 104, Update runBurn to return Promise<string> instead of Promise<string | null>, preserving its existing behavior of returning txHash or propagating errors. Remove the now-unnecessary null guard for runBurn’s result in the DeployScript page so refresh and clearSelection always execute after a successful burn.
🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/deployComponents.tsx:
- Around line 40-48: Update the selection button in TypeIdCellListItem to
include an aria-pressed attribute bound to isSelected, exposing the current
selection state to assistive technologies while preserving the existing visual
styling and onClick behavior.
In `@packages/demo/src/app/connected/`(tools)/DeployScript/page.tsx:
- Around line 116-133: Add a confirmation state or modal to the burn flow around
handleBurn that clearly identifies the selected cell and its Type ID, and
require explicit confirmation before invoking runBurn. Keep the existing
transaction, success, error, and cleanup behavior unchanged after confirmation.
In `@packages/demo/src/app/utils/`(tools)/FileUpload/page.tsx:
- Around line 51-67: Move FileUploadArea and readFileAsBytes from the route page
module into shared components/FileUploadArea.tsx so the route default export is
not used as a callback-driven component. Update the imports in
DeployScript/page.tsx and deployLogic.ts to reference the shared module, and
preserve the existing standalone route behavior by adding a page wrapper that
owns file state if that route remains intentional.
In `@packages/demo/src/components/Message.tsx`:
- Around line 46-51: Update the expandable root interaction in the Message
component to be keyboard-accessible: provide focusability, button semantics, an
aria-expanded state tied to isExpanded, and Enter/Space keyboard handling, while
preserving mouse toggling and non-expandable behavior. Prefer a dedicated button
if it fits the existing layout; otherwise apply these semantics directly to the
root div.
---
Nitpick comments:
In `@packages/demo/src/app/connected/`(tools)/DeployScript/deployComponents.tsx:
- Around line 118-123: Memoize the computed data hash in CellFoundSection
instead of invoking ccc.hashCkb during every render. Add the React useMemo
import and derive the hash from foundCell.outputData with the existing "0x"
fallback, using that memoized value in the Data Hash display.
In `@packages/demo/src/app/connected/`(tools)/DeployScript/deployLogic.ts:
- Around line 88-104: Update runBurn to return Promise<string> instead of
Promise<string | null>, preserving its existing behavior of returning txHash or
propagating errors. Remove the now-unnecessary null guard for runBurn’s result
in the DeployScript page so refresh and clearSelection always execute after a
successful burn.
In `@packages/demo/src/app/connected/`(tools)/DeployScript/page.tsx:
- Around line 100-108: Update the catch block in the DeployScript page to
capture the thrown error and log it with console.error or the existing error
sender before preserving the current !cancelled setNewCellOccupiedSizes
behavior.
- Around line 213-233: Extract the nested conditional logic for toOccupy and
deployButtonLabel into small named helpers or early-return blocks above the JSX,
preserving their existing outputs for null, undefined, numeric, operation, file,
and typeIdArgs states. Add a brief comment near the
foundCell.cellOutput.occupiedSize usage clarifying that this output-only size
excludes the cell data.
In `@packages/demo/src/app/connected/`(tools)/DeployScript/useDeployScript.ts:
- Line 227: Remove normalizeTypeIdArgs from the hook’s returned contract and
import it directly from ./helpers in page.tsx, matching the existing import in
useDeployScript.ts. Update page.tsx to call the direct helper and keep the hook
focused on hook state and behavior.
- Around line 164-178: Update loadMoreTypeIdCells to use an in-flight ref for
the concurrency guard instead of the isLoadingMoreCells state captured by the
callback; set the ref before starting the load and clear it in the finally
block. Keep the state updates for UI rendering, and remove isLoadingMoreCells
from the callback dependency array.
- Around line 105-144: Update refreshTypeIdCells so refreshes preserve the
number of previously loaded type-ID pages instead of always replacing
typeIdCells with the first page. Re-fetch enough cells for the existing
loaded-page count, populate the visible cells and buffered cell consistently,
and retain the current cancellation/generation guards.
In `@packages/demo/src/app/utils/`(tools)/FileUpload/page.tsx:
- Around line 190-196: Update the clear-file button that invokes handleClearFile
to explicitly set its type to button, matching the other buttons in the
component and preventing unintended form submission when nested in a form.
- Around line 7-20: Update readFileAsBytes to use the file’s native
arrayBuffer() method and return a Uint8Array from the resulting buffer. Remove
the manual Promise, FileReader setup, event handlers, and associated error
handling while preserving the function’s Promise-based async result.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 312e1e8f-db48-47fe-bba9-ed21339d336f
📒 Files selected for processing (9)
.gitignorepackages/demo/src/app/connected/(tools)/DeployScript/deployComponents.tsxpackages/demo/src/app/connected/(tools)/DeployScript/deployLogic.tspackages/demo/src/app/connected/(tools)/DeployScript/helpers.tspackages/demo/src/app/connected/(tools)/DeployScript/page.tsxpackages/demo/src/app/connected/(tools)/DeployScript/useDeployScript.tspackages/demo/src/app/connected/page.tsxpackages/demo/src/app/utils/(tools)/FileUpload/page.tsxpackages/demo/src/components/Message.tsx
| <button | ||
| type="button" | ||
| onClick={onSelect} | ||
| className={`flex w-full flex-col gap-2 rounded-lg border p-4 text-left text-sm transition-colors hover:border-purple-300 hover:bg-purple-50/50 ${ | ||
| isSelected | ||
| ? "border-purple-400 bg-purple-50" | ||
| : "border-gray-200 bg-white" | ||
| }`} | ||
| > |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add aria-pressed to the cell selection button.
TypeIdCellListItem signals selection only through border and background colors. Screen-reader users receive no selection state. The list is the primary control for choosing which cell to update or burn, so the state matters.
♿ Proposed change
<button
type="button"
onClick={onSelect}
+ aria-pressed={isSelected}
className={`flex w-full flex-col gap-2 rounded-lg border p-4 text-left text-sm transition-colors hover:border-purple-300 hover:bg-purple-50/50 ${📝 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.
| <button | |
| type="button" | |
| onClick={onSelect} | |
| className={`flex w-full flex-col gap-2 rounded-lg border p-4 text-left text-sm transition-colors hover:border-purple-300 hover:bg-purple-50/50 ${ | |
| isSelected | |
| ? "border-purple-400 bg-purple-50" | |
| : "border-gray-200 bg-white" | |
| }`} | |
| > | |
| <button | |
| type="button" | |
| onClick={onSelect} | |
| aria-pressed={isSelected} | |
| className={`flex w-full flex-col gap-2 rounded-lg border p-4 text-left text-sm transition-colors hover:border-purple-300 hover:bg-purple-50/50 ${ | |
| isSelected | |
| ? "border-purple-400 bg-purple-50" | |
| : "border-gray-200 bg-white" | |
| }`} | |
| > |
🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/deployComponents.tsx
around lines 40 - 48, Update the selection button in TypeIdCellListItem to
include an aria-pressed attribute bound to isSelected, exposing the current
selection state to assistive technologies while preserving the existing visual
styling and onClick behavior.
| const handleBurn = useCallback(async () => { | ||
| if (!signer || !foundCell) return; | ||
| setOperation("burn"); | ||
| setLastDeployment(null); | ||
| try { | ||
| const txHash = await runBurn(signer, foundCell, log); | ||
| if (!txHash) return; | ||
| log("Transaction sent:", explorerTransaction(txHash)); | ||
| await signer.client.waitTransaction(txHash); | ||
| log("Transaction committed:", explorerTransaction(txHash)); | ||
| clearSelection(); | ||
| refreshCellsAfterTransaction(); | ||
| } catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| error("Burn failed:", msg); | ||
| } finally { | ||
| setOperation(null); | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Add a confirmation step before burning a cell.
handleBurn builds and sends the burn transaction on the first click. The action destroys the Type ID cell permanently and no undo exists. The wallet prompt shows a raw transaction, which does not tell the user that a deployed script is about to disappear.
Add a confirmation state to the burn button, or show a modal that names the cell and its Type ID before calling runBurn.
🤖 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 `@packages/demo/src/app/connected/`(tools)/DeployScript/page.tsx around lines
116 - 133, Add a confirmation state or modal to the burn flow around handleBurn
that clearly identifies the selected cell and its Type ID, and require explicit
confirmation before invoking runBurn. Keep the existing transaction, success,
error, and cleanup behavior unchanged after confirmation.
| export default function FileUploadArea({ | ||
| file, | ||
| onFileChange, | ||
| fileInputRef: externalFileInputRef, | ||
| toOccupy, | ||
| immutable = false, | ||
| onImmutableChange, | ||
| children, | ||
| }: { | ||
| file: File | null; | ||
| onFileChange: (file: File | null) => void; | ||
| fileInputRef?: React.RefObject<HTMLInputElement | null>; | ||
| toOccupy?: string; | ||
| immutable?: boolean; | ||
| onImmutableChange?: () => void; | ||
| children?: React.ReactNode; | ||
| }) { |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Move FileUploadArea out of a route page.tsx.
This file is a Next.js App Router route (/utils/FileUpload). The default export becomes the page component for that route. Next.js passes only params and searchParams, so onFileChange is undefined when a user opens the route directly. Selecting a file then throws onFileChange is not a function.
The file also exports readFileAsBytes, which deployLogic.ts imports across routes. Both problems disappear if the component moves to a shared module.
Suggested change: create packages/demo/src/components/FileUploadArea.tsx with FileUploadArea and readFileAsBytes, then update the imports in packages/demo/src/app/connected/(tools)/DeployScript/page.tsx and packages/demo/src/app/connected/(tools)/DeployScript/deployLogic.ts. If the standalone route is intentional, add a wrapper page that owns the file state.
🤖 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 `@packages/demo/src/app/utils/`(tools)/FileUpload/page.tsx around lines 51 -
67, Move FileUploadArea and readFileAsBytes from the route page module into
shared components/FileUploadArea.tsx so the route default export is not used as
a callback-driven component. Update the imports in DeployScript/page.tsx and
deployLogic.ts to reference the shared module, and preserve the existing
standalone route behavior by adding a page wrapper that owns file state if that
route remains intentional.
| const showFull = expandable ? isExpanded : true; | ||
|
|
||
| return ( | ||
| <div | ||
| onClick={() => setIsExpanded(!isExpanded)} | ||
| className={`my-2 flex cursor-pointer flex-col items-start rounded-md p-4 ${bgColorClass} ${className}`} | ||
| onClick={expandable ? () => setIsExpanded(!isExpanded) : undefined} | ||
| className={`my-2 flex flex-col items-start rounded-md p-4 ${bgColorClass} ${className} ${expandable ? "cursor-pointer" : ""}`} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Make expandable messages keyboard-accessible.
When expandable is true, the root div is clickable but cannot receive focus and has no aria-expanded or keyboard handler. Keyboard users cannot reveal clamped content. Use a dedicated <button> for the toggle, or implement button semantics with focusability, aria-expanded, and Enter/Space handling. (w3.org)
🤖 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 `@packages/demo/src/components/Message.tsx` around lines 46 - 51, Update the
expandable root interaction in the Message component to be keyboard-accessible:
provide focusability, button semantics, an aria-expanded state tied to
isExpanded, and Enter/Space keyboard handling, while preserving mouse toggling
and non-expandable behavior. Prefer a dedicated button if it fits the existing
layout; otherwise apply these semantics directly to the root div.
967b2bf to
0aafb2f
Compare
|
I know, I know, this PR is not perfect currently. But it won't be published to npm as a part of the SDK so we can update it anytime, and I do think the script deployer is useful. I'll just accept it's imperfect. |

Recreated for #356