Skip to content
Merged
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
40 changes: 30 additions & 10 deletions extensions/roopik/src/canvasPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,8 @@ export class CanvasPanel implements vscode.Disposable {
filePath: string;
line?: number;
column?: number;
endLine?: number;
endColumn?: number;
});
break;

Expand Down Expand Up @@ -734,13 +736,15 @@ export class CanvasPanel implements vscode.Disposable {

/**
* Handle open file request from webview (View Code button)
* Supports both cursor positioning and selection highlighting
*/
private async handleOpenFile(payload: {
filePath: string;
line?: number;
column?: number;
endLine?: number;
endColumn?: number;
}): Promise<void> {
this.logger.info(`Opening file: ${payload.filePath}`);

try {
// Resolve the file path relative to workspace
Expand All @@ -766,16 +770,32 @@ export class CanvasPanel implements vscode.Disposable {
viewColumn: vscode.ViewColumn.One
});

// Move cursor to specified line/column if provided
// Move cursor or create selection if line is provided
if (payload.line !== undefined) {
const line = Math.max(0, payload.line - 1); // Convert to 0-based
const column = Math.max(0, (payload.column || 1) - 1); // Convert to 0-based
const position = new vscode.Position(line, column);
editor.selection = new vscode.Selection(position, position);
editor.revealRange(
new vscode.Range(position, position),
vscode.TextEditorRevealType.InCenter
);
const startLine = Math.max(0, payload.line - 1); // Convert to 0-based
const startColumn = Math.max(0, (payload.column || 1) - 1); // Convert to 0-based
const startPos = new vscode.Position(startLine, startColumn);

// Check if we have end position for selection highlighting
if (payload.endLine !== undefined && payload.endColumn !== undefined) {
const endLine = Math.max(0, payload.endLine - 1);
const endColumn = Math.max(0, payload.endColumn - 1);
const endPos = new vscode.Position(endLine, endColumn);

// Create selection range (highlighted)
editor.selection = new vscode.Selection(startPos, endPos);
editor.revealRange(
new vscode.Range(startPos, endPos),
vscode.TextEditorRevealType.InCenter
);
} else {
// Just position cursor (no selection)
editor.selection = new vscode.Selection(startPos, startPos);
editor.revealRange(
new vscode.Range(startPos, startPos),
vscode.TextEditorRevealType.InCenter
);
}
}

// this.logger.info(`File opened successfully: ${absolutePath}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface InfiniteCanvasProps {
snapMode?: SnapMode;
/** Viewport dimensions for dynamic focused sandbox sizing */
viewport?: { width: number; height: number };
/** Whether select mode is enabled globally */
isSelectMode?: boolean;
/** Whether inspect mode is enabled globally */
isInspectMode?: boolean;
/** Whether inspect mode should auto-capture screenshots */
Expand Down Expand Up @@ -61,6 +63,8 @@ interface SandboxLayerProps {
globalDeviceMode: DevicePreset;
/** Viewport dimensions for dynamic focused sandbox sizing */
viewport?: { width: number; height: number };
/** Whether select mode is enabled globally */
isSelectMode?: boolean;
/** Whether inspect mode is enabled globally */
isInspectMode?: boolean;
/** Whether inspect mode should auto-capture screenshots */
Expand All @@ -87,6 +91,7 @@ function SandboxLayer({
dragOffset,
globalDeviceMode,
viewport,
isSelectMode,
isInspectMode,
captureOnInspectSelect,
onSandboxDragStart,
Expand Down Expand Up @@ -125,6 +130,7 @@ function SandboxLayer({
globalDeviceMode={globalDeviceMode}
viewport={viewport}
focusedSandboxPosition={focusedSandboxPosition}
isSelectMode={isSelectMode}
isInspectMode={isInspectMode}
captureOnInspectSelect={captureOnInspectSelect}
onMouseDown={(e) => onSandboxDragStart(e, sandbox.id)}
Expand Down Expand Up @@ -167,6 +173,7 @@ export function InfiniteCanvas({
globalDeviceMode,
snapMode = 'free',
viewport,
isSelectMode,
isInspectMode,
captureOnInspectSelect,
onTransformChange,
Expand Down Expand Up @@ -240,6 +247,7 @@ export function InfiniteCanvas({
dragOffset={dragOffset}
globalDeviceMode={globalDeviceMode}
viewport={viewport}
isSelectMode={isSelectMode}
isInspectMode={isInspectMode}
captureOnInspectSelect={captureOnInspectSelect}
onSandboxDragStart={handleSandboxDragStart}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface SandboxCardProps {
viewport?: { width: number; height: number };
/** Position of the focused sandbox (for calculating push-away offset) */
focusedSandboxPosition?: { x: number; y: number } | null;
/** Whether select mode is enabled globally */
isSelectMode?: boolean;
/** Whether inspect mode is enabled globally */
isInspectMode?: boolean;
/** Whether inspect mode should auto-capture screenshots */
Expand Down Expand Up @@ -490,6 +492,7 @@ export function SandboxCard({
globalDeviceMode,
viewport,
focusedSandboxPosition,
isSelectMode = false,
isInspectMode = false,
captureOnInspectSelect = false,
onMouseDown,
Expand All @@ -508,6 +511,16 @@ export function SandboxCard({
// Track if component is "activated" for interaction (click-to-activate for better zoom UX)
const [isActivated, setIsActivated] = useState(false);

// Send select mode toggle to iframe when isSelectMode changes
useEffect(() => {
if (iframeRef.current?.contentWindow) {
iframeRef.current.contentWindow.postMessage({
type: 'roopik-toggle-select',
enabled: isSelectMode
}, '*');
}
}, [isSelectMode, sandbox.id]);

// Send inspect mode toggle to iframe when isInspectMode changes
useEffect(() => {
if (iframeRef.current?.contentWindow) {
Expand Down
2 changes: 1 addition & 1 deletion extensions/roopik/webview/src/canvasView/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ export type WebviewMessage =
// Canvas state
| { type: 'saveCanvas'; payload: { canvasId: string; state: CanvasState } }
| { type: 'loadCanvas'; payload: { canvasId: string } }
| { type: 'openFile'; payload: { filePath: string; line?: number; column?: number } }
| { type: 'openFile'; payload: { filePath: string; line?: number; column?: number; endLine?: number; endColumn?: number } }
| { type: 'log'; payload: { level: 'debug' | 'info' | 'warn' | 'error'; message: string; data?: unknown } }
// Code editor - request to load component files from workspace
| { type: 'loadComponentFiles'; payload: { componentId: string } }
Expand Down
54 changes: 54 additions & 0 deletions extensions/roopik/webview/src/componentView/ComponentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,59 @@ function App() {
}
});
}

// Handle select mode: open source file in editor with selection
if (data.type === "roopik-select-open-source") {
const { sourceLocation, componentId } = data;
if (sourceLocation?.file) {
// Source file is relative (e.g., "index.tsx"), need to resolve to absolute
// using the component's folderPath
let absoluteFilePath = sourceLocation.file;
const sandbox = sandboxes.find(s => s.id === componentId);
if (sandbox?.componentInput?.folderPath && !sourceLocation.file.includes('/') && !sourceLocation.file.includes('\\')) {
// Relative path - combine with folderPath
absoluteFilePath = `${sandbox.componentInput.folderPath}/${sourceLocation.file}`;
}

vscode.postMessage({
type: 'openFile',
payload: {
filePath: absoluteFilePath,
line: sourceLocation.startLine || 1,
column: sourceLocation.startCol || 1,
endLine: sourceLocation.endLine,
endColumn: sourceLocation.endCol
}
});
}
}

// Handle select mode: open component in editor
if (data.type === "roopik-select-open-component") {
const { componentId } = data;
const sandbox = sandboxes.find(s => s.id === componentId);
if (sandbox?.componentInput?.folderPath && sandbox.componentInput.entryFile) {
const filePath = `${sandbox.componentInput.folderPath}/${sandbox.componentInput.entryFile}`;
vscode.postMessage({
type: 'openFile',
payload: {
filePath,
line: 1,
column: 1
}
});
}
}

// Handle select mode: ESC pressed, deselect
if (data.type === "roopik-select-escape") {
setIsSelectMode(false);
}

// Handle select mode: selection cleared (click outside elements)
if (data.type === "roopik-select-cleared") {
// Selection was cleared in the sandbox
}
};

window.addEventListener("message", handleIframeMessage);
Expand Down Expand Up @@ -1513,6 +1566,7 @@ function App() {
globalDeviceMode={globalDeviceMode}
snapMode={snapMode}
viewport={viewport}
isSelectMode={isSelectMode}
isInspectMode={isInspectMode}
captureOnInspectSelect={AUTO_ATTACH_SCREENSHOT_ON_INSPECT}
onTransformChange={setTransform}
Expand Down
Loading
Loading