fix(ui): permission mode select + remove notification stub (#186, #188) (#190)

- Replace free-text AgentConfig.permissions field with a Select showing
  the four valid modes (default, accept-edits, auto, bypass-permissions)
  so users can't enter invalid strings (#186).
- Delete the non-functional Notifications bell button from Topbar and
  DashboardTopbar; remove the now-unused Bell import (#188).

Closes #186
Closes #188

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Harshit Singh Bhandari 2026-06-11 20:11:52 +05:30 committed by GitHub
parent 9c7866daba
commit a5d034ae1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 10 deletions

View File

@ -1,6 +1,6 @@
import { useQueryClient } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router";
import { Bell, Waypoints } from "lucide-react";
import { Waypoints } from "lucide-react";
import { useState } from "react";
import { findProjectOrchestrator } from "../types/workspace";
import { useWorkspaceQuery, workspaceQueryKey } from "../hooks/useWorkspaceQuery";
@ -90,9 +90,6 @@ export function DashboardTopbar({ activeTab, projectId, projectLabel = "agent-or
</div>
<div className="dashboard-app-header__spacer" />
<div className="dashboard-app-header__actions">
<button aria-label="Notifications" className="dashboard-app-header__icon-btn" style={noDragStyle} type="button">
<Bell className="h-[15px] w-[15px]" aria-hidden="true" />
</button>
{projectId ? (
orchestrator ? (
<button

View File

@ -16,6 +16,13 @@ type ProjectConfig = components["schemas"]["ProjectConfig"];
// default". Kept short — the spawn modal owns the full list.
const AGENT_OPTIONS = ["claude-code", "codex", "opencode", "amp", "goose", "kiro"] as const;
const PERMISSION_MODE_OPTIONS = [
{ value: "default", label: "Default" },
{ value: "accept-edits", label: "Accept edits" },
{ value: "auto", label: "Auto" },
{ value: "bypass-permissions", label: "Bypass permissions" },
] as const;
const projectQueryKey = (id: string) => ["project", id] as const;
export function ProjectSettingsForm({ projectId }: { projectId: string }) {
@ -67,6 +74,7 @@ function SettingsBody({ project, projectId, onSaved }: { project: Project; proje
workerAgent: config.worker?.agent ?? "",
orchestratorAgent: config.orchestrator?.agent ?? "",
model: config.agentConfig?.model ?? "",
permissions: config.agentConfig?.permissions ?? "",
});
const [savedAt, setSavedAt] = useState<number | null>(null);
@ -80,7 +88,11 @@ function SettingsBody({ project, projectId, onSaved }: { project: Project; proje
sessionPrefix: form.sessionPrefix || undefined,
worker: blankToUndefined({ ...config.worker, agent: form.workerAgent || undefined }),
orchestrator: blankToUndefined({ ...config.orchestrator, agent: form.orchestratorAgent || undefined }),
agentConfig: blankToUndefined({ ...config.agentConfig, model: form.model || undefined }),
agentConfig: blankToUndefined({
...config.agentConfig,
model: form.model || undefined,
permissions: form.permissions || undefined,
}),
};
const { error } = await apiClient.PUT("/api/v1/projects/{id}/config", {
params: { path: { id: projectId } },
@ -163,6 +175,12 @@ function SettingsBody({ project, projectId, onSaved }: { project: Project; proje
placeholder="(agent default)"
/>
</Field>
<Field label="Permission mode">
<PermissionModeSelect
value={form.permissions}
onChange={(v) => setForm((f) => ({ ...f, permissions: v }))}
/>
</Field>
</CardContent>
</Card>
@ -183,6 +201,24 @@ function SettingsBody({ project, projectId, onSaved }: { project: Project; proje
);
}
function PermissionModeSelect({ value, onChange }: { value: string; onChange: (value: string) => void }) {
return (
<Select value={value || "__default__"} onValueChange={(v) => onChange(v === "__default__" ? "" : v)}>
<SelectTrigger className="h-8 w-full text-[13px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="__default__">Project default</SelectItem>
{PERMISSION_MODE_OPTIONS.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
function AgentSelect({ value, onChange }: { value: string; onChange: (value: string) => void }) {
// "" sentinel → daemon default; Select can't hold an empty value, so map it.
return (

View File

@ -1,4 +1,4 @@
import { Bell, GitBranch, LayoutGrid, PanelRightClose, PanelRightOpen, Waypoints } from "lucide-react";
import { GitBranch, LayoutGrid, PanelRightClose, PanelRightOpen, Waypoints } from "lucide-react";
import { type WorkbenchView, useUiStore } from "../stores/ui-store";
import type { WorkerDisplayStatus, WorkspaceSession } from "../types/workspace";
import { workerDisplayStatus } from "../types/workspace";
@ -68,10 +68,6 @@ export function Topbar({ view, session, projectLabel, onOpenBoard }: TopbarProps
<div className="dashboard-app-header__spacer" />
<div className="dashboard-app-header__actions">
{/* Bell leads the actions row, as in AO's SessionDetailHeader. */}
<button aria-label="Notifications" className="dashboard-app-header__icon-btn" style={noDragStyle} type="button">
<Bell className="h-[15px] w-[15px]" aria-hidden="true" />
</button>
<button
aria-label={view === "orchestrator" ? "Open Kanban" : "Back to board"}
className="dashboard-app-header__primary-btn"