From c031a58a22b8a97f80dff8a2033b42efdc63fb73 Mon Sep 17 00:00:00 2001 From: Ivanruii Date: Mon, 11 May 2026 13:10:42 +0200 Subject: [PATCH 1/6] fix(vscode-editor): reorder webview HTML assignment for improved message handling --- packages/vscode-extension/src/editor/provider.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/vscode-extension/src/editor/provider.ts b/packages/vscode-extension/src/editor/provider.ts index 64d018db..395936f7 100644 --- a/packages/vscode-extension/src/editor/provider.ts +++ b/packages/vscode-extension/src/editor/provider.ts @@ -109,11 +109,6 @@ export class QuickMockEditorProvider implements vscode.CustomEditorProvider { await handleWebviewMessage(msg, doc, reply => @@ -121,6 +116,12 @@ export class QuickMockEditorProvider implements vscode.CustomEditorProvider Date: Mon, 11 May 2026 13:17:27 +0200 Subject: [PATCH 2/6] chore: added changeset --- .changeset/tasty-steaks-know.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tasty-steaks-know.md diff --git a/.changeset/tasty-steaks-know.md b/.changeset/tasty-steaks-know.md new file mode 100644 index 00000000..35abdf1f --- /dev/null +++ b/.changeset/tasty-steaks-know.md @@ -0,0 +1,5 @@ +--- +'quickmock': patch +--- + +Fix editor failing to load files opened from outside the workspace. The webview HTML was assigned before registering `onDidReceiveMessage`, causing a race where the initial `READY` / `WEBVIEW_READY` message from the app could be lost and the file content never delivered. The listener is now registered before the HTML assignment. From d75c15c562bc77ddc283b52c6b724a861f8445d8 Mon Sep 17 00:00:00 2001 From: Ivanruii Date: Tue, 12 May 2026 08:58:58 +0200 Subject: [PATCH 3/6] feat(vscode-extension): integrate new file functionality and command handling --- .../components/new-button/new-button.tsx | 11 ++++- packages/bridge-protocol/src/constant.ts | 1 + packages/bridge-protocol/src/model.ts | 3 +- .../src/commands/new-wireframe.ts | 11 ----- .../src/commands/new-wireframe/index.ts | 2 + .../new-wireframe/new-wireframe.command.ts | 14 ++++++ .../new-wireframe/new-wireframe.handler.ts | 45 +++++++++++++++++++ .../new-wireframe/new-wireframe.id.ts | 1 + .../vscode-extension/src/editor/handlers.ts | 6 +++ packages/vscode-extension/src/editor/index.ts | 1 + .../vscode-extension/src/editor/template.ts | 17 +++++++ 11 files changed, 98 insertions(+), 14 deletions(-) delete mode 100644 packages/vscode-extension/src/commands/new-wireframe.ts create mode 100644 packages/vscode-extension/src/commands/new-wireframe/index.ts create mode 100644 packages/vscode-extension/src/commands/new-wireframe/new-wireframe.command.ts create mode 100644 packages/vscode-extension/src/commands/new-wireframe/new-wireframe.handler.ts create mode 100644 packages/vscode-extension/src/commands/new-wireframe/new-wireframe.id.ts create mode 100644 packages/vscode-extension/src/editor/template.ts diff --git a/apps/web/src/pods/toolbar/components/new-button/new-button.tsx b/apps/web/src/pods/toolbar/components/new-button/new-button.tsx index 147843d3..24bc75ea 100644 --- a/apps/web/src/pods/toolbar/components/new-button/new-button.tsx +++ b/apps/web/src/pods/toolbar/components/new-button/new-button.tsx @@ -1,13 +1,20 @@ import { NewIcon } from '#common/components/icons/new-button.components'; -import classes from '#pods/toolbar/toolbar.pod.module.css'; +import { isVSCodeEnv } from '#common/utils/env.utils'; +import { sendToExtension } from '#common/utils/vscode-bridge.utils'; import { useCanvasContext } from '#core/providers'; -import { ToolbarButton } from '../toolbar-button'; +import classes from '#pods/toolbar/toolbar.pod.module.css'; +import { APP_MESSAGE_TYPE } from '@lemoncode/quickmock-bridge-protocol'; import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ToolbarButton } from '../toolbar-button'; export const NewButton = () => { const { createNewFullDocument: clearCanvas } = useCanvasContext(); const handleClick = () => { + if (isVSCodeEnv()) { + sendToExtension({ type: APP_MESSAGE_TYPE.NEW_FILE }); + return; + } clearCanvas(); }; diff --git a/packages/bridge-protocol/src/constant.ts b/packages/bridge-protocol/src/constant.ts index 5caee2c2..b96aa128 100644 --- a/packages/bridge-protocol/src/constant.ts +++ b/packages/bridge-protocol/src/constant.ts @@ -10,4 +10,5 @@ export const APP_MESSAGE_TYPE = { SAVE: 'qm:save', RENDER_COMPLETE: 'qm:render-complete', WEBVIEW_READY: 'WEBVIEW_READY', + NEW_FILE: 'qm:new-file', } as const; diff --git a/packages/bridge-protocol/src/model.ts b/packages/bridge-protocol/src/model.ts index 513b2501..ccbdedb2 100644 --- a/packages/bridge-protocol/src/model.ts +++ b/packages/bridge-protocol/src/model.ts @@ -34,7 +34,8 @@ export type AppMessage = | { type: typeof APP_MESSAGE_TYPE.RENDER_COMPLETE; payload?: ContentBbox; - }; + } + | { type: typeof APP_MESSAGE_TYPE.NEW_FILE }; export type PayloadOf = Extract extends { payload: infer P } ? P : undefined; diff --git a/packages/vscode-extension/src/commands/new-wireframe.ts b/packages/vscode-extension/src/commands/new-wireframe.ts deleted file mode 100644 index 628f537c..00000000 --- a/packages/vscode-extension/src/commands/new-wireframe.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as vscode from 'vscode'; - -export const registerNewWireframeCommand = ( - context: vscode.ExtensionContext -): void => { - context.subscriptions.push( - vscode.commands.registerCommand('quickmock.newWireframe', () => { - vscode.window.showInformationMessage('New wireframe coming soon'); // TODO: Implement the actual functionality for creating a new wireframe - }) - ); -}; diff --git a/packages/vscode-extension/src/commands/new-wireframe/index.ts b/packages/vscode-extension/src/commands/new-wireframe/index.ts new file mode 100644 index 00000000..9dc0e646 --- /dev/null +++ b/packages/vscode-extension/src/commands/new-wireframe/index.ts @@ -0,0 +1,2 @@ +export * from './new-wireframe.id'; +export * from './new-wireframe.command'; diff --git a/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.command.ts b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.command.ts new file mode 100644 index 00000000..5e839522 --- /dev/null +++ b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.command.ts @@ -0,0 +1,14 @@ +import * as vscode from 'vscode'; +import { QUICKMOCK_NEW_WIREFRAME_COMMAND_ID } from './new-wireframe.id'; +import { handleNewWireframe } from './new-wireframe.handler'; + +export const registerNewWireframeCommand = ( + context: vscode.ExtensionContext +): void => { + context.subscriptions.push( + vscode.commands.registerCommand( + QUICKMOCK_NEW_WIREFRAME_COMMAND_ID, + handleNewWireframe + ) + ); +}; diff --git a/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.handler.ts b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.handler.ts new file mode 100644 index 00000000..95139c4d --- /dev/null +++ b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.handler.ts @@ -0,0 +1,45 @@ +import { getPrimaryWorkspaceFolder } from '#core'; +import { writeFile } from '#editor/document'; +import { + createEmptyQuickMockContent, + DEFAULT_QUICKMOCK_FILE_NAME, + QUICKMOCK_FILE_EXTENSION, +} from '#editor/template'; +import * as vscode from 'vscode'; + +const CUSTOM_EDITOR_VIEW_TYPE = 'quickmock.editor'; + +const pickSaveTarget = async (): Promise => { + const folder = getPrimaryWorkspaceFolder(); + const defaultUri = folder + ? vscode.Uri.joinPath(folder.uri, DEFAULT_QUICKMOCK_FILE_NAME) + : undefined; + + return vscode.window.showSaveDialog({ + defaultUri, + filters: { 'QuickMock Wireframe': [QUICKMOCK_FILE_EXTENSION] }, + saveLabel: 'Create', + title: 'Create QuickMock File', + }); +}; + +export const handleNewWireframe = async (): Promise => { + const target = await pickSaveTarget(); + if (!target) return; + + try { + await writeFile(target, createEmptyQuickMockContent()); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + vscode.window.showErrorMessage( + `Failed to create QuickMock file: ${message}` + ); + return; + } + + await vscode.commands.executeCommand( + 'vscode.openWith', + target, + CUSTOM_EDITOR_VIEW_TYPE + ); +}; diff --git a/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.id.ts b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.id.ts new file mode 100644 index 00000000..22b740b5 --- /dev/null +++ b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.id.ts @@ -0,0 +1 @@ +export const QUICKMOCK_NEW_WIREFRAME_COMMAND_ID = 'quickmock.newWireframe'; diff --git a/packages/vscode-extension/src/editor/handlers.ts b/packages/vscode-extension/src/editor/handlers.ts index e6795936..074d99ae 100644 --- a/packages/vscode-extension/src/editor/handlers.ts +++ b/packages/vscode-extension/src/editor/handlers.ts @@ -5,6 +5,8 @@ import { HOST_MESSAGE_TYPE, type HostMessage, } from '@lemoncode/quickmock-bridge-protocol'; +import * as vscode from 'vscode'; +import { QUICKMOCK_NEW_WIREFRAME_COMMAND_ID } from '#commands'; import { type QuickMockDocument, writeFile } from './document'; type PostMessageFn = (msg: HostMessage) => void; @@ -41,5 +43,9 @@ export const handleWebviewMessage = async ( await writeFile(doc.uri, doc.content); postMessage({ type: HOST_MESSAGE_TYPE.SAVED }); break; + + case APP_MESSAGE_TYPE.NEW_FILE: + await vscode.commands.executeCommand(QUICKMOCK_NEW_WIREFRAME_COMMAND_ID); + break; } }; diff --git a/packages/vscode-extension/src/editor/index.ts b/packages/vscode-extension/src/editor/index.ts index 3951f00f..816b772f 100644 --- a/packages/vscode-extension/src/editor/index.ts +++ b/packages/vscode-extension/src/editor/index.ts @@ -2,3 +2,4 @@ export * from './document'; export * from './handlers'; export * from './panel'; export * from './provider'; +export * from './template'; diff --git a/packages/vscode-extension/src/editor/template.ts b/packages/vscode-extension/src/editor/template.ts new file mode 100644 index 00000000..5e53e87f --- /dev/null +++ b/packages/vscode-extension/src/editor/template.ts @@ -0,0 +1,17 @@ +const TEMPLATE_VERSION = '0.1'; +const DEFAULT_CANVAS_SIZE = { width: 1920, height: 1080 }; + +export const QUICKMOCK_FILE_EXTENSION = 'qm'; +export const DEFAULT_QUICKMOCK_FILE_NAME = `untitled.${QUICKMOCK_FILE_EXTENSION}`; + +export const createEmptyQuickMockContent = (): string => + JSON.stringify( + { + version: TEMPLATE_VERSION, + pages: [{ id: 'page-1', name: 'Page 1', shapes: [] }], + customColors: [], + size: DEFAULT_CANVAS_SIZE, + }, + null, + 2 + ); From 3cd4892b0f955d5294daaa9b1dc6b50ee4c3bf56 Mon Sep 17 00:00:00 2001 From: Ivanruii Date: Tue, 12 May 2026 09:04:46 +0200 Subject: [PATCH 4/6] chore: added changeset --- .changeset/wise-hornets-post.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wise-hornets-post.md diff --git a/.changeset/wise-hornets-post.md b/.changeset/wise-hornets-post.md new file mode 100644 index 00000000..0870b287 --- /dev/null +++ b/.changeset/wise-hornets-post.md @@ -0,0 +1,5 @@ +--- +'quickmock': minor +--- + +Toolbar's **New** button now creates a real `.qm` file when running inside the VS Code extension instead of just clearing the canvas. From 7781d10200989154ec443cc6a5726e947235fea3 Mon Sep 17 00:00:00 2001 From: Ivanruii Date: Tue, 12 May 2026 09:35:48 +0200 Subject: [PATCH 5/6] feat(vscode-extension): refactor template handling and integrate new wireframe template --- .../src/commands/new-wireframe/new-wireframe.handler.ts | 4 ++-- .../new-wireframe/new-wireframe.template.ts} | 0 packages/vscode-extension/src/editor/index.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) rename packages/vscode-extension/src/{editor/template.ts => commands/new-wireframe/new-wireframe.template.ts} (100%) diff --git a/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.handler.ts b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.handler.ts index 95139c4d..5aed18b0 100644 --- a/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.handler.ts +++ b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.handler.ts @@ -1,11 +1,11 @@ import { getPrimaryWorkspaceFolder } from '#core'; import { writeFile } from '#editor/document'; +import * as vscode from 'vscode'; import { createEmptyQuickMockContent, DEFAULT_QUICKMOCK_FILE_NAME, QUICKMOCK_FILE_EXTENSION, -} from '#editor/template'; -import * as vscode from 'vscode'; +} from './new-wireframe.template'; const CUSTOM_EDITOR_VIEW_TYPE = 'quickmock.editor'; diff --git a/packages/vscode-extension/src/editor/template.ts b/packages/vscode-extension/src/commands/new-wireframe/new-wireframe.template.ts similarity index 100% rename from packages/vscode-extension/src/editor/template.ts rename to packages/vscode-extension/src/commands/new-wireframe/new-wireframe.template.ts diff --git a/packages/vscode-extension/src/editor/index.ts b/packages/vscode-extension/src/editor/index.ts index 816b772f..3951f00f 100644 --- a/packages/vscode-extension/src/editor/index.ts +++ b/packages/vscode-extension/src/editor/index.ts @@ -2,4 +2,3 @@ export * from './document'; export * from './handlers'; export * from './panel'; export * from './provider'; -export * from './template'; From 0be37f118caeeea2a91dec251f2e7ee28d993d84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 07:55:06 +0000 Subject: [PATCH 6/6] chore: version packages --- .changeset/tasty-steaks-know.md | 5 ----- .changeset/wise-hornets-post.md | 5 ----- packages/vscode-extension/CHANGELOG.md | 10 ++++++++++ packages/vscode-extension/package.json | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 .changeset/tasty-steaks-know.md delete mode 100644 .changeset/wise-hornets-post.md diff --git a/.changeset/tasty-steaks-know.md b/.changeset/tasty-steaks-know.md deleted file mode 100644 index 35abdf1f..00000000 --- a/.changeset/tasty-steaks-know.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'quickmock': patch ---- - -Fix editor failing to load files opened from outside the workspace. The webview HTML was assigned before registering `onDidReceiveMessage`, causing a race where the initial `READY` / `WEBVIEW_READY` message from the app could be lost and the file content never delivered. The listener is now registered before the HTML assignment. diff --git a/.changeset/wise-hornets-post.md b/.changeset/wise-hornets-post.md deleted file mode 100644 index 0870b287..00000000 --- a/.changeset/wise-hornets-post.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'quickmock': minor ---- - -Toolbar's **New** button now creates a real `.qm` file when running inside the VS Code extension instead of just clearing the canvas. diff --git a/packages/vscode-extension/CHANGELOG.md b/packages/vscode-extension/CHANGELOG.md index 303f77de..87ff55cd 100644 --- a/packages/vscode-extension/CHANGELOG.md +++ b/packages/vscode-extension/CHANGELOG.md @@ -1,5 +1,15 @@ # @lemoncode/quickmock-vscode-extension +## 0.2.0 + +### Minor Changes + +- 3cd4892: Toolbar's **New** button now creates a real `.qm` file when running inside the VS Code extension instead of just clearing the canvas. + +### Patch Changes + +- b7f9cf1: Fix editor failing to load files opened from outside the workspace. The webview HTML was assigned before registering `onDidReceiveMessage`, causing a race where the initial `READY` / `WEBVIEW_READY` message from the app could be lost and the file content never delivered. The listener is now registered before the HTML assignment. + ## 0.1.0 ### Minor Changes diff --git a/packages/vscode-extension/package.json b/packages/vscode-extension/package.json index e17332ce..8929d7ee 100644 --- a/packages/vscode-extension/package.json +++ b/packages/vscode-extension/package.json @@ -1,6 +1,6 @@ { "name": "quickmock", - "version": "0.1.0", + "version": "0.2.0", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs",