Skip to content

Commit dd6613a

Browse files
committed
feat: refine codex binary management
1 parent d6120b5 commit dd6613a

17 files changed

Lines changed: 1892 additions & 347 deletions

File tree

app.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,14 @@ func (a *App) UseCodexBinary(input codexbinary.UseInput) (*codexbinary.UseResult
245245
return a.core.UseCodexBinary(input)
246246
}
247247

248+
func (a *App) RevealCodexBinaryVersion(input codexbinary.VersionActionInput) error {
249+
return a.core.RevealCodexBinaryVersion(input)
250+
}
251+
252+
func (a *App) DeleteCodexBinaryVersion(input codexbinary.VersionActionInput) (*codexbinary.DeleteVersionResult, error) {
253+
return a.core.DeleteCodexBinaryVersion(input)
254+
}
255+
248256
func (a *App) GetCodexBinaryVersionNotes(input codexbinary.VersionNotesInput) (*codexbinary.VersionNotesView, error) {
249257
return a.core.GetCodexBinaryVersionNotes(input)
250258
}

frontend/src/features/codex-binary/CodexBinaryFeature.tsx

Lines changed: 132 additions & 230 deletions
Large diffs are not rendered by default.

frontend/src/features/codex-binary/api.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ type WailsCodexBinaryBridge = {
99
DownloadCodexBinary?: (input: { sourceID: string; tag: string; activateAfterInstall: boolean }) => Promise<{ snapshot?: CodexBinarySnapshot }>;
1010
EnableCodexBinaryManagedPath?: () => Promise<CodexBinaryEnableManagedPathResult>;
1111
UseCodexBinary?: (input: { versionID: string; expectedCurrentVersionID?: string }) => Promise<{ snapshot?: CodexBinarySnapshot }>;
12+
RevealCodexBinaryVersion?: (input: { versionID: string }) => Promise<void>;
13+
DeleteCodexBinaryVersion?: (input: { versionID: string }) => Promise<{ snapshot?: CodexBinarySnapshot }>;
1214
};
1315

1416
function getBridge(): WailsCodexBinaryBridge | null {
@@ -68,7 +70,7 @@ export async function useCodexBinary(versionID: string, expectedCurrentVersionID
6870
return result.snapshot || getCodexBinarySnapshot();
6971
}
7072

71-
export async function downloadCodexBinary(sourceID: string, tag: string): Promise<CodexBinarySnapshot> {
73+
export async function downloadCodexBinary(sourceID: string, tag: string, activateAfterInstall = false): Promise<CodexBinarySnapshot> {
7274
const bridge = getBridge();
7375
if (!bridge?.DownloadCodexBinary || hasPreviewMode('codex-binary')) {
7476
return {
@@ -79,7 +81,27 @@ export async function downloadCodexBinary(sourceID: string, tag: string): Promis
7981
})),
8082
};
8183
}
82-
const result = await bridge.DownloadCodexBinary({ sourceID, tag, activateAfterInstall: true });
84+
const result = await bridge.DownloadCodexBinary({ sourceID, tag, activateAfterInstall });
85+
return result.snapshot || getCodexBinarySnapshot();
86+
}
87+
88+
export async function revealCodexBinaryVersion(versionID: string): Promise<void> {
89+
const bridge = getBridge();
90+
if (!bridge?.RevealCodexBinaryVersion || hasPreviewMode('codex-binary')) {
91+
return;
92+
}
93+
await bridge.RevealCodexBinaryVersion({ versionID });
94+
}
95+
96+
export async function deleteCodexBinaryVersion(versionID: string): Promise<CodexBinarySnapshot> {
97+
const bridge = getBridge();
98+
if (!bridge?.DeleteCodexBinaryVersion || hasPreviewMode('codex-binary')) {
99+
return {
100+
...codexBinaryPreviewSnapshot,
101+
versions: codexBinaryPreviewSnapshot.versions.filter((version) => version.id !== versionID),
102+
};
103+
}
104+
const result = await bridge.DeleteCodexBinaryVersion({ versionID });
83105
return result.snapshot || getCodexBinarySnapshot();
84106
}
85107

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { RefreshCw, ShieldCheck } from 'lucide-react';
2+
import type { CodexBinarySnapshot } from '../model';
3+
4+
export default function CodexBinarySummaryPanel({
5+
snapshot,
6+
message,
7+
loading,
8+
managedBusy,
9+
onEnableManagedPath,
10+
onRefresh,
11+
t,
12+
}: {
13+
snapshot: CodexBinarySnapshot | null;
14+
message: string;
15+
loading: boolean;
16+
managedBusy: boolean;
17+
onEnableManagedPath: () => void;
18+
onRefresh: () => void;
19+
t: (key: string) => string;
20+
}) {
21+
return (
22+
<section className="border-2 border-[var(--border-color)] bg-[var(--bg-main)] p-3 shadow-[5px_5px_0_var(--shadow-color)] sm:p-4">
23+
<div className="grid gap-3 xl:grid-cols-[minmax(0,1fr)_auto] xl:items-start">
24+
<div className="min-w-0 space-y-1.5">
25+
<div className="flex min-w-0 flex-col gap-1.5 lg:flex-row lg:items-center">
26+
<div className="min-w-0 truncate text-xl font-black text-[var(--text-primary)]">
27+
{snapshot?.currentVersion?.displayName || t('codex_binary.no_active')}
28+
</div>
29+
<div className="flex flex-wrap items-center gap-1.5">
30+
{snapshot?.doctor.severity === 'error' ? <StatusPill severity="error" text={snapshot.doctor.message} /> : null}
31+
{snapshot?.managedConfig ? (
32+
<StatusPill
33+
severity={snapshot.managedConfig.isPathConfigured ? 'ok' : 'warning'}
34+
text={snapshot.managedConfig.isPathConfigured ? t('codex_binary.managed_path_enabled') : t('codex_binary.managed_path_disabled')}
35+
/>
36+
) : null}
37+
</div>
38+
</div>
39+
</div>
40+
41+
<div className="flex flex-wrap items-center gap-2 xl:justify-end">
42+
{snapshot?.managedConfig && !snapshot.managedConfig.isPathConfigured ? (
43+
<button
44+
type="button"
45+
onClick={onEnableManagedPath}
46+
disabled={managedBusy}
47+
className="btn-swiss whitespace-nowrap bg-[var(--text-primary)] !px-2.5 !py-1.5 !text-[0.5625rem] !text-[var(--bg-main)] disabled:cursor-not-allowed disabled:opacity-50"
48+
>
49+
<ShieldCheck className="h-3.5 w-3.5" />
50+
{managedBusy ? t('codex_binary.managing') : t('codex_binary.enable_managed')}
51+
</button>
52+
) : null}
53+
<button
54+
type="button"
55+
onClick={onRefresh}
56+
disabled={loading}
57+
className="btn-swiss whitespace-nowrap !px-2.5 !py-1.5 !text-[0.5625rem] disabled:cursor-not-allowed disabled:opacity-50"
58+
>
59+
<RefreshCw className={`h-3.5 w-3.5 ${loading ? 'animate-spin' : ''}`} />
60+
{t('codex_binary.refresh')}
61+
</button>
62+
</div>
63+
</div>
64+
{snapshot?.managedConfig ? (
65+
<div className="mt-2 grid gap-x-5 gap-y-1 border-t border-[var(--border-color)] pt-2 text-[0.5625rem] font-semibold text-[var(--text-muted)] md:grid-cols-3">
66+
<ManagedMeta label={t('codex_binary.managed_bin_dir')} value={snapshot.managedConfig.binDir} />
67+
<ManagedMeta label={t('codex_binary.resolved_codex_path')} value={snapshot.managedConfig.resolvedCodexPath || t('codex_binary.resolved_codex_missing')} />
68+
<ManagedMeta label={t('codex_binary.managed_profile_target')} value={snapshot.managedConfig.profilePath || t('codex_binary.managed_profile_unknown')} strong />
69+
</div>
70+
) : null}
71+
{message ? <div className="mt-2 border-t border-[var(--border-color)] pt-2 text-xs font-semibold text-[var(--text-muted)]">{message}</div> : null}
72+
</section>
73+
);
74+
}
75+
76+
function StatusPill({ severity, text }: { severity: string; text: string }) {
77+
const color = severity === 'ok' ? 'bg-[var(--accent-green)] text-white' : severity === 'error' ? 'bg-[var(--accent-red)] text-white' : 'bg-[var(--accent-yellow)] text-[var(--text-primary)]';
78+
return <span className={`inline-flex items-center px-2 py-1 text-[0.5625rem] font-black ${color}`}>{text}</span>;
79+
}
80+
81+
function ManagedMeta({ label, value, strong }: { label: string; value: string; strong?: boolean }) {
82+
return (
83+
<div className="min-w-0 truncate">
84+
<span className="font-black uppercase tracking-[0.12em] text-[var(--text-primary)]">{label}: </span>
85+
<span className={strong ? 'text-[var(--text-primary)]' : ''}>{value}</span>
86+
</div>
87+
);
88+
}

0 commit comments

Comments
 (0)