diff --git a/apps/app-frontend/src/composables/useInstanceConsole.ts b/apps/app-frontend/src/composables/useInstanceConsole.ts index 4c64e4d84..24a611db2 100644 --- a/apps/app-frontend/src/composables/useInstanceConsole.ts +++ b/apps/app-frontend/src/composables/useInstanceConsole.ts @@ -73,6 +73,12 @@ function invalidate(profilePathId: string): void { entry.logList = null } +async function clearLive(profilePathId: string): Promise { + const entry = getOrCreate(profilePathId) + entry.liveConsole.clear() + await clear_log_buffer(profilePathId).catch(() => {}) +} + async function destroy(profilePathId: string): Promise { instances.delete(profilePathId) await clear_log_buffer(profilePathId).catch(() => {}) @@ -87,6 +93,7 @@ export function useInstanceConsole(profilePathId: string) { getHistoricalLogs: (instancePath: string) => getHistoricalLogs(profilePathId, instancePath), getHistoricalContent: (filename: string) => getHistoricalContent(profilePathId, filename), invalidate: () => invalidate(profilePathId), + clearLive: () => clearLive(profilePathId), destroy: () => destroy(profilePathId), } } diff --git a/apps/app-frontend/src/pages/instance/Logs.vue b/apps/app-frontend/src/pages/instance/Logs.vue index 967985deb..2bc221863 100644 --- a/apps/app-frontend/src/pages/instance/Logs.vue +++ b/apps/app-frontend/src/pages/instance/Logs.vue @@ -63,6 +63,7 @@ const { getHistoricalLogs, getHistoricalContent, invalidate, + clearLive, } = useInstanceConsole(profilePathId.value) await hydrate() @@ -159,7 +160,8 @@ provideConsoleManager({ showCommandInput: false, loading: ref(false), onClear: () => { - activeConsole.value.clear() + if (!isLive.value) return + void clearLive() }, onDelete: deleteSelectedLog, deleteDisabled, diff --git a/packages/api-client/src/features/verbose-logging.ts b/packages/api-client/src/features/verbose-logging.ts index 5ff30b1a3..960443dac 100644 --- a/packages/api-client/src/features/verbose-logging.ts +++ b/packages/api-client/src/features/verbose-logging.ts @@ -23,8 +23,50 @@ export class VerboseLoggingFeature extends AbstractFeature { } return result } catch (error) { - console.debug(`${prefix} ${context.url} FAILED`) + const details = formatErrorDetails(error) + console.debug(`${prefix} ${context.url} FAILED${details ? ` — ${details}` : ''}`) throw error } } } + +function formatErrorDetails(error: unknown): string { + if (!error || typeof error !== 'object') { + return typeof error === 'string' ? error : '' + } + + const err = error as { + status?: number + statusCode?: number + statusText?: string + message?: string + data?: unknown + responseData?: unknown + originalError?: unknown + response?: { status?: number; statusText?: string; _data?: unknown } + } + + const status = err.status ?? err.statusCode ?? err.response?.status + const statusText = err.statusText ?? err.response?.statusText + const data = err.responseData ?? err.data ?? err.response?._data + + const parts: string[] = [] + if (status !== undefined) { + parts.push(statusText ? `${status} ${statusText}` : String(status)) + } + if (data !== undefined) { + parts.push(`body: ${safeStringify(data)}`) + } else if (err.message) { + parts.push(err.message) + } + return parts.join(' ') +} + +function safeStringify(value: unknown): string { + if (typeof value === 'string') return value + try { + return JSON.stringify(value) + } catch { + return String(value) + } +} diff --git a/packages/api-client/src/platform/tauri.ts b/packages/api-client/src/platform/tauri.ts index 6b3c2ed61..6b998b017 100644 --- a/packages/api-client/src/platform/tauri.ts +++ b/packages/api-client/src/platform/tauri.ts @@ -60,10 +60,18 @@ export class TauriModrinthClient extends XHRUploadClient { let body: BodyInit | null | undefined = undefined if (options.body) { - if (typeof options.body === 'object' && !(options.body instanceof FormData)) { - body = JSON.stringify(options.body) + const raw = options.body + if ( + typeof raw === 'object' && + !(raw instanceof FormData) && + !(raw instanceof URLSearchParams) && + !(raw instanceof Blob) && + !(raw instanceof ArrayBuffer) && + !ArrayBuffer.isView(raw as ArrayBufferView) + ) { + body = JSON.stringify(raw) } else { - body = options.body as BodyInit + body = raw as BodyInit } } diff --git a/packages/ui/src/components/modal/ShareModal.vue b/packages/ui/src/components/modal/ShareModal.vue index 1cd49e8a5..8c2d2ee92 100644 --- a/packages/ui/src/components/modal/ShareModal.vue +++ b/packages/ui/src/components/modal/ShareModal.vue @@ -183,7 +183,7 @@ defineExpose({ v-tooltip="'Copy Link'" type="button" aria-label="Copy Link" - class="flex h-10 w-full cursor-pointer items-center justify-between gap-2 rounded-lg border-none bg-button-bg px-3 pr-1.5 text-primary transition-all hover:bg-button-bg-hover hover:brightness-125 active:scale-95" + class="flex h-10 w-full cursor-pointer items-center justify-between gap-2 rounded-xl border-none bg-button-bg px-3 pr-1.5 text-primary transition-all hover:bg-button-bg-hover hover:brightness-125 active:scale-95" @click="copyText" > @@ -195,8 +195,8 @@ defineExpose({ -