Brendan Allan 4 settimane fa
parent
commit
32d542d1c6

+ 3 - 3
packages/app/src/components/prompt-input.tsx

@@ -310,9 +310,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
   })
 
   const hasUserPrompt = createMemo(() => {
-    const id = params.id
-    if (!id) return false
-    const messages = sync.data.message[id]
+    const sessionID = params.id
+    if (!sessionID) return false
+    const messages = sync.data.message[sessionID]
     if (!messages) return false
     return messages.some((m) => m.role === "user")
   })

+ 45 - 43
packages/app/src/pages/session.tsx

@@ -1620,7 +1620,6 @@ export default function Page() {
 
   const { clearMessageHash, scrollToMessage } = useSessionHashScroll({
     sessionKey,
-    sessionID: () => params.id,
     messagesReady,
     visibleUserMessages,
     turnStart: historyWindow.turnStart,
@@ -1694,48 +1693,51 @@ export default function Page() {
           <div class="flex-1 min-h-0 overflow-hidden">
             <Switch>
               <Match when={params.id}>
-                <Show when={lastUserMessage()}>
-                  <MessageTimeline
-                    mobileChanges={mobileChanges()}
-                    mobileFallback={reviewContent({
-                      diffStyle: "unified",
-                      classes: {
-                        root: "pb-8",
-                        header: "px-4",
-                        container: "px-4",
-                      },
-                      loadingClass: "px-4 py-4 text-text-weak",
-                      emptyClass: "h-full pb-64 -mt-4 flex flex-col items-center justify-center text-center gap-6",
-                    })}
-                    actions={actions}
-                    scroll={ui.scroll}
-                    onResumeScroll={resumeScroll}
-                    setScrollRef={setScrollRef}
-                    onScheduleScrollState={scheduleScrollState}
-                    onAutoScrollHandleScroll={autoScroll.handleScroll}
-                    onMarkScrollGesture={markScrollGesture}
-                    hasScrollGesture={hasScrollGesture}
-                    onUserScroll={markUserScroll}
-                    onTurnBackfillScroll={historyWindow.onScrollerScroll}
-                    onAutoScrollInteraction={autoScroll.handleInteraction}
-                    centered={centered()}
-                    setContentRef={(el) => {
-                      content = el
-                      autoScroll.contentRef(el)
-
-                      const root = scroller
-                      if (root) scheduleScrollState(root)
-                    }}
-                    turnStart={historyWindow.turnStart()}
-                    historyMore={historyMore()}
-                    historyLoading={historyLoading()}
-                    onLoadEarlier={() => {
-                      void historyWindow.loadAndReveal()
-                    }}
-                    renderedUserMessages={historyWindow.renderedUserMessages()}
-                    anchor={anchor}
-                  />
-                </Show>
+                {(sessionID) => (
+                  <Show when={lastUserMessage()}>
+                    <MessageTimeline
+                      sessionID={sessionID()}
+                      mobileChanges={mobileChanges()}
+                      mobileFallback={reviewContent({
+                        diffStyle: "unified",
+                        classes: {
+                          root: "pb-8",
+                          header: "px-4",
+                          container: "px-4",
+                        },
+                        loadingClass: "px-4 py-4 text-text-weak",
+                        emptyClass: "h-full pb-64 -mt-4 flex flex-col items-center justify-center text-center gap-6",
+                      })}
+                      actions={actions}
+                      scroll={ui.scroll}
+                      onResumeScroll={resumeScroll}
+                      setScrollRef={setScrollRef}
+                      onScheduleScrollState={scheduleScrollState}
+                      onAutoScrollHandleScroll={autoScroll.handleScroll}
+                      onMarkScrollGesture={markScrollGesture}
+                      hasScrollGesture={hasScrollGesture}
+                      onUserScroll={markUserScroll}
+                      onTurnBackfillScroll={historyWindow.onScrollerScroll}
+                      onAutoScrollInteraction={autoScroll.handleInteraction}
+                      centered={centered()}
+                      setContentRef={(el) => {
+                        content = el
+                        autoScroll.contentRef(el)
+
+                        const root = scroller
+                        if (root) scheduleScrollState(root)
+                      }}
+                      turnStart={historyWindow.turnStart()}
+                      historyMore={historyMore()}
+                      historyLoading={historyLoading()}
+                      onLoadEarlier={() => {
+                        void historyWindow.loadAndReveal()
+                      }}
+                      renderedUserMessages={historyWindow.renderedUserMessages()}
+                      anchor={anchor}
+                    />
+                  </Show>
+                )}
               </Match>
               <Match when={true}>
                 <NewSessionView worktree={newSessionWorktree()} />

+ 178 - 187
packages/app/src/pages/session/message-timeline.tsx

@@ -29,7 +29,6 @@ import { normalizeWheelDelta, shouldMarkBoundaryGesture } from "@/pages/session/
 import { useSessionKey } from "@/pages/session/session-layout"
 import { messageAgentColor } from "@/utils/agent"
 import { parseCommentNote, readCommentMetadata } from "@/utils/comment-note"
-import { Optional } from "@/utils/optional"
 
 type MessageComment = {
   path: string
@@ -196,6 +195,7 @@ function createTimelineStaging(input: TimelineStageInput) {
 }
 
 export function MessageTimeline(props: {
+  sessionID: string
   mobileChanges: boolean
   mobileFallback: JSX.Element
   actions?: UserActions
@@ -227,17 +227,17 @@ export function MessageTimeline(props: {
   const settings = useSettings()
   const dialog = useDialog()
   const language = useLanguage()
-  const { params, sessionKey } = useSessionKey()
+  const { sessionKey } = useSessionKey()
   const platform = usePlatform()
 
   const rendered = createMemo(() => props.renderedUserMessages.map((message) => message.id))
-  const sessionMessages = createMemo(() => Optional.map(params.id, (id) => sync.data.message[id]) ?? emptyMessages)
+  const sessionMessages = createMemo(() => sync.data.message[props.sessionID] ?? emptyMessages)
   const pending = createMemo(() =>
     sessionMessages().findLast(
       (item): item is AssistantMessage => item.role === "assistant" && typeof item.time.completed !== "number",
     ),
   )
-  const sessionStatus = createMemo(() => Optional.map(params.id, (id) => sync.data.session_status[id]) ?? idle)
+  const sessionStatus = createMemo(() => sync.data.session_status[props.sessionID] ?? idle)
   const working = createMemo(() => !!pending() || sessionStatus().type !== "idle")
   const tint = createMemo(() => messageAgentColor(sessionMessages(), sync.data.agent))
 
@@ -292,7 +292,7 @@ export function MessageTimeline(props: {
 
     return undefined
   })
-  const info = createMemo(() => Optional.map(params.id, (id) => sync.session.get(id)))
+  const info = createMemo(() => sync.session.get(props.sessionID))
   const titleValue = createMemo(() => info()?.title)
   const shareUrl = createMemo(() => info()?.share?.url)
   const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
@@ -326,12 +326,11 @@ export function MessageTimeline(props: {
   const [req, setReq] = createStore({ share: false, unshare: false })
 
   const shareSession = () => {
-    const id = params.id
-    if (!id || req.share) return
+    if (req.share) return
     if (!shareEnabled()) return
     setReq("share", true)
     globalSDK.client.session
-      .share({ sessionID: id, directory: sdk.directory })
+      .share({ sessionID: props.sessionID, directory: sdk.directory })
       .catch((err: unknown) => {
         console.error("Failed to share session", err)
       })
@@ -341,12 +340,11 @@ export function MessageTimeline(props: {
   }
 
   const unshareSession = () => {
-    const id = params.id
-    if (!id || req.unshare) return
+    if (req.unshare) return
     if (!shareEnabled()) return
     setReq("unshare", true)
     globalSDK.client.session
-      .unshare({ sessionID: id, directory: sdk.directory })
+      .unshare({ sessionID: props.sessionID, directory: sdk.directory })
       .catch((err: unknown) => {
         console.error("Failed to unshare session", err)
       })
@@ -387,7 +385,6 @@ export function MessageTimeline(props: {
   )
 
   const openTitleEditor = () => {
-    if (!params.id) return
     setTitle({ editing: true, draft: titleValue() ?? "" })
     requestAnimationFrame(() => {
       titleRef?.focus()
@@ -401,8 +398,6 @@ export function MessageTimeline(props: {
   }
 
   const saveTitleEditor = async () => {
-    const id = params.id
-    if (!id) return
     if (title.saving) return
 
     const next = title.draft.trim()
@@ -413,11 +408,11 @@ export function MessageTimeline(props: {
 
     setTitle("saving", true)
     await sdk.client.session
-      .update({ sessionID: id, title: next })
+      .update({ sessionID: props.sessionID, title: next })
       .then(() => {
         sync.set(
           produce((draft) => {
-            const index = draft.session.findIndex((s) => s.id === id)
+            const index = draft.session.findIndex((s) => s.id === props.sessionID)
             if (index !== -1) draft.session[index].title = next
           }),
         )
@@ -433,7 +428,7 @@ export function MessageTimeline(props: {
   }
 
   const navigateAfterSessionRemoval = (id: string, parentID?: string, nextSessionID?: string) => {
-    if (params.id !== id) return
+    if (props.sessionID !== id) return
     if (parentID) {
       navigate(`../${parentID}`)
       return
@@ -722,184 +717,180 @@ export function MessageTimeline(props: {
                       </Show>
                     </div>
                   </div>
-                  <Show when={params.id}>
-                    {(id) => (
-                      <div class="shrink-0 flex items-center gap-3">
-                        <SessionContextUsage placement="bottom" />
-                        <DropdownMenu
-                          gutter={4}
-                          placement="bottom-end"
-                          open={title.menuOpen}
-                          onOpenChange={(open) => {
-                            setTitle("menuOpen", open)
-                            if (open) return
+                  <div class="shrink-0 flex items-center gap-3">
+                    <SessionContextUsage placement="bottom" />
+                    <DropdownMenu
+                      gutter={4}
+                      placement="bottom-end"
+                      open={title.menuOpen}
+                      onOpenChange={(open) => {
+                        setTitle("menuOpen", open)
+                        if (open) return
+                      }}
+                    >
+                      <DropdownMenu.Trigger
+                        as={IconButton}
+                        icon="dot-grid"
+                        variant="ghost"
+                        class="size-6 rounded-md data-[expanded]:bg-surface-base-active"
+                        classList={{
+                          "bg-surface-base-active": share.open || title.pendingShare,
+                        }}
+                        aria-label={language.t("common.moreOptions")}
+                        aria-expanded={title.menuOpen || share.open || title.pendingShare}
+                        ref={(el: HTMLButtonElement) => {
+                          more = el
+                        }}
+                      />
+                      <DropdownMenu.Portal>
+                        <DropdownMenu.Content
+                          style={{ "min-width": "104px" }}
+                          onCloseAutoFocus={(event) => {
+                            if (title.pendingRename) {
+                              event.preventDefault()
+                              setTitle("pendingRename", false)
+                              openTitleEditor()
+                              return
+                            }
+                            if (title.pendingShare) {
+                              event.preventDefault()
+                              requestAnimationFrame(() => {
+                                setShare({ open: true, dismiss: null })
+                                setTitle("pendingShare", false)
+                              })
+                            }
                           }}
                         >
-                          <DropdownMenu.Trigger
-                            as={IconButton}
-                            icon="dot-grid"
-                            variant="ghost"
-                            class="size-6 rounded-md data-[expanded]:bg-surface-base-active"
-                            classList={{
-                              "bg-surface-base-active": share.open || title.pendingShare,
-                            }}
-                            aria-label={language.t("common.moreOptions")}
-                            aria-expanded={title.menuOpen || share.open || title.pendingShare}
-                            ref={(el: HTMLButtonElement) => {
-                              more = el
+                          <DropdownMenu.Item
+                            onSelect={() => {
+                              setTitle("pendingRename", true)
+                              setTitle("menuOpen", false)
                             }}
-                          />
-                          <DropdownMenu.Portal>
-                            <DropdownMenu.Content
-                              style={{ "min-width": "104px" }}
-                              onCloseAutoFocus={(event) => {
-                                if (title.pendingRename) {
-                                  event.preventDefault()
-                                  setTitle("pendingRename", false)
-                                  openTitleEditor()
-                                  return
-                                }
-                                if (title.pendingShare) {
-                                  event.preventDefault()
-                                  requestAnimationFrame(() => {
-                                    setShare({ open: true, dismiss: null })
-                                    setTitle("pendingShare", false)
-                                  })
-                                }
+                          >
+                            <DropdownMenu.ItemLabel>{language.t("common.rename")}</DropdownMenu.ItemLabel>
+                          </DropdownMenu.Item>
+                          <Show when={shareEnabled()}>
+                            <DropdownMenu.Item
+                              onSelect={() => {
+                                setTitle({ pendingShare: true, menuOpen: false })
                               }}
                             >
-                              <DropdownMenu.Item
-                                onSelect={() => {
-                                  setTitle("pendingRename", true)
-                                  setTitle("menuOpen", false)
-                                }}
-                              >
-                                <DropdownMenu.ItemLabel>{language.t("common.rename")}</DropdownMenu.ItemLabel>
-                              </DropdownMenu.Item>
-                              <Show when={shareEnabled()}>
-                                <DropdownMenu.Item
-                                  onSelect={() => {
-                                    setTitle({ pendingShare: true, menuOpen: false })
-                                  }}
-                                >
-                                  <DropdownMenu.ItemLabel>
-                                    {language.t("session.share.action.share")}
-                                  </DropdownMenu.ItemLabel>
-                                </DropdownMenu.Item>
-                              </Show>
-                              <DropdownMenu.Item onSelect={() => void archiveSession(id())}>
-                                <DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel>
-                              </DropdownMenu.Item>
-                              <DropdownMenu.Separator />
-                              <DropdownMenu.Item
-                                onSelect={() => dialog.show(() => <DialogDeleteSession sessionID={id()} />)}
-                              >
-                                <DropdownMenu.ItemLabel>{language.t("common.delete")}</DropdownMenu.ItemLabel>
-                              </DropdownMenu.Item>
-                            </DropdownMenu.Content>
-                          </DropdownMenu.Portal>
-                        </DropdownMenu>
-
-                        <KobaltePopover
-                          open={share.open}
-                          anchorRef={() => more}
-                          placement="bottom-end"
-                          gutter={4}
-                          modal={false}
-                          onOpenChange={(open) => {
-                            if (open) setShare("dismiss", null)
-                            setShare("open", open)
+                              <DropdownMenu.ItemLabel>
+                                {language.t("session.share.action.share")}
+                              </DropdownMenu.ItemLabel>
+                            </DropdownMenu.Item>
+                          </Show>
+                          <DropdownMenu.Item onSelect={() => void archiveSession(props.sessionID)}>
+                            <DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel>
+                          </DropdownMenu.Item>
+                          <DropdownMenu.Separator />
+                          <DropdownMenu.Item
+                            onSelect={() => dialog.show(() => <DialogDeleteSession sessionID={props.sessionID} />)}
+                          >
+                            <DropdownMenu.ItemLabel>{language.t("common.delete")}</DropdownMenu.ItemLabel>
+                          </DropdownMenu.Item>
+                        </DropdownMenu.Content>
+                      </DropdownMenu.Portal>
+                    </DropdownMenu>
+
+                    <KobaltePopover
+                      open={share.open}
+                      anchorRef={() => more}
+                      placement="bottom-end"
+                      gutter={4}
+                      modal={false}
+                      onOpenChange={(open) => {
+                        if (open) setShare("dismiss", null)
+                        setShare("open", open)
+                      }}
+                    >
+                      <KobaltePopover.Portal>
+                        <KobaltePopover.Content
+                          data-component="popover-content"
+                          style={{ "min-width": "320px" }}
+                          onEscapeKeyDown={(event) => {
+                            setShare({ dismiss: "escape", open: false })
+                            event.preventDefault()
+                            event.stopPropagation()
+                          }}
+                          onPointerDownOutside={() => {
+                            setShare({ dismiss: "outside", open: false })
+                          }}
+                          onFocusOutside={() => {
+                            setShare({ dismiss: "outside", open: false })
+                          }}
+                          onCloseAutoFocus={(event) => {
+                            if (share.dismiss === "outside") event.preventDefault()
+                            setShare("dismiss", null)
                           }}
                         >
-                          <KobaltePopover.Portal>
-                            <KobaltePopover.Content
-                              data-component="popover-content"
-                              style={{ "min-width": "320px" }}
-                              onEscapeKeyDown={(event) => {
-                                setShare({ dismiss: "escape", open: false })
-                                event.preventDefault()
-                                event.stopPropagation()
-                              }}
-                              onPointerDownOutside={() => {
-                                setShare({ dismiss: "outside", open: false })
-                              }}
-                              onFocusOutside={() => {
-                                setShare({ dismiss: "outside", open: false })
-                              }}
-                              onCloseAutoFocus={(event) => {
-                                if (share.dismiss === "outside") event.preventDefault()
-                                setShare("dismiss", null)
-                              }}
-                            >
-                              <div class="flex flex-col p-3">
-                                <div class="flex flex-col gap-1">
-                                  <div class="text-13-medium text-text-strong">
-                                    {language.t("session.share.popover.title")}
-                                  </div>
-                                  <div class="text-12-regular text-text-weak">
-                                    {shareUrl()
-                                      ? language.t("session.share.popover.description.shared")
-                                      : language.t("session.share.popover.description.unshared")}
-                                  </div>
-                                </div>
-                                <div class="mt-3 flex flex-col gap-2">
-                                  <Show
-                                    when={shareUrl()}
-                                    fallback={
-                                      <Button
-                                        size="large"
-                                        variant="primary"
-                                        class="w-full"
-                                        onClick={shareSession}
-                                        disabled={req.share}
-                                      >
-                                        {req.share
-                                          ? language.t("session.share.action.publishing")
-                                          : language.t("session.share.action.publish")}
-                                      </Button>
-                                    }
+                          <div class="flex flex-col p-3">
+                            <div class="flex flex-col gap-1">
+                              <div class="text-13-medium text-text-strong">
+                                {language.t("session.share.popover.title")}
+                              </div>
+                              <div class="text-12-regular text-text-weak">
+                                {shareUrl()
+                                  ? language.t("session.share.popover.description.shared")
+                                  : language.t("session.share.popover.description.unshared")}
+                              </div>
+                            </div>
+                            <div class="mt-3 flex flex-col gap-2">
+                              <Show
+                                when={shareUrl()}
+                                fallback={
+                                  <Button
+                                    size="large"
+                                    variant="primary"
+                                    class="w-full"
+                                    onClick={shareSession}
+                                    disabled={req.share}
                                   >
-                                    <div class="flex flex-col gap-2">
-                                      <TextField
-                                        value={shareUrl() ?? ""}
-                                        readOnly
-                                        copyable
-                                        copyKind="link"
-                                        tabIndex={-1}
-                                        class="w-full"
-                                      />
-                                      <div class="grid grid-cols-2 gap-2">
-                                        <Button
-                                          size="large"
-                                          variant="secondary"
-                                          class="w-full shadow-none border border-border-weak-base"
-                                          onClick={unshareSession}
-                                          disabled={req.unshare}
-                                        >
-                                          {req.unshare
-                                            ? language.t("session.share.action.unpublishing")
-                                            : language.t("session.share.action.unpublish")}
-                                        </Button>
-                                        <Button
-                                          size="large"
-                                          variant="primary"
-                                          class="w-full"
-                                          onClick={viewShare}
-                                          disabled={req.unshare}
-                                        >
-                                          {language.t("session.share.action.view")}
-                                        </Button>
-                                      </div>
-                                    </div>
-                                  </Show>
+                                    {req.share
+                                      ? language.t("session.share.action.publishing")
+                                      : language.t("session.share.action.publish")}
+                                  </Button>
+                                }
+                              >
+                                <div class="flex flex-col gap-2">
+                                  <TextField
+                                    value={shareUrl() ?? ""}
+                                    readOnly
+                                    copyable
+                                    copyKind="link"
+                                    tabIndex={-1}
+                                    class="w-full"
+                                  />
+                                  <div class="grid grid-cols-2 gap-2">
+                                    <Button
+                                      size="large"
+                                      variant="secondary"
+                                      class="w-full shadow-none border border-border-weak-base"
+                                      onClick={unshareSession}
+                                      disabled={req.unshare}
+                                    >
+                                      {req.unshare
+                                        ? language.t("session.share.action.unpublishing")
+                                        : language.t("session.share.action.unpublish")}
+                                    </Button>
+                                    <Button
+                                      size="large"
+                                      variant="primary"
+                                      class="w-full"
+                                      onClick={viewShare}
+                                      disabled={req.unshare}
+                                    >
+                                      {language.t("session.share.action.view")}
+                                    </Button>
+                                  </div>
                                 </div>
-                              </div>
-                            </KobaltePopover.Content>
-                          </KobaltePopover.Portal>
-                        </KobaltePopover>
-                      </div>
-                    )}
-                  </Show>
+                              </Show>
+                            </div>
+                          </div>
+                        </KobaltePopover.Content>
+                      </KobaltePopover.Portal>
+                    </KobaltePopover>
+                  </div>
                 </div>
               </div>
             </Show>
@@ -987,7 +978,7 @@ export function MessageTimeline(props: {
                         </div>
                       </Show>
                       <SessionTurn
-                        sessionID={params.id ?? ""}
+                        sessionID={props.sessionID}
                         messageID={messageID}
                         actions={props.actions}
                         active={active()}

+ 19 - 20
packages/app/src/pages/session/use-session-commands.tsx

@@ -126,8 +126,8 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
   const permissionsCommand = withCategory(language.t("command.category.permissions"))
 
   const isAutoAcceptActive = () => {
-    const id = params.id
-    if (id) return permission.isAutoAccepting(id, sdk.directory)
+    const sessionID = params.id
+    if (sessionID) return permission.isAutoAccepting(sessionID, sdk.directory)
     return permission.isAutoAcceptingDirectory(sdk.directory)
   }
   command.register("session", () => {
@@ -146,8 +146,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
               slash: "share",
               disabled: !params.id,
               onSelect: async () => {
-                const id = params.id
-                if (!id) return
+                if (!params.id) return
 
                 const write = (value: string) => {
                   const body = typeof document === "undefined" ? undefined : document.body
@@ -199,7 +198,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
                 }
 
                 const url = await sdk.client.session
-                  .share({ sessionID: id })
+                  .share({ sessionID: params.id })
                   .then((res) => res.data?.share?.url)
                   .catch(() => undefined)
                 if (!url) {
@@ -391,12 +390,12 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
         keybind: "mod+shift+a",
         disabled: false,
         onSelect: () => {
-          const id = params.id
-          if (id) permission.toggleAutoAccept(id, sdk.directory)
+          const sessionID = params.id
+          if (sessionID) permission.toggleAutoAccept(sessionID, sdk.directory)
           else permission.toggleAutoAcceptDirectory(sdk.directory)
 
-          const active = id
-            ? permission.isAutoAccepting(id, sdk.directory)
+          const active = sessionID
+            ? permission.isAutoAccepting(sessionID, sdk.directory)
             : permission.isAutoAcceptingDirectory(sdk.directory)
           showToast({
             title: active
@@ -415,16 +414,16 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
         slash: "undo",
         disabled: !params.id || visibleUserMessages().length === 0,
         onSelect: async () => {
-          const id = params.id
-          if (!id) return
+          const sessionID = params.id
+          if (!sessionID) return
           if (status().type !== "idle") {
-            await sdk.client.session.abort({ sessionID: id }).catch(() => {})
+            await sdk.client.session.abort({ sessionID }).catch(() => {})
           }
           const revert = info()?.revert?.messageID
           const message = findLast(userMessages(), (x) => !revert || x.id < revert)
           if (!message) return
           await sdk.client.session.revert({
-            sessionID: id,
+            sessionID,
             messageID: message.id,
           })
           const parts = sync.data.part[message.id]
@@ -445,20 +444,20 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
         slash: "redo",
         disabled: !params.id || !info()?.revert?.messageID,
         onSelect: async () => {
-          const id = params.id
-          if (!id) return
+          const sessionID = params.id
+          if (!sessionID) return
           const revertMessageID = info()?.revert?.messageID
           if (!revertMessageID) return
           const nextMessage = userMessages().find((x) => x.id > revertMessageID)
           if (!nextMessage) {
-            await sdk.client.session.unrevert({ sessionID: id })
+            await sdk.client.session.unrevert({ sessionID })
             prompt.reset()
             const lastMsg = findLast(userMessages(), (x) => x.id >= revertMessageID)
             setActiveMessage(lastMsg)
             return
           }
           await sdk.client.session.revert({
-            sessionID: id,
+            sessionID,
             messageID: nextMessage.id,
           })
           const priorMsg = findLast(userMessages(), (x) => x.id < nextMessage.id)
@@ -472,8 +471,8 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
         slash: "compact",
         disabled: !params.id || visibleUserMessages().length === 0,
         onSelect: async () => {
-          const id = params.id
-          if (!id) return
+          const sessionID = params.id
+          if (!sessionID) return
           const model = local.model.current()
           if (!model) {
             showToast({
@@ -483,7 +482,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
             return
           }
           await sdk.client.session.summarize({
-            sessionID: id,
+            sessionID,
             modelID: model.id,
             providerID: model.provider.id,
           })

+ 1 - 1
packages/app/src/pages/session/use-session-hash-scroll.ts

@@ -6,7 +6,7 @@ import { useSessionLayout } from "./session-layout"
 
 export const useSessionHashScroll = (input: {
   sessionKey: () => string
-  sessionID: () => string | undefined
+
   messagesReady: () => boolean
   visibleUserMessages: () => UserMessage[]
   turnStart: () => number