perf(ui): trim hidden session and bootstrap work
This commit is contained in:
@@ -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 <div class="flex-1 min-h-0" />
|
||||
}
|
||||
|
||||
interface InstanceShellProps {
|
||||
instance: Instance
|
||||
@@ -499,28 +503,30 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<RightPanel
|
||||
t={t}
|
||||
instanceId={props.instance.id}
|
||||
instance={props.instance}
|
||||
activeSessionId={activeSessionIdForInstance}
|
||||
activeSession={activeSessionForInstance}
|
||||
activeSessionDiffs={activeSessionDiffs}
|
||||
latestTodoState={latestTodoState}
|
||||
backgroundProcessList={backgroundProcessList}
|
||||
onOpenBackgroundOutput={openBackgroundOutput}
|
||||
onStopBackgroundProcess={stopBackgroundProcess}
|
||||
onTerminateBackgroundProcess={terminateBackgroundProcess}
|
||||
isPhoneLayout={isPhoneLayout}
|
||||
rightDrawerWidth={rightDrawerWidth}
|
||||
rightDrawerWidthInitialized={rightDrawerWidthInitialized}
|
||||
rightDrawerState={rightDrawerState}
|
||||
rightPinned={rightPinned}
|
||||
onCloseRightDrawer={closeRightDrawer}
|
||||
onPinRightDrawer={pinRightDrawer}
|
||||
onUnpinRightDrawer={unpinRightDrawer}
|
||||
setContentEl={setRightDrawerContentEl}
|
||||
/>
|
||||
<Suspense fallback={<RightPanelFallback />}>
|
||||
<LazyRightPanel
|
||||
t={t}
|
||||
instanceId={props.instance.id}
|
||||
instance={props.instance}
|
||||
activeSessionId={activeSessionIdForInstance}
|
||||
activeSession={activeSessionForInstance}
|
||||
activeSessionDiffs={activeSessionDiffs}
|
||||
latestTodoState={latestTodoState}
|
||||
backgroundProcessList={backgroundProcessList}
|
||||
onOpenBackgroundOutput={openBackgroundOutput}
|
||||
onStopBackgroundProcess={stopBackgroundProcess}
|
||||
onTerminateBackgroundProcess={terminateBackgroundProcess}
|
||||
isPhoneLayout={isPhoneLayout}
|
||||
rightDrawerWidth={rightDrawerWidth}
|
||||
rightDrawerWidthInitialized={rightDrawerWidthInitialized}
|
||||
rightDrawerState={rightDrawerState}
|
||||
rightPinned={rightPinned}
|
||||
onCloseRightDrawer={closeRightDrawer}
|
||||
onPinRightDrawer={pinRightDrawer}
|
||||
onUnpinRightDrawer={unpinRightDrawer}
|
||||
setContentEl={setRightDrawerContentEl}
|
||||
/>
|
||||
</Suspense>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -560,28 +566,32 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Show>
|
||||
<RightPanel
|
||||
t={t}
|
||||
instanceId={props.instance.id}
|
||||
instance={props.instance}
|
||||
activeSessionId={activeSessionIdForInstance}
|
||||
activeSession={activeSessionForInstance}
|
||||
activeSessionDiffs={activeSessionDiffs}
|
||||
latestTodoState={latestTodoState}
|
||||
backgroundProcessList={backgroundProcessList}
|
||||
onOpenBackgroundOutput={openBackgroundOutput}
|
||||
onStopBackgroundProcess={stopBackgroundProcess}
|
||||
onTerminateBackgroundProcess={terminateBackgroundProcess}
|
||||
isPhoneLayout={isPhoneLayout}
|
||||
rightDrawerWidth={rightDrawerWidth}
|
||||
rightDrawerWidthInitialized={rightDrawerWidthInitialized}
|
||||
rightDrawerState={rightDrawerState}
|
||||
rightPinned={rightPinned}
|
||||
onCloseRightDrawer={closeRightDrawer}
|
||||
onPinRightDrawer={pinRightDrawer}
|
||||
onUnpinRightDrawer={unpinRightDrawer}
|
||||
setContentEl={setRightDrawerContentEl}
|
||||
/>
|
||||
<Show when={rightOpen() || rightPinned()} fallback={<RightPanelFallback />}>
|
||||
<Suspense fallback={<RightPanelFallback />}>
|
||||
<LazyRightPanel
|
||||
t={t}
|
||||
instanceId={props.instance.id}
|
||||
instance={props.instance}
|
||||
activeSessionId={activeSessionIdForInstance}
|
||||
activeSession={activeSessionForInstance}
|
||||
activeSessionDiffs={activeSessionDiffs}
|
||||
latestTodoState={latestTodoState}
|
||||
backgroundProcessList={backgroundProcessList}
|
||||
onOpenBackgroundOutput={openBackgroundOutput}
|
||||
onStopBackgroundProcess={stopBackgroundProcess}
|
||||
onTerminateBackgroundProcess={terminateBackgroundProcess}
|
||||
isPhoneLayout={isPhoneLayout}
|
||||
rightDrawerWidth={rightDrawerWidth}
|
||||
rightDrawerWidthInitialized={rightDrawerWidthInitialized}
|
||||
rightDrawerState={rightDrawerState}
|
||||
rightPinned={rightPinned}
|
||||
onCloseRightDrawer={closeRightDrawer}
|
||||
onPinRightDrawer={pinRightDrawer}
|
||||
onUnpinRightDrawer={unpinRightDrawer}
|
||||
setContentEl={setRightDrawerContentEl}
|
||||
/>
|
||||
</Suspense>
|
||||
</Show>
|
||||
</Drawer>
|
||||
|
||||
)
|
||||
|
||||
@@ -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<string>
|
||||
|
||||
@@ -131,9 +131,15 @@ export const SessionView: Component<SessionViewProps> = (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) {
|
||||
|
||||
48
packages/ui/src/lib/ui-bootstrap-cache.ts
Normal file
48
packages/ui/src/lib/ui-bootstrap-cache.ts
Normal file
@@ -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 */
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user