chore: cleanup (#14181)
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user