summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app/src/pages')
-rw-r--r--packages/app/src/pages/layout.tsx6
-rw-r--r--packages/app/src/pages/layout/helpers.test.ts16
-rw-r--r--packages/app/src/pages/layout/helpers.ts8
-rw-r--r--packages/app/src/pages/layout/sidebar-project-helpers.test.ts63
-rw-r--r--packages/app/src/pages/layout/sidebar-project-helpers.ts11
-rw-r--r--packages/app/src/pages/layout/sidebar-project.tsx18
-rw-r--r--packages/app/src/pages/layout/sidebar-shell-helpers.ts1
-rw-r--r--packages/app/src/pages/layout/sidebar-shell.test.ts13
-rw-r--r--packages/app/src/pages/layout/sidebar-shell.tsx3
-rw-r--r--packages/app/src/pages/layout/sidebar-workspace-helpers.ts2
-rw-r--r--packages/app/src/pages/layout/sidebar-workspace.test.ts13
-rw-r--r--packages/app/src/pages/layout/sidebar-workspace.tsx100
-rw-r--r--packages/app/src/pages/session/composer/index.ts3
-rw-r--r--packages/app/src/pages/session/composer/session-composer-helpers.ts10
-rw-r--r--packages/app/src/pages/session/composer/session-composer-state.test.ts2
-rw-r--r--packages/app/src/pages/session/composer/session-composer-state.ts31
-rw-r--r--packages/app/src/pages/session/helpers.ts56
-rw-r--r--packages/app/src/pages/session/session-command-helpers.ts10
-rw-r--r--packages/app/src/pages/session/session-prompt-dock.test.ts22
-rw-r--r--packages/app/src/pages/session/session-prompt-helpers.ts4
-rw-r--r--packages/app/src/pages/session/use-session-commands.test.ts44
-rw-r--r--packages/app/src/pages/session/use-session-commands.tsx15
-rw-r--r--packages/app/src/pages/session/use-session-hash-scroll.ts2
23 files changed, 70 insertions, 383 deletions
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 052a03c54..daad100c3 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -51,7 +51,7 @@ import { DialogSelectProvider } from "@/components/dialog-select-provider"
import { DialogSelectServer } from "@/components/dialog-select-server"
import { DialogSettings } from "@/components/dialog-settings"
import { useCommand, type CommandOption } from "@/context/command"
-import { ConstrainDragXAxis } from "@/utils/solid-dnd"
+import { ConstrainDragXAxis, getDraggableId } from "@/utils/solid-dnd"
import { DialogSelectDirectory } from "@/components/dialog-select-directory"
import { DialogEditProject } from "@/components/dialog-edit-project"
import { DebugBar } from "@/components/debug-bar"
@@ -62,7 +62,6 @@ import {
displayName,
effectiveWorkspaceOrder,
errorMessage,
- getDraggableId,
latestRootSession,
sortedRootSessions,
workspaceKey,
@@ -80,7 +79,6 @@ import {
WorkspaceDragOverlay,
type WorkspaceSidebarContext,
} from "./layout/sidebar-workspace"
-import { workspaceOpenState } from "./layout/sidebar-workspace-helpers"
import { ProjectDragOverlay, SortableProject, type ProjectSidebarContext } from "./layout/sidebar-project"
import { SidebarContent } from "./layout/sidebar-shell"
@@ -1860,7 +1858,7 @@ export default function Layout(props: ParentProps) {
setEditor,
InlineEditor,
isBusy,
- workspaceExpanded: (directory, local) => workspaceOpenState(store.workspaceExpanded, directory, local),
+ workspaceExpanded: (directory, local) => store.workspaceExpanded[directory] ?? local,
setWorkspaceExpanded: (directory, value) => setStore("workspaceExpanded", directory, value),
showResetWorkspaceDialog: (root, directory) =>
dialog.show(() => <DialogResetWorkspace root={root} directory={directory} />),
diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts
index d1569dbd9..916b80214 100644
--- a/packages/app/src/pages/layout/helpers.test.ts
+++ b/packages/app/src/pages/layout/helpers.test.ts
@@ -6,9 +6,15 @@ import {
parseDeepLink,
parseNewSessionDeepLink,
} from "./deep-links"
-import { displayName, errorMessage, getDraggableId, syncWorkspaceOrder, workspaceKey } from "./helpers"
import { type Session } from "@opencode-ai/sdk/v2/client"
-import { hasProjectPermissions, latestRootSession } from "./helpers"
+import {
+ displayName,
+ errorMessage,
+ hasProjectPermissions,
+ latestRootSession,
+ syncWorkspaceOrder,
+ workspaceKey,
+} from "./helpers"
const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) =>
({
@@ -192,12 +198,6 @@ describe("layout workspace helpers", () => {
expect(result?.id).toBe("root")
})
- test("extracts draggable id safely", () => {
- expect(getDraggableId({ draggable: { id: "x" } })).toBe("x")
- expect(getDraggableId({ draggable: { id: 42 } })).toBeUndefined()
- expect(getDraggableId(null)).toBeUndefined()
- })
-
test("formats fallback project display name", () => {
expect(displayName({ worktree: "/tmp/app" })).toBe("app")
expect(displayName({ worktree: "/tmp/app", name: "My App" })).toBe("My App")
diff --git a/packages/app/src/pages/layout/helpers.ts b/packages/app/src/pages/layout/helpers.ts
index 42315e589..8881b8a48 100644
--- a/packages/app/src/pages/layout/helpers.ts
+++ b/packages/app/src/pages/layout/helpers.ts
@@ -54,14 +54,6 @@ export const childMapByParent = (sessions: Session[]) => {
return map
}
-export function getDraggableId(event: unknown): string | undefined {
- if (typeof event !== "object" || event === null) return undefined
- if (!("draggable" in event)) return undefined
- const draggable = (event as { draggable?: { id?: unknown } }).draggable
- if (!draggable) return undefined
- return typeof draggable.id === "string" ? draggable.id : undefined
-}
-
export const displayName = (project: { name?: string; worktree: string }) =>
project.name || getFilename(project.worktree)
diff --git a/packages/app/src/pages/layout/sidebar-project-helpers.test.ts b/packages/app/src/pages/layout/sidebar-project-helpers.test.ts
deleted file mode 100644
index 75958d49e..000000000
--- a/packages/app/src/pages/layout/sidebar-project-helpers.test.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { describe, expect, test } from "bun:test"
-import { projectSelected, projectTileActive } from "./sidebar-project-helpers"
-
-describe("projectSelected", () => {
- test("matches direct worktree", () => {
- expect(projectSelected("/tmp/root", "/tmp/root")).toBe(true)
- })
-
- test("matches sandbox worktree", () => {
- expect(projectSelected("/tmp/branch", "/tmp/root", ["/tmp/branch"])).toBe(true)
- expect(projectSelected("/tmp/other", "/tmp/root", ["/tmp/branch"])).toBe(false)
- })
-})
-
-describe("projectTileActive", () => {
- test("menu state always wins", () => {
- expect(
- projectTileActive({
- menu: true,
- preview: false,
- open: false,
- overlay: false,
- worktree: "/tmp/root",
- }),
- ).toBe(true)
- })
-
- test("preview mode uses open state", () => {
- expect(
- projectTileActive({
- menu: false,
- preview: true,
- open: true,
- overlay: true,
- hoverProject: "/tmp/other",
- worktree: "/tmp/root",
- }),
- ).toBe(true)
- })
-
- test("overlay mode uses hovered project", () => {
- expect(
- projectTileActive({
- menu: false,
- preview: false,
- open: false,
- overlay: true,
- hoverProject: "/tmp/root",
- worktree: "/tmp/root",
- }),
- ).toBe(true)
- expect(
- projectTileActive({
- menu: false,
- preview: false,
- open: false,
- overlay: true,
- hoverProject: "/tmp/other",
- worktree: "/tmp/root",
- }),
- ).toBe(false)
- })
-})
diff --git a/packages/app/src/pages/layout/sidebar-project-helpers.ts b/packages/app/src/pages/layout/sidebar-project-helpers.ts
deleted file mode 100644
index 06d38a3cd..000000000
--- a/packages/app/src/pages/layout/sidebar-project-helpers.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export const projectSelected = (currentDir: string, worktree: string, sandboxes?: string[]) =>
- worktree === currentDir || sandboxes?.includes(currentDir) === true
-
-export const projectTileActive = (args: {
- menu: boolean
- preview: boolean
- open: boolean
- overlay: boolean
- hoverProject?: string
- worktree: string
-}) => args.menu || (args.preview ? args.open : args.overlay && args.hoverProject === args.worktree)
diff --git a/packages/app/src/pages/layout/sidebar-project.tsx b/packages/app/src/pages/layout/sidebar-project.tsx
index 187cd2f33..551090fd5 100644
--- a/packages/app/src/pages/layout/sidebar-project.tsx
+++ b/packages/app/src/pages/layout/sidebar-project.tsx
@@ -12,7 +12,6 @@ import { useLanguage } from "@/context/language"
import { useNotification } from "@/context/notification"
import { ProjectIcon, SessionItem, type SessionItemProps } from "./sidebar-items"
import { childMapByParent, displayName, sortedRootSessions } from "./helpers"
-import { projectSelected, projectTileActive } from "./sidebar-project-helpers"
export type ProjectSidebarContext = {
currentDir: Accessor<string>
@@ -277,8 +276,10 @@ export const SortableProject = (props: {
const globalSync = useGlobalSync()
const language = useLanguage()
const sortable = createSortable(props.project.worktree)
- const selected = createMemo(() =>
- projectSelected(props.ctx.currentDir(), props.project.worktree, props.project.sandboxes),
+ const selected = createMemo(
+ () =>
+ props.project.worktree === props.ctx.currentDir() ||
+ props.project.sandboxes?.includes(props.ctx.currentDir()) === true,
)
const workspaces = createMemo(() => props.ctx.workspaceIds(props.project).slice(0, 2))
const workspaceEnabled = createMemo(() => props.ctx.workspacesEnabled(props.project))
@@ -291,15 +292,8 @@ export const SortableProject = (props: {
const preview = createMemo(() => !props.mobile && props.ctx.sidebarOpened())
const overlay = createMemo(() => !props.mobile && !props.ctx.sidebarOpened())
- const active = createMemo(() =>
- projectTileActive({
- menu: state.menu,
- preview: preview(),
- open: state.open,
- overlay: overlay(),
- hoverProject: props.ctx.hoverProject(),
- worktree: props.project.worktree,
- }),
+ const active = createMemo(
+ () => state.menu || (preview() ? state.open : overlay() && props.ctx.hoverProject() === props.project.worktree),
)
createEffect(() => {
diff --git a/packages/app/src/pages/layout/sidebar-shell-helpers.ts b/packages/app/src/pages/layout/sidebar-shell-helpers.ts
deleted file mode 100644
index 93c286c15..000000000
--- a/packages/app/src/pages/layout/sidebar-shell-helpers.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const sidebarExpanded = (mobile: boolean | undefined, opened: boolean) => !!mobile || opened
diff --git a/packages/app/src/pages/layout/sidebar-shell.test.ts b/packages/app/src/pages/layout/sidebar-shell.test.ts
deleted file mode 100644
index 694025a65..000000000
--- a/packages/app/src/pages/layout/sidebar-shell.test.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { describe, expect, test } from "bun:test"
-import { sidebarExpanded } from "./sidebar-shell-helpers"
-
-describe("sidebarExpanded", () => {
- test("expands on mobile regardless of desktop open state", () => {
- expect(sidebarExpanded(true, false)).toBe(true)
- })
-
- test("follows desktop open state when not mobile", () => {
- expect(sidebarExpanded(false, true)).toBe(true)
- expect(sidebarExpanded(false, false)).toBe(false)
- })
-})
diff --git a/packages/app/src/pages/layout/sidebar-shell.tsx b/packages/app/src/pages/layout/sidebar-shell.tsx
index d3070e374..82be4f024 100644
--- a/packages/app/src/pages/layout/sidebar-shell.tsx
+++ b/packages/app/src/pages/layout/sidebar-shell.tsx
@@ -11,7 +11,6 @@ import { ConstrainDragXAxis } from "@/utils/solid-dnd"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { type LocalProject } from "@/context/layout"
-import { sidebarExpanded } from "./sidebar-shell-helpers"
export const SidebarContent = (props: {
mobile?: boolean
@@ -33,7 +32,7 @@ export const SidebarContent = (props: {
onOpenHelp: () => void
renderPanel: () => JSX.Element
}): JSX.Element => {
- const expanded = createMemo(() => sidebarExpanded(props.mobile, props.opened()))
+ const expanded = createMemo(() => !!props.mobile || props.opened())
const placement = () => (props.mobile ? "bottom" : "right")
let panel: HTMLDivElement | undefined
diff --git a/packages/app/src/pages/layout/sidebar-workspace-helpers.ts b/packages/app/src/pages/layout/sidebar-workspace-helpers.ts
deleted file mode 100644
index aa7cb480e..000000000
--- a/packages/app/src/pages/layout/sidebar-workspace-helpers.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export const workspaceOpenState = (expanded: Record<string, boolean>, directory: string, local: boolean) =>
- expanded[directory] ?? local
diff --git a/packages/app/src/pages/layout/sidebar-workspace.test.ts b/packages/app/src/pages/layout/sidebar-workspace.test.ts
deleted file mode 100644
index d71c39fc8..000000000
--- a/packages/app/src/pages/layout/sidebar-workspace.test.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { describe, expect, test } from "bun:test"
-import { workspaceOpenState } from "./sidebar-workspace-helpers"
-
-describe("workspaceOpenState", () => {
- test("defaults to local workspace open", () => {
- expect(workspaceOpenState({}, "/tmp/root", true)).toBe(true)
- })
-
- test("uses persisted expansion state when present", () => {
- expect(workspaceOpenState({ "/tmp/root": false }, "/tmp/root", true)).toBe(false)
- expect(workspaceOpenState({ "/tmp/branch": true }, "/tmp/branch", false)).toBe(true)
- })
-})
diff --git a/packages/app/src/pages/layout/sidebar-workspace.tsx b/packages/app/src/pages/layout/sidebar-workspace.tsx
index c317b9c5e..1a54fdd8a 100644
--- a/packages/app/src/pages/layout/sidebar-workspace.tsx
+++ b/packages/app/src/pages/layout/sidebar-workspace.tsx
@@ -144,8 +144,6 @@ const WorkspaceActions = (props: {
setMenuOpen: (open: boolean) => void
setPendingRename: (value: boolean) => void
sidebarHovering: Accessor<boolean>
- mobile?: boolean
- nav: Accessor<HTMLElement | undefined>
touch: Accessor<boolean>
language: ReturnType<typeof useLanguage>
workspaceValue: Accessor<string>
@@ -340,6 +338,22 @@ export const SortableWorkspace = (props: {
}
const workspaceEditActive = createMemo(() => props.ctx.editorOpen(`workspace:${props.directory}`))
+ const header = () => (
+ <WorkspaceHeader
+ local={local}
+ busy={busy}
+ open={open}
+ directory={props.directory}
+ language={language}
+ branch={() => workspaceStore.vcs?.branch}
+ workspaceValue={workspaceValue}
+ workspaceEditActive={workspaceEditActive}
+ InlineEditor={props.ctx.InlineEditor}
+ renameWorkspace={props.ctx.renameWorkspace}
+ setEditor={props.ctx.setEditor}
+ projectId={props.project.id}
+ />
+ )
const openWrapper = (value: boolean) => {
props.ctx.setWorkspaceExpanded(props.directory, value)
@@ -379,20 +393,7 @@ export const SortableWorkspace = (props: {
data-action="workspace-toggle"
data-workspace={base64Encode(props.directory)}
>
- <WorkspaceHeader
- local={local}
- busy={busy}
- open={open}
- directory={props.directory}
- language={language}
- branch={() => workspaceStore.vcs?.branch}
- workspaceValue={workspaceValue}
- workspaceEditActive={workspaceEditActive}
- InlineEditor={props.ctx.InlineEditor}
- renameWorkspace={props.ctx.renameWorkspace}
- setEditor={props.ctx.setEditor}
- projectId={props.project.id}
- />
+ {header()}
</Collapsible.Trigger>
}
>
@@ -401,20 +402,7 @@ export const SortableWorkspace = (props: {
menu.open ? "pr-16" : "pr-2"
} group-hover/workspace:pr-16 group-focus-within/workspace:pr-16`}
>
- <WorkspaceHeader
- local={local}
- busy={busy}
- open={open}
- directory={props.directory}
- language={language}
- branch={() => workspaceStore.vcs?.branch}
- workspaceValue={workspaceValue}
- workspaceEditActive={workspaceEditActive}
- InlineEditor={props.ctx.InlineEditor}
- renameWorkspace={props.ctx.renameWorkspace}
- setEditor={props.ctx.setEditor}
- projectId={props.project.id}
- />
+ {header()}
</div>
</Show>
<WorkspaceActions
@@ -426,8 +414,6 @@ export const SortableWorkspace = (props: {
setMenuOpen={(open) => setMenu("open", open)}
setPendingRename={(value) => setMenu("pendingRename", value)}
sidebarHovering={props.ctx.sidebarHovering}
- mobile={props.mobile}
- nav={props.ctx.nav}
touch={touch}
language={language}
workspaceValue={workspaceValue}
@@ -490,44 +476,18 @@ export const LocalWorkspace = (props: {
ref={(el) => props.ctx.setScrollContainerRef(el, props.mobile)}
class="size-full flex flex-col py-2 overflow-y-auto no-scrollbar [overflow-anchor:none]"
>
- <nav class="flex flex-col gap-1 px-3">
- <Show when={loading()}>
- <SessionSkeleton />
- </Show>
- <For each={sessions()}>
- {(session) => (
- <SessionItem
- session={session}
- slug={slug()}
- mobile={props.mobile}
- children={children()}
- sidebarExpanded={props.ctx.sidebarExpanded}
- sidebarHovering={props.ctx.sidebarHovering}
- nav={props.ctx.nav}
- hoverSession={props.ctx.hoverSession}
- setHoverSession={props.ctx.setHoverSession}
- clearHoverProjectSoon={props.ctx.clearHoverProjectSoon}
- prefetchSession={props.ctx.prefetchSession}
- archiveSession={props.ctx.archiveSession}
- />
- )}
- </For>
- <Show when={hasMore()}>
- <div class="relative w-full py-1">
- <Button
- variant="ghost"
- class="flex w-full text-left justify-start text-14-regular text-text-weak pl-9 pr-10"
- size="large"
- onClick={(e: MouseEvent) => {
- loadMore()
- ;(e.currentTarget as HTMLButtonElement).blur()
- }}
- >
- {language.t("common.loadMore")}
- </Button>
- </div>
- </Show>
- </nav>
+ <WorkspaceSessionList
+ slug={slug}
+ mobile={props.mobile}
+ ctx={props.ctx}
+ showNew={() => false}
+ loading={loading}
+ sessions={sessions}
+ children={children}
+ hasMore={hasMore}
+ loadMore={loadMore}
+ language={language}
+ />
</div>
)
}
diff --git a/packages/app/src/pages/session/composer/index.ts b/packages/app/src/pages/session/composer/index.ts
index e244a1536..b0069de53 100644
--- a/packages/app/src/pages/session/composer/index.ts
+++ b/packages/app/src/pages/session/composer/index.ts
@@ -1,3 +1,2 @@
export { SessionComposerRegion } from "./session-composer-region"
-export { createSessionComposerBlocked, createSessionComposerState } from "./session-composer-state"
-export type { SessionComposerState } from "./session-composer-state"
+export { createSessionComposerState } from "./session-composer-state"
diff --git a/packages/app/src/pages/session/composer/session-composer-helpers.ts b/packages/app/src/pages/session/composer/session-composer-helpers.ts
deleted file mode 100644
index 90c238af4..000000000
--- a/packages/app/src/pages/session/composer/session-composer-helpers.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export const todoState = (input: {
- count: number
- done: boolean
- live: boolean
-}): "hide" | "clear" | "open" | "close" => {
- if (input.count === 0) return "hide"
- if (!input.live) return "clear"
- if (!input.done) return "open"
- return "close"
-}
diff --git a/packages/app/src/pages/session/composer/session-composer-state.test.ts b/packages/app/src/pages/session/composer/session-composer-state.test.ts
index f7c11715c..c27454f7e 100644
--- a/packages/app/src/pages/session/composer/session-composer-state.test.ts
+++ b/packages/app/src/pages/session/composer/session-composer-state.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import type { PermissionRequest, QuestionRequest, Session } from "@opencode-ai/sdk/v2/client"
-import { todoState } from "./session-composer-helpers"
+import { todoState } from "./session-composer-state"
import { sessionPermissionRequest, sessionQuestionRequest } from "./session-request-tree"
const session = (input: { id: string; parentID?: string }) =>
diff --git a/packages/app/src/pages/session/composer/session-composer-state.ts b/packages/app/src/pages/session/composer/session-composer-state.ts
index a007e4c84..525766dcf 100644
--- a/packages/app/src/pages/session/composer/session-composer-state.ts
+++ b/packages/app/src/pages/session/composer/session-composer-state.ts
@@ -8,30 +8,21 @@ import { useLanguage } from "@/context/language"
import { usePermission } from "@/context/permission"
import { useSDK } from "@/context/sdk"
import { useSync } from "@/context/sync"
-import { todoState } from "./session-composer-helpers"
import { sessionPermissionRequest, sessionQuestionRequest } from "./session-request-tree"
-const idle = { type: "idle" as const }
-
-export function createSessionComposerBlocked() {
- const params = useParams()
- const permission = usePermission()
- const sdk = useSDK()
- const sync = useSync()
- const permissionRequest = createMemo(() =>
- sessionPermissionRequest(sync.data.session, sync.data.permission, params.id, (item) => {
- return !permission.autoResponds(item, sdk.directory)
- }),
- )
- const questionRequest = createMemo(() => sessionQuestionRequest(sync.data.session, sync.data.question, params.id))
-
- return createMemo(() => {
- const id = params.id
- if (!id) return false
- return !!permissionRequest() || !!questionRequest()
- })
+export const todoState = (input: {
+ count: number
+ done: boolean
+ live: boolean
+}): "hide" | "clear" | "open" | "close" => {
+ if (input.count === 0) return "hide"
+ if (!input.live) return "clear"
+ if (!input.done) return "open"
+ return "close"
}
+const idle = { type: "idle" as const }
+
export function createSessionComposerState(options?: { closeMs?: number | (() => number) }) {
const params = useParams()
const sdk = useSDK()
diff --git a/packages/app/src/pages/session/helpers.ts b/packages/app/src/pages/session/helpers.ts
index be9656900..2da5ce6b8 100644
--- a/packages/app/src/pages/session/helpers.ts
+++ b/packages/app/src/pages/session/helpers.ts
@@ -1,4 +1,4 @@
-import { batch, createEffect, on, onCleanup, onMount, type Accessor } from "solid-js"
+import { batch, onCleanup, onMount } from "solid-js"
import { createStore } from "solid-js/store"
export const focusTerminalById = (id: string) => {
@@ -117,57 +117,3 @@ export const createSizing = () => {
}
export type Sizing = ReturnType<typeof createSizing>
-
-export const createPresence = (open: Accessor<boolean>, wait = 200) => {
- const [state, setState] = createStore({
- show: open(),
- open: open(),
- })
- let frame: number | undefined
- let t: number | undefined
-
- const clear = () => {
- if (frame !== undefined) {
- cancelAnimationFrame(frame)
- frame = undefined
- }
- if (t !== undefined) {
- clearTimeout(t)
- t = undefined
- }
- }
-
- createEffect(
- on(open, (next) => {
- clear()
-
- if (next) {
- if (state.show) {
- setState("open", true)
- return
- }
-
- setState({ show: true, open: false })
- frame = requestAnimationFrame(() => {
- frame = undefined
- setState("open", true)
- })
- return
- }
-
- if (!state.show) return
- setState("open", false)
- t = window.setTimeout(() => {
- t = undefined
- setState("show", false)
- }, wait)
- }),
- )
-
- onCleanup(clear)
-
- return {
- show: () => state.show,
- open: () => state.open,
- }
-}
diff --git a/packages/app/src/pages/session/session-command-helpers.ts b/packages/app/src/pages/session/session-command-helpers.ts
deleted file mode 100644
index b71a7b768..000000000
--- a/packages/app/src/pages/session/session-command-helpers.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export const canAddSelectionContext = (input: {
- active?: string
- pathFromTab: (tab: string) => string | undefined
- selectedLines: (path: string) => unknown
-}) => {
- if (!input.active) return false
- const path = input.pathFromTab(input.active)
- if (!path) return false
- return input.selectedLines(path) != null
-}
diff --git a/packages/app/src/pages/session/session-prompt-dock.test.ts b/packages/app/src/pages/session/session-prompt-dock.test.ts
deleted file mode 100644
index b3a9945d6..000000000
--- a/packages/app/src/pages/session/session-prompt-dock.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { describe, expect, test } from "bun:test"
-import { questionSubtitle } from "./session-prompt-helpers"
-
-describe("questionSubtitle", () => {
- const t = (key: string) => {
- if (key === "ui.common.question.one") return "question"
- if (key === "ui.common.question.other") return "questions"
- return key
- }
-
- test("returns empty for zero", () => {
- expect(questionSubtitle(0, t)).toBe("")
- })
-
- test("uses singular label", () => {
- expect(questionSubtitle(1, t)).toBe("1 question")
- })
-
- test("uses plural label", () => {
- expect(questionSubtitle(3, t)).toBe("3 questions")
- })
-})
diff --git a/packages/app/src/pages/session/session-prompt-helpers.ts b/packages/app/src/pages/session/session-prompt-helpers.ts
deleted file mode 100644
index ac3234c93..000000000
--- a/packages/app/src/pages/session/session-prompt-helpers.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export const questionSubtitle = (count: number, t: (key: string) => string) => {
- if (count === 0) return ""
- return `${count} ${t(count > 1 ? "ui.common.question.other" : "ui.common.question.one")}`
-}
diff --git a/packages/app/src/pages/session/use-session-commands.test.ts b/packages/app/src/pages/session/use-session-commands.test.ts
deleted file mode 100644
index ada1871e1..000000000
--- a/packages/app/src/pages/session/use-session-commands.test.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { describe, expect, test } from "bun:test"
-import { canAddSelectionContext } from "./session-command-helpers"
-
-describe("canAddSelectionContext", () => {
- test("returns false without active tab", () => {
- expect(
- canAddSelectionContext({
- active: undefined,
- pathFromTab: () => "src/a.ts",
- selectedLines: () => ({ start: 1, end: 1 }),
- }),
- ).toBe(false)
- })
-
- test("returns false when active tab is not a file", () => {
- expect(
- canAddSelectionContext({
- active: "context",
- pathFromTab: () => undefined,
- selectedLines: () => ({ start: 1, end: 1 }),
- }),
- ).toBe(false)
- })
-
- test("returns false without selected lines", () => {
- expect(
- canAddSelectionContext({
- active: "file://src/a.ts",
- pathFromTab: () => "src/a.ts",
- selectedLines: () => null,
- }),
- ).toBe(false)
- })
-
- test("returns true when file and selection exist", () => {
- expect(
- canAddSelectionContext({
- active: "file://src/a.ts",
- pathFromTab: () => "src/a.ts",
- selectedLines: () => ({ start: 1, end: 2 }),
- }),
- ).toBe(true)
- })
-})
diff --git a/packages/app/src/pages/session/use-session-commands.tsx b/packages/app/src/pages/session/use-session-commands.tsx
index b8ddeda82..ea3b5ec57 100644
--- a/packages/app/src/pages/session/use-session-commands.tsx
+++ b/packages/app/src/pages/session/use-session-commands.tsx
@@ -19,7 +19,6 @@ import { showToast } from "@opencode-ai/ui/toast"
import { findLast } from "@opencode-ai/util/array"
import { extractPromptFromParts } from "@/utils/prompt"
import { UserMessage } from "@opencode-ai/sdk/v2"
-import { canAddSelectionContext } from "@/pages/session/session-command-helpers"
export type SessionCommandContext = {
navigateMessageByOffset: (offset: number) => void
@@ -84,6 +83,14 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
prompt.context.add({ type: "file", path, selection, preview })
}
+ const canAddSelectionContext = () => {
+ const active = tabs().active()
+ if (!active) return false
+ const path = file.pathFromTab(active)
+ if (!path) return false
+ return file.selectedLines(path) != null
+ }
+
const navigateMessageByOffset = actions.navigateMessageByOffset
const setActiveMessage = actions.setActiveMessage
const focusInput = actions.focusInput
@@ -136,11 +143,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
title: language.t("command.context.addSelection"),
description: language.t("command.context.addSelection.description"),
keybind: "mod+shift+l",
- disabled: !canAddSelectionContext({
- active: tabs().active(),
- pathFromTab: file.pathFromTab,
- selectedLines: file.selectedLines,
- }),
+ disabled: !canAddSelectionContext(),
onSelect: () => {
const active = tabs().active()
if (!active) return
diff --git a/packages/app/src/pages/session/use-session-hash-scroll.ts b/packages/app/src/pages/session/use-session-hash-scroll.ts
index 1ea6a302b..5fadb1f22 100644
--- a/packages/app/src/pages/session/use-session-hash-scroll.ts
+++ b/packages/app/src/pages/session/use-session-hash-scroll.ts
@@ -3,8 +3,6 @@ import { useLocation, useNavigate } from "@solidjs/router"
import { createEffect, createMemo, onCleanup, onMount } from "solid-js"
import { messageIdFromHash } from "./message-id-from-hash"
-export { messageIdFromHash } from "./message-id-from-hash"
-
export const useSessionHashScroll = (input: {
sessionKey: () => string
sessionID: () => string | undefined