clean up layouts, add favicons

This commit is contained in:
Evan Jarrett
2025-10-26 15:20:54 -05:00
parent 6bc929f2dc
commit 1727801df3
24 changed files with 107 additions and 68 deletions
+1 -1
View File
@@ -525,7 +525,7 @@ This ensures:
### Key Design Decisions
1. **No fork of distribution**: Uses distribution as library, extends via middleware
2. **Hybrid storage**: Manifests in ATProto (small, federated), blobs in S3 or BYOS (cheap, scalable)
2. **Hybrid storage**: Manifests in ATProto (small), blobs in S3 or BYOS (cheap, scalable)
3. **Content addressing**: Manifests stored by digest, blobs deduplicated globally
4. **ATProto-native**: Manifests are first-class ATProto records, discoverable via AT Protocol
5. **OCI compliant**: Fully compatible with Docker/containerd/podman
+22 -2
View File
@@ -338,8 +338,28 @@ func serveRegistry(cmd *cobra.Command, args []string) error {
// Mount static files if UI is enabled
if uiSessionStore != nil && uiTemplates != nil {
// Mount static files
mainRouter.Handle("/static/*", http.StripPrefix("/static/", appview.StaticHandler()))
// Register dynamic routes for root-level files (favicons, manifests, etc.)
staticHandler := appview.StaticHandler()
rootFiles, err := appview.StaticRootFiles()
if err != nil {
slog.Warn("Failed to scan static root files", "error", err)
} else {
for _, filename := range rootFiles {
// Create a closure to capture the filename
file := filename
mainRouter.Get("/"+file, func(w http.ResponseWriter, r *http.Request) {
// Serve the specific file from static root
r.URL.Path = "/" + file
staticHandler.ServeHTTP(w, r)
})
}
slog.Info("Registered dynamic root file routes", "count", len(rootFiles), "files", rootFiles)
}
// Mount subdirectory routes with clean paths
mainRouter.Handle("/css/*", http.StripPrefix("/css/", appview.StaticSubdir("css")))
mainRouter.Handle("/js/*", http.StripPrefix("/js/", appview.StaticSubdir("js")))
mainRouter.Handle("/static/*", http.StripPrefix("/static/", appview.StaticSubdir("static")))
slog.Info("UI enabled", "home", "/", "settings", "/settings")
}
+2 -2
View File
@@ -4,7 +4,7 @@
The ATCR AppView UI provides a web interface for discovering, managing, and configuring container images in the ATCR registry. Version 1 focuses on three core pages that leverage existing functionality:
1. **Front Page** - Federated image discovery via firehose
1. **Front Page** - Distributed image discovery via firehose
2. **Settings Page** - Profile and hold configuration
3. **Personal Page** - Manage your images and tags
@@ -204,7 +204,7 @@ Jetstream is simpler and filters events server-side.
## Page Specifications
### 1. Front Page - Federated Discovery
### 1. Front Page - Distributed Discovery
**URL:** `/ui/` or `/ui/explore`
+1 -1
View File
@@ -14,7 +14,7 @@ Several ATProto projects face similar challenges with large data storage:
| **stream.place** | Video segments | Stream info, chat | Embedded "static PDS" |
| **ATCR** | Container blobs | Manifests, comments, builds | Embedded PDS in hold service |
**Common problem:** Large binary data can't realistically live in user PDSs, but application metadata needs a federated home.
**Common problem:** Large binary data can't realistically live in user PDSs, but application metadata needs a distributed home.
**ATCR's approach:** Each hold service is a full ATProto actor with its own embedded PDS for **shared data** (captain + crew records, not user-specific data). This PDS stores access control and metadata about the hold itself.
Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 288 KiB

+21
View File
@@ -0,0 +1,21 @@
{
"name": "MyWebSite",
"short_name": "MySite",
"icons": [
{
"src": "/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

@@ -0,0 +1,20 @@
{{ define "head" }}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicons -->
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<!-- Stylesheets -->
<link rel="stylesheet" href="/css/style.css">
<!-- HTMX -->
<script src="https://unpkg.com/htmx.org@2.0.8/dist/htmx.min.js"></script>
<!-- App Scripts -->
<script src="/js/app.js"></script>
{{ end }}
-24
View File
@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ block "title" . }}ATCR - Federated Container Registry{{ end }}</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
{{ block "head" . }}{{ end }}
</head>
<body>
{{ template "nav" . }}
<main class="container">
{{ block "content" . }}{{ end }}
</main>
<!-- Modal container for HTMX -->
<div id="modal"></div>
<script src="/static/js/app.js"></script>
{{ block "scripts" . }}{{ end }}
</body>
</html>
+2 -7
View File
@@ -2,11 +2,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ATCR - Federated Container Registry</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<title>ATCR - Distributed Container Registry</title>
{{ template "head" . }}
</head>
<body>
{{ template "nav" . }}
@@ -86,8 +83,6 @@
<!-- Modal container for HTMX -->
<div id="modal"></div>
<script src="/static/js/app.js"></script>
</body>
</html>
{{ end }}
+1 -5
View File
@@ -2,11 +2,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Install ATCR Credential Helper - ATCR</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
{{ template "head" . }}
</head>
<body>
{{ template "nav" . }}
@@ -139,7 +136,6 @@ export PATH="/usr/local/bin:$PATH"</code></div>
</script>
<div id="modal"></div>
<script src="/static/js/app.js"></script>
</body>
</html>
{{ end }}
+1 -3
View File
@@ -2,10 +2,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - ATCR</title>
<link rel="stylesheet" href="/static/css/style.css">
{{ template "head" . }}
</head>
<body>
<nav class="navbar">
+1 -5
View File
@@ -2,12 +2,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ if .Repository.Title }}{{ .Repository.Title }}{{ else }}{{ .Owner.Handle }}/{{ .Repository.Name }}{{ end }} - ATCR</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<script src="/static/js/app.js"></script>
{{ template "head" . }}
</head>
<body>
{{ template "nav" . }}
+2 -7
View File
@@ -2,11 +2,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Search: {{ .SearchQuery }} - ATCR</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<title>Search{{ if .SearchQuery }}: {{ .SearchQuery }}{{ end }} - ATCR</title>
{{ template "head" . }}
</head>
<body>
{{ template "nav" . }}
@@ -31,8 +28,6 @@
<!-- Modal container for HTMX -->
<div id="modal"></div>
<script src="/static/js/app.js"></script>
</body>
</html>
{{ end }}
+2 -5
View File
@@ -2,11 +2,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Settings - ATCR</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
{{ template "head" . }}
</head>
<body>
{{ template "nav" . }}
@@ -113,7 +110,7 @@
</main>
<script src="/static/js/app.js"></script>
<script src="/js/app.js"></script>
<script>
// Default Hold Update - Dynamic display update
+1 -5
View File
@@ -2,12 +2,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ .ViewedUser.Handle }} - ATCR</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<script src="/static/js/app.js"></script>
{{ template "head" . }}
</head>
<body>
{{ template "nav" . }}
+26
View File
@@ -109,3 +109,29 @@ func StaticHandler() http.Handler {
}
return http.FileServer(http.FS(sub))
}
// StaticRootFiles returns list of root-level files in static directory (not subdirectories)
func StaticRootFiles() ([]string, error) {
entries, err := staticFS.ReadDir("static")
if err != nil {
return nil, err
}
var files []string
for _, entry := range entries {
// Only include files, not directories
if !entry.IsDir() {
files = append(files, entry.Name())
}
}
return files, nil
}
// StaticSubdir returns an fs.FS for a subdirectory within static/
func StaticSubdir(name string) http.Handler {
sub, err := fs.Sub(staticFS, "static/"+name)
if err != nil {
panic(err)
}
return http.FileServer(http.FS(sub))
}
+1 -1
View File
@@ -510,7 +510,7 @@ func (t *CaptainRecord) UnmarshalCBOR(r io.Reader) (err error) {
n := extra
nameBuf := make([]byte, 19)
nameBuf := make([]byte, 18)
for i := uint64(0); i < n; i++ {
nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
if err != nil {