diff --git a/packages/ui/src/components/instance/instance-shell2.tsx b/packages/ui/src/components/instance/instance-shell2.tsx
index d8407b1f..8ac7858d 100644
--- a/packages/ui/src/components/instance/instance-shell2.tsx
+++ b/packages/ui/src/components/instance/instance-shell2.tsx
@@ -36,7 +36,6 @@ import { useI18n } from "../../lib/i18n"
import { getPermissionQueueLength, getQuestionQueueLength } from "../../stores/instances"
import SessionSidebar from "./shell/SessionSidebar"
import { useSessionSidebarRequests } from "./shell/useSessionSidebarRequests"
-import RightPanel from "./shell/right-panel/RightPanel"
import { useDrawerChrome } from "./shell/useDrawerChrome"
import { getSessionStatus } from "../../stores/session-status"
import { Maximize2, ShieldAlert } from "lucide-solid"
@@ -64,6 +63,11 @@ const LazyBackgroundProcessOutputDialog = lazy(() =>
import("../background-process-output-dialog").then((module) => ({ default: module.BackgroundProcessOutputDialog })),
)
const LazyPermissionApprovalModal = lazy(() => import("../permission-approval-modal"))
+const LazyRightPanel = lazy(() => import("./shell/right-panel/RightPanel"))
+
+function RightPanelFallback() {
+ return
+}
interface InstanceShellProps {
instance: Instance
@@ -499,28 +503,30 @@ const InstanceShell2: Component = (props) => {
role="presentation"
aria-hidden="true"
/>
-
+ }>
+
+
)
}
@@ -560,28 +566,32 @@ const InstanceShell2: Component = (props) => {
aria-hidden="true"
/>
-
+ }>
+ }>
+
+
+
)
diff --git a/packages/ui/src/components/instance/shell/useSessionCache.ts b/packages/ui/src/components/instance/shell/useSessionCache.ts
index 0e3a3476..8839d569 100644
--- a/packages/ui/src/components/instance/shell/useSessionCache.ts
+++ b/packages/ui/src/components/instance/shell/useSessionCache.ts
@@ -2,10 +2,30 @@ import { createEffect, createSignal, type Accessor } from "solid-js"
import { messageStoreBus } from "../../../stores/message-v2/bus"
import { clearSessionRenderCache } from "../../message-block"
import { getLogger } from "../../../lib/logger"
+import { runtimeEnv } from "../../../lib/runtime-env"
const log = getLogger("session")
-const SESSION_CACHE_LIMIT = 5
+function getSessionCacheLimit() {
+ if (runtimeEnv.platform === "mobile") {
+ return 2
+ }
+
+ if (runtimeEnv.host === "tauri") {
+ return 3
+ }
+
+ if (typeof navigator !== "undefined") {
+ const deviceMemory = (navigator as Navigator & { deviceMemory?: number }).deviceMemory
+ if (typeof deviceMemory === "number" && deviceMemory <= 4) {
+ return 3
+ }
+ }
+
+ return 5
+}
+
+const SESSION_CACHE_LIMIT = getSessionCacheLimit()
type SessionCacheOptions = {
instanceId: Accessor
diff --git a/packages/ui/src/components/session/session-view.tsx b/packages/ui/src/components/session/session-view.tsx
index ba78382c..b234b09b 100644
--- a/packages/ui/src/components/session/session-view.tsx
+++ b/packages/ui/src/components/session/session-view.tsx
@@ -131,9 +131,15 @@ export const SessionView: Component = (props) => {
createEffect(() => {
const currentSession = session()
- if (currentSession) {
- loadMessages(props.instanceId, currentSession.id).catch((error) => log.error("Failed to load messages", error))
+ if (!currentSession) {
+ return
}
+
+ if (props.isActive === false) {
+ return
+ }
+
+ loadMessages(props.instanceId, currentSession.id).catch((error) => log.error("Failed to load messages", error))
})
function registerPromptInputApi(api: PromptInputApi) {
diff --git a/packages/ui/src/lib/ui-bootstrap-cache.ts b/packages/ui/src/lib/ui-bootstrap-cache.ts
new file mode 100644
index 00000000..422d3887
--- /dev/null
+++ b/packages/ui/src/lib/ui-bootstrap-cache.ts
@@ -0,0 +1,48 @@
+export type UiBootstrapTheme = "light" | "dark" | "system"
+
+export interface UiBootstrapCacheSnapshot {
+ theme?: UiBootstrapTheme
+ locale?: string
+}
+
+const UI_BOOTSTRAP_CACHE_KEY = "codenomad:ui-bootstrap"
+
+export function readUiBootstrapCache(): UiBootstrapCacheSnapshot {
+ if (typeof window === "undefined" || typeof window.localStorage === "undefined") {
+ return {}
+ }
+
+ try {
+ const raw = window.localStorage.getItem(UI_BOOTSTRAP_CACHE_KEY)
+ if (!raw) {
+ return {}
+ }
+
+ const parsed = JSON.parse(raw) as UiBootstrapCacheSnapshot
+ if (!parsed || typeof parsed !== "object") {
+ return {}
+ }
+
+ return {
+ theme:
+ parsed.theme === "light" || parsed.theme === "dark" || parsed.theme === "system"
+ ? parsed.theme
+ : undefined,
+ locale: typeof parsed.locale === "string" ? parsed.locale : undefined,
+ }
+ } catch {
+ return {}
+ }
+}
+
+export function writeUiBootstrapCache(snapshot: UiBootstrapCacheSnapshot) {
+ if (typeof window === "undefined" || typeof window.localStorage === "undefined") {
+ return
+ }
+
+ try {
+ window.localStorage.setItem(UI_BOOTSTRAP_CACHE_KEY, JSON.stringify(snapshot))
+ } catch {
+ /* noop */
+ }
+}
diff --git a/packages/ui/src/main.tsx b/packages/ui/src/main.tsx
index cc6c6f1e..6ddc40e1 100644
--- a/packages/ui/src/main.tsx
+++ b/packages/ui/src/main.tsx
@@ -5,7 +5,7 @@ import { ConfigProvider } from "./stores/preferences"
import { InstanceConfigProvider } from "./stores/instance-config"
import { runtimeEnv } from "./lib/runtime-env"
import { I18nProvider, preloadLocaleMessages } from "./lib/i18n"
-import { storage } from "./lib/storage"
+import { readUiBootstrapCache } from "./lib/ui-bootstrap-cache"
import "./index.css"
import "@git-diff-view/solid/styles/diff-view-pure.css"
@@ -29,22 +29,16 @@ async function bootstrap() {
// (and then refine once persisted config loads).
document.documentElement.removeAttribute("data-theme")
- try {
- const uiConfig = await storage.loadConfigOwner("ui")
- const theme = (uiConfig as any)?.theme ?? "system"
- const locale = (uiConfig as any)?.settings?.locale
+ const bootstrapCache = readUiBootstrapCache()
+ const theme = bootstrapCache.theme ?? "system"
- if (theme === "system") {
- document.documentElement.removeAttribute("data-theme")
- } else {
- document.documentElement.setAttribute("data-theme", theme)
- }
-
- await preloadLocaleMessages(typeof locale === "string" ? locale : undefined)
- } catch {
- // If config fails to load, fall back to CSS defaults.
- await preloadLocaleMessages(undefined)
+ if (theme === "system") {
+ document.documentElement.removeAttribute("data-theme")
+ } else {
+ document.documentElement.setAttribute("data-theme", theme)
}
+
+ await preloadLocaleMessages(bootstrapCache.locale)
}
render(
diff --git a/packages/ui/src/stores/preferences.tsx b/packages/ui/src/stores/preferences.tsx
index 8ac2ead0..442adfca 100644
--- a/packages/ui/src/stores/preferences.tsx
+++ b/packages/ui/src/stores/preferences.tsx
@@ -1,6 +1,7 @@
-import { createContext, createMemo, createSignal, onMount, useContext } from "solid-js"
+import { createContext, createEffect, createMemo, createSignal, onMount, useContext } from "solid-js"
import type { Accessor, ParentComponent } from "solid-js"
import { storage, type OwnerBucket } from "../lib/storage"
+import { writeUiBootstrapCache } from "../lib/ui-bootstrap-cache"
import {
ensureInstanceConfigLoaded,
getInstanceConfig,
@@ -598,6 +599,14 @@ const configContextValue: ConfigContextValue = {
}
export const ConfigProvider: ParentComponent = (props) => {
+ createEffect(() => {
+ const bucket = uiConfigBucket()
+ writeUiBootstrapCache({
+ theme: bucket.theme,
+ locale: bucket.settings?.locale,
+ })
+ })
+
onMount(() => {
ensureLoaded().catch((error: unknown) => {
log.error("Failed to initialize settings", error)