fix: show not deployed yet for undeployed apps in session preview tabs (#12076)

* fix: show not deployed yet for undeployed apps in session preview tabs

* fix: say what an embed-token 404 means in the app viewer comment

* fix: name the protection rule only when the rules were actually read
This commit is contained in:
AlexRV12
2026-10-28 19:24:43 +01:00
committed by GitHub
parent 65f3c5ee5c
commit bda59e6d23
3 changed files with 30 additions and 5 deletions
@@ -117,6 +117,9 @@
headers
})
if (!res.ok) {
// Minted for the deployed app under the caller's RLS: a 404 is "nothing deployed here" or
// "not visible to this member", the same reading as `getAppLiteByPath`'s 404 below.
if (res.status == 404) onLoadState?.('not_found')
const err: any = new Error('Failed to fetch embed token')
err.status = res.status
throw err
@@ -21,8 +21,11 @@
previewLocationLabel,
barePath
} from './previewRouter'
import { useSessionPermissions } from './sessionPermissions.svelte'
import { useSessionPermissions, useSessionReadOnly } from './sessionPermissions.svelte'
import { agentWriteCount } from '$lib/components/flows/agentEditorStore.svelte'
import { userWorkspaces } from '$lib/stores'
import { findCanonicalDevWorkspace } from '$lib/utils/workspaceHierarchy'
import { devLabelNoun } from '$lib/utils/devWorkspaceLabel'
let {
runtime,
@@ -70,6 +73,8 @@
() => runtime.manager
)
const canEdit = $derived(permissions.canOpenEditor(kind))
const protectedWorkspace = useSessionReadOnly(() => workspaceId)
const devWorkspace = $derived(findCanonicalDevWorkspace(workspaceId, $userWorkspaces))
// This reads the deployed version over the API rather than from the editor cell the
// chat mutates, so it is the one preview kind that does not self-sync (the invariant
@@ -259,7 +264,11 @@
<div class="text-xs text-tertiary max-w-sm">
Nothing is deployed at <span class="font-mono">{path}</span>, so there is no deployed
version to view.{#if canEdit}{' '}
Deploy it from the editor to see it here.{/if}
Deploy it from the editor to see it here.{:else if protectedWorkspace.confirmed}{' '}
Direct deployment is disabled in this workspace, so this session can read and run deployed
items, but not edit. Pick {devWorkspace
? `its ${devLabelNoun(devWorkspace.dev_workspace_label)}`
: 'a fork'} to make changes.{/if}
</div>
{#if canEdit}
<Button unifiedSize="sm" variant="default" startIcon={{ icon: Pen }} on:click={toEditSide}>
@@ -16,9 +16,12 @@ import { previewPageAllowed, type PreviewPage } from './previewRouter'
/** Whether a session in `workspace` is read-only because the workspace is protected: the verdict
* `resolveSessionAccess` gives the chat, so the UI and the toolset agree. */
export function useSessionReadOnly(workspace: () => string | undefined) {
const verdict = resource(workspace, async (ws) =>
ws ? { workspace: ws, readOnly: await isReadOnlySessionWorkspace(ws) } : undefined
)
const verdict = resource(workspace, async (ws) => {
if (!ws) return undefined
const readOnly = await isReadOnlySessionWorkspace(ws)
// Only a successful read settles a verdict; a failed one is read-only to fail closed.
return { workspace: ws, readOnly, rulesRead: settledReadOnlyVerdict(ws) !== undefined }
})
// `resource` keeps the previous workspace's verdict while it asks about the next one.
const verdictFor = (ws: string): boolean | undefined => {
const landed = verdict.current
@@ -38,6 +41,16 @@ export function useSessionReadOnly(workspace: () => string | undefined) {
const ws = workspace()
return !!ws && (verdictFor(ws) ?? false)
},
/** Protected by a rule that was actually read: false when the read failed, where the session
* is still read-only. For text that names the rule as the reason. */
get confirmed() {
const ws = workspace()
if (!ws) return false
const landed = verdict.current
return landed?.workspace === ws
? landed.readOnly && landed.rulesRead
: (settledReadOnlyVerdict(ws) ?? false)
},
/** Whether the rules were read, a failed read included, which settles as read-only. */
get settled() {
const ws = workspace()