{workload?.name ?? $t('apps.detail.pageTitleFallback')} · Tinyforge
{#if loading && !workload}
{$t('apps.detail.loading')}
{:else if error && !workload}
{$t('apps.detail.alertTag')}{error}
{:else if workload} {#snippet detailToolbar()} {#if !editing && firstFaceFqdn} {$t('apps.detail.toolbar.openSite')} {/if} {#if !editing && canStop} {/if} {#if !editing && canStart} {/if} {#if !editing}
{$t('apps.detail.toolbar.more')}
{#if firstFaceFqdn} {$t('apps.detail.toolbar.openSite')} {/if}
{/if} {/snippet} {#snippet detailLede()} {liveBadge.count ? $t(liveBadge.labelKey, { running: String(liveBadge.count.running), total: String(liveBadge.count.total) }) : $t(liveBadge.labelKey)} {workload!.source_kind} {bindings.length === 0 ? $t('apps.detail.chainTriggersZero') : bindings.length === 1 ? $t('apps.detail.chainTriggersOne') : $t('apps.detail.chainTriggersMany', { count: String(bindings.length) })} · {$t('apps.detail.createdAt')} {workload!.created_at} {/snippet} {#if error}
{$t('apps.detail.alertTag')}{error}
{/if} {#if editing}

{$t('apps.detail.editTitle')}.

{$t('apps.detail.editSubPrefix')} {workload.source_kind} {$t('apps.detail.editSubSuffix')}
03 {$t('apps.detail.editSourceConfig')} {useEditComposeForm ? $t('apps.detail.editConfigYaml') : useEditImageForm || useEditStaticForm || useEditDockerfileForm ? $t('apps.detail.editConfigForm') : $t('apps.detail.editConfigJson')}
{#if useEditComposeForm} {:else if useEditImageForm} {:else if useEditStaticForm} {:else if useEditDockerfileForm} {:else}
{$t('apps.detail.editSourceJsonHeader')} {#if (workload?.source_kind ?? '') === 'compose' || (workload?.source_kind ?? '') === 'image' || (workload?.source_kind ?? '') === 'static' || (workload?.source_kind ?? '') === 'dockerfile'} {/if}
{sourceValid ? $t('apps.detail.jsonOk') : $t('apps.detail.jsonInvalid')}
{/if}
04 {$t('apps.detail.editPublicFaces')} {$t('apps.detail.editPublicFacesTag')}
{$t('apps.detail.editPublicFacesHeader')}
{facesValid ? $t('apps.detail.jsonOk') : $t('apps.detail.jsonInvalid')}
{/if} {#if !editing && hasRuntimeState && (runtimeState !== null || runtimeError || storageEnabledOnSource)}

{$t('apps.detail.runtimeState.title')}.

{$t('apps.detail.runtimeState.sub')}
{#if runtimeError} {:else if runtimeState && !runtimeState.has_state}

{$t('apps.detail.runtimeState.neverDeployed')}

{:else if runtimeState}
{$t('apps.detail.runtimeState.status')}
{runtimeState.status || runtimeState.state || '—'}
{$t('apps.detail.runtimeState.lastCommit')}
{#if runtimeState.last_commit_sha} {runtimeState.last_commit_sha.slice(0, 8)} {:else} {/if}
{$t('apps.detail.runtimeState.lastSync')}
{#if runtimeState.last_sync_at} {$fmt.dateTime(runtimeState.last_sync_at)} {:else} {/if}
{#if runtimeState.container_id}
{$t('apps.detail.runtimeState.container')}
{runtimeState.container_id.slice(0, 12)}
{/if}
{#if runtimeState.last_error} {/if} {:else}

{$t('apps.detail.runtimeState.loading')}

{/if}
{#if storageEnabledOnSource}

{$t('apps.detail.storage.title')}.

{$t('apps.detail.storage.sub')}
{#if storageError} {:else if storage}
{$t('apps.detail.storage.used')}
{formatBytes(storage.used_bytes)}
{$t('apps.detail.storage.limit')}
{#if storage.limit_mb && storage.limit_mb > 0} {storage.limit_mb} MB {:else} {$t('apps.detail.storage.unlimited')} {/if}
{#if storage.probe_error}

{$t('apps.detail.storage.unavailable')}

{:else if storage.limit_mb && storage.limit_mb > 0}

{Math.round(storageUsedPct * 100)}%

{/if} {:else}

{$t('apps.detail.storage.loading')}

{/if}
{/if}
{/if} {#if !editing && workload.source_kind === 'dockerfile' && buildLogLines.length > 0}

{$t('apps.detail.buildLog.title')}.

{#each buildLogLines as line (line.id)}
{line.text}
{/each}
{/if} {#if !editing && containers.length > 0}

{$t('apps.detail.stats.title')}.

{containers.length === 1 ? $t('apps.detail.stats.sub') : $t('apps.detail.stats.subMany', { count: String(containers.length) })}
    {#each containers as c (c.id)} {@const collapseByDefault = containers.length > 2}
  • {#if collapseByDefault}
    {c.role || c.image_ref || c.id.slice(0, 8)} {#if c.container_id} {c.container_id.slice(0, 12)} {/if}
    {:else}
    {c.role || c.image_ref || c.id.slice(0, 8)} {#if c.container_id} {c.container_id.slice(0, 12)} {/if}
    {/if}
  • {/each}
{/if} {#if !editing && webhookBindings.length > 0}

{$t('apps.detail.webhooks.title')}.

{$t('apps.detail.webhooks.sub')}
    {#each webhookBindings as b (b.id)}
  • {b.trigger_name || b.trigger_id} {$t(`redeployTriggers.kindShort.${b.trigger_kind}`) === `redeployTriggers.kindShort.${b.trigger_kind}` ? b.trigger_kind.toUpperCase() : $t(`redeployTriggers.kindShort.${b.trigger_kind}`)} {#if !b.enabled} {$t('apps.detail.webhooks.disabled')} {/if} {$t('apps.detail.webhooks.openTrigger')}
  • {/each}
{/if}

{$t('apps.detail.manualDeployTitle')}.

{$t('apps.detail.manualDeploySub')}
{#if lastDeployMsg}
{$t('apps.detail.manualDeployOk')} {lastDeployMsg}
{/if}

{$t('apps.detail.manualDeployHint')}

{#if !editing}

{$t('apps.detail.bindings.title')}.

{bindings.length === 0 ? $t('apps.detail.bindings.subEmpty') : bindings.length === 1 ? $t('apps.detail.bindings.subCount', { count: '1' }) : $t('apps.detail.bindings.subCountMany', { count: String(bindings.length) })}
{#if bindingsError} {/if} {#if bindingsLoading}

{$t('apps.detail.bindings.loading')}

{:else if bindings.length === 0}
{$t('apps.detail.bindings.subEmpty')}
{:else}
    {#each bindings as b, i (b.id)} {@const overrideOpen = openOverrideId === b.id} {@const overrideCount = overrideKeyCount(b.binding_config)} {@const editText = overrideTexts[b.id] ?? formatJsonValue(b.binding_config ?? {})} {@const baseText = overrideBaseConfigs[b.trigger_id] ?? ''} {@const baseLoading = overrideBaseLoading[b.trigger_id] === true} {@const editValid = overrideJsonValid(editText)} {@const editBytes = overrideTextSize(editText)} {@const editTooLarge = editBytes > BINDING_CONFIG_MAX_BYTES} {@const editKeys = overrideKeyCountFromText(editText)} {@const rowError = overrideErrors[b.id] ?? ''} {@const rowSaving = overrideSaving[b.id] === true}
  • 0} class:open={overrideOpen}>
    {String(i + 1).padStart(2, '0')} {b.trigger_name || b.trigger_id} {$t(`redeployTriggers.kindShort.${b.trigger_kind}`) === `redeployTriggers.kindShort.${b.trigger_kind}` ? b.trigger_kind.toUpperCase() : $t(`redeployTriggers.kindShort.${b.trigger_kind}`)} {b.enabled ? $t('apps.detail.bindings.rowEnabled') : $t('apps.detail.bindings.rowDisabled')} {#if overrideCount > 0} {overrideCount === 1 ? $t('apps.detail.bindings.override.badgeOne') : $t('apps.detail.bindings.override.badgeMany', { count: String(overrideCount) })} {/if}
    toggleBinding(b, next)} label={$t('apps.detail.bindings.rowEnabled')} />
    {#if overrideOpen}

    {$t('apps.detail.bindings.override.title')}

    {$t('apps.detail.bindings.override.subtitle')}

    {#if rowError} {/if}
    {$t('apps.detail.bindings.override.baseLabel')} READ-ONLY
    {#if baseLoading}
    {$t('apps.detail.bindings.override.baseLoading')}
    {:else if baseText}
    {baseText}
    {:else}
    {'{}'}
    {/if}

    {$t('apps.detail.bindings.override.baseHint')}

    {$t('apps.detail.bindings.override.editLabel')} {editKeys} {editKeys === 1 ? $t('apps.detail.overrideKeyUnitSingular') : $t('apps.detail.overrideKeyUnitPlural')}
    {#if !editValid} {$t('apps.detail.bindings.override.invalidJson')} {:else if editTooLarge} {$t('apps.detail.bindings.override.tooLarge', { size: String(editBytes), limit: String(BINDING_CONFIG_MAX_BYTES) })} {:else} {$t('apps.detail.bindings.override.editHint')} {/if} {editBytes}/{BINDING_CONFIG_MAX_BYTES}B
    {#if editValid && !editTooLarge && baseText}
    {$t('apps.detail.bindings.override.previewLabel')} EFFECTIVE
    {mergedPreview(baseText, editText)}

    {$t('apps.detail.bindings.override.previewHint')}

    {/if}
    {/if}
  • {/each}

{$t('apps.detail.bindings.rowEnableHint')}

{/if}
{/if}

{$t('apps.detail.containersTitle')}.

{containers.length === 0 ? $t('apps.detail.containersEmpty') : $t('apps.detail.containersCount', { count: String(containers.length) })}
{#if containers.length === 0}
{$t('apps.detail.containersEmptyInline')}
{:else} {#if logContainerRowID}
(logContainerRowID = null)} />
{/if}
{#each containers as c (c.id)} {/each}
{$t('apps.detail.containersColRole')} {$t('apps.detail.containersColState')} {$t('apps.detail.containersColImage')} {$t('apps.detail.containersColSubdomain')} {$t('apps.detail.containersColLastSeen')} {$t('apps.detail.containersColActions')}
{c.role || '—'} {c.state} {c.image_ref || '—'} {c.subdomain || '—'} {c.last_seen_at || '—'} {#if c.container_id} {:else} {/if}
{/if}
{#if !editing && chain && (chain.parent || chain.children.length > 0)}

{$t('apps.detail.chainTitle')}.

{chain.parent ? $t('apps.detail.chainSubFromParent') : $t('apps.detail.chainSubParentOf')} {chain.children.length} {chain.children.length === 1 ? $t('apps.detail.chainChildSingular') : $t('apps.detail.chainChildPlural')}
{#if chainError}
{$t('apps.detail.alertTag')}{chainError}
{/if} {#if chain.parent}
{$t('apps.detail.chainParentLabel')} {chain.parent.name} {chain.parent.source_kind} {#if workload?.source_kind === 'image' && chain.parent.source_kind === 'image'} {/if}
{/if}
{$t('apps.detail.chainSelfLabel')}
{workload?.name ?? '—'} {workload?.source_kind ?? ''} · {bindings.length === 0 ? $t('apps.detail.chainTriggersZero') : bindings.length === 1 ? $t('apps.detail.chainTriggersOne') : $t('apps.detail.chainTriggersMany', { count: String(bindings.length) })}
{#if chain.children.length > 0}
{$t('apps.detail.chainChildrenLabel')}
{/if}

{@html $t('apps.detail.chainHint')}

{/if} {#if !editing && (previewChildren.length > 0 || isPreviewArmed)}

{$t('apps.detail.previews.title')}.

{previewChildren.length === 0 ? $t('apps.detail.previews.subEmpty') : previewChildren.length === 1 ? $t('apps.detail.previews.subCountOne') : $t('apps.detail.previews.subCount', { count: String(previewChildren.length) })}
{#if previewChildren.length === 0}
{#if armedBranchPattern} {$t('apps.detail.previews.armedEmpty')} {armedBranchPattern} {:else} {$t('apps.detail.previews.noneEmpty')} {/if}
{:else}
{#each previewChildren as child (child.id)} {@const meta = previewMeta[child.id]} {@const st = meta?.state ?? ''}
{previewStateLabel(st)} {child.preview_branch || child.name} {#if meta && meta.fqdn} {$t('apps.detail.previews.open')} {:else if meta && meta.loaded} {$t('apps.detail.previews.noUrl')} {:else} {/if}
{/each}
{/if} {#if chainError} {/if}

{@html $t('apps.detail.previews.hint')}

{/if} {#if !editing}

{$t('apps.detail.activity.title')}.

{$t('apps.detail.activity.subtitle')} {#if activityEvents.length > 0}
{/if}
{#if activityLoading}
{:else if activityEvents.length === 0}

{$t('apps.detail.activity.empty')}

{:else if visibleActivity.length === 0}

{$t('apps.detail.activity.noErrors')}

{:else}
{#each visibleActivity as entry (entry.id)} {/each}
{/if} {#if !activityLoading && activityHasMore}
{/if}
{/if} {#if !editing} {/if} {#if !editing}

{$t('logscan.panel.heading')}.

{logRules.length === 0 ? $t('logscan.panel.subEmpty') : logRules.length === 1 ? $t('logscan.panel.subCountOne') : $t('logscan.panel.subCount', { count: String(logRules.length) })} · {$t('observability.manage')}
{#if logRulesError} {/if} {#if logRules.length === 0}

{$t('logscan.panel.emptyHint')} {$t('logscan.panel.newRule')}.

{:else}
{#each logRules as r (r.id)} {@const kind = classifyRule(r)} {/each}
{$t('logscan.list.name')} {$t('logscan.list.pattern')} {$t('logscan.list.scope')} {$t('logscan.list.severity')} {$t('logscan.list.streams')} {$t('logscan.list.status')} {$t('triggers.list.action')}
{r.name} /{r.pattern}/ {$t(`logscan.filter.${kind === 'override' ? 'overrides' : kind}`).toLowerCase()} {r.severity} {r.streams} {r.enabled ? $t('logscan.status.on') : $t('logscan.status.off')} {#if kind === 'global'} {:else} {$t('observability.edit')} {/if}

{$t('logscan.panel.footerHint')}

{/if}
{/if} {#if !editing}

{$t('apps.detail.volumesTitle')}.

{volumeRows.length === 0 ? $t('apps.detail.volumesEmpty') : volumeRows.length === 1 ? $t('apps.detail.volumesCountSingular', { count: '1' }) : $t('apps.detail.volumesCountPlural', { count: String(volumeRows.length) })}
{#if volumeError}
{$t('apps.detail.alertTag')}{volumeError}
{/if} {#if volumeRows.length > 0}
{#each volumeRows as v (v.id)} {/each}
{$t('apps.detail.volumesColTarget')} {$t('apps.detail.volumesColSource')} {$t('apps.detail.volumesColScope')} {$t('apps.detail.volumesColUpdated')} {$t('apps.detail.volumesColActions')}
{v.target} {v.source || '—'} {v.scope} {v.updated_at}
{/if}
{ ev.preventDefault(); addVolume(); }} >

{$t('apps.detail.volumeHint')}

{/if} {#if !editing}

{$t('apps.detail.envTitle')}.

{envRows.length === 0 ? $t('apps.detail.envEmpty') : envRows.length === 1 ? $t('apps.detail.envCountSingular', { count: '1' }) : $t('apps.detail.envCountPlural', { count: String(envRows.length) })}
{#if envError}
{$t('apps.detail.alertTag')}{envError}
{/if} {#if envRows.length > 0}
{#each envRows as e (e.id)} {/each}
{$t('apps.detail.envColKey')} {$t('apps.detail.envColValue')} {$t('apps.detail.envColUpdated')} {$t('apps.detail.envColActions')}
{e.key} {#if e.encrypted} {$t('apps.detail.envEncrypted')} {:else} {e.value || '—'} {/if} {e.updated_at}
{/if}
{ ev.preventDefault(); addEnv(); }} >

{$t('apps.detail.envHint')}

{/if} {#if !editing}
{#if openSource}
{prettyJson(workload.source_config)}
{/if}
{#if workload.public_faces && workload.public_faces !== '[]'}
{#if openFaces}
{prettyJson(workload.public_faces)}
{/if}
{/if} {/if} {/if}
{ if (!deleting) confirmDelete = false; }} /> (confirmUnbindId = null)} /> { if (!tearingDown) confirmTeardownId = null; }} /> {#if addModalOpen} {/if}