fix(mi,canvas): filter disabled modules from MI assistant and eliminate app-switcher flash
MI now loads space doc to filter module list, capabilities, and fallback by enabledModules. Canvas fetches /api/modules and space modules in parallel via Promise.all, calling setModules once with filtered list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ac028cbe04
commit
8d4e1fd0ff
|
|
@ -13,6 +13,7 @@ import type { MiMessage } from "./mi-provider";
|
||||||
import { getModuleInfoList, getAllModules } from "../shared/module";
|
import { getModuleInfoList, getAllModules } from "../shared/module";
|
||||||
import { resolveCallerRole, roleAtLeast } from "./spaces";
|
import { resolveCallerRole, roleAtLeast } from "./spaces";
|
||||||
import type { SpaceRoleString } from "./spaces";
|
import type { SpaceRoleString } from "./spaces";
|
||||||
|
import { loadCommunity, getDocumentData } from "./community-store";
|
||||||
import { verifyToken, extractToken } from "./auth";
|
import { verifyToken, extractToken } from "./auth";
|
||||||
import type { EncryptIDClaims } from "./auth";
|
import type { EncryptIDClaims } from "./auth";
|
||||||
import { buildModuleCapabilities, MODULE_ROUTES } from "../lib/mi-module-routes";
|
import { buildModuleCapabilities, MODULE_ROUTES } from "../lib/mi-module-routes";
|
||||||
|
|
@ -58,6 +59,14 @@ mi.post("/ask", async (c) => {
|
||||||
callerRole = "member";
|
callerRole = "member";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Resolve space's enabled modules ──
|
||||||
|
let enabledModuleIds: string[] | null = null;
|
||||||
|
if (space) {
|
||||||
|
await loadCommunity(space);
|
||||||
|
const spaceDoc = getDocumentData(space);
|
||||||
|
enabledModuleIds = spaceDoc?.meta?.enabledModules ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Resolve model ──
|
// ── Resolve model ──
|
||||||
const modelId = requestedModel || miRegistry.getDefaultModel();
|
const modelId = requestedModel || miRegistry.getDefaultModel();
|
||||||
let providerInfo = miRegistry.resolveModel(modelId);
|
let providerInfo = miRegistry.resolveModel(modelId);
|
||||||
|
|
@ -75,7 +84,11 @@ mi.post("/ask", async (c) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Build system prompt ──
|
// ── Build system prompt ──
|
||||||
const moduleList = getModuleInfoList()
|
const allModuleInfo = getModuleInfoList();
|
||||||
|
const filteredModuleInfo = enabledModuleIds
|
||||||
|
? allModuleInfo.filter(m => m.id === "rspace" || enabledModuleIds!.includes(m.id))
|
||||||
|
: allModuleInfo;
|
||||||
|
const moduleList = filteredModuleInfo
|
||||||
.map((m) => `- **${m.name}** (${m.id}): ${m.icon} ${m.description}`)
|
.map((m) => `- **${m.name}** (${m.id}): ${m.icon} ${m.description}`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
|
|
@ -123,8 +136,10 @@ mi.post("/ask", async (c) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Module capabilities for enabled modules
|
// Module capabilities for enabled modules
|
||||||
const enabledModuleIds = Object.keys(MODULE_ROUTES);
|
const capabilityModuleIds = enabledModuleIds
|
||||||
const moduleCapabilities = buildModuleCapabilities(enabledModuleIds);
|
? Object.keys(MODULE_ROUTES).filter(id => enabledModuleIds!.includes(id))
|
||||||
|
: Object.keys(MODULE_ROUTES);
|
||||||
|
const moduleCapabilities = buildModuleCapabilities(capabilityModuleIds);
|
||||||
|
|
||||||
// Role-permission mapping
|
// Role-permission mapping
|
||||||
const rolePermissions: Record<SpaceRoleString, string> = {
|
const rolePermissions: Record<SpaceRoleString, string> = {
|
||||||
|
|
@ -285,7 +300,7 @@ Use requireConfirm:true for destructive batches.`;
|
||||||
});
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("mi: Provider error:", e.message);
|
console.error("mi: Provider error:", e.message);
|
||||||
const fallback = generateFallbackResponse(query, currentModule, space, getModuleInfoList());
|
const fallback = generateFallbackResponse(query, currentModule, space, filteredModuleInfo);
|
||||||
return c.json({ response: fallback });
|
return c.json({ response: fallback });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2534,36 +2534,32 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load module list for app switcher and tab bar + menu
|
// Load module list for app switcher and tab bar + menu
|
||||||
|
// Parallel fetch: modules + space-specific filter — setModules called once
|
||||||
let moduleList = [];
|
let moduleList = [];
|
||||||
fetch("/api/modules").then(r => r.json()).then(data => {
|
const spaceSlug = window.location.pathname.split("/").filter(Boolean)[0] || "demo";
|
||||||
|
Promise.all([
|
||||||
|
fetch("/api/modules").then(r => r.json()),
|
||||||
|
fetch(`/api/spaces/${encodeURIComponent(spaceSlug)}/modules`).then(r => r.ok ? r.json() : null),
|
||||||
|
]).then(([data, spaceData]) => {
|
||||||
moduleList = data.modules || [];
|
moduleList = data.modules || [];
|
||||||
window.__rspaceAllModules = moduleList;
|
window.__rspaceAllModules = moduleList;
|
||||||
document.querySelector("rstack-app-switcher")?.setModules(moduleList);
|
|
||||||
const tb = document.querySelector("rstack-tab-bar");
|
|
||||||
if (tb) tb.setModules(moduleList);
|
|
||||||
|
|
||||||
// Fetch space-specific enabled modules and apply filtering
|
const enabledIds = spaceData?.enabledModules ?? null;
|
||||||
const spaceSlug = window.location.pathname.split("/").filter(Boolean)[0] || "demo";
|
window.__rspaceEnabledModules = enabledIds;
|
||||||
fetch(`/api/spaces/${encodeURIComponent(spaceSlug)}/modules`)
|
|
||||||
.then(r => r.ok ? r.json() : null)
|
const visible = enabledIds
|
||||||
.then(spaceData => {
|
? moduleList.filter(m => m.id === "rspace" || new Set(enabledIds).has(m.id))
|
||||||
if (!spaceData) return;
|
: moduleList;
|
||||||
const enabledIds = spaceData.enabledModules; // null = all
|
|
||||||
window.__rspaceEnabledModules = enabledIds;
|
document.querySelector("rstack-app-switcher")?.setModules(visible);
|
||||||
if (enabledIds) {
|
const tb = document.querySelector("rstack-tab-bar");
|
||||||
const enabledSet = new Set(enabledIds);
|
if (tb) tb.setModules(visible);
|
||||||
const filtered = moduleList.filter(m => m.id === "rspace" || enabledSet.has(m.id));
|
|
||||||
document.querySelector("rstack-app-switcher")?.setModules(filtered);
|
// Initialize folk-rapp filtering
|
||||||
const tb2 = document.querySelector("rstack-tab-bar");
|
customElements.whenDefined("folk-rapp").then(() => {
|
||||||
if (tb2) tb2.setModules(filtered);
|
const FolkRApp = customElements.get("folk-rapp");
|
||||||
}
|
if (FolkRApp?.setEnabledModules) FolkRApp.setEnabledModules(enabledIds);
|
||||||
// Initialize folk-rapp filtering
|
});
|
||||||
customElements.whenDefined("folk-rapp").then(() => {
|
|
||||||
const FolkRApp = customElements.get("folk-rapp");
|
|
||||||
if (FolkRApp?.setEnabledModules) FolkRApp.setEnabledModules(enabledIds);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
|
||||||
// React to runtime module toggling from app switcher
|
// React to runtime module toggling from app switcher
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue