import { useState } from "react"; import { Check, Copy, RefreshCw, } from "lucide-react"; import { cn } from "../core/cn"; import { useToast } from "../shared"; import type { WorkspaceUpdatesInfo } from "./workspaceUpdatesModel"; const SETTINGS_MAIN_CLASS = "openpress-workspace-settings mx-auto grid w-full max-w-[760px] content-start gap-9 py-1"; const SETTINGS_HEADER_CLASS = "grid gap-2 border-b border-[var(--op-workspace-border)] pb-5"; const SETTINGS_EYEBROW_CLASS = "m-0 font-mono text-[0.68rem] font-semibold uppercase tracking-[0.12em] text-[var(--op-workspace-text-muted)]"; const SETTINGS_TITLE_CLASS = "m-0 text-[1.35rem] font-semibold tracking-[-0.02em] text-[var(--op-workspace-text)]"; const SETTINGS_DESCRIPTION_CLASS = "m-0 max-w-[580px] text-[0.82rem] leading-relaxed text-[var(--op-workspace-text-muted)]"; interface Props { updates: WorkspaceUpdatesInfo | null; } export function WorkspaceUpdatesSettings({ updates }: Props) { const [copiedKey, setCopiedKey] = useState(null); const { showToast } = useToast(); const copyPrompt = (key: string, promptText?: string) => { if (!promptText || typeof navigator === "undefined" || !navigator.clipboard) return; void navigator.clipboard.writeText(promptText).then(() => { setCopiedKey(key); showToast("Prompt copied. Paste it into Claude or Codex to continue."); setTimeout(() => { setCopiedKey((current) => (current === key ? null : current)); }, 2200); }); }; if (!updates) { return ; } const { openpress, builtInSkills, plugins } = updates; return (
{/* Header */}

Capability & Updates

Updates & Agent Handoff

Local capability status only. Copy a prompt and continue the update with Claude or Codex.

{/* 1. OpenPress Core Section */}

OpenPress Core

@open-press/core · FRAMEWORK

OpenPress runtime, rendering engine, and workspace settings system.

{openpress.isLocalDev ? "Source: local monorepo development" : openpress.coreVersion ? `Current version: v${openpress.coreVersion}` : "Current version: package not detected"}

{openpress.isLocalDev ? ( Local Source ) : openpress.prompts.update ? ( ) : null}
{/* 2. Built-in Skills Section (Unified with Plugins layout) */}

Built-in Skills

({builtInSkills.installedCount}/{builtInSkills.expected.length}) {builtInSkills.missing.length === 0 ? ( ✓ All installed ) : ( {builtInSkills.missing.length} missing )}
{builtInSkills.prompts.sync && ( )}
{/* Unified Flat Rows for Built-in Skills */}
{(builtInSkills.items || []).map((skill) => { const syncKey = `sync-${skill.name}`; return (
{skill.displayName} ({skill.name}) · CORE

{skill.description}

Path: .agents/skills/{skill.name}

{skill.isInstalled ? ( Ready ) : ( )}
); })}
{/* 3. Companion Plugins Section */}

Companion Plugins

({plugins.installedCount}/{plugins.totalCatalogCount})
{plugins.installedCount > 0 && plugins.prompts.updateAll && ( )}
{/* Unified Flat Rows for Plugins */}
{plugins.items.map((plugin) => { const checkKey = `check-${plugin.name}`; const updateKey = `update-${plugin.name}`; return (
{plugin.displayName} ({plugin.name}) · {plugin.category}

{plugin.description}

Source: {plugin.source}

{plugin.isInstalled ? ( <> ) : ( Not installed )}
); })}
); } function UpdatesSettingsSkeleton() { return ( ); }