Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1626,7 +1626,7 @@ class SuperdesignCanvasPanel {

// Handle messages from the webview
this._panel.webview.onDidReceiveMessage(
message => {
async message => {
switch (message.command) {
case 'loadDesignFiles':
this._loadDesignFiles();
Expand All @@ -1648,6 +1648,9 @@ class SuperdesignCanvasPanel {
data: message.data
});
break;
case 'deleteDesignFile':
await this._deleteDesignFile(message.fileName);
break;
}
},
null,
Expand Down Expand Up @@ -1876,6 +1879,48 @@ class SuperdesignCanvasPanel {
}
}

private async _deleteDesignFile(fileName: string) {
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
if (!workspaceFolder) {
Logger.error('No workspace folder found for deleting file');
return;
}

try {
const filePath = vscode.Uri.joinPath(
workspaceFolder.uri,
'.superdesign',
'design_iterations',
fileName
);

// Check if file exists
try {
await vscode.workspace.fs.stat(filePath);
} catch {
Logger.warn(`File not found for deletion: ${fileName}`);
return;
}

// Delete the file
await vscode.workspace.fs.delete(filePath);
Logger.info(`Deleted design file: ${fileName}`);

// Notify webview of successful deletion
this._panel.webview.postMessage({
command: 'fileDeleted',
data: { fileName }
});

} catch (error) {
Logger.error(`Failed to delete file ${fileName}: ${error}`);
this._panel.webview.postMessage({
command: 'error',
data: { error: `Failed to delete file: ${error}` }
});
}
}

private async _inlineExternalCSS(htmlContent: string, designFolder: vscode.Uri): Promise<string> {
// Match link tags that reference CSS files
const linkRegex = /<link\s+[^>]*rel=["']stylesheet["'][^>]*href=["']([^"']+)["'][^>]*>/gi;
Expand Down
13 changes: 11 additions & 2 deletions src/providers/claudeCodeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,12 @@ Whenever there are UI implementation task, think deeply of the design style firs

let buffer = '';
const outputs: string[] = [];
let wasAborted = false;

if (abortController) {
abortController.signal.addEventListener('abort', () => {
Logger.info('Aborting claude-code process');
wasAborted = true;
child.kill('SIGTERM');
});
}
Expand Down Expand Up @@ -389,12 +391,19 @@ Whenever there are UI implementation task, think deeply of the design style firs
child.on('close', (code) => {
Logger.info(`Claude Code process closed with code: ${code}`);
Logger.info(`Process outputs collected: ${outputs.length} items`);

// Process any remaining buffer
if (buffer.trim()) {
outputs.push(buffer.trim());
}


// If aborted, always resolve successfully
if (wasAborted) {
Logger.info('Process was aborted by user, resolving successfully');
resolve(outputs);
return;
}

if (code !== 0 && !abortController?.signal.aborted) {
let errorMessage = `Claude Code process exited with code ${code}`;
if (code === 2) {
Expand Down
96 changes: 96 additions & 0 deletions src/webview/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -1417,6 +1417,102 @@
}
}

/* Delete Confirmation Modal */
.delete-confirm-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
animation: fadeIn 0.2s ease;
}

.delete-confirm-dialog {
background: var(--vscode-editorWidget-background);
border: 1px solid var(--vscode-editorWidget-border);
border-radius: 6px;
padding: 20px;
min-width: 300px;
max-width: 400px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
animation: slideUp 0.2s ease;
}

.delete-confirm-dialog h3 {
margin: 0 0 12px 0;
font-size: 1rem;
font-weight: 600;
color: var(--vscode-foreground);
}

.delete-confirm-dialog p {
margin: 0 0 20px 0;
font-size: 0.9rem;
color: var(--vscode-descriptionForeground);
line-height: 1.4;
}

.delete-confirm-buttons {
display: flex;
gap: 8px;
justify-content: flex-end;
}

.delete-confirm-cancel,
.delete-confirm-delete {
padding: 6px 16px;
border-radius: 4px;
border: none;
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}

.delete-confirm-cancel {
background: var(--vscode-button-secondaryBackground);
color: var(--vscode-button-secondaryForeground);
}

.delete-confirm-cancel:hover {
background: var(--vscode-button-secondaryHoverBackground);
}

.delete-confirm-delete {
background: var(--vscode-errorForeground);
color: white;
}

.delete-confirm-delete:hover {
opacity: 0.9;
transform: translateY(-1px);
}

@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

/* Copied Notification */
.copied-notification {
position: absolute;
Expand Down
21 changes: 21 additions & 0 deletions src/webview/components/CanvasView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,26 @@ const CanvasView: React.FC<CanvasViewProps> = ({ vscode, nonce }) => {
});
};

// Handle design file deletion
const handleDeleteDesign = (fileName: string) => {
// Send delete request to backend
vscode.postMessage({
command: 'deleteDesignFile',
fileName: fileName
});

// Optimistically remove from UI
setDesignFiles(prev => prev.filter(file => file.name !== fileName));
setSelectedFrames(prev => prev.filter(name => name !== fileName));

// Clean up custom positions
setCustomPositions(prev => {
const updated = { ...prev };
delete updated[fileName];
return updated;
});
};

// Keyboard shortcuts for zoom
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
Expand Down Expand Up @@ -847,6 +867,7 @@ const CanvasView: React.FC<CanvasViewProps> = ({ vscode, nonce }) => {
isDragging={dragState.isDragging && dragState.draggedFrame === file.name}
nonce={nonce}
onSendToChat={handleSendToChat}
onDelete={handleDeleteDesign}
/>
);
})}
Expand Down
6 changes: 4 additions & 2 deletions src/webview/components/Chat/ChatInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1481,10 +1481,12 @@ const ChatInterface: React.FC<ChatInterfaceProps> = ({ layout, vscode }) => {
</svg>
</button>
{isLoading ? (
<button
<button
onClick={() => {
// Stop functionality can be added later
console.log('Stop requested');
vscode.postMessage({
command: 'stopChat'
});
}}
className="send-btn stop-btn"
title="Stop response"
Expand Down
71 changes: 70 additions & 1 deletion src/webview/components/DesignFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface DesignFrameProps {
isDragging?: boolean;
nonce?: string | null;
onSendToChat?: (fileName: string, prompt: string) => void;
onDelete?: (fileName: string) => void;
}

const DesignFrame: React.FC<DesignFrameProps> = ({
Expand All @@ -42,14 +43,16 @@ const DesignFrame: React.FC<DesignFrameProps> = ({
onDragStart,
isDragging = false,
nonce = null,
onSendToChat
onSendToChat,
onDelete
}) => {
const [isLoading, setIsLoading] = React.useState(renderMode === 'iframe');
const [hasError, setHasError] = React.useState(false);
const [dragPreventOverlay, setDragPreventOverlay] = React.useState(false);
const [showCopyDropdown, setShowCopyDropdown] = React.useState(false);
const [copyButtonState, setCopyButtonState] = React.useState<{ text: string; isSuccess: boolean }>({ text: 'Copy prompt', isSuccess: false });
const [copyPathButtonState, setCopyPathButtonState] = React.useState<{ text: string; isSuccess: boolean }>({ text: 'Copy design path', isSuccess: false });
const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);

const handleClick = () => {
onSelect(file.name);
Expand Down Expand Up @@ -828,6 +831,72 @@ const DesignFrame: React.FC<DesignFrameProps> = ({
</svg>
<span className="btn-text">{copyPathButtonState.text}</span>
</button>

{/* Delete Button */}
{onDelete && (
<>
<button
className="floating-action-btn delete-btn"
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
setShowDeleteConfirm(true);
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
title="Delete design file"
>
<svg className="btn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 6h18"/>
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/>
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>
</svg>
<span className="btn-text">Delete</span>
</button>

{/* Delete Confirmation Modal */}
{showDeleteConfirm && (
<div
className="delete-confirm-overlay"
onClick={(e) => {
e.stopPropagation();
setShowDeleteConfirm(false);
}}
>
<div
className="delete-confirm-dialog"
onClick={(e) => e.stopPropagation()}
>
<h3>Delete Design?</h3>
<p>Are you sure you want to delete "{file.name}"?</p>
<div className="delete-confirm-buttons">
<button
className="delete-confirm-cancel"
onClick={(e) => {
e.stopPropagation();
setShowDeleteConfirm(false);
}}
>
Cancel
</button>
<button
className="delete-confirm-delete"
onClick={(e) => {
e.stopPropagation();
setShowDeleteConfirm(false);
onDelete(file.name);
}}
>
Delete
</button>
</div>
</div>
</div>
)}
</>
)}
</div>
)}

Expand Down