|
|
@@ -1,14 +1,13 @@
|
|
|
import { createEffect, createMemo, createSignal, onCleanup, Show, type Ref } from "solid-js"
|
|
|
import { makeEventListener } from "@solid-primitives/event-listener"
|
|
|
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
|
|
+import { createMutation } from "@tanstack/solid-query"
|
|
|
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
|
|
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
|
|
import { useGlobal } from "@/context/global"
|
|
|
-import { useLanguage } from "@/context/language"
|
|
|
import { ServerConnection, serverName } from "@/context/server"
|
|
|
import { displayName, projectForSession } from "@/pages/layout/helpers"
|
|
|
import { SessionTabAvatar } from "@/pages/layout/session-tab-avatar"
|
|
|
-import { showToast } from "@/utils/toast"
|
|
|
import type { Session } from "@opencode-ai/sdk/v2"
|
|
|
import { canOpenTabRename, forwardTabRef } from "./titlebar-tab-gesture"
|
|
|
import { TabPreviewPopover } from "./titlebar-tab-popover"
|
|
|
@@ -23,8 +22,7 @@ export function TabNavItem(props: {
|
|
|
server: ServerConnection.Key
|
|
|
session: () => Session | undefined
|
|
|
fallbackTitle?: string
|
|
|
- onTitleChange?: (title: string) => void
|
|
|
- onTitleChangeFailed?: (title: string) => void
|
|
|
+ onRename: (title: string) => Promise<void>
|
|
|
onClose: () => void
|
|
|
onNavigate: () => void
|
|
|
active?: boolean
|
|
|
@@ -34,13 +32,12 @@ export function TabNavItem(props: {
|
|
|
pressed?: boolean
|
|
|
hidden?: boolean
|
|
|
}) {
|
|
|
- const language = useLanguage()
|
|
|
const [editing, setEditing] = createSignal(false)
|
|
|
const [titleOverflowing, setTitleOverflowing] = createSignal(false)
|
|
|
let tabRoot!: HTMLDivElement
|
|
|
let titleEl!: HTMLSpanElement
|
|
|
- let committing = false
|
|
|
let measureFrame: number | undefined
|
|
|
+ const rename = createMutation(() => ({ mutationFn: props.onRename }))
|
|
|
|
|
|
const closeTab = (event: MouseEvent) => {
|
|
|
event.preventDefault()
|
|
|
@@ -116,40 +113,20 @@ export function TabNavItem(props: {
|
|
|
selection?.addRange(range)
|
|
|
}
|
|
|
|
|
|
- const rename = async (title: string) => {
|
|
|
- const ctx = serverCtx()
|
|
|
- const session = props.session()
|
|
|
- if (!ctx || !session) return
|
|
|
- await ctx.sdk.api.session.rename({ sessionID: session.id, title })
|
|
|
- }
|
|
|
-
|
|
|
const closeRename = async (save: boolean) => {
|
|
|
- if (committing || !editing()) return
|
|
|
- committing = true
|
|
|
+ if (rename.isPending || !editing()) return
|
|
|
|
|
|
const original = props.session()?.title ?? ""
|
|
|
const next = (titleEl.textContent ?? "").trim()
|
|
|
|
|
|
titleEl.scrollLeft = 0
|
|
|
- if (save && next && next !== original) props.onTitleChange?.(next)
|
|
|
setEditing(false)
|
|
|
|
|
|
if (!save || !next || next === original) {
|
|
|
- committing = false
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- try {
|
|
|
- await rename(next)
|
|
|
- } catch (err) {
|
|
|
- props.onTitleChangeFailed?.(original)
|
|
|
- showToast({
|
|
|
- title: language.t("common.requestFailed"),
|
|
|
- description: err instanceof Error ? err.message : undefined,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- committing = false
|
|
|
+ await rename.mutateAsync(next)
|
|
|
}
|
|
|
|
|
|
createEffect(() => {
|
|
|
@@ -163,7 +140,7 @@ export function TabNavItem(props: {
|
|
|
const openRename = (event: MouseEvent) => {
|
|
|
event.preventDefault()
|
|
|
event.stopPropagation()
|
|
|
- if (!canOpenTabRename(props.dragging, editing(), committing)) return
|
|
|
+ if (!canOpenTabRename(props.dragging, editing(), rename.isPending)) return
|
|
|
const session = props.session()
|
|
|
if (!session) return
|
|
|
titleEl.textContent = session.title
|