@rmdes/indiekit-endpoint-blogroll 1.0.13 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,273 +1,116 @@
1
- {% extends "document.njk" %}
1
+ {% extends "layouts/blogroll.njk" %}
2
2
 
3
- {% block content %}
4
- <style>
5
- .br-dashboard {
6
- display: flex;
7
- flex-direction: column;
8
- gap: var(--space-xl, 2rem);
9
- }
10
-
11
- .br-section {
12
- background: var(--color-offset, #f5f5f5);
13
- border-radius: var(--border-radius-small, 0.5rem);
14
- padding: var(--space-m, 1.5rem);
15
- }
16
-
17
- .br-section h2 {
18
- font: var(--font-heading, bold 1.25rem/1.4 sans-serif);
19
- margin-block-end: var(--space-s, 0.75rem);
20
- padding-block-end: var(--space-xs, 0.5rem);
21
- border-block-end: 1px solid var(--color-outline-variant, #ddd);
22
- }
23
-
24
- .br-stats-grid {
25
- display: grid;
26
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
27
- gap: var(--space-s, 0.75rem);
28
- }
29
-
30
- .br-stat {
31
- background: var(--color-background, #fff);
32
- border-radius: var(--border-radius-small, 0.5rem);
33
- padding: var(--space-s, 0.75rem);
34
- text-align: center;
35
- }
36
-
37
- .br-stat dt {
38
- color: var(--color-on-offset, #666);
39
- font: var(--font-caption, 0.875rem/1.4 sans-serif);
40
- margin-block-end: var(--space-2xs, 0.25rem);
41
- }
42
-
43
- .br-stat dd {
44
- font: var(--font-subhead, bold 1.125rem/1.4 sans-serif);
45
- margin: 0;
46
- }
47
-
48
- .br-stat dd.br-stat--error {
49
- color: var(--color-error, #dc3545);
50
- }
51
-
52
- .br-quick-links {
53
- display: flex;
54
- flex-wrap: wrap;
55
- gap: var(--space-s, 0.75rem);
56
- margin-block-start: var(--space-m, 1rem);
57
- }
58
-
59
- .br-list {
60
- list-style: none;
61
- padding: 0;
62
- margin: 0;
63
- display: flex;
64
- flex-direction: column;
65
- gap: var(--space-xs, 0.5rem);
66
- }
67
-
68
- .br-list li {
69
- background: var(--color-background, #fff);
70
- border-radius: var(--border-radius-small, 0.25rem);
71
- padding: var(--space-xs, 0.5rem) var(--space-s, 0.75rem);
72
- display: flex;
73
- justify-content: space-between;
74
- align-items: center;
75
- flex-wrap: wrap;
76
- gap: var(--space-xs, 0.5rem);
77
- }
78
-
79
- .br-list-name {
80
- font: var(--font-body, 0.875rem/1.4 sans-serif);
81
- font-weight: 600;
82
- }
83
-
84
- .br-list-meta {
85
- color: var(--color-on-offset, #666);
86
- font: var(--font-caption, 0.75rem/1.4 sans-serif);
87
- }
88
-
89
- .br-list-error {
90
- color: var(--color-error, #dc3545);
91
- font: var(--font-caption, 0.75rem/1.4 sans-serif);
92
- }
93
-
94
- .br-api-list {
95
- list-style: none;
96
- padding: 0;
97
- margin: 0;
98
- display: flex;
99
- flex-direction: column;
100
- gap: var(--space-xs, 0.5rem);
101
- }
102
-
103
- .br-api-list li {
104
- background: var(--color-background, #fff);
105
- border-radius: var(--border-radius-small, 0.25rem);
106
- padding: var(--space-xs, 0.5rem) var(--space-s, 0.75rem);
107
- font: var(--font-caption, 0.875rem/1.4 sans-serif);
108
- }
109
-
110
- .br-api-list code {
111
- font-weight: 600;
112
- color: var(--color-primary, #0066cc);
113
- }
114
-
115
- .br-empty {
116
- color: var(--color-on-offset, #666);
117
- font: var(--font-caption, 0.875rem/1.4 sans-serif);
118
- text-align: center;
119
- padding: var(--space-m, 1rem);
120
- }
121
- </style>
122
-
123
- <header class="page-header">
124
- <h1 class="page-header__title">{{ __("blogroll.title") }}</h1>
125
- <p class="page-header__description">{{ __("blogroll.description") }}</p>
126
- </header>
127
-
128
- {% for message in request.session.messages %}
129
- <div class="notice notice--{{ message.type }}">
130
- <p>{{ message.content }}</p>
131
- </div>
132
- {% endfor %}
133
-
134
- <div class="br-dashboard">
135
- <section class="br-section">
136
- <h2>{{ __("blogroll.stats.title") }}</h2>
137
- <dl class="br-stats-grid">
138
- <div class="br-stat">
3
+ {% block blogroll %}
4
+ {% call section({ title: __("blogroll.stats.title") }) %}
5
+ <dl class="blogroll-stats">
6
+ <div class="blogroll-stat">
139
7
  <dt>{{ __("blogroll.stats.sources") }}</dt>
140
8
  <dd>{{ stats.sources }}</dd>
141
9
  </div>
142
- <div class="br-stat">
10
+ <div class="blogroll-stat">
143
11
  <dt>{{ __("blogroll.stats.blogs") }}</dt>
144
12
  <dd>{{ stats.blogs }}</dd>
145
13
  </div>
146
- <div class="br-stat">
14
+ <div class="blogroll-stat">
147
15
  <dt>{{ __("blogroll.stats.items") }}</dt>
148
16
  <dd>{{ stats.items }}</dd>
149
17
  </div>
150
- <div class="br-stat">
18
+ <div class="blogroll-stat">
151
19
  <dt>{{ __("blogroll.stats.errors") }}</dt>
152
- <dd class="{% if stats.errors > 0 %}br-stat--error{% endif %}">{{ stats.errors }}</dd>
20
+ <dd class="{% if stats.errors > 0 %}blogroll-stat--error{% endif %}">{{ stats.errors }}</dd>
153
21
  </div>
154
- <div class="br-stat">
22
+ <div class="blogroll-stat">
155
23
  <dt>{{ __("blogroll.stats.lastSync") }}</dt>
156
24
  <dd>{% if syncStatus.lastSync %}{{ syncStatus.lastSync | date("PPpp") }}{% else %}{{ __("blogroll.never") }}{% endif %}</dd>
157
25
  </div>
158
26
  </dl>
159
-
160
- <div class="br-quick-links">
161
- <a href="{{ baseUrl }}/sources" class="button button--secondary">
162
- {{ __("blogroll.sources.manage") }}
163
- </a>
164
- <a href="{{ baseUrl }}/blogs" class="button button--secondary">
165
- {{ __("blogroll.blogs.manage") }}
166
- </a>
27
+ <div class="blogroll-actions">
28
+ {{ button({ href: baseUrl + "/sources", text: __("blogroll.sources.manage"), classes: "button--secondary" }) }}
29
+ {{ button({ href: baseUrl + "/blogs", text: __("blogroll.blogs.manage"), classes: "button--secondary" }) }}
167
30
  </div>
168
- </section>
31
+ {% endcall %}
169
32
 
170
- <section class="br-section">
171
- <h2>{{ __("blogroll.actions.title") }}</h2>
172
- <div class="button-group">
33
+ {% call section({ title: __("blogroll.actions.title") }) %}
34
+ <div class="blogroll-actions">
173
35
  <form method="post" action="{{ baseUrl }}/sync" style="display: inline;">
174
- <button type="submit" class="button button--primary">
175
- {{ __("blogroll.actions.syncNow") }}
176
- </button>
36
+ {{ button({ type: "submit", text: __("blogroll.actions.syncNow") }) }}
177
37
  </form>
178
38
  <form method="post" action="{{ baseUrl }}/clear-resync" style="display: inline;" onsubmit="return confirm('{{ __("blogroll.actions.clearConfirm") }}');">
179
- <button type="submit" class="button button--secondary">
180
- {{ __("blogroll.actions.clearResync") }}
181
- </button>
39
+ {{ button({ type: "submit", text: __("blogroll.actions.clearResync"), classes: "button--secondary" }) }}
182
40
  </form>
183
41
  </div>
184
- </section>
42
+ {% endcall %}
185
43
 
186
44
  {% if blogsWithErrors.length > 0 %}
187
- <section class="br-section">
188
- <h2>{{ __("blogroll.errors.title") }}</h2>
189
- <ul class="br-list">
45
+ {% call section({ title: __("blogroll.errors.title") }) %}
46
+ <ul class="blogroll-list">
190
47
  {% for blog in blogsWithErrors %}
191
- <li>
48
+ <li class="blogroll-list__item blogroll-list__item--compact">
192
49
  <div>
193
- <span class="br-list-name">{{ blog.title }}</span>
194
- <span class="br-list-error">{{ blog.lastError }}</span>
50
+ <span class="blogroll-item__title">{{ blog.title }}</span>
51
+ <span class="blogroll-item__error">{{ blog.lastError }}</span>
195
52
  </div>
196
- <a href="{{ baseUrl }}/blogs/{{ blog._id }}" class="button button--small button--secondary">
197
- {{ __("blogroll.edit") }}
198
- </a>
53
+ {{ button({ href: baseUrl + "/blogs/" + blog._id, text: __("blogroll.edit"), classes: "button--small button--secondary" }) }}
199
54
  </li>
200
55
  {% endfor %}
201
56
  </ul>
202
57
  {% if stats.errors > blogsWithErrors.length %}
203
- <p class="br-empty">
58
+ <p class="blogroll-empty">
204
59
  <a href="{{ baseUrl }}/blogs?status=error">{{ __("blogroll.errors.seeAll", { count: stats.errors }) }}</a>
205
60
  </p>
206
61
  {% endif %}
207
- </section>
62
+ {% endcall %}
208
63
  {% endif %}
209
64
 
210
- <section class="br-section">
211
- <h2>{{ __("blogroll.sources.recent") }}</h2>
65
+ {% call section({ title: __("blogroll.sources.recent") }) %}
212
66
  {% if sources.length > 0 %}
213
- <ul class="br-list">
67
+ <ul class="blogroll-list">
214
68
  {% for source in sources %}
215
- <li>
69
+ <li class="blogroll-list__item blogroll-list__item--compact">
216
70
  <div>
217
- <span class="br-list-name">{{ source.name }}</span>
218
- <span class="br-list-meta">{{ source.type }} {% if source.lastSyncAt %}{{ source.lastSyncAt | date("PP") }}{% else %}{{ __("blogroll.never") }}{% endif %}</span>
71
+ <span class="blogroll-item__title">{{ source.name }}</span>
72
+ <span class="blogroll-item__meta">{{ source.type }} · {% if source.lastSyncAt %}{{ source.lastSyncAt | date("PP") }}{% else %}{{ __("blogroll.never") }}{% endif %}</span>
219
73
  </div>
220
- <span class="badge {% if source.enabled %}badge--green{% else %}badge--yellow{% endif %}">
221
- {% if source.enabled %}{{ __("blogroll.enabled") }}{% else %}{{ __("blogroll.disabled") }}{% endif %}
222
- </span>
74
+ {{ badge({ color: "green" if source.enabled else "yellow", text: __("blogroll.enabled") if source.enabled else __("blogroll.disabled") }) }}
223
75
  </li>
224
76
  {% endfor %}
225
77
  </ul>
226
78
  {% else %}
227
- <p class="br-empty">{{ __("blogroll.sources.empty") }}</p>
79
+ {{ prose({ text: __("blogroll.sources.empty") }) }}
228
80
  {% endif %}
229
- <div class="br-quick-links">
230
- <a href="{{ baseUrl }}/sources/new" class="button button--secondary button--small">
231
- {{ __("blogroll.sources.add") }}
232
- </a>
81
+ <div class="blogroll-actions">
82
+ {{ button({ href: baseUrl + "/sources/new", text: __("blogroll.sources.add"), classes: "button--secondary button--small" }) }}
233
83
  </div>
234
- </section>
84
+ {% endcall %}
235
85
 
236
- <section class="br-section">
237
- <h2>{{ __("blogroll.blogs.recent") }}</h2>
86
+ {% call section({ title: __("blogroll.blogs.recent") }) %}
238
87
  {% if recentBlogs.length > 0 %}
239
- <ul class="br-list">
88
+ <ul class="blogroll-list">
240
89
  {% for blog in recentBlogs %}
241
- <li>
90
+ <li class="blogroll-list__item blogroll-list__item--compact">
242
91
  <div>
243
- <span class="br-list-name">{{ blog.title }}</span>
244
- <span class="br-list-meta">{{ blog.category or "Uncategorized" }} {{ blog.itemCount }} items</span>
92
+ <span class="blogroll-item__title">{{ blog.title }}</span>
93
+ <span class="blogroll-item__meta">{{ blog.category or "Uncategorized" }} · {{ blog.itemCount }} items</span>
245
94
  </div>
246
- <span class="badge {% if blog.status == 'active' %}badge--green{% elif blog.status == 'error' %}badge--red{% else %}badge--yellow{% endif %}">
247
- {{ blog.status }}
248
- </span>
95
+ {{ badge({ color: "green" if blog.status == "active" else ("red" if blog.status == "error" else "yellow"), text: blog.status }) }}
249
96
  </li>
250
97
  {% endfor %}
251
98
  </ul>
252
99
  {% else %}
253
- <p class="br-empty">{{ __("blogroll.blogs.empty") }}</p>
100
+ {{ prose({ text: __("blogroll.blogs.empty") }) }}
254
101
  {% endif %}
255
- <div class="br-quick-links">
256
- <a href="{{ baseUrl }}/blogs/new" class="button button--secondary button--small">
257
- {{ __("blogroll.blogs.add") }}
258
- </a>
102
+ <div class="blogroll-actions">
103
+ {{ button({ href: baseUrl + "/blogs/new", text: __("blogroll.blogs.add"), classes: "button--secondary button--small" }) }}
259
104
  </div>
260
- </section>
105
+ {% endcall %}
261
106
 
262
- <section class="br-section">
263
- <h2>{{ __("blogroll.api.title") }}</h2>
264
- <ul class="br-api-list">
107
+ {% call section({ title: __("blogroll.api.title") }) %}
108
+ <ul class="blogroll-api-list">
265
109
  <li><code>GET {{ baseUrl }}/api/blogs</code> - {{ __("blogroll.api.blogs") }}</li>
266
110
  <li><code>GET {{ baseUrl }}/api/items</code> - {{ __("blogroll.api.items") }}</li>
267
111
  <li><code>GET {{ baseUrl }}/api/categories</code> - {{ __("blogroll.api.categories") }}</li>
268
112
  <li><code>GET {{ baseUrl }}/api/opml</code> - {{ __("blogroll.api.opml") }}</li>
269
113
  <li><code>GET {{ baseUrl }}/api/status</code> - {{ __("blogroll.api.status") }}</li>
270
114
  </ul>
271
- </section>
272
- </div>
115
+ {% endcall %}
273
116
  {% endblock %}
@@ -1,147 +1,75 @@
1
- {% extends "document.njk" %}
2
-
3
- {% block content %}
4
- <style>
5
- .br-form {
6
- max-width: 600px;
7
- }
8
-
9
- .br-field {
10
- display: flex;
11
- flex-direction: column;
12
- gap: var(--space-2xs, 0.25rem);
13
- margin-block-end: var(--space-m, 1rem);
14
- }
15
-
16
- .br-field label {
17
- font: var(--font-label, bold 0.875rem/1.4 sans-serif);
18
- }
19
-
20
- .br-field-hint {
21
- color: var(--color-on-offset, #666);
22
- font: var(--font-caption, 0.875rem/1.4 sans-serif);
23
- }
24
-
25
- .br-field input,
26
- .br-field select,
27
- .br-field textarea {
28
- appearance: none;
29
- background-color: var(--color-background, #fff);
30
- border: 1px solid var(--color-outline-variant, #ccc);
31
- border-radius: var(--border-radius-small, 0.25rem);
32
- font: var(--font-body, 0.875rem/1.4 sans-serif);
33
- padding: calc(var(--space-s, 0.75rem) / 2) var(--space-s, 0.75rem);
34
- width: 100%;
35
- }
36
-
37
- .br-field textarea {
38
- min-height: 150px;
39
- font-family: monospace;
40
- }
41
-
42
- .br-field input:focus,
43
- .br-field select:focus,
44
- .br-field textarea:focus {
45
- border-color: var(--color-primary, #0066cc);
46
- outline: 2px solid var(--color-primary, #0066cc);
47
- outline-offset: 1px;
48
- }
49
-
50
- .br-field-inline {
51
- flex-direction: row;
52
- align-items: center;
53
- gap: var(--space-s, 0.75rem);
54
- }
55
-
56
- .br-field-inline input[type="checkbox"] {
57
- appearance: auto;
58
- width: auto;
59
- cursor: pointer;
60
- }
61
- </style>
62
-
63
- <header class="page-header">
64
- <a href="{{ baseUrl }}/sources" class="page-header__back">{{ icon("previous") }} {{ __("blogroll.sources.title") }}</a>
65
- <h1 class="page-header__title">{{ title }}</h1>
66
- </header>
67
-
68
- {% for message in request.session.messages %}
69
- <div class="notice notice--{{ message.type }}">
70
- <p>{{ message.content }}</p>
71
- </div>
72
- {% endfor %}
73
-
74
- <form method="post" action="{% if isNew %}{{ baseUrl }}/sources{% else %}{{ baseUrl }}/sources/{{ source._id }}{% endif %}" class="br-form">
75
- <div class="br-field">
76
- <label for="name">{{ __("blogroll.sources.form.name") }}</label>
77
- <input type="text" id="name" name="name" value="{{ source.name if source else '' }}" required>
78
- </div>
79
-
80
- <div class="br-field">
81
- <label for="type">{{ __("blogroll.sources.form.type") }}</label>
82
- <select id="type" name="type" required onchange="toggleTypeFields()">
83
- <option value="opml_url" {% if source.type == 'opml_url' %}selected{% endif %}>OPML URL (auto-sync)</option>
84
- <option value="opml_file" {% if source.type == 'opml_file' %}selected{% endif %}>OPML File (one-time import)</option>
85
- {% if microsubAvailable %}
86
- <option value="microsub" {% if source.type == 'microsub' %}selected{% endif %}>Microsub Subscriptions</option>
87
- {% endif %}
88
- </select>
89
- <span class="br-field-hint">{{ __("blogroll.sources.form.typeHint") }}</span>
90
- </div>
91
-
92
- <div class="br-field" id="urlField">
93
- <label for="url">{{ __("blogroll.sources.form.url") }}</label>
94
- <input type="url" id="url" name="url" value="{{ source.url if source else '' }}" placeholder="https://...">
95
- <span class="br-field-hint">{{ __("blogroll.sources.form.urlHint") }}</span>
96
- </div>
97
-
98
- <div class="br-field" id="opmlContentField" style="display: none;">
99
- <label for="opmlContent">{{ __("blogroll.sources.form.opmlContent") }}</label>
100
- <textarea id="opmlContent" name="opmlContent" placeholder="<?xml version=&quot;1.0&quot;?>...">{{ source.opmlContent if source else '' }}</textarea>
101
- <span class="br-field-hint">{{ __("blogroll.sources.form.opmlContentHint") }}</span>
102
- </div>
103
-
104
- <div class="br-field" id="microsubChannelField" style="display: none;">
105
- <label for="channelFilter">{{ __("blogroll.sources.form.microsubChannel") | default("Microsub Channel") }}</label>
106
- <select id="channelFilter" name="channelFilter">
107
- <option value="">All channels</option>
108
- {% for channel in microsubChannels %}
109
- <option value="{{ channel.uid }}" {% if source.channelFilter == channel.uid %}selected{% endif %}>{{ channel.name }}</option>
110
- {% endfor %}
111
- </select>
112
- <span class="br-field-hint">{{ __("blogroll.sources.form.microsubChannelHint") | default("Sync feeds from a specific channel, or all channels") }}</span>
113
- </div>
114
-
115
- <div class="br-field" id="categoryPrefixField" style="display: none;">
116
- <label for="categoryPrefix">{{ __("blogroll.sources.form.categoryPrefix") | default("Category Prefix") }}</label>
117
- <input type="text" id="categoryPrefix" name="categoryPrefix" value="{{ source.categoryPrefix if source else '' }}" placeholder="e.g., Microsub: ">
118
- <span class="br-field-hint">{{ __("blogroll.sources.form.categoryPrefixHint") | default("Optional prefix for blog categories (e.g., 'Following: ')") }}</span>
119
- </div>
120
-
121
- <div class="br-field">
122
- <label for="syncInterval">{{ __("blogroll.sources.form.syncInterval") }}</label>
123
- <select id="syncInterval" name="syncInterval">
124
- <option value="30" {% if source.syncInterval == 30 %}selected{% endif %}>30 minutes</option>
125
- <option value="60" {% if not source or source.syncInterval == 60 %}selected{% endif %}>1 hour</option>
126
- <option value="180" {% if source.syncInterval == 180 %}selected{% endif %}>3 hours</option>
127
- <option value="360" {% if source.syncInterval == 360 %}selected{% endif %}>6 hours</option>
128
- <option value="720" {% if source.syncInterval == 720 %}selected{% endif %}>12 hours</option>
129
- <option value="1440" {% if source.syncInterval == 1440 %}selected{% endif %}>24 hours</option>
130
- </select>
131
- </div>
132
-
133
- <div class="br-field br-field-inline">
134
- <input type="checkbox" id="enabled" name="enabled" {% if not source or source.enabled %}checked{% endif %}>
135
- <label for="enabled">{{ __("blogroll.sources.form.enabled") }}</label>
136
- </div>
137
-
138
- <div class="button-group">
139
- <button type="submit" class="button button--primary">
140
- {% if isNew %}{{ __("blogroll.sources.create") }}{% else %}{{ __("blogroll.sources.save") }}{% endif %}
141
- </button>
142
- <a href="{{ baseUrl }}/sources" class="button button--secondary">{{ __("blogroll.cancel") }}</a>
143
- </div>
144
- </form>
1
+ {% extends "layouts/blogroll.njk" %}
2
+
3
+ {% block blogroll %}
4
+ <form method="post" action="{% if isNew %}{{ baseUrl }}/sources{% else %}{{ baseUrl }}/sources/{{ source._id }}{% endif %}" class="blogroll-form">
5
+ <div class="blogroll-field">
6
+ <label for="name">{{ __("blogroll.sources.form.name") }}</label>
7
+ <input type="text" id="name" name="name" value="{{ source.name if source else '' }}" required>
8
+ </div>
9
+
10
+ <div class="blogroll-field">
11
+ <label for="type">{{ __("blogroll.sources.form.type") }}</label>
12
+ <select id="type" name="type" required onchange="toggleTypeFields()">
13
+ <option value="opml_url" {% if source.type == 'opml_url' %}selected{% endif %}>OPML URL (auto-sync)</option>
14
+ <option value="opml_file" {% if source.type == 'opml_file' %}selected{% endif %}>OPML File (one-time import)</option>
15
+ {% if microsubAvailable %}
16
+ <option value="microsub" {% if source.type == 'microsub' %}selected{% endif %}>Microsub Subscriptions</option>
17
+ {% endif %}
18
+ </select>
19
+ <span class="blogroll-field-hint">{{ __("blogroll.sources.form.typeHint") }}</span>
20
+ </div>
21
+
22
+ <div class="blogroll-field" id="urlField">
23
+ <label for="url">{{ __("blogroll.sources.form.url") }}</label>
24
+ <input type="url" id="url" name="url" value="{{ source.url if source else '' }}" placeholder="https://...">
25
+ <span class="blogroll-field-hint">{{ __("blogroll.sources.form.urlHint") }}</span>
26
+ </div>
27
+
28
+ <div class="blogroll-field" id="opmlContentField" style="display: none;">
29
+ <label for="opmlContent">{{ __("blogroll.sources.form.opmlContent") }}</label>
30
+ <textarea id="opmlContent" name="opmlContent" placeholder="<?xml version=&quot;1.0&quot;?>...">{{ source.opmlContent if source else '' }}</textarea>
31
+ <span class="blogroll-field-hint">{{ __("blogroll.sources.form.opmlContentHint") }}</span>
32
+ </div>
33
+
34
+ <div class="blogroll-field" id="microsubChannelField" style="display: none;">
35
+ <label for="channelFilter">{{ __("blogroll.sources.form.microsubChannel") | default("Microsub Channel") }}</label>
36
+ <select id="channelFilter" name="channelFilter">
37
+ <option value="">All channels</option>
38
+ {% for channel in microsubChannels %}
39
+ <option value="{{ channel.uid }}" {% if source.channelFilter == channel.uid %}selected{% endif %}>{{ channel.name }}</option>
40
+ {% endfor %}
41
+ </select>
42
+ <span class="blogroll-field-hint">{{ __("blogroll.sources.form.microsubChannelHint") | default("Sync feeds from a specific channel, or all channels") }}</span>
43
+ </div>
44
+
45
+ <div class="blogroll-field" id="categoryPrefixField" style="display: none;">
46
+ <label for="categoryPrefix">{{ __("blogroll.sources.form.categoryPrefix") | default("Category Prefix") }}</label>
47
+ <input type="text" id="categoryPrefix" name="categoryPrefix" value="{{ source.categoryPrefix if source else '' }}" placeholder="e.g., Microsub: ">
48
+ <span class="blogroll-field-hint">{{ __("blogroll.sources.form.categoryPrefixHint") | default("Optional prefix for blog categories (e.g., 'Following: ')") }}</span>
49
+ </div>
50
+
51
+ <div class="blogroll-field">
52
+ <label for="syncInterval">{{ __("blogroll.sources.form.syncInterval") }}</label>
53
+ <select id="syncInterval" name="syncInterval">
54
+ <option value="30" {% if source.syncInterval == 30 %}selected{% endif %}>30 minutes</option>
55
+ <option value="60" {% if not source or source.syncInterval == 60 %}selected{% endif %}>1 hour</option>
56
+ <option value="180" {% if source.syncInterval == 180 %}selected{% endif %}>3 hours</option>
57
+ <option value="360" {% if source.syncInterval == 360 %}selected{% endif %}>6 hours</option>
58
+ <option value="720" {% if source.syncInterval == 720 %}selected{% endif %}>12 hours</option>
59
+ <option value="1440" {% if source.syncInterval == 1440 %}selected{% endif %}>24 hours</option>
60
+ </select>
61
+ </div>
62
+
63
+ <div class="blogroll-field blogroll-field--inline">
64
+ <input type="checkbox" id="enabled" name="enabled" {% if not source or source.enabled %}checked{% endif %}>
65
+ <label for="enabled">{{ __("blogroll.sources.form.enabled") }}</label>
66
+ </div>
67
+
68
+ <div class="blogroll-actions">
69
+ {{ button({ type: "submit", text: __("blogroll.sources.create") if isNew else __("blogroll.sources.save") }) }}
70
+ {{ button({ href: baseUrl + "/sources", text: __("blogroll.cancel"), classes: "button--secondary" }) }}
71
+ </div>
72
+ </form>
145
73
 
146
74
  <script>
147
75
  function toggleTypeFields() {