From 715d92e4ba5c17ebcdcbd6ba3bad4e4925660a39 Mon Sep 17 00:00:00 2001 From: Gagan Date: Tue, 7 Jul 2026 13:48:13 +0530 Subject: [PATCH] fix(apps): route 'Publish update' through apps:publishUpdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The detail panel's Publish update button opened the same dialog as first publish, which hardcoded apps:publish — so updating an already published app always failed with name_taken from the registry check. The dialog now takes a published flag: it runs apps:publishUpdate with a version-bump picker (patch/minor/major), shows a simple progress state (the update path emits no step events), and links the new release on success. --- .../src/components/apps/app-detail.tsx | 1 + .../src/components/apps/publish-dialog.tsx | 59 ++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/apps/x/apps/renderer/src/components/apps/app-detail.tsx b/apps/x/apps/renderer/src/components/apps/app-detail.tsx index 39d294f0..6e8aa554 100644 --- a/apps/x/apps/renderer/src/components/apps/app-detail.tsx +++ b/apps/x/apps/renderer/src/components/apps/app-detail.tsx @@ -272,6 +272,7 @@ export function AppDetail({ folder, onClose }: { folder: string; onClose: () => setShowPublish(false)} onPublished={() => setReloadNonce((n) => n + 1)} /> diff --git a/apps/x/apps/renderer/src/components/apps/publish-dialog.tsx b/apps/x/apps/renderer/src/components/apps/publish-dialog.tsx index f14dd7e0..ed798a8e 100644 --- a/apps/x/apps/renderer/src/components/apps/publish-dialog.tsx +++ b/apps/x/apps/renderer/src/components/apps/publish-dialog.tsx @@ -3,13 +3,18 @@ import { CheckCircle2, Github, Loader2, XCircle } from 'lucide-react' // Publish dialog (spec §14): device-flow sign-in → confirmation → step // progress (§11.2 step names) → success links / typed error (name_taken gets -// an inline hint to rename and retry). +// an inline hint to rename and retry). With `published` the dialog runs the +// UPDATE path instead (§11.3: version bump + new release, no registry) — +// re-running first-publish on a published app always fails with name_taken. type Phase = 'auth' | 'device' | 'confirm' | 'publishing' | 'done' | 'error' +type Increment = 'patch' | 'minor' | 'major' -export function PublishDialog({ folder, appName, onClose, onPublished }: { +export function PublishDialog({ folder, appName, published, onClose, onPublished }: { folder: string appName: string + /** App already has a publish record — run publish-update instead. */ + published?: boolean onClose: () => void onPublished: () => void }) { @@ -18,7 +23,8 @@ export function PublishDialog({ folder, appName, onClose, onPublished }: { const [userCode, setUserCode] = useState('') const [steps, setSteps] = useState([]) const [error, setError] = useState(null) - const [result, setResult] = useState<{ repoUrl: string; releaseUrl: string; prUrl?: string; status: string } | null>(null) + const [increment, setIncrement] = useState('patch') + const [result, setResult] = useState<{ repoUrl?: string; releaseUrl: string; prUrl?: string; status: string; version?: string } | null>(null) const pollTimer = useRef(null) useEffect(() => { @@ -75,8 +81,13 @@ export function PublishDialog({ folder, appName, onClose, onPublished }: { setError(null) setSteps([]) try { - const r = await window.ipc.invoke('apps:publish', { folder }) - setResult(r) + if (published) { + const r = await window.ipc.invoke('apps:publishUpdate', { folder, increment }) + setResult({ releaseUrl: r.releaseUrl, status: 'published', version: r.version }) + } else { + const r = await window.ipc.invoke('apps:publish', { folder }) + setResult(r) + } setPhase('done') onPublished() } catch (e) { @@ -128,15 +139,47 @@ export function PublishDialog({ folder, appName, onClose, onPublished }: { {phase === 'confirm' && (
-

Signed in as @{login}. This will publish {appName} publicly.

+

+ Signed in as @{login}.{' '} + {published + ? <>This will publish a new version of {appName} (a new release on the existing repo). + : <>This will publish {appName} publicly.} +

+ {published && ( + + )}
)} - {(phase === 'publishing' || phase === 'done' || phase === 'error') && ( + {published && (phase === 'publishing' || phase === 'done' || phase === 'error') && ( +
+ {phase === 'publishing' && ( +

+ Publishing update… +

+ )} + {phase === 'done' && result && ( +
+

Published v{result.version}

+ +
+ )} +
+ )} + + {!published && (phase === 'publishing' || phase === 'done' || phase === 'error') && (
{Object.entries(STEP_LABELS).map(([key, label]) => { const doneStep = steps.includes(key) || phase === 'done'