{{/* Copyright (c) ClaceIO, LLC */}} {{/* SPDX-License-Identifier: Apache-2.0 */}} {{/* Shared layout pieces for all console pages. Defines used across page templates must live here (page-local defines are not visible to other pages) */}} {{/* head renders the html head: title, pre-paint theme/background scripts, console.js and the generated style/htmx imports */}} {{ define "head" }} {{/* htmx halts requests from invalid forms; without this setting the halt is SILENT (no bubble, no focus) and reads as a dead button */}} OpenRun - {{ .Data.Title }} {{/* Brand fonts (see the Typography section of static/css/style.css); the faces used above the fold are preloaded so text does not swap after first paint. Plain (unhashed) paths on purpose: the css url() references resolve to these exact urls, a hashed static-helper href would preload a different url and download the font twice */}} {{/* Cross-document view transitions (see accessibility.css) snapshot the outgoing and incoming pages; block rendering until the sidebar nav - the last element in the body - is parsed, so the incoming snapshot is never a partially rendered page (Chrome 124+, ignored elsewhere) */}} {{ template "openrun_gen_import" . }} {{ template "ext_head" . }} {{ end }} {{/* hamburger renders the skip-to-navigation link (the main content comes before the nav in DOM order) and the button opening the nav drawer on small screens. Rendered at the top of every page header */}} {{ define "hamburger" }} {{ end }} {{/* drawer_toggle is the hidden checkbox controlled by the mobile hamburger and sidebar overlay. It must be the first child of .drawer on every full page */}} {{ define "drawer_toggle" }} {{ end }} {{/* time_ago renders an RFC3339 timestamp as a compact "4m ago" style age, with the exact UTC time as a tooltip */}} {{ define "time_ago" }} {{- $t := toDate "2006-01-02T15:04:05Z07:00" . -}} {{- $d := ago $t -}} {{- $d = regexReplaceAll "([hm])[0-9]+(\\.[0-9]+)?s$" $d "${1}" -}} {{- $d = regexReplaceAll "(h)[0-9]+m$" $d "${1}" -}} {{ $d }} ago {{- end }} {{/* page_title renders the top-level screen title carrying the brand fade bar (.page-title::after in static/css/style.css). Every screen renders it first, subpages follow it with breadcrumbs and their own h2 heading. Context: the title string */}} {{ define "page_title" }}

{{ . }}{{ template "ext_brand" }}

{{ end }} {{/* help_link renders a page's documentation link, next to its breadcrumbs. Context: the absolute docs URL (built by utils.star docs_link from the docs_url install param); empty renders nothing */}} {{ define "help_link" }} {{ if . }} {{ end }} {{ end }} {{/* open_app_link renders a right-aligned open-in-new-tab link for a running app. Context: dict with Href, Label ("Open app"/"Open staging") and What ("app"/"staging app", used in the tooltip) */}} {{ define "open_app_link" }} {{ .Label }} {{ template "globe_icon" }} {{ end }} {{/* promote_prompt renders a staged-changes promotion alert: the message, optional open-staging / keep-in-staging links and the Promote now button (gated on app:promote). Context: dict with Message, Path (the app the promote posts for), PostUrl, Target (hx-target selector), Perms and optional Warning (renders alert-warning instead of alert-info), Confirm (hx-confirm text), StageUrl, KeepHref */}} {{ define "promote_prompt" }} {{ end }} {{/* page_progress renders the branded progress line shown while HTMX requests are in flight (targeted with hx-indicator="#page-progress") */}} {{ define "page_progress" }}
{{ end }} {{/* error_alert renders the standard error alert when the message is not empty. Context: the error message string */}} {{ define "error_alert" }} {{ if . }} {{ end }} {{ end }} {{/* flash_alerts renders the action result alerts (Flash success and FlashError). Context: the page's .Data */}} {{ define "flash_alerts" }} {{ if .Flash }} {{ end }} {{ template "error_alert" .FlashError }} {{ end }} {{/* validation_alert renders the shared dry-run success result used by create forms. Context: dict with Validated, Value, Suffix and optional BreakAll */}} {{ define "validation_alert" }} {{ if .Validated }} {{ end }} {{ end }} {{/* secret_input renders the web component (console.js): a value input with a "store as secret" button which encrypts the value into the db secrets provider and swaps in the {{secret ... }} reference. Context: dict with Name (form field name), AppPath, Prefix (secret name prefix), CanCreate (secret:create held), CanDelete (secret:delete held; unlocking a stored field then offers deleting the secret) and optional Value, InputId, Placeholder, Masked, File, Description, Error */}} {{ define "secret_input" }} {{ end }} {{/* secret_input_response is the response template of the POST /secrets/store route (referenced by define name in app.star, no page file): unwraps the handler data for the component define */}} {{ define "secret_input_response" }}{{ template "secret_input" .Data }}{{ end }} {{/* error_toast is what the app's error_handler retargets into #error-toast when a partial (HTMX) request fails unexpectedly; referenced by define name from ace.response in app.star */}} {{ define "error_toast" }} {{ end }} {{/* kv_row renders one key/value row of a kv_table. Context: dict with Field, AppPath, Prefix, CanCreate, CanDelete and optional Key, Value, KeyPlaceholder, ValuePlaceholder */}} {{ define "kv_row" }}
{{ template "secret_input" (dict "Name" (printf "%s_value" .Field) "AppPath" .AppPath "Prefix" .Prefix "CanCreate" .CanCreate "CanDelete" .CanDelete "Value" .Value "Placeholder" (or .ValuePlaceholder "value")) }}
{{ end }} {{/* kv_table renders KEY=value rows with add/remove buttons; the value column is a secret_input so any value can be stored as a secret. Rows post as repeated _key / _value form values. Context: dict with Field, AppPath, Prefix, CanCreate, CanDelete and optional Rows (list of key/value dicts), KeyPlaceholder, ValuePlaceholder, AddLabel */}} {{ define "kv_table" }} {{ $ctx := . }}
{{ range .Rows }} {{ template "kv_row" (dict "Field" $ctx.Field "AppPath" $ctx.AppPath "Prefix" $ctx.Prefix "CanCreate" $ctx.CanCreate "CanDelete" $ctx.CanDelete "KeyPlaceholder" $ctx.KeyPlaceholder "ValuePlaceholder" $ctx.ValuePlaceholder "Key" (index . "key") "Value" (index . "value")) }} {{ end }} {{ if not .Rows }} {{ template "kv_row" (dict "Field" $ctx.Field "AppPath" $ctx.AppPath "Prefix" $ctx.Prefix "CanCreate" $ctx.CanCreate "CanDelete" $ctx.CanDelete "KeyPlaceholder" $ctx.KeyPlaceholder "ValuePlaceholder" $ctx.ValuePlaceholder) }} {{ end }}
{{ end }} {{/* globe_icon marks links that open the app itself in a new tab (vs the ↗ used for drill-in/source links); shared by the app tables' hover links and the app detail page's Open app button */}} {{/* drilldown_icon is the shared first-column click-in indicator (see the tables convention in design/notes.md: static svg, primary color baked in, explicit size, wrapping must be btn-square) */}} {{ define "drilldown_icon" }} {{ end }} {{/* builder_status_badge renders a builder session status as a colored badge with a pulse dot on the in-flight states. Context: the status string */}} {{ define "builder_status_badge" }} {{ . }} {{ end }} {{ define "globe_icon" }} {{ end }} {{/* app_table renders a list of app rows; shared by the apps page groups and the sync detail page. Context: dict with Apps (row list), AppPath (console root path), Perms (the user's management permissions) and ShowActions (row action buttons; the apps page passes true - the actions re-render the apps page partial, so view-only pages omit them). RowAction ("promote"/"approve", optional) switches the reload row action to the promote/approve button used by the apps page tabs */}} {{ define "app_table" }} {{ $root := .AppPath }} {{ $perms := .Perms }} {{ $actions := .ShowActions }} {{ $rowAction := .RowAction | default "" }}
{{ if $actions }} {{ end }} {{ range .Apps }} {{ if $actions }} {{ end }} {{ end }}
Details App Version Actions
{{ template "drilldown_icon" }} {{ .path }}{{ template "globe_icon" }} v{{ .version }}{{ if .git_sha }} · {{ .git_sha }} {{ end }} {{ if or .staging .is_dev .needs_approval }}
{{ if .needs_approval }} needs approval {{ end }} {{ if .is_dev }} dev {{ else if not .staging }} {{ else if .staging.ahead }} pending {{ else }} in sync {{ end }}
{{ end }}
{{/* The promote/approval tab rows carry only their one action; edit/delete stay on the default list */}} {{ if eq $rowAction "" }} {{ end }} {{ if eq $rowAction "promote" }} {{ else if eq $rowAction "approve" }} {{ else }} {{ end }} {{ if eq $rowAction "" }} {{ end }}
{{ end }} {{/* sync_result_row renders one category line of a sync result */}} {{ define "sync_result_row" }} {{ if .Items }}
{{ .Label }} {{ range $i, $item := .Items }} {{ if $i }},{{ end }}{{ $item }} {{ end }}
{{ end }} {{ end }} {{/* sync_result renders the detailed results of a sync run. Context: the SyncResult summary dict */}} {{/* op_redirect: empty body for form-success responses that navigate via HX-Redirect (form_redirect in handler.star) */}} {{ define "op_redirect" }}{{ end }} {{/* builder_services_checklist: the new-app form's service auto-binding choice, shaped by the selected profile's services offer. Re-rendered as a fragment when the profile select changes. Context: .Data.ServicesOffer (list of {id,type,name,is_default}) and .Data.ServicesChecked (id->bool, error re-render state) */}} {{ define "builder_services_checklist" }}
{{ if .Data.ServicesOffer }}
Services
{{ $checked := .Data.ServicesChecked }} {{ range .Data.ServicesOffer }} {{ end }}
Each picked service is auto-bound to the app; its credentials arrive as environment variables (one service per type)
{{ end }}
{{ end }} {{/* publish_check_result: outcome of the builder publish dialog's Validate button, rendered into #publish-check-result (fragment block-only render, so the define must live in the base templates) */}} {{ define "publish_check_result" }} {{ if .Data.CheckError }} {{ else if .Data.CheckPath }}
✓ will publish at {{ .Data.CheckPath }}{{ if and .Data.CheckResolved (ne .Data.CheckResolved .Data.CheckPath) }} ({{ .Data.CheckResolved }} on this server){{ end }}{{ if .Data.CheckExists }} (updates the existing app){{ end }}
{{ end }} {{ end }} {{ define "bc_send_error" }} {{/* generic chat-action error slot: the handlers pass the full text ("Message not sent: ...", "Not stopped: ...") since both the composer and the Stop button retarget their failures here */}} {{ if .Data.SendError }} {{ end }} {{ end }} {{ define "sync_result" }}
Sync completed {{ if .commit }} {{ .commit }} {{ end }} {{ if .skipped_apply }} - no new commit, apply was skipped {{ else if not .changed }} - {{ .total }} apps checked, everything matches the declared state {{ else }} - {{ .total }} apps in scope {{ end }}
{{ if .changed }}
{{ template "sync_result_row" (dict "Label" "Created" "Items" .created) }} {{ template "sync_result_row" (dict "Label" "Updated" "Items" .updated) }} {{ template "sync_result_row" (dict "Label" "Reloaded" "Items" .reloaded) }} {{ template "sync_result_row" (dict "Label" "Promoted" "Items" .promoted) }} {{ template "sync_result_row" (dict "Label" "Approved" "Items" .approved) }} {{ template "sync_result_row" (dict "Label" "Skipped" "Items" .skipped) }} {{ template "sync_result_row" (dict "Label" "Bindings +" "Items" .bindings_created) }} {{ template "sync_result_row" (dict "Label" "Bindings ~" "Items" .bindings_updated) }} {{ template "sync_result_row" (dict "Label" "Bindings ↑" "Items" .bindings_promoted) }}
{{ end }}
{{ end }} {{/* perms_card renders the requested permissions summary. Context: dict with Review (loads + permissions) and Note (footer text) */}} {{ define "perms_card" }}

Requested permissions

{{ if or .Review.loads .Review.permissions }} {{ if .Review.loads }}
Plugins {{ range .Review.loads }} {{ . }} {{ end }}
{{ end }} {{ if .Review.permissions }}
{{ range .Review.permissions }}
{{ .plugin }}.{{ .method }} {{ if .arguments }} {{ .arguments }} {{ end }} {{ if eq .access "read" }} read {{ else if eq .access "write" }} write {{ end }}
{{ end }}
{{ end }}

{{ .Note }}

{{ else }}

This app requests no plugin permissions

{{ end }}
{{ end }} {{/* container_state renders the state dot and label for a container. The dot is decorative - the state is always conveyed by the text label */}} {{ define "container_state" }} {{ if eq .state "running" }} running {{ else if eq .state "exited" }} exited {{ else if eq .state "failed" }} failed {{ else if eq .state "pending" }} pending {{ else }} {{ .state }} {{ end }} {{ end }} {{/* theme_toggle is the sun/moon switch, persisted via localStorage. The checkbox stays focusable (daisyui swap hides it visually, style.css stretches it over the label) so the toggle works from the keyboard */}} {{ define "theme_toggle" }} {{ end }} {{/* sidebar renders the nav drawer: logo, menu (highlighted via .Data.Nav), user footer with theme toggle, the nav progress bar and the error toast target used by the error_handler fallback */}} {{ define "sidebar" }}
{{/* Mobile companion of the sign-in notice below: the drawer (and the detailed notice in it) is hidden under lg, so a slim bottom strip gives the cue there; activating it (tap or keyboard, a real button unlike the pointer-only drawer overlay label) opens the drawer with the full explanation */}} {{ if and .Data.Perms (index .Data.Perms "feature:system_blocked") }} {{ end }}
{{ end }} {{/* publish_form: the builder publish dialog form. Lives in the base templates so a failed publish re-renders JUST the form in place (retarget #publish-form, outerHTML) - the dropdown stays open with the submitted values and the error inline; success swaps #session-content with the publish result */}} {{ define "publish_form" }} {{ $s := .Data.Session }} {{ $canPublish := index .Data.Perms "builder:publish" }}

{{ if $s.edit_app }}Update{{ else }}Publish{{ end }}

{{ if .Data.PublishError }} {{ end }}

Writing code to {{ if eq .Data.PublishMode "git" }}{{ .Data.GitRepo }}{{ else }}this server{{ end }}

{{ if and $s.edit_app $s.publish_path }}

The app source is updated in place; the app declaration in apps.star is not changed.

{{ else if $s.edit_app }}

Publishes a new app copied from {{ $s.edit_app }} (settings carried over, fresh auto bindings); the original app is not changed and cannot be the destination.

{{ end }} {{ if $s.publish_path }} {{/* Republish targets the same path */}}

App path: {{ $s.publish_path }}

{{ else }} {{/* One input shaped by the profile's publish destination mode, prefilled from the session name */}} {{ if eq .Data.PublishDestMode "subdomain" }} {{ else if eq .Data.PublishDestMode "path" }} {{ else }} {{ if eq .Data.PublishDestMode "glob" }}

must match {{ .Data.PublishDestTarget }}

{{ end }} {{ end }} {{ if .Data.PublishDestDesc }}

{{ .Data.PublishDestDesc }}

{{ end }} {{ end }} {{ if eq .Data.PublishMode "git" }} {{ end }}
{{ template "publish_check_result" . }}

{{ if eq .Data.PublishMode "git" }} The app goes live when the prod instance syncs the repo's apps.star {{ else }} The app is created on this server immediately {{ end }}

{{/* hx-disabled-elt=this OVERRIDES the form's inherited "find button[type=submit]": find is relative to the requesting element (this button), matches nothing, and htmx throws on the null before sending the request */}}
{{ end }}