Extract pagination templates and align detail card.Unverified
ffa0890 parent: 078b9a3 modified
docs/specs.md +8 -5 | @@ -303,10 +303,10 @@ A background scheduler polls subscribed feeds on a fixed 5-minute tick. Feeds ar | ||
| 303 | 303 | │ |
| 304 | 304 | ┌──────────────┼──────────────┐ |
| 305 | 305 | │ │ │ |
| 306 | - ┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐ | |
| 307 | - │RSS/XML │ │Atom/XML │ │JSON Feed │ | |
| 308 | - │Parser │ │Parser │ │Parser │ | |
| 309 | - └─────────┘ └───────────┘ └───────────┘ | |
| 306 | + ┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ | |
| 307 | + │RSS/XML │ │RSS1/RDF │ │Atom/XML │ │JSON Feed │ | |
| 308 | + │Parser │ │Parser │ │Parser │ │Parser │ | |
| 309 | + └─────────┘ └───────────┘ └───────────┘ └───────────┘ | |
| 310 | 310 | ``` |
| 311 | 311 | |
| 312 | 312 | ### 4.2 Fetch Schedule |
| @@ -799,6 +799,7 @@ glean/ | ||
| 799 | 799 | │ ├── atproto/ |
| 800 | 800 | │ │ ├── auth.go # DID resolution, OAuth flow |
| 801 | 801 | │ │ ├── client.go # XRPC client (write to user PDS) |
| 802 | +│ │ ├── collectiondir.go # Collection directory backfill (startup) | |
| 802 | 803 | │ │ ├── jetstream.go # Subscribe to Jetstream via official client |
| 803 | 804 | │ │ ├── stream_handler.go # Stream event → DB handler |
| 804 | 805 | │ │ ├── lexicon.go # Lexicon record types (at.glean.*, maintained by hand) |
| @@ -817,7 +818,7 @@ glean/ | ||
| 817 | 818 | │ │ ├── oauth_store.go # OAuth session storage |
| 818 | 819 | │ │ └── store.go # FeedStore adapter for scheduler |
| 819 | 820 | │ ├── feed/ |
| 820 | -│ │ ├── parser.go # RSS/Atom/JSON feed parser | |
| 821 | +│ │ ├── parser.go # RSS/Atom/RDF/JSON feed parser | |
| 821 | 822 | │ │ ├── fetcher.go # Scheduler with dedup + Fetcher |
| 822 | 823 | │ │ ├── discover.go # Feed auto-discovery from URLs |
| 823 | 824 | │ │ └── opml.go # OPML import/export |
| @@ -847,6 +848,7 @@ glean/ | ||
| 847 | 848 | │ └── tmpl/ |
| 848 | 849 | │ ├── base.html # Base template with htmx + Tailwind |
| 849 | 850 | │ ├── index.html # Landing page |
| 851 | +│ ├── login.html # Login page | |
| 850 | 852 | │ ├── dashboard.html # Dashboard |
| 851 | 853 | │ ├── feeds.html # Feed management |
| 852 | 854 | │ ├── articles.html # Article listing |
| @@ -854,6 +856,7 @@ glean/ | ||
| 854 | 856 | │ ├── trending.html # Trending articles |
| 855 | 857 | │ ├── library.html # Liked articles + annotations |
| 856 | 858 | │ ├── profile.html # User profile |
| 859 | +│ ├── 404.html # Not found page | |
| 857 | 860 | │ └── partials/ # Reusable template fragments |
| 858 | 861 | ├── static/ |
| 859 | 862 | │ ├── input.css # Tailwind input |
| @@ -303,10 +303,10 @@ A background scheduler polls subscribed feeds on a fixed 5-minute tick. Feeds ar | |||
| 303 | │ | 303 | │ |
| 304 | ┌──────────────┼──────────────┐ | 304 | ┌──────────────┼──────────────┐ |
| 305 | │ │ │ | 305 | │ │ │ |
| 306 | - ┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐ | 306 | + ┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ |
| 307 | - │RSS/XML │ │Atom/XML │ │JSON Feed │ | 307 | + │RSS/XML │ │RSS1/RDF │ │Atom/XML │ │JSON Feed │ |
| 308 | - │Parser │ │Parser │ │Parser │ | 308 | + │Parser │ │Parser │ │Parser │ │Parser │ |
| 309 | - └─────────┘ └───────────┘ └───────────┘ | 309 | + └─────────┘ └───────────┘ └───────────┘ └───────────┘ |
| 310 | ``` | 310 | ``` |
| 311 | 311 | ||
| 312 | ### 4.2 Fetch Schedule | 312 | ### 4.2 Fetch Schedule |
| @@ -799,6 +799,7 @@ glean/ | |||
| 799 | │ ├── atproto/ | 799 | │ ├── atproto/ |
| 800 | │ │ ├── auth.go # DID resolution, OAuth flow | 800 | │ │ ├── auth.go # DID resolution, OAuth flow |
| 801 | │ │ ├── client.go # XRPC client (write to user PDS) | 801 | │ │ ├── client.go # XRPC client (write to user PDS) |
| 802 | +│ │ ├── collectiondir.go # Collection directory backfill (startup) | ||
| 802 | │ │ ├── jetstream.go # Subscribe to Jetstream via official client | 803 | │ │ ├── jetstream.go # Subscribe to Jetstream via official client |
| 803 | │ │ ├── stream_handler.go # Stream event → DB handler | 804 | │ │ ├── stream_handler.go # Stream event → DB handler |
| 804 | │ │ ├── lexicon.go # Lexicon record types (at.glean.*, maintained by hand) | 805 | │ │ ├── lexicon.go # Lexicon record types (at.glean.*, maintained by hand) |
| @@ -817,7 +818,7 @@ glean/ | |||
| 817 | │ │ ├── oauth_store.go # OAuth session storage | 818 | │ │ ├── oauth_store.go # OAuth session storage |
| 818 | │ │ └── store.go # FeedStore adapter for scheduler | 819 | │ │ └── store.go # FeedStore adapter for scheduler |
| 819 | │ ├── feed/ | 820 | │ ├── feed/ |
| 820 | -│ │ ├── parser.go # RSS/Atom/JSON feed parser | 821 | +│ │ ├── parser.go # RSS/Atom/RDF/JSON feed parser |
| 821 | │ │ ├── fetcher.go # Scheduler with dedup + Fetcher | 822 | │ │ ├── fetcher.go # Scheduler with dedup + Fetcher |
| 822 | │ │ ├── discover.go # Feed auto-discovery from URLs | 823 | │ │ ├── discover.go # Feed auto-discovery from URLs |
| 823 | │ │ └── opml.go # OPML import/export | 824 | │ │ └── opml.go # OPML import/export |
| @@ -847,6 +848,7 @@ glean/ | |||
| 847 | │ └── tmpl/ | 848 | │ └── tmpl/ |
| 848 | │ ├── base.html # Base template with htmx + Tailwind | 849 | │ ├── base.html # Base template with htmx + Tailwind |
| 849 | │ ├── index.html # Landing page | 850 | │ ├── index.html # Landing page |
| 851 | +│ ├── login.html # Login page | ||
| 850 | │ ├── dashboard.html # Dashboard | 852 | │ ├── dashboard.html # Dashboard |
| 851 | │ ├── feeds.html # Feed management | 853 | │ ├── feeds.html # Feed management |
| 852 | │ ├── articles.html # Article listing | 854 | │ ├── articles.html # Article listing |
| @@ -854,6 +856,7 @@ glean/ | |||
| 854 | │ ├── trending.html # Trending articles | 856 | │ ├── trending.html # Trending articles |
| 855 | │ ├── library.html # Liked articles + annotations | 857 | │ ├── library.html # Liked articles + annotations |
| 856 | │ ├── profile.html # User profile | 858 | │ ├── profile.html # User profile |
| 859 | +│ ├── 404.html # Not found page | ||
| 857 | │ └── partials/ # Reusable template fragments | 860 | │ └── partials/ # Reusable template fragments |
| 858 | ├── static/ | 861 | ├── static/ |
| 859 | │ ├── input.css # Tailwind input | 862 | │ ├── input.css # Tailwind input |
modified
internal/tmpl/article_detail.html +2 -2 | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | {{if .Article.Published.Valid}}<span>{{.Article.Published.Time.Format "Jan 2, 2006 15:04"}}</span>{{end}} |
| 17 | 17 | {{if .Feed}} |
| 18 | 18 | <a href="/articles?feed={{.Feed.FeedURL}}" class="inline-flex items-center gap-1 hover:text-spot-green transition"> |
| 19 | - {{if .Feed.FaviconURL.Valid}}<img src="{{.Feed.FaviconURL.String}}" class="w-4 h-4 rounded-sm">{{end}} | |
| 19 | + {{if .Feed.FaviconURL.Valid}}<img src="{{.Feed.FaviconURL.String}}" class="w-4 h-4 rounded">{{end}} | |
| 20 | 20 | {{if .Feed.Title.Valid}}{{.Feed.Title.String}}{{else}}{{.Feed.FeedURL}}{{end}} |
| 21 | 21 | </a> |
| 22 | 22 | {{end}} |
| @@ -109,7 +109,7 @@ | ||
| 109 | 109 | <input type="hidden" name="quote" id="quote-input"> |
| 110 | 110 | <div> |
| 111 | 111 | <textarea name="note" rows="2" placeholder="Add a note..." |
| 112 | - class="w-full bg-spot-hover text-spot-text rounded-lg px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-spot-green placeholder:text-spot-placeholder resize-none"></textarea> | |
| 112 | + class="w-full bg-spot-hover text-spot-text rounded-pill px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-spot-green placeholder:text-spot-placeholder resize-none"></textarea> | |
| 113 | 113 | </div> |
| 114 | 114 | <div class="flex gap-2"> |
| 115 | 115 | <input type="text" name="tags" placeholder="Tags (comma separated)" |
| @@ -16,7 +16,7 @@ | |||
| 16 | {{if .Article.Published.Valid}}<span>{{.Article.Published.Time.Format "Jan 2, 2006 15:04"}}</span>{{end}} | 16 | {{if .Article.Published.Valid}}<span>{{.Article.Published.Time.Format "Jan 2, 2006 15:04"}}</span>{{end}} |
| 17 | {{if .Feed}} | 17 | {{if .Feed}} |
| 18 | <a href="/articles?feed={{.Feed.FeedURL}}" class="inline-flex items-center gap-1 hover:text-spot-green transition"> | 18 | <a href="/articles?feed={{.Feed.FeedURL}}" class="inline-flex items-center gap-1 hover:text-spot-green transition"> |
| 19 | - {{if .Feed.FaviconURL.Valid}}<img src="{{.Feed.FaviconURL.String}}" class="w-4 h-4 rounded-sm">{{end}} | 19 | + {{if .Feed.FaviconURL.Valid}}<img src="{{.Feed.FaviconURL.String}}" class="w-4 h-4 rounded">{{end}} |
| 20 | {{if .Feed.Title.Valid}}{{.Feed.Title.String}}{{else}}{{.Feed.FeedURL}}{{end}} | 20 | {{if .Feed.Title.Valid}}{{.Feed.Title.String}}{{else}}{{.Feed.FeedURL}}{{end}} |
| 21 | </a> | 21 | </a> |
| 22 | {{end}} | 22 | {{end}} |
| @@ -109,7 +109,7 @@ | |||
| 109 | <input type="hidden" name="quote" id="quote-input"> | 109 | <input type="hidden" name="quote" id="quote-input"> |
| 110 | <div> | 110 | <div> |
| 111 | <textarea name="note" rows="2" placeholder="Add a note..." | 111 | <textarea name="note" rows="2" placeholder="Add a note..." |
| 112 | - class="w-full bg-spot-hover text-spot-text rounded-lg px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-spot-green placeholder:text-spot-placeholder resize-none"></textarea> | 112 | + class="w-full bg-spot-hover text-spot-text rounded-pill px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-spot-green placeholder:text-spot-placeholder resize-none"></textarea> |
| 113 | </div> | 113 | </div> |
| 114 | <div class="flex gap-2"> | 114 | <div class="flex gap-2"> |
| 115 | <input type="text" name="tags" placeholder="Tags (comma separated)" | 115 | <input type="text" name="tags" placeholder="Tags (comma separated)" |
modified
internal/tmpl/articles.html +1 -17 | @@ -70,23 +70,7 @@ | ||
| 70 | 70 | {{template "articles-content.html" .}} |
| 71 | 71 | </div> |
| 72 | 72 | |
| 73 | -{{if or .Page.HasPrev .Page.HasNext}} | |
| 74 | -<div class="flex items-center justify-center gap-3 py-6"> | |
| 75 | - {{if .Page.HasPrev}} | |
| 76 | - <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | |
| 77 | - {{else}} | |
| 78 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | |
| 79 | - {{end}} | |
| 80 | - | |
| 81 | - <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span> | |
| 82 | - | |
| 83 | - {{if .Page.HasNext}} | |
| 84 | - <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | |
| 85 | - {{else}} | |
| 86 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | |
| 87 | - {{end}} | |
| 88 | -</div> | |
| 89 | -{{end}} | |
| 73 | +{{template "pagination.html" (dict "HasPrev" .Page.HasPrev "HasNext" .Page.HasNext "PrevPage" .Page.PrevPage "NextPage" .Page.NextPage "Page" .Page.Page "BaseURL" .BaseURL "QueryParams" .QueryParams)}} | |
| 90 | 74 | |
| 91 | 75 | <script> |
| 92 | 76 | (function() { |
| @@ -70,23 +70,7 @@ | |||
| 70 | {{template "articles-content.html" .}} | 70 | {{template "articles-content.html" .}} |
| 71 | </div> | 71 | </div> |
| 72 | 72 | ||
| 73 | -{{if or .Page.HasPrev .Page.HasNext}} | 73 | +{{template "pagination.html" (dict "HasPrev" .Page.HasPrev "HasNext" .Page.HasNext "PrevPage" .Page.PrevPage "NextPage" .Page.NextPage "Page" .Page.Page "BaseURL" .BaseURL "QueryParams" .QueryParams)}} |
| 74 | -<div class="flex items-center justify-center gap-3 py-6"> | ||
| 75 | - {{if .Page.HasPrev}} | ||
| 76 | - <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | ||
| 77 | - {{else}} | ||
| 78 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | ||
| 79 | - {{end}} | ||
| 80 | - | ||
| 81 | - <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span> | ||
| 82 | - | ||
| 83 | - {{if .Page.HasNext}} | ||
| 84 | - <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | ||
| 85 | - {{else}} | ||
| 86 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | ||
| 87 | - {{end}} | ||
| 88 | -</div> | ||
| 89 | -{{end}} | ||
| 90 | 74 | ||
| 91 | <script> | 75 | <script> |
| 92 | (function() { | 76 | (function() { |
modified
internal/tmpl/dashboard.html +2 -25 | @@ -97,23 +97,7 @@ | ||
| 97 | 97 | {{end}} |
| 98 | 98 | </div> |
| 99 | 99 | |
| 100 | - {{if or .Page.HasPrev .Page.HasNext}} | |
| 101 | - <div class="flex items-center justify-center gap-3 py-6"> | |
| 102 | - {{if .Page.HasPrev}} | |
| 103 | - <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | |
| 104 | - {{else}} | |
| 105 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | |
| 106 | - {{end}} | |
| 107 | - | |
| 108 | - <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span> | |
| 109 | - | |
| 110 | - {{if .Page.HasNext}} | |
| 111 | - <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | |
| 112 | - {{else}} | |
| 113 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | |
| 114 | - {{end}} | |
| 115 | - </div> | |
| 116 | - {{end}} | |
| 100 | + {{template "pagination.html" (dict "HasPrev" .Page.HasPrev "HasNext" .Page.HasNext "PrevPage" .Page.PrevPage "NextPage" .Page.NextPage "Page" .Page.Page "BaseURL" .BaseURL "QueryParams" .QueryParams)}} | |
| 117 | 101 | </div> |
| 118 | 102 | |
| 119 | 103 | <div class="space-y-8"> |
| @@ -136,14 +120,7 @@ | ||
| 136 | 120 | <h2 class="text-lg font-semibold text-spot-text mb-4">Similar readers</h2> |
| 137 | 121 | <div class="space-y-3"> |
| 138 | 122 | {{range .PeopleRecommendations}} |
| 139 | - <div class="bg-spot-surface rounded-xl p-4 flex items-center gap-3 hover:bg-spot-hover-50 transition"> | |
| 140 | - {{if .AvatarURL}}<img src="{{.AvatarURL}}" class="w-10 h-10 rounded-full">{{end}} | |
| 141 | - <div class="min-w-0 flex-1"> | |
| 142 | - <a href="/profile/{{.Handle}}" class="font-bold text-spot-text hover:text-spot-green transition">@{{.Handle}}</a> | |
| 143 | - {{if .DisplayName}}<div class="text-sm text-spot-secondary">{{.DisplayName}}</div>{{end}} | |
| 144 | - </div> | |
| 145 | - <span class="text-xs text-spot-secondary">{{.CommonFeeds}} shared</span> | |
| 146 | - </div> | |
| 123 | + {{template "profile-card.html" .}} | |
| 147 | 124 | {{end}} |
| 148 | 125 | </div> |
| 149 | 126 | </div> |
| @@ -97,23 +97,7 @@ | |||
| 97 | {{end}} | 97 | {{end}} |
| 98 | </div> | 98 | </div> |
| 99 | 99 | ||
| 100 | - {{if or .Page.HasPrev .Page.HasNext}} | 100 | + {{template "pagination.html" (dict "HasPrev" .Page.HasPrev "HasNext" .Page.HasNext "PrevPage" .Page.PrevPage "NextPage" .Page.NextPage "Page" .Page.Page "BaseURL" .BaseURL "QueryParams" .QueryParams)}} |
| 101 | - <div class="flex items-center justify-center gap-3 py-6"> | ||
| 102 | - {{if .Page.HasPrev}} | ||
| 103 | - <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | ||
| 104 | - {{else}} | ||
| 105 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | ||
| 106 | - {{end}} | ||
| 107 | - | ||
| 108 | - <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span> | ||
| 109 | - | ||
| 110 | - {{if .Page.HasNext}} | ||
| 111 | - <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | ||
| 112 | - {{else}} | ||
| 113 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | ||
| 114 | - {{end}} | ||
| 115 | - </div> | ||
| 116 | - {{end}} | ||
| 117 | </div> | 101 | </div> |
| 118 | 102 | ||
| 119 | <div class="space-y-8"> | 103 | <div class="space-y-8"> |
| @@ -136,14 +120,7 @@ | |||
| 136 | <h2 class="text-lg font-semibold text-spot-text mb-4">Similar readers</h2> | 120 | <h2 class="text-lg font-semibold text-spot-text mb-4">Similar readers</h2> |
| 137 | <div class="space-y-3"> | 121 | <div class="space-y-3"> |
| 138 | {{range .PeopleRecommendations}} | 122 | {{range .PeopleRecommendations}} |
| 139 | - <div class="bg-spot-surface rounded-xl p-4 flex items-center gap-3 hover:bg-spot-hover-50 transition"> | 123 | + {{template "profile-card.html" .}} |
| 140 | - {{if .AvatarURL}}<img src="{{.AvatarURL}}" class="w-10 h-10 rounded-full">{{end}} | ||
| 141 | - <div class="min-w-0 flex-1"> | ||
| 142 | - <a href="/profile/{{.Handle}}" class="font-bold text-spot-text hover:text-spot-green transition">@{{.Handle}}</a> | ||
| 143 | - {{if .DisplayName}}<div class="text-sm text-spot-secondary">{{.DisplayName}}</div>{{end}} | ||
| 144 | - </div> | ||
| 145 | - <span class="text-xs text-spot-secondary">{{.CommonFeeds}} shared</span> | ||
| 146 | - </div> | ||
| 147 | {{end}} | 124 | {{end}} |
| 148 | </div> | 125 | </div> |
| 149 | </div> | 126 | </div> |
modified
internal/tmpl/feeds.html +2 -25 | @@ -93,23 +93,7 @@ | ||
| 93 | 93 | {{end}} |
| 94 | 94 | </div> |
| 95 | 95 | |
| 96 | - {{if or .Page.HasPrev .Page.HasNext}} | |
| 97 | - <div class="flex items-center justify-center gap-3 py-6"> | |
| 98 | - {{if .Page.HasPrev}} | |
| 99 | - <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | |
| 100 | - {{else}} | |
| 101 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | |
| 102 | - {{end}} | |
| 103 | - | |
| 104 | - <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span> | |
| 105 | - | |
| 106 | - {{if .Page.HasNext}} | |
| 107 | - <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | |
| 108 | - {{else}} | |
| 109 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | |
| 110 | - {{end}} | |
| 111 | - </div> | |
| 112 | - {{end}} | |
| 96 | + {{template "pagination.html" (dict "HasPrev" .Page.HasPrev "HasNext" .Page.HasNext "PrevPage" .Page.PrevPage "NextPage" .Page.NextPage "Page" .Page.Page "BaseURL" .BaseURL "QueryParams" .QueryParams)}} | |
| 113 | 97 | </div> |
| 114 | 98 | |
| 115 | 99 | <div class="space-y-6"> |
| @@ -140,14 +124,7 @@ | ||
| 140 | 124 | <h2 class="text-sm font-bold text-spot-text uppercase tracking-wide mb-4">Similar readers</h2> |
| 141 | 125 | <div class="space-y-3"> |
| 142 | 126 | {{range .PeopleRecommendations}} |
| 143 | - <div class="bg-spot-surface rounded-xl p-4 flex items-center gap-3 hover:bg-spot-hover-50 transition"> | |
| 144 | - {{if .AvatarURL}}<img src="{{.AvatarURL}}" class="w-10 h-10 rounded-full">{{end}} | |
| 145 | - <div class="min-w-0 flex-1"> | |
| 146 | - <a href="/profile/{{.Handle}}" class="font-bold text-spot-text hover:text-spot-green transition">@{{.Handle}}</a> | |
| 147 | - {{if .DisplayName}}<div class="text-sm text-spot-secondary">{{.DisplayName}}</div>{{end}} | |
| 148 | - </div> | |
| 149 | - <span class="text-xs text-spot-secondary">{{.CommonFeeds}} shared</span> | |
| 150 | - </div> | |
| 127 | + {{template "profile-card.html" .}} | |
| 151 | 128 | {{end}} |
| 152 | 129 | </div> |
| 153 | 130 | </div> |
| @@ -93,23 +93,7 @@ | |||
| 93 | {{end}} | 93 | {{end}} |
| 94 | </div> | 94 | </div> |
| 95 | 95 | ||
| 96 | - {{if or .Page.HasPrev .Page.HasNext}} | 96 | + {{template "pagination.html" (dict "HasPrev" .Page.HasPrev "HasNext" .Page.HasNext "PrevPage" .Page.PrevPage "NextPage" .Page.NextPage "Page" .Page.Page "BaseURL" .BaseURL "QueryParams" .QueryParams)}} |
| 97 | - <div class="flex items-center justify-center gap-3 py-6"> | ||
| 98 | - {{if .Page.HasPrev}} | ||
| 99 | - <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | ||
| 100 | - {{else}} | ||
| 101 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | ||
| 102 | - {{end}} | ||
| 103 | - | ||
| 104 | - <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span> | ||
| 105 | - | ||
| 106 | - {{if .Page.HasNext}} | ||
| 107 | - <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | ||
| 108 | - {{else}} | ||
| 109 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | ||
| 110 | - {{end}} | ||
| 111 | - </div> | ||
| 112 | - {{end}} | ||
| 113 | </div> | 97 | </div> |
| 114 | 98 | ||
| 115 | <div class="space-y-6"> | 99 | <div class="space-y-6"> |
| @@ -140,14 +124,7 @@ | |||
| 140 | <h2 class="text-sm font-bold text-spot-text uppercase tracking-wide mb-4">Similar readers</h2> | 124 | <h2 class="text-sm font-bold text-spot-text uppercase tracking-wide mb-4">Similar readers</h2> |
| 141 | <div class="space-y-3"> | 125 | <div class="space-y-3"> |
| 142 | {{range .PeopleRecommendations}} | 126 | {{range .PeopleRecommendations}} |
| 143 | - <div class="bg-spot-surface rounded-xl p-4 flex items-center gap-3 hover:bg-spot-hover-50 transition"> | 127 | + {{template "profile-card.html" .}} |
| 144 | - {{if .AvatarURL}}<img src="{{.AvatarURL}}" class="w-10 h-10 rounded-full">{{end}} | ||
| 145 | - <div class="min-w-0 flex-1"> | ||
| 146 | - <a href="/profile/{{.Handle}}" class="font-bold text-spot-text hover:text-spot-green transition">@{{.Handle}}</a> | ||
| 147 | - {{if .DisplayName}}<div class="text-sm text-spot-secondary">{{.DisplayName}}</div>{{end}} | ||
| 148 | - </div> | ||
| 149 | - <span class="text-xs text-spot-secondary">{{.CommonFeeds}} shared</span> | ||
| 150 | - </div> | ||
| 151 | {{end}} | 128 | {{end}} |
| 152 | </div> | 129 | </div> |
| 153 | </div> | 130 | </div> |
deleted
internal/tmpl/partials/article-list.html +0 -22 | deleted file mode 100644 | ||
| @@ -1,22 +0,0 @@ | ||
| 1 | -{{define "article-list.html"}} | |
| 2 | -{{range .Articles}} | |
| 3 | -<article class="bg-spot-surface rounded-xl px-5 py-4 hover:bg-spot-hover-50 transition shadow-spot"> | |
| 4 | - <div class="flex items-start justify-between gap-4"> | |
| 5 | - <div class="min-w-0 flex-1"> | |
| 6 | - <a href="/articles/{{.ID}}" class="font-bold text-spot-text hover:text-spot-green transition text-lg leading-tight">{{.Title}}</a> | |
| 7 | - <div class="text-sm text-spot-secondary mt-1 flex items-center gap-2"> | |
| 8 | - {{if .FeedFaviconURL.Valid}}<img src="{{.FeedFaviconURL.String}}" class="w-4 h-4 rounded shrink-0" loading="lazy">{{end}} | |
| 9 | - {{if .Author.Valid}}{{if .Author.String}}<span>{{.Author.String}}</span>{{end}}{{end}} | |
| 10 | - {{if .Published.Valid}}<span>{{.Published.Time.Format "Jan 2, 2006 15:04"}}</span>{{end}} | |
| 11 | - <span class="text-spot-muted">{{if .FeedTitle}}{{.FeedTitle}}{{else}}{{.FeedURL}}{{end}}</span> | |
| 12 | - </div> | |
| 13 | - {{if .Summary.Valid}}{{if .Summary.String}} | |
| 14 | - <p class="text-sm text-spot-secondary mt-2 line-clamp-2">{{plainText .Summary.String}}</p> | |
| 15 | - {{end}}{{end}} | |
| 16 | - </div> | |
| 17 | - </div> | |
| 18 | -</article> | |
| 19 | -{{else}} | |
| 20 | -<div class="text-center text-spot-secondary py-12">No articles found.</div> | |
| 21 | -{{end}} | |
| 22 | -{{end}} | |
| deleted file mode 100644 | |||
| @@ -1,22 +0,0 @@ | |||
| 1 | -{{define "article-list.html"}} | ||
| 2 | -{{range .Articles}} | ||
| 3 | -<article class="bg-spot-surface rounded-xl px-5 py-4 hover:bg-spot-hover-50 transition shadow-spot"> | ||
| 4 | - <div class="flex items-start justify-between gap-4"> | ||
| 5 | - <div class="min-w-0 flex-1"> | ||
| 6 | - <a href="/articles/{{.ID}}" class="font-bold text-spot-text hover:text-spot-green transition text-lg leading-tight">{{.Title}}</a> | ||
| 7 | - <div class="text-sm text-spot-secondary mt-1 flex items-center gap-2"> | ||
| 8 | - {{if .FeedFaviconURL.Valid}}<img src="{{.FeedFaviconURL.String}}" class="w-4 h-4 rounded shrink-0" loading="lazy">{{end}} | ||
| 9 | - {{if .Author.Valid}}{{if .Author.String}}<span>{{.Author.String}}</span>{{end}}{{end}} | ||
| 10 | - {{if .Published.Valid}}<span>{{.Published.Time.Format "Jan 2, 2006 15:04"}}</span>{{end}} | ||
| 11 | - <span class="text-spot-muted">{{if .FeedTitle}}{{.FeedTitle}}{{else}}{{.FeedURL}}{{end}}</span> | ||
| 12 | - </div> | ||
| 13 | - {{if .Summary.Valid}}{{if .Summary.String}} | ||
| 14 | - <p class="text-sm text-spot-secondary mt-2 line-clamp-2">{{plainText .Summary.String}}</p> | ||
| 15 | - {{end}}{{end}} | ||
| 16 | - </div> | ||
| 17 | - </div> | ||
| 18 | -</article> | ||
| 19 | -{{else}} | ||
| 20 | -<div class="text-center text-spot-secondary py-12">No articles found.</div> | ||
| 21 | -{{end}} | ||
| 22 | -{{end}} | ||
deleted
internal/tmpl/partials/feed-list.html +0 -16 | deleted file mode 100644 | ||
| @@ -1,16 +0,0 @@ | ||
| 1 | -{{define "feed-list.html"}} | |
| 2 | -{{if .Error}}<div class="px-4 py-2 text-sm text-spot-red bg-spot-red/10 border-b border-spot-red/30">{{.Error}}</div>{{end}} | |
| 3 | -{{range .Subscriptions}} | |
| 4 | -<div class="px-5 py-4 flex items-center justify-between hover:bg-spot-hover-50 transition rounded-xl"> | |
| 5 | - <div class="min-w-0"> | |
| 6 | - <div class="font-bold text-spot-text">{{.FeedURL}}</div> | |
| 7 | - {{if .Category.Valid}}<span class="text-xs text-spot-secondary">{{.Category.String}}</span>{{end}} | |
| 8 | - </div> | |
| 9 | - <button hx-delete="/feeds/remove" hx-vals='{"url": "{{.FeedURL}}"}' hx-target="closest .px-5" hx-swap="outerHTML swap:0.3s" | |
| 10 | - hx-confirm="Unsubscribe from this feed?" | |
| 11 | - class="text-sm text-spot-secondary hover:text-spot-red transition">Unsubscribe</button> | |
| 12 | -</div> | |
| 13 | -{{else}} | |
| 14 | -<div class="px-4 py-8 text-center text-spot-secondary">No feeds yet.</div> | |
| 15 | -{{end}} | |
| 16 | -{{end}} | |
| deleted file mode 100644 | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | -{{define "feed-list.html"}} | ||
| 2 | -{{if .Error}}<div class="px-4 py-2 text-sm text-spot-red bg-spot-red/10 border-b border-spot-red/30">{{.Error}}</div>{{end}} | ||
| 3 | -{{range .Subscriptions}} | ||
| 4 | -<div class="px-5 py-4 flex items-center justify-between hover:bg-spot-hover-50 transition rounded-xl"> | ||
| 5 | - <div class="min-w-0"> | ||
| 6 | - <div class="font-bold text-spot-text">{{.FeedURL}}</div> | ||
| 7 | - {{if .Category.Valid}}<span class="text-xs text-spot-secondary">{{.Category.String}}</span>{{end}} | ||
| 8 | - </div> | ||
| 9 | - <button hx-delete="/feeds/remove" hx-vals='{"url": "{{.FeedURL}}"}' hx-target="closest .px-5" hx-swap="outerHTML swap:0.3s" | ||
| 10 | - hx-confirm="Unsubscribe from this feed?" | ||
| 11 | - class="text-sm text-spot-secondary hover:text-spot-red transition">Unsubscribe</button> | ||
| 12 | -</div> | ||
| 13 | -{{else}} | ||
| 14 | -<div class="px-4 py-8 text-center text-spot-secondary">No feeds yet.</div> | ||
| 15 | -{{end}} | ||
| 16 | -{{end}} | ||
added
internal/tmpl/partials/pagination.html +19 -0 | new file mode 100644 | ||
| @@ -0,0 +1,19 @@ | ||
| 1 | +{{define "pagination.html"}} | |
| 2 | +{{if or .HasPrev .HasNext}} | |
| 3 | +<div class="flex items-center justify-center gap-3 py-6"> | |
| 4 | + {{if .HasPrev}} | |
| 5 | + <a href="{{paginationURL .BaseURL .PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | |
| 6 | + {{else}} | |
| 7 | + <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | |
| 8 | + {{end}} | |
| 9 | + | |
| 10 | + <span class="text-sm text-spot-secondary">Page {{.Page}}</span> | |
| 11 | + | |
| 12 | + {{if .HasNext}} | |
| 13 | + <a href="{{paginationURL .BaseURL .NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | |
| 14 | + {{else}} | |
| 15 | + <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | |
| 16 | + {{end}} | |
| 17 | +</div> | |
| 18 | +{{end}} | |
| 19 | +{{end}} | |
| new file mode 100644 | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | +{{define "pagination.html"}} | ||
| 2 | +{{if or .HasPrev .HasNext}} | ||
| 3 | +<div class="flex items-center justify-center gap-3 py-6"> | ||
| 4 | + {{if .HasPrev}} | ||
| 5 | + <a href="{{paginationURL .BaseURL .PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | ||
| 6 | + {{else}} | ||
| 7 | + <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | ||
| 8 | + {{end}} | ||
| 9 | + | ||
| 10 | + <span class="text-sm text-spot-secondary">Page {{.Page}}</span> | ||
| 11 | + | ||
| 12 | + {{if .HasNext}} | ||
| 13 | + <a href="{{paginationURL .BaseURL .NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | ||
| 14 | + {{else}} | ||
| 15 | + <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | ||
| 16 | + {{end}} | ||
| 17 | +</div> | ||
| 18 | +{{end}} | ||
| 19 | +{{end}} | ||
modified
internal/tmpl/partials/profile-card.html +4 -3 | @@ -1,9 +1,10 @@ | ||
| 1 | 1 | {{define "profile-card.html"}} |
| 2 | 2 | <div class="bg-spot-surface rounded-xl p-4 flex items-center gap-3 hover:bg-spot-hover-50 transition"> |
| 3 | + {{if .AvatarURL}}<img src="{{.AvatarURL}}" class="w-10 h-10 rounded-full">{{end}} | |
| 3 | 4 | <div class="min-w-0 flex-1"> |
| 4 | - <a href="/profile/{{.did}}" class="font-bold text-spot-text hover:text-spot-green transition">@{{.handle}}</a> | |
| 5 | - {{if .display_name}}<div class="text-sm text-spot-secondary">{{.display_name}}</div>{{end}} | |
| 5 | + <a href="/profile/{{.Handle}}" class="font-bold text-spot-text hover:text-spot-green transition">@{{.Handle}}</a> | |
| 6 | + {{if .DisplayName}}<div class="text-sm text-spot-secondary">{{.DisplayName}}</div>{{end}} | |
| 6 | 7 | </div> |
| 7 | - <span class="text-xs text-spot-secondary">{{.common_feeds}} shared feeds</span> | |
| 8 | + <span class="text-xs text-spot-secondary">{{.CommonFeeds}} shared</span> | |
| 8 | 9 | </div> |
| 9 | 10 | {{end}} |
| @@ -1,9 +1,10 @@ | |||
| 1 | {{define "profile-card.html"}} | 1 | {{define "profile-card.html"}} |
| 2 | <div class="bg-spot-surface rounded-xl p-4 flex items-center gap-3 hover:bg-spot-hover-50 transition"> | 2 | <div class="bg-spot-surface rounded-xl p-4 flex items-center gap-3 hover:bg-spot-hover-50 transition"> |
| 3 | + {{if .AvatarURL}}<img src="{{.AvatarURL}}" class="w-10 h-10 rounded-full">{{end}} | ||
| 3 | <div class="min-w-0 flex-1"> | 4 | <div class="min-w-0 flex-1"> |
| 4 | - <a href="/profile/{{.did}}" class="font-bold text-spot-text hover:text-spot-green transition">@{{.handle}}</a> | 5 | + <a href="/profile/{{.Handle}}" class="font-bold text-spot-text hover:text-spot-green transition">@{{.Handle}}</a> |
| 5 | - {{if .display_name}}<div class="text-sm text-spot-secondary">{{.display_name}}</div>{{end}} | 6 | + {{if .DisplayName}}<div class="text-sm text-spot-secondary">{{.DisplayName}}</div>{{end}} |
| 6 | </div> | 7 | </div> |
| 7 | - <span class="text-xs text-spot-secondary">{{.common_feeds}} shared feeds</span> | 8 | + <span class="text-xs text-spot-secondary">{{.CommonFeeds}} shared</span> |
| 8 | </div> | 9 | </div> |
| 9 | {{end}} | 10 | {{end}} |
modified
internal/tmpl/trending.html +1 -17 | @@ -26,21 +26,5 @@ | ||
| 26 | 26 | {{end}} |
| 27 | 27 | </div> |
| 28 | 28 | |
| 29 | -{{if or .Page.HasPrev .Page.HasNext}} | |
| 30 | -<div class="flex items-center justify-center gap-3 py-6"> | |
| 31 | - {{if .Page.HasPrev}} | |
| 32 | - <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | |
| 33 | - {{else}} | |
| 34 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | |
| 35 | - {{end}} | |
| 36 | - | |
| 37 | - <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span> | |
| 38 | - | |
| 39 | - {{if .Page.HasNext}} | |
| 40 | - <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | |
| 41 | - {{else}} | |
| 42 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | |
| 43 | - {{end}} | |
| 44 | -</div> | |
| 45 | -{{end}} | |
| 29 | +{{template "pagination.html" (dict "HasPrev" .Page.HasPrev "HasNext" .Page.HasNext "PrevPage" .Page.PrevPage "NextPage" .Page.NextPage "Page" .Page.Page "BaseURL" .BaseURL "QueryParams" .QueryParams)}} | |
| 46 | 30 | {{end}} |
| @@ -26,21 +26,5 @@ | |||
| 26 | {{end}} | 26 | {{end}} |
| 27 | </div> | 27 | </div> |
| 28 | 28 | ||
| 29 | -{{if or .Page.HasPrev .Page.HasNext}} | 29 | +{{template "pagination.html" (dict "HasPrev" .Page.HasPrev "HasNext" .Page.HasNext "PrevPage" .Page.PrevPage "NextPage" .Page.NextPage "Page" .Page.Page "BaseURL" .BaseURL "QueryParams" .QueryParams)}} |
| 30 | -<div class="flex items-center justify-center gap-3 py-6"> | ||
| 31 | - {{if .Page.HasPrev}} | ||
| 32 | - <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">« Prev</a> | ||
| 33 | - {{else}} | ||
| 34 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">« Prev</span> | ||
| 35 | - {{end}} | ||
| 36 | - | ||
| 37 | - <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span> | ||
| 38 | - | ||
| 39 | - {{if .Page.HasNext}} | ||
| 40 | - <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next »</a> | ||
| 41 | - {{else}} | ||
| 42 | - <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next »</span> | ||
| 43 | - {{end}} | ||
| 44 | -</div> | ||
| 45 | -{{end}} | ||
| 46 | {{end}} | 30 | {{end}} |
modified
readme.md +1 -0 | @@ -44,6 +44,7 @@ Then open `http://localhost:8080`. | ||
| 44 | 44 | | `GLEAN_JETSTREAM` | `wss://jetstream.glean.at` | Jetstream WebSocket URL | |
| 45 | 45 | | `GLEAN_SYNC_INTERVAL` | `1h` | PDS sync interval (Go duration: `30m`, `2h30m`, etc.) | |
| 46 | 46 | | `GLEAN_CLUSTER_INTERVAL` | `10m` | Cluster recomputation interval (Go duration) | |
| 47 | +| `GLEAN_COLLECTION_DIR_URL` | _(empty)_ | Collection directory URL for startup backfill | | |
| 47 | 48 | | `GLEAN_PLC_URL` | `https://didplc.glean.at` | PLC directory URL for DID resolution | |
| 48 | 49 | | `GLEAN_OAUTH_CLIENT_ID` | _(empty)_ | OAuth client metadata URL (leave empty for localhost dev) | |
| 49 | 50 | | `GLEAN_OAUTH_REDIRECT_URL` | _(empty)_ | OAuth redirect URL (leave empty for localhost dev) | |
| @@ -44,6 +44,7 @@ Then open `http://localhost:8080`. | |||
| 44 | | `GLEAN_JETSTREAM` | `wss://jetstream.glean.at` | Jetstream WebSocket URL | | 44 | | `GLEAN_JETSTREAM` | `wss://jetstream.glean.at` | Jetstream WebSocket URL | |
| 45 | | `GLEAN_SYNC_INTERVAL` | `1h` | PDS sync interval (Go duration: `30m`, `2h30m`, etc.) | | 45 | | `GLEAN_SYNC_INTERVAL` | `1h` | PDS sync interval (Go duration: `30m`, `2h30m`, etc.) | |
| 46 | | `GLEAN_CLUSTER_INTERVAL` | `10m` | Cluster recomputation interval (Go duration) | | 46 | | `GLEAN_CLUSTER_INTERVAL` | `10m` | Cluster recomputation interval (Go duration) | |
| 47 | +| `GLEAN_COLLECTION_DIR_URL` | _(empty)_ | Collection directory URL for startup backfill | | ||
| 47 | | `GLEAN_PLC_URL` | `https://didplc.glean.at` | PLC directory URL for DID resolution | | 48 | | `GLEAN_PLC_URL` | `https://didplc.glean.at` | PLC directory URL for DID resolution | |
| 48 | | `GLEAN_OAUTH_CLIENT_ID` | _(empty)_ | OAuth client metadata URL (leave empty for localhost dev) | | 49 | | `GLEAN_OAUTH_CLIENT_ID` | _(empty)_ | OAuth client metadata URL (leave empty for localhost dev) | |
| 49 | | `GLEAN_OAUTH_REDIRECT_URL` | _(empty)_ | OAuth redirect URL (leave empty for localhost dev) | | 50 | | `GLEAN_OAUTH_REDIRECT_URL` | _(empty)_ | OAuth redirect URL (leave empty for localhost dev) | |