A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go

update search results page

evan.jarrett.net 2d7d2fd5 c48a7635

verified
+71 -46
+1
.tangled/workflows/lint.yaml
··· 15 15 command: | 16 16 go mod download 17 17 go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.7.2 18 + go generate ./... 18 19 19 20 - name: Run Linter 20 21 environment:
+1
.tangled/workflows/tests.yml
··· 14 14 CGO_ENABLED: 1 15 15 command: | 16 16 go mod download 17 + go generate ./... 17 18 18 19 - name: Run Tests 19 20 environment:
+51
pkg/appview/templates/components/card-grid.html
··· 1 + {{ define "card-grid" }} 2 + {{/* 3 + Card grid component - displays repository cards in a responsive grid 4 + 5 + Required: 6 + - .Repositories: []db.RepoCardData - list of repositories to display 7 + 8 + Optional: 9 + - .Columns: int - number of columns on xl screens (3 or 4, default 3) 10 + - .EmptyMessage: string - message when no repositories (default: "No repositories found.") 11 + - .EmptyIcon: string - lucide icon name for empty state 12 + - .EmptySubtext: string - secondary text for empty state 13 + - .LoadMoreURL: string - URL for Load More button (HTMX) 14 + - .TargetID: string - HTMX target ID for Load More 15 + - .HasMore: bool - whether to show Load More button 16 + */}} 17 + {{ if .Repositories }} 18 + <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"> 19 + {{ range .Repositories }} 20 + {{ template "repo-card" . }} 21 + {{ end }} 22 + </div> 23 + {{ if and .HasMore .LoadMoreURL }} 24 + <div class="mt-6 text-center"> 25 + <button 26 + class="btn btn-outline" 27 + hx-get="{{ .LoadMoreURL }}" 28 + hx-trigger="click" 29 + hx-target="#{{ .TargetID }}" 30 + hx-swap="beforeend" 31 + > 32 + Load More 33 + </button> 34 + </div> 35 + {{ end }} 36 + {{ else }} 37 + <div class="py-12 text-center"> 38 + {{ if .EmptyIcon }} 39 + <div class="text-base-content/60 mb-4"> 40 + <i data-lucide="{{ .EmptyIcon }}" class="size-12 mx-auto mb-4"></i> 41 + <p class="text-lg">{{ or .EmptyMessage "No repositories found." }}</p> 42 + </div> 43 + {{ if .EmptySubtext }} 44 + <p class="text-base-content/40 text-sm">{{ .EmptySubtext }}</p> 45 + {{ end }} 46 + {{ else }} 47 + <p class="text-base-content/60">{{ or .EmptyMessage "No repositories found." }}</p> 48 + {{ end }} 49 + </div> 50 + {{ end }} 51 + {{ end }}
+1 -5
pkg/appview/templates/pages/home.html
··· 56 56 {{ if .RecentRepos }} 57 57 <section> 58 58 <h2 class="text-2xl font-bold mb-6">What's New</h2> 59 - <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> 60 - {{ range .RecentRepos }} 61 - {{ template "repo-card" . }} 62 - {{ end }} 63 - </div> 59 + {{ template "card-grid" (dict "Repositories" .RecentRepos) }} 64 60 </section> 65 61 {{ end }} 66 62 </div>
+5 -3
pkg/appview/templates/pages/privacy.html
··· 89 89 <p>If you are located in the European Economic Area (EEA), you have the following rights:</p> 90 90 91 91 <h3 class="text-lg font-medium mt-4">Right to Access</h3> 92 - <p>You may request a copy of all personal data we store about you. This includes:</p> 92 + <p>You may export a copy of all personal data we store about you via the "Export Data" button in your account settings. This export includes:</p> 93 93 <ul class="list-disc list-inside space-y-1 ml-4"> 94 94 <li>Layer records associated with your DID on our PDS</li> 95 - <li>Server logs containing your handle, DID, IP address, or actions (if retained)</li> 96 95 <li>OAuth tokens, web UI sessions, and device tokens</li> 97 96 <li>Cached PDS data</li> 98 97 <li>List of registered devices (credential helper)</li> 99 98 </ul> 99 + <p class="mt-2">For data not included in the self-service export (such as server logs), contact us and we will respond within 30 days.</p> 100 100 <p class="text-base-content/70 text-sm mt-2">Note: Data stored on your own PDS is already under your control and accessible to you directly.</p> 101 101 102 102 <h3 class="text-lg font-medium mt-6">Right to Erasure ("Right to be Forgotten")</h3> ··· 156 156 <p>If you are a California resident, you have the following rights under the California Consumer Privacy Act:</p> 157 157 158 158 <h3 class="text-lg font-medium mt-4">Right to Know</h3> 159 - <p>You may request disclosure of:</p> 159 + <p>You may export a copy of your personal data via the "Export Data" button in your account settings. You may also request disclosure of:</p> 160 160 <ul class="list-disc list-inside space-y-1 ml-4"> 161 161 <li>The categories of personal information we collect</li> 162 162 <li>The purposes for which we use your personal information</li> 163 163 <li>The categories of third parties with whom we share your personal information</li> 164 164 </ul> 165 + <p class="mt-2">For data not included in the self-service export (such as server logs), contact us and we will respond within 30 days.</p> 165 166 166 167 <h3 class="text-lg font-medium mt-6">Right to Delete</h3> 167 168 <p>You may delete your personal information via the account settings page, subject to the same technical limitations described in the GDPR section above. For data not accessible through self-service, we will respond to requests within 45 days, except where retention is necessary for:</p> ··· 305 306 <h3 class="text-lg font-medium mt-4">Self-Service (via Settings)</h3> 306 307 <p>Most data management can be done directly through your account settings at {{ .RegistryURL }}:</p> 307 308 <ul class="list-disc list-inside space-y-1 ml-4"> 309 + <li><strong>Export your data:</strong> Use the "Export Data" button in settings to download a copy of all personal data we store about you.</li> 308 310 <li><strong>Delete your data:</strong> Use the "Delete Account" button in settings. This will remove your layer records, cached data, and authentication tokens. You may also choose to have us delete <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> records from your PDS (requires active OAuth session).</li> 309 311 <li><strong>Revoke device tokens:</strong> Manage and revoke credential helper devices in settings.</li> 310 312 <li><strong>Update your data:</strong> Corrections happen through normal use of the service.</li>
+2 -8
pkg/appview/templates/pages/user.html
··· 53 53 <div class="text-center text-base-content/60 py-12"> 54 54 <p>This user hasn't set up their ATCR profile yet.</p> 55 55 </div> 56 - {{ else if .Repositories }} 57 - <div class="w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> 58 - {{ range .Repositories }} 59 - {{ template "repo-card" . }} 60 - {{ end }} 61 - </div> 62 56 {{ else }} 63 - <div class="text-center text-base-content/60 py-12"> 64 - <p>No images yet.</p> 57 + <div class="w-full"> 58 + {{ template "card-grid" (dict "Repositories" .Repositories "EmptyMessage" "No images yet.") }} 65 59 </div> 66 60 {{ end }} 67 61 </div>
+9 -29
pkg/appview/templates/partials/search-results.html
··· 1 1 {{/* Search results partial - renders repository cards in a grid */}} 2 - {{ if gt (len .Repositories) 0 }} 3 - <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> 4 - {{ range .Repositories }} 5 - {{ template "repo-card" . }} 6 - {{ end }} 7 - </div> 8 - 9 - {{ if .HasMore }} 10 - <div class="mt-6 text-center"> 11 - <button 12 - class="btn btn-outline" 13 - hx-get="/api/search-results?q={{ .SearchQuery }}&offset={{ .NextOffset }}" 14 - hx-trigger="click" 15 - hx-target="#search-results" 16 - hx-swap="beforeend" 17 - > 18 - Load More 19 - </button> 20 - </div> 21 - {{ end }} 22 - {{ else }} 23 - <div class="py-12 text-center"> 24 - <div class="text-base-content/60 mb-4"> 25 - <i data-lucide="search-x" class="size-12 mx-auto mb-4"></i> 26 - <p class="text-lg">No repositories found matching your search.</p> 27 - </div> 28 - <p class="text-base-content/40 text-sm">Try a different search term or browse the homepage.</p> 29 - </div> 30 - {{ end }} 2 + {{ template "card-grid" (dict 3 + "Repositories" .Repositories 4 + "EmptyIcon" "search-x" 5 + "EmptyMessage" "No repositories found matching your search." 6 + "EmptySubtext" "Try a different search term or browse the homepage." 7 + "LoadMoreURL" (printf "/api/search-results?q=%s&offset=%d" .SearchQuery .NextOffset) 8 + "TargetID" "search-results" 9 + "HasMore" .HasMore 10 + ) }}
+1 -1
pkg/appview/ui.go
··· 12 12 "atcr.io/pkg/appview/licenses" 13 13 ) 14 14 15 - //go:generate sh -c "cd ../.. && npm run build" 15 + //go:generate sh -c "command -v npm >/dev/null 2>&1 && cd ../.. && npm run build || echo 'npm not found, skipping build'" 16 16 17 17 //go:embed templates/**/*.html 18 18 var templatesFS embed.FS