diff --git a/packages/app/src/components/status-popover.tsx b/packages/app/src/components/status-popover.tsx index a3e76517f5..3963a54f3c 100644 --- a/packages/app/src/components/status-popover.tsx +++ b/packages/app/src/components/status-popover.tsx @@ -153,7 +153,7 @@ export function StatusPopover() { "bg-border-weak-base": server.healthy() === undefined, }} /> - Status + {language.t("status.popover.trigger")} } class="[&_[data-slot=popover-body]]:p-0 w-[360px] max-w-[calc(100vw-40px)] bg-transparent border-0 shadow-none rounded-xl" @@ -166,7 +166,7 @@ export function StatusPopover() { style={{ "box-shadow": "var(--shadow-lg-border-base)" }} > - {serverCount() > 0 ? `${serverCount()} ` : ""}Servers + {serverCount() > 0 ? `${serverCount()} ` : ""} + {language.t("status.popover.tab.servers")} - {mcpConnected() > 0 ? `${mcpConnected()} ` : ""}MCP + {mcpConnected() > 0 ? `${mcpConnected()} ` : ""} + {language.t("status.popover.tab.mcp")} - {lspCount() > 0 ? `${lspCount()} ` : ""}LSP + {lspCount() > 0 ? `${lspCount()} ` : ""} + {language.t("status.popover.tab.lsp")} - {pluginCount() > 0 ? `${pluginCount()} ` : ""}Plugins + {pluginCount() > 0 ? `${pluginCount()} ` : ""} + {language.t("status.popover.tab.plugins")} @@ -274,7 +278,7 @@ export function StatusPopover() { - Default + {language.t("common.default")}
@@ -292,7 +296,7 @@ export function StatusPopover() { class="mt-3 self-start h-8 px-3 py-1.5" onClick={() => dialog.show(() => )} > - Manage servers + {language.t("status.popover.action.manageServers")}
@@ -304,7 +308,9 @@ export function StatusPopover() { 0} fallback={ -
No MCP servers configured
+
+ {language.t("dialog.mcp.empty")} +
} > @@ -351,7 +357,7 @@ export function StatusPopover() { when={lspItems().length > 0} fallback={
- LSPs auto-detected from file types + {language.t("dialog.lsp.empty")}
} > @@ -381,8 +387,19 @@ export function StatusPopover() { when={plugins().length > 0} fallback={
- Plugins configured in{" "} - opencode.json + {(() => { + const value = language.t("dialog.plugins.empty") + const file = "opencode.json" + const parts = value.split(file) + if (parts.length === 1) return value + return ( + <> + {parts[0]} + {file} + {parts.slice(1).join(file)} + + ) + })()}
} > diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 1dfb8d4424..99e516a0a4 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -426,6 +426,14 @@ export const dict = { "session.header.search.placeholder": "بحث {{project}}", "session.header.searchFiles": "بحث عن الملفات", + "status.popover.trigger": "الحالة", + "status.popover.ariaLabel": "إعدادات الخوادم", + "status.popover.tab.servers": "الخوادم", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "الإضافات", + "status.popover.action.manageServers": "إدارة الخوادم", + "session.share.popover.title": "نشر على الويب", "session.share.popover.description.shared": "هذه الجلسة عامة على الويب. يمكن لأي شخص لديه الرابط الوصول إليها.", "session.share.popover.description.unshared": "شارك الجلسة علنًا على الويب. ستكون متاحة لأي شخص لديه الرابط.", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 52d4bd270d..93dc2f1feb 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -422,6 +422,14 @@ export const dict = { "session.header.search.placeholder": "Buscar {{project}}", "session.header.searchFiles": "Buscar arquivos", + "status.popover.trigger": "Status", + "status.popover.ariaLabel": "Configurações de servidores", + "status.popover.tab.servers": "Servidores", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugins", + "status.popover.action.manageServers": "Gerenciar servidores", + "session.share.popover.title": "Publicar na web", "session.share.popover.description.shared": "Esta sessão é pública na web. Está acessível para qualquer pessoa com o link.", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index eb711dcea8..b2f0a9afe7 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -409,6 +409,14 @@ export const dict = { "session.header.search.placeholder": "Søg {{project}}", "session.header.searchFiles": "Søg efter filer", + "status.popover.trigger": "Status", + "status.popover.ariaLabel": "Serverkonfigurationer", + "status.popover.tab.servers": "Servere", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugins", + "status.popover.action.manageServers": "Administrer servere", + "session.share.popover.title": "Udgiv på nettet", "session.share.popover.description.shared": "Denne session er offentlig på nettet. Den er tilgængelig for alle med linket.", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 39ef515d19..42f628d5ed 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -416,6 +416,14 @@ export const dict = { "session.header.search.placeholder": "{{project}} durchsuchen", "session.header.searchFiles": "Dateien suchen", + "status.popover.trigger": "Status", + "status.popover.ariaLabel": "Serverkonfigurationen", + "status.popover.tab.servers": "Server", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugins", + "status.popover.action.manageServers": "Server verwalten", + "session.share.popover.title": "Im Web veröffentlichen", "session.share.popover.description.shared": "Diese Sitzung ist öffentlich im Web. Sie ist für jeden mit dem Link zugänglich.", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index d3e43d0896..b32f034855 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -430,6 +430,14 @@ export const dict = { "session.header.search.placeholder": "Search {{project}}", "session.header.searchFiles": "Search files", + "status.popover.trigger": "Status", + "status.popover.ariaLabel": "Server configurations", + "status.popover.tab.servers": "Servers", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugins", + "status.popover.action.manageServers": "Manage servers", + "session.share.popover.title": "Publish on web", "session.share.popover.description.shared": "This session is public on the web. It is accessible to anyone with the link.", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 725213cfb7..1039a2d3a4 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -410,6 +410,14 @@ export const dict = { "session.header.search.placeholder": "Buscar {{project}}", "session.header.searchFiles": "Buscar archivos", + "status.popover.trigger": "Estado", + "status.popover.ariaLabel": "Configuraciones del servidor", + "status.popover.tab.servers": "Servidores", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugins", + "status.popover.action.manageServers": "Administrar servidores", + "session.share.popover.title": "Publicar en web", "session.share.popover.description.shared": "Esta sesión es pública en la web. Es accesible para cualquiera con el enlace.", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index 348aef1205..09eeea44c4 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -415,6 +415,14 @@ export const dict = { "session.header.search.placeholder": "Rechercher {{project}}", "session.header.searchFiles": "Rechercher des fichiers", + "status.popover.trigger": "Statut", + "status.popover.ariaLabel": "Configurations des serveurs", + "status.popover.tab.servers": "Serveurs", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugins", + "status.popover.action.manageServers": "Gérer les serveurs", + "session.share.popover.title": "Publier sur le web", "session.share.popover.description.shared": "Cette session est publique sur le web. Elle est accessible à toute personne disposant du lien.", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 62f01c8d72..821c6ccdb1 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -407,6 +407,14 @@ export const dict = { "session.header.search.placeholder": "{{project}}を検索", "session.header.searchFiles": "ファイルを検索", + "status.popover.trigger": "ステータス", + "status.popover.ariaLabel": "サーバー設定", + "status.popover.tab.servers": "サーバー", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "プラグイン", + "status.popover.action.manageServers": "サーバーを管理", + "session.share.popover.title": "ウェブで公開", "session.share.popover.description.shared": "このセッションはウェブで公開されています。リンクを知っている人なら誰でもアクセスできます。", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 71ac64ae80..ddd00e763d 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -410,6 +410,14 @@ export const dict = { "session.header.search.placeholder": "{{project}} 검색", "session.header.searchFiles": "파일 검색", + "status.popover.trigger": "상태", + "status.popover.ariaLabel": "서버 구성", + "status.popover.tab.servers": "서버", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "플러그인", + "status.popover.action.manageServers": "서버 관리", + "session.share.popover.title": "웹에 게시", "session.share.popover.description.shared": "이 세션은 웹에 공개되었습니다. 링크가 있는 누구나 액세스할 수 있습니다.", "session.share.popover.description.unshared": diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index 1572d391bb..3262d3e04c 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -430,6 +430,14 @@ export const dict = { "session.header.search.placeholder": "Søk i {{project}}", "session.header.searchFiles": "Søk etter filer", + "status.popover.trigger": "Status", + "status.popover.ariaLabel": "Serverkonfigurasjoner", + "status.popover.tab.servers": "Servere", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugins", + "status.popover.action.manageServers": "Administrer servere", + "session.share.popover.title": "Publiser på nett", "session.share.popover.description.shared": "Denne sesjonen er offentlig på nettet. Den er tilgjengelig for alle med lenken.", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index 4b2a7ccb2b..7af9d21798 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -428,6 +428,14 @@ export const dict = { "session.header.search.placeholder": "Szukaj {{project}}", "session.header.searchFiles": "Szukaj plików", + "status.popover.trigger": "Status", + "status.popover.ariaLabel": "Konfiguracje serwerów", + "status.popover.tab.servers": "Serwery", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Wtyczki", + "status.popover.action.manageServers": "Zarządzaj serwerami", + "session.share.popover.title": "Opublikuj w sieci", "session.share.popover.description.shared": "Ta sesja jest publiczna w sieci. Jest dostępna dla każdego, kto posiada link.", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index ebe8265c7d..d7fa135fa0 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -430,6 +430,14 @@ export const dict = { "session.header.search.placeholder": "Поиск {{project}}", "session.header.searchFiles": "Поиск файлов", + "status.popover.trigger": "Статус", + "status.popover.ariaLabel": "Настройки серверов", + "status.popover.tab.servers": "Серверы", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Плагины", + "status.popover.action.manageServers": "Управлять серверами", + "session.share.popover.title": "Опубликовать в интернете", "session.share.popover.description.shared": "Эта сессия общедоступна. Доступ к ней может получить любой, у кого есть ссылка.", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 2ab985c684..e2b7df0d10 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -405,6 +405,14 @@ export const dict = { "session.header.search.placeholder": "搜索 {{project}}", "session.header.searchFiles": "搜索文件", + "status.popover.trigger": "状态", + "status.popover.ariaLabel": "服务器配置", + "status.popover.tab.servers": "服务器", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "插件", + "status.popover.action.manageServers": "管理服务器", + "session.share.popover.title": "发布到网页", "session.share.popover.description.shared": "此会话已在网页上公开。任何拥有链接的人都可以访问。", "session.share.popover.description.unshared": "在网页上公开分享此会话。任何拥有链接的人都可以访问。", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index c1d7580262..9973b443b3 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -407,6 +407,14 @@ export const dict = { "session.header.search.placeholder": "搜尋 {{project}}", "session.header.searchFiles": "搜尋檔案", + "status.popover.trigger": "狀態", + "status.popover.ariaLabel": "伺服器設定", + "status.popover.tab.servers": "伺服器", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "外掛程式", + "status.popover.action.manageServers": "管理伺服器", + "session.share.popover.title": "發佈到網頁", "session.share.popover.description.shared": "此工作階段已在網頁上公開。任何擁有連結的人都可以存取。", "session.share.popover.description.unshared": "在網頁上公開分享此工作階段。任何擁有連結的人都可以存取。", diff --git a/packages/console/app/src/routes/changelog.json.ts b/packages/console/app/src/routes/changelog.json.ts new file mode 100644 index 0000000000..a23f205032 --- /dev/null +++ b/packages/console/app/src/routes/changelog.json.ts @@ -0,0 +1,114 @@ +type Release = { + tag_name: string + name: string + body: string + published_at: string + html_url: string +} + +type HighlightMedia = { type: "video"; src: string } | { type: "image"; src: string; width: string; height: string } + +type HighlightItem = { + title: string + description: string + shortDescription?: string + media: HighlightMedia +} + +type HighlightGroup = { + source: string + items: HighlightItem[] +} + +function parseHighlights(body: string): HighlightGroup[] { + const groups = new Map() + const regex = /([\s\S]*?)<\/highlight>/g + let match + + while ((match = regex.exec(body)) !== null) { + const source = match[1] + const content = match[2] + + const titleMatch = content.match(/

([^<]+)<\/h2>/) + const pMatch = content.match(/([^<]+)<\/p>/) + const imgMatch = content.match(/ ({ source, items })) +} + +function parseMarkdown(body: string) { + const lines = body.split("\n") + const sections: { title: string; items: string[] }[] = [] + let current: { title: string; items: string[] } | null = null + let skip = false + + for (const line of lines) { + if (line.startsWith("## ")) { + if (current) sections.push(current) + const title = line.slice(3).trim() + current = { title, items: [] } + skip = false + } else if (line.startsWith("**Thank you")) { + skip = true + } else if (line.startsWith("- ") && !skip) { + current?.items.push(line.slice(2).trim()) + } + } + if (current) sections.push(current) + + const highlights = parseHighlights(body) + + return { sections, highlights } +} + +export async function GET() { + const response = await fetch("https://api.github.com/repos/anomalyco/opencode/releases?per_page=20", { + headers: { + Accept: "application/vnd.github.v3+json", + "User-Agent": "OpenCode-Console", + }, + }) + + if (!response.ok) { + return { releases: [] } + } + + const releases = (await response.json()) as Release[] + + return { + releases: releases.map((release) => { + const parsed = parseMarkdown(release.body || "") + return { + tag: release.tag_name, + name: release.name, + date: release.published_at, + url: release.html_url, + highlights: parsed.highlights, + sections: parsed.sections, + } + }), + } +} diff --git a/packages/console/app/src/routes/changelog/index.css b/packages/console/app/src/routes/changelog/index.css index a445c74474..233d85cc0e 100644 --- a/packages/console/app/src/routes/changelog/index.css +++ b/packages/console/app/src/routes/changelog/index.css @@ -367,11 +367,18 @@ display: flex; flex-direction: column; gap: 4px; + position: sticky; + top: 80px; + align-self: start; + background: var(--color-background); + padding: 8px 0; @media (max-width: 50rem) { + position: static; flex-direction: row; align-items: center; gap: 12px; + padding: 0; } [data-slot="version"] { @@ -402,24 +409,26 @@ [data-component="section"] { h3 { - font-size: 14px; + font-size: 13px; font-weight: 600; color: var(--color-text-strong); - margin-bottom: 8px; + margin-bottom: 6px; } ul { list-style: none; padding: 0; margin: 0; + padding-left: 16px; display: flex; flex-direction: column; - gap: 6px; + gap: 4px; li { color: var(--color-text); + font-size: 13px; line-height: 1.5; - padding-left: 16px; + padding-left: 12px; position: relative; &::before { @@ -431,7 +440,7 @@ [data-slot="author"] { color: var(--color-text-weak); - font-size: 13px; + font-size: 12px; margin-left: 4px; text-decoration: none; @@ -465,6 +474,120 @@ } } } + + [data-component="highlights"] { + display: flex; + flex-direction: column; + gap: 2rem; + margin-bottom: 1.5rem; + } + + [data-component="collapsible-sections"] { + display: flex; + flex-direction: column; + gap: 0; + } + + [data-component="collapsible-section"] { + [data-slot="toggle"] { + display: flex; + align-items: center; + gap: 6px; + background: none; + border: none; + padding: 6px 0; + cursor: pointer; + font-family: inherit; + font-size: 13px; + font-weight: 600; + color: var(--color-text-weak); + + &:hover { + color: var(--color-text); + } + + [data-slot="icon"] { + font-size: 10px; + } + } + + ul { + list-style: none; + padding: 0; + margin: 0; + padding-left: 16px; + padding-bottom: 8px; + + li { + color: var(--color-text); + font-size: 13px; + line-height: 1.5; + padding-left: 12px; + position: relative; + + &::before { + content: "-"; + position: absolute; + left: 0; + color: var(--color-text-weak); + } + + [data-slot="author"] { + color: var(--color-text-weak); + font-size: 12px; + margin-left: 4px; + text-decoration: none; + + &:hover { + text-decoration: underline; + text-underline-offset: 2px; + text-decoration-thickness: 1px; + } + } + } + } + } + + [data-component="highlight"] { + h4 { + font-size: 14px; + font-weight: 600; + color: var(--color-text-strong); + margin-bottom: 8px; + } + + hr { + border: none; + border-top: 1px solid var(--color-border-weak); + margin-bottom: 16px; + } + + [data-slot="highlight-item"] { + margin-bottom: 24px; + + &:last-child { + margin-bottom: 0; + } + + p[data-slot="title"] { + font-weight: 600; + font-size: 16px; + margin-bottom: 4px; + } + + p { + font-size: 14px; + margin-bottom: 12px; + } + } + + img, + video { + max-width: 100%; + height: auto; + border-radius: 4px; + } + } } a { diff --git a/packages/console/app/src/routes/changelog/index.tsx b/packages/console/app/src/routes/changelog/index.tsx index c1b931fe3e..87e021ec88 100644 --- a/packages/console/app/src/routes/changelog/index.tsx +++ b/packages/console/app/src/routes/changelog/index.tsx @@ -5,7 +5,7 @@ import { Header } from "~/component/header" import { Footer } from "~/component/footer" import { Legal } from "~/component/legal" import { config } from "~/config" -import { For, Show } from "solid-js" +import { For, Show, createSignal } from "solid-js" type Release = { tag_name: string @@ -40,6 +40,59 @@ function formatDate(dateString: string) { }) } +type HighlightMedia = { type: "video"; src: string } | { type: "image"; src: string; width: string; height: string } + +type HighlightItem = { + title: string + description: string + shortDescription?: string + media: HighlightMedia +} + +type HighlightGroup = { + source: string + items: HighlightItem[] +} + +function parseHighlights(body: string): HighlightGroup[] { + const groups = new Map() + const regex = /([\s\S]*?)<\/highlight>/g + let match + + while ((match = regex.exec(body)) !== null) { + const source = match[1] + const content = match[2] + + const titleMatch = content.match(/

([^<]+)<\/h2>/) + const pMatch = content.match(/([^<]+)<\/p>/) + const imgMatch = content.match(/ ({ source, items })) +} + function parseMarkdown(body: string) { const lines = body.split("\n") const sections: { title: string; items: string[] }[] = [] @@ -60,7 +113,9 @@ function parseMarkdown(body: string) { } if (current) sections.push(current) - return { sections } + const highlights = parseHighlights(body) + + return { sections, highlights } } function ReleaseItem(props: { item: string }) { @@ -87,6 +142,60 @@ function ReleaseItem(props: { item: string }) { ) } +function HighlightSection(props: { group: HighlightGroup }) { + return ( +
+

{props.group.source}

+
+ + {(item) => ( +
+

{item.title}

+

{item.description}

+ + + + {item.title} + +
+ )} +
+
+ ) +} + +function CollapsibleSection(props: { section: { title: string; items: string[] } }) { + const [open, setOpen] = createSignal(false) + + return ( +
+ + +
    + {(item) => } +
+
+
+ ) +} + +function CollapsibleSections(props: { sections: { title: string; items: string[] }[] }) { + return ( +
+ {(section) => } +
+ ) +} + export default function Changelog() { const releases = createAsync(() => getReleases()) @@ -120,16 +229,26 @@ export default function Changelog() {
- - {(section) => ( -
-

{section.title}

-
    - {(item) => } -
-
- )} -
+ 0}> +
+ {(group) => } +
+
+ 0 && parsed().sections.length > 0}> + + + + + {(section) => ( +
+

{section.title}

+
    + {(item) => } +
+
+ )} +
+
) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 1bbd0cdaf3..340b972ac6 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -488,15 +488,6 @@ function App() { }, category: "System", }, - { - title: "Open WebUI", - value: "webui.open", - onSelect: () => { - open(sdk.url).catch(() => {}) - dialog.clear() - }, - category: "System", - }, { title: "Exit the app", value: "app.exit", diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 392cfb7f12..eb8ed2d9bb 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -333,32 +333,57 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const start = Date.now() - 30 * 24 * 60 * 60 * 1000 const sessionListPromise = sdk.client.session .list({ start: start }) - .then((x) => setStore("session", reconcile((x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id))))) + .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id))) // blocking - include session.list when continuing a session + const providersPromise = sdk.client.config.providers({}, { throwOnError: true }) + const providerListPromise = sdk.client.provider.list({}, { throwOnError: true }) + const agentsPromise = sdk.client.app.agents({}, { throwOnError: true }) + const configPromise = sdk.client.config.get({}, { throwOnError: true }) const blockingRequests: Promise[] = [ - sdk.client.config.providers({}, { throwOnError: true }).then((x) => { - batch(() => { - setStore("provider", reconcile(x.data!.providers)) - setStore("provider_default", reconcile(x.data!.default)) - }) - }), - sdk.client.provider.list({}, { throwOnError: true }).then((x) => { - batch(() => { - setStore("provider_next", reconcile(x.data!)) - }) - }), - sdk.client.app.agents({}, { throwOnError: true }).then((x) => setStore("agent", reconcile(x.data ?? []))), - sdk.client.config.get({}, { throwOnError: true }).then((x) => setStore("config", reconcile(x.data!))), + providersPromise, + providerListPromise, + agentsPromise, + configPromise, ...(args.continue ? [sessionListPromise] : []), ] await Promise.all(blockingRequests) + .then(() => { + const providersResponse = providersPromise.then((x) => x.data!) + const providerListResponse = providerListPromise.then((x) => x.data!) + const agentsResponse = agentsPromise.then((x) => x.data ?? []) + const configResponse = configPromise.then((x) => x.data!) + const sessionListResponse = args.continue ? sessionListPromise : undefined + + return Promise.all([ + providersResponse, + providerListResponse, + agentsResponse, + configResponse, + ...(sessionListResponse ? [sessionListResponse] : []), + ]).then((responses) => { + const providers = responses[0] + const providerList = responses[1] + const agents = responses[2] + const config = responses[3] + const sessions = responses[4] + + batch(() => { + setStore("provider", reconcile(providers.providers)) + setStore("provider_default", reconcile(providers.default)) + setStore("provider_next", reconcile(providerList)) + setStore("agent", reconcile(agents)) + setStore("config", reconcile(config)) + if (sessions !== undefined) setStore("session", reconcile(sessions)) + }) + }) + }) .then(() => { if (store.status !== "complete") setStore("status", "partial") // non-blocking Promise.all([ - ...(args.continue ? [] : [sessionListPromise]), + ...(args.continue ? [] : [sessionListPromise.then((sessions) => setStore("session", reconcile(sessions)))]), sdk.client.command.list().then((x) => setStore("command", reconcile(x.data ?? []))), sdk.client.lsp.status().then((x) => setStore("lsp", reconcile(x.data!))), sdk.client.mcp.status().then((x) => setStore("mcp", reconcile(x.data!))),