more hardening, inline tangled svg into the sprite

This commit is contained in:
Evan Jarrett
2026-04-22 20:13:40 -05:00
parent 267012b41e
commit c7783bf87c
20 changed files with 96 additions and 357 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -253,22 +253,6 @@
--btn-border: transparent;
}
/* ========================================
THEME-AWARE ICON SWITCHING
======================================== */
.icon-light {
display: block;
}
.icon-dark {
display: none;
}
[data-theme="dark"] .icon-light {
display: none;
}
[data-theme="dark"] .icon-dark {
display: block;
}
/* ========================================
STICKY FOOTER LAYOUT + BASE TYPE
======================================== */

View File

@@ -1,6 +1,6 @@
{{ define "footer" }}
<footer class="footer footer-center bg-base-200 text-base-content p-6 pt-20 mt-auto relative">
<img src="/static/wave-pattern.svg" alt="" width="1440" height="64" decoding="async" class="absolute top-0 left-0 w-full h-16 pointer-events-none rotate-180" aria-hidden="true">
<img src="/static/wave-pattern.svg" alt="" width="1440" height="64" loading="lazy" decoding="async" class="absolute top-0 left-0 w-full h-16 pointer-events-none rotate-180" aria-hidden="true">
<nav class="flex flex-wrap justify-center items-center gap-x-2 gap-y-1 text-sm">
<a href="/privacy" class="link link-hover">Privacy</a>
<span aria-hidden="true" class="text-base-content/30">·</span>
@@ -13,8 +13,7 @@
{{ with .SourceURL }}
<span aria-hidden="true" class="text-base-content/30">·</span>
<a href="{{ . }}" target="_blank" rel="noopener" class="link link-hover inline-flex items-center gap-1">
<img src="/static/tangled-black.svg" alt="" width="14" height="14" loading="lazy" decoding="async" class="size-3.5 icon-light" aria-hidden="true">
<img src="/static/tangled-white.svg" alt="" width="14" height="14" loading="lazy" decoding="async" class="size-3.5 icon-dark" aria-hidden="true">
{{ icon "tangled" "size-3.5" }}
Source
</a>
{{ end }}

View File

@@ -20,11 +20,11 @@
</div>
{{ if or .FromFailed .ToFailed }}
{{ template "alert" (dict "Type" "warning" "Message" (printf "We couldn't fetch details for %s%s%s — showing what we have." (or (and .FromFailed .FromTag) "") (or (and .FromFailed .ToFailed) " and ") (or (and .ToFailed .ToTag) ""))) }}
{{ template "alert" (dict "Type" "warning" "Message" (printf "We couldn't fetch details for %s%s%s. Showing what we have." (or (and .FromFailed .FromTag) "") (or (and .FromFailed .ToFailed) " and ") (or (and .ToFailed .ToTag) ""))) }}
{{ end }}
{{ if and (not .FromFailed) (not .ToFailed) (eq .FromDigest .ToDigest) }}
{{ template "alert" (dict "Type" "info" "Message" "These manifests are identical — no layers or vulnerabilities changed.") }}
{{ template "alert" (dict "Type" "info" "Message" "These manifests are identical. No layers or vulnerabilities changed.") }}
{{ end }}
<!-- Summary Card -->

View File

@@ -166,8 +166,7 @@
<a href="/install" class="btn btn-primary btn-lg">Get Started</a>
{{ with .SourceURL }}
<a href="{{ . }}" target="_blank" rel="noopener noreferrer" class="btn btn-ghost btn-lg">
<img src="/static/tangled-black.svg" alt="" aria-hidden="true" width="20" height="20" loading="lazy" decoding="async" class="size-5 mr-2 icon-light">
<img src="/static/tangled-white.svg" alt="" aria-hidden="true" width="20" height="20" loading="lazy" decoding="async" class="size-5 mr-2 icon-dark">
{{ icon "tangled" "size-5 mr-2" }}
View Source
</a>
{{ end }}

View File

@@ -21,7 +21,7 @@
{{ else if eq .Error "invalid_handle" }}
That handle doesn't look right. Check for typos and try again.
{{ else if eq .Error "pds_unreachable" }}
We couldn't reach your PDS. It may be offline try again in a minute.
We couldn't reach your PDS. It may be offline; try again in a minute.
{{ else if eq .Error "state_mismatch" }}
Your sign-in session expired before we finished. Please start over.
{{ else if eq .Error "access_denied" }}
@@ -44,7 +44,9 @@
<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>
<label for="handle" class="block text-xs font-medium uppercase tracking-wider text-base-content/60 mb-2">
Atmosphere handle
</label>
<input type="text"
id="handle"
name="handle"

View File

@@ -32,11 +32,15 @@
</div>
</details>
{{ else if .Size }}
<p class="text-sm text-base-content/70">Binary content ({{ humanizeBytes .Size }}) cannot display inline</p>
<p class="text-sm text-base-content/70">Binary content ({{ humanizeBytes .Size }}); cannot display inline</p>
{{ end }}
</div>
{{ else }}
<p class="text-base-content/60">No attestations attached to this manifest.</p>
{{ template "state-empty" (dict
"Icon" "fingerprint"
"Title" "No attestations attached"
"Subtext" "Signed provenance or SBOM attestations will appear here once attached to this manifest."
) }}
{{ end }}
</div>
{{ end }}

View File

@@ -40,7 +40,11 @@
</table>
</div>
{{ else }}
<p class="text-base-content/60">No layer information available</p>
{{ template "state-empty" (dict
"Icon" "history"
"Title" "No layer history"
"Subtext" "Neither side of this diff reports layer history."
) }}
{{ end }}
</div>

View File

@@ -37,7 +37,11 @@
</table>
</div>
{{ else }}
<p class="text-base-content">No layer information available</p>
{{ template "state-empty" (dict
"Icon" "history"
"Title" "No layer history"
"Subtext" "This manifest has no layer history recorded."
) }}
{{ end }}
</div>
@@ -49,7 +53,11 @@
{{ if .VulnData }}
{{ template "vuln-details" .VulnData }}
{{ else }}
<p class="text-base-content">No vulnerability scan data available</p>
{{ template "state-empty" (dict
"Icon" "shield-check"
"Title" "No scan data yet"
"Subtext" "The scanner hasn't reported results for this manifest."
) }}
{{ end }}
</div>

View File

@@ -8,13 +8,13 @@
{{/* Branch on classified reason so the tooltip tells operators what
kind of failure they're seeing, not just "it's down". */}}
{{ if eq .Reason "dns" }}
<span class="badge badge-sm badge-warning" role="status" title="DNS lookup failed the hostname can't be resolved">{{ icon "triangle-alert" "size-3" }} DNS failed</span>
<span class="badge badge-sm badge-warning" role="status" title="DNS lookup failed: the hostname can't be resolved">{{ icon "triangle-alert" "size-3" }} DNS failed</span>
{{ else if eq .Reason "tls" }}
<span class="badge badge-sm badge-warning" role="status" title="TLS handshake failed certificate may be expired or invalid">{{ icon "triangle-alert" "size-3" }} TLS error</span>
<span class="badge badge-sm badge-warning" role="status" title="TLS handshake failed: certificate may be expired or invalid">{{ icon "triangle-alert" "size-3" }} TLS error</span>
{{ else if eq .Reason "refused" }}
<span class="badge badge-sm badge-warning" role="status" title="Connection refused nothing is listening on the endpoint">{{ icon "triangle-alert" "size-3" }} Refused</span>
<span class="badge badge-sm badge-warning" role="status" title="Connection refused: nothing is listening on the endpoint">{{ icon "triangle-alert" "size-3" }} Refused</span>
{{ else if eq .Reason "timeout" }}
<span class="badge badge-sm badge-warning" role="status" title="Request timed out the hold is slow or unreachable">{{ icon "triangle-alert" "size-3" }} Timeout</span>
<span class="badge badge-sm badge-warning" role="status" title="Request timed out: the hold is slow or unreachable">{{ icon "triangle-alert" "size-3" }} Timeout</span>
{{ else if eq .Reason "http" }}
<span class="badge badge-sm badge-warning" role="status" title="The hold responded with an error status">{{ icon "triangle-alert" "size-3" }} HTTP error</span>
{{ else }}

View File

@@ -31,7 +31,7 @@
{{ if .Command }}
<code class="font-mono text-xs break-all line-clamp-2" title="{{ .Command }}">{{ .Command }}</code>
{{ else if not .EmptyLayer }}
<span class="text-xs text-base-content/40 italic">no command recorded</span>
<span class="text-xs text-base-content/40 italic">no command recorded</span>
{{ end }}
</td>
<td class="text-right text-sm whitespace-nowrap" data-bytes="{{ .Size }}">{{ humanizeBytes .Size }}</td>
@@ -41,10 +41,11 @@
</table>
</div>
{{ else }}
<div class="py-8 text-sm text-base-content/70 max-w-prose">
<p class="font-medium text-base-content">No layer history available</p>
<p class="mt-1">This image was built without history metadata — common for squashed images or tarballs imported with <code class="font-mono text-xs bg-base-300 px-1 py-0.5 rounded">docker load</code>.</p>
</div>
{{ template "state-empty" (dict
"Icon" "history"
"Title" "No layer history"
"Subtext" "This image was built without history metadata, common for squashed images or tarballs imported with docker load."
) }}
{{ end }}
</div>
{{ end }}

View File

@@ -67,10 +67,11 @@
{{ else }}
<!-- No tags exist -->
<div class="text-center py-8 text-base-content/60">
<p class="text-lg">No tags yet</p>
<p class="text-sm mt-1">Push an image to get started.</p>
</div>
{{ template "state-empty" (dict
"Icon" "tag"
"Title" "No tags yet"
"Subtext" "Push an image to this repository and its tags will appear here."
) }}
{{ end }}
<!-- Tab Navigation -->

View File

@@ -18,8 +18,12 @@
{{ end }}
</div>
{{ else }}
<div class="card bg-base-200 shadow-sm p-6 text-center text-base-content/60">
No holds configured. Push an image to get started.
<div class="card bg-base-200 shadow-sm">
{{ template "state-empty" (dict
"Icon" "anchor"
"Title" "No holds configured"
"Subtext" "Push an image to atcr.io and your first hold will appear here."
) }}
</div>
{{ end }}

View File

@@ -1,7 +1,7 @@
{{ define "vuln-badge" }}
{{ if .Error }}
{{/* Hold unreachable. Warning color distinguishes from "not scanned" (gray). */}}
<span class="badge badge-sm badge-warning" title="Hold is unreachable try again in a moment">
<span class="badge badge-sm badge-warning" title="Hold is unreachable; try again in a moment">
{{ icon "wifi-off" "size-3" }} Hold offline
</span>
{{ else if .NotScanned }}

View File

@@ -146,7 +146,10 @@ func AssetHash(path string) string {
// fontPreloadPaths returns the list of /fonts/*.woff2 files that exist in
// the resolved public FS. Skips *-ext (extended glyph) subsets to avoid
// preloading weights the primary latin face already covers.
// preloading weights the primary latin face already covers, and skips
// non-canonical weights so the critical preload burst stays focused on
// the faces needed above the fold. Commit Mono 700 (~47KB) is loaded
// on demand via @font-face with font-display: swap.
func fontPreloadPaths(overrides *BrandingOverrides) []string {
fsys := resolvePublicFS(overrides)
entries, err := fs.ReadDir(fsys, "public/fonts")
@@ -168,6 +171,9 @@ func fontPreloadPaths(overrides *BrandingOverrides) []string {
if strings.Contains(name, "italic") {
continue
}
if strings.Contains(name, "-700") {
continue
}
paths = append(paths, "/fonts/"+name)
}
return paths

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -72,14 +72,37 @@ function discoverIcons() {
const ICONS = discoverIcons();
// Custom Helm icon (from Simple Icons - official Helm logo)
// Custom icon registry. Entries can inline `content`, or reference a source
// SVG via `sourceFile` — the sprite builder extracts the inner markup of the
// <svg> and wraps it in a <symbol> with the resolved viewBox.
const CUSTOM_ICONS = {
'helm': {
viewBox: '0 0 24 24',
content: '<path d="M12.337 0c-.475 0-.861 1.016-.861 2.269 0 .527.069 1.011.183 1.396a8.514 8.514 0 0 0-3.961 1.22 5.229 5.229 0 0 0-.595-1.093c-.606-.866-1.34-1.436-1.79-1.43a.381.381 0 0 0-.217.066c-.39.273-.123 1.326.596 2.353.267.381.559.705.84.948a8.683 8.683 0 0 0-1.528 1.716h1.734a7.179 7.179 0 0 1 5.381-2.421 7.18 7.18 0 0 1 5.382 2.42h1.733a8.687 8.687 0 0 0-1.32-1.53c.35-.249.735-.643 1.078-1.133.719-1.027.986-2.08.596-2.353a.382.382 0 0 0-.217-.065c-.45-.007-1.184.563-1.79 1.43a4.897 4.897 0 0 0-.676 1.325 8.52 8.52 0 0 0-3.899-1.42c.12-.39.193-.887.193-1.429 0-1.253-.386-2.269-.862-2.269zM1.624 9.443v5.162h1.358v-1.968h1.64v1.968h1.357V9.443H4.62v1.838H2.98V9.443zm5.912 0v5.162h3.21v-1.108H8.893v-.95h1.64v-1.142h-1.64v-.84h1.853V9.443zm4.698 0v5.162h3.218v-1.362h-1.86v-3.8zm4.706 0v5.162h1.364v-2.643l1.357 1.225 1.35-1.232v2.65h1.365V9.443h-.614l-2.1 1.914-2.109-1.914zm-11.82 7.28a8.688 8.688 0 0 0 1.412 1.548 5.206 5.206 0 0 0-.841.948c-.719 1.027-.985 2.08-.596 2.353.39.273 1.289-.338 2.007-1.364a5.23 5.23 0 0 0 .595-1.092 8.514 8.514 0 0 0 3.961 1.219 5.01 5.01 0 0 0-.183 1.396c0 1.253.386 2.269.861 2.269.476 0 .862-1.016.862-2.269 0-.542-.072-1.04-.193-1.43a8.52 8.52 0 0 0 3.9-1.42c.121.4.352.865.675 1.327.719 1.026 1.617 1.637 2.007 1.364.39-.273.123-1.326-.596-2.353-.343-.49-.727-.885-1.077-1.135a8.69 8.69 0 0 0 1.202-1.36h-1.771a7.174 7.174 0 0 1-5.227 2.252 7.174 7.174 0 0 1-5.226-2.252z" fill="currentColor" stroke="none"/>'
},
'tangled': {
sourceFile: 'scripts/icons/tangled.svg'
}
};
// Read a source SVG and extract its viewBox + inner markup.
function loadSvgSource(relPath) {
const fullPath = path.join(__dirname, '..', relPath);
const raw = fs.readFileSync(fullPath, 'utf8');
const viewBoxMatch = raw.match(/viewBox="([^"]+)"/);
if (!viewBoxMatch) {
throw new Error(`No viewBox in ${relPath}`);
}
const innerMatch = raw.match(/<svg[^>]*>([\s\S]*)<\/svg>/);
if (!innerMatch) {
throw new Error(`Could not extract inner markup from ${relPath}`);
}
return {
viewBox: viewBoxMatch[1],
content: innerMatch[1].trim()
};
}
// Lucide icon name to Pascal case mapping (for require path)
function toPascalCase(str) {
return str.split('-').map(word =>
@@ -133,7 +156,8 @@ function generateSprite() {
// Process custom icons
for (const [name, icon] of Object.entries(CUSTOM_ICONS)) {
symbols.push(` <symbol id="${name}" viewBox="${icon.viewBox}">${icon.content}</symbol>`);
const resolved = icon.sourceFile ? loadSvgSource(icon.sourceFile) : icon;
symbols.push(` <symbol id="${name}" viewBox="${resolved.viewBox}">${resolved.content}</symbol>`);
}
const sprite = `<svg xmlns="http://www.w3.org/2000/svg" style="display:none">

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.5 KiB