Files
CodeNomad/packages/ui/src/components/instance-tabs.tsx
Shantur Rathore d0a0325d7e feat(sidecars): add proxied sidecar tabs (#279)
## Summary
- add SideCar support across the server and UI, including proxied tabs,
picker/settings flows, and websocket-aware proxying
- unify top-level tab handling so workspace instances and SideCars share
the same tab model and navigation flows
- limit SideCars to port-based services only, removing server-managed
process control from the final API and UI

---------

Co-authored-by: Shantur <shantur@Mac.home>
Co-authored-by: Shantur <shantur@Shanturs-MacBook-Pro-M5.local>
2026-04-02 23:00:17 +01:00

119 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Component, For, Show, createMemo } from "solid-js"
import { Dynamic } from "solid-js/web"
import InstanceTab from "./instance-tab"
import KeyboardHint from "./keyboard-hint"
import { Plus, MonitorUp, Bell, BellOff, Settings } from "lucide-solid"
import { keyboardRegistry } from "../lib/keyboard-registry"
import { useI18n } from "../lib/i18n"
import { isOsNotificationSupportedSync } from "../lib/os-notifications"
import { useConfig } from "../stores/preferences"
import { openSettings } from "../stores/settings-screen"
import type { AppTabRecord } from "../stores/app-tabs"
interface InstanceTabsProps {
tabs: AppTabRecord[]
activeTabId: string | null
onSelect: (tabId: string) => void
onClose: (tabId: string) => void
onNew: () => void
}
const InstanceTabs: Component<InstanceTabsProps> = (props) => {
const { t } = useI18n()
const { preferences } = useConfig()
const notificationsSupported = createMemo(() => isOsNotificationSupportedSync())
const notificationsEnabled = createMemo(() => Boolean(preferences().osNotificationsEnabled))
const notificationIcon = createMemo(() => {
if (!notificationsSupported()) return BellOff
return notificationsEnabled() ? Bell : BellOff
})
const notificationTitle = createMemo(() => {
if (!notificationsSupported()) return t("settings.notifications.status.unsupported")
return notificationsEnabled()
? t("settings.notifications.status.enabled")
: t("settings.notifications.status.disabled")
})
return (
<div class="tab-bar tab-bar-instance">
<div class="tab-container" role="tablist">
<div class="tab-scroll">
<div class="tab-strip">
<div class="tab-strip-tabs">
<For each={props.tabs}>
{(tab) =>
tab.kind === "instance" ? (
<InstanceTab
instance={tab.instance}
active={tab.id === props.activeTabId}
onSelect={() => props.onSelect(tab.id)}
onClose={() => props.onClose(tab.id)}
/>
) : (
<div class={`tab-pill ${tab.id === props.activeTabId ? "tab-pill-active" : ""}`}>
<button class="tab-pill-button" onClick={() => props.onSelect(tab.id)}>
<span class="truncate max-w-[180px]">{tab.sidecarTab.name}</span>
</button>
<button class="tab-pill-close" onClick={() => props.onClose(tab.id)} aria-label={tab.sidecarTab.name}>
×
</button>
</div>
)}
</For>
<button
class="new-tab-button"
onClick={props.onNew}
title={t("instanceTabs.new.title")}
aria-label={t("instanceTabs.new.ariaLabel")}
>
<Plus class="w-4 h-4" />
</button>
</div>
<div class="tab-strip-spacer" />
<Show when={props.tabs.length > 1}>
<div class="tab-shortcuts">
<KeyboardHint
shortcuts={[keyboardRegistry.get("instance-prev")!, keyboardRegistry.get("instance-next")!].filter(
Boolean,
)}
/>
</div>
</Show>
<button
class="new-tab-button"
onClick={() => openSettings("appearance")}
title={t("settings.open.title")}
aria-label={t("settings.open.ariaLabel")}
>
<Settings class="w-4 h-4" />
</button>
<button
class={`new-tab-button ${!notificationsSupported() ? "opacity-50" : ""}`}
onClick={() => openSettings("notifications")}
title={notificationTitle()}
aria-label={notificationTitle()}
>
<Dynamic component={notificationIcon()} class="w-4 h-4" />
</button>
<button
class="new-tab-button tab-remote-button"
onClick={() => openSettings("remote")}
title={t("instanceTabs.remote.title")}
aria-label={t("instanceTabs.remote.ariaLabel")}
>
<MonitorUp class="w-4 h-4" />
</button>
</div>
</div>
</div>
</div>
)
}
export default InstanceTabs