fix(desktop): surface configuration and provider failures instead of hiding them

Several desktop paths quietly substituted defaults or partial results: a
config write could fall back to a throwaway in-memory store, speech provider
errors were absorbed into empty transcriptions, and meeting exports built
file names from raw titles.

Writes now fail explicitly when the store is unavailable, provider and model
failures reach the UI as errors, and export names pass through one
sanitizer. Settings, license, ad, and support surfaces use the shared theme
tokens, unused hotkey helpers are gone, and the package gains strict
node/renderer typecheck configs plus red-team e2e scenarios for these flows.
This commit is contained in:
Yun Chan 2026-09-16 23:23:58 +09:00
parent c8d802d78f
commit 6ba25f53b7
43 changed files with 1721 additions and 204 deletions

View file

@ -33,9 +33,14 @@ export function registerInstructionHandlers(): void {
ipcMain.handle(
IPC_CHANNELS.INSTRUCTION.UPDATE,
async (_event, params: { id: string; data: Partial<CustomInstruction> }) => {
async (
_event,
params: { id: string; data?: Partial<CustomInstruction>; name?: string; description?: string; prompt?: string; icon?: string }
) => {
try {
const result = getCustomInstructionService().update(params.id, params.data)
const { id, data, ...rest } = params
const updatePayload = data ?? rest
const result = getCustomInstructionService().update(id, updatePayload)
return ipcSuccess(result)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)

View file

@ -3,7 +3,7 @@
import { ipcMain } from 'electron'
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
import { ErrorCode, ipcSuccess, ipcError } from '@d3ro/core/errors'
import { ErrorCode, ipcSuccess, ipcError, D3ROError } from '@d3ro/core/errors'
import { getMeetingModeService } from '../services/MeetingModeService'
import { getLogger } from '../services/LoggerService'
import type {
@ -188,9 +188,9 @@ export function registerMeetingModeHandlers(): void {
}
})
ipcMain.handle(ch.EXPORT_DOCUMENT, async (_event, params: { documentId: string; format: MeetingExportFormat }) => {
ipcMain.handle(ch.EXPORT_DOCUMENT, async (_event, params: { documentId: string; format: MeetingExportFormat; targetPath?: string }) => {
try {
const filePath = await getMeetingModeService().exportDocument(params.documentId, params.format)
const filePath = await getMeetingModeService().exportDocument(params.documentId, params.format, params.targetPath)
return ipcSuccess(filePath)
} catch (err) {
logger.error(`exportDocument 실패: ${err instanceof Error ? err.message : String(err)}`)
@ -201,9 +201,9 @@ export function registerMeetingModeHandlers(): void {
}
})
ipcMain.handle(ch.EXPORT_TRANSCRIPT, async (_event, params: { sessionId: string }) => {
ipcMain.handle(ch.EXPORT_TRANSCRIPT, async (_event, params: { sessionId: string; targetPath?: string }) => {
try {
const filePath = await getMeetingModeService().exportTranscript(params.sessionId)
const filePath = await getMeetingModeService().exportTranscript(params.sessionId, params.targetPath)
return ipcSuccess(filePath)
} catch (err) {
logger.error(`exportTranscript 실패: ${err instanceof Error ? err.message : String(err)}`)