diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index a7132b3..d29ae8f 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -49,6 +49,9 @@ jobs: - run: yarn install --frozen-lockfile --network-timeout 120000 - run: yarn run typecheck - run: yarn run lint + # Windows checkouts get CRLF line endings, which prettier reports as unformatted. + - run: yarn run format:check + if: runner.os == 'Linux' - run: yarn run build - run: yarn run test # Sanity-check `npm pack` only once (Linux + Node 24) to avoid diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..2e8551b --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +.scoria/ +scoria.config.json +dist/ +coverage/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..91a3517 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 160 +} diff --git a/demo/App.vue b/demo/App.vue index e05893e..dfe9989 100644 --- a/demo/App.vue +++ b/demo/App.vue @@ -36,12 +36,7 @@

View Component

- +
Sent Message: diff --git a/demo/markdownArgs.ts b/demo/markdownArgs.ts index 06a89fa..21fb257 100644 --- a/demo/markdownArgs.ts +++ b/demo/markdownArgs.ts @@ -1,7 +1,6 @@ import type { MarkdownArgs } from "../src/core/types"; -const isRecord = (value: unknown): value is Record => - typeof value === "object" && value !== null && !Array.isArray(value); +const isRecord = (value: unknown): value is Record => typeof value === "object" && value !== null && !Array.isArray(value); export const isMarkdownArgs = (value: unknown): value is MarkdownArgs => isRecord(value) && diff --git a/package.json b/package.json index e869d2c..f8829c3 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,9 @@ "preview": "vite preview", "test": "tsx --test tests/*.test.ts", "quality": "scoria", - "test:coverage": "c8 yarn run test" + "test:coverage": "c8 yarn run test", + "format": "prettier --write \"{src,demo,tests}/**/*.{ts,tsx,vue,json,css}\" \"*.{ts,js,json}\"", + "format:check": "prettier --check \"{src,demo,tests}/**/*.{ts,tsx,vue,json,css}\" \"*.{ts,js,json}\"" }, "resolutions": { "minimatch": "^10.2.4" @@ -53,6 +55,7 @@ "eslint-plugin-vue": "^10.11.1", "globals": "^17.13.0", "gui-chat-protocol": "^2.3.0", + "prettier": "^3.9.10", "scoria": "^0.4.1", "tailwindcss": "^4.3.3", "tsx": "^4.23.15", diff --git a/src/core/hostResponse.ts b/src/core/hostResponse.ts index fd7412a..dd45986 100644 --- a/src/core/hostResponse.ts +++ b/src/core/hostResponse.ts @@ -7,29 +7,17 @@ export interface SaveImagesResponse { imageUrls?: Record; } -const isRecord = (value: unknown): value is Record => - typeof value === "object" && value !== null; +const isRecord = (value: unknown): value is Record => typeof value === "object" && value !== null; -const isOptionalString = (value: unknown): value is string | undefined => - value === undefined || typeof value === "string"; +const isOptionalString = (value: unknown): value is string | undefined => value === undefined || typeof value === "string"; -const isStringRecord = (value: unknown): value is Record => - isRecord(value) && - Object.values(value).every((item) => typeof item === "string"); +const isStringRecord = (value: unknown): value is Record => isRecord(value) && Object.values(value).every((item) => typeof item === "string"); -export const isGeneratedImageResponse = ( - value: unknown, -): value is GeneratedImageResponse => - isRecord(value) && - typeof value.success === "boolean" && - isOptionalString(value.imageData); +export const isGeneratedImageResponse = (value: unknown): value is GeneratedImageResponse => + isRecord(value) && typeof value.success === "boolean" && isOptionalString(value.imageData); -export const isSaveImagesResponse = ( - value: unknown, -): value is SaveImagesResponse => - isRecord(value) && - (value.imageUrls === undefined || isStringRecord(value.imageUrls)); +export const isSaveImagesResponse = (value: unknown): value is SaveImagesResponse => + isRecord(value) && (value.imageUrls === undefined || isStringRecord(value.imageUrls)); /** `loadBlankImageBase64` returns the reference image; anything else is "no reference". */ -export const readBlankImageBase64 = (value: unknown): string => - typeof value === "string" ? value : ""; +export const readBlankImageBase64 = (value: unknown): string => (typeof value === "string" ? value : ""); diff --git a/src/core/plugin.ts b/src/core/plugin.ts index 5cc87fa..d530cb4 100644 --- a/src/core/plugin.ts +++ b/src/core/plugin.ts @@ -1,19 +1,12 @@ import type { ToolContext, ToolPluginCore } from "gui-chat-protocol"; import type { MarkdownArgs, MarkdownToolData, MarkdownResult } from "./types"; -import { - isGeneratedImageResponse, - isSaveImagesResponse, - readBlankImageBase64, -} from "./hostResponse"; +import { isGeneratedImageResponse, isSaveImagesResponse, readBlankImageBase64 } from "./hostResponse"; import { TOOL_NAME, TOOL_DEFINITION } from "./definition"; // context is nullable on purpose: hosts that run the plugin without client-side // state (MulmoClaude's server bridge) pass an empty or missing context, and // reading through it unguarded threw a TypeError instead of returning a result. -export const presentDocument = async ( - context: ToolContext | null | undefined, - args: MarkdownArgs, -): Promise => { +export const presentDocument = async (context: ToolContext | null | undefined, args: MarkdownArgs): Promise => { let { markdown } = args; const { title, filenameHint } = args; const app = context?.app; @@ -34,9 +27,7 @@ export const presentDocument = async ( const images: Record = {}; // Load blank image for aspect ratio reference - const blankImageBase64 = app.loadBlankImageBase64 - ? readBlankImageBase64(await app.loadBlankImageBase64()) - : ""; + const blankImageBase64 = app.loadBlankImageBase64 ? readBlankImageBase64(await app.loadBlankImageBase64()) : ""; // Generate images for each placeholder in parallel const imagePromises = matches.map(async (match, i) => { @@ -44,11 +35,7 @@ export const presentDocument = async ( const imageId = `image_${i}`; try { - const result = await app!.generateImageWithBackend!( - prompt, - blankImageBase64 ? [blankImageBase64] : [], - context, - ); + const result = await app!.generateImageWithBackend!(prompt, blankImageBase64 ? [blankImageBase64] : [], context); if (isGeneratedImageResponse(result) && result.success && result.imageData) { images[imageId] = `data:image/png;base64,${result.imageData}`; @@ -87,8 +74,7 @@ export const presentDocument = async ( title, data: { markdown, filenameHint }, uuid: docUuid, - instructions: - "Acknowledge that the document has been created and is displayed to the user.", + instructions: "Acknowledge that the document has been created and is displayed to the user.", }; } @@ -96,8 +82,7 @@ export const presentDocument = async ( message: `Created markdown document: ${title}`, title, data: { markdown, filenameHint }, - instructions: - "Acknowledge that the document has been created and is displayed to the user.", + instructions: "Acknowledge that the document has been created and is displayed to the user.", }; }; @@ -105,8 +90,7 @@ export const pluginCore: ToolPluginCore toolDefinition: TOOL_DEFINITION, execute: presentDocument, generatingMessage: "Creating document...", - waitingMessage: - "Tell the user that the document was created, will be presented to the user shortly.", + waitingMessage: "Tell the user that the document was created, will be presented to the user shortly.", isEnabled: () => true, backends: ["imageGen"], }; diff --git a/src/vue/View.vue b/src/vue/View.vue index b50a8fa..ec0d50e 100644 --- a/src/vue/View.vue +++ b/src/vue/View.vue @@ -1,9 +1,6 @@