diff --git a/.prettierrc.json b/.prettierrc.json index 9134ee4..09009a6 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,7 +1,7 @@ { "tabWidth": 2, "trailingComma": "es5", - "printWidth": 200, + "printWidth": 160, "semi": true, "singleQuote": false } diff --git a/src/main/env.d.ts b/src/main/env.d.ts index 1f9911f..e87792d 100644 --- a/src/main/env.d.ts +++ b/src/main/env.d.ts @@ -1,10 +1,10 @@ /// interface ImportMetaEnv { - readonly MAIN_VITE_API_URL: string - readonly MAIN_VITE_DEBUG: string + readonly MAIN_VITE_API_URL: string; + readonly MAIN_VITE_DEBUG: string; } interface ImportMeta { - readonly env: ImportMetaEnv -} \ No newline at end of file + readonly env: ImportMetaEnv; +} diff --git a/src/main/errors.ts b/src/main/errors.ts index 34475ef..989fbc2 100644 --- a/src/main/errors.ts +++ b/src/main/errors.ts @@ -27,8 +27,7 @@ export type MainErrorKey = const errorMessages: Record> = { "zh-CN": { domainNotFound: "域名未找到", - domainDuplicate: - '域名 "{{domain}}" 与用户 "{{username}}" 已存在。请编辑现有域名。', + domainDuplicate: '域名 "{{domain}}" 与用户 "{{username}}" 已存在。请编辑现有域名。', connectionFailed: "连接失败", unknownError: "未知错误", rollbackNotImplemented: "回滚功能尚未实现", @@ -40,8 +39,7 @@ const errorMessages: Record> = { }, "en-US": { domainNotFound: "Domain not found", - domainDuplicate: - 'Domain "{{domain}}" with user "{{username}}" already exists. Please edit the existing domain instead.', + domainDuplicate: 'Domain "{{domain}}" with user "{{username}}" already exists. Please edit the existing domain instead.', connectionFailed: "Connection failed", unknownError: "Unknown error", rollbackNotImplemented: "Rollback not yet implemented", @@ -53,8 +51,7 @@ const errorMessages: Record> = { }, "ja-JP": { domainNotFound: "ドメインが見つかりません", - domainDuplicate: - 'ドメイン "{{domain}}" とユーザー "{{username}}" は既に存在します。既存のドメインを編集してください。', + domainDuplicate: 'ドメイン "{{domain}}" とユーザー "{{username}}" は既に存在します。既存のドメインを編集してください。', connectionFailed: "接続に失敗しました", unknownError: "不明なエラー", rollbackNotImplemented: "ロールバック機能はまだ実装されていません", @@ -101,11 +98,7 @@ function interpolate(message: string, params?: ErrorParams): string { * @param params - Optional interpolation parameters * @param locale - Optional locale override (defaults to stored preference) */ -export function getErrorMessage( - key: MainErrorKey, - params?: ErrorParams, - locale?: LocaleCode, -): string { +export function getErrorMessage(key: MainErrorKey, params?: ErrorParams, locale?: LocaleCode): string { const targetLocale = locale ?? getLocale(); const messages = errorMessages[targetLocale] || errorMessages["zh-CN"]; const message = messages[key] || errorMessages["zh-CN"][key] || key; diff --git a/src/main/index.ts b/src/main/index.ts index 8cd6538..d8a5070 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -2,11 +2,7 @@ import { app, shell, BrowserWindow, ipcMain } from "electron"; import { join } from "path"; import { electronApp, optimizer, is } from "@electron-toolkit/utils"; import { registerIpcHandlers } from "./ipc-handlers"; -import { - initializeStorage, - isSecureStorageAvailable, - getStorageBackend, -} from "./storage"; +import { initializeStorage, isSecureStorageAvailable, getStorageBackend } from "./storage"; function createWindow(): void { // Create the browser window. @@ -71,9 +67,7 @@ app.whenReady().then(() => { // Check secure storage availability if (!isSecureStorageAvailable()) { - console.warn( - `Warning: Secure storage not available (backend: ${getStorageBackend()})`, - ); + console.warn(`Warning: Secure storage not available (backend: ${getStorageBackend()})`); console.warn("Passwords will not be securely encrypted on this system."); } diff --git a/src/main/ipc-handlers.ts b/src/main/ipc-handlers.ts index 4a10898..a15ede3 100644 --- a/src/main/ipc-handlers.ts +++ b/src/main/ipc-handlers.ts @@ -46,24 +46,9 @@ import type { FileDeleteParams, } from "@shared/types/ipc"; import type { LocaleCode } from "@shared/types/locale"; -import type { - Domain, - DomainWithStatus, - DomainWithPassword, -} from "@shared/types/domain"; -import type { - Version, - DownloadMetadata, - BackupMetadata, - DownloadFile, -} from "@shared/types/version"; -import { - FileConfigResponse, - isFileResource, - isUrlResource, - type AppCustomizeParameter, - type AppDetail, -} from "@shared/types/kintone"; +import type { Domain, DomainWithStatus, DomainWithPassword } from "@shared/types/domain"; +import type { Version, DownloadMetadata, BackupMetadata, DownloadFile } from "@shared/types/version"; +import { FileConfigResponse, isFileResource, isUrlResource, type AppCustomizeParameter, type AppDetail } from "@shared/types/kintone"; import { getDisplayName, getFileKey } from "@shared/utils/fileDisplay"; import { getErrorMessage } from "./errors"; @@ -91,10 +76,7 @@ async function getClient(domainId: string): Promise { /** * Helper to wrap IPC handlers with error handling */ -function handle

( - channel: string, - handler: (params: P) => Promise, -): void { +function handle

(channel: string, handler: (params: P) => Promise): void { ipcMain.handle(channel, async (_event, params: P): Promise> => { try { // For handlers without params (P=void), params will be undefined but handler ignores it @@ -102,10 +84,7 @@ function handle

( const data = await handler(params as P); return { success: true, data }; } catch (error) { - const message = - error instanceof Error - ? error.message - : getErrorMessage("unknownError"); + const message = error instanceof Error ? error.message : getErrorMessage("unknownError"); return { success: false, error: message }; } }); @@ -115,11 +94,7 @@ function handle

( * Wait for Kintone deployment to complete * Polls getDeployStatus until SUCCESS, FAIL, CANCEL, or timeout */ -async function waitForDeploySuccess( - client: KintoneClient, - appId: string, - options: { timeoutMs?: number; pollIntervalMs?: number } = {}, -): Promise { +async function waitForDeploySuccess(client: KintoneClient, appId: string, options: { timeoutMs?: number; pollIntervalMs?: number } = {}): Promise { const { timeoutMs = 60000, pollIntervalMs = 1000 } = options; const startTime = Date.now(); @@ -165,9 +140,7 @@ function registerCreateDomain(): void { // Check for duplicate domain+username const existingDomains = await listDomains(); const duplicate = existingDomains.find( - (d) => - d.domain.toLowerCase() === params.domain.toLowerCase() && - d.username.toLowerCase() === params.username.toLowerCase(), + (d) => d.domain.toLowerCase() === params.domain.toLowerCase() && d.username.toLowerCase() === params.username.toLowerCase() ); if (duplicate) { @@ -175,7 +148,7 @@ function registerCreateDomain(): void { getErrorMessage("domainDuplicate", { domain: params.domain, username: params.username, - }), + }) ); } @@ -267,29 +240,26 @@ function registerTestConnection(): void { * Test domain connection with temporary credentials */ function registerTestDomainConnection(): void { - handle( - "testDomainConnection", - async (params) => { - const tempDomain: DomainWithPassword = { - id: "temp", - name: "temp", - domain: params.domain, - username: params.username, - password: params.password || "", - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }; + handle("testDomainConnection", async (params) => { + const tempDomain: DomainWithPassword = { + id: "temp", + name: "temp", + domain: params.domain, + username: params.username, + password: params.password || "", + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; - const client = createKintoneClient(tempDomain); - const result = await client.testConnection(); + const client = createKintoneClient(tempDomain); + const result = await client.testConnection(); - if (!result.success) { - throw new Error(result.error || getErrorMessage("connectionFailed")); - } + if (!result.success) { + throw new Error(result.error || getErrorMessage("connectionFailed")); + } - return true; - }, - ); + return true; + }); } // ==================== Browse IPC Handlers ==================== @@ -298,26 +268,20 @@ function registerTestDomainConnection(): void { * Get apps */ function registerGetApps(): void { - handle>>( - "getApps", - async (params) => { - const client = await getClient(params.domainId); - return client.getApps({ - limit: params.limit, - offset: params.offset, - }); - }, - ); + handle>>("getApps", async (params) => { + const client = await getClient(params.domainId); + return client.getApps({ + limit: params.limit, + offset: params.offset, + }); + }); } /** * Get app detail */ function registerGetAppDetail(): void { - handle< - GetAppDetailParams, - Awaited> - >("getAppDetail", async (params) => { + handle>>("getAppDetail", async (params) => { const client = await getClient(params.domainId); return client.getAppDetail(params.appId); }); @@ -327,10 +291,7 @@ function registerGetAppDetail(): void { * Get file content */ function registerGetFileContent(): void { - handle< - GetFileContentParams, - Awaited> - >("getFileContent", async (params) => { + handle>>("getFileContent", async (params) => { const client = await getClient(params.domainId); return client.getFileContent(params.fileKey); }); @@ -347,7 +308,7 @@ async function addFilesToBackup( client: KintoneClient, appDetail: AppDetail, backupFiles: Map, - backupFileList: DownloadFile[], + backupFileList: DownloadFile[] ): Promise { const files = appDetail.customization?.[platform]?.[fileType] || []; @@ -396,38 +357,10 @@ function registerDeploy(): void { const backupFiles = new Map(); const backupFileList: BackupMetadata["files"] = []; - await addFilesToBackup( - "desktop", - "js", - client, - appDetail, - backupFiles, - backupFileList, - ); - await addFilesToBackup( - "desktop", - "css", - client, - appDetail, - backupFiles, - backupFileList, - ); - await addFilesToBackup( - "mobile", - "js", - client, - appDetail, - backupFiles, - backupFileList, - ); - await addFilesToBackup( - "mobile", - "css", - client, - appDetail, - backupFiles, - backupFileList, - ); + await addFilesToBackup("desktop", "js", client, appDetail, backupFiles, backupFileList); + await addFilesToBackup("desktop", "css", client, appDetail, backupFiles, backupFileList); + await addFilesToBackup("mobile", "js", client, appDetail, backupFiles, backupFileList); + await addFilesToBackup("mobile", "css", client, appDetail, backupFiles, backupFileList); const backupMetadata: BackupMetadata = { backedUpAt: new Date().toISOString(), @@ -452,9 +385,7 @@ function registerDeploy(): void { for (const file of params.files) { if (file.status === "deleted") continue; - type FileEntry = - | { type: "FILE"; file: { fileKey: string } } - | { type: "URL"; url: string }; + type FileEntry = { type: "FILE"; file: { fileKey: string } } | { type: "URL"; url: string }; let entry: FileEntry; if (file.status === "unchanged") { @@ -483,9 +414,7 @@ function registerDeploy(): void { // Step 1: Try exact fileKey match (most reliable) if (file.fileKey) { - matchingFile = currentFiles?.find( - (f) => isFileResource(f) && f.file.fileKey === file.fileKey, - ); + matchingFile = currentFiles?.find((f) => isFileResource(f) && f.file.fileKey === file.fileKey); if (matchingFile) { matchMethod = "fileKey exact match"; console.log(`[DEPLOY DEBUG] Matched by fileKey: ${file.fileKey}`); @@ -494,9 +423,7 @@ function registerDeploy(): void { // Step 2: Try URL match for URL-type files if (!matchingFile && file.url) { - matchingFile = currentFiles?.find( - (f) => isUrlResource(f) && f.url === file.url, - ); + matchingFile = currentFiles?.find((f) => isUrlResource(f) && f.url === file.url); if (matchingFile) { matchMethod = "URL exact match"; console.log(`[DEPLOY DEBUG] Matched by URL: ${file.url}`); @@ -513,9 +440,7 @@ function registerDeploy(): void { }); if (matchingFile) { matchMethod = "filename match (fallback)"; - console.log( - `[DEPLOY DEBUG] Matched by filename (fallback): ${file.fileName}`, - ); + console.log(`[DEPLOY DEBUG] Matched by filename (fallback): ${file.fileName}`); } } @@ -523,7 +448,7 @@ function registerDeploy(): void { `[DEPLOY DEBUG] Final matching result for "${file.fileName}":`, matchingFile ? `${matchMethod} → ${isFileResource(matchingFile) ? `FILE key="${matchingFile.file.fileKey}"` : `URL "${matchingFile.url}"`}` - : "NOT FOUND", + : "NOT FOUND" ); if (matchingFile) { @@ -531,15 +456,13 @@ function registerDeploy(): void { // Validate that the matched file has a valid fileKey if (!matchingFile.file.fileKey) { throw new Error( - `Matched file "${file.fileName}" has no fileKey in Kintone config. ` + - `This indicates corrupted data. Please refresh and try again.`, + `Matched file "${file.fileName}" has no fileKey in Kintone config. ` + `This indicates corrupted data. Please refresh and try again.` ); } // Verify filename matches (sanity check) if (matchingFile.file.name !== file.fileName) { console.warn( - `[DEPLOY WARNING] Filename mismatch: expected "${file.fileName}", found "${matchingFile.file.name}". ` + - `Proceeding with matched fileKey.`, + `[DEPLOY WARNING] Filename mismatch: expected "${file.fileName}", found "${matchingFile.file.name}". ` + `Proceeding with matched fileKey.` ); } entry = { @@ -549,16 +472,13 @@ function registerDeploy(): void { } else if (isUrlResource(matchingFile)) { entry = { type: "URL", url: matchingFile.url }; } else { - throw new Error( - `Invalid file type in Kintone config for "${file.fileName}"`, - ); + throw new Error(`Invalid file type in Kintone config for "${file.fileName}"`); } } else { // File not found in current Kintone config - this is an error // The file may have been deleted from Kintone externally throw new Error( - `File "${file.fileName}" not found in current Kintone configuration. ` + - `It may have been deleted externally. Please refresh and try again.`, + `File "${file.fileName}" not found in current Kintone configuration. ` + `It may have been deleted externally. Please refresh and try again.` ); } } else { @@ -633,12 +553,7 @@ function registerDownload(): void { const downloadFileList: DownloadMetadata["files"] = []; // Download based on requested file types - const fileTypes = params.fileTypes || [ - "pc_js", - "pc_css", - "mobile_js", - "mobile_css", - ]; + const fileTypes = params.fileTypes || ["pc_js", "pc_css", "mobile_js", "mobile_css"]; for (const fileType of fileTypes) { const files = @@ -699,88 +614,82 @@ function registerDownload(): void { * Download all files as ZIP */ function registerDownloadAllZip(): void { - handle( - "downloadAllZip", - async (params) => { - const client = await getClient(params.domainId); - const domainWithPassword = await getDomain(params.domainId); + handle("downloadAllZip", async (params) => { + const client = await getClient(params.domainId); + const domainWithPassword = await getDomain(params.domainId); - if (!domainWithPassword) { - throw new Error(getErrorMessage("domainNotFound")); + if (!domainWithPassword) { + throw new Error(getErrorMessage("domainNotFound")); + } + + const appDetail = await client.getAppDetail(params.appId); + const zip = new AdmZip(); + const metadata = { + downloadedAt: new Date().toISOString(), + domain: domainWithPassword.domain, + appId: params.appId, + appName: appDetail.name, + }; + + // Download and add PC files + const pcJsFiles = appDetail.customization?.desktop?.js || []; + const pcCssFiles = appDetail.customization?.desktop?.css || []; + + for (const [index, file] of pcJsFiles.entries()) { + const fileKey = getFileKey(file); + if (fileKey) { + const content = await client.getFileContent(fileKey); + const buffer = Buffer.from(content.content || "", "base64"); + const fileName = getDisplayName(file, "js", index); + zip.addFile(`desktop-js/${fileName}`, buffer); } + } - const appDetail = await client.getAppDetail(params.appId); - const zip = new AdmZip(); - const metadata = { - downloadedAt: new Date().toISOString(), - domain: domainWithPassword.domain, - appId: params.appId, - appName: appDetail.name, - }; - - // Download and add PC files - const pcJsFiles = appDetail.customization?.desktop?.js || []; - const pcCssFiles = appDetail.customization?.desktop?.css || []; - - for (const [index, file] of pcJsFiles.entries()) { - const fileKey = getFileKey(file); - if (fileKey) { - const content = await client.getFileContent(fileKey); - const buffer = Buffer.from(content.content || "", "base64"); - const fileName = getDisplayName(file, "js", index); - zip.addFile(`desktop-js/${fileName}`, buffer); - } + for (const [index, file] of pcCssFiles.entries()) { + const fileKey = getFileKey(file); + if (fileKey) { + const content = await client.getFileContent(fileKey); + const buffer = Buffer.from(content.content || "", "base64"); + const fileName = getDisplayName(file, "css", index); + zip.addFile(`desktop-css/${fileName}`, buffer); } + } - for (const [index, file] of pcCssFiles.entries()) { - const fileKey = getFileKey(file); - if (fileKey) { - const content = await client.getFileContent(fileKey); - const buffer = Buffer.from(content.content || "", "base64"); - const fileName = getDisplayName(file, "css", index); - zip.addFile(`desktop-css/${fileName}`, buffer); - } + // Download and add Mobile files + const mobileJsFiles = appDetail.customization?.mobile?.js || []; + const mobileCssFiles = appDetail.customization?.mobile?.css || []; + + for (const [index, file] of mobileJsFiles.entries()) { + const fileKey = getFileKey(file); + if (fileKey) { + const content = await client.getFileContent(fileKey); + const buffer = Buffer.from(content.content || "", "base64"); + const fileName = getDisplayName(file, "js", index); + zip.addFile(`mobile-js/${fileName}`, buffer); } + } - // Download and add Mobile files - const mobileJsFiles = appDetail.customization?.mobile?.js || []; - const mobileCssFiles = appDetail.customization?.mobile?.css || []; - - for (const [index, file] of mobileJsFiles.entries()) { - const fileKey = getFileKey(file); - if (fileKey) { - const content = await client.getFileContent(fileKey); - const buffer = Buffer.from(content.content || "", "base64"); - const fileName = getDisplayName(file, "js", index); - zip.addFile(`mobile-js/${fileName}`, buffer); - } + for (const [index, file] of mobileCssFiles.entries()) { + const fileKey = getFileKey(file); + if (fileKey) { + const content = await client.getFileContent(fileKey); + const buffer = Buffer.from(content.content || "", "base64"); + const fileName = getDisplayName(file, "css", index); + zip.addFile(`mobile-css/${fileName}`, buffer); } + } - for (const [index, file] of mobileCssFiles.entries()) { - const fileKey = getFileKey(file); - if (fileKey) { - const content = await client.getFileContent(fileKey); - const buffer = Buffer.from(content.content || "", "base64"); - const fileName = getDisplayName(file, "css", index); - zip.addFile(`mobile-css/${fileName}`, buffer); - } - } + // Add metadata.json + zip.addFile("metadata.json", Buffer.from(JSON.stringify(metadata, null, 2), "utf-8")); - // Add metadata.json - zip.addFile( - "metadata.json", - Buffer.from(JSON.stringify(metadata, null, 2), "utf-8"), - ); + // Write ZIP to user-provided path + zip.writeZip(params.savePath); - // Write ZIP to user-provided path - zip.writeZip(params.savePath); - - return { - success: true, - path: params.savePath, - }; - }, - ); + return { + success: true, + path: params.savePath, + }; + }); } // ==================== Version IPC Handlers ==================== @@ -953,32 +862,26 @@ export function registerIpcHandlers(): void { * Show save dialog */ function registerShowSaveDialog(): void { - handle<{ defaultPath?: string }, string | null>( - "showSaveDialog", - async (params) => { - const result = await dialog.showSaveDialog({ - defaultPath: params.defaultPath, - filters: [ - { name: "JavaScript", extensions: ["js"] }, - { name: "CSS", extensions: ["css"] }, - { name: "All Files", extensions: ["*"] }, - ], - }); - return result.filePath || null; - }, - ); + handle<{ defaultPath?: string }, string | null>("showSaveDialog", async (params) => { + const result = await dialog.showSaveDialog({ + defaultPath: params.defaultPath, + filters: [ + { name: "JavaScript", extensions: ["js"] }, + { name: "CSS", extensions: ["css"] }, + { name: "All Files", extensions: ["*"] }, + ], + }); + return result.filePath || null; + }); } /** * Save file content to disk */ function registerSaveFileContent(): void { - handle<{ filePath: string; content: string }, void>( - "saveFileContent", - async (params) => { - const fs = await import("fs"); - const buffer = Buffer.from(params.content, "base64"); - await fs.promises.writeFile(params.filePath, buffer); - }, - ); + handle<{ filePath: string; content: string }, void>("saveFileContent", async (params) => { + const fs = await import("fs"); + const buffer = Buffer.from(params.content, "base64"); + await fs.promises.writeFile(params.filePath, buffer); + }); } diff --git a/src/main/kintone-api.ts b/src/main/kintone-api.ts index b081b78..1d2ce71 100644 --- a/src/main/kintone-api.ts +++ b/src/main/kintone-api.ts @@ -1,13 +1,7 @@ import { KintoneRestAPIClient } from "@kintone/rest-api-client"; import type { KintoneRestAPIError } from "@kintone/rest-api-client"; import type { DomainWithPassword } from "@shared/types/domain"; -import { - type AppResponse, - type AppDetail, - type FileContent, - type KintoneApiError, - AppCustomizeParameter, -} from "@shared/types/kintone"; +import { type AppResponse, type AppDetail, type FileContent, type KintoneApiError, AppCustomizeParameter } from "@shared/types/kintone"; import { getErrorMessage } from "./errors"; /** @@ -18,11 +12,7 @@ export class KintoneError extends Error { public readonly id?: string; public readonly statusCode?: number; - constructor( - message: string, - apiError?: KintoneApiError, - statusCode?: number, - ) { + constructor(message: string, apiError?: KintoneApiError, statusCode?: number) { super(message); this.name = "KintoneError"; this.code = apiError?.code; @@ -53,11 +43,7 @@ export class KintoneClient { private convertError(error: unknown): KintoneError { if (error && typeof error === "object" && "code" in error) { const apiError = error as KintoneRestAPIError; - return new KintoneError( - apiError.message, - { code: apiError.code, message: apiError.message, id: apiError.id }, - apiError.status, - ); + return new KintoneError(apiError.message, { code: apiError.code, message: apiError.message, id: apiError.id }, apiError.status); } if (error instanceof Error) { @@ -81,10 +67,7 @@ export class KintoneClient { * Get all apps with pagination support * Fetches all apps by making multiple requests if needed */ - async getApps(options?: { - limit?: number; - offset?: number; - }): Promise { + async getApps(options?: { limit?: number; offset?: number }): Promise { return this.withErrorHandling(async () => { // If pagination options provided, use them directly if (options?.limit !== undefined || options?.offset !== undefined) { @@ -149,11 +132,7 @@ export class KintoneClient { }); } - async uploadFile( - content: string | Buffer, - fileName: string, - _mimeType?: string, - ): Promise<{ fileKey: string }> { + async uploadFile(content: string | Buffer, fileName: string, _mimeType?: string): Promise<{ fileKey: string }> { return this.withErrorHandling(async () => { const response = await this.client.file.uploadFile({ file: { name: fileName, data: content }, @@ -164,10 +143,7 @@ export class KintoneClient { // ==================== Deploy APIs ==================== - async updateAppCustomize( - appId: string, - config: Omit, - ): Promise { + async updateAppCustomize(appId: string, config: Omit): Promise { return this.withErrorHandling(async () => { await this.client.app.updateAppCustomize({ ...config, app: appId }); }); @@ -179,9 +155,7 @@ export class KintoneClient { }); } - async getDeployStatus( - appId: string, - ): Promise<"PROCESSING" | "SUCCESS" | "FAIL" | "CANCEL"> { + async getDeployStatus(appId: string): Promise<"PROCESSING" | "SUCCESS" | "FAIL" | "CANCEL"> { return this.withErrorHandling(async () => { const response = await this.client.app.getDeployStatus({ apps: [appId] }); return response.apps[0]?.status || "FAIL"; @@ -198,10 +172,7 @@ export class KintoneClient { } catch (error) { return { success: false, - error: - error instanceof KintoneError - ? error.message - : getErrorMessage("connectionFailed"), + error: error instanceof KintoneError ? error.message : getErrorMessage("connectionFailed"), }; } } diff --git a/src/main/storage.ts b/src/main/storage.ts index 68085cb..7553509 100644 --- a/src/main/storage.ts +++ b/src/main/storage.ts @@ -8,11 +8,7 @@ import { app, safeStorage } from "electron"; import * as fs from "fs"; import * as path from "path"; import type { Domain, DomainWithPassword } from "@shared/types/domain"; -import type { - Version, - DownloadMetadata, - BackupMetadata, -} from "@shared/types/version"; +import type { Version, DownloadMetadata, BackupMetadata } from "@shared/types/version"; import type { LocaleCode } from "@shared/types/locale"; import { DEFAULT_LOCALE } from "@shared/types/locale"; @@ -107,14 +103,14 @@ function writeJsonFile(filePath: string, data: T): void { export function isSecureStorageAvailable(): boolean { try { // Check if the method exists (added in Electron 30+) - if (typeof safeStorage.getSelectedStorageBackend === 'function') { - const backend = safeStorage.getSelectedStorageBackend() - return backend !== 'basic_text' + if (typeof safeStorage.getSelectedStorageBackend === "function") { + const backend = safeStorage.getSelectedStorageBackend(); + return backend !== "basic_text"; } // Fallback: check if encryption is available - return safeStorage.isEncryptionAvailable() + return safeStorage.isEncryptionAvailable(); } catch { - return false + return false; } } @@ -123,12 +119,12 @@ export function isSecureStorageAvailable(): boolean { */ export function getStorageBackend(): string { try { - if (typeof safeStorage.getSelectedStorageBackend === 'function') { - return safeStorage.getSelectedStorageBackend() + if (typeof safeStorage.getSelectedStorageBackend === "function") { + return safeStorage.getSelectedStorageBackend(); } - return 'unknown' + return "unknown"; } catch { - return 'unknown' + return "unknown"; } } @@ -139,9 +135,7 @@ export function encryptPassword(password: string): Buffer { try { return safeStorage.encryptString(password); } catch (error) { - throw new Error( - `Failed to encrypt password: ${error instanceof Error ? error.message : "Unknown error"}`, - ); + throw new Error(`Failed to encrypt password: ${error instanceof Error ? error.message : "Unknown error"}`); } } @@ -152,9 +146,7 @@ export function decryptPassword(encrypted: Buffer): string { try { return safeStorage.decryptString(encrypted); } catch (error) { - throw new Error( - `Failed to decrypt password: ${error instanceof Error ? error.message : "Unknown error"}`, - ); + throw new Error(`Failed to decrypt password: ${error instanceof Error ? error.message : "Unknown error"}`); } } @@ -163,10 +155,7 @@ export function decryptPassword(encrypted: Buffer): string { /** * Save a domain with encrypted password */ -export async function saveDomain( - domain: Domain, - password: string, -): Promise { +export async function saveDomain(domain: Domain, password: string): Promise { const configPath = getConfigPath(); const config = readJsonFile(configPath, { domains: [] }); const existingIndex = config.domains.findIndex((d) => d.id === domain.id); @@ -190,9 +179,7 @@ export async function saveDomain( /** * Get a domain by ID with decrypted password */ -export async function getDomain( - id: string, -): Promise { +export async function getDomain(id: string): Promise { const configPath = getConfigPath(); const config = readJsonFile(configPath, { domains: [] }); const domain = config.domains.find((d) => d.id === id); @@ -253,13 +240,7 @@ export async function deleteDomain(id: string): Promise { * Save a version to local storage */ export async function saveVersion(version: Version): Promise { - const versionDir = getStoragePath( - "versions", - version.domainId, - version.appId, - version.fileType, - version.id, - ); + const versionDir = getStoragePath("versions", version.domainId, version.appId, version.fileType, version.id); ensureDir(versionDir); @@ -271,10 +252,7 @@ export async function saveVersion(version: Version): Promise { /** * List versions for a specific app */ -export async function listVersions( - domainId: string, - appId: string, -): Promise { +export async function listVersions(domainId: string, appId: string): Promise { const versions: Version[] = []; const baseDir = getStoragePath("versions", domainId, appId); @@ -304,9 +282,7 @@ export async function listVersions( } // Sort by createdAt descending - return versions.sort( - (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), - ); + return versions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); } /** @@ -349,17 +325,9 @@ export async function deleteVersion(id: string): Promise { /** * Save downloaded files with metadata */ -export async function saveDownload( - metadata: DownloadMetadata, - files: Map, -): Promise { +export async function saveDownload(metadata: DownloadMetadata, files: Map): Promise { const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); - const downloadDir = getStoragePath( - "downloads", - metadata.domainId, - metadata.appId, - timestamp, - ); + const downloadDir = getStoragePath("downloads", metadata.domainId, metadata.appId, timestamp); ensureDir(downloadDir); @@ -392,18 +360,9 @@ export function getDownloadPath(domainId: string, appId?: string): string { /** * Save backup files with metadata */ -export async function saveBackup( - metadata: BackupMetadata, - files: Map, -): Promise { +export async function saveBackup(metadata: BackupMetadata, files: Map): Promise { const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); - const backupDir = getStoragePath( - "versions", - metadata.domainId, - metadata.appId, - "backup", - timestamp, - ); + const backupDir = getStoragePath("versions", metadata.domainId, metadata.appId, "backup", timestamp); ensureDir(backupDir); @@ -437,12 +396,7 @@ export async function saveCustomizationFile(params: { }): Promise<{ storagePath: string; fileName: string; size: number }> { const { domainId, appId, platform, fileType, fileId, sourcePath } = params; const fileName = path.basename(sourcePath); - const dir = getStoragePath( - "files", - domainId, - appId, - `${platform}_${fileType}`, - ); + const dir = getStoragePath("files", domainId, appId, `${platform}_${fileType}`); ensureDir(dir); const storagePath = path.join(dir, `${fileId}_${fileName}`); fs.copyFileSync(sourcePath, storagePath); @@ -453,9 +407,7 @@ export async function saveCustomizationFile(params: { /** * Delete a customization file from storage. */ -export async function deleteCustomizationFile( - storagePath: string, -): Promise { +export async function deleteCustomizationFile(storagePath: string): Promise { if (fs.existsSync(storagePath)) { fs.unlinkSync(storagePath); } diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index b499471..6cce900 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -23,12 +23,7 @@ import type { FileDeleteParams, } from "@shared/types/ipc"; import type { Domain, DomainWithStatus } from "@shared/types/domain"; -import type { - AppResponse, - AppDetail, - FileContent, - KintoneSpace, -} from "@shared/types/kintone"; +import type { AppResponse, AppDetail, FileContent, KintoneSpace } from "@shared/types/kintone"; import type { Version } from "@shared/types/version"; import type { LocaleCode } from "@shared/types/locale"; @@ -49,16 +44,12 @@ export interface SelfAPI { updateDomain: (params: UpdateDomainParams) => Promise>; deleteDomain: (id: string) => Promise>; testConnection: (id: string) => Promise>; - testDomainConnection: ( - params: TestDomainConnectionParams, - ) => Promise>; + testDomainConnection: (params: TestDomainConnectionParams) => Promise>; // ==================== Browse ==================== getApps: (params: GetAppsParams) => Promise>; getAppDetail: (params: GetAppDetailParams) => Promise>; - getFileContent: ( - params: GetFileContentParams, - ) => Promise>; + getFileContent: (params: GetFileContentParams) => Promise>; // ==================== Deploy ==================== deploy: (params: DeployParams) => Promise>; diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index eee6f24..6176633 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -9,13 +9,7 @@ import { Layout, Typography, Space, Modal } from "antd"; import { Button, Tooltip } from "@lobehub/ui"; -import { - Cloud, - History, - PanelLeftClose, - PanelLeftOpen, - Settings as SettingsIcon, -} from "lucide-react"; +import { Cloud, History, PanelLeftClose, PanelLeftOpen, Settings as SettingsIcon } from "lucide-react"; import { createStyles, useTheme } from "antd-style"; import { useDomainStore } from "@renderer/stores"; @@ -158,20 +152,11 @@ const App: React.FC = () => { }, []); const { currentDomain } = useDomainStore(); - const { - sidebarWidth, - siderCollapsed, - domainExpanded, - setSidebarWidth, - setSiderCollapsed, - setDomainExpanded, - } = useUIStore(); + const { sidebarWidth, siderCollapsed, domainExpanded, setSidebarWidth, setSiderCollapsed, setDomainExpanded } = useUIStore(); const [settingsOpen, setSettingsOpen] = React.useState(false); const [isResizing, setIsResizing] = React.useState(false); - const domainSectionHeight = domainExpanded - ? DOMAIN_SECTION_EXPANDED - : DOMAIN_SECTION_COLLAPSED; + const domainSectionHeight = domainExpanded ? DOMAIN_SECTION_EXPANDED : DOMAIN_SECTION_COLLAPSED; // Handle resize start const handleResizeStart = React.useCallback( @@ -184,10 +169,7 @@ const App: React.FC = () => { const handleMouseMove = (moveEvent: MouseEvent) => { const delta = moveEvent.clientX - startX; - const newWidth = Math.min( - MAX_SIDER_WIDTH, - Math.max(MIN_SIDER_WIDTH, startWidth + delta), - ); + const newWidth = Math.min(MAX_SIDER_WIDTH, Math.max(MIN_SIDER_WIDTH, startWidth + delta)); setSidebarWidth(newWidth); }; @@ -200,7 +182,7 @@ const App: React.FC = () => { document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); }, - [sidebarWidth, setSidebarWidth], + [sidebarWidth, setSidebarWidth] ); const toggleSider = () => { @@ -223,29 +205,14 @@ const App: React.FC = () => { Kintone JS/CSS Manager - {updateInfo && ( -

+
{updateInfo.hasUpdate ? ( {t("updateAvailable")}: v{updateInfo.version} - diff --git a/src/renderer/src/components/VersionHistory/VersionHistory.tsx b/src/renderer/src/components/VersionHistory/VersionHistory.tsx index 7a3e15b..93101a4 100644 --- a/src/renderer/src/components/VersionHistory/VersionHistory.tsx +++ b/src/renderer/src/components/VersionHistory/VersionHistory.tsx @@ -5,24 +5,10 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import { - List, - Tag, - Space, - Spin, - Popconfirm, - Typography, -} from "antd"; +import { List, Tag, Space, Spin, Popconfirm, Typography } from "antd"; import { Button, Tooltip, Empty, Avatar } from "@lobehub/ui"; -import { - History, - Download, - Trash2, - Undo2, - Code, - FileText, -} from "lucide-react"; +import { History, Download, Trash2, Undo2, Code, FileText } from "lucide-react"; import { createStyles } from "antd-style"; import { useVersionStore } from "@renderer/stores"; import { useDomainStore } from "@renderer/stores"; @@ -104,8 +90,7 @@ const VersionHistory: React.FC = () => { const { styles } = useStyles(); const { currentDomain } = useDomainStore(); const { currentApp } = useAppStore(); - const { versions, loading, setVersions, setLoading, removeVersion } = - useVersionStore(); + const { versions, loading, setVersions, setLoading, removeVersion } = useVersionStore(); // Load versions when app changes React.useEffect(() => { @@ -191,9 +176,7 @@ const VersionHistory: React.FC = () => { return (
- +
); @@ -215,20 +198,14 @@ const VersionHistory: React.FC = () => { {t("title")} {t("totalVersions", { count: versions.length })}
-
{versions.length === 0 ? ( - + ) : ( {
- ) : ( - - ) - } + icon={version.fileType === "js" ? : } style={{ - backgroundColor: - version.fileType === "js" ? "#f7df1e" : "#264de4", + backgroundColor: version.fileType === "js" ? "#f7df1e" : "#264de4", }} />
@@ -255,45 +225,28 @@ const VersionHistory: React.FC = () => {
{sourceTag.text} {version.fileType.toUpperCase()} - - {formatFileSize(version.size)} - - - {formatDate(version.createdAt)} - + {formatFileSize(version.size)} + {formatDate(version.createdAt)}
{version.tags && version.tags.length > 0 && (
{version.tags.map((tag, i) => ( - + {tag} ))}
)} - {version.notes && ( - {version.notes} - )} + {version.notes && {version.notes}}
-
diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/env.d.ts index 585cd77..7022b5e 100644 --- a/src/renderer/src/env.d.ts +++ b/src/renderer/src/env.d.ts @@ -1,10 +1,10 @@ /// interface ImportMetaEnv { - readonly MAIN_VITE_API_URL: string - readonly MAIN_VITE_DEBUG: string + readonly MAIN_VITE_API_URL: string; + readonly MAIN_VITE_DEBUG: string; } interface ImportMeta { - readonly env: ImportMetaEnv -} \ No newline at end of file + readonly env: ImportMetaEnv; +} diff --git a/src/renderer/src/index.css b/src/renderer/src/index.css index 67470a0..0f7baa2 100644 --- a/src/renderer/src/index.css +++ b/src/renderer/src/index.css @@ -4,9 +4,9 @@ body, margin: 0; padding: 0; height: 100%; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, - 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', - 'Noto Color Emoji'; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol", "Noto Color Emoji"; } /* macOS style window controls area */ @@ -47,4 +47,4 @@ body, ::-webkit-scrollbar-thumb:hover { background: #5a5a5a; } -} \ No newline at end of file +} diff --git a/src/renderer/src/locales/en-US/domain.json b/src/renderer/src/locales/en-US/domain.json index 99c2a9e..52c4dfa 100644 --- a/src/renderer/src/locales/en-US/domain.json +++ b/src/renderer/src/locales/en-US/domain.json @@ -60,4 +60,4 @@ "updateFailed": "Update failed", "expand": "Expand", "collapse": "Collapse" -} \ No newline at end of file +} diff --git a/src/renderer/src/locales/ja-JP/domain.json b/src/renderer/src/locales/ja-JP/domain.json index abaa162..7f540cb 100644 --- a/src/renderer/src/locales/ja-JP/domain.json +++ b/src/renderer/src/locales/ja-JP/domain.json @@ -60,4 +60,4 @@ "updateFailed": "更新に失敗しました", "expand": "展開", "collapse": "折りたたむ" -} \ No newline at end of file +} diff --git a/src/renderer/src/locales/zh-CN/domain.json b/src/renderer/src/locales/zh-CN/domain.json index 304da1e..1bf3543 100644 --- a/src/renderer/src/locales/zh-CN/domain.json +++ b/src/renderer/src/locales/zh-CN/domain.json @@ -60,4 +60,4 @@ "updateFailed": "更新失败", "expand": "展开", "collapse": "折叠" -} \ No newline at end of file +} diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 1193a31..b4ff74c 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -7,7 +7,7 @@ import i18n from "./i18n"; import App from "./App"; import { useThemeStore } from "./stores/themeStore"; -import { motion } from 'motion/react'; +import { motion } from "motion/react"; const ThemeApp: React.FC = () => { const { themeMode, setThemeMode } = useThemeStore(); @@ -17,8 +17,8 @@ const ThemeApp: React.FC = () => { themeMode={themeMode} onThemeModeChange={setThemeMode} // customTheme={{primaryColor: 'blue'}} - customToken={ () => ({ - colorLink: '#1890ff' + customToken={() => ({ + colorLink: "#1890ff", })} > @@ -35,5 +35,5 @@ ReactDOM.createRoot(document.getElementById("root")!).render( - , + ); diff --git a/src/renderer/src/stores/appStore.ts b/src/renderer/src/stores/appStore.ts index 7d71561..a4e62f8 100644 --- a/src/renderer/src/stores/appStore.ts +++ b/src/renderer/src/stores/appStore.ts @@ -100,6 +100,6 @@ export const useAppStore = create()( loadedAt: state.loadedAt, selectedAppId: state.selectedAppId, }), - }, - ), + } + ) ); diff --git a/src/renderer/src/stores/domainStore.ts b/src/renderer/src/stores/domainStore.ts index 59f4b2b..397ebc2 100644 --- a/src/renderer/src/stores/domainStore.ts +++ b/src/renderer/src/stores/domainStore.ts @@ -26,11 +26,7 @@ interface DomainState { removeDomain: (id: string) => void; reorderDomains: (fromIndex: number, toIndex: number) => void; setCurrentDomain: (domain: Domain | null) => void; - setConnectionStatus: ( - id: string, - status: ConnectionStatus, - error?: string, - ) => void; + setConnectionStatus: (id: string, status: ConnectionStatus, error?: string) => void; setLoading: (loading: boolean) => void; setError: (error: string | null) => void; clearError: () => void; @@ -66,17 +62,13 @@ export const useDomainStore = create()( updateDomain: (domain) => set((state) => ({ domains: state.domains.map((d) => (d.id === domain.id ? domain : d)), - currentDomain: - state.currentDomain?.id === domain.id - ? domain - : state.currentDomain, + currentDomain: state.currentDomain?.id === domain.id ? domain : state.currentDomain, })), removeDomain: (id) => set((state) => ({ domains: state.domains.filter((d) => d.id !== id), - currentDomain: - state.currentDomain?.id === id ? null : state.currentDomain, + currentDomain: state.currentDomain?.id === id ? null : state.currentDomain, })), reorderDomains: (fromIndex, toIndex) => @@ -92,9 +84,7 @@ export const useDomainStore = create()( setConnectionStatus: (id, status, error) => set((state) => ({ connectionStatuses: { ...state.connectionStatuses, [id]: status }, - connectionErrors: error - ? { ...state.connectionErrors, [id]: error } - : state.connectionErrors, + connectionErrors: error ? { ...state.connectionErrors, [id]: error } : state.connectionErrors, })), setLoading: (loading) => set({ loading }), @@ -115,8 +105,7 @@ export const useDomainStore = create()( } } catch (error) { set({ - error: - error instanceof Error ? error.message : "Failed to load domains", + error: error instanceof Error ? error.message : "Failed to load domains", loading: false, }); } @@ -124,9 +113,7 @@ export const useDomainStore = create()( createDomain: async (params: CreateDomainParams) => { // Check for duplicate domain - const existingDomain = get().domains.find( - (d) => d.domain === params.domain && d.username === params.username, - ); + const existingDomain = get().domains.find((d) => d.domain === params.domain && d.username === params.username); if (existingDomain) { set({ error: "domainAlreadyExists", loading: false }); return false; @@ -147,10 +134,7 @@ export const useDomainStore = create()( } } catch (error) { set({ - error: - error instanceof Error - ? error.message - : "Failed to create domain", + error: error instanceof Error ? error.message : "Failed to create domain", loading: false, }); return false; @@ -163,13 +147,8 @@ export const useDomainStore = create()( const result = await window.api.updateDomain(params); if (result.success) { set((state) => ({ - domains: state.domains.map((d) => - d.id === result.data.id ? result.data : d, - ), - currentDomain: - state.currentDomain?.id === result.data.id - ? result.data - : state.currentDomain, + domains: state.domains.map((d) => (d.id === result.data.id ? result.data : d)), + currentDomain: state.currentDomain?.id === result.data.id ? result.data : state.currentDomain, loading: false, })); return true; @@ -179,10 +158,7 @@ export const useDomainStore = create()( } } catch (error) { set({ - error: - error instanceof Error - ? error.message - : "Failed to update domain", + error: error instanceof Error ? error.message : "Failed to update domain", loading: false, }); return false; @@ -196,8 +172,7 @@ export const useDomainStore = create()( if (result.success) { set((state) => ({ domains: state.domains.filter((d) => d.id !== id), - currentDomain: - state.currentDomain?.id === id ? null : state.currentDomain, + currentDomain: state.currentDomain?.id === id ? null : state.currentDomain, loading: false, })); return true; @@ -207,10 +182,7 @@ export const useDomainStore = create()( } } catch (error) { set({ - error: - error instanceof Error - ? error.message - : "Failed to delete domain", + error: error instanceof Error ? error.message : "Failed to delete domain", loading: false, }); return false; @@ -271,9 +243,7 @@ export const useDomainStore = create()( ...state.connectionStatuses, [id]: status.connectionStatus, }, - connectionErrors: status.connectionError - ? { ...state.connectionErrors, [id]: status.connectionError } - : state.connectionErrors, + connectionErrors: status.connectionError ? { ...state.connectionErrors, [id]: status.connectionError } : state.connectionErrors, })); return status; } else { @@ -294,10 +264,7 @@ export const useDomainStore = create()( connectionStatuses: { ...state.connectionStatuses, [id]: "error" }, connectionErrors: { ...state.connectionErrors, - [id]: - error instanceof Error - ? error.message - : "Connection test failed", + [id]: error instanceof Error ? error.message : "Connection test failed", }, })); return null; @@ -310,6 +277,6 @@ export const useDomainStore = create()( domains: state.domains, currentDomain: state.currentDomain, }), - }, - ), + } + ) ); diff --git a/src/renderer/src/stores/fileChangeStore.ts b/src/renderer/src/stores/fileChangeStore.ts index 05c5066..3a4d472 100644 --- a/src/renderer/src/stores/fileChangeStore.ts +++ b/src/renderer/src/stores/fileChangeStore.ts @@ -41,11 +41,7 @@ interface FileChangeState { * No-op if the app is already initialized (has pending changes). * Call clearChanges() first to force re-initialization. */ - initializeApp: ( - domainId: string, - appId: string, - files: Array>, - ) => void; + initializeApp: (domainId: string, appId: string, files: Array>) => void; /** * Add a new locally-staged file (status: added). @@ -70,14 +66,7 @@ interface FileChangeState { * Reorder files within a specific (platform, fileType) section. * The dragged file's status will be set to "reordered" (if it was "unchanged"). */ - reorderSection: ( - domainId: string, - appId: string, - platform: "desktop" | "mobile", - fileType: "js" | "css", - newOrder: string[], - draggedFileId: string - ) => void; + reorderSection: (domainId: string, appId: string, platform: "desktop" | "mobile", fileType: "js" | "css", newOrder: string[], draggedFileId: string) => void; /** * Clear all pending changes and reset initialized state. @@ -89,18 +78,10 @@ interface FileChangeState { getFiles: (domainId: string, appId: string) => FileEntry[]; /** Get files for a specific section */ - getSectionFiles: ( - domainId: string, - appId: string, - platform: "desktop" | "mobile", - fileType: "js" | "css", - ) => FileEntry[]; + getSectionFiles: (domainId: string, appId: string, platform: "desktop" | "mobile", fileType: "js" | "css") => FileEntry[]; /** Count of added, deleted, and reordered files */ - getChangeCount: ( - domainId: string, - appId: string - ) => { added: number; deleted: number; reordered: number }; + getChangeCount: (domainId: string, appId: string) => { added: number; deleted: number; reordered: number }; isInitialized: (domainId: string, appId: string) => boolean; } @@ -175,9 +156,7 @@ export const useFileChangeStore = create()( updatedFiles = existing.files.filter((f) => f.id !== fileId); } else if (file.status === "unchanged" || file.status === "reordered") { // Mark unchanged/reordered files as deleted - updatedFiles = existing.files.map((f) => - f.id === fileId ? { ...f, status: "deleted" as FileStatus } : f, - ); + updatedFiles = existing.files.map((f) => (f.id === fileId ? { ...f, status: "deleted" as FileStatus } : f)); } else { return state; } @@ -202,11 +181,7 @@ export const useFileChangeStore = create()( ...state.appFiles, [key]: { ...existing, - files: existing.files.map((f) => - f.id === fileId && f.status === "deleted" - ? { ...f, status: "unchanged" as FileStatus } - : f, - ), + files: existing.files.map((f) => (f.id === fileId && f.status === "deleted" ? { ...f, status: "unchanged" as FileStatus } : f)), }, }, }; @@ -220,18 +195,12 @@ export const useFileChangeStore = create()( if (!existing) return state; // Split files into this section and others - const sectionFiles = existing.files.filter( - (f) => f.platform === platform && f.fileType === fileType, - ); - const otherFiles = existing.files.filter( - (f) => !(f.platform === platform && f.fileType === fileType), - ); + const sectionFiles = existing.files.filter((f) => f.platform === platform && f.fileType === fileType); + const otherFiles = existing.files.filter((f) => !(f.platform === platform && f.fileType === fileType)); // Reorder section files according to newOrder const sectionMap = new Map(sectionFiles.map((f) => [f.id, f])); - const reordered = newOrder - .map((id) => sectionMap.get(id)) - .filter((f): f is FileEntry => f !== undefined); + const reordered = newOrder.map((id) => sectionMap.get(id)).filter((f): f is FileEntry => f !== undefined); // Append any section files not in newOrder (safety) for (const f of sectionFiles) { @@ -279,9 +248,7 @@ export const useFileChangeStore = create()( getSectionFiles: (domainId, appId, platform, fileType) => { const key = appKey(domainId, appId); const files = get().appFiles[key]?.files ?? []; - return files.filter( - (f) => f.platform === platform && f.fileType === fileType, - ); + return files.filter((f) => f.platform === platform && f.fileType === fileType); }, getChangeCount: (domainId, appId) => { diff --git a/src/renderer/src/stores/localeStore.ts b/src/renderer/src/stores/localeStore.ts index 3b67cb9..6ce010e 100644 --- a/src/renderer/src/stores/localeStore.ts +++ b/src/renderer/src/stores/localeStore.ts @@ -29,6 +29,6 @@ export const useLocaleStore = create()( partialize: (state) => ({ locale: state.locale, }), - }, - ), -); \ No newline at end of file + } + ) +); diff --git a/src/renderer/src/stores/sessionStore.ts b/src/renderer/src/stores/sessionStore.ts index d3a3060..9b872b6 100644 --- a/src/renderer/src/stores/sessionStore.ts +++ b/src/renderer/src/stores/sessionStore.ts @@ -49,6 +49,6 @@ export const useSessionStore = create()( viewMode: state.viewMode, selectedFile: state.selectedFile, }), - }, - ), + } + ) ); diff --git a/src/renderer/src/stores/themeStore.ts b/src/renderer/src/stores/themeStore.ts index 4763236..d37c58c 100644 --- a/src/renderer/src/stores/themeStore.ts +++ b/src/renderer/src/stores/themeStore.ts @@ -27,6 +27,6 @@ export const useThemeStore = create()( }), { name: "theme-storage", - }, - ), + } + ) ); diff --git a/src/renderer/src/stores/uiStore.ts b/src/renderer/src/stores/uiStore.ts index 0f80e49..8581108 100644 --- a/src/renderer/src/stores/uiStore.ts +++ b/src/renderer/src/stores/uiStore.ts @@ -50,10 +50,7 @@ export const useUIStore = create()( // Actions setSidebarWidth: (width) => set({ - sidebarWidth: Math.min( - MAX_SIDEBAR_WIDTH, - Math.max(MIN_SIDEBAR_WIDTH, width), - ), + sidebarWidth: Math.min(MAX_SIDEBAR_WIDTH, Math.max(MIN_SIDEBAR_WIDTH, width)), }), setSiderCollapsed: (collapsed) => set({ siderCollapsed: collapsed }), @@ -74,9 +71,7 @@ export const useUIStore = create()( set((state) => { const currentPinned = state.pinnedApps[domainId] || []; const isPinned = currentPinned.includes(appId); - const newPinned = isPinned - ? currentPinned.filter((id) => id !== appId) - : [appId, ...currentPinned]; + const newPinned = isPinned ? currentPinned.filter((id) => id !== appId) : [appId, ...currentPinned]; return { pinnedApps: { ...state.pinnedApps, [domainId]: newPinned }, }; @@ -97,6 +92,6 @@ export const useUIStore = create()( appSortBy: state.appSortBy, appSortOrder: state.appSortOrder, }), - }, - ), + } + ) ); diff --git a/src/renderer/src/stores/versionStore.ts b/src/renderer/src/stores/versionStore.ts index fdf5a2a..ccd6266 100644 --- a/src/renderer/src/stores/versionStore.ts +++ b/src/renderer/src/stores/versionStore.ts @@ -45,8 +45,7 @@ export const useVersionStore = create()((set) => ({ removeVersion: (id) => set((state) => ({ versions: state.versions.filter((v) => v.id !== id), - selectedVersion: - state.selectedVersion?.id === id ? null : state.selectedVersion, + selectedVersion: state.selectedVersion?.id === id ? null : state.selectedVersion, })), setSelectedVersion: (version) => set({ selectedVersion: version }), diff --git a/src/shared/types/ipc.ts b/src/shared/types/ipc.ts index fcc02c1..3024f23 100644 --- a/src/shared/types/ipc.ts +++ b/src/shared/types/ipc.ts @@ -8,9 +8,7 @@ import type { AppResponse, AppDetail, FileContent } from "./kintone"; import type { Version, DownloadMetadata, BackupMetadata } from "./version"; // Unified result type -export type Result = - | { success: true; data: T } - | { success: false; error: string }; +export type Result = { success: true; data: T } | { success: false; error: string }; // ==================== Domain IPC Types ==================== @@ -193,16 +191,12 @@ export interface ElectronAPI { updateDomain: (params: UpdateDomainParams) => Promise>; deleteDomain: (id: string) => Promise>; testConnection: (id: string) => Promise>; - testDomainConnection: ( - params: TestDomainConnectionParams, - ) => Promise>; + testDomainConnection: (params: TestDomainConnectionParams) => Promise>; // Browse getApps: (params: GetAppsParams) => Promise>; getAppDetail: (params: GetAppDetailParams) => Promise>; - getFileContent: ( - params: GetFileContentParams, - ) => Promise>; + getFileContent: (params: GetFileContentParams) => Promise>; // Deploy deploy: (params: DeployParams) => Promise>; diff --git a/src/shared/types/kintone.ts b/src/shared/types/kintone.ts index ee801b7..fbd069e 100644 --- a/src/shared/types/kintone.ts +++ b/src/shared/types/kintone.ts @@ -22,14 +22,10 @@ type KintoneClient = KintoneRestAPIClient; export type AppResponse = Awaited>; /** App customization request - parameters for updateAppCustomize */ -export type AppCustomizeParameter = Parameters< - KintoneClient["app"]["updateAppCustomize"] ->[number]; +export type AppCustomizeParameter = Parameters[number]; /** App customization response */ -export type AppCustomizeResponse = Awaited< - ReturnType ->; +export type AppCustomizeResponse = Awaited>; // ============== Custom Business Types ============== @@ -53,7 +49,6 @@ export interface FileContent { content?: string; // Base64 encoded or text } - /** * File config for customization * Using SDK's type directly from AppCustomizeResponse @@ -75,16 +70,13 @@ type ExtractFileType = T extends { type: "FILE" } ? T : never; export type FileResourceParameter = ExtractFileType; export type FileResourceResponse = ExtractFileType; - // ============== Type Guards ============== /** * Check if resource is URL type - works with both Response and Parameter types * TypeScript will automatically narrow the type based on usage */ -export function isUrlResource( - resource: FileConfigResponse | FileConfigParameter, -): resource is UrlResourceParameter | UrlResourceResponse { +export function isUrlResource(resource: FileConfigResponse | FileConfigParameter): resource is UrlResourceParameter | UrlResourceResponse { return resource.type === "URL" && !!resource.url; } @@ -92,8 +84,6 @@ export function isUrlResource( * Check if resource is FILE type - works with both Response and Parameter types * TypeScript will automatically narrow the type based on usage */ -export function isFileResource( - resource: FileConfigResponse | FileConfigParameter, -): resource is FileResourceParameter | FileResourceResponse { +export function isFileResource(resource: FileConfigResponse | FileConfigParameter): resource is FileResourceParameter | FileResourceResponse { return resource.type === "FILE" && !!resource.file?.fileKey; } diff --git a/src/shared/types/locale.ts b/src/shared/types/locale.ts index 2ee34a5..3e9bb16 100644 --- a/src/shared/types/locale.ts +++ b/src/shared/types/locale.ts @@ -43,4 +43,4 @@ export const LOCALES: LocaleConfig[] = [ name: "English", nativeName: "English", }, -]; \ No newline at end of file +]; diff --git a/src/shared/utils/fileDisplay.ts b/src/shared/utils/fileDisplay.ts index bb0907f..93be6fb 100644 --- a/src/shared/utils/fileDisplay.ts +++ b/src/shared/utils/fileDisplay.ts @@ -3,11 +3,7 @@ import { isFileResource, isUrlResource, type FileConfigResponse } from "@shared/ /** * Get user-friendly display name for a file config */ -export function getDisplayName( - file: FileConfigResponse, - fileType: "js" | "css", - index: number, -): string { +export function getDisplayName(file: FileConfigResponse, fileType: "js" | "css", index: number): string { if (isUrlResource(file)) { return extractFilenameFromUrl(file.url) || `URL ${index + 1}`; } diff --git a/src/shared/utils/fileTransform.ts b/src/shared/utils/fileTransform.ts index ceaa022..5ec81c8 100644 --- a/src/shared/utils/fileTransform.ts +++ b/src/shared/utils/fileTransform.ts @@ -1,7 +1,4 @@ -import type { - AppCustomizeResponse, - FileConfigResponse, -} from "@shared/types/kintone"; +import type { AppCustomizeResponse, FileConfigResponse } from "@shared/types/kintone"; import { isFileResource, isUrlResource } from "@shared/types/kintone"; import type { FileEntry } from "@renderer/stores/fileChangeStore"; import { getDisplayName, getFileKey } from "./fileDisplay"; @@ -10,47 +7,29 @@ import { getDisplayName, getFileKey } from "./fileDisplay"; * Transform Kintone customize data into FileEntry array format * Used to initialize file change store from Kintone API response */ -export function transformCustomizeToFiles( - customize: AppCustomizeResponse | undefined, -): Array> { +export function transformCustomizeToFiles(customize: AppCustomizeResponse | undefined): Array> { if (!customize) return []; const files: Array> = []; // Desktop JS files if (customize.desktop?.js) { - files.push( - ...customize.desktop.js.map((file, index) => - transformFileConfig(file, "js", "desktop", index), - ), - ); + files.push(...customize.desktop.js.map((file, index) => transformFileConfig(file, "js", "desktop", index))); } // Desktop CSS files if (customize.desktop?.css) { - files.push( - ...customize.desktop.css.map((file, index) => - transformFileConfig(file, "css", "desktop", index), - ), - ); + files.push(...customize.desktop.css.map((file, index) => transformFileConfig(file, "css", "desktop", index))); } // Mobile JS files if (customize.mobile?.js) { - files.push( - ...customize.mobile.js.map((file, index) => - transformFileConfig(file, "js", "mobile", index), - ), - ); + files.push(...customize.mobile.js.map((file, index) => transformFileConfig(file, "js", "mobile", index))); } // Mobile CSS files if (customize.mobile?.css) { - files.push( - ...customize.mobile.css.map((file, index) => - transformFileConfig(file, "css", "mobile", index), - ), - ); + files.push(...customize.mobile.css.map((file, index) => transformFileConfig(file, "css", "mobile", index))); } return files; @@ -59,20 +38,13 @@ export function transformCustomizeToFiles( /** * Transform a single file config into FileEntry format */ -function transformFileConfig( - file: FileConfigResponse, - fileType: "js" | "css", - platform: "desktop" | "mobile", - index: number, -): Omit { +function transformFileConfig(file: FileConfigResponse, fileType: "js" | "css", platform: "desktop" | "mobile", index: number): Omit { return { id: crypto.randomUUID(), fileName: getDisplayName(file, fileType, index), fileType, platform, - size: isFileResource(file) - ? parseInt(file.file.size ?? "0", 10) || undefined - : undefined, + size: isFileResource(file) ? parseInt(file.file.size ?? "0", 10) || undefined : undefined, fileKey: getFileKey(file), url: isUrlResource(file) ? file.url : undefined, };