Files
NomadArch/packages/ui/src/stores/releases.ts
Gemini AI 4bd2893864 v0.5.0: Binary-Free Mode - No OpenCode binary required
 Major Features:
- Native session management without OpenCode binary
- Provider routing: OpenCode Zen (free), Qwen OAuth, Z.AI
- Streaming chat with tool execution loop
- Mode detection API (/api/meta/mode)
- MCP integration fix (resolved infinite loading)
- NomadArch Native option in UI with comparison info

🆓 Free Models (No API Key):
- GPT-5 Nano (400K context)
- Grok Code Fast 1 (256K context)
- GLM-4.7 (205K context)
- Doubao Seed Code (256K context)
- Big Pickle (200K context)

📦 New Files:
- session-store.ts: Native session persistence
- native-sessions.ts: REST API for sessions
- lite-mode.ts: UI mode detection client
- native-sessions.ts (UI): SolidJS store

🔧 Updated:
- All installers: Optional binary download
- All launchers: Mode detection display
- Binary selector: Added NomadArch Native option
- README: Binary-Free Mode documentation
2025-12-26 02:12:42 +04:00

99 lines
2.7 KiB
TypeScript

import { createEffect, createSignal, createRoot } from "solid-js"
import type { LatestReleaseInfo, WorkspaceEventPayload } from "../../../server/src/api-types"
import { getServerMeta } from "../lib/server-meta"
import { serverEvents } from "../lib/server-events"
import { showToastNotification, ToastHandle } from "../lib/notifications"
import { getLogger } from "../lib/logger"
import { hasInstances, showFolderSelection } from "./ui"
const log = getLogger("actions")
const [availableRelease, setAvailableRelease] = createSignal<LatestReleaseInfo | null>(null)
let initialized = false
let visibilityEffectInitialized = false
let activeToast: ToastHandle | null = null
let activeToastVersion: string | null = null
function dismissActiveToast() {
if (activeToast) {
activeToast.dismiss()
activeToast = null
activeToastVersion = null
}
}
function ensureVisibilityEffect() {
if (visibilityEffectInitialized) {
return
}
visibilityEffectInitialized = true
// Use createRoot to properly scope this effect
createRoot(() => {
createEffect(() => {
const release = availableRelease()
const shouldShow = Boolean(release) && (!hasInstances() || showFolderSelection())
if (!shouldShow || !release) {
dismissActiveToast()
return
}
if (!activeToast || activeToastVersion !== release.version) {
dismissActiveToast()
activeToast = showToastNotification({
title: `NomadArch ${release.version}`,
message: release.channel === "dev" ? "Dev release build available." : "New stable build on GitHub.",
variant: "info",
duration: Number.POSITIVE_INFINITY,
position: "bottom-right",
action: {
label: "View release",
href: release.url,
},
})
activeToastVersion = release.version
}
})
})
}
export function initReleaseNotifications() {
if (initialized) {
return
}
initialized = true
ensureVisibilityEffect()
void refreshFromMeta()
serverEvents.on("app.releaseAvailable", (event) => {
const typedEvent = event as Extract<WorkspaceEventPayload, { type: "app.releaseAvailable" }>
applyRelease(typedEvent.release)
})
}
async function refreshFromMeta() {
try {
const meta = await getServerMeta(true)
if (meta.latestRelease) {
applyRelease(meta.latestRelease)
}
} catch (error) {
log.warn("Unable to load server metadata for release info", error)
}
}
function applyRelease(release: LatestReleaseInfo | null | undefined) {
if (!release) {
setAvailableRelease(null)
return
}
setAvailableRelease(release)
}
export function useAvailableRelease() {
return availableRelease
}