Closes the gap between 'something broke' and 'a useful GitHub issue exists' — entirely within the local-first constraint: the only outbound path remains the user's own browser opening a prefilled issues/new URL. Backend: - core/scrub.py: privacy scrubber for anything leaving the machine — env-var secret values (*TOKEN*|*KEY*|*SECRET*|*PASSWORD*), credential shapes (hf_/ghp_/github_pat_/sk-), home dirs on all three OSes - core/diagnose.py: 9-check self-check (device+GPU, ffmpeg, HF token, disk, data-dir writability, RAM, engine registry, hub reachability), pre-scrubbed, ASCII-safe output - GET /system/diagnose + 'python main.py --diagnose' (exit 0/1) - /system/info: hardware inventory (os_version, cpu_model, cpu_count, ram_total_gb, gpu_name, vram_total_gb, disk_free_gb), cached statics Frontend: - utils/bugReport.js: single source for the prefilled-URL builder — scrubText twin, hardware context capture, scrubbed error+stack embed, URL-length cap; ReportBugButton refactored onto it - ErrorBoundary 'Report this bug' action with the error attached - utils/errorToast.jsx toastErrorWithReport(); wired into export toasts - Settings > About 'Run self-check' with per-check status badges Tests: 27 pytest (scrub, diagnose) + 15 vitest (bugReport); existing suites green; verified live (--diagnose, TestClient, vite build). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
1.7 KiB
React
50 lines
1.7 KiB
React
/**
|
|
* ReportBugButton — opt-in bug reporter via prefilled GitHub Issues URL.
|
|
*
|
|
* Capability 2 from CLAUDE.md. The user clicks → we build a URL like:
|
|
* https://github.com/{owner}/{repo}/issues/new?title=…&body=…&labels=bug
|
|
* and open it in their browser. They review what we captured and click
|
|
* Submit on github.com if they're happy. We never hold an auth token,
|
|
* never POST to GitHub directly, and never bypass the user's review —
|
|
* opt-in by construction, no separate consent dialog needed.
|
|
*
|
|
* Capture + scrubbing live in utils/bugReport.js (shared with the
|
|
* ErrorBoundary's report action and error toasts): version, OS, GPU/CPU/
|
|
* RAM, active TTS engine — home paths and credential-shaped strings are
|
|
* redacted, audio contents never included.
|
|
*/
|
|
import { useState } from 'react';
|
|
import { Bug } from 'lucide-react';
|
|
import { Button } from '../ui';
|
|
import { openExternal } from '../api/external';
|
|
import { buildBugReportUrl } from '../utils/bugReport';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
export default function ReportBugButton({ size = 'sm', variant = 'subtle', label, error }) {
|
|
const { t } = useTranslation();
|
|
const displayLabel = label || t('reportBug.label');
|
|
const [building, setBuilding] = useState(false);
|
|
|
|
const handleClick = async () => {
|
|
setBuilding(true);
|
|
try {
|
|
await openExternal(await buildBugReportUrl({ error }));
|
|
} finally {
|
|
setBuilding(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Button
|
|
size={size}
|
|
variant={variant}
|
|
onClick={handleClick}
|
|
loading={building}
|
|
leading={!building && <Bug size={12} />}
|
|
title={t('reportBug.title')}
|
|
>
|
|
{displayLabel}
|
|
</Button>
|
|
);
|
|
}
|