chore: cleanup (#14181)

This commit is contained in:
Adam
2026-02-18 13:23:20 -06:00
committed by GitHub
parent c6bd320003
commit 42aa28d512
7 changed files with 63 additions and 78 deletions

View File

@@ -403,15 +403,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const [composing, setComposing] = createSignal(false) const [composing, setComposing] = createSignal(false)
const isImeComposing = (event: KeyboardEvent) => event.isComposing || composing() || event.keyCode === 229 const isImeComposing = (event: KeyboardEvent) => event.isComposing || composing() || event.keyCode === 229
createEffect(() => { const handleBlur = () => {
if (!isFocused()) closePopover() closePopover()
}) setComposing(false)
}
// Safety: reset composing state on focus change to prevent stuck state
// This handles edge cases where compositionend event may not fire
createEffect(() => {
if (!isFocused()) setComposing(false)
})
const agentList = createMemo(() => const agentList = createMemo(() =>
sync.data.agent sync.data.agent
@@ -1118,6 +1113,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
onPaste={handlePaste} onPaste={handlePaste}
onCompositionStart={() => setComposing(true)} onCompositionStart={() => setComposing(true)}
onCompositionEnd={() => setComposing(false)} onCompositionEnd={() => setComposing(false)}
onBlur={handleBlur}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
classList={{ classList={{
"select-text": true, "select-text": true,

View File

@@ -257,27 +257,12 @@ export function SessionHeader() {
] as const ] as const
}) })
const checksReady = createMemo(() => {
if (platform.platform !== "desktop") return true
if (!platform.checkAppExists) return true
const list = apps()
return list.every((app) => exists[app.id] !== undefined)
})
const [prefs, setPrefs] = persisted(Persist.global("open.app"), createStore({ app: "finder" as OpenApp })) const [prefs, setPrefs] = persisted(Persist.global("open.app"), createStore({ app: "finder" as OpenApp }))
const [menu, setMenu] = createStore({ open: false }) const [menu, setMenu] = createStore({ open: false })
const canOpen = createMemo(() => platform.platform === "desktop" && !!platform.openPath && server.isLocal()) const canOpen = createMemo(() => platform.platform === "desktop" && !!platform.openPath && server.isLocal())
const current = createMemo(() => options().find((o) => o.id === prefs.app) ?? options()[0]) const current = createMemo(() => options().find((o) => o.id === prefs.app) ?? options()[0])
createEffect(() => {
if (platform.platform !== "desktop") return
if (!checksReady()) return
const value = prefs.app
if (options().some((o) => o.id === value)) return
setPrefs("app", options()[0]?.id ?? "finder")
})
const openDir = (app: OpenApp) => { const openDir = (app: OpenApp) => {
const directory = projectDirectory() const directory = projectDirectory()
if (!directory) return if (!directory) return
@@ -398,7 +383,7 @@ export function SessionHeader() {
<DropdownMenu.Group> <DropdownMenu.Group>
<DropdownMenu.GroupLabel>{language.t("session.header.openIn")}</DropdownMenu.GroupLabel> <DropdownMenu.GroupLabel>{language.t("session.header.openIn")}</DropdownMenu.GroupLabel>
<DropdownMenu.RadioGroup <DropdownMenu.RadioGroup
value={prefs.app} value={current().id}
onChange={(value) => { onChange={(value) => {
if (!OPEN_APPS.includes(value as OpenApp)) return if (!OPEN_APPS.includes(value as OpenApp)) return
setPrefs("app", value as OpenApp) setPrefs("app", value as OpenApp)

View File

@@ -174,6 +174,10 @@ function detectLocale(): Locale {
return "en" return "en"
} }
function normalizeLocale(value: string): Locale {
return LOCALES.includes(value as Locale) ? (value as Locale) : "en"
}
export const { use: useLanguage, provider: LanguageProvider } = createSimpleContext({ export const { use: useLanguage, provider: LanguageProvider } = createSimpleContext({
name: "Language", name: "Language",
init: () => { init: () => {
@@ -184,15 +188,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
}), }),
) )
const locale = createMemo<Locale>(() => const locale = createMemo<Locale>(() => normalizeLocale(store.locale))
LOCALES.includes(store.locale as Locale) ? (store.locale as Locale) : "en",
)
createEffect(() => {
const current = locale()
if (store.locale === current) return
setStore("locale", current)
})
const dict = createMemo<Dictionary>(() => DICT[locale()]) const dict = createMemo<Dictionary>(() => DICT[locale()])
@@ -213,7 +209,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
label, label,
t, t,
setLocale(next: Locale) { setLocale(next: Locale) {
setStore("locale", next) setStore("locale", normalizeLocale(next))
}, },
} }
}, },

View File

@@ -177,7 +177,12 @@ export default function Layout(props: ParentProps) {
const sidebarHovering = createMemo(() => !layout.sidebar.opened() && state.hoverProject !== undefined) const sidebarHovering = createMemo(() => !layout.sidebar.opened() && state.hoverProject !== undefined)
const sidebarExpanded = createMemo(() => layout.sidebar.opened() || sidebarHovering()) const sidebarExpanded = createMemo(() => layout.sidebar.opened() || sidebarHovering())
const clearHoverProjectSoon = () => queueMicrotask(() => setState("hoverProject", undefined)) const setHoverProject = (value: string | undefined) => {
setState("hoverProject", value)
if (value !== undefined) return
aim.reset()
}
const clearHoverProjectSoon = () => queueMicrotask(() => setHoverProject(undefined))
const setHoverSession = (id: string | undefined) => setState("hoverSession", id) const setHoverSession = (id: string | undefined) => setState("hoverSession", id)
const hoverProjectData = createMemo(() => { const hoverProjectData = createMemo(() => {
@@ -188,13 +193,7 @@ export default function Layout(props: ParentProps) {
createEffect(() => { createEffect(() => {
if (!layout.sidebar.opened()) return if (!layout.sidebar.opened()) return
aim.reset() setHoverProject(undefined)
setState("hoverProject", undefined)
})
createEffect(() => {
if (state.hoverProject !== undefined) return
aim.reset()
}) })
const autoselecting = createMemo(() => { const autoselecting = createMemo(() => {
@@ -225,7 +224,7 @@ export default function Layout(props: ParentProps) {
const clearSidebarHoverState = () => { const clearSidebarHoverState = () => {
if (layout.sidebar.opened()) return if (layout.sidebar.opened()) return
setState("hoverSession", undefined) setState("hoverSession", undefined)
setState("hoverProject", undefined) setHoverProject(undefined)
} }
const navigateWithSidebarReset = (href: string) => { const navigateWithSidebarReset = (href: string) => {
@@ -1490,7 +1489,7 @@ export default function Layout(props: ParentProps) {
function handleDragStart(event: unknown) { function handleDragStart(event: unknown) {
const id = getDraggableId(event) const id = getDraggableId(event)
if (!id) return if (!id) return
setState("hoverProject", undefined) setHoverProject(undefined)
setStore("activeProject", id) setStore("activeProject", id)
} }
@@ -1924,7 +1923,7 @@ export default function Layout(props: ParentProps) {
if (navLeave.current !== undefined) clearTimeout(navLeave.current) if (navLeave.current !== undefined) clearTimeout(navLeave.current)
navLeave.current = window.setTimeout(() => { navLeave.current = window.setTimeout(() => {
navLeave.current = undefined navLeave.current = undefined
setState("hoverProject", undefined) setHoverProject(undefined)
setState("hoverSession", undefined) setState("hoverSession", undefined)
}, 300) }, 300)
}} }}

View File

@@ -1,4 +1,4 @@
import { onCleanup, Show, Match, Switch, createMemo, createEffect, on } from "solid-js" import { onCleanup, Show, Match, Switch, createMemo, createEffect, on, onMount } from "solid-js"
import { createMediaQuery } from "@solid-primitives/media" import { createMediaQuery } from "@solid-primitives/media"
import { createResizeObserver } from "@solid-primitives/resize-observer" import { createResizeObserver } from "@solid-primitives/resize-observer"
import { useLocal } from "@/context/local" import { useLocal } from "@/context/local"
@@ -981,7 +981,7 @@ export default function Page() {
consumePendingMessage: layout.pendingMessage.consume, consumePendingMessage: layout.pendingMessage.consume,
}) })
createEffect(() => { onMount(() => {
document.addEventListener("keydown", handleKeyDown) document.addEventListener("keydown", handleKeyDown)
}) })

View File

@@ -168,6 +168,13 @@ export function FileTabContent(props: { tab: string }) {
draftTop: undefined as number | undefined, draftTop: undefined as number | undefined,
}) })
const setCommenting = (range: SelectedLineRange | null) => {
setNote("commenting", range)
scheduleComments()
if (!range) return
setNote("draft", "")
}
const getRoot = () => { const getRoot = () => {
const el = wrap const el = wrap
if (!el) return if (!el) return
@@ -260,13 +267,6 @@ export function FileTabContent(props: { tab: string }) {
scheduleComments() scheduleComments()
}) })
createEffect(() => {
const range = note.commenting
scheduleComments()
if (!range) return
setNote("draft", "")
})
createEffect(() => { createEffect(() => {
const focus = comments.focus() const focus = comments.focus()
const p = path() const p = path()
@@ -278,7 +278,7 @@ export function FileTabContent(props: { tab: string }) {
if (!target) return if (!target) return
setNote("openedComment", target.id) setNote("openedComment", target.id)
setNote("commenting", null) setCommenting(null)
file.setSelectedLines(p, target.selection) file.setSelectedLines(p, target.selection)
requestAnimationFrame(() => comments.clearFocus()) requestAnimationFrame(() => comments.clearFocus())
}) })
@@ -438,16 +438,16 @@ export function FileTabContent(props: { tab: string }) {
const p = path() const p = path()
if (!p) return if (!p) return
file.setSelectedLines(p, range) file.setSelectedLines(p, range)
if (!range) setNote("commenting", null) if (!range) setCommenting(null)
}} }}
onLineSelectionEnd={(range: SelectedLineRange | null) => { onLineSelectionEnd={(range: SelectedLineRange | null) => {
if (!range) { if (!range) {
setNote("commenting", null) setCommenting(null)
return return
} }
setNote("openedComment", null) setNote("openedComment", null)
setNote("commenting", range) setCommenting(range)
}} }}
overflow="scroll" overflow="scroll"
class="select-text" class="select-text"
@@ -468,7 +468,7 @@ export function FileTabContent(props: { tab: string }) {
onClick={() => { onClick={() => {
const p = path() const p = path()
if (!p) return if (!p) return
setNote("commenting", null) setCommenting(null)
setNote("openedComment", (current) => (current === comment.id ? null : comment.id)) setNote("openedComment", (current) => (current === comment.id ? null : comment.id))
file.setSelectedLines(p, comment.selection) file.setSelectedLines(p, comment.selection)
}} }}
@@ -483,12 +483,12 @@ export function FileTabContent(props: { tab: string }) {
value={note.draft} value={note.draft}
selection={formatCommentLabel(range())} selection={formatCommentLabel(range())}
onInput={(value) => setNote("draft", value)} onInput={(value) => setNote("draft", value)}
onCancel={() => setNote("commenting", null)} onCancel={() => setCommenting(null)}
onSubmit={(value) => { onSubmit={(value) => {
const p = path() const p = path()
if (!p) return if (!p) return
addCommentToContext({ file: p, selection: range(), comment: value, origin: "file" }) addCommentToContext({ file: p, selection: range(), comment: value, origin: "file" })
setNote("commenting", null) setCommenting(null)
}} }}
onPopoverFocusOut={(e: FocusEvent) => { onPopoverFocusOut={(e: FocusEvent) => {
const current = e.currentTarget as HTMLDivElement const current = e.currentTarget as HTMLDivElement
@@ -497,7 +497,7 @@ export function FileTabContent(props: { tab: string }) {
setTimeout(() => { setTimeout(() => {
if (!document.activeElement || !current.contains(document.activeElement)) { if (!document.activeElement || !current.contains(document.activeElement)) {
setNote("commenting", null) setCommenting(null)
} }
}, 0) }, 0)
}} }}

View File

@@ -70,29 +70,28 @@ export function SessionPromptDock(props: {
setSessionHandoff(sessionKey(), { prompt: previewPrompt() }) setSessionHandoff(sessionKey(), { prompt: previewPrompt() })
}) })
const [responding, setResponding] = createSignal(false) const [responding, setResponding] = createSignal<string | undefined>()
const permissionResponding = () => {
createEffect( const perm = permissionRequest()
on( if (!perm) return false
() => permissionRequest()?.id, return responding() === perm.id
() => setResponding(false), }
{ defer: true },
),
)
const decide = (response: "once" | "always" | "reject") => { const decide = (response: "once" | "always" | "reject") => {
const perm = permissionRequest() const perm = permissionRequest()
if (!perm) return if (!perm) return
if (responding()) return if (responding() === perm.id) return
setResponding(true) setResponding(perm.id)
sdk.client.permission sdk.client.permission
.respond({ sessionID: perm.sessionID, permissionID: perm.id, response }) .respond({ sessionID: perm.sessionID, permissionID: perm.id, response })
.catch((err: unknown) => { .catch((err: unknown) => {
const message = err instanceof Error ? err.message : String(err) const message = err instanceof Error ? err.message : String(err)
showToast({ title: language.t("common.requestFailed"), description: message }) showToast({ title: language.t("common.requestFailed"), description: message })
}) })
.finally(() => setResponding(false)) .finally(() => {
setResponding((id) => (id === perm.id ? undefined : id))
})
} }
const done = createMemo( const done = createMemo(
@@ -218,18 +217,28 @@ export function SessionPromptDock(props: {
<> <>
<div /> <div />
<div data-slot="permission-footer-actions"> <div data-slot="permission-footer-actions">
<Button variant="ghost" size="normal" onClick={() => decide("reject")} disabled={responding()}> <Button
variant="ghost"
size="normal"
onClick={() => decide("reject")}
disabled={permissionResponding()}
>
{language.t("ui.permission.deny")} {language.t("ui.permission.deny")}
</Button> </Button>
<Button <Button
variant="secondary" variant="secondary"
size="normal" size="normal"
onClick={() => decide("always")} onClick={() => decide("always")}
disabled={responding()} disabled={permissionResponding()}
> >
{language.t("ui.permission.allowAlways")} {language.t("ui.permission.allowAlways")}
</Button> </Button>
<Button variant="primary" size="normal" onClick={() => decide("once")} disabled={responding()}> <Button
variant="primary"
size="normal"
onClick={() => decide("once")}
disabled={permissionResponding()}
>
{language.t("ui.permission.allowOnce")} {language.t("ui.permission.allowOnce")}
</Button> </Button>
</div> </div>