Sync source to CNB / Sync Git ref (push) Skipped
将崩溃弹窗替换为双栏布局:左侧展示诊断摘要与分享入口, 右侧提供日志文件浏览器与 LogAgent 流式分析输出。 - MinecraftCrashModal 改用新版的分享票据、结构化摘要与 AI 事件流 - 新增 minecraft-crash-logs 工具函数,用于筛选与合并崩溃日志 - 事件层新增 logshare_ai_listener 监听 AI 分析进度 - log-share 工具更新为支持 provider 路由的新版 - Button 组件对齐新版(补 icon-only、circular、2xs 与 chip/highlight 变体) - console 导出 LogViewport,供日志浏览器复用 - 补齐 en-US / zh-CN 相关文案
75 lines
2.6 KiB
TypeScript
75 lines
2.6 KiB
TypeScript
import type { AbstractModrinthClient } from '@modrinth/api-client'
|
|
|
|
const textEncoder = new TextEncoder()
|
|
const textDecoder = new TextDecoder()
|
|
|
|
/**
|
|
* Maximum size in bytes of the log content sent to mclo.gs. Logs
|
|
* larger than this are trimmed to their last `LOG_SHARE_MAX_BYTES` before
|
|
* uploading, and the caller is notified through `LogShareResult.truncated`.
|
|
*/
|
|
export const LOG_SHARE_MAX_BYTES = 9 * 1024 * 1024
|
|
|
|
/**
|
|
* Return the tail of `content` that fits within `maxBytes` of UTF-8 without
|
|
* splitting a multi-byte code point. Returns the original string when it
|
|
* already fits.
|
|
*/
|
|
export function truncateLogToMaxBytes(content: string, maxBytes: number): string {
|
|
if (!content) return content
|
|
const bytes = textEncoder.encode(content)
|
|
if (bytes.byteLength <= maxBytes) return content
|
|
|
|
// Keep the last `maxBytes` bytes, starting at the first byte that begins a
|
|
// UTF-8 code point so decoding never produces replacement characters.
|
|
let start = bytes.byteLength - maxBytes
|
|
while (start < bytes.byteLength && (bytes[start] & 0xc0) === 0x80) start++
|
|
|
|
return textDecoder.decode(bytes.subarray(start))
|
|
}
|
|
|
|
/**
|
|
* Log sharing providers supported by the launcher. LogShare is the default
|
|
* and is implemented in the desktop app through native Tauri commands; mclo.gs
|
|
* is the network fallback used when LogShare is unreachable or unselected.
|
|
*/
|
|
export type LogShareProvider = 'logshare' | 'mclogs'
|
|
|
|
/**
|
|
* Result of sharing log content. `truncated` is set when the content exceeded
|
|
* `LOG_SHARE_MAX_BYTES` and only its tail was uploaded.
|
|
*/
|
|
export type LogShareResult = {
|
|
url: string
|
|
truncated: boolean
|
|
}
|
|
|
|
/**
|
|
* Share log content through mclo.gs. Content larger than `LOG_SHARE_MAX_BYTES`
|
|
* is trimmed to its last `LOG_SHARE_MAX_BYTES` before uploading.
|
|
*/
|
|
export async function shareLogs(
|
|
client: AbstractModrinthClient,
|
|
content: string,
|
|
): Promise<LogShareResult> {
|
|
return shareLogsWithProvider(client, content, 'mclogs')
|
|
}
|
|
|
|
/**
|
|
* Share log content through the given provider. The `logshare` provider is
|
|
* only available inside the desktop app (native commands); this shared helper
|
|
* routes the `mclogs` fallback so both launcher surfaces can share generic
|
|
* log buffers.
|
|
*/
|
|
export async function shareLogsWithProvider(
|
|
client: AbstractModrinthClient,
|
|
content: string,
|
|
_provider: LogShareProvider = 'mclogs',
|
|
): Promise<LogShareResult> {
|
|
const uploadContent = truncateLogToMaxBytes(content, LOG_SHARE_MAX_BYTES)
|
|
const truncated = uploadContent !== content
|
|
const data = await client.mclogs.logs_v1.create(uploadContent)
|
|
if (data.success && data.url) return { url: data.url, truncated }
|
|
throw new Error('mclo.gs upload failed')
|
|
}
|