KQ-1645 · NAME/CLASH
Name Clash
An upload conflict resolver that decides one clash at a time: the file in the folder and the one being uploaded arrive side by side, sliding together until they touch on springs.glide, and picking an action previews it on the cards themselves — Replace slides the old card under the new one on glide, Keep both parts them on springs.snap while the upload's name grows its " (2)", Skip sets the upload aside. Apply sinks the pair on the exit ease and deals the next, Apply to all settles the rest in a cascade while the counter rolls down on snap, and an empty queue closes the panel to a one-line receipt. The actions are a radiogroup — arrows pick and the cards answer — Enter applies from anywhere in the panel and A applies to every remaining conflict.
Name clash
Coldbrook / Brand / Exports
The upload is saved as menu-board (2).png.
6 of 6 left · keep both · menu-board.png
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/name-clash
Props
- conflicts
- NameClashConflict[]
- default defaultNameClashConflicts
- The queue: { id, existing, incoming }, each file { name, size, modified, author, kind?, seed? }.
- action / defaultAction
- "replace" | "keep" | "skip"
- default — / "keep"
- The action for the current conflict, controlled or initial.
- onActionChange
- (action) => void
- default —
- Fires from the click or arrow key that picked an action.
- onResolve
- (conflict, action, name) => void
- default —
- Fires once per file as it is settled, with the name the upload ends up with (empty when skipped).
- onDone
- (results: NameClashResult[]) => void
- default —
- Fires when the queue empties, with every decision in order.
- onCancel
- (remaining: NameClashConflict[]) => void
- default —
- The close button was pressed; these conflicts are left unsettled.
- status
- "ready" | "loading" | "error"
- default "ready"
- Loading blanks the thumbnails and reads Comparing files; error explains the folder's file couldn't be read and makes Replace unavailable.
- now
- number
- default defaultNameClashNow
- The moment ages count from, in ms; never read from the clock during render.
- folder
- string
- default "Coldbrook / Brand / Exports"
- The destination folder, shown under the title.
- nextName
- (conflict) => string
- default inserts " (2)" before the extension
- The name Keep both gives the upload; the part it adds grows in on the card.
- cardLayout
- "pair" | "stacked" | "tilted"
- default "pair"
- How the two cards meet: side by side, one above the other, or turned toward each other and overlapping the seam.
- thumbSize
- number
- default 80
- Thumbnail edge in px, capped at 48 below a 32rem container.
- accent
- string
- default "var(--accent-bright)"
- The chosen action, the upload's edge, the queue's current chip and Apply, any CSS colour.
- title
- string
- default "Name clash"
- The panel's heading and accessible name.
- height
- number
- default 540
- Height of the open panel in px; it glides down to a 64px receipt when the queue empties.
- disabled
- boolean
- default false
- Nothing can be picked or applied.
- className
- string
- default —
- Classes for the panel.
| Prop | Type | Default | Description |
|---|---|---|---|
| conflicts | NameClashConflict[] | defaultNameClashConflicts | The queue: { id, existing, incoming }, each file { name, size, modified, author, kind?, seed? }. |
| action / defaultAction | "replace" | "keep" | "skip" | — / "keep" | The action for the current conflict, controlled or initial. |
| onActionChange | (action) => void | — | Fires from the click or arrow key that picked an action. |
| onResolve | (conflict, action, name) => void | — | Fires once per file as it is settled, with the name the upload ends up with (empty when skipped). |
| onDone | (results: NameClashResult[]) => void | — | Fires when the queue empties, with every decision in order. |
| onCancel | (remaining: NameClashConflict[]) => void | — | The close button was pressed; these conflicts are left unsettled. |
| status | "ready" | "loading" | "error" | "ready" | Loading blanks the thumbnails and reads Comparing files; error explains the folder's file couldn't be read and makes Replace unavailable. |
| now | number | defaultNameClashNow | The moment ages count from, in ms; never read from the clock during render. |
| folder | string | "Coldbrook / Brand / Exports" | The destination folder, shown under the title. |
| nextName | (conflict) => string | inserts " (2)" before the extension | The name Keep both gives the upload; the part it adds grows in on the card. |
| cardLayout | "pair" | "stacked" | "tilted" | "pair" | How the two cards meet: side by side, one above the other, or turned toward each other and overlapping the seam. |
| thumbSize | number | 80 | Thumbnail edge in px, capped at 48 below a 32rem container. |
| accent | string | "var(--accent-bright)" | The chosen action, the upload's edge, the queue's current chip and Apply, any CSS colour. |
| title | string | "Name clash" | The panel's heading and accessible name. |
| height | number | 540 | Height of the open panel in px; it glides down to a 64px receipt when the queue empties. |
| disabled | boolean | false | Nothing can be picked or applied. |
| className | string | — | Classes for the panel. |