-
Notifications
You must be signed in to change notification settings - Fork 119
Daily RC #441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Daily RC #441
Conversation
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
WalkthroughThis PR refactors the Spinner component from a custom implementation to a lucide-react-based one, reorganizing it from Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Surface as Viewport Surface
participant RCM as RulerContextMenu
participant Ruler as AxisRuler
participant Editor as Editor
participant Toast as Toast
User->>Surface: Right-click on ruler
Surface->>RCM: Trigger context menu
RCM->>Ruler: Render ruler with menu wrapper
User->>RCM: Click "Hide ruler"
RCM->>Editor: Call editor.surface.surfaceConfigureRuler("off")
Editor->>Editor: Update ruler state
RCM->>Toast: Show "Ruler off" notification
Toast-->>User: Display success message
sequenceDiagram
participant App as App
participant Paste as Paste Handler
participant Target as Target Selection
participant HitTest as Hit Test
participant Transform as Transform
App->>Paste: User pastes nodes
Paste->>Target: Calculate target parents from selection
alt Selection exists
Target->>Target: Use container parent or parent container
Paste->>Transform: Transform with selected parent
else No selection
Paste->>HitTest: Perform nested insertion hit-test
HitTest->>HitTest: Exclude copied nodes, filter containers
alt Parent found via hit-test
Paste->>Transform: Adjust positions relative to hit-tested parent
Transform->>App: Apply layout adjustments
else No parent found
Paste->>App: Paste at scene root
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas requiring extra attention:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
grida/editor/kits/minimal-tiptap/extensions/image/components/image-view-block.tsx
Line 13 in 4c1a144
| import { Spinner } from "../../../components/spinner"; |
The relative import "../../../components/spinner" points to the deleted editor/components/spinner folder, so this module can no longer be resolved and the editor fails to build. Switch the import to the new location (e.g. @/components/ui/spinner) or restore the previous barrel export.
grida/editor/kits/minimal-tiptap/extensions/image/components/image-overlay.tsx
Lines 1 to 3 in 4c1a144
| import * as React from "react"; | |
| import { Spinner } from "../../../components/spinner"; | |
| import { cn } from "@/components/lib/utils"; |
This component still imports Spinner from the removed editor/components/spinner directory, so bundling or typechecking will fail with “Cannot find module '../../../components/spinner'”. Import the spinner from @/components/ui/spinner instead.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| function ButtonGroup({ | ||
| className, | ||
| orientation, | ||
| ...props | ||
| }: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) { | ||
| return ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add React import for button group component
This file uses React.ComponentProps to type the component props, but React is never imported. With jsx: "react-jsx" the runtime import is optional, yet the React namespace is still required for type references and tsc will fail with “Cannot find namespace 'React'” when building. Add import * as React from "react"; (or import just the needed types) before using React.ComponentProps.
Useful? React with 👍 / 👎.
| function Spinner({ className, ...props }: React.ComponentProps<"svg">) { | ||
| return ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add React import for Spinner component
The new spinner component also types its props via React.ComponentProps<"svg"> without importing React, which causes the same Cannot find namespace 'React' TypeScript error as soon as this module is compiled. Import React (or just type ComponentProps) so the file type-checks.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
editor/app/(site)/sign-in/email/_page.tsx (1)
86-92: Block open redirects from query paramsnext and redirect_uri are used directly. This allows open-redirect to external origins. Constrain to same-origin relative paths.
Apply:
@@ - if (next) { - router.replace(next); - } else if (redirect_uri) { - router.replace(redirect_uri); - } else { - router.replace("/dashboard"); - } + const safeRedirect = (target?: string) => { + if (!target) return "/dashboard"; + try { + const url = new URL(target, window.location.origin); + if (url.origin !== window.location.origin) return "/dashboard"; + return url.pathname + url.search + url.hash; + } catch { + return target.startsWith("/") ? target : "/dashboard"; + } + }; + if (next) { + router.replace(safeRedirect(next)); + } else if (redirect_uri) { + router.replace(safeRedirect(redirect_uri)); + } else { + router.replace("/dashboard"); + }editor/scaffolds/settings/response-preferences.tsx (3)
100-113: Coerce number input to a number in ControllerWithout coercion, RHF will store a string; n comparisons and API payload may be wrong.
<Controller name="max" control={control} render={({ field }) => ( <Input type="number" + inputMode="numeric" min={1} value={field.value} - onChange={field.onChange} + step={1} + onChange={(e) => field.onChange(e.target.valueAsNumber)} /> )} />
268-279: Apply the same numeric coercion in “total responses” sectionMirror the fix to keep types consistent and avoid string payloads.
<Controller name="max" control={control} render={({ field }) => ( <Input type="number" placeholder="Leave empty for unlimited responses" min={1} - value={field.value} - onChange={field.onChange} + inputMode="numeric" + step={1} + value={field.value} + onChange={(e) => field.onChange(e.target.valueAsNumber)} /> )} />
163-176: Fix minor text typosSmall copy fixes for polish.
- Lean more + Learn more @@ - Fingerprint generation for some platform/environment may confict + Fingerprint generation for some platforms/environments may conflict @@ - <strong>Vunarable platforms:</strong> + <strong>Vulnerable platforms:</strong>Also applies to: 168-170, 172-176
🧹 Nitpick comments (20)
editor/scaffolds/sidebar/sidebar.tsx (1)
61-64: Good UI enhancement with the back navigation icon.The CaretLeftIcon provides a clear visual cue for the back action. For consistency with the SlashIcon usage on line 68 (which has explicit width/height props), consider adding size props to the CaretLeftIcon.
- <CaretLeftIcon /> + <CaretLeftIcon width={15} height={15} />editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (1)
1361-1361: Good use of the disabled prop for root nodes.Disabling alignment controls when
is_rootis true makes sense—alignment operations require sibling context, which root nodes lack by definition. The logic correctly prevents meaningless operations.Optional enhancement: Consider adding a tooltip to the
Aligncomponent that explains why alignment is disabled when viewing a root node. This would improve discoverability, though it's not critical.Example enhancement to the
Aligncomponent:function Align({ disabled }: { disabled?: boolean }) { const editor = useCurrentEditor(); const { selection } = useSelectionState(); const has_selection = selection.length >= 1; return ( <Tooltip> <TooltipTrigger asChild> <_AlignControl disabled={!has_selection || disabled} onAlign={(alignment) => { editor.commands.align("selection", alignment); }} onDistributeEvenly={(axis) => { editor.commands.distributeEvenly("selection", axis); }} className="justify-between" /> </TooltipTrigger> {disabled && ( <TooltipContent> Alignment unavailable for root nodes </TooltipContent> )} </Tooltip> ); }editor/grida-canvas/reducers/surface.reducer.ts (1)
43-53: Consider null-safe handling for child node bounding rects.For consistency with the null-safety improvements made to parent bounding rects (lines 36-40), consider handling the case where
abs_rectmight be undefined. While child nodes in a layout snapshot should typically have bounding rects, defensive coding would prevent potential runtime errors.Example:
const objects: editor.gesture.LayoutSnapshot["objects"] = items.map( (node_id) => { - const abs_rect = context.geometry.getNodeAbsoluteBoundingRect(node_id)!; + const abs_rect = context.geometry.getNodeAbsoluteBoundingRect(node_id); + if (!abs_rect) { + throw new Error(`Node ${node_id} has no bounding rect`); + } const rel_rect = cmath.rect.translate(abs_rect, reldelta); return { ...rel_rect, id: node_id, }; } );editor/grida-canvas/reducers/document.reducer.ts (2)
688-701: Null-guard hit‑tested parent rect to avoid rare runtime crashGeometry may return null for degenerate/invisible nodes. Guard instead of asserting.
- if (parent_was_hit_tested && parent) { - const parent_rect = - context.geometry.getNodeAbsoluteBoundingRect(parent)!; - sub.scene.children_refs.forEach((node_id) => { - const node = sub.nodes[node_id]; - if ("position" in node && node.position === "absolute") { - node.left = (node.left ?? 0) - parent_rect.x; - node.top = (node.top ?? 0) - parent_rect.y; - } - }); - } + if (parent_was_hit_tested && parent) { + const parent_rect = + context.geometry.getNodeAbsoluteBoundingRect(parent); + if (parent_rect) { + sub.scene.children_refs.forEach((node_id) => { + const node = sub.nodes[node_id]; + if ("position" in node && node.position === "absolute") { + node.left = (node.left ?? 0) - parent_rect.x; + node.top = (node.top ?? 0) - parent_rect.y; + } + }); + } + }
603-608: UX follow‑up: remember last hit‑tested parent for subsequent pastesCurrent limitation makes only the first paste position-adjusted when using hit‑test. Consider storing the last hit‑tested parent in transient editor state and using it as the implicit target on immediate subsequent pastes (until selection changes), to keep paste behavior consistent.
Is duplicating across multiple selected parents an intended behavior change vs. previous “first valid target” logic?
editor/grida-canvas-react/viewport/surface.tsx (1)
1644-1654: Consider adding bidirectional ruler toggle in context menu.Currently, the context menu only provides a "Hide ruler" option. For improved UX, consider adding conditional logic to show either "Hide ruler" or "Show ruler" based on the current ruler state. This would allow users to toggle the ruler directly from the context menu without needing to access other menus.
Example implementation:
+ const ruler = useEditorState(editor, (state) => state.ruler); + return ( <ContextMenu> <ContextMenuTrigger asChild>{children}</ContextMenuTrigger> <ContextMenuContent className="w-48"> <ContextMenuItem className="text-xs" onSelect={() => { - editor.surface.surfaceConfigureRuler("off"); - toast.success("Ruler off"); + const newState = ruler === "on" ? "off" : "on"; + editor.surface.surfaceConfigureRuler(newState); + toast.success(`Ruler ${newState}`); }} > - Hide ruler + {ruler === "on" ? "Hide ruler" : "Show ruler"} <ContextMenuShortcut>⇧R</ContextMenuShortcut> </ContextMenuItem> </ContextMenuContent> </ContextMenu> );editor/components/ui/separator.tsx (1)
1-6: Code formatting improvements.Added semicolons for consistency across the file. This improves code style uniformity.
Also applies to: 25-28
editor/app/(workbench)/[org]/[proj]/[id]/canvas/page.tsx (1)
30-32: Add minimal a11y context for loading state.A lone spinner can be silent for screen readers. Optional: wrap with a status container and a visually hidden label.
- if (!document) { - return <Spinner />; - } + if (!document) { + return ( + <div role="status" aria-live="polite" className="flex items-center gap-2"> + <Spinner aria-label="Loading canvas" /> + <span className="sr-only">Loading canvas…</span> + </div> + ); + }editor/scaffolds/grid-editor/components/refresh.tsx (1)
16-26: Improve loading announcement on the button (optional).Expose busy state and announce label changes for AT users.
- <Button - disabled={refreshing} - onClick={onRefreshClick} - variant="outline" - size="sm" - > - <span className="me-2"> + <Button + disabled={refreshing} + onClick={onRefreshClick} + variant="outline" + size="sm" + aria-busy={!!refreshing} + > + <span className="me-2" aria-live="polite"> {refreshing ? <Spinner /> : <ReloadIcon className="w-3.5 h-3.5" />} </span> {refreshing ? "Loading..." : "Refresh"} </Button>editor/scaffolds/workspace/create-new-document-button/create-document-button.tsx (1)
341-346: Optional: expose busy state and add SR text on create actions.Keeps the UI identical while improving assistive tech feedback.
- <Button - disabled={save_disabled} - onClick={onSaveClick} - className="min-w-20" - > - {busy ? <Spinner /> : <>Create</>} + <Button + disabled={save_disabled} + onClick={onSaveClick} + className="min-w-20" + aria-busy={busy} + > + {busy ? ( + <> + <Spinner aria-label="Creating database" /> + <span className="sr-only">Creating database…</span> + </> + ) : ( + <>Create</> + )} </Button>- <Button disabled={busy} onClick={onSaveClick} className="min-w-20"> - {busy ? <Spinner /> : <>Create</>} + <Button disabled={busy} onClick={onSaveClick} className="min-w-20" aria-busy={busy}> + {busy ? ( + <> + <Spinner aria-label="Creating bucket" /> + <span className="sr-only">Creating bucket…</span> + </> + ) : ( + <>Create</> + )} </Button>Also applies to: 445-446
editor/app/(site)/sign-in/email/_page.tsx (1)
41-67: Harden loading/error handling with try/finally and clear previous errorsEnsure loading is reset on throw and clear stale error before verification.
@@ const handleEmail = async (e: React.FormEvent) => { e.preventDefault(); @@ - setIsLoading(true); - - // Simulate API call - const { data, error } = await supabase.auth.signInWithOtp({ - email: email, - options: { - shouldCreateUser: false, - }, - }); - setIsLoading(false); - - if (error) { - console.log("error", error); - toast.error(error.message); - return; - } - - setStep("otp"); + setIsLoading(true); + try { + const { data, error } = await supabase.auth.signInWithOtp({ + email, + options: { shouldCreateUser: false }, + }); + if (error) { + toast.error(error.message); + return; + } + setStep("otp"); + } finally { + setIsLoading(false); + } }; @@ - const handleOtp = async (otp: string) => { - setIsLoading(true); - const { - data: { session }, - error, - } = await supabase.auth.verifyOtp({ - email, - token: otp, - type: "email", - }); - setIsLoading(false); - - if (error) { - setError(error.message); - return; - } - - if (next) { - router.replace(next); - } else if (redirect_uri) { - router.replace(redirect_uri); - } else { - router.replace("/dashboard"); - } - }; + const handleOtp = async (otp: string) => { + setError(""); + setIsLoading(true); + try { + const { + data: { session }, + error, + } = await supabase.auth.verifyOtp({ + email, + token: otp, + type: "email", + }); + if (error) { + setError(error.message); + return; + } + // redirect handled below with safeRedirect in the other diff + if (next) { + router.replace(next); + } else if (redirect_uri) { + router.replace(redirect_uri); + } else { + router.replace("/dashboard"); + } + } finally { + setIsLoading(false); + } + };Also applies to: 69-93
editor/components/ui/spinner.tsx (2)
5-14: Type props from Loader2Icon and add aria-liveUse the icon’s props for better TS coverage and announce politely to AT.
-function Spinner({ className, ...props }: React.ComponentProps<"svg">) { +function Spinner({ + className, + ...props +}: React.ComponentProps<typeof Loader2Icon>) { return ( <Loader2Icon role="status" - aria-label="Loading" + aria-label="Loading" + aria-live="polite" className={cn("size-4 animate-spin", className)} {...props} /> ) }
3-3: Optional: unify utils import pathSome files import "@/components/lib/utils", others "@/components/lib/utils/index". Consider standardizing to one.
editor/components/ui/input-group.tsx (2)
71-76: Make Addon focus textareas too and ignore link clicksCurrent query focuses only inputs and only ignores buttons.
- onClick={(e) => { - if ((e.target as HTMLElement).closest("button")) { - return; - } - e.currentTarget.parentElement?.querySelector("input")?.focus(); - }} + onClick={(e) => { + const target = e.target as HTMLElement; + if (target.closest("button, a")) return; + const control = e.currentTarget.parentElement?.querySelector( + "input, textarea" + ) as HTMLElement | null; + control?.focus(); + }}
60-80: Optional: keyboard accessibility for AddonIf Addon is intended to be interactive, support keyboard focus with tabIndex and key handlers; otherwise consider role="presentation".
function InputGroupAddon({ @@ - <div + <div + tabIndex={0} @@ - {...props} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + (e.currentTarget.parentElement?.querySelector( + "input, textarea" + ) as HTMLElement | null)?.focus(); + e.preventDefault(); + } + }} + {...props} />editor/scaffolds/settings/response-preferences.tsx (1)
191-197: Exported name typo: MaxRespoonses requires multi-file refactorFunction has a spelling mistake and is imported and used in
editor/app/(workbench)/[org]/[proj]/[id]/form/page.tsx(lines 14, 48). Rename will require updating both files.-export function MaxRespoonses() { +export function MaxResponses() {Update import and usage in
editor/app/(workbench)/[org]/[proj]/[id]/form/page.tsx:
- Line 14: change import from
MaxRespoonsestoMaxResponses- Line 48: change usage from
<MaxRespoonses />to<MaxResponses />editor/scaffolds/sidecontrol/controls/corner-radius.tsx (4)
1-1: ImportuseEffectfor state sync (optional: type-only React import).You’ll need
useEffectfor the sync fix below. Also consider type-only React imports to avoid a runtime default import when only using types.-import React, { useMemo, useState } from "react"; +import React, { useMemo, useState, useEffect } from "react";
65-71: Default and sync the “individual” view to non-uniform values.If
valueis already non-uniform, the UI still opens in “all” mode; also it doesn’t react whenvaluebecomes non-uniform later. Initialize and sync to reduce confusion.- const [showIndividual, setShowIndividual] = useState(false); + const [showIndividual, setShowIndividual] = useState( + () => (value ? !isUniform(value) : false) + ); @@ const mode = useMemo(() => { if (!value) return "all"; return isUniform(value) ? "all" : "each"; }, [value]); + + useEffect(() => { + if (value && !isUniform(value)) setShowIndividual(true); + }, [value]);
97-104: Avoid coercing NaN/falsy values; use nullish coalescing.
|| 0treatsNaNas falsy and overwrites it to 0.?? 0preserves valid falsy numbers semantics and only falls back onundefined.- newCorners[index] = newValue || 0; + newCorners[index] = newValue ?? 0;
229-238: Use type-onlyPropsWithChildrento avoid runtime React import.This keeps runtime imports lean and clarifies intent.
-const Label = ({ children }: React.PropsWithChildren) => { +import type { PropsWithChildren } from "react"; +const Label = ({ children }: PropsWithChildren) => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (70)
apps/docs/AGENTS.md(1 hunks)docs/wg/feat-layout/index.md(1 hunks)editor/app/(dev)/canvas/tools/ai/_components/model-selector.tsx(1 hunks)editor/app/(dev)/canvas/tools/ai/page.tsx(1 hunks)editor/app/(dev)/ui/components/spinner/page.tsx(1 hunks)editor/app/(library)/library/_components/gallery.tsx(1 hunks)editor/app/(site)/organizations/new/page.tsx(1 hunks)editor/app/(site)/sign-in/email/_page.tsx(1 hunks)editor/app/(tenant)/~/[tenant]/(p)/p/login/login.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/_components/logs-table.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/_components/settings.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/(console)/(resources)/campaigns/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/(console)/(resources)/customers/[uid]/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/(console)/(resources)/www/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/[id]/canvas/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/[id]/connect/database/supabase/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/[id]/data/(data)/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/[id]/data/(data)/table/[tablename]/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/[id]/form/edit/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx(1 hunks)editor/app/(workbench)/[org]/[proj]/[id]/objects/[[...path]]/page.tsx(1 hunks)editor/components/dialogs/delete-confirmation-dialog.tsx(1 hunks)editor/components/formfield/file-upload-field/file-upload-field.tsx(1 hunks)editor/components/mediaviewer/index.tsx(1 hunks)editor/components/pdf-viewer/pdf-viewer.tsx(1 hunks)editor/components/spinner/index.ts(0 hunks)editor/components/spinner/spinner.tsx(0 hunks)editor/components/ui/button-group.tsx(1 hunks)editor/components/ui/button.tsx(2 hunks)editor/components/ui/input-group.tsx(1 hunks)editor/components/ui/input.tsx(1 hunks)editor/components/ui/kbd.tsx(1 hunks)editor/components/ui/separator.tsx(3 hunks)editor/components/ui/spinner.tsx(1 hunks)editor/grida-canvas-react/viewport/surface.tsx(3 hunks)editor/grida-canvas/editor.i.ts(1 hunks)editor/grida-canvas/reducers/document.reducer.ts(2 hunks)editor/grida-canvas/reducers/methods/transform.ts(1 hunks)editor/grida-canvas/reducers/surface.reducer.ts(2 hunks)editor/grida-forms-hosted/e/formview.tsx(1 hunks)editor/package.json(2 hunks)editor/scaffolds/ai/form-field-schema-assistant.tsx(1 hunks)editor/scaffolds/grid-editor/components/count.tsx(1 hunks)editor/scaffolds/grid-editor/components/refresh.tsx(1 hunks)editor/scaffolds/grid/cells/file-cell.tsx(1 hunks)editor/scaffolds/grid/cells/json-cell.tsx(1 hunks)editor/scaffolds/grid/widgets/fk-referenced-row-lookup-popover.tsx(1 hunks)editor/scaffolds/mediapicker/index.tsx(1 hunks)editor/scaffolds/panels/extensions/field-x-sb-storage-settings.tsx(1 hunks)editor/scaffolds/platform/customer/customer-edit-dialog.tsx(1 hunks)editor/scaffolds/platform/www/www-layout-provider.tsx(1 hunks)editor/scaffolds/settings/closing-preference.tsx(1 hunks)editor/scaffolds/settings/customize/custom-ending-page-preferences.tsx(1 hunks)editor/scaffolds/settings/customize/custom-ending-redirect-preferences.tsx(1 hunks)editor/scaffolds/settings/data-dynamic-field-preferences.tsx(1 hunks)editor/scaffolds/settings/form-method-preference.tsx(1 hunks)editor/scaffolds/settings/response-preferences.tsx(1 hunks)editor/scaffolds/settings/scheduling-preference/index.tsx(1 hunks)editor/scaffolds/sidebar/sidebar.tsx(2 hunks)editor/scaffolds/sidecontrol/controls/corner-radius.tsx(4 hunks)editor/scaffolds/sidecontrol/index.tsx(1 hunks)editor/scaffolds/sidecontrol/sidecontrol-doctype-canvas.tsx(1 hunks)editor/scaffolds/sidecontrol/sidecontrol-global.tsx(5 hunks)editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx(2 hunks)editor/scaffolds/workbench/saving-indicator.tsx(1 hunks)editor/scaffolds/workspace/create-new-document-button/create-document-button.tsx(1 hunks)editor/scaffolds/workspace/new-project-dialog.tsx(1 hunks)editor/scaffolds/workspace/workspace.tsx(1 hunks)editor/theme/templates/west-referral/referrer/share.tsx(1 hunks)
💤 Files with no reviewable changes (2)
- editor/components/spinner/spinner.tsx
- editor/components/spinner/index.ts
🧰 Additional context used
📓 Path-based instructions (10)
editor/scaffolds/**
📄 CodeRabbit inference engine (AGENTS.md)
Place feature-specific larger components/pages/editors under editor/scaffolds
Files:
editor/scaffolds/grid-editor/components/count.tsxeditor/scaffolds/panels/extensions/field-x-sb-storage-settings.tsxeditor/scaffolds/settings/form-method-preference.tsxeditor/scaffolds/grid-editor/components/refresh.tsxeditor/scaffolds/workbench/saving-indicator.tsxeditor/scaffolds/settings/customize/custom-ending-redirect-preferences.tsxeditor/scaffolds/settings/scheduling-preference/index.tsxeditor/scaffolds/sidecontrol/index.tsxeditor/scaffolds/workspace/create-new-document-button/create-document-button.tsxeditor/scaffolds/settings/closing-preference.tsxeditor/scaffolds/sidecontrol/sidecontrol-doctype-canvas.tsxeditor/scaffolds/platform/www/www-layout-provider.tsxeditor/scaffolds/grid/cells/file-cell.tsxeditor/scaffolds/platform/customer/customer-edit-dialog.tsxeditor/scaffolds/settings/data-dynamic-field-preferences.tsxeditor/scaffolds/settings/customize/custom-ending-page-preferences.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidebar/sidebar.tsxeditor/scaffolds/mediapicker/index.tsxeditor/scaffolds/ai/form-field-schema-assistant.tsxeditor/scaffolds/grid/widgets/fk-referenced-row-lookup-popover.tsxeditor/scaffolds/grid/cells/json-cell.tsxeditor/scaffolds/settings/response-preferences.tsxeditor/scaffolds/workspace/workspace.tsxeditor/scaffolds/sidecontrol/controls/corner-radius.tsxeditor/scaffolds/workspace/new-project-dialog.tsxeditor/scaffolds/sidecontrol/sidecontrol-global.tsx
editor/app/(site)/**
📄 CodeRabbit inference engine (AGENTS.md)
Place non-SEO site pages under editor/app/(site)
Files:
editor/app/(site)/organizations/new/page.tsxeditor/app/(site)/sign-in/email/_page.tsx
editor/components/**
📄 CodeRabbit inference engine (AGENTS.md)
Put generally reusable components under editor/components
Files:
editor/components/mediaviewer/index.tsxeditor/components/ui/spinner.tsxeditor/components/dialogs/delete-confirmation-dialog.tsxeditor/components/ui/button-group.tsxeditor/components/ui/kbd.tsxeditor/components/pdf-viewer/pdf-viewer.tsxeditor/components/formfield/file-upload-field/file-upload-field.tsxeditor/components/ui/input-group.tsxeditor/components/ui/separator.tsxeditor/components/ui/input.tsxeditor/components/ui/button.tsx
editor/grida-*/**
📄 CodeRabbit inference engine (AGENTS.md)
Use editor/grida-* directories to isolate domain-specific modules pending promotion to /packages
Files:
editor/grida-forms-hosted/e/formview.tsxeditor/grida-canvas/editor.i.tseditor/grida-canvas-react/viewport/surface.tsxeditor/grida-canvas/reducers/methods/transform.tseditor/grida-canvas/reducers/document.reducer.tseditor/grida-canvas/reducers/surface.reducer.ts
editor/app/(workbench)/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep the main editor page under editor/app/(workbench)
Files:
editor/app/(workbench)/[org]/[proj]/[id]/data/(data)/page.tsxeditor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsxeditor/app/(workbench)/[org]/[proj]/[id]/form/edit/page.tsxeditor/app/(workbench)/[org]/[proj]/[id]/data/(data)/table/[tablename]/page.tsxeditor/app/(workbench)/[org]/[proj]/[id]/objects/[[...path]]/page.tsxeditor/app/(workbench)/[org]/[proj]/[id]/canvas/page.tsxeditor/app/(workbench)/[org]/[proj]/(console)/(resources)/customers/[uid]/page.tsxeditor/app/(workbench)/[org]/[proj]/(console)/(resources)/www/page.tsxeditor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/_components/settings.tsxeditor/app/(workbench)/[org]/[proj]/[id]/connect/database/supabase/page.tsxeditor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/_components/logs-table.tsxeditor/app/(workbench)/[org]/[proj]/(console)/(resources)/campaigns/page.tsxeditor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/page.tsx
editor/components/ui/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep shadcn UI components under editor/components/ui
Files:
editor/components/ui/spinner.tsxeditor/components/ui/button-group.tsxeditor/components/ui/kbd.tsxeditor/components/ui/input-group.tsxeditor/components/ui/separator.tsxeditor/components/ui/input.tsxeditor/components/ui/button.tsx
docs/**
📄 CodeRabbit inference engine (AGENTS.md)
Author and maintain all documentation under the root docs directory; it is the source of truth
Files:
docs/wg/feat-layout/index.md
editor/app/(library)/**
📄 CodeRabbit inference engine (AGENTS.md)
Place Grida Library (open assets) pages under editor/app/(library)
Files:
editor/app/(library)/library/_components/gallery.tsx
apps/docs/**
📄 CodeRabbit inference engine (AGENTS.md)
Do not author documentation directly in apps/docs; treat it as the deployed Docusaurus site that syncs from /docs
Files:
apps/docs/AGENTS.md
editor/app/(tenant)/**
📄 CodeRabbit inference engine (AGENTS.md)
Place tenant-site rendered pages under editor/app/(tenant)
Files:
editor/app/(tenant)/~/[tenant]/(p)/p/login/login.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-14T08:23:46.382Z
Learnt from: CR
PR: gridaco/grida#0
File: AGENTS.md:0-0
Timestamp: 2025-10-14T08:23:46.382Z
Learning: Applies to apps/docs/** : Do not author documentation directly in apps/docs; treat it as the deployed Docusaurus site that syncs from /docs
Applied to files:
apps/docs/AGENTS.md
🧬 Code graph analysis (13)
editor/components/ui/spinner.tsx (1)
editor/components/lib/utils/index.ts (1)
cn(4-6)
editor/scaffolds/sidecontrol/index.tsx (2)
editor/scaffolds/sidecontrol/controls/src.tsx (1)
SrcUploaderProvider(31-45)editor/scaffolds/sidecontrol/sidecontrol-doctype-canvas.tsx (1)
SideControlDoctypeCanvas(13-37)
editor/scaffolds/sidecontrol/sidecontrol-doctype-canvas.tsx (3)
editor/grida-canvas/query/index.ts (1)
fonts(606-614)editor/scaffolds/sidecontrol/controls/font-family.tsx (1)
FontFamilyListProvider(39-48)editor/scaffolds/sidecontrol/sidecontrol-document-properties.tsx (1)
DocumentProperties(52-111)
editor/components/ui/button-group.tsx (1)
editor/components/lib/utils/index.ts (1)
cn(4-6)
editor/components/ui/kbd.tsx (1)
editor/components/lib/utils/index.ts (1)
cn(4-6)
editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (2)
editor/grida-canvas-react/use-editor.tsx (1)
useCurrentEditor(98-106)editor/grida-canvas-react/provider.tsx (1)
useSelectionState(292-298)
editor/grida-canvas-react/viewport/surface.tsx (2)
packages/grida-canvas-ruler/react.tsx (1)
AxisRuler(25-62)editor/grida-canvas-react/use-editor.tsx (1)
useCurrentEditor(98-106)
editor/components/ui/input-group.tsx (1)
editor/components/lib/utils/index.ts (1)
cn(4-6)
editor/grida-canvas/reducers/document.reducer.ts (1)
editor/grida-canvas/utils/insertion.ts (1)
hitTestNestedInsertionTarget(76-97)
editor/components/ui/input.tsx (1)
editor/components/lib/utils/index.ts (1)
cn(4-6)
editor/scaffolds/sidecontrol/controls/corner-radius.tsx (3)
packages/grida-canvas-cg/lib.ts (1)
CornerRadius4(1214-1214)editor/scaffolds/sidecontrol/ui/number.tsx (1)
InputPropertyNumber(123-238)editor/components/lib/utils/index.ts (1)
cn(4-6)
editor/grida-canvas/reducers/surface.reducer.ts (1)
crates/grida-canvas/src/window/state.rs (1)
context(65-67)
editor/scaffolds/sidecontrol/sidecontrol-global.tsx (2)
editor/grida-canvas/editor.ts (3)
state(323-325)state(2255-2257)state(3267-3269)editor/grida-canvas-react/index.ts (1)
useEditorState(1-1)
🪛 LanguageTool
apps/docs/AGENTS.md
[uncategorized] ~39-~39: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...se plain URLs: https://example.com or markdown links: [text](url) - MDX reserves `<>...
(MARKDOWN_NNP)
🪛 markdownlint-cli2 (0.18.1)
docs/wg/feat-layout/index.md
180-180: Bare URL used
(MD034, no-bare-urls)
182-182: Bare URL used
(MD034, no-bare-urls)
184-184: Bare URL used
(MD034, no-bare-urls)
186-186: Bare URL used
(MD034, no-bare-urls)
193-193: Bare URL used
(MD034, no-bare-urls)
196-196: Bare URL used
(MD034, no-bare-urls)
199-199: Bare URL used
(MD034, no-bare-urls)
202-202: Bare URL used
(MD034, no-bare-urls)
🔇 Additional comments (76)
editor/scaffolds/sidebar/sidebar.tsx (1)
18-18: LGTM!The import addition is clean and follows the existing pattern in the file.
editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (1)
137-154: LGTM! Clean implementation of the disabled prop.The optional
disabledparameter correctly extends theAligncomponent's functionality. The combined logic!has_selection || disabledensures the control is disabled when there's no selection OR when explicitly requested, which is the correct approach.editor/grida-canvas/reducers/surface.reducer.ts (2)
36-40: LGTM! Null-safe handling for root nodes.The conditional check for
parent_rectbefore accessing its properties prevents potential runtime errors when the parent is a scene node. The logic correctly defaultsreldeltato[0, 0]when no bounding rect exists, and the comment clearly explains the edge case.
705-705: LGTM! Call site correctly aligned with null-safe signature.The call site now properly passes
parent_idas a potentially nullable value, matching the updatedcreateLayoutSnapshotsignature. This ensures consistency with the null-safety improvements.editor/grida-canvas/reducers/methods/transform.ts (1)
280-285: LGTM! Correct parent comparison normalization.The normalization logic correctly treats
nullparent references as equivalent toscene_id, preventing false positives where a node staying in the scene (either asnullorscene_idparent) would be incorrectly identified as having changed parents. The implementation is consistent with the pattern used at line 304 (new_parent_id ?? draft.scene_id!), and the non-null assertion is safe given the assertion at line 120.editor/grida-canvas/editor.i.ts (1)
1512-1512: LGTM! Ruler now enabled by default.This change aligns with the PR objectives and works well with the new context menu feature added in
surface.tsxthat allows users to hide the ruler.editor/grida-canvas-react/viewport/surface.tsx (3)
51-57: LGTM! Context menu and toast imports added correctly.The imports for the ContextMenu components and toast notification are properly structured and support the new ruler context menu feature.
Also applies to: 61-61
1563-1622: LGTM! Context menu integration preserves existing functionality.The RulerContextMenu wrappers are correctly integrated around both the X-axis and Y-axis rulers. The original gesture handling (
bindX/bindY) and AxisRuler rendering are preserved, ensuring backward compatibility while adding the new context menu feature.
1634-1658: Keyboard shortcut is implemented and functional.The ⇧R (Shift+R) shortcut for toggling the ruler is properly implemented in
editor/grida-canvas-react/viewport/hotkeys.tsx(line 746-748) and registered in the keybindings sheet (line 201-203). The shortcut callseditor.surface.surfaceToggleRuler()and displays a success toast, confirming it's fully functional. The context menu correctly displays this shortcut hint.editor/scaffolds/platform/customer/customer-edit-dialog.tsx (1)
16-16: LGTM! Spinner import path updated.The import path change aligns with the project-wide refactor to consolidate UI components under
@/components/ui. As per coding guidelines.editor/app/(workbench)/[org]/[proj]/[id]/form/edit/page.tsx (1)
8-8: LGTM! Spinner import path updated.The import path change aligns with the project-wide refactor to consolidate UI components under
@/components/ui. As per coding guidelines.editor/app/(workbench)/[org]/[proj]/[id]/objects/[[...path]]/page.tsx (1)
68-68: LGTM! Spinner import path updated.The import path change aligns with the project-wide refactor to consolidate UI components under
@/components/ui. As per coding guidelines.editor/app/(dev)/canvas/tools/ai/_components/model-selector.tsx (1)
43-43: Width change from full to auto.The button width changed from
w-fulltow-auto, which will make it size according to content rather than taking full container width. Please verify that this doesn't cause layout issues in different viewport sizes.You can verify the visual impact by testing the model selector in the canvas tools UI at different screen sizes.
editor/scaffolds/panels/extensions/field-x-sb-storage-settings.tsx (1)
23-23: LGTM! Spinner import path updated.The import path change aligns with the project-wide refactor to consolidate UI components under
@/components/ui. As per coding guidelines.editor/scaffolds/platform/www/www-layout-provider.tsx (1)
5-5: LGTM! Spinner import path updated.The import path change aligns with the project-wide refactor to consolidate UI components under
@/components/ui. As per coding guidelines.editor/components/ui/separator.tsx (1)
16-16: The review comment appears to be incorrect. After thorough verification:
No
separator-rootfound: A comprehensive search of the entire codebase returned zero matches for "separator-root". This value does not exist and therefore cannot have been the previous value.No CSS selectors targeting data-slot: All CSS files in the codebase were searched; none contain
[data-slot]attribute selectors that could be affected by this change.Wrapper components are unaffected: Components like
button-group.tsxandsidebar.tsxthat use theSeparatorcomponent override the basedata-slotattribute with their own context-specific values ("button-group-separator"and"sidebar-separator"respectively), so they won't be impacted.No test dependencies found: No tests reference the
data-slotattribute or target the old value.The change to
data-slot="separator"is localized and does not break existing CSS selectors, tests, or downstream code that depends onseparator-root.Likely an incorrect or invalid review comment.
editor/package.json (1)
79-81: Dependency updates verified as backward compatible—no breaking changes detected.Web search confirmed that all three dependencies are safe:
- @radix-ui/react-separator v1.1.7 has no breaking changes listed
- @radix-ui/react-slot v1.2.3 has no documented breaking changes
- react-resizable-panels v3.0.6 contains only a bug fix with no breaking changes
The updates are indeed minor/patch versions and safe to merge.
editor/scaffolds/sidecontrol/sidecontrol-global.tsx (5)
3-3: LGTM: Removed unused import.The
useEffectimport has been removed as it's no longer used in this file.
74-74: LGTM: Simplified imports.Removed
useDocumentStateas it's no longer needed, keeping only the required hooks.
90-90: LGTM: Simplified to state-only usage.The component now only reads state without dispatching actions, which aligns with the refactoring pattern in this PR.
154-192: Renamed to indicate disabled state.The function has been prefixed with underscore to indicate it's not actively used. This is a good convention for disabled code.
112-113: Form Start Page control is intentionally disabled in the global side control panel, but the feature remains fully functional elsewhere.The
_FormStartPageControlcomponent exists (defined at line 154) and the entire form start page infrastructure remains active throughout the codebase—including a dedicated editor page ateditor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx, state management, sync mechanisms, and template dialogs. However, the component has a FIXME comment ("FIXME: 250303 UNKNOWN" at line 157) suggesting known issues.The disabling appears intentional, likely because start page editing functionality is provided through the dedicated form start page rather than the global side control panel. No explicit documentation or issue reference explains the specific reason.
docs/wg/feat-layout/index.md (2)
180-186: LGTM: Fixed MDX-incompatible URL syntax.The angle-bracketed URLs have been correctly changed to plain URLs, which resolves MDX parsing issues as noted in the
AGENTS.mddocumentation. Angle brackets are reserved for JSX/HTML tags in MDX.
192-203: LGTM: Improved formatting consistency.The wording adjustments (apostrophes, capitalization, and single-line descriptions) improve readability and consistency across the references section.
editor/scaffolds/sidecontrol/sidecontrol-doctype-canvas.tsx (3)
7-11: LGTM: Added necessary imports for font provider.The imports support the new
FontFamilyListProviderintegration, enabling font list context for child components.
14-18: LGTM: Retrieves font list from canvas editor state.The font list is properly retrieved from
state.webfontlist.itemsusing the canvas editor state hook.
22-34: LGTM: Improved provider scope.Moving
FontFamilyListProviderto this doctype-specific component (from the parentindex.tsx) narrows its scope and provides better separation of concerns. The fonts are now only provided where they're actually needed.editor/components/ui/button.tsx (2)
12-18: LGTM: Simplified button shadows.Removed
shadow-xsfrom default, destructive, and secondary variants for a flatter design. The outline variant retains its shadow, providing visual distinction.
28-29: LGTM: Added granular icon button sizes.The new
icon-sm(size-8) andicon-lg(size-10) variants provide more flexibility for icon button sizing alongside the existingicon(size-9) variant.editor/scaffolds/sidecontrol/index.tsx (1)
23-27: LGTM: Removed font provider from parent scope.The
FontFamilyListProviderhas been moved from this parent component toSideControlDoctypeCanvaswhere it's actually needed. This improves separation of concerns by scoping the provider only to the canvas doctype.editor/components/ui/kbd.tsx (1)
3-16: LGTM: Well-implemented Kbd component.The component follows the established
data-slotpattern and provides appropriate styling for keyboard input display.editor/components/ui/button-group.tsx (4)
7-22: LGTM: Well-structured variant system.The
buttonGroupVariantsuses CVA effectively to manage horizontal and vertical orientations with appropriate Tailwind classes for border radius and border collapsing.
24-38: LGTM: Accessible button group component.The component properly uses
role="group"for accessibility anddata-orientationfor styling hooks. The implementation follows established patterns in the UI component library.
40-58: LGTM: Flexible text component.The
asChildpattern provides flexibility for composition while maintaining consistent styling.
60-76: LGTM: Properly integrated separator.The separator component correctly extends the existing
Separatorcomponent and adapts its orientation based on the group's orientation.editor/scaffolds/grid/widgets/fk-referenced-row-lookup-popover.tsx (1)
13-13: LGTM! Clean import path update.The Spinner import path has been correctly updated to the new UI module location with no functional changes.
editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/_components/settings.tsx (1)
54-54: LGTM! Import path updated correctly.The Spinner import has been successfully migrated to the UI module with no behavioral changes.
editor/scaffolds/workspace/workspace.tsx (1)
15-15: LGTM! Import path correctly updated.The Spinner import has been properly migrated to the new UI module location.
editor/scaffolds/settings/data-dynamic-field-preferences.tsx (1)
23-23: LGTM! Import path migration successful.The Spinner import has been correctly updated to the UI module with no functional impact.
editor/scaffolds/settings/form-method-preference.tsx (1)
22-22: LGTM! Import path correctly updated.The Spinner import has been properly migrated to the new UI module location with no behavioral changes.
editor/app/(workbench)/[org]/[proj]/[id]/connect/database/supabase/page.tsx (1)
60-60: LGTM! Import path migration successful.The Spinner import has been correctly updated to the UI module. All usages throughout the file remain consistent.
editor/app/(workbench)/[org]/[proj]/(console)/(resources)/campaigns/page.tsx (1)
6-6: LGTM! Import path correctly updated.The Spinner import has been successfully migrated to the new UI module location.
editor/theme/templates/west-referral/referrer/share.tsx (1)
3-3: LGTM! Import path migration complete.The Spinner import has been correctly updated to the UI module, completing the consistent refactor across all reviewed files.
editor/app/(dev)/canvas/tools/ai/page.tsx (1)
22-22: LGTM - Clean import path refactor.The Spinner import has been successfully updated to the centralized UI components path. The usage throughout the file remains unchanged and consistent.
editor/scaffolds/workspace/new-project-dialog.tsx (1)
3-3: LGTM - Consistent with UI component reorganization.The import path update aligns with the project-wide refactor to centralize UI components under the
ui/directory.editor/scaffolds/settings/customize/custom-ending-page-preferences.tsx (1)
51-51: LGTM - Import path successfully updated.The Spinner component import has been updated consistently with the broader UI reorganization effort.
editor/scaffolds/settings/scheduling-preference/index.tsx (1)
24-24: LGTM - Clean refactor.The import update maintains consistency with the centralized UI component structure.
editor/scaffolds/settings/customize/custom-ending-redirect-preferences.tsx (1)
17-17: LGTM - Consistent import update.The Spinner import path has been updated correctly with no impact on functionality.
editor/components/dialogs/delete-confirmation-dialog.tsx (1)
14-14: LGTM - Import path modernized.The import has been updated from a relative path to the centralized UI components location, improving maintainability.
editor/app/(site)/organizations/new/page.tsx (1)
17-17: LGTM - Import successfully refactored.The Spinner import aligns with the centralized UI component structure.
editor/app/(library)/library/_components/gallery.tsx (1)
23-23: LGTM - Consistent refactor complete.The import path update completes the migration to the centralized UI Spinner component with no behavioral changes.
editor/app/(tenant)/~/[tenant]/(p)/p/login/login.tsx (1)
21-21: LGTM! Clean import path update.The Spinner import has been correctly updated to use the new UI components path. This aligns with the repository-wide refactor to organize UI components under
@/components/ui/.editor/scaffolds/grid/cells/json-cell.tsx (1)
11-11: LGTM! Import path correctly updated.The Spinner import path has been updated to match the new UI components structure. No functional changes.
editor/components/mediaviewer/index.tsx (1)
25-25: LGTM! Import path refactored correctly.The Spinner import has been updated to the new UI components location, consistent with the codebase-wide reorganization.
editor/scaffolds/grid/cells/file-cell.tsx (1)
51-51: LGTM! Spinner import updated correctly.The import path has been successfully updated to align with the new UI components organization. All Spinner usages throughout the file remain unchanged.
editor/app/(workbench)/[org]/[proj]/(console)/(resources)/customers/[uid]/page.tsx (1)
23-23: LGTM! Import path updated correctly.The Spinner import has been updated to use the new UI components path. This change is consistent with the repository-wide refactoring effort.
editor/components/pdf-viewer/pdf-viewer.tsx (1)
8-8: LGTM! Import refactored successfully.The Spinner import path has been updated to the UI components directory, maintaining consistency with the broader refactor.
editor/app/(workbench)/[org]/[proj]/(console)/(resources)/www/page.tsx (1)
17-17: LGTM! Import path correctly updated.The Spinner import has been successfully migrated to the new UI components location. No functional changes.
editor/app/(workbench)/[org]/[proj]/[id]/data/(data)/page.tsx (1)
4-4: LGTM! Import path updated correctly.The Spinner import has been successfully refactored to use the new UI components structure, completing the consistent migration across the codebase.
editor/components/formfield/file-upload-field/file-upload-field.tsx (1)
12-12: LGTM! Clean import path refactoring.The Spinner import path has been correctly updated to the centralized UI module location, consistent with the broader codebase reorganization.
editor/scaffolds/grid-editor/components/count.tsx (1)
1-1: LGTM! Clean import path refactoring.The Spinner import path has been correctly updated to the centralized UI module location.
editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/page.tsx (1)
11-11: LGTM! Clean import path refactoring.The Spinner import path has been correctly updated to the centralized UI module location.
editor/scaffolds/settings/closing-preference.tsx (1)
16-16: LGTM! Clean import path refactoring.The Spinner import path has been correctly updated to the centralized UI module location.
editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/_components/logs-table.tsx (1)
13-13: LGTM! Clean import path refactoring.The Spinner import path has been correctly updated to the centralized UI module location.
editor/grida-forms-hosted/e/formview.tsx (1)
52-52: LGTM! Clean import path refactoring.The Spinner import path has been correctly updated to the centralized UI module location.
editor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx (1)
51-51: LGTM! Clean import path refactoring.The Spinner import path has been correctly updated to the centralized UI module location.
editor/scaffolds/mediapicker/index.tsx (1)
10-10: LGTM! Clean import path refactoring.The Spinner import path has been correctly updated to the centralized UI module location.
editor/app/(workbench)/[org]/[proj]/[id]/data/(data)/table/[tablename]/page.tsx (1)
24-24: LGTM on the Spinner import path change.Consistent with the UI components reorg. No runtime impact.
editor/app/(workbench)/[org]/[proj]/[id]/canvas/page.tsx (1)
3-3: Import path update looks correct.Matches the new ui module location.
editor/scaffolds/grid-editor/components/refresh.tsx (1)
4-4: LGTM on the Spinner import switch.No behavior change; consistent with the refactor.
editor/app/(dev)/ui/components/spinner/page.tsx (1)
4-4: Import path update approved.Dev docs page now points at the consolidated UI Spinner.
editor/scaffolds/workspace/create-new-document-button/create-document-button.tsx (1)
34-34: Spinner import path change looks good.Consistent with the project-wide migration.
editor/scaffolds/ai/form-field-schema-assistant.tsx (1)
9-9: Approved: Spinner import updated to UI module.No behavioral change; matches the refactor pattern across the app.
editor/scaffolds/workbench/saving-indicator.tsx (1)
4-4: Spinner import path migration verified and correct.✓ No legacy imports from
@/components/spinnerremain in the codebase
✓ Spinner component properly exported as named export fromeditor/components/ui/spinner.tsx
✓ Import insaving-indicator.tsxand 43 other files consistently use the new path
✓ All checks passededitor/app/(site)/sign-in/email/_page.tsx (1)
25-25: Import path update looks goodSwitching to "@/components/ui/spinner" aligns with the UI placement guideline.
editor/components/ui/input.tsx (1)
11-15: Input components don't require flex display for layout contextsThe review comment misunderstands how CSS flexbox affects native form inputs. Native
<input>elements are inline-replaced elements that work correctly whether their parent uses flex layout or not. Addingdisplay: flexto an input doesn't improve flex growth behavior.Current code already lacks the
flexclass. If inputs previously had it, removing it was correct—flex display provides no layout benefit to native form elements and adds unnecessary CSS.Likely an incorrect or invalid review comment.
| # Grida Docs - AI Agent Guide | ||
|
|
||
| ## Important: Document Source Location | ||
|
|
||
| **DO NOT edit files under `apps/docs/docs/` directory directly!** | ||
|
|
||
| The documentation content is **NOT** maintained in `apps/docs/docs/`. Instead: | ||
|
|
||
| - **Source of Truth**: `/docs/` (root directory) | ||
| - **Build Destination**: `/apps/docs/docs/` (auto-generated, overwritten on build) | ||
|
|
||
| ### How it works | ||
|
|
||
| The build process (`pnpm content:setup` or `pnpm build`) runs scripts that: | ||
|
|
||
| 1. Copy all documentation from `/docs/` → `/apps/docs/docs/` | ||
| 2. Process translations from `translations/` subdirectories | ||
| 3. Build the Docusaurus site | ||
|
|
||
| See: | ||
|
|
||
| - `scripts/postinstall.js` - Entry point | ||
| - `scripts/setup-docs.js` - Setup orchestrator | ||
| - `scripts/docs-site-gen/copy-docs.js` - Copies from root `/docs` to `apps/docs/docs/` | ||
| - `scripts/docs-site-gen/copy-translations.js` - Handles translations | ||
|
|
||
| ### When fixing documentation issues | ||
|
|
||
| 1. **Always edit files in `/docs/`** (root directory) | ||
| 2. Never edit files in `apps/docs/docs/` (they will be overwritten) | ||
| 3. Run `pnpm build` to test your changes | ||
| 4. The build will copy your changes from `/docs/` to `apps/docs/docs/` | ||
|
|
||
| ### MDX/Markdown Syntax Issues | ||
|
|
||
| When fixing MDX compilation errors: | ||
|
|
||
| - Angle-bracketed URLs like `<https://example.com>` break MDX parsing | ||
| - Use plain URLs: `https://example.com` or markdown links: `[text](url)` | ||
| - MDX reserves `<>` for JSX/HTML tags only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CRITICAL: Documentation added in wrong directory.
This file is being created in apps/docs/AGENTS.md, which contradicts its own guidance and the project's documentation workflow. According to the retrieved learnings and the content of this very file, all documentation must be authored in /docs/ (root directory), not in apps/docs/.
The build process copies from /docs/ to /apps/docs/docs/, so any file added directly to apps/docs/ will not follow the established pattern and may be overwritten or lost.
Move this file to /docs/AGENTS.md instead:
-apps/docs/AGENTS.md
+docs/AGENTS.mdBased on learnings
📝 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.
| # Grida Docs - AI Agent Guide | |
| ## Important: Document Source Location | |
| **DO NOT edit files under `apps/docs/docs/` directory directly!** | |
| The documentation content is **NOT** maintained in `apps/docs/docs/`. Instead: | |
| - **Source of Truth**: `/docs/` (root directory) | |
| - **Build Destination**: `/apps/docs/docs/` (auto-generated, overwritten on build) | |
| ### How it works | |
| The build process (`pnpm content:setup` or `pnpm build`) runs scripts that: | |
| 1. Copy all documentation from `/docs/` → `/apps/docs/docs/` | |
| 2. Process translations from `translations/` subdirectories | |
| 3. Build the Docusaurus site | |
| See: | |
| - `scripts/postinstall.js` - Entry point | |
| - `scripts/setup-docs.js` - Setup orchestrator | |
| - `scripts/docs-site-gen/copy-docs.js` - Copies from root `/docs` to `apps/docs/docs/` | |
| - `scripts/docs-site-gen/copy-translations.js` - Handles translations | |
| ### When fixing documentation issues | |
| 1. **Always edit files in `/docs/`** (root directory) | |
| 2. Never edit files in `apps/docs/docs/` (they will be overwritten) | |
| 3. Run `pnpm build` to test your changes | |
| 4. The build will copy your changes from `/docs/` to `apps/docs/docs/` | |
| ### MDX/Markdown Syntax Issues | |
| When fixing MDX compilation errors: | |
| - Angle-bracketed URLs like `<https://example.com>` break MDX parsing | |
| - Use plain URLs: `https://example.com` or markdown links: `[text](url)` | |
| - MDX reserves `<>` for JSX/HTML tags only | |
| # Grida Docs - AI Agent Guide | |
| ## Important: Document Source Location | |
| **DO NOT edit files under `apps/docs/docs/` directory directly!** | |
| The documentation content is **NOT** maintained in `apps/docs/docs/`. Instead: | |
| - **Source of Truth**: `/docs/` (root directory) | |
| - **Build Destination**: `/apps/docs/docs/` (auto-generated, overwritten on build) | |
| ### How it works | |
| The build process (`pnpm content:setup` or `pnpm build`) runs scripts that: | |
| 1. Copy all documentation from `/docs/` → `/apps/docs/docs/` | |
| 2. Process translations from `translations/` subdirectories | |
| 3. Build the Docusaurus site | |
| See: | |
| - `scripts/postinstall.js` - Entry point | |
| - `scripts/setup-docs.js` - Setup orchestrator | |
| - `scripts/docs-site-gen/copy-docs.js` - Copies from root `/docs` to `apps/docs/docs/` | |
| - `scripts/docs-site-gen/copy-translations.js` - Handles translations | |
| ### When fixing documentation issues | |
| 1. **Always edit files in `/docs/`** (root directory) | |
| 2. Never edit files in `apps/docs/docs/` (they will be overwritten) | |
| 3. Run `pnpm build` to test your changes | |
| 4. The build will copy your changes from `/docs/` to `apps/docs/docs/` | |
| ### MDX/Markdown Syntax Issues | |
| When fixing MDX compilation errors: | |
| - Angle-bracketed URLs like `<https://example.com>` break MDX parsing | |
| - Use plain URLs: `https://example.com` or Markdown links: `[text](url)` | |
| - MDX reserves `<>` for JSX/HTML tags only |
🧰 Tools
🪛 LanguageTool
[uncategorized] ~39-~39: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...se plain URLs: https://example.com or markdown links: [text](url) - MDX reserves `<>...
(MARKDOWN_NNP)
🤖 Prompt for AI Agents
In apps/docs/AGENTS.md around lines 1 to 40: this documentation file was created
in the generated site directory (apps/docs/) instead of the canonical source
location; move the file to the repository root docs/ directory (i.e.,
/docs/AGENTS.md), remove the duplicate from apps/docs/, and ensure any edits are
made in /docs/ so the build process can copy it into apps/docs/docs/ during pnpm
content:setup or pnpm build.
| function KbdGroup({ className, ...props }: React.ComponentProps<"div">) { | ||
| return ( | ||
| <kbd | ||
| data-slot="kbd-group" | ||
| className={cn("inline-flex items-center gap-1", className)} | ||
| {...props} | ||
| /> | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix semantic HTML mismatch.
Line 20 uses <kbd> element but the type definition on line 18 specifies React.ComponentProps<"div">. A group container should use a <div> element, not <kbd>, as <kbd> represents keyboard input, not a container.
Apply this diff to fix the semantic HTML issue:
-function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
+function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
- <kbd
+ <div
data-slot="kbd-group"
className={cn("inline-flex items-center gap-1", className)}
{...props}
- />
+ />
)
}📝 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.
| function KbdGroup({ className, ...props }: React.ComponentProps<"div">) { | |
| return ( | |
| <kbd | |
| data-slot="kbd-group" | |
| className={cn("inline-flex items-center gap-1", className)} | |
| {...props} | |
| /> | |
| ) | |
| } | |
| function KbdGroup({ className, ...props }: React.ComponentProps<"div">) { | |
| return ( | |
| <div | |
| data-slot="kbd-group" | |
| className={cn("inline-flex items-center gap-1", className)} | |
| {...props} | |
| /> | |
| ) | |
| } |
🤖 Prompt for AI Agents
In editor/components/ui/kbd.tsx around lines 18 to 26, the component is typed as
React.ComponentProps<"div"> but renders a <kbd>, causing a semantic HTML
mismatch; change the rendered element to a <div> so the output matches the
declared prop types and intent as a group container, update the data-slot
attribute if needed (e.g., keep data-slot="kbd-group"), and ensure className and
{...props} are passed to the div.
| // Find target parents for each selected node: | ||
| // - If selected node is a container -> paste as child (target parent = node itself) | ||
| // - If selected node is not a container -> paste as sibling (target parent = node's parent) | ||
| // - Target parent must be a container or null (scene) | ||
| // - Target parent must not be one of the copied nodes | ||
| // | ||
| // KNOWN LIMITATION: | ||
| // When no selection exists, hit test is used to find parent (see below). | ||
| // However, after first paste, newly pasted nodes become selected, so subsequent | ||
| // pastes use this selection-based logic instead of hit test. This means position | ||
| // adjustment for hit-tested parents only applies to the first paste in a sequence. | ||
| const target_parents = Array.from( | ||
| new Set( | ||
| selection | ||
| .map((node_id) => { | ||
| const node = dq.__getNodeById(draft, node_id); | ||
|
|
||
| // If node is a container, use it as target parent (paste as child) | ||
| if (node.type === "container") { | ||
| return node_id; | ||
| } | ||
|
|
||
| // Otherwise, use its parent as target parent (paste as sibling) | ||
| const parent_id = dq.getParentId(draft.document_ctx, node_id); | ||
|
|
||
| // Parent can be null (scene) or a container | ||
| if (!parent_id) return null; | ||
|
|
||
| const parent = dq.__getNodeById(draft, parent_id); | ||
| // Only return valid container parents | ||
| return parent?.type === "container" ? parent_id : null; | ||
| }) | ||
| .filter((target_id) => { | ||
| // Ensure target parent is not one of the originals | ||
| if (target_id && ids.includes(target_id)) return false; | ||
| return true; | ||
| }) | ||
| ) | ||
| ); | ||
|
|
||
| const targets: Array<string | null> = | ||
| valid_target_selection.length > 0 ? valid_target_selection : [null]; | ||
| target_parents.length > 0 ? target_parents : [null]; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid duplicate paste when mixed parents; also use Set for O(1) copied-id checks
If selection yields both explicit container parents and null (scene), current targets includes null, causing an extra hit‑tested paste in addition to container pastes. Also, ids.includes runs in O(n) in two hot paths. Fix by preferring non‑null targets when present and using a Set.
Apply this diff:
@@
- const target_parents = Array.from(
- new Set(
- selection
- .map((node_id) => {
- const node = dq.__getNodeById(draft, node_id);
-
- // If node is a container, use it as target parent (paste as child)
- if (node.type === "container") {
- return node_id;
- }
-
- // Otherwise, use its parent as target parent (paste as sibling)
- const parent_id = dq.getParentId(draft.document_ctx, node_id);
-
- // Parent can be null (scene) or a container
- if (!parent_id) return null;
-
- const parent = dq.__getNodeById(draft, parent_id);
- // Only return valid container parents
- return parent?.type === "container" ? parent_id : null;
- })
- .filter((target_id) => {
- // Ensure target parent is not one of the originals
- if (target_id && ids.includes(target_id)) return false;
- return true;
- })
- )
- );
-
- const targets: Array<string | null> =
- target_parents.length > 0 ? target_parents : [null];
+ // O(1) lookup for originals
+ const copiedSet = new Set(ids);
+ const target_parents = Array.from(
+ new Set(
+ selection
+ .map((node_id) => {
+ const node = dq.__getNodeById(draft, node_id);
+ if (node.type === "container") return node_id; // paste as child
+ const parent_id = dq.getParentId(draft.document_ctx, node_id); // paste as sibling
+ if (!parent_id) return null; // scene/root
+ const parent = dq.__getNodeById(draft, parent_id);
+ return parent?.type === "container" ? parent_id : null;
+ })
+ .filter((target_id) => (target_id ? !copiedSet.has(target_id) : true))
+ )
+ );
+ // Prefer explicit parents; only fall back to root/hit-test when none exist
+ const nonNullTargets = target_parents.filter(
+ (t): t is string => t !== null
+ );
+ const targets: Array<string | null> =
+ nonNullTargets.length > 0 ? nonNullTargets : [null];
@@
- (id) => {
- // Exclude originals from hit test
- if (ids.includes(id)) return false;
- return dq.__getNodeById(draft, id).type === "container";
- },
+ (id) => {
+ // Exclude originals from hit test
+ if (copiedSet.has(id)) return false;
+ return dq.__getNodeById(draft, id).type === "container";
+ },Also applies to: 678-683
| <Toggle | ||
| size="sm" | ||
| variant="outline" | ||
| disabled={disabled} | ||
| pressed={showIndividual} | ||
| onPressedChange={setShowIndividual} | ||
| className="bg-transparent border-none shadow-none size-6 min-w-6 px-0 data-[state=on]:*:[svg]:text-workbench-accent-sky" | ||
| aria-label="Toggle individual corner radius controls" | ||
| > | ||
| <CornersIcon | ||
| className="size-3.5 text-muted-foreground" | ||
| aria-hidden="true" | ||
| /> | ||
| </Toggle> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix Tailwind descendant selector for pressed icon color.
data-[state=on]:*:[svg]:… won’t match. Use the arbitrary variant for descendants.
- className="bg-transparent border-none shadow-none size-6 min-w-6 px-0 data-[state=on]:*:[svg]:text-workbench-accent-sky"
+ className="bg-transparent border-none shadow-none size-6 min-w-6 px-0 data-[state=on]:[&_svg]:text-workbench-accent-sky"📝 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.
| <Toggle | |
| size="sm" | |
| variant="outline" | |
| disabled={disabled} | |
| pressed={showIndividual} | |
| onPressedChange={setShowIndividual} | |
| className="bg-transparent border-none shadow-none size-6 min-w-6 px-0 data-[state=on]:*:[svg]:text-workbench-accent-sky" | |
| aria-label="Toggle individual corner radius controls" | |
| > | |
| <CornersIcon | |
| className="size-3.5 text-muted-foreground" | |
| aria-hidden="true" | |
| /> | |
| </Toggle> | |
| </div> | |
| <Toggle | |
| size="sm" | |
| variant="outline" | |
| disabled={disabled} | |
| pressed={showIndividual} | |
| onPressedChange={setShowIndividual} | |
| className="bg-transparent border-none shadow-none size-6 min-w-6 px-0 data-[state=on]:[&_svg]:text-workbench-accent-sky" | |
| aria-label="Toggle individual corner radius controls" | |
| > | |
| <CornersIcon | |
| className="size-3.5 text-muted-foreground" | |
| aria-hidden="true" | |
| /> | |
| </Toggle> | |
| </div> |
🤖 Prompt for AI Agents
In editor/scaffolds/sidecontrol/controls/corner-radius.tsx around lines 129 to
143, the Tailwind descendant selector
`data-[state=on]:*:[svg]:text-workbench-accent-sky` is invalid; replace it with
an arbitrary variant that targets descendant svgs when the Toggle is pressed,
e.g. use `data-[state=on]:[&_*_svg]:text-workbench-accent-sky` (update the
className string accordingly) so the pressed state correctly applies the color
to the icon.
| {/* Second Row: Individual Corner Radius Controls */} | ||
| {showIndividual && ( | ||
| <div className="flex items-center"> | ||
| {/* Top Left */} | ||
| <div className="flex flex-col items-center flex-1"> | ||
| <InputPropertyNumber | ||
| mode="fixed" | ||
| type="number" | ||
| value={cornerValues[0]} | ||
| placeholder="0" | ||
| min={0} | ||
| step={1} | ||
| className="w-full h-7 rounded-none rounded-l-md border-r-0" | ||
| onValueCommit={(v) => handleIndividualChange(0, v)} | ||
| aria-label="Corner radius top left" | ||
| /> | ||
| <Label> | ||
| <CornerTopLeftIcon className="size-3" /> | ||
| </Label> | ||
| </div> | ||
| {/* Separator */} | ||
| <hr className="w-px h-10" /> | ||
| {/* Top Right */} | ||
| <div className="flex flex-col items-center flex-1"> | ||
| <InputPropertyNumber | ||
| mode="fixed" | ||
| type="number" | ||
| value={cornerValues[1]} | ||
| placeholder="0" | ||
| min={0} | ||
| step={1} | ||
| className="w-full h-7 rounded-none border-x-0" | ||
| onValueCommit={(v) => handleIndividualChange(1, v)} | ||
| aria-label="Corner radius top right" | ||
| /> | ||
| <Label> | ||
| <CornerTopRightIcon className="size-3" /> | ||
| </Label> | ||
| </div> | ||
| {/* Separator */} | ||
| <hr className="w-px h-10" /> | ||
| {/* Bottom Right */} | ||
| <div className="flex flex-col items-center flex-1"> | ||
| <InputPropertyNumber | ||
| mode="fixed" | ||
| type="number" | ||
| value={cornerValues[2]} | ||
| placeholder="0" | ||
| min={0} | ||
| step={1} | ||
| className="w-full h-7 rounded-none border-x-0" | ||
| onValueCommit={(v) => handleIndividualChange(2, v)} | ||
| aria-label="Corner radius bottom right" | ||
| /> | ||
| <Label> | ||
| <CornerBottomRightIcon className="size-3" /> | ||
| </Label> | ||
| </div> | ||
| {/* Separator */} | ||
| <hr className="w-px h-10" /> | ||
| {/* Bottom Left */} | ||
| <div className="flex flex-col items-center flex-1"> | ||
| <InputPropertyNumber | ||
| mode="fixed" | ||
| type="number" | ||
| value={cornerValues[3]} | ||
| placeholder="0" | ||
| min={0} | ||
| step={1} | ||
| className="w-full h-7 rounded-none rounded-r-md border-l-0" | ||
| onValueCommit={(v) => handleIndividualChange(3, v)} | ||
| aria-label="Corner radius bottom left" | ||
| /> | ||
| <Label> | ||
| <CornerBottomLeftIcon className="size-3" /> | ||
| </Label> | ||
| </div> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disabled state isn’t applied to per-corner inputs.
When disabled is true, the four inputs remain interactive. Forward the prop to each input.
<InputPropertyNumber
mode="fixed"
type="number"
+ disabled={disabled}
value={cornerValues[0]}
@@
<InputPropertyNumber
mode="fixed"
type="number"
+ disabled={disabled}
value={cornerValues[1]}
@@
<InputPropertyNumber
mode="fixed"
type="number"
+ disabled={disabled}
value={cornerValues[2]}
@@
<InputPropertyNumber
mode="fixed"
type="number"
+ disabled={disabled}
value={cornerValues[3]}📝 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.
| {/* Second Row: Individual Corner Radius Controls */} | |
| {showIndividual && ( | |
| <div className="flex items-center"> | |
| {/* Top Left */} | |
| <div className="flex flex-col items-center flex-1"> | |
| <InputPropertyNumber | |
| mode="fixed" | |
| type="number" | |
| value={cornerValues[0]} | |
| placeholder="0" | |
| min={0} | |
| step={1} | |
| className="w-full h-7 rounded-none rounded-l-md border-r-0" | |
| onValueCommit={(v) => handleIndividualChange(0, v)} | |
| aria-label="Corner radius top left" | |
| /> | |
| <Label> | |
| <CornerTopLeftIcon className="size-3" /> | |
| </Label> | |
| </div> | |
| {/* Separator */} | |
| <hr className="w-px h-10" /> | |
| {/* Top Right */} | |
| <div className="flex flex-col items-center flex-1"> | |
| <InputPropertyNumber | |
| mode="fixed" | |
| type="number" | |
| value={cornerValues[1]} | |
| placeholder="0" | |
| min={0} | |
| step={1} | |
| className="w-full h-7 rounded-none border-x-0" | |
| onValueCommit={(v) => handleIndividualChange(1, v)} | |
| aria-label="Corner radius top right" | |
| /> | |
| <Label> | |
| <CornerTopRightIcon className="size-3" /> | |
| </Label> | |
| </div> | |
| {/* Separator */} | |
| <hr className="w-px h-10" /> | |
| {/* Bottom Right */} | |
| <div className="flex flex-col items-center flex-1"> | |
| <InputPropertyNumber | |
| mode="fixed" | |
| type="number" | |
| value={cornerValues[2]} | |
| placeholder="0" | |
| min={0} | |
| step={1} | |
| className="w-full h-7 rounded-none border-x-0" | |
| onValueCommit={(v) => handleIndividualChange(2, v)} | |
| aria-label="Corner radius bottom right" | |
| /> | |
| <Label> | |
| <CornerBottomRightIcon className="size-3" /> | |
| </Label> | |
| </div> | |
| {/* Separator */} | |
| <hr className="w-px h-10" /> | |
| {/* Bottom Left */} | |
| <div className="flex flex-col items-center flex-1"> | |
| <InputPropertyNumber | |
| mode="fixed" | |
| type="number" | |
| value={cornerValues[3]} | |
| placeholder="0" | |
| min={0} | |
| step={1} | |
| className="w-full h-7 rounded-none rounded-r-md border-l-0" | |
| onValueCommit={(v) => handleIndividualChange(3, v)} | |
| aria-label="Corner radius bottom left" | |
| /> | |
| <Label> | |
| <CornerBottomLeftIcon className="size-3" /> | |
| </Label> | |
| </div> | |
| </div> | |
| )} | |
| {/* Second Row: Individual Corner Radius Controls */} | |
| {showIndividual && ( | |
| <div className="flex items-center"> | |
| {/* Top Left */} | |
| <div className="flex flex-col items-center flex-1"> | |
| <InputPropertyNumber | |
| mode="fixed" | |
| type="number" | |
| disabled={disabled} | |
| value={cornerValues[0]} | |
| placeholder="0" | |
| min={0} | |
| step={1} | |
| className="w-full h-7 rounded-none rounded-l-md border-r-0" | |
| onValueCommit={(v) => handleIndividualChange(0, v)} | |
| aria-label="Corner radius top left" | |
| /> | |
| <Label> | |
| <CornerTopLeftIcon className="size-3" /> | |
| </Label> | |
| </div> | |
| {/* Separator */} | |
| <hr className="w-px h-10" /> | |
| {/* Top Right */} | |
| <div className="flex flex-col items-center flex-1"> | |
| <InputPropertyNumber | |
| mode="fixed" | |
| type="number" | |
| disabled={disabled} | |
| value={cornerValues[1]} | |
| placeholder="0" | |
| min={0} | |
| step={1} | |
| className="w-full h-7 rounded-none border-x-0" | |
| onValueCommit={(v) => handleIndividualChange(1, v)} | |
| aria-label="Corner radius top right" | |
| /> | |
| <Label> | |
| <CornerTopRightIcon className="size-3" /> | |
| </Label> | |
| </div> | |
| {/* Separator */} | |
| <hr className="w-px h-10" /> | |
| {/* Bottom Right */} | |
| <div className="flex flex-col items-center flex-1"> | |
| <InputPropertyNumber | |
| mode="fixed" | |
| type="number" | |
| disabled={disabled} | |
| value={cornerValues[2]} | |
| placeholder="0" | |
| min={0} | |
| step={1} | |
| className="w-full h-7 rounded-none border-x-0" | |
| onValueCommit={(v) => handleIndividualChange(2, v)} | |
| aria-label="Corner radius bottom right" | |
| /> | |
| <Label> | |
| <CornerBottomRightIcon className="size-3" /> | |
| </Label> | |
| </div> | |
| {/* Separator */} | |
| <hr className="w-px h-10" /> | |
| {/* Bottom Left */} | |
| <div className="flex flex-col items-center flex-1"> | |
| <InputPropertyNumber | |
| mode="fixed" | |
| type="number" | |
| disabled={disabled} | |
| value={cornerValues[3]} | |
| placeholder="0" | |
| min={0} | |
| step={1} | |
| className="w-full h-7 rounded-none rounded-r-md border-l-0" | |
| onValueCommit={(v) => handleIndividualChange(3, v)} | |
| aria-label="Corner radius bottom left" | |
| /> | |
| <Label> | |
| <CornerBottomLeftIcon className="size-3" /> | |
| </Label> | |
| </div> | |
| </div> | |
| )} |
🤖 Prompt for AI Agents
In editor/scaffolds/sidecontrol/controls/corner-radius.tsx around lines 146–224,
the per-corner InputPropertyNumber controls do not receive the component's
disabled prop so they remain interactive when disabled is true; update each of
the four InputPropertyNumber usages to forward the disabled prop (e.g.,
disabled={disabled}) so they become non-interactive when disabled, and ensure
any visual disabled styles rely on that prop (no other logic changes required).
Summary by CodeRabbit
Release Notes
New Features
UI Improvements
Bug Fixes
Documentation