feat: add copy diagnostics button
Add copy diagnostics button in About dialog. Button copies report with OpenChamber state, OpenCode health, directories, and projects. Show success or error toasts after copying attempt.
This commit is contained in:
@@ -138,6 +138,7 @@ export const AboutSettings: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Desktop layout (unchanged)
|
||||
return (
|
||||
<div className="w-full space-y-6">
|
||||
@@ -200,6 +201,7 @@ export const AboutSettings: React.FC = () => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
{/* Links */}
|
||||
<div className="flex items-center gap-4">
|
||||
<a
|
||||
|
||||
@@ -122,6 +122,7 @@ export const OpenChamberSidebar: React.FC<OpenChamberSidebarProps> = ({
|
||||
<AboutSettings />
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,9 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { OpenChamberLogo } from '@/components/ui/OpenChamberLogo';
|
||||
import { RiDiscordFill, RiGithubFill, RiTwitterXFill } from '@remixicon/react';
|
||||
import { debugUtils } from '@/lib/debug';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
declare const __APP_VERSION__: string | undefined;
|
||||
|
||||
@@ -18,6 +21,28 @@ export const AboutDialog: React.FC<AboutDialogProps> = ({
|
||||
onOpenChange,
|
||||
}) => {
|
||||
const [version, setVersion] = React.useState<string | null>(null);
|
||||
const [isCopyingDiagnostics, setIsCopyingDiagnostics] = React.useState(false);
|
||||
const [copiedDiagnostics, setCopiedDiagnostics] = React.useState(false);
|
||||
|
||||
const handleCopyDiagnostics = React.useCallback(async () => {
|
||||
if (isCopyingDiagnostics) return;
|
||||
setIsCopyingDiagnostics(true);
|
||||
setCopiedDiagnostics(false);
|
||||
try {
|
||||
const result = await debugUtils.copyDiagnosticsReport();
|
||||
if (result.ok) {
|
||||
setCopiedDiagnostics(true);
|
||||
toast.success('Diagnostics copied');
|
||||
} else {
|
||||
toast.error('Copy failed');
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Copy failed');
|
||||
console.error('Failed to copy diagnostics:', error);
|
||||
} finally {
|
||||
setIsCopyingDiagnostics(false);
|
||||
}
|
||||
}, [isCopyingDiagnostics]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) return;
|
||||
@@ -70,6 +95,23 @@ export const AboutDialog: React.FC<AboutDialogProps> = ({
|
||||
agent
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col items-center gap-2 pt-2">
|
||||
<button
|
||||
onClick={handleCopyDiagnostics}
|
||||
disabled={isCopyingDiagnostics}
|
||||
className={cn(
|
||||
'typography-meta text-muted-foreground hover:text-foreground',
|
||||
'underline-offset-2 hover:underline',
|
||||
'disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
)}
|
||||
>
|
||||
{copiedDiagnostics ? 'Diagnostics copied' : 'Copy diagnostics'}
|
||||
</button>
|
||||
<p className="typography-micro text-muted-foreground">
|
||||
Includes OpenChamber state, OpenCode health, directories, and projects.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 pt-2">
|
||||
<a
|
||||
href="https://github.com/btriapitsyn/openchamber"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useSessionStore } from '@/stores/useSessionStore';
|
||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||
import { opencodeClient } from '@/lib/opencode/client';
|
||||
import { checkIsGitRepository } from '@/lib/gitApi';
|
||||
import { streamDebugEnabled } from '@/stores/utils/streamDebug';
|
||||
@@ -176,6 +177,7 @@ export const debugUtils = {
|
||||
async getAppStatus() {
|
||||
const directoryState = useDirectoryStore.getState();
|
||||
const sessionState = useSessionStore.getState();
|
||||
const projectsState = useProjectsStore.getState();
|
||||
const currentDirectory = directoryState.currentDirectory || null;
|
||||
const opencodeDirectory = opencodeClient.getDirectory() ?? null;
|
||||
|
||||
@@ -291,9 +293,17 @@ export const debugUtils = {
|
||||
}
|
||||
}
|
||||
|
||||
const projectSamples = projectsState.projects.map((project) => ({
|
||||
id: project.id,
|
||||
path: project.path,
|
||||
label: project.label,
|
||||
}));
|
||||
|
||||
const report = {
|
||||
runtime: {
|
||||
platform: runtimeApis?.runtime?.platform ?? null,
|
||||
isDesktop: isDesktopRuntime,
|
||||
isVSCode: Boolean(runtimeApis?.runtime?.isVSCode),
|
||||
hasRuntimeApis: Boolean(runtimeApis),
|
||||
desktopServerOrigin: desktopServer?.origin ?? null,
|
||||
},
|
||||
@@ -313,6 +323,11 @@ export const debugUtils = {
|
||||
hasPersistedDirectory: directoryState.hasPersistedDirectory,
|
||||
isSwitchingDirectory: directoryState.isSwitchingDirectory,
|
||||
},
|
||||
projects: {
|
||||
total: projectsState.projects.length,
|
||||
activeProjectId: projectsState.activeProjectId,
|
||||
samples: projectSamples,
|
||||
},
|
||||
sessions: {
|
||||
total: sessions.length,
|
||||
currentSessionId: sessionState.currentSessionId,
|
||||
@@ -341,6 +356,20 @@ export const debugUtils = {
|
||||
return report;
|
||||
},
|
||||
|
||||
async buildDiagnosticsReport() {
|
||||
const report = await this.getAppStatus();
|
||||
return JSON.stringify(report, null, 2);
|
||||
},
|
||||
|
||||
async copyDiagnosticsReport() {
|
||||
const report = await this.buildDiagnosticsReport();
|
||||
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(report);
|
||||
return { ok: true, report } as const;
|
||||
}
|
||||
return { ok: false, report } as const;
|
||||
},
|
||||
|
||||
checkLastMessage() {
|
||||
const info = this.getLastAssistantMessage();
|
||||
if (!info) return false;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isVSCodeRuntime } from '@/lib/desktop';
|
||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
|
||||
export const applyPersistedDirectoryPreferences = async (): Promise<void> => {
|
||||
@@ -21,7 +22,7 @@ export const applyPersistedDirectoryPreferences = async (): Promise<void> => {
|
||||
directoryStore.synchronizeHomeDirectory(savedHome);
|
||||
}
|
||||
|
||||
if (savedDirectory) {
|
||||
if (savedDirectory && !isVSCodeRuntime()) {
|
||||
directoryStore.setDirectory(savedDirectory, { showOverlay: false });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { opencodeClient } from '@/lib/opencode/client';
|
||||
import { getDesktopHomeDirectory } from '@/lib/desktop';
|
||||
import { getDesktopHomeDirectory, isVSCodeRuntime } from '@/lib/desktop';
|
||||
import { updateDesktopSettings } from '@/lib/persistence';
|
||||
import { useFileSearchStore } from '@/stores/useFileSearchStore';
|
||||
import { streamDebugEnabled } from '@/stores/utils/streamDebug';
|
||||
@@ -76,7 +76,7 @@ const getHomeDirectory = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const storedHome = safeStorage.getItem('homeDirectory') || cachedHomeDirectory || null;
|
||||
const saved = safeStorage.getItem('lastDirectory');
|
||||
if (saved) {
|
||||
if (saved && !isVSCodeRuntime()) {
|
||||
return resolveDirectoryPath(saved, storedHome);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ const getHomeDirectory = () => {
|
||||
return desktopHome;
|
||||
}
|
||||
|
||||
if (storedHome) {
|
||||
if (storedHome && !isVSCodeRuntime()) {
|
||||
cachedHomeDirectory = storedHome;
|
||||
return storedHome;
|
||||
}
|
||||
@@ -187,7 +187,19 @@ const initializeHomeDirectory = async () => {
|
||||
return fallback;
|
||||
};
|
||||
|
||||
const initialHomeDirectory = getHomeDirectory();
|
||||
const getVsCodeWorkspaceFolder = (): string | null => {
|
||||
if (!isVSCodeRuntime()) {
|
||||
return null;
|
||||
}
|
||||
const workspaceFolder = (window as unknown as { __VSCODE_CONFIG__?: { workspaceFolder?: unknown } }).__VSCODE_CONFIG__?.workspaceFolder;
|
||||
if (typeof workspaceFolder !== 'string' || workspaceFolder.trim().length === 0) {
|
||||
return null;
|
||||
}
|
||||
const normalized = normalizeDirectoryPath(workspaceFolder);
|
||||
return normalized.length > 0 ? normalized : null;
|
||||
};
|
||||
|
||||
const initialHomeDirectory = getVsCodeWorkspaceFolder() || getHomeDirectory();
|
||||
if (initialHomeDirectory) {
|
||||
opencodeClient.setDirectory(initialHomeDirectory);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user