Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/app/src/context/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type TabHandoff = {
at: number
}

export type LocalProject = Partial<Project> & { worktree: string; expanded: boolean }
export type LocalProject = Partial<Project> & { worktree: string; expanded: boolean; pinned?: boolean }

export type ReviewDiffStyle = "unified" | "split"

Expand Down Expand Up @@ -384,7 +384,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
return available[Math.floor(Math.random() * available.length)]
}

function enrich(project: { worktree: string; expanded: boolean }) {
function enrich(project: { worktree: string; expanded: boolean; pinned?: boolean }) {
const [childStore] = globalSync.child(project.worktree, { bootstrap: false })
const projectID = childStore.project
const metadata = projectID
Expand Down Expand Up @@ -597,6 +597,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
move(directory: string, toIndex: number) {
server.projects.move(directory, toIndex)
},
pin(directory: string, pinned: boolean) {
server.projects.pin(directory, pinned)
},
},
sidebar: {
opened: createMemo(() => store.sidebar.opened),
Expand Down
28 changes: 25 additions & 3 deletions packages/app/src/context/server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createStore } from "solid-js/store"
import { Persist, persisted } from "@/utils/persist"
import { useCheckServerHealth } from "@/utils/server-health"

type StoredProject = { worktree: string; expanded: boolean }
type StoredProject = { worktree: string; expanded: boolean; pinned?: boolean }
type StoredServer = string | ServerConnection.HttpBase | ServerConnection.Http
const HEALTH_POLL_INTERVAL_MS = 10_000

Expand Down Expand Up @@ -33,6 +33,10 @@ function isLocalHost(url: string) {
if (host === "localhost" || host === "127.0.0.1") return "local"
}

function order(list: StoredProject[]) {
return [...list.filter((item) => item.pinned), ...list.filter((item) => !item.pinned)]
}

export namespace ServerConnection {
type Base = { displayName?: string }

Expand Down Expand Up @@ -250,7 +254,7 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
if (!key) return
const current = store.projects[key] ?? []
if (current.find((x) => x.worktree === directory)) return
setStore("projects", key, [{ worktree: directory, expanded: true }, ...current])
setStore("projects", key, order([{ worktree: directory, expanded: true }, ...current]))
},
close(directory: string) {
const key = origin()
Expand Down Expand Up @@ -285,7 +289,25 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
const result = [...current]
const [item] = result.splice(fromIndex, 1)
result.splice(toIndex, 0, item)
setStore("projects", key, result)
setStore("projects", key, order(result))
},
pin(directory: string, pinned: boolean) {
const key = origin()
if (!key) return
const current = store.projects[key] ?? []
const index = current.findIndex((x) => x.worktree === directory)
if (index === -1) return
const item = current[index]
if (!item || !!item.pinned === pinned) return
const rest = current.filter((x) => x.worktree !== directory)
const next = { ...item, pinned }
if (pinned) {
const split = rest.findIndex((x) => !x.pinned)
const at = split === -1 ? rest.length : split
setStore("projects", key, [...rest.slice(0, at), next, ...rest.slice(at)])
return
}
setStore("projects", key, order([...rest, next]))
},
last() {
const key = origin()
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ export const dict = {
"sidebar.project.recentSessions": "الجلسات الحديثة",
"sidebar.project.viewAllSessions": "عرض جميع الجلسات",
"sidebar.project.clearNotifications": "مسح الإشعارات",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",
"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "سطح المكتب",
"settings.section.server": "الخادم",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/br.ts
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ export const dict = {
"sidebar.project.recentSessions": "Sessões recentes",
"sidebar.project.viewAllSessions": "Ver todas as sessões",
"sidebar.project.clearNotifications": "Limpar notificações",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",
"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "Desktop",
"settings.section.server": "Servidor",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/bs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,8 @@ export const dict = {
"sidebar.project.recentSessions": "Nedavne sesije",
"sidebar.project.viewAllSessions": "Prikaži sve sesije",
"sidebar.project.clearNotifications": "Očisti obavijesti",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Desktop",

Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/da.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,8 @@ export const dict = {
"sidebar.project.recentSessions": "Seneste sessioner",
"sidebar.project.viewAllSessions": "Vis alle sessioner",
"sidebar.project.clearNotifications": "Ryd notifikationer",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "Desktop",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,8 @@ export const dict = {
"sidebar.project.recentSessions": "Letzte Sitzungen",
"sidebar.project.viewAllSessions": "Alle Sitzungen anzeigen",
"sidebar.project.clearNotifications": "Benachrichtigungen löschen",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",
"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "Desktop",
"settings.section.server": "Server",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,8 @@ export const dict = {
"sidebar.project.recentSessions": "Recent sessions",
"sidebar.project.viewAllSessions": "View all sessions",
"sidebar.project.clearNotifications": "Clear notifications",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",
"sidebar.empty.title": "No projects open",
"sidebar.empty.description": "Open a project to get started",

Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,8 @@ export const dict = {
"sidebar.project.recentSessions": "Sesiones recientes",
"sidebar.project.viewAllSessions": "Ver todas las sesiones",
"sidebar.project.clearNotifications": "Borrar notificaciones",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Desktop",

Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,8 @@ export const dict = {
"sidebar.project.recentSessions": "Sessions récentes",
"sidebar.project.viewAllSessions": "Voir toutes les sessions",
"sidebar.project.clearNotifications": "Effacer les notifications",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",
"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "Bureau",
"settings.section.server": "Serveur",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@ export const dict = {
"sidebar.project.recentSessions": "最近のセッション",
"sidebar.project.viewAllSessions": "すべてのセッションを表示",
"sidebar.project.clearNotifications": "通知をクリア",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",
"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "デスクトップ",
"settings.section.server": "サーバー",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,8 @@ export const dict = {
"sidebar.project.recentSessions": "최근 세션",
"sidebar.project.viewAllSessions": "모든 세션 보기",
"sidebar.project.clearNotifications": "알림 지우기",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",
"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "데스크톱",
"settings.section.server": "서버",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/no.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,8 @@ export const dict = {
"sidebar.project.recentSessions": "Nylige sesjoner",
"sidebar.project.viewAllSessions": "Vis alle sesjoner",
"sidebar.project.clearNotifications": "Fjern varsler",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Desktop",

Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,8 @@ export const dict = {
"sidebar.project.recentSessions": "Ostatnie sesje",
"sidebar.project.viewAllSessions": "Zobacz wszystkie sesje",
"sidebar.project.clearNotifications": "Wyczyść powiadomienia",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",
"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "Pulpit",
"settings.section.server": "Serwer",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,8 @@ export const dict = {
"sidebar.project.recentSessions": "Недавние сессии",
"sidebar.project.viewAllSessions": "Посмотреть все сессии",
"sidebar.project.clearNotifications": "Очистить уведомления",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "Приложение",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/th.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,8 @@ export const dict = {
"sidebar.project.recentSessions": "เซสชันล่าสุด",
"sidebar.project.viewAllSessions": "ดูเซสชันทั้งหมด",
"sidebar.project.clearNotifications": "ล้างการแจ้งเตือน",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Desktop",

Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,8 @@ export const dict = {
"sidebar.project.recentSessions": "Son oturumlar",
"sidebar.project.viewAllSessions": "Tüm oturumları görüntüle",
"sidebar.project.clearNotifications": "Bildirimleri temizle",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Masaüstü",

Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ export const dict = {
"sidebar.project.recentSessions": "最近会话",
"sidebar.project.viewAllSessions": "查看全部会话",
"sidebar.project.clearNotifications": "清除通知",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Desktop",

Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/zht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,8 @@ export const dict = {
"sidebar.project.recentSessions": "最近工作階段",
"sidebar.project.viewAllSessions": "查看全部工作階段",
"sidebar.project.clearNotifications": "清除通知",
"sidebar.project.pin": "Pin project",
"sidebar.project.unpin": "Unpin project",

"app.name.desktop": "OpenCode Desktop",
"settings.section.desktop": "桌面",
Expand Down
28 changes: 25 additions & 3 deletions packages/app/src/pages/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,10 @@ export default function Layout(props: ParentProps) {
layout.sidebar.toggleWorkspaces(project.worktree)
}

function pinProject(directory: string, pinned: boolean) {
layout.projects.pin(directory, pinned)
}

const showEditProjectDialog = (project: LocalProject) => {
const run = ++dialogRun
void import("@/components/dialog-edit-project").then((x) => {
Expand Down Expand Up @@ -1848,10 +1852,14 @@ export default function Layout(props: ParentProps) {
const { draggable, droppable } = event
if (draggable && droppable) {
const projects = layout.projects.list()
const fromIndex = projects.findIndex((p) => p.worktree === draggable.id.toString())
const toIndex = projects.findIndex((p) => p.worktree === droppable.id.toString())
const source = projects.find((p) => p.worktree === draggable.id.toString())
const target = projects.find((p) => p.worktree === droppable.id.toString())
if (!source || !target) return
if (!!source.pinned !== !!target.pinned) return
const fromIndex = projects.findIndex((p) => p.worktree === source.worktree)
const toIndex = projects.findIndex((p) => p.worktree === target.worktree)
if (fromIndex !== toIndex && toIndex !== -1) {
layout.projects.move(draggable.id.toString(), toIndex)
layout.projects.move(source.worktree, toIndex)
}
}
}
Expand Down Expand Up @@ -2005,6 +2013,7 @@ export default function Layout(props: ParentProps) {
navigateToProject,
openSidebar: () => layout.sidebar.open(),
closeProject,
pinProject,
showEditProjectDialog,
toggleProjectWorkspaces,
workspacesEnabled: (project) => project.vcs === "git" && layout.sidebar.workspaces(project.worktree)(),
Expand Down Expand Up @@ -2159,6 +2168,19 @@ export default function Layout(props: ParentProps) {
>
<DropdownMenu.ItemLabel>{language.t("common.edit")}</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<DropdownMenu.Item
data-action="project-pin-toggle"
data-project={slug()}
onSelect={() => {
const item = project()
if (!item) return
pinProject(item.worktree, !item.pinned)
}}
>
<DropdownMenu.ItemLabel>
{project()?.pinned ? language.t("sidebar.project.unpin") : language.t("sidebar.project.pin")}
</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<DropdownMenu.Item
data-action="project-workspaces-toggle"
data-project={slug()}
Expand Down
21 changes: 20 additions & 1 deletion packages/app/src/pages/layout/sidebar-project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type ProjectSidebarContext = {
navigateToProject: (directory: string) => void
openSidebar: () => void
closeProject: (directory: string) => void
pinProject: (directory: string, pinned: boolean) => void
showEditProjectDialog: (project: LocalProject) => void
toggleProjectWorkspaces: (project: LocalProject) => void
workspacesEnabled: (project: LocalProject) => boolean
Expand Down Expand Up @@ -64,6 +65,7 @@ const ProjectTile = (props: {
onProjectFocus: (worktree: string) => void
navigateToProject: (directory: string) => void
showEditProjectDialog: (project: LocalProject) => void
pinProject: (directory: string, pinned: boolean) => void
toggleProjectWorkspaces: (project: LocalProject) => void
workspacesEnabled: (project: LocalProject) => boolean
closeProject: (directory: string) => void
Expand Down Expand Up @@ -143,13 +145,29 @@ const ProjectTile = (props: {
}}
onBlur={() => props.setOpen(false)}
>
<ProjectIcon project={props.project} notify />
<div class="relative">
<ProjectIcon project={props.project} notify />
<Show when={props.project.pinned}>
<div class="absolute bottom-0 right-0 flex size-4 items-center justify-center rounded-full border border-border-weak-base bg-background-base">
<Icon name="pin" class="size-2.5 text-icon-weak" />
</div>
</Show>
</div>
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item onSelect={() => props.showEditProjectDialog(props.project)}>
<ContextMenu.ItemLabel>{props.language.t("common.edit")}</ContextMenu.ItemLabel>
</ContextMenu.Item>
<ContextMenu.Item
data-action="project-pin-toggle"
data-project={base64Encode(props.project.worktree)}
onSelect={() => props.pinProject(props.project.worktree, !props.project.pinned)}
>
<ContextMenu.ItemLabel>
{props.project.pinned ? props.language.t("sidebar.project.unpin") : props.language.t("sidebar.project.pin")}
</ContextMenu.ItemLabel>
</ContextMenu.Item>
<ContextMenu.Item
data-action="project-workspaces-toggle"
data-project={base64Encode(props.project.worktree)}
Expand Down Expand Up @@ -321,6 +339,7 @@ export const SortableProject = (props: {
onProjectFocus={props.ctx.onProjectFocus}
navigateToProject={props.ctx.navigateToProject}
showEditProjectDialog={props.ctx.showEditProjectDialog}
pinProject={props.ctx.pinProject}
toggleProjectWorkspaces={props.ctx.toggleProjectWorkspaces}
workspacesEnabled={props.ctx.workspacesEnabled}
closeProject={props.ctx.closeProject}
Expand Down
12 changes: 11 additions & 1 deletion packages/app/src/pages/layout/sidebar-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const SidebarContent = (props: {
}): JSX.Element => {
const expanded = createMemo(() => !!props.mobile || props.opened())
const placement = () => (props.mobile ? "bottom" : "right")
const first = createMemo(() => props.projects().findIndex((project) => !project.pinned))
let panel: HTMLDivElement | undefined

createEffect(() => {
Expand Down Expand Up @@ -64,7 +65,16 @@ export const SidebarContent = (props: {
<ConstrainDragXAxis />
<div class="h-full w-full flex flex-col items-center gap-3 px-3 py-3 overflow-y-auto no-scrollbar">
<SortableProvider ids={props.projects().map((p) => p.worktree)}>
<For each={props.projects()}>{(project) => props.renderProject(project)}</For>
<For each={props.projects()}>
{(project, index) => (
<>
<Show when={first() > 0 && index() === first()}>
<div class="h-px w-6 rounded-full bg-border-weak-base" aria-hidden="true" />
</Show>
{props.renderProject(project)}
</>
)}
</For>
</SortableProvider>
<Tooltip
placement={placement()}
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const icons = {
"new-session-active": `<path d="M6 11.3818V14H8.61818L18 4.61818L15.3818 2L6 11.3818Z" fill="currentColor" fill-opacity="0.1"/>
<path d="M12 2H2V18H18V8M6 11.3818V14H8.61818L18 4.61818L15.3818 2L6 11.3818Z" stroke="currentColor"/>`,
"pencil-line": `<path d="M9.58301 17.9166H17.9163M17.9163 5.83325L14.1663 2.08325L2.08301 14.1666V17.9166H5.83301L17.9163 5.83325Z" stroke="currentColor" stroke-linecap="square"/>`,
pin: `<path d="M12.9089 2.91699H7.07552V6.66699L4.58301 9.1595V10.417H9.37552V17.0837L10.0005 17.7087L10.6255 17.0837V10.417H15.417V9.1595L12.9089 6.65169V2.91699Z" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round"/>`,
mcp: `<g><path d="M0.972656 9.37176L9.5214 1.60019C10.7018 0.527151 12.6155 0.527151 13.7957 1.60019C14.9761 2.67321 14.9761 4.41295 13.7957 5.48599L7.3397 11.3552" stroke="currentColor" stroke-linecap="round"/><path d="M7.42871 11.2747L13.7957 5.48643C14.9761 4.41338 16.8898 4.41338 18.0702 5.48643L18.1147 5.52688C19.2951 6.59993 19.2951 8.33966 18.1147 9.4127L10.3831 16.4414C9.98966 16.7991 9.98966 17.379 10.3831 17.7366L11.9707 19.1799" stroke="currentColor" stroke-linecap="round"/><path d="M11.6587 3.54346L5.33619 9.29119C4.15584 10.3642 4.15584 12.1039 5.33619 13.177C6.51649 14.25 8.43019 14.25 9.61054 13.177L15.9331 7.42923" stroke="currentColor" stroke-linecap="round"/></g>`,
glasses: `<path d="M0.416626 7.91667H1.66663M19.5833 7.91667H18.3333M11.866 7.57987C11.3165 7.26398 10.6793 7.08333 9.99996 7.08333C9.32061 7.08333 8.68344 7.26398 8.13389 7.57987M8.74996 10C8.74996 12.0711 7.07103 13.75 4.99996 13.75C2.92889 13.75 1.24996 12.0711 1.24996 10C1.24996 7.92893 2.92889 6.25 4.99996 6.25C7.07103 6.25 8.74996 7.92893 8.74996 10ZM18.75 10C18.75 12.0711 17.071 13.75 15 13.75C12.9289 13.75 11.25 12.0711 11.25 10C11.25 7.92893 12.9289 6.25 15 6.25C17.071 6.25 18.75 7.92893 18.75 10Z" stroke="currentColor" stroke-linecap="square"/>`,
"magnifying-glass-menu": `<path d="M2.08325 10.0002H4.58325M2.08325 5.41683H5.41659M2.08325 14.5835H5.41659M16.4583 13.9585L18.7499 16.2502M17.9166 10.0002C17.9166 12.9917 15.4915 15.4168 12.4999 15.4168C9.50838 15.4168 7.08325 12.9917 7.08325 10.0002C7.08325 7.00862 9.50838 4.5835 12.4999 4.5835C15.4915 4.5835 17.9166 7.00862 17.9166 10.0002Z" stroke="currentColor" stroke-linecap="square"/>`,
Expand Down
Loading