feat: show rename indicators in file tree and preview, right-align import button
- Move "Import n files" button to right side of its container - Show "→ newName" rename indicator next to files/directories in the file tree when an agent or project is being renamed on import - Show "→ newName" rename indicator in the file preview header when viewing a file that will be renamed - Uses cyan color to distinguish rename info from action badges Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
4a5aba5bac
commit
bf9b057670
1 changed files with 48 additions and 7 deletions
|
|
@ -132,16 +132,28 @@ function FrontmatterCard({ data }: { data: FrontmatterData }) {
|
||||||
|
|
||||||
// ── Import file tree customization ───────────────────────────────────
|
// ── Import file tree customization ───────────────────────────────────
|
||||||
|
|
||||||
function renderImportFileExtra(node: FileTreeNode, checked: boolean) {
|
function renderImportFileExtra(node: FileTreeNode, checked: boolean, renameMap: Map<string, string>) {
|
||||||
if (!node.action) return null;
|
const renamedTo = renameMap.get(node.path);
|
||||||
const actionColor = ACTION_COLORS[node.action] ?? ACTION_COLORS.skip;
|
const actionBadge = node.action ? (
|
||||||
return (
|
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"shrink-0 rounded-full border px-2 py-0.5 text-[10px] uppercase tracking-wide",
|
"shrink-0 rounded-full border px-2 py-0.5 text-[10px] uppercase tracking-wide",
|
||||||
actionColor,
|
ACTION_COLORS[node.action] ?? ACTION_COLORS.skip,
|
||||||
)}>
|
)}>
|
||||||
{checked ? node.action : "skip"}
|
{checked ? node.action : "skip"}
|
||||||
</span>
|
</span>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
if (!actionBadge && !renamedTo) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="inline-flex items-center gap-1.5 shrink-0">
|
||||||
|
{renamedTo && checked && (
|
||||||
|
<span className="text-[10px] text-cyan-500 font-mono truncate max-w-[7rem]" title={renamedTo}>
|
||||||
|
→ {renamedTo}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{actionBadge}
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,10 +167,12 @@ function ImportPreviewPane({
|
||||||
selectedFile,
|
selectedFile,
|
||||||
content,
|
content,
|
||||||
action,
|
action,
|
||||||
|
renamedTo,
|
||||||
}: {
|
}: {
|
||||||
selectedFile: string | null;
|
selectedFile: string | null;
|
||||||
content: string | null;
|
content: string | null;
|
||||||
action: string | null;
|
action: string | null;
|
||||||
|
renamedTo: string | null;
|
||||||
}) {
|
}) {
|
||||||
if (!selectedFile || content === null) {
|
if (!selectedFile || content === null) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -174,7 +188,14 @@ function ImportPreviewPane({
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="border-b border-border px-5 py-3">
|
<div className="border-b border-border px-5 py-3">
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex items-center justify-between gap-3">
|
||||||
<div className="truncate font-mono text-sm">{selectedFile}</div>
|
<div className="min-w-0 flex items-center gap-2">
|
||||||
|
<span className="truncate font-mono text-sm">{selectedFile}</span>
|
||||||
|
{renamedTo && (
|
||||||
|
<span className="shrink-0 font-mono text-sm text-cyan-500">
|
||||||
|
→ {renamedTo}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{action && (
|
{action && (
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"shrink-0 rounded-full border px-2 py-0.5 text-xs uppercase tracking-wide",
|
"shrink-0 rounded-full border px-2 py-0.5 text-xs uppercase tracking-wide",
|
||||||
|
|
@ -652,6 +673,25 @@ export function CompanyImport() {
|
||||||
[importPreview],
|
[importPreview],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Map file paths (and their parent dir) → planned rename name for display in tree + preview
|
||||||
|
const renameMap = useMemo(() => {
|
||||||
|
const map = new Map<string, string>();
|
||||||
|
if (!importPreview) return map;
|
||||||
|
for (const c of conflicts) {
|
||||||
|
if (!c.filePath) continue;
|
||||||
|
const isSkipped = skippedSlugs.has(c.slug);
|
||||||
|
if (isSkipped) continue;
|
||||||
|
const renamedTo = nameOverrides[c.slug] ?? c.plannedName;
|
||||||
|
if (renamedTo === c.originalName) continue;
|
||||||
|
// Map the file itself
|
||||||
|
map.set(c.filePath, renamedTo);
|
||||||
|
// Map the parent directory (e.g. agents/ceo → gstack-ceo)
|
||||||
|
const parentDir = c.filePath.split("/").slice(0, -1).join("/");
|
||||||
|
if (parentDir) map.set(parentDir, renamedTo);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [importPreview, conflicts, nameOverrides, skippedSlugs]);
|
||||||
|
|
||||||
const totalFiles = useMemo(() => countFiles(tree), [tree]);
|
const totalFiles = useMemo(() => countFiles(tree), [tree]);
|
||||||
const selectedCount = checkedFiles.size;
|
const selectedCount = checkedFiles.size;
|
||||||
|
|
||||||
|
|
@ -972,7 +1012,7 @@ export function CompanyImport() {
|
||||||
onToggleDir={handleToggleDir}
|
onToggleDir={handleToggleDir}
|
||||||
onSelectFile={setSelectedFile}
|
onSelectFile={setSelectedFile}
|
||||||
onToggleCheck={handleToggleCheck}
|
onToggleCheck={handleToggleCheck}
|
||||||
renderFileExtra={renderImportFileExtra}
|
renderFileExtra={(node, checked) => renderImportFileExtra(node, checked, renameMap)}
|
||||||
fileRowClassName={importFileRowClassName}
|
fileRowClassName={importFileRowClassName}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -982,6 +1022,7 @@ export function CompanyImport() {
|
||||||
selectedFile={selectedFile}
|
selectedFile={selectedFile}
|
||||||
content={previewContent}
|
content={previewContent}
|
||||||
action={selectedAction}
|
action={selectedAction}
|
||||||
|
renamedTo={selectedFile ? (renameMap.get(selectedFile) ?? null) : null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue