feat: load plugin frontends at runtime and remove plugin ids from the shell

This commit is contained in:
LukeGus committed 2026-09-22 20:15:52 -05:00
1 parent 49f0286a14
commit e88d5a52e3
721 files changed
+54265 -44416

No files matched your search

+12 -110
View File
@@ -74,9 +74,8 @@ import {
User,
X,
} from "lucide-react";
import { readHiddenRailTabs } from "@/sidebar/hidden-rail-tabs";
import { SettingRow, FakeSwitch } from "@/components/section-card";
import { visibleRailItems } from "./rail-items";
import { NavigationVisibilityToggles } from "./NavigationVisibilityToggles";
import { InterfacePresetSettings } from "./InterfacePresetSettings";
import { useUiPreferencesContext } from "@/contexts/UiPreferencesContext";
import { KeybindingsDialog } from "./KeybindingsDialog";
@@ -693,57 +692,12 @@ export function UserProfilePanel({
return v !== null ? v === "true" : true;
});
const [keybindingsDialogOpen, setKeybindingsDialogOpen] = useState(false);
const [aiAssistantEnabled, setAiAssistantEnabled] = useState(false);
const [aiReadOnlyCommands, setAiReadOnlyCommands] = useState(false);
// Null until the status call answers; the whole section stays hidden while
// unknown so a user who cannot have the feature never sees it mentioned.
const [aiGloballyEnabled, setAiGloballyEnabled] = useState<boolean | null>(
null,
);
// Sidebar display customization (density, tags, tray trigger, status
// colors) now lives in the dedicated Customize Sidebar panel opened from
// the Hosts toolbar, not here -- resetToDefaults still needs write access.
const { update: updateSidebarPrefs } = useHostSidebarPreferences();
const uiPrefs = useUiPreferencesContext();
useEffect(() => {
let cancelled = false;
import("@/api/ai-api")
.then(({ getAiStatus }) => getAiStatus())
.then((status) => {
if (cancelled) return;
setAiGloballyEnabled(status.globallyEnabled);
setAiAssistantEnabled(status.enabled);
setAiReadOnlyCommands(status.allowReadOnlyCommands);
})
.catch(() => {
if (!cancelled) setAiGloballyEnabled(false);
});
return () => {
cancelled = true;
};
}, []);
/**
* Turning the assistant off also hides its rail tab, so declining removes
* the feature from view entirely rather than leaving an inert entry behind.
*/
const applyAiEnabled = (enabled: boolean) => {
setAiAssistantEnabled(enabled);
const hidden = readHiddenRailTabs();
if (enabled) hidden.delete("ai");
else hidden.add("ai");
const serialized = JSON.stringify([...hidden]);
localStorage.setItem("hiddenRailTabs", serialized);
setHiddenRailTabs(hidden);
window.dispatchEvent(new Event("hiddenRailTabsChanged"));
if (storageMode === "cloud") {
saveToCloud({ aiAssistantEnabled: enabled, hiddenRailTabs: serialized });
}
};
const [pinAppRail, setPinAppRail] = useState(() =>
readRailPreference("pinAppRail"),
);
@@ -947,12 +901,6 @@ export function UserProfilePanel({
String(prefs.commandPaletteEnabled),
);
}
if (prefs.aiAssistantEnabled != null) {
setAiAssistantEnabled(prefs.aiAssistantEnabled);
}
if (prefs.aiReadOnlyCommands != null) {
setAiReadOnlyCommands(prefs.aiReadOnlyCommands);
}
// showHostTags/hostTrayOnClick/compactHostView/statusColorScheme are
// no longer restored here -- useHostSidebarPreferences independently
// fetches its own authoritative copy from /host-sidebar/preferences.
@@ -2082,34 +2030,6 @@ export function UserProfilePanel({
}}
/>
</SettingRow>
{aiGloballyEnabled && (
<>
<SettingRow
label={t("ai.enableTitle")}
description={t("ai.enableDescription")}
>
<FakeSwitch
checked={aiAssistantEnabled}
onChange={applyAiEnabled}
/>
</SettingRow>
{aiAssistantEnabled && (
<SettingRow
label={t("ai.readOnlyCommands")}
description={t("ai.readOnlyCommandsDescription")}
>
<FakeSwitch
checked={aiReadOnlyCommands}
onChange={(v) => {
setAiReadOnlyCommands(v);
if (storageMode === "cloud")
saveToCloud({ aiReadOnlyCommands: v });
}}
/>
</SettingRow>
)}
</>
)}
<SettingRow
label={t("newUi.sidebar.userProfile.reopenTabsOnLogin")}
description={t("newUi.sidebar.userProfile.reopenTabsOnLoginDesc")}
@@ -2211,35 +2131,17 @@ export function UserProfilePanel({
<p className="text-[10px] text-muted-foreground mb-2">
{t("newUi.sidebar.userProfile.navigationTabsDesc")}
</p>
{visibleRailItems()
.filter((tab) => tab.id !== "ai" || aiGloballyEnabled)
.map((tab) => (
<div
key={tab.id}
className="flex items-center justify-between py-1.5"
>
<span className="flex items-center gap-1.5 text-xs font-medium text-foreground">
<span className="text-muted-foreground">
<tab.icon size={12} />
</span>
{t(tab.labelKey)}
</span>
<FakeSwitch
checked={!hiddenRailTabs.has(tab.id)}
onChange={(visible) => {
const next = new Set(hiddenRailTabs);
if (visible) next.delete(tab.id);
else next.add(tab.id);
setHiddenRailTabs(next);
const serialized = JSON.stringify([...next]);
localStorage.setItem("hiddenRailTabs", serialized);
window.dispatchEvent(new Event("hiddenRailTabsChanged"));
if (storageMode === "cloud")
saveToCloud({ hiddenRailTabs: serialized });
}}
/>
</div>
))}
<NavigationVisibilityToggles
hidden={hiddenRailTabs}
onChange={(next) => {
setHiddenRailTabs(next);
const serialized = JSON.stringify([...next]);
localStorage.setItem("hiddenRailTabs", serialized);
window.dispatchEvent(new Event("hiddenRailTabsChanged"));
if (storageMode === "cloud")
saveToCloud({ hiddenRailTabs: serialized });
}}
/>
</div>
<div className="flex flex-col gap-1 border-t border-border pt-3">