update fonts

This commit is contained in:
Evan Jarrett
2026-04-14 20:56:53 -05:00
parent e843b7233c
commit 9809c26281
35 changed files with 211 additions and 57 deletions
+2 -2
View File
@@ -31,8 +31,8 @@ func (h *HomeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
db.SetRegistryURL(featuredCards, h.RegistryURL)
// Fetch recently updated repositories (top 24 by last push - 6 rows at 4-col xl)
recentCards, err := db.GetRepoCards(h.ReadOnlyDB, 24, currentUserDID, db.SortByLastUpdate)
// Fetch recently updated repositories (top 18 by last push - 6 rows at 3-col lg)
recentCards, err := db.GetRepoCards(h.ReadOnlyDB, 18, currentUserDID, db.SortByLastUpdate)
if err != nil {
log.Printf("Error fetching recent repos: %v", err)
recentCards = []db.RepoCardData{}
+3 -1
View File
@@ -425,13 +425,15 @@ func (h *UpdateAutoRemoveUntaggedHandler) ServeHTTP(w http.ResponseWriter, r *ht
w.WriteHeader(http.StatusNoContent)
}
// validOciClients is the set of allowed OCI client values
// validOciClients is the set of allowed OCI client values.
// "none" means "image reference only" — no `<client> pull ` prefix.
var validOciClients = map[string]bool{
"docker": true,
"podman": true,
"buildah": true,
"nerdctl": true,
"crane": true,
"none": true,
}
// UpdateOciClientHandler handles updating the preferred OCI client
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -43,7 +43,6 @@
<symbol id="loader" viewBox="0 0 24 24"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></symbol>
<symbol id="loader-2" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></symbol>
<symbol id="moon" viewBox="0 0 24 24"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></symbol>
<symbol id="package" viewBox="0 0 24 24"><path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"/><path d="M12 22V12"/><polyline points="3.29 7 12 12 20.71 7"/><path d="m7.5 4.27 9 5.15"/></symbol>
<symbol id="pencil" viewBox="0 0 24 24"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></symbol>
<symbol id="plus" viewBox="0 0 24 24"><path d="M5 12h14"/><path d="M12 5v14"/></symbol>
<symbol id="radio-tower" viewBox="0 0 24 24"><path d="M4.9 16.1C1 12.2 1 5.8 4.9 1.9"/><path d="M7.8 4.7a6.14 6.14 0 0 0-.8 7.5"/><circle cx="12" cy="9" r="2"/><path d="M16.2 4.8c2 2 2.26 5.11.8 7.47"/><path d="M19.1 1.9a9.96 9.96 0 0 1 0 14.1"/><path d="M9.5 18h5"/><path d="m8 22 4-11 4 11"/></symbol>

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+1
View File
@@ -505,6 +505,7 @@ func NewAppViewServer(cfg *Config, branding *BrandingOverrides) (*AppViewServer,
mainRouter.Handle("/css/*", CacheMiddleware(http.StripPrefix("/css/", PublicSubdir("css", branding)), 31536000))
mainRouter.Handle("/js/*", CacheMiddleware(http.StripPrefix("/js/", PublicSubdir("js", branding)), 31536000))
mainRouter.Handle("/fonts/*", CacheMiddleware(http.StripPrefix("/fonts/", PublicSubdir("fonts", branding)), 31536000))
mainRouter.Handle("/static/*", CacheMiddleware(http.StripPrefix("/static/", PublicSubdir("static", branding)), 31536000))
slog.Info("UI enabled", "home", "/", "settings", "/settings")
+116 -1
View File
@@ -15,6 +15,82 @@
dark --prefersdark;
}
/* ========================================
WEB FONTS — Onest (display), Figtree (body), Commit Mono (mono)
All self-hosted under /fonts/. Variable bodies for display/body,
static weights for mono.
======================================== */
/* Onest — variable (400..800), display face */
@font-face {
font-family: "Onest";
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url("/fonts/onest-latin.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Onest";
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url("/fonts/onest-latin-ext.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Figtree — variable (300..900), body face */
@font-face {
font-family: "Figtree";
font-style: normal;
font-weight: 300 900;
font-display: swap;
src: url("/fonts/figtree-latin.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Figtree";
font-style: normal;
font-weight: 300 900;
font-display: swap;
src: url("/fonts/figtree-latin-ext.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Commit Mono — static 400/400-italic/700, mono face */
@font-face {
font-family: "Commit Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/fonts/commit-mono-400.woff2") format("woff2");
}
@font-face {
font-family: "Commit Mono";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("/fonts/commit-mono-400-italic.woff2") format("woff2");
}
@font-face {
font-family: "Commit Mono";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("/fonts/commit-mono-700.woff2") format("woff2");
}
/* ========================================
TYPE SYSTEM — semantic tokens
Override Tailwind 4's default font stacks so `font-sans` / `font-mono`
utilities (and DaisyUI components) pick up our vendored fonts.
======================================== */
@theme inline {
--font-sans: "Figtree", ui-sans-serif, system-ui, sans-serif;
--font-mono: "Commit Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
--font-display: "Onest", ui-sans-serif, system-ui, sans-serif;
}
/* ============================================
DARK - "Deep Ocean"
============================================ */
@@ -120,6 +196,19 @@
border-color: var(--color-star);
}
/* ========================================
TYPE UTILITIES — display font + tabular numerals
`font-display` applies Onest; use it on brand lockups and hero
headings. `tabular-nums` aligns numeric columns in dense tables.
======================================== */
@utility font-display {
font-family: var(--font-display);
letter-spacing: 0.005em;
}
@utility tabular-nums {
font-variant-numeric: tabular-nums;
}
[data-theme="dark"] {
--shadow-card-hover:
0 8px 25px oklch(67.1% 0.05 145 / 0.2), 0 4px 12px oklch(0% 0 0 / 0.2);
@@ -161,16 +250,32 @@
}
/* ========================================
STICKY FOOTER LAYOUT
STICKY FOOTER LAYOUT + BASE TYPE
======================================== */
@layer base {
body {
@apply min-h-screen flex flex-col;
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
main {
@apply flex-1;
}
/* Cap long prose at a comfortable reading width. Pages that need to
break out of this (dashboards, tables) override with max-w-* utilities. */
.prose {
max-width: 68ch;
}
/* Every table gets tabular-nums by default. Registry UIs are tables of
numbers (pull counts, sizes, CVE counts, timestamps) — digits should
align vertically so the eye can compare rows. */
table {
font-variant-numeric: tabular-nums;
}
}
/* ========================================
@@ -245,6 +350,16 @@
}
}
/* ========================================
KEYBOARD FOCUS RING
Applied only on :focus-visible so mouse users don't see it.
Uses the primary hue so it reads as part of the Deep Ocean palette.
======================================== */
:where(a, button, [role="button"], [role="tab"], input, select, textarea, summary, [tabindex]):focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* ========================================
CUSTOM COMPONENTS (Not in DaisyUI)
======================================== */
@@ -15,7 +15,7 @@
- .HasMore: bool - whether to show Load More button
*/}}
{{ if .Repositories }}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3{{ if eq .Columns 4 }} xl:grid-cols-4{{ end }} gap-4">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3{{ if eq .Columns 4 }} xl:grid-cols-4{{ end }} gap-6">
{{ range .Repositories }}
{{ template "repo-card" . }}
{{ end }}
@@ -24,10 +24,10 @@
- Display: string - short form shown in the UI (e.g. "alice.bsky.social/myapp:v1.2.3")
- Copy: string - full command copied to clipboard (e.g. "docker pull atcr.io/alice.bsky.social/myapp:v1.2.3")
*/}}
<div class="cmd group !w-full">
{{ icon "package" "size-4 shrink-0 text-base-content/60" }}
<code class="flex-1">{{ .Display }}</code>
<button class="btn btn-ghost btn-xs shrink-0 sm:opacity-0 sm:group-hover:opacity-100 focus:opacity-100 transition-opacity" data-cmd="{{ .Copy }}" aria-label="Copy pull command to clipboard">
<div class="cmd group !w-full !bg-base-100">
{{ icon "terminal" "size-4 shrink-0 text-base-content/60" }}
<code class="flex-1 min-w-0">{{ .Display }}</code>
<button class="btn btn-ghost btn-xs absolute right-1 top-1/2 -translate-y-1/2 sm:opacity-0 sm:group-hover:opacity-100 focus:opacity-100 transition-opacity" data-cmd="{{ .Copy }}" aria-label="Copy pull command to clipboard">
{{ icon "copy" "size-4" }}
</button>
</div>
@@ -16,6 +16,8 @@
<!-- Preload critical assets -->
<link rel="preload" href="/icons.svg" as="image" type="image/svg+xml">
<link rel="preload" href="/fonts/figtree-latin.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/commit-mono-400.woff2" as="font" type="font/woff2" crossorigin>
<!-- Theme: apply early to prevent flash -->
<script>
+1 -1
View File
@@ -4,7 +4,7 @@
*/}}
<section class="hero bg-base-200 min-h-[60vh] py-16 pb-24 relative overflow-hidden">
<div class="hero-content text-center flex-col relative z-10 w-full">
<h1 class="text-4xl md:text-5xl font-bold">your registry <span class="text-primary">at</span> sea.</h1>
<h1 class="text-4xl md:text-5xl font-display font-bold tracking-tight">your registry <span class="text-primary">at</span> sea.</h1>
<p class="text-lg text-base-content/70 max-w-lg mt-4">
Push and pull Docker images on the AT Protocol.<br>
Browse public registries or control your data.
@@ -1,6 +1,6 @@
{{ define "nav-brand" }}
<a href="/" class="flex items-center gap-2 text-2xl font-bold text-secondary no-underline">
<a href="/" class="flex items-center gap-2 text-2xl font-display font-bold text-secondary no-underline tracking-tight">
<img src="/favicon-96x96.png" class="h-12 w-auto" alt="{{ .ClientName }} logo">
{{ .ClientName }}
{{ .ClientName }}
</a>
{{ end }}
@@ -28,12 +28,13 @@
<option value="nerdctl"{{ if eq .OciClient "nerdctl" }} selected{{ end }}>nerdctl</option>
<option value="buildah"{{ if eq .OciClient "buildah" }} selected{{ end }}>buildah</option>
<option value="crane"{{ if eq .OciClient "crane" }} selected{{ end }}>crane</option>
<option value="none"{{ if eq .OciClient "none" }} selected{{ end }}>image ref only</option>
</select>
<div id="pull-cmd-display" class="flex-1 min-w-0">
{{ if .Tag }}
{{ template "docker-command" (print (ociClientName .OciClient) " pull " .RegistryURL "/" .OwnerHandle "/" .RepoName ":" .Tag) }}
{{ template "docker-command" (print (pullPrefix .OciClient) .RegistryURL "/" .OwnerHandle "/" .RepoName ":" .Tag) }}
{{ else }}
{{ template "docker-command" (print (ociClientName .OciClient) " pull " .RegistryURL "/" .OwnerHandle "/" .RepoName ":latest") }}
{{ template "docker-command" (print (pullPrefix .OciClient) .RegistryURL "/" .OwnerHandle "/" .RepoName ":latest") }}
{{ end }}
</div>
</div>
@@ -59,7 +60,8 @@
}
window.updatePullCommand = function(client) {
var cmd = client + ' pull ' + registryURL + '/' + ownerHandle + '/' + repoName + ':' + tag;
var prefix = client === 'none' ? '' : client + ' pull ';
var cmd = prefix + registryURL + '/' + ownerHandle + '/' + repoName + ':' + tag;
var container = document.getElementById('pull-cmd-display');
if (!container) return;
var code = container.querySelector('code');
+10 -10
View File
@@ -31,10 +31,10 @@
</div>
{{ end }}
<div class="flex-1 min-w-0">
<div class="font-semibold text-sm truncate">
<a href="/u/{{ .OwnerHandle }}" class="link link-primary" onclick="event.stopPropagation()">{{ .OwnerHandle }}</a>
<span class="text-base-content/60">/</span>
<a href="/r/{{ .OwnerHandle }}/{{ .Repository }}" class="link text-base-content hover:underline" onclick="event.stopPropagation()">{{ .Repository }}</a>
<div class="font-semibold text-sm flex items-baseline gap-1 min-w-0">
<a href="/u/{{ .OwnerHandle }}" class="link link-primary truncate min-w-0" onclick="event.stopPropagation()">{{ .OwnerHandle }}</a>
<span class="text-base-content/60 shrink-0">/</span>
<a href="/r/{{ .OwnerHandle }}/{{ .Repository }}" class="link text-base-content hover:underline truncate min-w-0" onclick="event.stopPropagation()">{{ .Repository }}</a>
</div>
{{ if .Tag }}
<span class="block text-base-content/60 text-sm truncate">Tag: {{ .Tag }}</span>
@@ -48,22 +48,22 @@
{{ if eq .ArtifactType "helm-chart" }}
{{ if .Tag }}
{{ template "image-ref" (dict
"Display" (printf "%s/%s:%s" .OwnerHandle .Repository .Tag)
"Display" (printf "%s/%s/%s:%s" .RegistryURL .OwnerHandle .Repository .Tag)
"Copy" (printf "helm pull oci://%s/%s/%s --version %s" .RegistryURL .OwnerHandle .Repository .Tag)) }}
{{ else }}
{{ template "image-ref" (dict
"Display" (printf "%s/%s" .OwnerHandle .Repository)
"Display" (printf "%s/%s/%s" .RegistryURL .OwnerHandle .Repository)
"Copy" (printf "helm pull oci://%s/%s/%s" .RegistryURL .OwnerHandle .Repository)) }}
{{ end }}
{{ else }}
{{ if .Tag }}
{{ template "image-ref" (dict
"Display" (printf "%s/%s:%s" .OwnerHandle .Repository .Tag)
"Copy" (printf "%s pull %s/%s/%s:%s" (ociClientName .OciClient) .RegistryURL .OwnerHandle .Repository .Tag)) }}
"Display" (printf "%s/%s/%s:%s" .RegistryURL .OwnerHandle .Repository .Tag)
"Copy" (printf "%s%s/%s/%s:%s" (pullPrefix .OciClient) .RegistryURL .OwnerHandle .Repository .Tag)) }}
{{ else }}
{{ template "image-ref" (dict
"Display" (printf "%s/%s" .OwnerHandle .Repository)
"Copy" (printf "%s pull %s/%s/%s" (ociClientName .OciClient) .RegistryURL .OwnerHandle .Repository)) }}
"Display" (printf "%s/%s/%s" .RegistryURL .OwnerHandle .Repository)
"Copy" (printf "%s%s/%s/%s" (pullPrefix .OciClient) .RegistryURL .OwnerHandle .Repository)) }}
{{ end }}
{{ end }}
</div>
+1 -1
View File
@@ -45,7 +45,7 @@
{{ if .RecentRepos }}
<section>
<h2 class="text-2xl font-bold mb-6">What's New</h2>
{{ template "card-grid" (dict "Repositories" .RecentRepos "Columns" 4) }}
{{ template "card-grid" (dict "Repositories" .RecentRepos) }}
</section>
{{ end }}
</div>
+1 -1
View File
@@ -9,7 +9,7 @@
{{ template "nav" . }}
<main class="container mx-auto px-4 py-8 max-w-4xl">
<h1 class="text-3xl font-bold mb-2">Install {{ .ClientShortName }} Credential Helper</h1>
<h1 class="text-3xl font-display font-bold tracking-tight mb-2">Install {{ .ClientShortName }} Credential Helper</h1>
<p class="text-base-content/70 mb-8">The {{ .ClientShortName }} credential helper enables Docker to authenticate with {{ .ClientShortName }} registries using your ATProto identity.</p>
<div class="space-y-12">
+1 -1
View File
@@ -11,7 +11,7 @@
<main class="container mx-auto px-4 py-8 max-w-4xl">
<!-- Hero Section -->
<section class="text-center mb-16">
<h1 class="text-4xl md:text-5xl font-bold mb-4">Docker meets the decentralized web</h1>
<h1 class="text-4xl md:text-5xl font-display font-bold tracking-tight mb-4">Docker meets the decentralized web</h1>
<p class="text-xl text-base-content/70 max-w-2xl mx-auto">
{{ .ClientName }} is an OCI-compliant container registry built on the AT Protocol.
Push and pull Docker images using your Bluesky identity.
+3 -2
View File
@@ -10,7 +10,7 @@
<main class="min-h-[calc(100vh-4rem)] flex items-start justify-center px-4 pt-16 sm:pt-24">
<div class="w-full max-w-2xl">
<h1 class="text-3xl sm:text-4xl font-semibold text-center mb-8">Sign in to {{ .ClientName }}</h1>
<h1 class="text-3xl sm:text-4xl font-display font-semibold tracking-tight text-center mb-8">Sign in to {{ .ClientName }}</h1>
{{ if .Error }}
<div class="alert alert-error mb-6">
@@ -31,12 +31,13 @@
<input type="hidden" name="return_to" value="{{ .ReturnTo }}" />
<div class="sailor-typeahead relative order-1">
<label for="handle" class="sr-only">Atmosphere handle or DID</label>
<input type="text"
id="handle"
name="handle"
class="input input-bordered input-lg w-full"
placeholder="alice.bsky.social"
autocomplete="off"
autocomplete="username"
autocapitalize="off"
autocorrect="off"
spellcheck="false"
+1 -1
View File
@@ -9,7 +9,7 @@
{{ template "nav" . }}
<main class="container mx-auto px-4 py-8 max-w-4xl">
<h1 class="text-3xl font-bold mb-2">Privacy Policy - {{ .CompanyName }} ({{ .SiteURL }})</h1>
<h1 class="text-3xl font-display font-bold tracking-tight mb-2">Privacy Policy - {{ .CompanyName }} ({{ .SiteURL }})</h1>
<p class="text-base-content/60 mb-8"><em>Last updated: January 2025</em></p>
<div class="prose prose-sm max-w-none space-y-8">
+3 -1
View File
@@ -15,7 +15,7 @@
<div class="flex gap-4 items-start">
{{ template "repo-avatar" (dict "IconURL" .Repository.IconURL "RepositoryName" .Repository.Name "IsOwner" .IsOwner) }}
<div class="flex-1 min-w-0">
<h1 class="text-2xl font-bold">
<h1 class="text-2xl md:text-3xl font-display font-bold tracking-tight">
<a href="/u/{{ .Owner.Handle }}" class="link link-primary">{{ .Owner.Handle }}</a>
<span class="text-base-content/60">/</span>
<span>{{ .Repository.Name }}</span>
@@ -502,9 +502,11 @@
if (tab.dataset.tab === tabId) {
tab.classList.add('border-primary', 'text-primary');
tab.classList.remove('border-transparent', 'text-base-content/60');
tab.setAttribute('aria-selected', 'true');
} else {
tab.classList.remove('border-primary', 'text-primary');
tab.classList.add('border-transparent', 'text-base-content/60');
tab.setAttribute('aria-selected', 'false');
}
});
+7 -6
View File
@@ -9,7 +9,7 @@
{{ template "nav" . }}
<main class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold mb-6">Settings</h1>
<h1 class="text-3xl font-display font-bold tracking-tight mb-6">Settings</h1>
<!-- Mobile identity info (below lg) -->
<div class="lg:hidden mb-4 space-y-1 text-xs text-base-content/50">
@@ -67,20 +67,21 @@
<p class="text-base-content/70 mt-1">Customize your experience across the site.</p>
</div>
<!-- OCI Client Selector -->
<!-- Preferred Client Selector -->
<div class="flex items-center gap-4">
<div>
<label class="text-sm font-medium">OCI Client</label>
<p class="text-xs text-base-content/60">Changes how pull commands are displayed across the site.</p>
<label for="oci-client-select" class="text-sm font-medium">Preferred client</label>
<p id="oci-client-hint" class="text-xs text-base-content/60">Sets the pull command shown on repository pages. Choose <em>Image reference only</em> to copy just the image ref (useful for Kubernetes manifests or docker-compose).</p>
</div>
{{ $oci := .Profile.OciClient }}
<select id="oci-client-select" class="select select-sm select-bordered min-w-40"
<select id="oci-client-select" aria-describedby="oci-client-hint" class="select select-sm select-bordered min-w-40"
onchange="htmx.ajax('POST', '/api/profile/oci-client', {values: {oci_client: this.value}, swap: 'none'})">
<option value="docker"{{ if or (eq $oci "") (eq $oci "docker") }} selected{{ end }}>Docker</option>
<option value="podman"{{ if eq $oci "podman" }} selected{{ end }}>Podman</option>
<option value="buildah"{{ if eq $oci "buildah" }} selected{{ end }}>Buildah</option>
<option value="nerdctl"{{ if eq $oci "nerdctl" }} selected{{ end }}>nerdctl</option>
<option value="crane"{{ if eq $oci "crane" }} selected{{ end }}>crane</option>
<option value="none"{{ if eq $oci "none" }} selected{{ end }}>Image reference only</option>
</select>
</div>
@@ -189,7 +190,7 @@
}</code></pre>
</li>
<li>Run any Docker command:
<div class="mt-2">{{ template "docker-command" (print (ociClientName .OciClient) " pull " .RegistryURL "/" .Profile.Handle "/myimage") }}</div>
<div class="mt-2">{{ template "docker-command" (print (pullPrefix .OciClient) .RegistryURL "/" .Profile.Handle "/myimage") }}</div>
</li>
<li>Browser will open for authorization - click Approve</li>
<li>Done! Device is automatically authorized</li>
+1 -1
View File
@@ -9,7 +9,7 @@
{{ template "nav" . }}
<main class="container mx-auto px-4 py-8 max-w-4xl">
<h1 class="text-3xl font-bold mb-2">Terms of Service - {{ .CompanyName }} ({{ .SiteURL }})</h1>
<h1 class="text-3xl font-display font-bold tracking-tight mb-2">Terms of Service - {{ .CompanyName }} ({{ .SiteURL }})</h1>
<p class="text-base-content/60 mb-8"><em>Last updated: January 2025</em></p>
<p class="mb-8">These Terms of Service ("Terms") govern your use of {{ .CompanyName }} ("{{ .SiteURL }}", "the Service", "we", "us", "our"). By using the Service, you agree to these Terms. If you do not agree, do not use the Service.</p>
+1 -1
View File
@@ -32,7 +32,7 @@
</div>
{{ end }}
<div class="flex items-center gap-2">
<h1 class="text-2xl font-bold">{{ .ViewedUser.Handle }}</h1>
<h1 class="text-2xl md:text-3xl font-display font-bold tracking-tight">{{ .ViewedUser.Handle }}</h1>
{{ if or (eq .SupporterBadge "Captain") (eq .SupporterBadge "owner") }}
<span class="badge badge-sm supporter-badge-owner">{{ .SupporterBadge }}</span>
{{ else if .SupporterBadge }}
@@ -24,9 +24,15 @@
{{ else }}<span class="badge badge-xs badge-secondary">Crew</span>{{ end }}
</td>
<td class="text-center">
{{ if eq .Status "online" }}<span class="text-success" title="Online">&#9679;</span>
{{ else if eq .Status "offline" }}<span class="text-error" title="Offline">&#9679;</span>
{{ else }}<span class="text-base-content/50" title="Unknown" aria-label="Unknown status">&#9679;</span>
{{ if eq .Status "online" }}
<span class="text-success" aria-hidden="true">&#9679;</span>
<span class="sr-only">Online</span>
{{ else if eq .Status "offline" }}
<span class="text-error" aria-hidden="true">&#9679;</span>
<span class="sr-only">Offline</span>
{{ else }}
<span class="text-base-content/50" aria-hidden="true">&#9679;</span>
<span class="sr-only">Unknown</span>
{{ end }}
</td>
<td class="text-right">
@@ -73,10 +73,13 @@
<!-- Tab Navigation -->
<div class="border-b border-base-300 mt-6">
<nav class="flex gap-0 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" role="tablist">
<nav class="flex gap-0 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" role="tablist" aria-label="Repository sections">
<button class="repo-tab shrink-0 whitespace-nowrap px-4 sm:px-6 py-3 text-sm font-medium border-b-2 border-transparent text-base-content/60 transition-colors cursor-pointer"
data-tab="overview"
role="tab"
aria-selected="false"
aria-controls="tab-overview"
id="overview-tab-btn"
onclick="switchRepoTab('overview')">
Overview
</button>
@@ -84,6 +87,8 @@
<button class="repo-tab shrink-0 whitespace-nowrap px-4 sm:px-6 py-3 text-sm font-medium border-b-2 border-transparent text-base-content/60 transition-colors cursor-pointer"
data-tab="layers"
role="tab"
aria-selected="false"
aria-controls="tab-layers"
id="layers-tab-btn"
onclick="switchRepoTab('layers')">
Layers
@@ -91,6 +96,8 @@
<button class="repo-tab shrink-0 whitespace-nowrap px-4 sm:px-6 py-3 text-sm font-medium border-b-2 border-transparent text-base-content/60 transition-colors cursor-pointer"
data-tab="vulns"
role="tab"
aria-selected="false"
aria-controls="tab-vulns"
id="vulns-tab-btn"
onclick="switchRepoTab('vulns')">
Vulnerabilities
@@ -98,6 +105,8 @@
<button class="repo-tab shrink-0 whitespace-nowrap px-4 sm:px-6 py-3 text-sm font-medium border-b-2 border-transparent text-base-content/60 transition-colors cursor-pointer"
data-tab="sbom"
role="tab"
aria-selected="false"
aria-controls="tab-sbom"
id="sbom-tab-btn"
onclick="switchRepoTab('sbom')">
SBOM
@@ -106,6 +115,8 @@
<button class="repo-tab shrink-0 whitespace-nowrap px-4 sm:px-6 py-3 text-sm font-medium border-b-2 border-transparent text-base-content/60 transition-colors cursor-pointer"
data-tab="artifacts"
role="tab"
aria-selected="false"
aria-controls="tab-artifacts"
id="artifacts-tab-btn"
onclick="switchRepoTab('artifacts')">
Artifacts
@@ -114,7 +125,7 @@
</div>
<!-- Tab Panels -->
<div id="tab-overview" class="repo-panel">
<div id="tab-overview" class="repo-panel" role="tabpanel" aria-labelledby="overview-tab-btn" tabindex="0">
<div id="overview-view" class="card bg-base-100 shadow-sm p-6 space-y-4 min-w-0">
{{ if and .AIAdvisorEnabled .User .IsOwner .SelectedTag }}
<div id="ai-advisor-section">
@@ -169,7 +180,7 @@
</div>
{{ if .SelectedTag }}
<div id="tab-layers" class="repo-panel hidden">
<div id="tab-layers" class="repo-panel hidden" role="tabpanel" aria-labelledby="layers-tab-btn" tabindex="0">
<div id="layers-content">
<div class="flex justify-center py-12">
<span class="loading loading-spinner loading-lg"></span>
@@ -177,7 +188,7 @@
</div>
</div>
<div id="tab-vulns" class="repo-panel hidden">
<div id="tab-vulns" class="repo-panel hidden" role="tabpanel" aria-labelledby="vulns-tab-btn" tabindex="0">
<div id="vulns-content">
<div class="flex justify-center py-12">
<span class="loading loading-spinner loading-lg"></span>
@@ -185,7 +196,7 @@
</div>
</div>
<div id="tab-sbom" class="repo-panel hidden">
<div id="tab-sbom" class="repo-panel hidden" role="tabpanel" aria-labelledby="sbom-tab-btn" tabindex="0">
<div id="sbom-content">
<div class="flex justify-center py-12">
<span class="loading loading-spinner loading-lg"></span>
@@ -194,7 +205,7 @@
</div>
{{ end }}
<div id="tab-artifacts" class="repo-panel hidden">
<div id="tab-artifacts" class="repo-panel hidden" role="tabpanel" aria-labelledby="artifacts-tab-btn" tabindex="0">
<div id="artifacts-content">
<div class="flex justify-center py-12">
<span class="loading loading-spinner loading-lg"></span>
@@ -56,9 +56,9 @@
{{ end }}
{{ else }}
{{ if .Entry.IsTagged }}
{{ template "docker-command" (print (ociClientName .OciClient) " pull " .RegistryURL "/" .OwnerHandle "/" .RepoName ":" .Entry.Label) }}
{{ template "docker-command" (print (pullPrefix .OciClient) .RegistryURL "/" .OwnerHandle "/" .RepoName ":" .Entry.Label) }}
{{ else }}
{{ template "docker-command" (print (ociClientName .OciClient) " pull " .RegistryURL "/" .OwnerHandle "/" .RepoName "@" .Entry.Digest) }}
{{ template "docker-command" (print (pullPrefix .OciClient) .RegistryURL "/" .OwnerHandle "/" .RepoName "@" .Entry.Digest) }}
{{ end }}
{{ end }}
</div>
@@ -44,8 +44,8 @@
{{ if .Matches }}
<!-- CVE table -->
<div class="overflow-y-auto max-h-[32rem]">
<table class="table table-xs table-pin-rows w-full">
<div class="overflow-x-auto overflow-y-auto max-h-[32rem]">
<table class="table table-xs table-pin-rows w-full min-w-[40rem]">
<thead>
<tr>
<th>CVE</th>
+13
View File
@@ -348,6 +348,19 @@ func Templates(overrides *BrandingOverrides) (*template.Template, error) {
return client
},
// pullPrefix returns the "<client> pull " prefix for a pull command, or an
// empty string when the user has selected "none" (image reference only).
// Usage: {{ pullPrefix .OciClient }}
"pullPrefix": func(client string) string {
if client == "none" {
return ""
}
if client == "" {
return "docker pull "
}
return client + " pull "
},
// extraCSS returns a <style> block with consumer CSS overrides, or empty string.
"extraCSS": func() template.HTML {
if extraCSS == "" {
+2 -2
View File
@@ -353,8 +353,8 @@ type SailorProfileRecord struct {
// overwrite) are deleted from PDS, and their layers are cleaned up by hold GC.
AutoRemoveUntagged bool `json:"autoRemoveUntagged,omitempty"`
// OciClient is the preferred OCI client for pull commands (docker, podman, buildah, nerdctl, crane).
// Defaults to "docker" if empty.
// OciClient is the preferred client for pull commands (docker, podman, buildah, nerdctl, crane).
// "none" means image reference only (no `<client> pull ` prefix). Defaults to "docker" if empty.
OciClient string `json:"ociClient,omitempty"`
// AIAdvisorEnabled controls whether the AI Image Advisor feature is active for this user.
-1
View File
@@ -43,7 +43,6 @@
<symbol id="loader" viewBox="0 0 24 24"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></symbol>
<symbol id="loader-2" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></symbol>
<symbol id="moon" viewBox="0 0 24 24"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></symbol>
<symbol id="package" viewBox="0 0 24 24"><path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"/><path d="M12 22V12"/><polyline points="3.29 7 12 12 20.71 7"/><path d="m7.5 4.27 9 5.15"/></symbol>
<symbol id="pencil" viewBox="0 0 24 24"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></symbol>
<symbol id="plus" viewBox="0 0 24 24"><path d="M5 12h14"/><path d="M12 5v14"/></symbol>
<symbol id="radio-tower" viewBox="0 0 24 24"><path d="M4.9 16.1C1 12.2 1 5.8 4.9 1.9"/><path d="M7.8 4.7a6.14 6.14 0 0 0-.8 7.5"/><circle cx="12" cy="9" r="2"/><path d="M16.2 4.8c2 2 2.26 5.11.8 7.47"/><path d="M19.1 1.9a9.96 9.96 0 0 1 0 14.1"/><path d="M9.5 18h5"/><path d="m8 22 4-11 4 11"/></symbol>

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB