Add room skill setting mutations

This commit is contained in:
ejclaw
2026-05-04 03:23:38 +09:00
parent e71bf3edd7
commit 55c250cbf1
11 changed files with 577 additions and 17 deletions

View File

@@ -171,6 +171,43 @@
font-size: 0.86rem;
}
.runtime-room-skill-toggles {
display: grid;
gap: 8px;
margin-top: 8px;
}
.runtime-room-skill-toggle {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
align-items: center;
gap: 10px;
min-height: 44px;
border-radius: 12px;
background: rgba(15, 23, 42, 0.38);
padding: 8px 10px;
}
.runtime-room-skill-toggle input {
width: 18px;
height: 18px;
}
.runtime-room-skill-toggle > span {
display: grid;
gap: 2px;
min-width: 0;
}
.runtime-room-skill-toggle strong {
color: var(--text);
font-size: 0.84rem;
}
.runtime-room-skill-toggle small {
color: var(--muted);
}
@media (max-width: 980px) {
.runtime-summary-card,
.runtime-skill-grid,

View File

@@ -3,11 +3,13 @@ import { useEffect, useState } from 'react';
import {
fetchRoomSkillSettings,
fetchRuntimeInventory,
updateRoomSkillSetting,
type RuntimeAgentInventory,
type RuntimeInventorySnapshot,
type RuntimePathSnapshot,
type RuntimeSkillDirSnapshot,
type RoomSkillCatalogItem,
type RoomSkillSettingUpdateInput,
type RoomSkillSettingsSnapshot,
} from './api';
import { SettingsSectionHeading } from './SettingsPanelChrome';
@@ -106,8 +108,12 @@ function agentLabel(agentType: 'claude-code' | 'codex') {
}
function RoomSkillPolicyCard({
onToggle,
savingKey,
snapshot,
}: {
onToggle: (input: RoomSkillSettingUpdateInput) => void;
savingKey: string | null;
snapshot: RoomSkillSettingsSnapshot | null;
}) {
if (!snapshot) {
@@ -159,6 +165,7 @@ function RoomSkillPolicyCard({
const disabledNames = agent.disabledSkillIds
.map((id) => catalogById.get(id)?.displayName ?? id)
.slice(0, 3);
const enabledIds = new Set(agent.effectiveEnabledSkillIds);
return (
<div
className="runtime-room-agent-policy"
@@ -177,6 +184,39 @@ function RoomSkillPolicyCard({
{disabledNames.length > 0 ? (
<small>OFF: {disabledNames.join(', ')}</small>
) : null}
<div className="runtime-room-skill-toggles">
{agent.availableSkillIds.map((skillId) => {
const skill = catalogById.get(skillId);
const key = `${room.jid}:${agent.agentType}:${skillId}`;
return (
<label
className="runtime-room-skill-toggle"
key={skillId}
>
<input
checked={enabledIds.has(skillId)}
disabled={savingKey !== null}
onChange={(event) =>
onToggle({
roomJid: room.jid,
agentType: agent.agentType,
skillId,
enabled: event.currentTarget.checked,
})
}
type="checkbox"
/>
<span>
<strong>{skill?.displayName ?? skillId}</strong>
<small>
{skill?.scope ?? 'unknown'}
{savingKey === key ? ' · 저장 중' : ''}
</small>
</span>
</label>
);
})}
</div>
</div>
);
})}
@@ -201,6 +241,10 @@ export function RuntimeInventorySettings() {
const [roomSkillSnapshot, setRoomSkillSnapshot] =
useState<RoomSkillSettingsSnapshot | null>(null);
const [error, setError] = useState<string | null>(null);
const [roomSkillError, setRoomSkillError] = useState<string | null>(null);
const [savingRoomSkillKey, setSavingRoomSkillKey] = useState<string | null>(
null,
);
useEffect(() => {
let cancelled = false;
@@ -220,6 +264,20 @@ export function RuntimeInventorySettings() {
};
}, []);
async function handleRoomSkillToggle(input: RoomSkillSettingUpdateInput) {
const key = `${input.roomJid}:${input.agentType}:${input.skillId}`;
setSavingRoomSkillKey(key);
setRoomSkillError(null);
try {
const next = await updateRoomSkillSetting(input);
setRoomSkillSnapshot(next);
} catch (err) {
setRoomSkillError(err instanceof Error ? err.message : String(err));
} finally {
setSavingRoomSkillKey(null);
}
}
return (
<section
aria-labelledby="settings-runtime-tab"
@@ -255,7 +313,16 @@ export function RuntimeInventorySettings() {
<AgentInventoryCard title="Codex" inventory={snapshot.codex} />
<AgentInventoryCard title="Claude Code" inventory={snapshot.claude} />
<RoomSkillPolicyCard snapshot={roomSkillSnapshot} />
{roomSkillError ? (
<p className="settings-error">{roomSkillError}</p>
) : null}
<RoomSkillPolicyCard
onToggle={(input) => {
void handleRoomSkillToggle(input);
}}
savingKey={savingRoomSkillKey}
snapshot={roomSkillSnapshot}
/>
<article className="runtime-agent-card">
<header className="runtime-card-head">

View File

@@ -473,6 +473,13 @@ export interface RoomSkillSettingsSnapshot {
rooms: RoomSkillPolicyRoom[];
}
export interface RoomSkillSettingUpdateInput {
roomJid: string;
agentType: 'claude-code' | 'codex';
skillId: string;
enabled: boolean;
}
export interface MoaReferenceStatus {
model: string;
checkedAt: string;
@@ -635,6 +642,30 @@ export async function fetchRoomSkillSettings(): Promise<RoomSkillSettingsSnapsho
return fetchJson('/api/settings/room-skills');
}
export async function updateRoomSkillSetting(
input: RoomSkillSettingUpdateInput,
): Promise<RoomSkillSettingsSnapshot> {
const response = await fetch('/api/settings/room-skills', {
method: 'PATCH',
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify(input),
});
if (!response.ok) {
let msg = `update room skill failed: ${response.status}`;
try {
const payload = (await response.json()) as { error?: string };
if (payload.error) msg = payload.error;
} catch {
/* ignore */
}
throw new Error(msg);
}
return (await response.json()) as RoomSkillSettingsSnapshot;
}
export async function updateCodexFeatures(
input: Partial<CodexFeatureSnapshot>,
): Promise<CodexFeatureSnapshot> {