mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Desktop startup can fail before React mounts on older WebViews, so an HTML-level watchdog now renders startup diagnostics even when the module bundle never reaches the app code. Persistent provider migration now imports legacy root provider config into cc-haha-owned storage without deleting the old source file, and plugin marketplace cleanup refuses obvious corrupted cache roots or outside paths. Constraint: User explicitly accepted the current reviewed state for landing despite remaining review concerns. Constraint: Global ~/.claude state is user-owned and protected; automatic repair must avoid deleting shared config, transcripts, skills, MCP, plugins, OAuth, adapters, and teams. Rejected: Tell users to delete ~/.claude or ~/.claude/cc-haha | unsafe because it can destroy user-owned Claude state and still may not fix WebView compatibility failures. Confidence: medium Scope-risk: moderate Directive: Do not weaken protected-path checks; future deletion paths should validate real paths and symlink behavior before recursive rm. Tested: bun test src/utils/plugins/installedPluginsManager.test.ts src/utils/plugins/marketplaceManager.test.ts Tested: bun run check:server Tested: cd desktop && bun run test -- --run src/main.test.tsx index-html.test.ts vite-config.test.ts src/theme/globals.test.ts Tested: cd desktop && bun run build Tested: bun run check:coverage Not-tested: live macOS 12/Safari 15 WKWebView startup on an affected machine. Not-tested: H5 diagnostic URL redaction and symlink-escape hardening are known follow-up risks from review.
107 lines
4.0 KiB
HTML
107 lines
4.0 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Claude Code Companion</title>
|
|
<!-- Fonts are self-hosted in /fonts/ and declared via @font-face in globals.css -->
|
|
<style>
|
|
/* Prevent a warm flash before the theme is hydrated. */
|
|
html { background: #FFFFFF; }
|
|
html, body, #root { min-height: 100%; margin: 0; }
|
|
</style>
|
|
<script>
|
|
(function () {
|
|
var bootDeadlineMs = 8000
|
|
|
|
function summarize(value) {
|
|
if (!value) return 'Unknown startup error'
|
|
if (typeof value === 'string') return value
|
|
if (value.message) return value.message
|
|
try {
|
|
return JSON.stringify(value)
|
|
} catch (_) {
|
|
return String(value)
|
|
}
|
|
}
|
|
|
|
function renderStartupError(reason) {
|
|
if (window.__CC_HAHA_BOOTSTRAPPED__) return
|
|
var root = document.getElementById('root')
|
|
if (!root) return
|
|
|
|
var summary = summarize(reason)
|
|
var details = [
|
|
summary,
|
|
'',
|
|
'URL: ' + window.location.href,
|
|
'User Agent: ' + navigator.userAgent,
|
|
'Time: ' + new Date().toISOString()
|
|
].join('\n')
|
|
|
|
root.innerHTML = ''
|
|
var shell = document.createElement('main')
|
|
shell.style.cssText = [
|
|
'box-sizing:border-box',
|
|
'min-height:100vh',
|
|
'padding:32px',
|
|
'background:#fff',
|
|
'color:#151515',
|
|
'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',
|
|
'display:flex',
|
|
'align-items:center',
|
|
'justify-content:center'
|
|
].join(';')
|
|
|
|
var panel = document.createElement('section')
|
|
panel.style.cssText = 'width:min(760px,100%);border:1px solid #d8d8d8;border-radius:8px;padding:24px;background:#fff'
|
|
|
|
var title = document.createElement('h1')
|
|
title.textContent = 'Desktop startup failed'
|
|
title.style.cssText = 'margin:0 0 8px;font-size:20px;line-height:1.3;font-weight:700;color:#111'
|
|
|
|
var body = document.createElement('p')
|
|
body.textContent = 'The app could not finish booting. Keep this text when reporting the issue.'
|
|
body.style.cssText = 'margin:0 0 16px;font-size:14px;line-height:1.6;color:#555'
|
|
|
|
var pre = document.createElement('pre')
|
|
pre.textContent = details
|
|
pre.style.cssText = 'box-sizing:border-box;max-height:360px;overflow:auto;white-space:pre-wrap;word-break:break-word;margin:0;padding:14px;border-radius:6px;background:#f6f6f6;color:#222;font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace'
|
|
|
|
panel.appendChild(title)
|
|
panel.appendChild(body)
|
|
panel.appendChild(pre)
|
|
shell.appendChild(panel)
|
|
root.appendChild(shell)
|
|
}
|
|
|
|
window.__CC_HAHA_SHOW_STARTUP_ERROR__ = renderStartupError
|
|
|
|
window.addEventListener('error', function (event) {
|
|
var target = event.target
|
|
if (target && target !== window) {
|
|
var tagName = target.tagName
|
|
if (tagName === 'SCRIPT' || tagName === 'LINK') {
|
|
renderStartupError('Startup resource failed to load: ' + (target.src || target.href || tagName))
|
|
}
|
|
return
|
|
}
|
|
renderStartupError(event.message || event.error || 'Startup script error')
|
|
}, true)
|
|
|
|
window.addEventListener('unhandledrejection', function (event) {
|
|
renderStartupError(event.reason || 'Unhandled startup promise rejection')
|
|
})
|
|
|
|
window.setTimeout(function () {
|
|
renderStartupError('Desktop app did not finish bootstrapping within ' + bootDeadlineMs + 'ms. This usually means the WebView could not execute the app bundle or startup code stopped before React mounted.')
|
|
}, bootDeadlineMs)
|
|
})()
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
</body>
|
|
</html>
|