>({})
+ const visibleTabs = createMemo(() => props.tabs.filter((tab) => tab.type === "draft" || visibility[tabKey(tab)]))
+ const visibleTabIds = () => visibleTabs().map(tabKey)
- const tabIds = () => props.tabs.map(tabKey)
+ command.register("titlebar-tab-cycle", () => [
+ {
+ id: `tab.prev`,
+ category: "tab",
+ title: "",
+ keybind: `mod+option+ArrowLeft,ctrl+shift+tab`,
+ hidden: true,
+ onSelect: () => selectAdjacentTab(-1),
+ },
+ {
+ id: `tab.next`,
+ category: "tab",
+ title: "",
+ keybind: `mod+option+ArrowRight,ctrl+tab`,
+ hidden: true,
+ onSelect: () => selectAdjacentTab(1),
+ },
+ ])
+
+ function selectAdjacentTab(offset: -1 | 1) {
+ const current = props.currentTab()
+ const key = adjacentTabKey(visibleTabIds(), current ? tabKey(current) : undefined, offset)
+ const next = props.tabs.find((tab) => tabKey(tab) === key)
+ if (next) props.onNavigate(next)
+ }
function refreshOverflow() {
if (!scrollRef) return
@@ -215,7 +280,7 @@ export function TitlebarTabStrip(props: {
createEffect(() => {
props.tabs.length
- tabIds()
+ visibleTabIds()
refreshOverflow()
})
@@ -251,22 +316,29 @@ export function TitlebarTabStrip(props: {
props.onNavigate(tab, tabEl ?? undefined)
}}
onDragEnd={(event) => {
- const current = tabIds()
+ const current = visibleTabIds()
const source = event.operation.source
if (event.canceled || !isSortable(source)) return
const { initialIndex, index } = source
if (initialIndex !== index) {
- props.onReorder(arrayMove(current, source.initialIndex, source.index))
+ props.onReorder(
+ mergeVisibleTabOrder(
+ props.tabs.map(tabKey),
+ current,
+ arrayMove(current, source.initialIndex, source.index),
+ ),
+ )
}
}}
>
- {(tab, index) => {
+ {(tab) => {
const id = tabKey(tab)
let ref!: HTMLDivElement
- useTabShortcut(index, () => props.onNavigate(tab, ref))
+ const visibleIndex = () => visibleTabs().findIndex((item) => tabKey(item) === id)
+ useTabShortcut(visibleIndex, () => props.onNavigate(tab, ref))
const serverCtx = createMemo(() => {
if (tab.type !== "session") return
const conn = global.servers.list().find((item) => ServerConnection.key(item) === tab.server)
@@ -275,13 +347,14 @@ export function TitlebarTabStrip(props: {
if (tab.type === "session") {
return (
- props.currentTab() === tab}
forceTruncate={props.forceTruncate}
serverCtx={serverCtx}
+ onVisibleChange={(visible) => setVisibility(id, visible)}
onNavigate={(element) => {
ref = element
props.onNavigate(tab, element)
@@ -295,7 +368,7 @@ export function TitlebarTabStrip(props: {
props.currentTab() === tab}
title={language.t("command.session.new")}
onNavigate={(element) => {
@@ -329,7 +402,7 @@ function useTabShortcut(index: () => number, onSelect: () => void) {
command.register(() => {
const number = index() + 1
- if (number > 9) return []
+ if (number < 1 || number > 9) return []
return [
{
id: `tab.${number}`,
diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx
index 41d134f0f41..32d7e80b2f0 100644
--- a/packages/app/src/components/titlebar.tsx
+++ b/packages/app/src/components/titlebar.tsx
@@ -338,40 +338,6 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
keybind: "mod+shift+t",
onSelect: () => tabsStoreActions.reopenClosedTab(),
},
- {
- id: `tab.prev`,
- category: "tab",
- title: "",
- keybind: `mod+option+ArrowLeft,ctrl+shift+tab`,
- hidden: true,
- onSelect: () => {
- let index = tabsStore.findIndex((tab) => tab === currentTab())
- if (index === -1) return
-
- index -= 1
- if (index === -1) index = tabsStore.length - 1
-
- const next = tabsStore[index]
- if (next) tabs.select(next)
- },
- },
- {
- id: `tab.next`,
- category: "tab",
- title: "",
- keybind: `mod+option+ArrowRight,ctrl+tab`,
- hidden: true,
- onSelect: () => {
- let index = tabsStore.findIndex((tab) => tab === currentTab())
- if (index === -1) return
-
- index += 1
- if (index === tabsStore.length) index = 0
-
- const next = tabsStore[index]
- if (next) tabs.select(next)
- },
- },
].filter((v) => v !== undefined)
})