Skip to content

Commit d085ceb

Browse files
committed
UI: consolidate tab and config panel refresh routing
1 parent 6c33e65 commit d085ceb

2 files changed

Lines changed: 149 additions & 136 deletions

File tree

ui/src/ui/app-render.ts

Lines changed: 88 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,27 @@ export function renderApp(state: AppViewState) {
568568
includeVirtualSections: false,
569569
...overrides,
570570
});
571+
const buildScopedConfigTabOverrides = (params: {
572+
formMode: ConfigProps["formMode"];
573+
searchQuery: string;
574+
selection: ConfigSectionSelection;
575+
setFormMode: (mode: ConfigProps["formMode"]) => void;
576+
setSearchQuery: (query: string) => void;
577+
setActiveSection: (section: string | null) => void;
578+
setActiveSubsection: (section: string | null) => void;
579+
}): ConfigTabOverrides => ({
580+
formMode: params.formMode,
581+
searchQuery: params.searchQuery,
582+
activeSection: params.selection.activeSection,
583+
activeSubsection: params.selection.activeSubsection,
584+
onFormModeChange: params.setFormMode,
585+
onSearchChange: params.setSearchQuery,
586+
onSectionChange: (section) => {
587+
params.setActiveSection(section);
588+
params.setActiveSubsection(null);
589+
},
590+
onSubsectionChange: params.setActiveSubsection,
591+
});
571592
const configSelection = normalizeMainConfigSelection(
572593
state.configActiveSection,
573594
state.configActiveSubsection,
@@ -601,17 +622,15 @@ export function renderApp(state: AppViewState) {
601622
switch (state.tab) {
602623
case "config":
603624
return renderConfigTab({
604-
formMode: state.configFormMode,
605-
searchQuery: state.configSearchQuery,
606-
activeSection: configSelection.activeSection,
607-
activeSubsection: configSelection.activeSubsection,
608-
onFormModeChange: (mode) => (state.configFormMode = mode),
609-
onSearchChange: (query) => (state.configSearchQuery = query),
610-
onSectionChange: (section) => {
611-
state.configActiveSection = section;
612-
state.configActiveSubsection = null;
613-
},
614-
onSubsectionChange: (section) => (state.configActiveSubsection = section),
625+
...buildScopedConfigTabOverrides({
626+
formMode: state.configFormMode,
627+
searchQuery: state.configSearchQuery,
628+
selection: configSelection,
629+
setFormMode: (mode) => (state.configFormMode = mode),
630+
setSearchQuery: (query) => (state.configSearchQuery = query),
631+
setActiveSection: (section) => (state.configActiveSection = section),
632+
setActiveSubsection: (section) => (state.configActiveSubsection = section),
633+
}),
615634
showModeToggle: true,
616635
excludeSections: [
617636
...COMMUNICATION_SECTION_KEYS,
@@ -624,82 +643,72 @@ export function renderApp(state: AppViewState) {
624643
});
625644
case "communications":
626645
return renderConfigTab({
627-
formMode: state.communicationsFormMode,
628-
searchQuery: state.communicationsSearchQuery,
629-
activeSection: communicationsSelection.activeSection,
630-
activeSubsection: communicationsSelection.activeSubsection,
631-
onFormModeChange: (mode) => (state.communicationsFormMode = mode),
632-
onSearchChange: (query) => (state.communicationsSearchQuery = query),
633-
onSectionChange: (section) => {
634-
state.communicationsActiveSection = section;
635-
state.communicationsActiveSubsection = null;
636-
},
637-
onSubsectionChange: (section) => (state.communicationsActiveSubsection = section),
646+
...buildScopedConfigTabOverrides({
647+
formMode: state.communicationsFormMode,
648+
searchQuery: state.communicationsSearchQuery,
649+
selection: communicationsSelection,
650+
setFormMode: (mode) => (state.communicationsFormMode = mode),
651+
setSearchQuery: (query) => (state.communicationsSearchQuery = query),
652+
setActiveSection: (section) => (state.communicationsActiveSection = section),
653+
setActiveSubsection: (section) => (state.communicationsActiveSubsection = section),
654+
}),
638655
navRootLabel: "Communication",
639656
includeSections: [...COMMUNICATION_SECTION_KEYS],
640657
});
641658
case "appearance":
642659
return renderConfigTab({
643-
formMode: state.appearanceFormMode,
644-
searchQuery: state.appearanceSearchQuery,
645-
activeSection: appearanceSelection.activeSection,
646-
activeSubsection: appearanceSelection.activeSubsection,
647-
onFormModeChange: (mode) => (state.appearanceFormMode = mode),
648-
onSearchChange: (query) => (state.appearanceSearchQuery = query),
649-
onSectionChange: (section) => {
650-
state.appearanceActiveSection = section;
651-
state.appearanceActiveSubsection = null;
652-
},
653-
onSubsectionChange: (section) => (state.appearanceActiveSubsection = section),
660+
...buildScopedConfigTabOverrides({
661+
formMode: state.appearanceFormMode,
662+
searchQuery: state.appearanceSearchQuery,
663+
selection: appearanceSelection,
664+
setFormMode: (mode) => (state.appearanceFormMode = mode),
665+
setSearchQuery: (query) => (state.appearanceSearchQuery = query),
666+
setActiveSection: (section) => (state.appearanceActiveSection = section),
667+
setActiveSubsection: (section) => (state.appearanceActiveSubsection = section),
668+
}),
654669
navRootLabel: t("tabs.appearance"),
655670
includeSections: [...APPEARANCE_SECTION_KEYS],
656671
includeVirtualSections: true,
657672
});
658673
case "automation":
659674
return renderConfigTab({
660-
formMode: state.automationFormMode,
661-
searchQuery: state.automationSearchQuery,
662-
activeSection: automationSelection.activeSection,
663-
activeSubsection: automationSelection.activeSubsection,
664-
onFormModeChange: (mode) => (state.automationFormMode = mode),
665-
onSearchChange: (query) => (state.automationSearchQuery = query),
666-
onSectionChange: (section) => {
667-
state.automationActiveSection = section;
668-
state.automationActiveSubsection = null;
669-
},
670-
onSubsectionChange: (section) => (state.automationActiveSubsection = section),
675+
...buildScopedConfigTabOverrides({
676+
formMode: state.automationFormMode,
677+
searchQuery: state.automationSearchQuery,
678+
selection: automationSelection,
679+
setFormMode: (mode) => (state.automationFormMode = mode),
680+
setSearchQuery: (query) => (state.automationSearchQuery = query),
681+
setActiveSection: (section) => (state.automationActiveSection = section),
682+
setActiveSubsection: (section) => (state.automationActiveSubsection = section),
683+
}),
671684
navRootLabel: "Automation",
672685
includeSections: [...AUTOMATION_SECTION_KEYS],
673686
});
674687
case "infrastructure":
675688
return renderConfigTab({
676-
formMode: state.infrastructureFormMode,
677-
searchQuery: state.infrastructureSearchQuery,
678-
activeSection: infrastructureSelection.activeSection,
679-
activeSubsection: infrastructureSelection.activeSubsection,
680-
onFormModeChange: (mode) => (state.infrastructureFormMode = mode),
681-
onSearchChange: (query) => (state.infrastructureSearchQuery = query),
682-
onSectionChange: (section) => {
683-
state.infrastructureActiveSection = section;
684-
state.infrastructureActiveSubsection = null;
685-
},
686-
onSubsectionChange: (section) => (state.infrastructureActiveSubsection = section),
689+
...buildScopedConfigTabOverrides({
690+
formMode: state.infrastructureFormMode,
691+
searchQuery: state.infrastructureSearchQuery,
692+
selection: infrastructureSelection,
693+
setFormMode: (mode) => (state.infrastructureFormMode = mode),
694+
setSearchQuery: (query) => (state.infrastructureSearchQuery = query),
695+
setActiveSection: (section) => (state.infrastructureActiveSection = section),
696+
setActiveSubsection: (section) => (state.infrastructureActiveSubsection = section),
697+
}),
687698
navRootLabel: "Infrastructure",
688699
includeSections: [...INFRASTRUCTURE_SECTION_KEYS],
689700
});
690701
case "aiAgents":
691702
return renderConfigTab({
692-
formMode: state.aiAgentsFormMode,
693-
searchQuery: state.aiAgentsSearchQuery,
694-
activeSection: aiAgentsSelection.activeSection,
695-
activeSubsection: aiAgentsSelection.activeSubsection,
696-
onFormModeChange: (mode) => (state.aiAgentsFormMode = mode),
697-
onSearchChange: (query) => (state.aiAgentsSearchQuery = query),
698-
onSectionChange: (section) => {
699-
state.aiAgentsActiveSection = section;
700-
state.aiAgentsActiveSubsection = null;
701-
},
702-
onSubsectionChange: (section) => (state.aiAgentsActiveSubsection = section),
703+
...buildScopedConfigTabOverrides({
704+
formMode: state.aiAgentsFormMode,
705+
searchQuery: state.aiAgentsSearchQuery,
706+
selection: aiAgentsSelection,
707+
setFormMode: (mode) => (state.aiAgentsFormMode = mode),
708+
setSearchQuery: (query) => (state.aiAgentsSearchQuery = query),
709+
setActiveSection: (section) => (state.aiAgentsActiveSection = section),
710+
setActiveSubsection: (section) => (state.aiAgentsActiveSubsection = section),
711+
}),
703712
navRootLabel: "AI & Agents",
704713
includeSections: [...AI_AGENTS_SECTION_KEYS],
705714
});
@@ -1387,20 +1396,20 @@ export function renderApp(state: AppViewState) {
13871396
},
13881397
onSelectPanel: (panel) => {
13891398
state.agentsPanel = panel;
1390-
if (panel === "files" && resolvedAgentId) {
1391-
if (state.agentFilesList?.agentId !== resolvedAgentId) {
1392-
state.agentFilesList = null;
1393-
state.agentFilesError = null;
1394-
state.agentFileActive = null;
1395-
state.agentFileContents = {};
1396-
state.agentFileDrafts = {};
1397-
void loadAgentFiles(state, resolvedAgentId);
1398-
}
1399+
if (
1400+
panel === "files" &&
1401+
resolvedAgentId &&
1402+
state.agentFilesList?.agentId !== resolvedAgentId
1403+
) {
1404+
state.agentFilesList = null;
1405+
state.agentFilesError = null;
1406+
state.agentFileActive = null;
1407+
state.agentFileContents = {};
1408+
state.agentFileDrafts = {};
1409+
void loadAgentFiles(state, resolvedAgentId);
13991410
}
1400-
if (panel === "skills") {
1401-
if (resolvedAgentId) {
1402-
void loadAgentSkills(state, resolvedAgentId);
1403-
}
1411+
if (panel === "skills" && resolvedAgentId) {
1412+
void loadAgentSkills(state, resolvedAgentId);
14041413
}
14051414
if (panel === "tools" && resolvedAgentId) {
14061415
if (

ui/src/ui/app-settings.ts

Lines changed: 61 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -249,60 +249,73 @@ export function setThemeMode(
249249
);
250250
}
251251

252-
export async function refreshActiveTab(host: SettingsHost) {
253-
const app = host as unknown as OpenClawApp;
254-
switch (host.tab) {
255-
case "overview":
256-
await loadOverview(host);
257-
return;
258-
case "channels":
259-
await loadChannelsTab(host);
260-
return;
261-
case "instances":
262-
await loadPresence(app);
252+
async function refreshAgentsTab(host: SettingsHost, app: OpenClawApp) {
253+
await loadAgents(app);
254+
await loadConfig(app);
255+
const agentIds = host.agentsList?.agents?.map((entry) => entry.id) ?? [];
256+
if (agentIds.length > 0) {
257+
void loadAgentIdentities(app, agentIds);
258+
}
259+
const agentId =
260+
host.agentsSelectedId ?? host.agentsList?.defaultId ?? host.agentsList?.agents?.[0]?.id;
261+
if (!agentId) {
262+
return;
263+
}
264+
void loadAgentIdentity(app, agentId);
265+
switch (host.agentsPanel) {
266+
case "files":
267+
void loadAgentFiles(app, agentId);
263268
return;
264-
case "usage":
265-
await loadUsage(app);
269+
case "skills":
270+
void loadAgentSkills(app, agentId);
266271
return;
267-
case "sessions":
268-
await loadSessions(app);
272+
case "channels":
273+
void loadChannels(app, false);
269274
return;
270275
case "cron":
271-
await loadCron(host);
276+
void loadCron(host);
272277
return;
273-
case "skills":
274-
await loadSkills(app);
278+
default:
279+
return;
280+
}
281+
}
282+
283+
export async function refreshActiveTab(host: SettingsHost) {
284+
const app = host as unknown as OpenClawApp;
285+
if (
286+
(
287+
[
288+
"config",
289+
"communications",
290+
"appearance",
291+
"automation",
292+
"infrastructure",
293+
"aiAgents",
294+
] as Tab[]
295+
).includes(host.tab)
296+
) {
297+
await loadConfigSchema(app);
298+
await loadConfig(app);
299+
return;
300+
}
301+
const simpleTabLoaders: Partial<Record<Tab, () => Promise<void>>> = {
302+
overview: () => loadOverview(host),
303+
channels: () => loadChannelsTab(host),
304+
instances: () => loadPresence(app),
305+
usage: () => loadUsage(app),
306+
sessions: () => loadSessions(app),
307+
cron: () => loadCron(host),
308+
skills: () => loadSkills(app),
309+
};
310+
const simpleTabLoader = simpleTabLoaders[host.tab];
311+
if (simpleTabLoader) {
312+
await simpleTabLoader();
313+
return;
314+
}
315+
switch (host.tab) {
316+
case "agents":
317+
await refreshAgentsTab(host, app);
275318
return;
276-
case "agents": {
277-
await loadAgents(app);
278-
await loadConfig(app);
279-
const agentIds = host.agentsList?.agents?.map((entry) => entry.id) ?? [];
280-
if (agentIds.length > 0) {
281-
void loadAgentIdentities(app, agentIds);
282-
}
283-
const agentId =
284-
host.agentsSelectedId ?? host.agentsList?.defaultId ?? host.agentsList?.agents?.[0]?.id;
285-
if (!agentId) {
286-
return;
287-
}
288-
void loadAgentIdentity(app, agentId);
289-
switch (host.agentsPanel) {
290-
case "files":
291-
void loadAgentFiles(app, agentId);
292-
return;
293-
case "skills":
294-
void loadAgentSkills(app, agentId);
295-
return;
296-
case "channels":
297-
void loadChannels(app, false);
298-
return;
299-
case "cron":
300-
void loadCron(host);
301-
return;
302-
default:
303-
return;
304-
}
305-
}
306319
case "nodes":
307320
await loadNodes(app);
308321
await loadDevices(app);
@@ -320,15 +333,6 @@ export async function refreshActiveTab(host: SettingsHost) {
320333
!host.chatHasAutoScrolled,
321334
);
322335
return;
323-
case "config":
324-
case "communications":
325-
case "appearance":
326-
case "automation":
327-
case "infrastructure":
328-
case "aiAgents":
329-
await loadConfigSchema(app);
330-
await loadConfig(app);
331-
return;
332336
case "debug":
333337
await loadDebug(app);
334338
host.eventLog = host.eventLogBuffer;

0 commit comments

Comments
 (0)