-
+ {inlineApp && (
+
+ )}
+
{folderLayout !== "sidebar" && (
@@ -433,7 +444,13 @@ export default function FilesPage() {
{isMobile && (
-
+
)}
@@ -457,6 +474,7 @@ export default function FilesPage() {
/>
)}
+
);
diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx
index 11e14c39..4d76c768 100644
--- a/app/[locale]/page.tsx
+++ b/app/[locale]/page.tsx
@@ -35,6 +35,9 @@ import { DragDropProvider } from "@/contexts/drag-drop-context";
import { isFilterEmpty, activeFilterCount } from "@/lib/jmap/search-utils";
import { WelcomeBanner } from "@/components/ui/welcome-banner";
import { NavigationRail } from "@/components/layout/navigation-rail";
+import { SidebarAppsModal } from "@/components/layout/sidebar-apps-modal";
+import { InlineAppView } from "@/components/layout/inline-app-view";
+import { useSidebarApps } from "@/hooks/use-sidebar-apps";
import { Input } from "@/components/ui/input";
import { FilePreviewModal } from "@/components/files/file-preview-modal";
import { isFilePreviewable } from "@/lib/file-preview";
@@ -53,6 +56,7 @@ export default function Home() {
const [composerDraftText, setComposerDraftText] = useState("");
const [pendingDraft, setPendingDraft] = useState
(null);
const { dialogProps: confirmDialogProps, confirm: confirmDialog } = useConfirmDialog();
+ const { showAppsModal, inlineApp, loadedApps, handleManageApps, handleInlineApp, closeInlineApp, closeAppsModal } = useSidebarApps();
const [initialCheckDone, setInitialCheckDone] = useState(() => useAuthStore.getState().isAuthenticated && !!useAuthStore.getState().client);
const [showShortcutsModal, setShowShortcutsModal] = useState(false);
const [showAdvancedFields, setShowAdvancedFields] = useState(false);
@@ -1007,12 +1011,20 @@ export default function Home() {
isPushConnected={isPushConnected}
onLogout={handleLogout}
onShowShortcuts={() => setShowShortcutsModal(true)}
+ onManageApps={handleManageApps}
+ onInlineApp={handleInlineApp}
+ onCloseInlineApp={closeInlineApp}
+ activeAppId={inlineApp?.id ?? null}
/>
)}
+ {inlineApp && (
+
+ )}
+
{/* Mobile/Tablet Sidebar Overlay Backdrop */}
- {(isMobile || isTablet) && sidebarOpen && (
+ {(isMobile || isTablet) && sidebarOpen && !inlineApp && (
setSidebarOpen(false)}
@@ -1029,7 +1041,8 @@ export default function Home() {
"max-lg:transform max-lg:transition-transform max-lg:duration-300 max-lg:ease-in-out",
!sidebarOpen && "max-lg:-translate-x-full",
// Desktop: normal flow
- "lg:relative lg:translate-x-0"
+ "lg:relative lg:translate-x-0",
+ inlineApp && "hidden"
)}
style={!isMobile && !isTablet ? { width: sidebarCollapsed ? 64 : sidebarWidth } : undefined}
>
@@ -1055,7 +1068,7 @@ export default function Home() {
{/* Sidebar resize handle (desktop only, hidden when collapsed) */}
- {!isMobile && !isTablet && !sidebarCollapsed && (
+ {!isMobile && !isTablet && !sidebarCollapsed && !inlineApp && (
{ dragStartWidth.current = sidebarWidth; setIsResizing(true); }}
onResize={(delta) => setSidebarWidth(dragStartWidth.current + delta)}
@@ -1065,7 +1078,7 @@ export default function Home() {
)}
{/* Main Content Area */}
-
+
{/* Email List - full width on mobile, fixed width on tablet/desktop */}
+
)}
@@ -1575,6 +1594,7 @@ export default function Home() {
{/* Screen reader live region for dynamic status announcements */}
+
diff --git a/app/[locale]/settings/page.tsx b/app/[locale]/settings/page.tsx
index 4a682019..66a3e9a0 100644
--- a/app/[locale]/settings/page.tsx
+++ b/app/[locale]/settings/page.tsx
@@ -23,6 +23,7 @@ import {
Wrench,
BookUser,
KeyRound,
+ PanelLeftClose,
type LucideIcon,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
@@ -42,15 +43,19 @@ import { AccountSecuritySettings } from '@/components/settings/account-security-
import { FilesSettingsComponent } from '@/components/settings/files-settings';
import { ContactsSettings } from '@/components/settings/contacts-settings';
import { SmimeSettings } from '@/components/settings/smime-settings';
+import { SidebarAppsSettings } from '@/components/settings/sidebar-apps-settings';
import { useAuthStore } from '@/stores/auth-store';
import { useEmailStore } from '@/stores/email-store';
import { useIsDesktop } from '@/hooks/use-media-query';
import { NavigationRail } from '@/components/layout/navigation-rail';
+import { SidebarAppsModal } from '@/components/layout/sidebar-apps-modal';
+import { InlineAppView } from '@/components/layout/inline-app-view';
+import { useSidebarApps } from '@/hooks/use-sidebar-apps';
import { ResizeHandle } from '@/components/layout/resize-handle';
import { useConfig } from '@/hooks/use-config';
import { cn } from '@/lib/utils';
-type Tab = 'appearance' | 'email' | 'account' | 'security' | 'identities' | 'encryption' | 'vacation' | 'calendar' | 'contacts' | 'filters' | 'templates' | 'folders' | 'keywords' | 'files' | 'advanced';
+type Tab = 'appearance' | 'email' | 'account' | 'security' | 'identities' | 'encryption' | 'vacation' | 'calendar' | 'contacts' | 'filters' | 'templates' | 'folders' | 'keywords' | 'files' | 'sidebar_apps' | 'advanced';
type TabGroup = 'general' | 'account' | 'organization' | 'apps' | 'system';
interface TabDef {
@@ -75,6 +80,7 @@ const tabIcons: Record = {
folders: FolderOpen,
keywords: Tags,
files: HardDrive,
+ sidebar_apps: PanelLeftClose,
advanced: Wrench,
};
@@ -85,6 +91,7 @@ export default function SettingsPage() {
const t = useTranslations('settings');
const tSidebar = useTranslations('sidebar');
const { client, isAuthenticated, logout, checkAuth, isLoading: authLoading } = useAuthStore();
+ const { showAppsModal, inlineApp, loadedApps, handleManageApps, handleInlineApp, closeInlineApp, closeAppsModal } = useSidebarApps();
const [initialCheckDone, setInitialCheckDone] = useState(() => useAuthStore.getState().isAuthenticated && !!useAuthStore.getState().client);
const { quota, isPushConnected } = useEmailStore();
const { stalwartFeaturesEnabled } = useConfig();
@@ -143,6 +150,7 @@ export default function SettingsPage() {
...(supportsCalendar ? [{ id: 'calendar' as Tab, label: t('tabs.calendar'), icon: tabIcons.calendar, group: 'apps' as TabGroup }] : []),
{ id: 'contacts', label: t('tabs.contacts'), icon: tabIcons.contacts, group: 'apps' },
...(supportsFiles ? [{ id: 'files' as Tab, label: t('tabs.files'), icon: tabIcons.files, group: 'apps' as TabGroup }] : []),
+ { id: 'sidebar_apps', label: t('tabs.sidebar_apps'), icon: tabIcons.sidebar_apps, group: 'apps' },
{ id: 'advanced', label: t('tabs.advanced'), icon: tabIcons.advanced, group: 'system' },
];
@@ -181,6 +189,7 @@ export default function SettingsPage() {
{activeTab === 'folders' && }
{activeTab === 'keywords' && }
{activeTab === 'files' && }
+ {activeTab === 'sidebar_apps' && }
{activeTab === 'advanced' && }
>
);
@@ -212,7 +221,14 @@ export default function SettingsPage() {
{/* Bottom Navigation */}
-