mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-16 05:34:44 +00:00
chore: cleanup (#17184)
This commit is contained in:
@@ -6,7 +6,7 @@ import { Keybind } from "@opencode-ai/ui/keybind"
|
|||||||
import { List } from "@opencode-ai/ui/list"
|
import { List } from "@opencode-ai/ui/list"
|
||||||
import { base64Encode } from "@opencode-ai/util/encode"
|
import { base64Encode } from "@opencode-ai/util/encode"
|
||||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||||
import { useNavigate, useParams } from "@solidjs/router"
|
import { useNavigate } from "@solidjs/router"
|
||||||
import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js"
|
import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js"
|
||||||
import { formatKeybind, useCommand, type CommandOption } from "@/context/command"
|
import { formatKeybind, useCommand, type CommandOption } from "@/context/command"
|
||||||
import { useGlobalSDK } from "@/context/global-sdk"
|
import { useGlobalSDK } from "@/context/global-sdk"
|
||||||
@@ -14,6 +14,7 @@ import { useGlobalSync } from "@/context/global-sync"
|
|||||||
import { useLayout } from "@/context/layout"
|
import { useLayout } from "@/context/layout"
|
||||||
import { useFile } from "@/context/file"
|
import { useFile } from "@/context/file"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
import { decode64 } from "@/utils/base64"
|
import { decode64 } from "@/utils/base64"
|
||||||
import { getRelativeTime } from "@/utils/time"
|
import { getRelativeTime } from "@/utils/time"
|
||||||
|
|
||||||
@@ -259,14 +260,11 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
|||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const file = useFile()
|
const file = useFile()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const params = useParams()
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
const globalSync = useGlobalSync()
|
const globalSync = useGlobalSync()
|
||||||
|
const { params, tabs, view } = useSessionLayout()
|
||||||
const filesOnly = () => props.mode === "files"
|
const filesOnly = () => props.mode === "files"
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
const state = { cleanup: undefined as (() => void) | void, committed: false }
|
const state = { cleanup: undefined as (() => void) | void, committed: false }
|
||||||
const [grouped, setGrouped] = createSignal(false)
|
const [grouped, setGrouped] = createSignal(false)
|
||||||
const commandEntries = createCommandEntries({ filesOnly, command, language })
|
const commandEntries = createCommandEntries({ filesOnly, command, language })
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
} from "@/context/prompt"
|
} from "@/context/prompt"
|
||||||
import { useLayout } from "@/context/layout"
|
import { useLayout } from "@/context/layout"
|
||||||
import { useSDK } from "@/context/sdk"
|
import { useSDK } from "@/context/sdk"
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import { useComments } from "@/context/comments"
|
import { useComments } from "@/context/comments"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
@@ -37,6 +36,7 @@ import { Persist, persisted } from "@/utils/persist"
|
|||||||
import { usePermission } from "@/context/permission"
|
import { usePermission } from "@/context/permission"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
import { createTextFragment, getCursorPosition, setCursorPosition, setRangeEdge } from "./prompt-input/editor-dom"
|
import { createTextFragment, getCursorPosition, setCursorPosition, setRangeEdge } from "./prompt-input/editor-dom"
|
||||||
import { createPromptAttachments, ACCEPTED_FILE_TYPES } from "./prompt-input/attachments"
|
import { createPromptAttachments, ACCEPTED_FILE_TYPES } from "./prompt-input/attachments"
|
||||||
import {
|
import {
|
||||||
@@ -102,13 +102,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const comments = useComments()
|
const comments = useComments()
|
||||||
const params = useParams()
|
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const providers = useProviders()
|
const providers = useProviders()
|
||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
const permission = usePermission()
|
const permission = usePermission()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
|
const { params, tabs, view } = useSessionLayout()
|
||||||
let editorRef!: HTMLDivElement
|
let editorRef!: HTMLDivElement
|
||||||
let fileInputRef: HTMLInputElement | undefined
|
let fileInputRef: HTMLInputElement | undefined
|
||||||
let scrollRef!: HTMLDivElement
|
let scrollRef!: HTMLDivElement
|
||||||
@@ -154,10 +154,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
requestAnimationFrame(scrollCursorIntoView)
|
requestAnimationFrame(scrollCursorIntoView)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
|
|
||||||
const commentInReview = (path: string) => {
|
const commentInReview = (path: string) => {
|
||||||
const sessionID = params.id
|
const sessionID = params.id
|
||||||
if (!sessionID) return false
|
if (!sessionID) return false
|
||||||
@@ -255,6 +251,15 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const buttonsSpring = useSpring(() => (store.mode === "normal" ? 1 : 0), { visualDuration: 0.2, bounce: 0 })
|
const buttonsSpring = useSpring(() => (store.mode === "normal" ? 1 : 0), { visualDuration: 0.2, bounce: 0 })
|
||||||
|
const motion = (value: number) => ({
|
||||||
|
opacity: value,
|
||||||
|
transform: `scale(${0.95 + value * 0.05})`,
|
||||||
|
filter: `blur(${(1 - value) * 2}px)`,
|
||||||
|
"pointer-events": value > 0.5 ? ("auto" as const) : ("none" as const),
|
||||||
|
})
|
||||||
|
const buttons = createMemo(() => motion(buttonsSpring()))
|
||||||
|
const shell = createMemo(() => motion(1 - buttonsSpring()))
|
||||||
|
const control = createMemo(() => ({ height: "28px", ...buttons() }))
|
||||||
|
|
||||||
const commentCount = createMemo(() => {
|
const commentCount = createMemo(() => {
|
||||||
if (store.mode === "shell") return 0
|
if (store.mode === "shell") return 0
|
||||||
@@ -1275,11 +1280,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="size-8 p-0"
|
class="size-8 p-0"
|
||||||
style={{
|
style={buttons()}
|
||||||
opacity: buttonsSpring(),
|
|
||||||
transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
|
|
||||||
filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
|
|
||||||
}}
|
|
||||||
onClick={pick}
|
onClick={pick}
|
||||||
disabled={store.mode !== "normal"}
|
disabled={store.mode !== "normal"}
|
||||||
tabIndex={store.mode === "normal" ? undefined : -1}
|
tabIndex={store.mode === "normal" ? undefined : -1}
|
||||||
@@ -1317,11 +1318,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
icon={working() ? "stop" : "arrow-up"}
|
icon={working() ? "stop" : "arrow-up"}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
class="size-8"
|
class="size-8"
|
||||||
style={{
|
style={buttons()}
|
||||||
opacity: buttonsSpring(),
|
|
||||||
transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
|
|
||||||
filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
|
|
||||||
}}
|
|
||||||
aria-label={working() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
|
aria-label={working() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -1379,10 +1376,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
class="h-7 flex items-center gap-1.5 max-w-[160px] min-w-0 absolute inset-y-0 left-0"
|
class="h-7 flex items-center gap-1.5 max-w-[160px] min-w-0 absolute inset-y-0 left-0"
|
||||||
style={{
|
style={{
|
||||||
padding: "0 4px 0 8px",
|
padding: "0 4px 0 8px",
|
||||||
opacity: 1 - buttonsSpring(),
|
...shell(),
|
||||||
transform: `scale(${0.95 + (1 - buttonsSpring()) * 0.05})`,
|
|
||||||
filter: `blur(${buttonsSpring() * 2}px)`,
|
|
||||||
"pointer-events": buttonsSpring() < 0.5 ? "auto" : "none",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span class="truncate text-13-medium text-text-strong">{language.t("prompt.mode.shell")}</span>
|
<span class="truncate text-13-medium text-text-strong">{language.t("prompt.mode.shell")}</span>
|
||||||
@@ -1402,13 +1396,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
onSelect={local.agent.set}
|
onSelect={local.agent.set}
|
||||||
class="capitalize max-w-[160px]"
|
class="capitalize max-w-[160px]"
|
||||||
valueClass="truncate text-13-regular"
|
valueClass="truncate text-13-regular"
|
||||||
triggerStyle={{
|
triggerStyle={control()}
|
||||||
height: "28px",
|
|
||||||
opacity: buttonsSpring(),
|
|
||||||
transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
|
|
||||||
filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
|
|
||||||
"pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
|
|
||||||
}}
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
/>
|
/>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
@@ -1426,13 +1414,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="normal"
|
size="normal"
|
||||||
class="min-w-0 max-w-[320px] text-13-regular group"
|
class="min-w-0 max-w-[320px] text-13-regular group"
|
||||||
style={{
|
style={control()}
|
||||||
height: "28px",
|
|
||||||
opacity: buttonsSpring(),
|
|
||||||
transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
|
|
||||||
filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
|
|
||||||
"pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
|
|
||||||
}}
|
|
||||||
onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}
|
onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}
|
||||||
>
|
>
|
||||||
<Show when={local.model.current()?.provider?.id}>
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
@@ -1461,13 +1443,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
triggerProps={{
|
triggerProps={{
|
||||||
variant: "ghost",
|
variant: "ghost",
|
||||||
size: "normal",
|
size: "normal",
|
||||||
style: {
|
style: control(),
|
||||||
height: "28px",
|
|
||||||
opacity: buttonsSpring(),
|
|
||||||
transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
|
|
||||||
filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
|
|
||||||
"pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
|
|
||||||
},
|
|
||||||
class: "min-w-0 max-w-[320px] text-13-regular group",
|
class: "min-w-0 max-w-[320px] text-13-regular group",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -1499,13 +1475,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
onSelect={(x) => local.model.variant.set(x === "default" ? undefined : x)}
|
onSelect={(x) => local.model.variant.set(x === "default" ? undefined : x)}
|
||||||
class="capitalize max-w-[160px]"
|
class="capitalize max-w-[160px]"
|
||||||
valueClass="truncate text-13-regular"
|
valueClass="truncate text-13-regular"
|
||||||
triggerStyle={{
|
triggerStyle={control()}
|
||||||
height: "28px",
|
|
||||||
opacity: buttonsSpring(),
|
|
||||||
transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
|
|
||||||
filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
|
|
||||||
"pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
|
|
||||||
}}
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
/>
|
/>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { Match, Show, Switch, createMemo } from "solid-js"
|
|||||||
import { Tooltip, type TooltipProps } from "@opencode-ai/ui/tooltip"
|
import { Tooltip, type TooltipProps } from "@opencode-ai/ui/tooltip"
|
||||||
import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
|
import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
|
|
||||||
import { useLayout } from "@/context/layout"
|
import { useLayout } from "@/context/layout"
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { getSessionContextMetrics } from "@/components/session/session-context-metrics"
|
import { getSessionContextMetrics } from "@/components/session/session-context-metrics"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
|
|
||||||
interface SessionContextUsageProps {
|
interface SessionContextUsageProps {
|
||||||
variant?: "button" | "indicator"
|
variant?: "button" | "indicator"
|
||||||
@@ -27,14 +27,11 @@ function openSessionContext(args: {
|
|||||||
|
|
||||||
export function SessionContextUsage(props: SessionContextUsageProps) {
|
export function SessionContextUsage(props: SessionContextUsageProps) {
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const params = useParams()
|
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
|
const { params, tabs, view } = useSessionLayout()
|
||||||
|
|
||||||
const variant = createMemo(() => props.variant ?? "button")
|
const variant = createMemo(() => props.variant ?? "button")
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
||||||
|
|
||||||
const usd = createMemo(
|
const usd = createMemo(
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js"
|
import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js"
|
||||||
import type { JSX } from "solid-js"
|
import type { JSX } from "solid-js"
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import { useLayout } from "@/context/layout"
|
|
||||||
import { checksum } from "@opencode-ai/util/encode"
|
import { checksum } from "@opencode-ai/util/encode"
|
||||||
import { findLast } from "@opencode-ai/util/array"
|
import { findLast } from "@opencode-ai/util/array"
|
||||||
import { same } from "@/utils/same"
|
import { same } from "@/utils/same"
|
||||||
@@ -14,6 +12,7 @@ import { Markdown } from "@opencode-ai/ui/markdown"
|
|||||||
import { ScrollView } from "@opencode-ai/ui/scroll-view"
|
import { ScrollView } from "@opencode-ai/ui/scroll-view"
|
||||||
import type { Message, Part, UserMessage } from "@opencode-ai/sdk/v2/client"
|
import type { Message, Part, UserMessage } from "@opencode-ai/sdk/v2/client"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
import { getSessionContextMetrics } from "./session-context-metrics"
|
import { getSessionContextMetrics } from "./session-context-metrics"
|
||||||
import { estimateSessionContextBreakdown, type SessionContextBreakdownKey } from "./session-context-breakdown"
|
import { estimateSessionContextBreakdown, type SessionContextBreakdownKey } from "./session-context-breakdown"
|
||||||
import { createSessionContextFormatter } from "./session-context-format"
|
import { createSessionContextFormatter } from "./session-context-format"
|
||||||
@@ -91,13 +90,10 @@ const emptyMessages: Message[] = []
|
|||||||
const emptyUserMessages: UserMessage[] = []
|
const emptyUserMessages: UserMessage[] = []
|
||||||
|
|
||||||
export function SessionContextTab() {
|
export function SessionContextTab() {
|
||||||
const params = useParams()
|
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const layout = useLayout()
|
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
|
const { params, view } = useSessionLayout()
|
||||||
|
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
||||||
|
|
||||||
const messages = createMemo(
|
const messages = createMemo(
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { TextField } from "@opencode-ai/ui/text-field"
|
|||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
import { getFilename } from "@opencode-ai/util/path"
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
import { createEffect, createMemo, For, onCleanup, Show } from "solid-js"
|
import { createEffect, createMemo, For, onCleanup, Show } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { Portal } from "solid-js/web"
|
import { Portal } from "solid-js/web"
|
||||||
@@ -23,6 +22,7 @@ import { useServer } from "@/context/server"
|
|||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import { useTerminal } from "@/context/terminal"
|
import { useTerminal } from "@/context/terminal"
|
||||||
import { focusTerminalById } from "@/pages/session/helpers"
|
import { focusTerminalById } from "@/pages/session/helpers"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
import { decode64 } from "@/utils/base64"
|
import { decode64 } from "@/utils/base64"
|
||||||
import { Persist, persisted } from "@/utils/persist"
|
import { Persist, persisted } from "@/utils/persist"
|
||||||
import { StatusPopover } from "../status-popover"
|
import { StatusPopover } from "../status-popover"
|
||||||
@@ -225,13 +225,13 @@ function useSessionShare(args: {
|
|||||||
export function SessionHeader() {
|
export function SessionHeader() {
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const params = useParams()
|
|
||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
const server = useServer()
|
const server = useServer()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const terminal = useTerminal()
|
const terminal = useTerminal()
|
||||||
|
const { params, view } = useSessionLayout()
|
||||||
|
|
||||||
const projectDirectory = createMemo(() => decode64(params.dir) ?? "")
|
const projectDirectory = createMemo(() => decode64(params.dir) ?? "")
|
||||||
const project = createMemo(() => {
|
const project = createMemo(() => {
|
||||||
@@ -249,8 +249,6 @@ export function SessionHeader() {
|
|||||||
const currentSession = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
const currentSession = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
||||||
const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
|
const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
|
||||||
const showShare = createMemo(() => shareEnabled() && !!params.id)
|
const showShare = createMemo(() => shareEnabled() && !!params.id)
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
const os = createMemo(() => detectOS(platform))
|
const os = createMemo(() => detectOS(platform))
|
||||||
|
|
||||||
const [exists, setExists] = createStore<Partial<Record<OpenApp, boolean>>>({
|
const [exists, setExists] = createStore<Partial<Record<OpenApp, boolean>>>({
|
||||||
@@ -282,10 +280,7 @@ export function SessionHeader() {
|
|||||||
Promise.resolve(platform.checkAppExists?.(app.openWith))
|
Promise.resolve(platform.checkAppExists?.(app.openWith))
|
||||||
.then((value) => Boolean(value))
|
.then((value) => Boolean(value))
|
||||||
.catch(() => false)
|
.catch(() => false)
|
||||||
.then((ok) => {
|
.then((ok) => [app.id, ok] as const),
|
||||||
console.debug(`[session-header] App "${app.label}" (${app.openWith}): ${ok ? "exists" : "does not exist"}`)
|
|
||||||
return [app.id, ok] as const
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
).then((entries) => {
|
).then((entries) => {
|
||||||
setExists(Object.fromEntries(entries) as Partial<Record<OpenApp, boolean>>)
|
setExists(Object.fromEntries(entries) as Partial<Record<OpenApp, boolean>>)
|
||||||
|
|||||||
@@ -2136,6 +2136,41 @@ export default function Layout(props: ParentProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projects = () => layout.projects.list()
|
||||||
|
const projectOverlay = () => <ProjectDragOverlay projects={projects} activeProject={() => store.activeProject} />
|
||||||
|
const sidebarContent = (mobile?: boolean) => (
|
||||||
|
<SidebarContent
|
||||||
|
mobile={mobile}
|
||||||
|
opened={() => layout.sidebar.opened()}
|
||||||
|
aimMove={aim.move}
|
||||||
|
projects={projects}
|
||||||
|
renderProject={(project) => (
|
||||||
|
<SortableProject ctx={projectSidebarCtx} project={project} sortNow={sortNow} mobile={mobile} />
|
||||||
|
)}
|
||||||
|
handleDragStart={handleDragStart}
|
||||||
|
handleDragEnd={handleDragEnd}
|
||||||
|
handleDragOver={handleDragOver}
|
||||||
|
openProjectLabel={language.t("command.project.open")}
|
||||||
|
openProjectKeybind={() => command.keybind("project.open")}
|
||||||
|
onOpenProject={chooseProject}
|
||||||
|
renderProjectOverlay={projectOverlay}
|
||||||
|
settingsLabel={() => language.t("sidebar.settings")}
|
||||||
|
settingsKeybind={() => command.keybind("settings.open")}
|
||||||
|
onOpenSettings={openSettings}
|
||||||
|
helpLabel={() => language.t("sidebar.help")}
|
||||||
|
onOpenHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")}
|
||||||
|
renderPanel={() =>
|
||||||
|
mobile ? (
|
||||||
|
<SidebarPanel project={currentProject()} mobile />
|
||||||
|
) : (
|
||||||
|
<Show when={currentProject()} keyed>
|
||||||
|
{(project) => <SidebarPanel project={project} merged />}
|
||||||
|
</Show>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative bg-background-base flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
|
<div class="relative bg-background-base flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
|
||||||
<Titlebar />
|
<Titlebar />
|
||||||
@@ -2164,38 +2199,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
arm()
|
arm()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="@container w-full h-full contain-strict">
|
<div class="@container w-full h-full contain-strict">{sidebarContent()}</div>
|
||||||
<SidebarContent
|
|
||||||
opened={() => layout.sidebar.opened()}
|
|
||||||
aimMove={aim.move}
|
|
||||||
projects={() => layout.projects.list()}
|
|
||||||
renderProject={(project) => (
|
|
||||||
<SortableProject ctx={projectSidebarCtx} project={project} sortNow={sortNow} />
|
|
||||||
)}
|
|
||||||
handleDragStart={handleDragStart}
|
|
||||||
handleDragEnd={handleDragEnd}
|
|
||||||
handleDragOver={handleDragOver}
|
|
||||||
openProjectLabel={language.t("command.project.open")}
|
|
||||||
openProjectKeybind={() => command.keybind("project.open")}
|
|
||||||
onOpenProject={chooseProject}
|
|
||||||
renderProjectOverlay={() => (
|
|
||||||
<ProjectDragOverlay
|
|
||||||
projects={() => layout.projects.list()}
|
|
||||||
activeProject={() => store.activeProject}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
settingsLabel={() => language.t("sidebar.settings")}
|
|
||||||
settingsKeybind={() => command.keybind("settings.open")}
|
|
||||||
onOpenSettings={openSettings}
|
|
||||||
helpLabel={() => language.t("sidebar.help")}
|
|
||||||
onOpenHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")}
|
|
||||||
renderPanel={() => (
|
|
||||||
<Show when={currentProject()} keyed>
|
|
||||||
{(project) => <SidebarPanel project={project} merged />}
|
|
||||||
</Show>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Show when={layout.sidebar.opened()}>
|
<Show when={layout.sidebar.opened()}>
|
||||||
<div onPointerDown={() => setSizing(true)}>
|
<div onPointerDown={() => setSizing(true)}>
|
||||||
<ResizeHandle
|
<ResizeHandle
|
||||||
@@ -2242,33 +2246,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
}}
|
}}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<SidebarContent
|
{sidebarContent(true)}
|
||||||
mobile
|
|
||||||
opened={() => layout.sidebar.opened()}
|
|
||||||
aimMove={aim.move}
|
|
||||||
projects={() => layout.projects.list()}
|
|
||||||
renderProject={(project) => (
|
|
||||||
<SortableProject ctx={projectSidebarCtx} project={project} sortNow={sortNow} mobile />
|
|
||||||
)}
|
|
||||||
handleDragStart={handleDragStart}
|
|
||||||
handleDragEnd={handleDragEnd}
|
|
||||||
handleDragOver={handleDragOver}
|
|
||||||
openProjectLabel={language.t("command.project.open")}
|
|
||||||
openProjectKeybind={() => command.keybind("project.open")}
|
|
||||||
onOpenProject={chooseProject}
|
|
||||||
renderProjectOverlay={() => (
|
|
||||||
<ProjectDragOverlay
|
|
||||||
projects={() => layout.projects.list()}
|
|
||||||
activeProject={() => store.activeProject}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
settingsLabel={() => language.t("sidebar.settings")}
|
|
||||||
settingsKeybind={() => command.keybind("settings.open")}
|
|
||||||
onOpenSettings={openSettings}
|
|
||||||
helpLabel={() => language.t("sidebar.help")}
|
|
||||||
onOpenHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")}
|
|
||||||
renderPanel={() => <SidebarPanel project={currentProject()} mobile />}
|
|
||||||
/>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import {
|
|||||||
import { type Session } from "@opencode-ai/sdk/v2/client"
|
import { type Session } from "@opencode-ai/sdk/v2/client"
|
||||||
import {
|
import {
|
||||||
displayName,
|
displayName,
|
||||||
|
effectiveWorkspaceOrder,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
hasProjectPermissions,
|
hasProjectPermissions,
|
||||||
latestRootSession,
|
latestRootSession,
|
||||||
syncWorkspaceOrder,
|
|
||||||
workspaceKey,
|
workspaceKey,
|
||||||
} from "./helpers"
|
} from "./helpers"
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ describe("layout workspace helpers", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test("keeps local first while preserving known order", () => {
|
test("keeps local first while preserving known order", () => {
|
||||||
const result = syncWorkspaceOrder("/root", ["/root", "/b", "/c"], ["/root", "/c", "/a", "/b"])
|
const result = effectiveWorkspaceOrder("/root", ["/root", "/b", "/c"], ["/root", "/c", "/a", "/b"])
|
||||||
expect(result).toEqual(["/root", "/c", "/b"])
|
expect(result).toEqual(["/root", "/c", "/b"])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const workspaceKey = (directory: string) => {
|
|||||||
return directory.replace(/[\\/]+$/, "")
|
return directory.replace(/[\\/]+$/, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sortSessions(now: number) {
|
function sortSessions(now: number) {
|
||||||
const oneMinuteAgo = now - 60 * 1000
|
const oneMinuteAgo = now - 60 * 1000
|
||||||
return (a: Session, b: Session) => {
|
return (a: Session, b: Session) => {
|
||||||
const aUpdated = a.time.updated ?? a.time.created
|
const aUpdated = a.time.updated ?? a.time.created
|
||||||
@@ -22,7 +22,7 @@ export function sortSessions(now: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isRootVisibleSession = (session: Session, directory: string) =>
|
const isRootVisibleSession = (session: Session, directory: string) =>
|
||||||
workspaceKey(session.directory) === workspaceKey(directory) && !session.parentID && !session.time?.archived
|
workspaceKey(session.directory) === workspaceKey(directory) && !session.parentID && !session.time?.archived
|
||||||
|
|
||||||
export const sortedRootSessions = (store: { session: Session[]; path: { directory: string } }, now: number) =>
|
export const sortedRootSessions = (store: { session: Session[]; path: { directory: string } }, now: number) =>
|
||||||
@@ -90,5 +90,3 @@ export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted
|
|||||||
|
|
||||||
return [...result, ...live.values()]
|
return [...result, ...live.values()]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const syncWorkspaceOrder = effectiveWorkspaceOrder
|
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ import { createStore } from "solid-js/store"
|
|||||||
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
||||||
import { Select } from "@opencode-ai/ui/select"
|
import { Select } from "@opencode-ai/ui/select"
|
||||||
import { createAutoScroll } from "@opencode-ai/ui/hooks"
|
import { createAutoScroll } from "@opencode-ai/ui/hooks"
|
||||||
|
import { previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import { base64Encode, checksum } from "@opencode-ai/util/encode"
|
import { base64Encode, checksum } from "@opencode-ai/util/encode"
|
||||||
import { useNavigate, useParams, useSearchParams } from "@solidjs/router"
|
import { useNavigate, useSearchParams } from "@solidjs/router"
|
||||||
import { NewSessionView, SessionHeader } from "@/components/session"
|
import { NewSessionView, SessionHeader } from "@/components/session"
|
||||||
import { useComments } from "@/context/comments"
|
import { useComments } from "@/context/comments"
|
||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
@@ -37,6 +38,7 @@ import { createSessionComposerState, SessionComposerRegion } from "@/pages/sessi
|
|||||||
import { createOpenReviewFile, createSizing, focusTerminalById } from "@/pages/session/helpers"
|
import { createOpenReviewFile, createSizing, focusTerminalById } from "@/pages/session/helpers"
|
||||||
import { MessageTimeline } from "@/pages/session/message-timeline"
|
import { MessageTimeline } from "@/pages/session/message-timeline"
|
||||||
import { type DiffStyle, SessionReviewTab, type SessionReviewTabProps } from "@/pages/session/review-tab"
|
import { type DiffStyle, SessionReviewTab, type SessionReviewTabProps } from "@/pages/session/review-tab"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
import { resetSessionModel, syncSessionModel } from "@/pages/session/session-model-helpers"
|
import { resetSessionModel, syncSessionModel } from "@/pages/session/session-model-helpers"
|
||||||
import { SessionMobileTabs } from "@/pages/session/session-mobile-tabs"
|
import { SessionMobileTabs } from "@/pages/session/session-mobile-tabs"
|
||||||
import { SessionSidePanel } from "@/pages/session/session-side-panel"
|
import { SessionSidePanel } from "@/pages/session/session-side-panel"
|
||||||
@@ -264,13 +266,13 @@ export default function Page() {
|
|||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const params = useParams()
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
const comments = useComments()
|
const comments = useComments()
|
||||||
const terminal = useTerminal()
|
const terminal = useTerminal()
|
||||||
const [searchParams, setSearchParams] = useSearchParams<{ prompt?: string }>()
|
const [searchParams, setSearchParams] = useSearchParams<{ prompt?: string }>()
|
||||||
|
const { params, sessionKey, tabs, view } = useSessionLayout()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!untrack(() => prompt.ready())) return
|
if (!untrack(() => prompt.ready())) return
|
||||||
@@ -298,11 +300,8 @@ export default function Page() {
|
|||||||
|
|
||||||
const composer = createSessionComposerState()
|
const composer = createSessionComposerState()
|
||||||
|
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const workspaceKey = createMemo(() => params.dir ?? "")
|
const workspaceKey = createMemo(() => params.dir ?? "")
|
||||||
const workspaceTabs = createMemo(() => layout.tabs(workspaceKey))
|
const workspaceTabs = createMemo(() => layout.tabs(workspaceKey))
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
on(
|
on(
|
||||||
@@ -670,11 +669,7 @@ export default function Page() {
|
|||||||
const selectionPreview = (path: string, selection: FileSelection) => {
|
const selectionPreview = (path: string, selection: FileSelection) => {
|
||||||
const content = file.get(path)?.content?.content
|
const content = file.get(path)?.content?.content
|
||||||
if (!content) return undefined
|
if (!content) return undefined
|
||||||
const start = Math.max(1, Math.min(selection.startLine, selection.endLine))
|
return previewSelectedLines(content, { start: selection.startLine, end: selection.endLine })
|
||||||
const end = Math.max(selection.startLine, selection.endLine)
|
|
||||||
const lines = content.split("\n").slice(start - 1, end)
|
|
||||||
if (lines.length === 0) return undefined
|
|
||||||
return lines.slice(0, 2).join("\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const addCommentToContext = (input: {
|
const addCommentToContext = (input: {
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { Show, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
import { Show, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
import { useSpring } from "@opencode-ai/ui/motion-spring"
|
import { useSpring } from "@opencode-ai/ui/motion-spring"
|
||||||
import { PromptInput } from "@/components/prompt-input"
|
import { PromptInput } from "@/components/prompt-input"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { usePrompt } from "@/context/prompt"
|
import { usePrompt } from "@/context/prompt"
|
||||||
import { getSessionHandoff, setSessionHandoff } from "@/pages/session/handoff"
|
import { getSessionHandoff, setSessionHandoff } from "@/pages/session/handoff"
|
||||||
|
import { useSessionKey } from "@/pages/session/session-layout"
|
||||||
import { SessionPermissionDock } from "@/pages/session/composer/session-permission-dock"
|
import { SessionPermissionDock } from "@/pages/session/composer/session-permission-dock"
|
||||||
import { SessionQuestionDock } from "@/pages/session/composer/session-question-dock"
|
import { SessionQuestionDock } from "@/pages/session/composer/session-question-dock"
|
||||||
import { SessionRevertDock } from "@/pages/session/composer/session-revert-dock"
|
import { SessionRevertDock } from "@/pages/session/composer/session-revert-dock"
|
||||||
@@ -27,29 +26,11 @@ export function SessionComposerRegion(props: {
|
|||||||
onRestore: (id: string) => void
|
onRestore: (id: string) => void
|
||||||
}
|
}
|
||||||
setPromptDockRef: (el: HTMLDivElement) => void
|
setPromptDockRef: (el: HTMLDivElement) => void
|
||||||
visualDuration?: number
|
|
||||||
bounce?: number
|
|
||||||
dockOpenVisualDuration?: number
|
|
||||||
dockOpenBounce?: number
|
|
||||||
dockCloseVisualDuration?: number
|
|
||||||
dockCloseBounce?: number
|
|
||||||
drawerExpandVisualDuration?: number
|
|
||||||
drawerExpandBounce?: number
|
|
||||||
drawerCollapseVisualDuration?: number
|
|
||||||
drawerCollapseBounce?: number
|
|
||||||
subtitleDuration?: number
|
|
||||||
subtitleTravel?: number
|
|
||||||
subtitleEdge?: number
|
|
||||||
countDuration?: number
|
|
||||||
countMask?: number
|
|
||||||
countMaskHeight?: number
|
|
||||||
countWidthDuration?: number
|
|
||||||
}) {
|
}) {
|
||||||
const params = useParams()
|
|
||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
|
const { sessionKey } = useSessionKey()
|
||||||
|
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const handoffPrompt = createMemo(() => getSessionHandoff(sessionKey())?.prompt)
|
const handoffPrompt = createMemo(() => getSessionHandoff(sessionKey())?.prompt)
|
||||||
|
|
||||||
const previewPrompt = () =>
|
const previewPrompt = () =>
|
||||||
@@ -69,9 +50,7 @@ export function SessionComposerRegion(props: {
|
|||||||
setSessionHandoff(sessionKey(), { prompt: previewPrompt() })
|
setSessionHandoff(sessionKey(), { prompt: previewPrompt() })
|
||||||
})
|
})
|
||||||
|
|
||||||
const [gate, setGate] = createStore({
|
const [ready, setReady] = createSignal(false)
|
||||||
ready: false,
|
|
||||||
})
|
|
||||||
let timer: number | undefined
|
let timer: number | undefined
|
||||||
let frame: number | undefined
|
let frame: number | undefined
|
||||||
|
|
||||||
@@ -88,17 +67,17 @@ export function SessionComposerRegion(props: {
|
|||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
sessionKey()
|
sessionKey()
|
||||||
const ready = props.ready
|
const active = props.ready
|
||||||
const delay = 140
|
const delay = 140
|
||||||
|
|
||||||
clear()
|
clear()
|
||||||
setGate("ready", false)
|
setReady(false)
|
||||||
if (!ready) return
|
if (!active) return
|
||||||
|
|
||||||
frame = requestAnimationFrame(() => {
|
frame = requestAnimationFrame(() => {
|
||||||
frame = undefined
|
frame = undefined
|
||||||
timer = window.setTimeout(() => {
|
timer = window.setTimeout(() => {
|
||||||
setGate("ready", true)
|
setReady(true)
|
||||||
timer = undefined
|
timer = undefined
|
||||||
}, delay)
|
}, delay)
|
||||||
})
|
})
|
||||||
@@ -106,22 +85,11 @@ export function SessionComposerRegion(props: {
|
|||||||
|
|
||||||
onCleanup(clear)
|
onCleanup(clear)
|
||||||
|
|
||||||
const open = createMemo(() => gate.ready && props.state.dock() && !props.state.closing())
|
const open = createMemo(() => ready() && props.state.dock() && !props.state.closing())
|
||||||
const config = createMemo(() =>
|
const progress = useSpring(() => (open() ? 1 : 0), { visualDuration: 0.3, bounce: 0 })
|
||||||
open()
|
|
||||||
? {
|
|
||||||
visualDuration: props.dockOpenVisualDuration ?? props.visualDuration ?? 0.3,
|
|
||||||
bounce: props.dockOpenBounce ?? props.bounce ?? 0,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
visualDuration: props.dockCloseVisualDuration ?? props.visualDuration ?? 0.3,
|
|
||||||
bounce: props.dockCloseBounce ?? props.bounce ?? 0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
const progress = useSpring(() => (open() ? 1 : 0), config)
|
|
||||||
const value = createMemo(() => Math.max(0, Math.min(1, progress())))
|
const value = createMemo(() => Math.max(0, Math.min(1, progress())))
|
||||||
const [height, setHeight] = createSignal(320)
|
const [height, setHeight] = createSignal(320)
|
||||||
const dock = createMemo(() => (gate.ready && props.state.dock()) || value() > 0.001)
|
const dock = createMemo(() => (ready() && props.state.dock()) || value() > 0.001)
|
||||||
const rolled = createMemo(() => (props.revert?.items.length ? props.revert : undefined))
|
const rolled = createMemo(() => (props.revert?.items.length ? props.revert : undefined))
|
||||||
const lift = createMemo(() => (rolled() ? 18 : 36 * value()))
|
const lift = createMemo(() => (rolled() ? 18 : 36 * value()))
|
||||||
const full = createMemo(() => Math.max(78, height()))
|
const full = createMemo(() => Math.max(78, height()))
|
||||||
@@ -213,19 +181,6 @@ export function SessionComposerRegion(props: {
|
|||||||
collapseLabel={language.t("session.todo.collapse")}
|
collapseLabel={language.t("session.todo.collapse")}
|
||||||
expandLabel={language.t("session.todo.expand")}
|
expandLabel={language.t("session.todo.expand")}
|
||||||
dockProgress={value()}
|
dockProgress={value()}
|
||||||
visualDuration={props.visualDuration}
|
|
||||||
bounce={props.bounce}
|
|
||||||
expandVisualDuration={props.drawerExpandVisualDuration}
|
|
||||||
expandBounce={props.drawerExpandBounce}
|
|
||||||
collapseVisualDuration={props.drawerCollapseVisualDuration}
|
|
||||||
collapseBounce={props.drawerCollapseBounce}
|
|
||||||
subtitleDuration={props.subtitleDuration}
|
|
||||||
subtitleTravel={props.subtitleTravel}
|
|
||||||
subtitleEdge={props.subtitleEdge}
|
|
||||||
countDuration={props.countDuration}
|
|
||||||
countMask={props.countMask}
|
|
||||||
countMaskHeight={props.countMaskHeight}
|
|
||||||
countWidthDuration={props.countWidthDuration}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { useSpring } from "@opencode-ai/ui/motion-spring"
|
|||||||
import { TextReveal } from "@opencode-ai/ui/text-reveal"
|
import { TextReveal } from "@opencode-ai/ui/text-reveal"
|
||||||
import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
|
import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
|
||||||
import { Index, createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
|
import { Index, createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
|
||||||
|
|
||||||
function dot(status: Todo["status"]) {
|
function dot(status: Todo["status"]) {
|
||||||
if (status !== "in_progress") return undefined
|
if (status !== "in_progress") return undefined
|
||||||
@@ -39,26 +38,10 @@ export function SessionTodoDock(props: {
|
|||||||
title: string
|
title: string
|
||||||
collapseLabel: string
|
collapseLabel: string
|
||||||
expandLabel: string
|
expandLabel: string
|
||||||
dockProgress?: number
|
dockProgress: number
|
||||||
visualDuration?: number
|
|
||||||
bounce?: number
|
|
||||||
expandVisualDuration?: number
|
|
||||||
expandBounce?: number
|
|
||||||
collapseVisualDuration?: number
|
|
||||||
collapseBounce?: number
|
|
||||||
subtitleDuration?: number
|
|
||||||
subtitleTravel?: number
|
|
||||||
subtitleEdge?: number
|
|
||||||
countDuration?: number
|
|
||||||
countMask?: number
|
|
||||||
countMaskHeight?: number
|
|
||||||
countWidthDuration?: number
|
|
||||||
}) {
|
}) {
|
||||||
const [store, setStore] = createStore({
|
const [collapsed, setCollapsed] = createSignal(false)
|
||||||
collapsed: false,
|
const toggle = () => setCollapsed((value) => !value)
|
||||||
})
|
|
||||||
|
|
||||||
const toggle = () => setStore("collapsed", (value) => !value)
|
|
||||||
|
|
||||||
const total = createMemo(() => props.todos.length)
|
const total = createMemo(() => props.todos.length)
|
||||||
const done = createMemo(() => props.todos.filter((todo) => todo.status === "completed").length)
|
const done = createMemo(() => props.todos.filter((todo) => todo.status === "completed").length)
|
||||||
@@ -73,19 +56,8 @@ export function SessionTodoDock(props: {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const preview = createMemo(() => active()?.content ?? "")
|
const preview = createMemo(() => active()?.content ?? "")
|
||||||
const config = createMemo(() =>
|
const collapse = useSpring(() => (collapsed() ? 1 : 0), { visualDuration: 0.3, bounce: 0 })
|
||||||
store.collapsed
|
const dock = createMemo(() => Math.max(0, Math.min(1, props.dockProgress)))
|
||||||
? {
|
|
||||||
visualDuration: props.collapseVisualDuration ?? props.visualDuration ?? 0.3,
|
|
||||||
bounce: props.collapseBounce ?? props.bounce ?? 0,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
visualDuration: props.expandVisualDuration ?? props.visualDuration ?? 0.3,
|
|
||||||
bounce: props.expandBounce ?? props.bounce ?? 0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
const collapse = useSpring(() => (store.collapsed ? 1 : 0), config)
|
|
||||||
const dock = createMemo(() => Math.max(0, Math.min(1, props.dockProgress ?? 1)))
|
|
||||||
const shut = createMemo(() => 1 - dock())
|
const shut = createMemo(() => 1 - dock())
|
||||||
const value = createMemo(() => Math.max(0, Math.min(1, collapse())))
|
const value = createMemo(() => Math.max(0, Math.min(1, collapse())))
|
||||||
const hide = createMemo(() => Math.max(value(), shut()))
|
const hide = createMemo(() => Math.max(value(), shut()))
|
||||||
@@ -133,10 +105,10 @@ export function SessionTodoDock(props: {
|
|||||||
class="text-14-regular text-text-strong cursor-default inline-flex items-baseline shrink-0 whitespace-nowrap overflow-visible"
|
class="text-14-regular text-text-strong cursor-default inline-flex items-baseline shrink-0 whitespace-nowrap overflow-visible"
|
||||||
aria-label={label()}
|
aria-label={label()}
|
||||||
style={{
|
style={{
|
||||||
"--tool-motion-odometer-ms": `${props.countDuration ?? 600}ms`,
|
"--tool-motion-odometer-ms": "600ms",
|
||||||
"--tool-motion-mask": `${props.countMask ?? 18}%`,
|
"--tool-motion-mask": "18%",
|
||||||
"--tool-motion-mask-height": `${props.countMaskHeight ?? 0}px`,
|
"--tool-motion-mask-height": "0px",
|
||||||
"--tool-motion-spring-ms": `${props.countWidthDuration ?? 560}ms`,
|
"--tool-motion-spring-ms": "560ms",
|
||||||
opacity: `${Math.max(0, Math.min(1, 1 - shut()))}`,
|
opacity: `${Math.max(0, Math.min(1, 1 - shut()))}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -155,10 +127,10 @@ export function SessionTodoDock(props: {
|
|||||||
>
|
>
|
||||||
<TextReveal
|
<TextReveal
|
||||||
class="text-14-regular text-text-base cursor-default"
|
class="text-14-regular text-text-base cursor-default"
|
||||||
text={store.collapsed ? preview() : undefined}
|
text={collapsed() ? preview() : undefined}
|
||||||
duration={props.subtitleDuration ?? 600}
|
duration={600}
|
||||||
travel={props.subtitleTravel ?? 25}
|
travel={25}
|
||||||
edge={props.subtitleEdge ?? 17}
|
edge={17}
|
||||||
spring="cubic-bezier(0.34, 1, 0.64, 1)"
|
spring="cubic-bezier(0.34, 1, 0.64, 1)"
|
||||||
springSoft="cubic-bezier(0.34, 1, 0.64, 1)"
|
springSoft="cubic-bezier(0.34, 1, 0.64, 1)"
|
||||||
growOnly
|
growOnly
|
||||||
@@ -168,7 +140,7 @@ export function SessionTodoDock(props: {
|
|||||||
<div class="ml-auto">
|
<div class="ml-auto">
|
||||||
<IconButton
|
<IconButton
|
||||||
data-action="session-todo-toggle-button"
|
data-action="session-todo-toggle-button"
|
||||||
data-collapsed={store.collapsed ? "true" : "false"}
|
data-collapsed={collapsed() ? "true" : "false"}
|
||||||
icon="chevron-down"
|
icon="chevron-down"
|
||||||
size="normal"
|
size="normal"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -181,14 +153,14 @@ export function SessionTodoDock(props: {
|
|||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
toggle()
|
toggle()
|
||||||
}}
|
}}
|
||||||
aria-label={store.collapsed ? props.expandLabel : props.collapseLabel}
|
aria-label={collapsed() ? props.expandLabel : props.collapseLabel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
data-slot="session-todo-list"
|
data-slot="session-todo-list"
|
||||||
aria-hidden={store.collapsed || off()}
|
aria-hidden={collapsed() || off()}
|
||||||
classList={{
|
classList={{
|
||||||
"pointer-events-none": hide() > 0.1,
|
"pointer-events-none": hide() > 0.1,
|
||||||
}}
|
}}
|
||||||
@@ -197,7 +169,7 @@ export function SessionTodoDock(props: {
|
|||||||
opacity: `${Math.max(0, Math.min(1, 1 - hide()))}`,
|
opacity: `${Math.max(0, Math.min(1, 1 - hide()))}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TodoList todos={props.todos} open={!store.collapsed} />
|
<TodoList todos={props.todos} open={!collapsed()} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DockTray>
|
</DockTray>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { createEffect, createMemo, Match, on, onCleanup, Switch } from "solid-js"
|
import { createEffect, createMemo, Match, on, onCleanup, Switch } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { Dynamic } from "solid-js/web"
|
import { Dynamic } from "solid-js/web"
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
import type { FileSearchHandle } from "@opencode-ai/ui/file"
|
import type { FileSearchHandle } from "@opencode-ai/ui/file"
|
||||||
import { useFileComponent } from "@opencode-ai/ui/context/file"
|
import { useFileComponent } from "@opencode-ai/ui/context/file"
|
||||||
import { cloneSelectedLineRange, previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
|
import { cloneSelectedLineRange, previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
|
||||||
@@ -12,12 +11,12 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
|
|||||||
import { Tabs } from "@opencode-ai/ui/tabs"
|
import { Tabs } from "@opencode-ai/ui/tabs"
|
||||||
import { ScrollView } from "@opencode-ai/ui/scroll-view"
|
import { ScrollView } from "@opencode-ai/ui/scroll-view"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import { useLayout } from "@/context/layout"
|
|
||||||
import { selectionFromLines, useFile, type FileSelection, type SelectedLineRange } from "@/context/file"
|
import { selectionFromLines, useFile, type FileSelection, type SelectedLineRange } from "@/context/file"
|
||||||
import { useComments } from "@/context/comments"
|
import { useComments } from "@/context/comments"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { usePrompt } from "@/context/prompt"
|
import { usePrompt } from "@/context/prompt"
|
||||||
import { getSessionHandoff } from "@/pages/session/handoff"
|
import { getSessionHandoff } from "@/pages/session/handoff"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
|
|
||||||
function FileCommentMenu(props: {
|
function FileCommentMenu(props: {
|
||||||
moreLabel: string
|
moreLabel: string
|
||||||
@@ -53,17 +52,12 @@ function FileCommentMenu(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function FileTabContent(props: { tab: string }) {
|
export function FileTabContent(props: { tab: string }) {
|
||||||
const params = useParams()
|
|
||||||
const layout = useLayout()
|
|
||||||
const file = useFile()
|
const file = useFile()
|
||||||
const comments = useComments()
|
const comments = useComments()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const prompt = usePrompt()
|
const prompt = usePrompt()
|
||||||
const fileComponent = useFileComponent()
|
const fileComponent = useFileComponent()
|
||||||
|
const { sessionKey, tabs, view } = useSessionLayout()
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
|
|
||||||
let scroll: HTMLDivElement | undefined
|
let scroll: HTMLDivElement | undefined
|
||||||
let scrollFrame: number | undefined
|
let scrollFrame: number | undefined
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { For, createEffect, createMemo, on, onCleanup, Show, Index, type JSX } from "solid-js"
|
import { For, createEffect, createMemo, on, onCleanup, Show, Index, type JSX } from "solid-js"
|
||||||
import { createStore, produce } from "solid-js/store"
|
import { createStore, produce } from "solid-js/store"
|
||||||
import { useNavigate, useParams } from "@solidjs/router"
|
import { useNavigate } from "@solidjs/router"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
@@ -19,6 +19,7 @@ import { shouldMarkBoundaryGesture, normalizeWheelDelta } from "@/pages/session/
|
|||||||
import { SessionContextUsage } from "@/components/session-context-usage"
|
import { SessionContextUsage } from "@/components/session-context-usage"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
|
import { useSessionKey } from "@/pages/session/session-layout"
|
||||||
import { useSettings } from "@/context/settings"
|
import { useSettings } from "@/context/settings"
|
||||||
import { useSDK } from "@/context/sdk"
|
import { useSDK } from "@/context/sdk"
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
@@ -213,16 +214,15 @@ export function MessageTimeline(props: {
|
|||||||
}) {
|
}) {
|
||||||
let touchGesture: number | undefined
|
let touchGesture: number | undefined
|
||||||
|
|
||||||
const params = useParams()
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const settings = useSettings()
|
const settings = useSettings()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
|
const { params, sessionKey } = useSessionKey()
|
||||||
|
|
||||||
const rendered = createMemo(() => props.renderedUserMessages.map((message) => message.id))
|
const rendered = createMemo(() => props.renderedUserMessages.map((message) => message.id))
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const sessionID = createMemo(() => params.id)
|
const sessionID = createMemo(() => params.id)
|
||||||
const sessionMessages = createMemo(() => {
|
const sessionMessages = createMemo(() => {
|
||||||
const id = sessionID()
|
const id = sessionID()
|
||||||
|
|||||||
20
packages/app/src/pages/session/session-layout.ts
Normal file
20
packages/app/src/pages/session/session-layout.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useParams } from "@solidjs/router"
|
||||||
|
import { createMemo } from "solid-js"
|
||||||
|
import { useLayout } from "@/context/layout"
|
||||||
|
|
||||||
|
export const useSessionKey = () => {
|
||||||
|
const params = useParams()
|
||||||
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
||||||
|
return { params, sessionKey }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSessionLayout = () => {
|
||||||
|
const layout = useLayout()
|
||||||
|
const { params, sessionKey } = useSessionKey()
|
||||||
|
return {
|
||||||
|
params,
|
||||||
|
sessionKey,
|
||||||
|
tabs: createMemo(() => layout.tabs(sessionKey)),
|
||||||
|
view: createMemo(() => layout.view(sessionKey)),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { For, Match, Show, Switch, createEffect, createMemo, onCleanup, type JSX } from "solid-js"
|
import { For, Match, Show, Switch, createEffect, createMemo, onCleanup, type JSX } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { createMediaQuery } from "@solid-primitives/media"
|
import { createMediaQuery } from "@solid-primitives/media"
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
import { Tabs } from "@opencode-ai/ui/tabs"
|
import { Tabs } from "@opencode-ai/ui/tabs"
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
@@ -26,6 +25,7 @@ import { FileTabContent } from "@/pages/session/file-tabs"
|
|||||||
import { createOpenSessionFileTab, getTabReorderIndex, type Sizing } from "@/pages/session/helpers"
|
import { createOpenSessionFileTab, getTabReorderIndex, type Sizing } from "@/pages/session/helpers"
|
||||||
import { StickyAddButton } from "@/pages/session/review-tab"
|
import { StickyAddButton } from "@/pages/session/review-tab"
|
||||||
import { setSessionHandoff } from "@/pages/session/handoff"
|
import { setSessionHandoff } from "@/pages/session/handoff"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
|
|
||||||
export function SessionSidePanel(props: {
|
export function SessionSidePanel(props: {
|
||||||
reviewPanel: () => JSX.Element
|
reviewPanel: () => JSX.Element
|
||||||
@@ -34,18 +34,15 @@ export function SessionSidePanel(props: {
|
|||||||
reviewSnap: boolean
|
reviewSnap: boolean
|
||||||
size: Sizing
|
size: Sizing
|
||||||
}) {
|
}) {
|
||||||
const params = useParams()
|
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const file = useFile()
|
const file = useFile()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
|
const { params, sessionKey, tabs, view } = useSessionLayout()
|
||||||
|
|
||||||
const isDesktop = createMediaQuery("(min-width: 768px)")
|
const isDesktop = createMediaQuery("(min-width: 768px)")
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
|
|
||||||
const reviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened())
|
const reviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened())
|
||||||
const fileOpen = createMemo(() => isDesktop() && layout.fileTree.opened())
|
const fileOpen = createMemo(() => isDesktop() && layout.fileTree.opened())
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { For, Show, createEffect, createMemo, on, onCleanup } from "solid-js"
|
import { For, Show, createEffect, createMemo, on, onCleanup } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
import { Tabs } from "@opencode-ai/ui/tabs"
|
import { Tabs } from "@opencode-ai/ui/tabs"
|
||||||
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
@@ -18,16 +17,14 @@ import { useTerminal, type LocalPTY } from "@/context/terminal"
|
|||||||
import { terminalTabLabel } from "@/pages/session/terminal-label"
|
import { terminalTabLabel } from "@/pages/session/terminal-label"
|
||||||
import { createSizing, focusTerminalById } from "@/pages/session/helpers"
|
import { createSizing, focusTerminalById } from "@/pages/session/helpers"
|
||||||
import { getTerminalHandoff, setTerminalHandoff } from "@/pages/session/handoff"
|
import { getTerminalHandoff, setTerminalHandoff } from "@/pages/session/handoff"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
|
|
||||||
export function TerminalPanel() {
|
export function TerminalPanel() {
|
||||||
const params = useParams()
|
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const terminal = useTerminal()
|
const terminal = useTerminal()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
|
const { params, view } = useSessionLayout()
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
|
|
||||||
const opened = createMemo(() => view().terminal.opened())
|
const opened = createMemo(() => view().terminal.opened())
|
||||||
const size = createSizing()
|
const size = createSizing()
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { createMemo } from "solid-js"
|
import { createMemo } from "solid-js"
|
||||||
import { useNavigate, useParams } from "@solidjs/router"
|
import { useNavigate } from "@solidjs/router"
|
||||||
import { useCommand, type CommandOption } from "@/context/command"
|
import { useCommand, type CommandOption } from "@/context/command"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
|
import { previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
|
||||||
import { useFile, selectionFromLines, type FileSelection, type SelectedLineRange } from "@/context/file"
|
import { useFile, selectionFromLines, type FileSelection, type SelectedLineRange } from "@/context/file"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { useLayout } from "@/context/layout"
|
import { useLayout } from "@/context/layout"
|
||||||
@@ -19,6 +20,7 @@ import { showToast } from "@opencode-ai/ui/toast"
|
|||||||
import { findLast } from "@opencode-ai/util/array"
|
import { findLast } from "@opencode-ai/util/array"
|
||||||
import { extractPromptFromParts } from "@/utils/prompt"
|
import { extractPromptFromParts } from "@/utils/prompt"
|
||||||
import { UserMessage } from "@opencode-ai/sdk/v2"
|
import { UserMessage } from "@opencode-ai/sdk/v2"
|
||||||
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
|
|
||||||
export type SessionCommandContext = {
|
export type SessionCommandContext = {
|
||||||
navigateMessageByOffset: (offset: number) => void
|
navigateMessageByOffset: (offset: number) => void
|
||||||
@@ -45,12 +47,9 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const terminal = useTerminal()
|
const terminal = useTerminal()
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const params = useParams()
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { params, tabs, view } = useSessionLayout()
|
||||||
|
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
||||||
|
|
||||||
const idle = { type: "idle" as const }
|
const idle = { type: "idle" as const }
|
||||||
@@ -71,11 +70,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||||||
const selectionPreview = (path: string, selection: FileSelection) => {
|
const selectionPreview = (path: string, selection: FileSelection) => {
|
||||||
const content = file.get(path)?.content?.content
|
const content = file.get(path)?.content?.content
|
||||||
if (!content) return undefined
|
if (!content) return undefined
|
||||||
const start = Math.max(1, Math.min(selection.startLine, selection.endLine))
|
return previewSelectedLines(content, { start: selection.startLine, end: selection.endLine })
|
||||||
const end = Math.max(selection.startLine, selection.endLine)
|
|
||||||
const lines = content.split("\n").slice(start - 1, end)
|
|
||||||
if (lines.length === 0) return undefined
|
|
||||||
return lines.slice(0, 2).join("\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const addSelectionToContext = (path: string, selection: FileSelection) => {
|
const addSelectionToContext = (path: string, selection: FileSelection) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user