summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages/session/composer
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-03-12 11:32:05 -0500
committerGitHub <[email protected]>2026-03-12 11:32:05 -0500
commitdce7eceb2855bc36a41bc49d9c56d5dcc92a8eb2 (patch)
tree31cd7d7aa33733579134e9a6cf3a61762599d8e0 /packages/app/src/pages/session/composer
parent0e077f748352df6d44c811829baff3c26b3436ac (diff)
downloadopencode-dce7eceb2855bc36a41bc49d9c56d5dcc92a8eb2.tar.gz
opencode-dce7eceb2855bc36a41bc49d9c56d5dcc92a8eb2.zip
chore: cleanup (#17197)
Diffstat (limited to 'packages/app/src/pages/session/composer')
-rw-r--r--packages/app/src/pages/session/composer/session-composer-region.tsx31
-rw-r--r--packages/app/src/pages/session/composer/session-todo-dock.tsx46
2 files changed, 43 insertions, 34 deletions
diff --git a/packages/app/src/pages/session/composer/session-composer-region.tsx b/packages/app/src/pages/session/composer/session-composer-region.tsx
index 964bf18dd..6d60d81b5 100644
--- a/packages/app/src/pages/session/composer/session-composer-region.tsx
+++ b/packages/app/src/pages/session/composer/session-composer-region.tsx
@@ -1,4 +1,5 @@
-import { Show, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
+import { Show, createEffect, createMemo, onCleanup } from "solid-js"
+import { createStore } from "solid-js/store"
import { useSpring } from "@opencode-ai/ui/motion-spring"
import { PromptInput } from "@/components/prompt-input"
import { useLanguage } from "@/context/language"
@@ -50,7 +51,11 @@ export function SessionComposerRegion(props: {
setSessionHandoff(sessionKey(), { prompt: previewPrompt() })
})
- const [ready, setReady] = createSignal(false)
+ const [store, setStore] = createStore({
+ ready: false,
+ height: 320,
+ body: undefined as HTMLDivElement | undefined,
+ })
let timer: number | undefined
let frame: number | undefined
@@ -67,17 +72,17 @@ export function SessionComposerRegion(props: {
createEffect(() => {
sessionKey()
- const active = props.ready
+ const ready = props.ready
const delay = 140
clear()
- setReady(false)
- if (!active) return
+ setStore("ready", false)
+ if (!ready) return
frame = requestAnimationFrame(() => {
frame = undefined
timer = window.setTimeout(() => {
- setReady(true)
+ setStore("ready", true)
timer = undefined
}, delay)
})
@@ -85,21 +90,19 @@ export function SessionComposerRegion(props: {
onCleanup(clear)
- const open = createMemo(() => ready() && props.state.dock() && !props.state.closing())
+ const open = createMemo(() => store.ready && props.state.dock() && !props.state.closing())
const progress = useSpring(() => (open() ? 1 : 0), { visualDuration: 0.3, bounce: 0 })
const value = createMemo(() => Math.max(0, Math.min(1, progress())))
- const [height, setHeight] = createSignal(320)
- const dock = createMemo(() => (ready() && props.state.dock()) || value() > 0.001)
+ const dock = createMemo(() => (store.ready && props.state.dock()) || value() > 0.001)
const rolled = createMemo(() => (props.revert?.items.length ? props.revert : undefined))
const lift = createMemo(() => (rolled() ? 18 : 36 * value()))
- const full = createMemo(() => Math.max(78, height()))
- const [contentRef, setContentRef] = createSignal<HTMLDivElement>()
+ const full = createMemo(() => Math.max(78, store.height))
createEffect(() => {
- const el = contentRef()
+ const el = store.body
if (!el) return
const update = () => {
- setHeight(el.getBoundingClientRect().height)
+ setStore("height", el.getBoundingClientRect().height)
}
update()
const observer = new ResizeObserver(update)
@@ -174,7 +177,7 @@ export function SessionComposerRegion(props: {
"max-height": `${full() * value()}px`,
}}
>
- <div ref={setContentRef}>
+ <div ref={(el) => setStore("body", el)}>
<SessionTodoDock
todos={props.state.todos()}
title={language.t("session.todo.title")}
diff --git a/packages/app/src/pages/session/composer/session-todo-dock.tsx b/packages/app/src/pages/session/composer/session-todo-dock.tsx
index baea51593..c7907bb54 100644
--- a/packages/app/src/pages/session/composer/session-todo-dock.tsx
+++ b/packages/app/src/pages/session/composer/session-todo-dock.tsx
@@ -6,7 +6,8 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
import { useSpring } from "@opencode-ai/ui/motion-spring"
import { TextReveal } from "@opencode-ai/ui/text-reveal"
import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
-import { Index, createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
+import { Index, createEffect, createMemo, on, onCleanup } from "solid-js"
+import { createStore } from "solid-js/store"
function dot(status: Todo["status"]) {
if (status !== "in_progress") return undefined
@@ -40,8 +41,12 @@ export function SessionTodoDock(props: {
expandLabel: string
dockProgress: number
}) {
- const [collapsed, setCollapsed] = createSignal(false)
- const toggle = () => setCollapsed((value) => !value)
+ const [store, setStore] = createStore({
+ collapsed: false,
+ height: 320,
+ })
+
+ const toggle = () => setStore("collapsed", (value) => !value)
const total = createMemo(() => props.todos.length)
const done = createMemo(() => props.todos.filter((todo) => todo.status === "completed").length)
@@ -56,22 +61,21 @@ export function SessionTodoDock(props: {
)
const preview = createMemo(() => active()?.content ?? "")
- const collapse = useSpring(() => (collapsed() ? 1 : 0), { visualDuration: 0.3, bounce: 0 })
+ const collapse = useSpring(() => (store.collapsed ? 1 : 0), { visualDuration: 0.3, bounce: 0 })
const dock = createMemo(() => Math.max(0, Math.min(1, props.dockProgress)))
const shut = createMemo(() => 1 - dock())
const value = createMemo(() => Math.max(0, Math.min(1, collapse())))
const hide = createMemo(() => Math.max(value(), shut()))
const off = createMemo(() => hide() > 0.98)
const turn = createMemo(() => Math.max(0, Math.min(1, value())))
- const [height, setHeight] = createSignal(320)
- const full = createMemo(() => Math.max(78, height()))
+ const full = createMemo(() => Math.max(78, store.height))
let contentRef: HTMLDivElement | undefined
createEffect(() => {
const el = contentRef
if (!el) return
const update = () => {
- setHeight(el.getBoundingClientRect().height)
+ setStore("height", el.getBoundingClientRect().height)
}
update()
const observer = new ResizeObserver(update)
@@ -127,7 +131,7 @@ export function SessionTodoDock(props: {
>
<TextReveal
class="text-14-regular text-text-base cursor-default"
- text={collapsed() ? preview() : undefined}
+ text={store.collapsed ? preview() : undefined}
duration={600}
travel={25}
edge={17}
@@ -140,7 +144,7 @@ export function SessionTodoDock(props: {
<div class="ml-auto">
<IconButton
data-action="session-todo-toggle-button"
- data-collapsed={collapsed() ? "true" : "false"}
+ data-collapsed={store.collapsed ? "true" : "false"}
icon="chevron-down"
size="normal"
variant="ghost"
@@ -153,14 +157,14 @@ export function SessionTodoDock(props: {
event.stopPropagation()
toggle()
}}
- aria-label={collapsed() ? props.expandLabel : props.collapseLabel}
+ aria-label={store.collapsed ? props.expandLabel : props.collapseLabel}
/>
</div>
</div>
<div
data-slot="session-todo-list"
- aria-hidden={collapsed() || off()}
+ aria-hidden={store.collapsed || off()}
classList={{
"pointer-events-none": hide() > 0.1,
}}
@@ -169,7 +173,7 @@ export function SessionTodoDock(props: {
opacity: `${Math.max(0, Math.min(1, 1 - hide()))}`,
}}
>
- <TodoList todos={props.todos} open={!collapsed()} />
+ <TodoList todos={props.todos} open={!store.collapsed} />
</div>
</div>
</DockTray>
@@ -177,8 +181,10 @@ export function SessionTodoDock(props: {
}
function TodoList(props: { todos: Todo[]; open: boolean }) {
- const [stuck, setStuck] = createSignal(false)
- const [scrolling, setScrolling] = createSignal(false)
+ const [store, setStore] = createStore({
+ stuck: false,
+ scrolling: false,
+ })
let scrollRef!: HTMLDivElement
let timer: number | undefined
@@ -186,7 +192,7 @@ function TodoList(props: { todos: Todo[]; open: boolean }) {
const ensure = () => {
if (!props.open) return
- if (scrolling()) return
+ if (store.scrolling) return
if (!scrollRef || scrollRef.offsetParent === null) return
const el = scrollRef.querySelector("[data-in-progress]")
@@ -207,7 +213,7 @@ function TodoList(props: { todos: Todo[]; open: boolean }) {
scrollRef.scrollTop = bottom - (scrollRef.clientHeight - bottomFade)
}
- setStuck(scrollRef.scrollTop > 0)
+ setStore("stuck", scrollRef.scrollTop > 0)
}
createEffect(
@@ -229,11 +235,11 @@ function TodoList(props: { todos: Todo[]; open: boolean }) {
ref={scrollRef}
style={{ "overflow-anchor": "none" }}
onScroll={(e) => {
- setStuck(e.currentTarget.scrollTop > 0)
- setScrolling(true)
+ setStore("stuck", e.currentTarget.scrollTop > 0)
+ setStore("scrolling", true)
if (timer) window.clearTimeout(timer)
timer = window.setTimeout(() => {
- setScrolling(false)
+ setStore("scrolling", false)
if (inProgress() < 0) return
requestAnimationFrame(ensure)
}, 250)
@@ -278,7 +284,7 @@ function TodoList(props: { todos: Todo[]; open: boolean }) {
class="pointer-events-none absolute top-0 left-0 right-0 h-4 transition-opacity duration-150"
style={{
background: "linear-gradient(to bottom, var(--background-base), transparent)",
- opacity: stuck() ? 1 : 0,
+ opacity: store.stuck ? 1 : 0,
}}
/>
</div>