refactor(ui): keep locale bootstrap branch focused

This commit is contained in:
Pascal André
2026-03-19 21:21:00 +01:00
parent 57efe5def3
commit 8c0a82d3a8
4 changed files with 98 additions and 175 deletions

View File

@@ -1,12 +1,15 @@
import { Component, For, Show, Suspense, createMemo, createEffect, createSignal, lazy, onMount, onCleanup } from "solid-js" import { Component, For, Show, createMemo, createEffect, createSignal, onMount, onCleanup } from "solid-js"
import { Dialog } from "@kobalte/core/dialog" import { Dialog } from "@kobalte/core/dialog"
import { Toaster } from "solid-toast" import { Toaster } from "solid-toast"
import useMediaQuery from "@suid/material/useMediaQuery" import useMediaQuery from "@suid/material/useMediaQuery"
import { Minimize2 } from "lucide-solid" import { Minimize2 } from "lucide-solid"
import AlertDialog from "./components/alert-dialog" import AlertDialog from "./components/alert-dialog"
import FolderSelectionView from "./components/folder-selection-view"
import { showConfirmDialog } from "./stores/alerts" import { showConfirmDialog } from "./stores/alerts"
import InstanceTabs from "./components/instance-tabs" import InstanceTabs from "./components/instance-tabs"
import InstanceDisconnectedModal from "./components/instance-disconnected-modal"
import InstanceShell from "./components/instance/instance-shell2" import InstanceShell from "./components/instance/instance-shell2"
import { SettingsScreen } from "./components/settings-screen"
import { InstanceMetadataProvider } from "./lib/contexts/instance-metadata-context" import { InstanceMetadataProvider } from "./lib/contexts/instance-metadata-context"
import { initMarkdown } from "./lib/markdown" import { initMarkdown } from "./lib/markdown"
import { initGithubStars } from "./stores/github-stars" import { initGithubStars } from "./stores/github-stars"
@@ -51,16 +54,10 @@ import {
} from "./stores/sessions" } from "./stores/sessions"
import { getInstanceSessionIndicatorStatus } from "./stores/session-status" import { getInstanceSessionIndicatorStatus } from "./stores/session-status"
import { openSettings, settingsOpen } from "./stores/settings-screen" import { openSettings } from "./stores/settings-screen"
const log = getLogger("actions") const log = getLogger("actions")
const LazyFolderSelectionView = lazy(() => import("./components/folder-selection-view"))
const LazyInstanceDisconnectedModal = lazy(() => import("./components/instance-disconnected-modal"))
const LazySettingsScreen = lazy(() =>
import("./components/settings-screen").then((module) => ({ default: module.SettingsScreen })),
)
const App: Component = () => { const App: Component = () => {
const { isDark } = useTheme() const { isDark } = useTheme()
const { t } = useI18n() const { t } = useI18n()
@@ -412,16 +409,12 @@ const App: Component = () => {
return ( return (
<> <>
<Show when={Boolean(disconnectedInstance())}> <InstanceDisconnectedModal
<Suspense fallback={null}> open={Boolean(disconnectedInstance())}
<LazyInstanceDisconnectedModal folder={disconnectedInstance()?.folder}
open={Boolean(disconnectedInstance())} reason={disconnectedInstance()?.reason}
folder={disconnectedInstance()?.folder} onClose={handleDisconnectedInstanceClose}
reason={disconnectedInstance()?.reason} />
onClose={handleDisconnectedInstanceClose}
/>
</Suspense>
</Show>
<Dialog open={Boolean(launchError())} modal> <Dialog open={Boolean(launchError())} modal>
<Dialog.Portal> <Dialog.Portal>
@@ -534,36 +527,28 @@ const App: Component = () => {
</> </>
} }
> >
<Suspense fallback={<div class="flex-1 min-h-0" />}> <FolderSelectionView
<LazyFolderSelectionView onSelectFolder={handleSelectFolder}
onSelectFolder={handleSelectFolder} isLoading={isSelectingFolder()}
isLoading={isSelectingFolder()} />
/>
</Suspense>
</Show> </Show>
<Show when={showFolderSelection()}> <Show when={showFolderSelection()}>
<div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center"> <div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center">
<div class="w-full h-full relative"> <div class="w-full h-full relative">
<Suspense fallback={<div class="w-full h-full" />}> <FolderSelectionView
<LazyFolderSelectionView onSelectFolder={handleSelectFolder}
onSelectFolder={handleSelectFolder} isLoading={isSelectingFolder()}
isLoading={isSelectingFolder()} onClose={() => {
onClose={() => { setShowFolderSelection(false)
setShowFolderSelection(false) clearLaunchError()
clearLaunchError() }}
}} />
/>
</Suspense>
</div> </div>
</div> </div>
</Show> </Show>
<Show when={settingsOpen()}> <SettingsScreen />
<Suspense fallback={null}>
<LazySettingsScreen />
</Suspense>
</Show>
<AlertDialog /> <AlertDialog />

View File

@@ -1,11 +1,9 @@
import { import {
For, For,
Show, Show,
Suspense,
createEffect, createEffect,
createMemo, createMemo,
createSignal, createSignal,
lazy,
onCleanup, onCleanup,
onMount, onMount,
type Accessor, type Accessor,
@@ -24,7 +22,11 @@ import { keyboardRegistry, type KeyboardShortcut } from "../../lib/keyboard-regi
import { isOpen as isCommandPaletteOpen, hideCommandPalette, showCommandPalette } from "../../stores/command-palette" import { isOpen as isCommandPaletteOpen, hideCommandPalette, showCommandPalette } from "../../stores/command-palette"
import Kbd from "../kbd" import Kbd from "../kbd"
import InstanceWelcomeView from "../instance-welcome-view"
import InfoView from "../info-view"
import CommandPalette from "../command-palette"
import PermissionNotificationBanner from "../permission-notification-banner" import PermissionNotificationBanner from "../permission-notification-banner"
import PermissionApprovalModal from "../permission-approval-modal"
import SessionView from "../session/session-view" import SessionView from "../session/session-view"
import { formatTokenTotal } from "../../lib/formatters" import { formatTokenTotal } from "../../lib/formatters"
import ContextMeter from "../context-meter" import ContextMeter from "../context-meter"
@@ -32,10 +34,12 @@ import { sseManager } from "../../lib/sse-manager"
import { getLogger } from "../../lib/logger" import { getLogger } from "../../lib/logger"
import { serverApi } from "../../lib/api-client" import { serverApi } from "../../lib/api-client"
import { loadBackgroundProcesses } from "../../stores/background-processes" import { loadBackgroundProcesses } from "../../stores/background-processes"
import { BackgroundProcessOutputDialog } from "../background-process-output-dialog"
import { useI18n } from "../../lib/i18n" import { useI18n } from "../../lib/i18n"
import { getPermissionQueueLength, getQuestionQueueLength } from "../../stores/instances" import { getPermissionQueueLength, getQuestionQueueLength } from "../../stores/instances"
import SessionSidebar from "./shell/SessionSidebar" import SessionSidebar from "./shell/SessionSidebar"
import { useSessionSidebarRequests } from "./shell/useSessionSidebarRequests" import { useSessionSidebarRequests } from "./shell/useSessionSidebarRequests"
import RightPanel from "./shell/right-panel/RightPanel"
import { useDrawerChrome } from "./shell/useDrawerChrome" import { useDrawerChrome } from "./shell/useDrawerChrome"
import { getSessionStatus } from "../../stores/session-status" import { getSessionStatus } from "../../stores/session-status"
import { Maximize2, ShieldAlert } from "lucide-solid" import { Maximize2, ShieldAlert } from "lucide-solid"
@@ -56,19 +60,6 @@ import { useInstanceSessionContext } from "./shell/useInstanceSessionContext"
const log = getLogger("session") const log = getLogger("session")
const LazyInstanceWelcomeView = lazy(() => import("../instance-welcome-view"))
const LazyInfoView = lazy(() => import("../info-view"))
const LazyCommandPalette = lazy(() => import("../command-palette"))
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 { interface InstanceShellProps {
instance: Instance instance: Instance
// Provided by App-level instance tabs; lets us pause heavy rendering // Provided by App-level instance tabs; lets us pause heavy rendering
@@ -503,30 +494,28 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
role="presentation" role="presentation"
aria-hidden="true" aria-hidden="true"
/> />
<Suspense fallback={<RightPanelFallback />}> <RightPanel
<LazyRightPanel t={t}
t={t} instanceId={props.instance.id}
instanceId={props.instance.id} instance={props.instance}
instance={props.instance} activeSessionId={activeSessionIdForInstance}
activeSessionId={activeSessionIdForInstance} activeSession={activeSessionForInstance}
activeSession={activeSessionForInstance} activeSessionDiffs={activeSessionDiffs}
activeSessionDiffs={activeSessionDiffs} latestTodoState={latestTodoState}
latestTodoState={latestTodoState} backgroundProcessList={backgroundProcessList}
backgroundProcessList={backgroundProcessList} onOpenBackgroundOutput={openBackgroundOutput}
onOpenBackgroundOutput={openBackgroundOutput} onStopBackgroundProcess={stopBackgroundProcess}
onStopBackgroundProcess={stopBackgroundProcess} onTerminateBackgroundProcess={terminateBackgroundProcess}
onTerminateBackgroundProcess={terminateBackgroundProcess} isPhoneLayout={isPhoneLayout}
isPhoneLayout={isPhoneLayout} rightDrawerWidth={rightDrawerWidth}
rightDrawerWidth={rightDrawerWidth} rightDrawerWidthInitialized={rightDrawerWidthInitialized}
rightDrawerWidthInitialized={rightDrawerWidthInitialized} rightDrawerState={rightDrawerState}
rightDrawerState={rightDrawerState} rightPinned={rightPinned}
rightPinned={rightPinned} onCloseRightDrawer={closeRightDrawer}
onCloseRightDrawer={closeRightDrawer} onPinRightDrawer={pinRightDrawer}
onPinRightDrawer={pinRightDrawer} onUnpinRightDrawer={unpinRightDrawer}
onUnpinRightDrawer={unpinRightDrawer} setContentEl={setRightDrawerContentEl}
setContentEl={setRightDrawerContentEl} />
/>
</Suspense>
</Box> </Box>
) )
} }
@@ -566,32 +555,28 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
aria-hidden="true" aria-hidden="true"
/> />
</Show> </Show>
<Show when={rightOpen() || rightPinned()} fallback={<RightPanelFallback />}> <RightPanel
<Suspense fallback={<RightPanelFallback />}> t={t}
<LazyRightPanel instanceId={props.instance.id}
t={t} instance={props.instance}
instanceId={props.instance.id} activeSessionId={activeSessionIdForInstance}
instance={props.instance} activeSession={activeSessionForInstance}
activeSessionId={activeSessionIdForInstance} activeSessionDiffs={activeSessionDiffs}
activeSession={activeSessionForInstance} latestTodoState={latestTodoState}
activeSessionDiffs={activeSessionDiffs} backgroundProcessList={backgroundProcessList}
latestTodoState={latestTodoState} onOpenBackgroundOutput={openBackgroundOutput}
backgroundProcessList={backgroundProcessList} onStopBackgroundProcess={stopBackgroundProcess}
onOpenBackgroundOutput={openBackgroundOutput} onTerminateBackgroundProcess={terminateBackgroundProcess}
onStopBackgroundProcess={stopBackgroundProcess} isPhoneLayout={isPhoneLayout}
onTerminateBackgroundProcess={terminateBackgroundProcess} rightDrawerWidth={rightDrawerWidth}
isPhoneLayout={isPhoneLayout} rightDrawerWidthInitialized={rightDrawerWidthInitialized}
rightDrawerWidth={rightDrawerWidth} rightDrawerState={rightDrawerState}
rightDrawerWidthInitialized={rightDrawerWidthInitialized} rightPinned={rightPinned}
rightDrawerState={rightDrawerState} onCloseRightDrawer={closeRightDrawer}
rightPinned={rightPinned} onPinRightDrawer={pinRightDrawer}
onCloseRightDrawer={closeRightDrawer} onUnpinRightDrawer={unpinRightDrawer}
onPinRightDrawer={pinRightDrawer} setContentEl={setRightDrawerContentEl}
onUnpinRightDrawer={unpinRightDrawer} />
setContentEl={setRightDrawerContentEl}
/>
</Suspense>
</Show>
</Drawer> </Drawer>
) )
@@ -849,9 +834,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
} }
> >
<div class="info-view-pane flex flex-col flex-1 min-h-0 overflow-y-auto"> <div class="info-view-pane flex flex-col flex-1 min-h-0 overflow-y-auto">
<Suspense fallback={<div class="flex-1 min-h-0" />}> <InfoView instanceId={props.instance.id} />
<LazyInfoView instanceId={props.instance.id} />
</Suspense>
</div> </div>
</Show> </Show>
</Box> </Box>
@@ -867,49 +850,30 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
class="instance-shell2 flex flex-col flex-1 min-h-0" class="instance-shell2 flex flex-col flex-1 min-h-0"
data-instance-id={props.instance.id} data-instance-id={props.instance.id}
> >
<Show <Show when={hasSessions()} fallback={<InstanceWelcomeView instance={props.instance} />}>
when={hasSessions()}
fallback={
<Suspense fallback={<div class="flex-1 min-h-0" />}>
<LazyInstanceWelcomeView instance={props.instance} />
</Suspense>
}
>
{sessionLayout} {sessionLayout}
</Show> </Show>
</div> </div>
<Show when={paletteOpen()}> <CommandPalette
<Suspense fallback={null}> open={paletteOpen()}
<LazyCommandPalette onClose={() => hideCommandPalette(props.instance.id)}
open={paletteOpen()} commands={instancePaletteCommands()}
onClose={() => hideCommandPalette(props.instance.id)} onExecute={props.onExecuteCommand}
commands={instancePaletteCommands()} />
onExecute={props.onExecuteCommand}
/>
</Suspense>
</Show>
<Show when={showBackgroundOutput()}> <BackgroundProcessOutputDialog
<Suspense fallback={null}> open={showBackgroundOutput()}
<LazyBackgroundProcessOutputDialog instanceId={props.instance.id}
open={showBackgroundOutput()} process={selectedBackgroundProcess()}
instanceId={props.instance.id} onClose={closeBackgroundOutput}
process={selectedBackgroundProcess()} />
onClose={closeBackgroundOutput}
/>
</Suspense>
</Show>
<Show when={permissionModalOpen()}> <PermissionApprovalModal
<Suspense fallback={null}> instanceId={props.instance.id}
<LazyPermissionApprovalModal isOpen={permissionModalOpen()}
instanceId={props.instance.id} onClose={() => setPermissionModalOpen(false)}
isOpen={permissionModalOpen()} />
onClose={() => setPermissionModalOpen(false)}
/>
</Suspense>
</Show>
</> </>
) )
} }

View File

@@ -2,30 +2,10 @@ import { createEffect, createSignal, type Accessor } from "solid-js"
import { messageStoreBus } from "../../../stores/message-v2/bus" import { messageStoreBus } from "../../../stores/message-v2/bus"
import { clearSessionRenderCache } from "../../message-block" import { clearSessionRenderCache } from "../../message-block"
import { getLogger } from "../../../lib/logger" import { getLogger } from "../../../lib/logger"
import { runtimeEnv } from "../../../lib/runtime-env"
const log = getLogger("session") const log = getLogger("session")
function getSessionCacheLimit() { const SESSION_CACHE_LIMIT = 5
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 = { type SessionCacheOptions = {
instanceId: Accessor<string> instanceId: Accessor<string>

View File

@@ -131,15 +131,9 @@ export const SessionView: Component<SessionViewProps> = (props) => {
createEffect(() => { createEffect(() => {
const currentSession = session() const currentSession = session()
if (!currentSession) { if (currentSession) {
return loadMessages(props.instanceId, currentSession.id).catch((error) => log.error("Failed to load messages", error))
} }
if (props.isActive === false) {
return
}
loadMessages(props.instanceId, currentSession.id).catch((error) => log.error("Failed to load messages", error))
}) })
function registerPromptInputApi(api: PromptInputApi) { function registerPromptInputApi(api: PromptInputApi) {