@agent-cms/agentcms 0.0.0-stage → 0.1.0

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.
Files changed (84) hide show
  1. package/LICENSE +143 -0
  2. package/README.md +1119 -2
  3. package/changelog.json +20 -0
  4. package/dist/admin/admin-files.json +48 -0
  5. package/dist/admin/assets/CodeEditor-DcFzGUt8.js +24 -0
  6. package/dist/admin/assets/flexweg-logo-CdtuqAFA.png +0 -0
  7. package/dist/admin/assets/index-CLZ1gQZl.js +5460 -0
  8. package/dist/admin/assets/index-DMcGogR2.css +1 -0
  9. package/dist/admin/assets/sql-wasm-C1U8OeUW.wasm +0 -0
  10. package/dist/admin/assets/sql-wasm-CYl3LXH_.js +2 -0
  11. package/dist/admin/assets/sql-wasm-V5jTDcrT.js +1 -0
  12. package/dist/admin/config.js +11 -0
  13. package/dist/admin/external.default.json +72 -0
  14. package/dist/admin/index.html +116 -0
  15. package/dist/admin/plugins/core-seo/bundle.js +55 -0
  16. package/dist/admin/plugins/core-seo/manifest.json +7 -0
  17. package/dist/admin/plugins/flexweg-archives/bundle.js +1414 -0
  18. package/dist/admin/plugins/flexweg-archives/manifest.json +7 -0
  19. package/dist/admin/plugins/flexweg-multilang/bundle.js +1187 -0
  20. package/dist/admin/plugins/flexweg-multilang/manifest.json +7 -0
  21. package/dist/admin/plugins/flexweg-rss/bundle.js +1354 -0
  22. package/dist/admin/plugins/flexweg-rss/manifest.json +7 -0
  23. package/dist/admin/plugins/flexweg-search/bundle.js +1243 -0
  24. package/dist/admin/plugins/flexweg-search/manifest.json +7 -0
  25. package/dist/admin/plugins/flexweg-sitemaps/bundle.js +1293 -0
  26. package/dist/admin/plugins/flexweg-sitemaps/manifest.json +7 -0
  27. package/dist/admin/runtime/cms-runtime.js +164 -0
  28. package/dist/admin/runtime/react-dom-client.js +13 -0
  29. package/dist/admin/runtime/react-dom.js +16 -0
  30. package/dist/admin/runtime/react-i18next.js +21 -0
  31. package/dist/admin/runtime/react-jsx-runtime.js +17 -0
  32. package/dist/admin/runtime/react.js +55 -0
  33. package/dist/admin/themes/corporate/bundle.js +14465 -0
  34. package/dist/admin/themes/corporate/manifest.json +7 -0
  35. package/dist/admin/themes/corporate/theme.css +2 -0
  36. package/dist/admin/themes/magazine/bundle.js +10229 -0
  37. package/dist/admin/themes/magazine/manifest.json +7 -0
  38. package/dist/admin/themes/magazine/theme.css +2 -0
  39. package/dist/admin/themes/marketplace-core/bundle.js +11213 -0
  40. package/dist/admin/themes/marketplace-core/manifest.json +7 -0
  41. package/dist/admin/themes/marketplace-core/theme.css +1613 -0
  42. package/dist/admin/themes/portfolio/bundle.js +9195 -0
  43. package/dist/admin/themes/portfolio/manifest.json +7 -0
  44. package/dist/admin/themes/portfolio/theme.css +2 -0
  45. package/dist/admin/themes/storefront/bundle.js +13172 -0
  46. package/dist/admin/themes/storefront/manifest.json +7 -0
  47. package/dist/admin/themes/storefront/theme.css +2 -0
  48. package/dist/agent/agentcms.mjs +1711 -0
  49. package/dist/agent/chunks/hosting-D_uj-Mt1.mjs +117 -0
  50. package/dist/agent/chunks/index-CrvEoyi0.mjs +3608 -0
  51. package/dist/agent/chunks/install-DkCCcKAO.mjs +577 -0
  52. package/dist/agent/chunks/plan-EYKegfhp.mjs +245 -0
  53. package/dist/agent/chunks/registry-lkfb_E9O.mjs +53517 -0
  54. package/dist/agent/chunks/siteAdd-BqcYt8nu.mjs +118 -0
  55. package/dist/agent/chunks/sql-wasm-GjJDiefO.mjs +10 -0
  56. package/dist/agent/chunks/sqljs-DjSCStJ0.mjs +20 -0
  57. package/dist/agent/vendor/sql-wasm.cjs +188 -0
  58. package/dist/theme-assets/corporate-menu.js +266 -0
  59. package/dist/theme-assets/corporate-posts.js +571 -0
  60. package/dist/theme-assets/corporate.css +2 -0
  61. package/dist/theme-assets/default-menu.js +266 -0
  62. package/dist/theme-assets/default-posts.js +378 -0
  63. package/dist/theme-assets/default.css +1 -0
  64. package/dist/theme-assets/magazine-menu.js +227 -0
  65. package/dist/theme-assets/magazine-posts.js +361 -0
  66. package/dist/theme-assets/magazine.css +2 -0
  67. package/dist/theme-assets/portfolio-filters.js +104 -0
  68. package/dist/theme-assets/portfolio-menu.js +240 -0
  69. package/dist/theme-assets/portfolio-posts.js +358 -0
  70. package/dist/theme-assets/portfolio.css +2 -0
  71. package/dist/theme-assets/storefront-catalog.js +504 -0
  72. package/dist/theme-assets/storefront-menu.js +239 -0
  73. package/dist/theme-assets/storefront-posts.js +404 -0
  74. package/dist/theme-assets/storefront.css +2 -0
  75. package/dist/version.json +9 -0
  76. package/package.json +101 -4
  77. package/plugin/.claude-plugin/plugin.json +16 -0
  78. package/plugin/skills/agentcms-install/SKILL.md +110 -0
  79. package/plugin/skills/agentcms-install/references/cloudflare.md +15 -0
  80. package/plugin/skills/agentcms-install/references/flexweg.md +11 -0
  81. package/plugin/skills/agentcms-install/references/github.md +67 -0
  82. package/plugin/skills/agentcms-install/references/gitlab.md +15 -0
  83. package/plugin/skills/agentcms-install/references/s3.md +81 -0
  84. package/plugin/skills/agentcms-manage/SKILL.md +92 -0
@@ -0,0 +1,1354 @@
1
+ import { pickPublicLocale as ce, uploadFile as U, pathToPublicUrl as S, deleteFile as v, buildRssFeedXml as I, applyFilters as ue, buildTermUrl as O, buildPostUrl as ge, markdownToPlainText as pe, useCmsData as he, EntityCombobox as fe, fetchAllPosts as N, publishMenuJson as H, toast as x, updatePluginConfig as B, i18n as $ } from "@flexweg/cms-runtime";
2
+ import { jsxs as g, jsx as l, Fragment as me } from "react/jsx-runtime";
3
+ import { forwardRef as K, createElement as z, useState as T, useEffect as be, useMemo as ye } from "react";
4
+ import { useTranslation as Se } from "react-i18next";
5
+ const V = {
6
+ title: "RSS feeds",
7
+ description: "Generates an RSS 2.0 feed at /rss.xml plus optional per-category feeds. Each feed can be added to the site footer.",
8
+ regenerationTarget: {
9
+ label: "RSS feeds",
10
+ description: "Site feed + per-category feeds + XSL stylesheet + footer menu refresh"
11
+ },
12
+ sections: {
13
+ site: "Site feed",
14
+ categoryFeeds: "Category feeds",
15
+ actions: "Actions"
16
+ },
17
+ site: {
18
+ enabled: "Generate /rss.xml",
19
+ enabledHelp: "When enabled, an RSS file is published at the site root listing the most recent online posts.",
20
+ itemCount: "Items in feed",
21
+ itemCountHelp: "Number of most-recent posts to include (1–100).",
22
+ excluded: "Excluded categories",
23
+ excludedHelp: "Posts in checked categories are skipped from the site feed. Leave empty to include everything.",
24
+ addToFooter: "Show in footer"
25
+ },
26
+ category: {
27
+ addLabel: "Add a category feed",
28
+ placeholder: "Search a category…",
29
+ add: "Add",
30
+ itemCount: "Items",
31
+ addToFooter: "Show in footer",
32
+ remove: "Remove",
33
+ pathHint: "Published at /{{path}}",
34
+ none: "No category feeds yet."
35
+ },
36
+ actions: {
37
+ save: "Save settings",
38
+ saving: "Saving…",
39
+ saved: "Settings saved.",
40
+ uploadXsl: "Upload stylesheet",
41
+ uploadingXsl: "Uploading…",
42
+ xslUploaded: "Stylesheet uploaded.",
43
+ xslFailed: "Stylesheet upload failed.",
44
+ forceRegenerate: "Force regenerate all feeds",
45
+ forceRegenerating: "Regenerating…",
46
+ regenerated: "Feeds regenerated ({{count}} files).",
47
+ regenerateFailed: "Feed regeneration failed."
48
+ },
49
+ footerLabels: {
50
+ site: "RSS",
51
+ // Used as "RSS — <category>"; rendered with `name` interpolated.
52
+ category: "RSS — {{name}}"
53
+ },
54
+ baseUrlMissing: "Set the public site URL in Settings → General before regenerating feeds."
55
+ }, xe = {
56
+ title: "Flux RSS",
57
+ description: "Génère un flux RSS 2.0 à /rss.xml ainsi que des flux par catégorie optionnels. Chaque flux peut être ajouté au pied de page du site.",
58
+ regenerationTarget: {
59
+ label: "Flux RSS",
60
+ description: "Flux site + flux par catégorie + feuille XSL + rafraîchissement du menu pied de page"
61
+ },
62
+ sections: {
63
+ site: "Flux du site",
64
+ categoryFeeds: "Flux par catégorie",
65
+ actions: "Actions"
66
+ },
67
+ site: {
68
+ enabled: "Générer /rss.xml",
69
+ enabledHelp: "Quand activé, un fichier RSS est publié à la racine du site listant les derniers articles en ligne.",
70
+ itemCount: "Éléments dans le flux",
71
+ itemCountHelp: "Nombre d'articles récents à inclure (1–100).",
72
+ excluded: "Catégories à exclure",
73
+ excludedHelp: "Les articles des catégories cochées sont écartés du flux du site. Laissez vide pour tout inclure.",
74
+ addToFooter: "Afficher dans le pied de page"
75
+ },
76
+ category: {
77
+ addLabel: "Ajouter un flux de catégorie",
78
+ placeholder: "Rechercher une catégorie…",
79
+ add: "Ajouter",
80
+ itemCount: "Éléments",
81
+ addToFooter: "Afficher dans le pied de page",
82
+ remove: "Retirer",
83
+ pathHint: "Publié à /{{path}}",
84
+ none: "Aucun flux de catégorie."
85
+ },
86
+ actions: {
87
+ save: "Enregistrer les réglages",
88
+ saving: "Enregistrement…",
89
+ saved: "Réglages enregistrés.",
90
+ uploadXsl: "Envoyer la feuille de style",
91
+ uploadingXsl: "Envoi…",
92
+ xslUploaded: "Feuille de style envoyée.",
93
+ xslFailed: "Échec de l'envoi de la feuille de style.",
94
+ forceRegenerate: "Forcer la régénération",
95
+ forceRegenerating: "Régénération…",
96
+ regenerated: "Flux régénérés ({{count}} fichiers).",
97
+ regenerateFailed: "Échec de la régénération."
98
+ },
99
+ footerLabels: {
100
+ site: "RSS",
101
+ category: "RSS — {{name}}"
102
+ },
103
+ baseUrlMissing: "Définissez l'URL publique du site dans Réglages → Général avant de régénérer les flux."
104
+ }, Fe = {
105
+ title: "RSS-Feeds",
106
+ description: "Erzeugt einen RSS-2.0-Feed unter /rss.xml sowie optionale Feeds pro Kategorie. Jeder Feed kann zur Fußzeile der Website hinzugefügt werden.",
107
+ regenerationTarget: {
108
+ label: "RSS-Feeds",
109
+ description: "Site-Feed + Kategorie-Feeds + XSL-Stylesheet + Footer-Menü-Refresh"
110
+ },
111
+ sections: {
112
+ site: "Website-Feed",
113
+ categoryFeeds: "Kategorie-Feeds",
114
+ actions: "Aktionen"
115
+ },
116
+ site: {
117
+ enabled: "/rss.xml erzeugen",
118
+ enabledHelp: "Wenn aktiviert, wird im Stammverzeichnis der Website eine RSS-Datei mit den neuesten Online-Artikeln veröffentlicht.",
119
+ itemCount: "Einträge im Feed",
120
+ itemCountHelp: "Anzahl der jüngsten Artikel, die einbezogen werden (1–100).",
121
+ excluded: "Ausgeschlossene Kategorien",
122
+ excludedHelp: "Artikel in markierten Kategorien werden vom Website-Feed übersprungen. Leer lassen, um alle einzubeziehen.",
123
+ addToFooter: "In Fußzeile anzeigen"
124
+ },
125
+ category: {
126
+ addLabel: "Einen Kategorie-Feed hinzufügen",
127
+ placeholder: "Kategorie suchen…",
128
+ add: "Hinzufügen",
129
+ itemCount: "Einträge",
130
+ addToFooter: "In Fußzeile anzeigen",
131
+ remove: "Entfernen",
132
+ pathHint: "Veröffentlicht unter /{{path}}",
133
+ none: "Noch keine Kategorie-Feeds."
134
+ },
135
+ actions: {
136
+ save: "Einstellungen speichern",
137
+ saving: "Speichert…",
138
+ saved: "Einstellungen gespeichert.",
139
+ uploadXsl: "Stylesheet hochladen",
140
+ uploadingXsl: "Lädt hoch…",
141
+ xslUploaded: "Stylesheet hochgeladen.",
142
+ xslFailed: "Stylesheet-Upload fehlgeschlagen.",
143
+ forceRegenerate: "Alle Feeds zwangsweise neu erzeugen",
144
+ forceRegenerating: "Neu erzeugen…",
145
+ regenerated: "Feeds neu erzeugt ({{count}} Dateien).",
146
+ regenerateFailed: "Feed-Neuerzeugung fehlgeschlagen."
147
+ },
148
+ footerLabels: {
149
+ site: "RSS",
150
+ category: "RSS — {{name}}"
151
+ },
152
+ baseUrlMissing: "Legen Sie die öffentliche Website-URL unter Einstellungen → Allgemein fest, bevor Sie Feeds neu erzeugen."
153
+ }, ve = {
154
+ title: "Feeds RSS",
155
+ description: "Genera un feed RSS 2.0 en /rss.xml más feeds opcionales por categoría. Cada feed se puede añadir al pie del sitio.",
156
+ regenerationTarget: {
157
+ label: "Feeds RSS",
158
+ description: "Feed del sitio + feeds por categoría + hoja XSL + actualización del menú del pie"
159
+ },
160
+ sections: {
161
+ site: "Feed del sitio",
162
+ categoryFeeds: "Feeds de categoría",
163
+ actions: "Acciones"
164
+ },
165
+ site: {
166
+ enabled: "Generar /rss.xml",
167
+ enabledHelp: "Cuando está activado, se publica un archivo RSS en la raíz del sitio con las entradas más recientes en línea.",
168
+ itemCount: "Elementos en el feed",
169
+ itemCountHelp: "Número de entradas más recientes a incluir (1–100).",
170
+ excluded: "Categorías excluidas",
171
+ excludedHelp: "Las entradas de las categorías marcadas se omiten del feed del sitio. Déjalo vacío para incluir todo.",
172
+ addToFooter: "Mostrar en el pie"
173
+ },
174
+ category: {
175
+ addLabel: "Añadir un feed de categoría",
176
+ placeholder: "Buscar una categoría…",
177
+ add: "Añadir",
178
+ itemCount: "Elementos",
179
+ addToFooter: "Mostrar en el pie",
180
+ remove: "Eliminar",
181
+ pathHint: "Publicado en /{{path}}",
182
+ none: "Aún no hay feeds de categoría."
183
+ },
184
+ actions: {
185
+ save: "Guardar ajustes",
186
+ saving: "Guardando…",
187
+ saved: "Ajustes guardados.",
188
+ uploadXsl: "Subir hoja de estilo",
189
+ uploadingXsl: "Subiendo…",
190
+ xslUploaded: "Hoja de estilo subida.",
191
+ xslFailed: "Error al subir la hoja de estilo.",
192
+ forceRegenerate: "Forzar regeneración de todos los feeds",
193
+ forceRegenerating: "Regenerando…",
194
+ regenerated: "Feeds regenerados ({{count}} archivos).",
195
+ regenerateFailed: "La regeneración de feeds falló."
196
+ },
197
+ footerLabels: {
198
+ site: "RSS",
199
+ category: "RSS — {{name}}"
200
+ },
201
+ baseUrlMissing: "Define la URL pública del sitio en Ajustes → General antes de regenerar los feeds."
202
+ }, we = {
203
+ title: "RSS-feeds",
204
+ description: "Genereert een RSS 2.0-feed op /rss.xml plus optionele feeds per categorie. Elke feed kan aan de footer van de site worden toegevoegd.",
205
+ regenerationTarget: {
206
+ label: "RSS-feeds",
207
+ description: "Site-feed + categorie-feeds + XSL-stylesheet + footermenu-vernieuwing"
208
+ },
209
+ sections: {
210
+ site: "Site-feed",
211
+ categoryFeeds: "Categoriefeeds",
212
+ actions: "Acties"
213
+ },
214
+ site: {
215
+ enabled: "/rss.xml genereren",
216
+ enabledHelp: "Wanneer ingeschakeld wordt op de root van de site een RSS-bestand gepubliceerd met de meest recente online berichten.",
217
+ itemCount: "Items in feed",
218
+ itemCountHelp: "Aantal meest recente berichten om op te nemen (1–100).",
219
+ excluded: "Uitgesloten categorieën",
220
+ excludedHelp: "Berichten in aangevinkte categorieën worden overgeslagen in de site-feed. Laat leeg om alles op te nemen.",
221
+ addToFooter: "Tonen in footer"
222
+ },
223
+ category: {
224
+ addLabel: "Een categoriefeed toevoegen",
225
+ placeholder: "Een categorie zoeken…",
226
+ add: "Toevoegen",
227
+ itemCount: "Items",
228
+ addToFooter: "Tonen in footer",
229
+ remove: "Verwijderen",
230
+ pathHint: "Gepubliceerd op /{{path}}",
231
+ none: "Nog geen categoriefeeds."
232
+ },
233
+ actions: {
234
+ save: "Instellingen opslaan",
235
+ saving: "Opslaan…",
236
+ saved: "Instellingen opgeslagen.",
237
+ uploadXsl: "Stylesheet uploaden",
238
+ uploadingXsl: "Uploaden…",
239
+ xslUploaded: "Stylesheet geüpload.",
240
+ xslFailed: "Stylesheet-upload mislukt.",
241
+ forceRegenerate: "Alle feeds geforceerd regenereren",
242
+ forceRegenerating: "Regenereren…",
243
+ regenerated: "Feeds geregenereerd ({{count}} bestanden).",
244
+ regenerateFailed: "Feed-regeneratie mislukt."
245
+ },
246
+ footerLabels: {
247
+ site: "RSS",
248
+ category: "RSS — {{name}}"
249
+ },
250
+ baseUrlMissing: "Stel de publieke site-URL in via Instellingen → Algemeen voordat je feeds regenereert."
251
+ }, Re = {
252
+ title: "Feeds RSS",
253
+ description: "Gera um feed RSS 2.0 em /rss.xml mais feeds opcionais por categoria. Cada feed pode ser adicionado ao rodapé do site.",
254
+ regenerationTarget: {
255
+ label: "Feeds RSS",
256
+ description: "Feed do site + feeds por categoria + folha XSL + atualização do menu do rodapé"
257
+ },
258
+ sections: {
259
+ site: "Feed do site",
260
+ categoryFeeds: "Feeds de categoria",
261
+ actions: "Ações"
262
+ },
263
+ site: {
264
+ enabled: "Gerar /rss.xml",
265
+ enabledHelp: "Quando ativo, é publicado um ficheiro RSS na raiz do site com os artigos online mais recentes.",
266
+ itemCount: "Itens no feed",
267
+ itemCountHelp: "Número de artigos mais recentes a incluir (1–100).",
268
+ excluded: "Categorias excluídas",
269
+ excludedHelp: "Os artigos das categorias marcadas são ignorados no feed do site. Deixa vazio para incluir tudo.",
270
+ addToFooter: "Mostrar no rodapé"
271
+ },
272
+ category: {
273
+ addLabel: "Adicionar um feed de categoria",
274
+ placeholder: "Pesquisar uma categoria…",
275
+ add: "Adicionar",
276
+ itemCount: "Itens",
277
+ addToFooter: "Mostrar no rodapé",
278
+ remove: "Remover",
279
+ pathHint: "Publicado em /{{path}}",
280
+ none: "Ainda não há feeds de categoria."
281
+ },
282
+ actions: {
283
+ save: "Guardar definições",
284
+ saving: "A guardar…",
285
+ saved: "Definições guardadas.",
286
+ uploadXsl: "Enviar folha de estilo",
287
+ uploadingXsl: "A enviar…",
288
+ xslUploaded: "Folha de estilo enviada.",
289
+ xslFailed: "Falha ao enviar folha de estilo.",
290
+ forceRegenerate: "Forçar regeneração de todos os feeds",
291
+ forceRegenerating: "A regenerar…",
292
+ regenerated: "Feeds regenerados ({{count}} ficheiros).",
293
+ regenerateFailed: "Falha na regeneração de feeds."
294
+ },
295
+ footerLabels: {
296
+ site: "RSS",
297
+ category: "RSS — {{name}}"
298
+ },
299
+ baseUrlMissing: "Define o URL público do site em Definições → Geral antes de regenerar feeds."
300
+ }, Ce = {
301
+ title: "RSS 피드",
302
+ description: "/rss.xml에 RSS 2.0 피드와 선택적 카테고리별 피드를 생성합니다. 각 피드는 사이트 푸터에 추가할 수 있습니다.",
303
+ regenerationTarget: {
304
+ label: "RSS 피드",
305
+ description: "사이트 피드 + 카테고리별 피드 + XSL 스타일시트 + 푸터 메뉴 새로고침"
306
+ },
307
+ sections: {
308
+ site: "사이트 피드",
309
+ categoryFeeds: "카테고리 피드",
310
+ actions: "작업"
311
+ },
312
+ site: {
313
+ enabled: "/rss.xml 생성",
314
+ enabledHelp: "활성화하면 사이트 루트에 가장 최근의 온라인 게시물을 나열하는 RSS 파일이 게시됩니다.",
315
+ itemCount: "피드 항목 수",
316
+ itemCountHelp: "포함할 최신 게시물 수 (1–100).",
317
+ excluded: "제외할 카테고리",
318
+ excludedHelp: "체크된 카테고리의 게시물은 사이트 피드에서 제외됩니다. 모두 포함하려면 비워 두세요.",
319
+ addToFooter: "푸터에 표시"
320
+ },
321
+ category: {
322
+ addLabel: "카테고리 피드 추가",
323
+ placeholder: "카테고리 검색…",
324
+ add: "추가",
325
+ itemCount: "항목 수",
326
+ addToFooter: "푸터에 표시",
327
+ remove: "제거",
328
+ pathHint: "/{{path}}에 게시됨",
329
+ none: "아직 카테고리 피드가 없습니다."
330
+ },
331
+ actions: {
332
+ save: "설정 저장",
333
+ saving: "저장 중…",
334
+ saved: "설정이 저장되었습니다.",
335
+ uploadXsl: "스타일시트 업로드",
336
+ uploadingXsl: "업로드 중…",
337
+ xslUploaded: "스타일시트가 업로드되었습니다.",
338
+ xslFailed: "스타일시트 업로드에 실패했습니다.",
339
+ forceRegenerate: "모든 피드 강제 재생성",
340
+ forceRegenerating: "재생성 중…",
341
+ regenerated: "피드가 재생성되었습니다 ({{count}}개 파일).",
342
+ regenerateFailed: "피드 재생성에 실패했습니다."
343
+ },
344
+ footerLabels: {
345
+ site: "RSS",
346
+ category: "RSS — {{name}}"
347
+ },
348
+ baseUrlMissing: "피드를 재생성하기 전에 설정 → 일반에서 공개 사이트 URL을 설정하세요."
349
+ }, q = "rss.xsl", ke = {
350
+ en: {
351
+ pageTitle: "RSS feed",
352
+ heading: "RSS feed",
353
+ meta: "This RSS feed is meant for RSS readers. Subscribe by copying this page's URL into your reader.",
354
+ countBefore: "This feed contains ",
355
+ countAfter: " articles.",
356
+ columnTitle: "Title",
357
+ columnPubdate: "Published",
358
+ columnDescription: "Description"
359
+ },
360
+ fr: {
361
+ pageTitle: "Flux RSS",
362
+ heading: "Flux RSS",
363
+ meta: "Ce flux RSS est destiné aux lecteurs RSS. Abonnez-vous en copiant l'URL de cette page dans votre lecteur.",
364
+ countBefore: "Ce flux contient ",
365
+ countAfter: " articles.",
366
+ columnTitle: "Titre",
367
+ columnPubdate: "Publié",
368
+ columnDescription: "Description"
369
+ },
370
+ de: {
371
+ pageTitle: "RSS-Feed",
372
+ heading: "RSS-Feed",
373
+ meta: "Dieser RSS-Feed ist für RSS-Reader gedacht. Abonnieren Sie ihn, indem Sie die URL dieser Seite in Ihren Reader kopieren.",
374
+ countBefore: "Dieser Feed enthält ",
375
+ countAfter: " Artikel.",
376
+ columnTitle: "Titel",
377
+ columnPubdate: "Veröffentlicht",
378
+ columnDescription: "Beschreibung"
379
+ },
380
+ es: {
381
+ pageTitle: "Feed RSS",
382
+ heading: "Feed RSS",
383
+ meta: "Este feed RSS está destinado a los lectores RSS. Suscríbete copiando la URL de esta página en tu lector.",
384
+ countBefore: "Este feed contiene ",
385
+ countAfter: " artículos.",
386
+ columnTitle: "Título",
387
+ columnPubdate: "Publicado",
388
+ columnDescription: "Descripción"
389
+ },
390
+ nl: {
391
+ pageTitle: "RSS-feed",
392
+ heading: "RSS-feed",
393
+ meta: "Deze RSS-feed is bedoeld voor RSS-lezers. Abonneer door de URL van deze pagina in je lezer te kopiëren.",
394
+ countBefore: "Deze feed bevat ",
395
+ countAfter: " artikelen.",
396
+ columnTitle: "Titel",
397
+ columnPubdate: "Gepubliceerd",
398
+ columnDescription: "Beschrijving"
399
+ },
400
+ pt: {
401
+ pageTitle: "Feed RSS",
402
+ heading: "Feed RSS",
403
+ meta: "Este feed RSS destina-se a leitores RSS. Subscreve copiando o URL desta página para o teu leitor.",
404
+ countBefore: "Este feed contém ",
405
+ countAfter: " artigos.",
406
+ columnTitle: "Título",
407
+ columnPubdate: "Publicado",
408
+ columnDescription: "Descrição"
409
+ },
410
+ ko: {
411
+ pageTitle: "RSS 피드",
412
+ heading: "RSS 피드",
413
+ meta: "이 RSS 피드는 RSS 리더를 위한 것입니다. 이 페이지의 URL을 리더에 복사하여 구독하세요.",
414
+ countBefore: "이 피드에는 ",
415
+ countAfter: "개의 기사가 포함되어 있습니다.",
416
+ columnTitle: "제목",
417
+ columnPubdate: "게시일",
418
+ columnDescription: "설명"
419
+ }
420
+ };
421
+ function Te(e) {
422
+ return ce(e);
423
+ }
424
+ const Pe = `
425
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; color: #1f2937; margin: 0; padding: 32px 16px; background: #ffffff; }
426
+ main { max-width: 1024px; margin: 0 auto; }
427
+ h1 { font-size: 22px; margin: 0 0 8px; font-weight: 600; }
428
+ .meta { color: #6b7280; font-size: 13px; margin: 0 0 24px; }
429
+ .count { color: #6b7280; font-size: 13px; margin: 0 0 16px; }
430
+ table { width: 100%; border-collapse: collapse; font-size: 13px; }
431
+ th { text-align: left; padding: 10px 12px; background: #f3f4f6; border-bottom: 1px solid #e5e7eb; font-weight: 600; color: #1f2937; }
432
+ td { padding: 10px 12px; border-bottom: 1px solid #f3f4f6; vertical-align: top; }
433
+ tr:nth-child(even) td { background: #fafafa; }
434
+ tr:hover td { background: #f3f4f6; }
435
+ a { color: #2563eb; text-decoration: none; }
436
+ a:hover { text-decoration: underline; }
437
+ .title { width: 40%; }
438
+ .pubdate { white-space: nowrap; color: #4b5563; width: 200px; }
439
+ .description { color: #4b5563; }
440
+ `;
441
+ function Le(e) {
442
+ const a = Te(e), t = ke[a];
443
+ return `<?xml version="1.0" encoding="UTF-8"?>
444
+ <xsl:stylesheet version="1.0"
445
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
446
+ xmlns:atom="http://www.w3.org/2005/Atom">
447
+ <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"
448
+ doctype-system="about:legacy-compat"/>
449
+ <xsl:template match="/">
450
+ <html lang="${a}">
451
+ <head>
452
+ <title>${t.pageTitle} — <xsl:value-of select="/rss/channel/title"/></title>
453
+ <meta charset="utf-8"/>
454
+ <meta name="robots" content="noindex,follow"/>
455
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
456
+ <style type="text/css">${Pe}</style>
457
+ </head>
458
+ <body>
459
+ <main>
460
+ <h1>${t.heading} — <xsl:value-of select="/rss/channel/title"/></h1>
461
+ <p class="meta">${t.meta}</p>
462
+ <p class="count">${t.countBefore}<xsl:value-of select="count(/rss/channel/item)"/>${t.countAfter}</p>
463
+ <table>
464
+ <thead>
465
+ <tr>
466
+ <th class="title">${t.columnTitle}</th>
467
+ <th class="pubdate">${t.columnPubdate}</th>
468
+ <th class="description">${t.columnDescription}</th>
469
+ </tr>
470
+ </thead>
471
+ <tbody>
472
+ <xsl:for-each select="/rss/channel/item">
473
+ <tr>
474
+ <td class="title"><a href="{link}"><xsl:value-of select="title"/></a></td>
475
+ <td class="pubdate"><xsl:value-of select="pubDate"/></td>
476
+ <td class="description"><xsl:value-of select="description"/></td>
477
+ </tr>
478
+ </xsl:for-each>
479
+ </tbody>
480
+ </table>
481
+ </main>
482
+ </body>
483
+ </html>
484
+ </xsl:template>
485
+ </xsl:stylesheet>`;
486
+ }
487
+ const w = "rss.xml", _ = {
488
+ site: {
489
+ enabled: !1,
490
+ itemCount: 20,
491
+ excludedTermIds: [],
492
+ addToFooter: !1
493
+ },
494
+ categoryFeeds: []
495
+ };
496
+ function D(e) {
497
+ return `${e}/${e}.xml`;
498
+ }
499
+ function Ne(e, a) {
500
+ if (!e.heroMediaId) return null;
501
+ const t = a.get(e.heroMediaId);
502
+ if (!t) return null;
503
+ if (t.formats && Object.keys(t.formats).length > 0) {
504
+ const n = t.formats.large ?? (t.defaultFormat ? t.formats[t.defaultFormat] : void 0) ?? Object.values(t.formats)[0];
505
+ if (n != null && n.url)
506
+ return {
507
+ url: n.url,
508
+ length: n.bytes ?? 0,
509
+ type: t.contentType || G(n.url)
510
+ };
511
+ }
512
+ return t.url ? {
513
+ url: t.url,
514
+ length: t.size ?? 0,
515
+ type: t.contentType || G(t.url)
516
+ } : null;
517
+ }
518
+ function G(e) {
519
+ const a = e.toLowerCase();
520
+ return a.endsWith(".webp") ? "image/webp" : a.endsWith(".png") ? "image/png" : a.endsWith(".gif") ? "image/gif" : a.endsWith(".avif") ? "image/avif" : (a.endsWith(".jpg") || a.endsWith(".jpeg"), "image/jpeg");
521
+ }
522
+ function J(e, a, t, n) {
523
+ var c, m, b, C, y, k;
524
+ const s = e.primaryTermId ? a.find((P) => P.id === e.primaryTermId && P.type === "category") : void 0;
525
+ let i;
526
+ try {
527
+ i = ge({ post: e, primaryTerm: s });
528
+ } catch {
529
+ return null;
530
+ }
531
+ const d = S(n, i), r = (e.excerpt && e.excerpt.trim().length > 0 ? e.excerpt : pe(e.contentMarkdown, 300)).trim(), p = ((m = (c = e.publishedAt) == null ? void 0 : c.toMillis) == null ? void 0 : m.call(c)) ?? ((C = (b = e.updatedAt) == null ? void 0 : b.toMillis) == null ? void 0 : C.call(b)) ?? ((k = (y = e.createdAt) == null ? void 0 : y.toMillis) == null ? void 0 : k.call(y)) ?? Date.now();
532
+ return {
533
+ title: e.title || e.slug,
534
+ link: d,
535
+ guid: d,
536
+ description: r,
537
+ pubDateMs: p,
538
+ category: s == null ? void 0 : s.name,
539
+ enclosure: Ne(e, t) ?? void 0
540
+ };
541
+ }
542
+ function Ue(e, a, t, n, s) {
543
+ const i = new Set(n.site.excludedTermIds);
544
+ return e.filter((d) => d.status === "online").filter((d) => !d.primaryTermId || !i.has(d.primaryTermId)).map((d) => J(d, a, t, s)).filter((d) => d !== null).sort((d, r) => r.pubDateMs - d.pubDateMs).slice(0, Math.max(1, Math.min(100, n.site.itemCount)));
545
+ }
546
+ function Ae(e, a, t, n, s) {
547
+ return e.filter((i) => i.status === "online" && i.primaryTermId === n.termId).map((i) => J(i, a, t, s)).filter((i) => i !== null).sort((i, d) => d.pubDateMs - i.pubDateMs).slice(0, Math.max(1, Math.min(100, n.itemCount)));
548
+ }
549
+ function Me(e) {
550
+ return e instanceof Map ? e : new Map(e.map((a) => [a.id, a]));
551
+ }
552
+ function Q(e) {
553
+ const a = (e.settings.baseUrl || "").replace(/\/+$/, "");
554
+ if (!a) throw new Error("baseUrl is empty — cannot build feed URLs.");
555
+ return {
556
+ posts: e.posts,
557
+ terms: e.terms,
558
+ mediaMap: Me(e.media),
559
+ settings: e.settings,
560
+ baseUrl: a,
561
+ xslHref: S(a, q)
562
+ };
563
+ }
564
+ async function Z(e, a) {
565
+ const t = [], n = [];
566
+ if (!a.site.enabled) {
567
+ if (a.site.lastPublishedPath)
568
+ try {
569
+ await v(a.site.lastPublishedPath), n.push(a.site.lastPublishedPath);
570
+ } catch {
571
+ }
572
+ for (const c of a.site.lastLocalePaths ?? [])
573
+ try {
574
+ await v(c), n.push(c);
575
+ } catch {
576
+ }
577
+ return {
578
+ uploaded: t,
579
+ deleted: n,
580
+ nextSite: { ...a.site, lastPublishedPath: void 0, lastLocalePaths: [] }
581
+ };
582
+ }
583
+ const s = Ue(e.posts, e.terms, e.mediaMap, a, e.baseUrl), i = I({
584
+ title: e.settings.title || "Site",
585
+ link: S(e.baseUrl, ""),
586
+ description: e.settings.description || e.settings.title || "",
587
+ feedUrl: S(e.baseUrl, w),
588
+ language: e.settings.language || "en",
589
+ items: s,
590
+ xslHref: e.xslHref
591
+ });
592
+ await U({ path: w, content: i }), t.push(w);
593
+ const d = await ue(
594
+ "rss.site.locales",
595
+ [],
596
+ {
597
+ posts: e.posts,
598
+ terms: e.terms,
599
+ mediaMap: e.mediaMap,
600
+ settings: e.settings,
601
+ config: a,
602
+ baseUrl: e.baseUrl,
603
+ xslHref: e.xslHref
604
+ }
605
+ ), r = [];
606
+ for (const c of d) {
607
+ if (!c.path || !c.language) continue;
608
+ const m = I({
609
+ title: c.channelTitle,
610
+ link: c.channelLink,
611
+ description: c.channelDescription,
612
+ feedUrl: S(e.baseUrl, c.path),
613
+ language: c.language,
614
+ items: c.items,
615
+ xslHref: e.xslHref
616
+ });
617
+ await U({ path: c.path, content: m }), t.push(c.path), r.push(c.path);
618
+ }
619
+ const p = new Set(r);
620
+ for (const c of a.site.lastLocalePaths ?? [])
621
+ if (!p.has(c))
622
+ try {
623
+ await v(c), n.push(c);
624
+ } catch {
625
+ }
626
+ return {
627
+ uploaded: t,
628
+ deleted: n,
629
+ nextSite: {
630
+ ...a.site,
631
+ lastPublishedPath: w,
632
+ lastLocalePaths: r
633
+ }
634
+ };
635
+ }
636
+ async function Y(e, a) {
637
+ const t = [], n = [], s = e.terms.find((m) => m.id === a.termId && m.type === "category");
638
+ if (!s) {
639
+ if (a.lastPublishedPath)
640
+ try {
641
+ await v(a.lastPublishedPath), n.push(a.lastPublishedPath);
642
+ } catch {
643
+ }
644
+ return { uploaded: t, deleted: n, nextFeed: null };
645
+ }
646
+ const i = D(s.slug);
647
+ if (a.lastPublishedPath && a.lastPublishedPath !== i)
648
+ try {
649
+ await v(a.lastPublishedPath), n.push(a.lastPublishedPath);
650
+ } catch {
651
+ }
652
+ const d = Ae(e.posts, e.terms, e.mediaMap, a, e.baseUrl), r = S(e.baseUrl, O(s)), p = `${e.settings.title || "Site"} — ${s.name}`, c = I({
653
+ title: p,
654
+ link: r,
655
+ description: s.description || s.name,
656
+ feedUrl: S(e.baseUrl, i),
657
+ language: e.settings.language || "en",
658
+ items: d,
659
+ xslHref: e.xslHref
660
+ });
661
+ return await U({ path: i, content: c }), t.push(i), {
662
+ uploaded: t,
663
+ deleted: n,
664
+ nextFeed: { ...a, lastPublishedPath: i }
665
+ };
666
+ }
667
+ async function ee(e) {
668
+ const a = Q(e), t = [], n = [], s = await Z(a, e.config);
669
+ t.push(...s.uploaded), n.push(...s.deleted);
670
+ const i = [];
671
+ for (const d of e.config.categoryFeeds) {
672
+ const r = await Y(a, d);
673
+ t.push(...r.uploaded), n.push(...r.deleted), r.nextFeed && i.push(r.nextFeed);
674
+ }
675
+ return {
676
+ result: { uploaded: t, deleted: n },
677
+ nextConfig: { site: s.nextSite, categoryFeeds: i }
678
+ };
679
+ }
680
+ async function Ie(e) {
681
+ const a = Q(e), t = [], n = [];
682
+ let s = e.config.site;
683
+ const i = [...e.config.categoryFeeds];
684
+ if (e.config.site.enabled) {
685
+ const d = new Set(e.config.site.excludedTermIds);
686
+ if (!e.post.primaryTermId || !d.has(e.post.primaryTermId)) {
687
+ const r = await Z(a, e.config);
688
+ t.push(...r.uploaded), n.push(...r.deleted), s = r.nextSite;
689
+ }
690
+ }
691
+ if (e.post.primaryTermId) {
692
+ const d = i.findIndex((r) => r.termId === e.post.primaryTermId);
693
+ if (d >= 0) {
694
+ const r = await Y(a, i[d]);
695
+ t.push(...r.uploaded), n.push(...r.deleted), r.nextFeed ? i[d] = r.nextFeed : i.splice(d, 1);
696
+ }
697
+ }
698
+ return {
699
+ result: { uploaded: t, deleted: n },
700
+ nextConfig: { site: s, categoryFeeds: i }
701
+ };
702
+ }
703
+ async function E(e) {
704
+ await U({
705
+ path: q,
706
+ content: Le(e.settings.language)
707
+ });
708
+ }
709
+ async function W(e) {
710
+ const a = [];
711
+ let t = e.nextConfig.site;
712
+ if (!e.nextConfig.site.enabled) {
713
+ const s = e.prevConfig.site.lastPublishedPath ?? w;
714
+ try {
715
+ await v(s), a.push(s);
716
+ } catch {
717
+ }
718
+ t = { ...t, lastPublishedPath: void 0 };
719
+ }
720
+ const n = new Set(e.nextConfig.categoryFeeds.map((s) => s.termId));
721
+ for (const s of e.prevConfig.categoryFeeds)
722
+ if (!n.has(s.termId) && s.lastPublishedPath)
723
+ try {
724
+ await v(s.lastPublishedPath), a.push(s.lastPublishedPath);
725
+ } catch {
726
+ }
727
+ return {
728
+ deleted: a,
729
+ nextConfig: { site: t, categoryFeeds: e.nextConfig.categoryFeeds }
730
+ };
731
+ }
732
+ /**
733
+ * @license lucide-react v0.460.0 - ISC
734
+ *
735
+ * This source code is licensed under the ISC license.
736
+ * See the LICENSE file in the root directory of this source tree.
737
+ */
738
+ const He = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), te = (...e) => e.filter((a, t, n) => !!a && a.trim() !== "" && n.indexOf(a) === t).join(" ").trim();
739
+ /**
740
+ * @license lucide-react v0.460.0 - ISC
741
+ *
742
+ * This source code is licensed under the ISC license.
743
+ * See the LICENSE file in the root directory of this source tree.
744
+ */
745
+ var ze = {
746
+ xmlns: "http://www.w3.org/2000/svg",
747
+ width: 24,
748
+ height: 24,
749
+ viewBox: "0 0 24 24",
750
+ fill: "none",
751
+ stroke: "currentColor",
752
+ strokeWidth: 2,
753
+ strokeLinecap: "round",
754
+ strokeLinejoin: "round"
755
+ };
756
+ /**
757
+ * @license lucide-react v0.460.0 - ISC
758
+ *
759
+ * This source code is licensed under the ISC license.
760
+ * See the LICENSE file in the root directory of this source tree.
761
+ */
762
+ const Ee = K(
763
+ ({
764
+ color: e = "currentColor",
765
+ size: a = 24,
766
+ strokeWidth: t = 2,
767
+ absoluteStrokeWidth: n,
768
+ className: s = "",
769
+ children: i,
770
+ iconNode: d,
771
+ ...r
772
+ }, p) => z(
773
+ "svg",
774
+ {
775
+ ref: p,
776
+ ...ze,
777
+ width: a,
778
+ height: a,
779
+ stroke: e,
780
+ strokeWidth: n ? Number(t) * 24 / Number(a) : t,
781
+ className: te("lucide", s),
782
+ ...r
783
+ },
784
+ [
785
+ ...d.map(([c, m]) => z(c, m)),
786
+ ...Array.isArray(i) ? i : [i]
787
+ ]
788
+ )
789
+ );
790
+ /**
791
+ * @license lucide-react v0.460.0 - ISC
792
+ *
793
+ * This source code is licensed under the ISC license.
794
+ * See the LICENSE file in the root directory of this source tree.
795
+ */
796
+ const R = (e, a) => {
797
+ const t = K(
798
+ ({ className: n, ...s }, i) => z(Ee, {
799
+ ref: i,
800
+ iconNode: a,
801
+ className: te(`lucide-${He(e)}`, n),
802
+ ...s
803
+ })
804
+ );
805
+ return t.displayName = `${e}`, t;
806
+ };
807
+ /**
808
+ * @license lucide-react v0.460.0 - ISC
809
+ *
810
+ * This source code is licensed under the ISC license.
811
+ * See the LICENSE file in the root directory of this source tree.
812
+ */
813
+ const De = R("FileCode2", [
814
+ ["path", { d: "M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4", key: "1pf5j1" }],
815
+ ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
816
+ ["path", { d: "m5 12-3 3 3 3", key: "oke12k" }],
817
+ ["path", { d: "m9 18 3-3-3-3", key: "112psh" }]
818
+ ]);
819
+ /**
820
+ * @license lucide-react v0.460.0 - ISC
821
+ *
822
+ * This source code is licensed under the ISC license.
823
+ * See the LICENSE file in the root directory of this source tree.
824
+ */
825
+ const A = R("LoaderCircle", [
826
+ ["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]
827
+ ]);
828
+ /**
829
+ * @license lucide-react v0.460.0 - ISC
830
+ *
831
+ * This source code is licensed under the ISC license.
832
+ * See the LICENSE file in the root directory of this source tree.
833
+ */
834
+ const je = R("Plus", [
835
+ ["path", { d: "M5 12h14", key: "1ays0h" }],
836
+ ["path", { d: "M12 5v14", key: "s699le" }]
837
+ ]);
838
+ /**
839
+ * @license lucide-react v0.460.0 - ISC
840
+ *
841
+ * This source code is licensed under the ISC license.
842
+ * See the LICENSE file in the root directory of this source tree.
843
+ */
844
+ const Xe = R("RefreshCw", [
845
+ ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
846
+ ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
847
+ ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
848
+ ["path", { d: "M8 16H3v5", key: "1cv678" }]
849
+ ]);
850
+ /**
851
+ * @license lucide-react v0.460.0 - ISC
852
+ *
853
+ * This source code is licensed under the ISC license.
854
+ * See the LICENSE file in the root directory of this source tree.
855
+ */
856
+ const $e = R("Save", [
857
+ [
858
+ "path",
859
+ {
860
+ d: "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",
861
+ key: "1c8476"
862
+ }
863
+ ],
864
+ ["path", { d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7", key: "1ydtos" }],
865
+ ["path", { d: "M7 3v4a1 1 0 0 0 1 1h7", key: "t51u73" }]
866
+ ]);
867
+ /**
868
+ * @license lucide-react v0.460.0 - ISC
869
+ *
870
+ * This source code is licensed under the ISC license.
871
+ * See the LICENSE file in the root directory of this source tree.
872
+ */
873
+ const Ge = R("Trash2", [
874
+ ["path", { d: "M3 6h18", key: "d0wm0j" }],
875
+ ["path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6", key: "4alrt4" }],
876
+ ["path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2", key: "v07s0e" }],
877
+ ["line", { x1: "10", x2: "10", y1: "11", y2: "17", key: "1uufr5" }],
878
+ ["line", { x1: "14", x2: "14", y1: "11", y2: "17", key: "xtxkd" }]
879
+ ]);
880
+ function We({ config: e, save: a }) {
881
+ const { t } = Se("flexweg-rss"), { settings: n, terms: s, categories: i, media: d } = he(), [r, p] = T(e);
882
+ be(() => p(e), [e]);
883
+ const [c, m] = T(!1), [b, C] = T(!1), [y, k] = T(!1), P = ye(() => {
884
+ const o = new Set(r.categoryFeeds.map((u) => u.termId));
885
+ return i.filter((u) => !o.has(u.id)).map((u) => ({ id: u.id, label: u.name, subtitle: `/${u.slug}` }));
886
+ }, [i, r.categoryFeeds]);
887
+ function L(o) {
888
+ p((u) => ({ ...u, site: { ...u.site, ...o } }));
889
+ }
890
+ function X(o, u) {
891
+ p((f) => ({
892
+ ...f,
893
+ categoryFeeds: f.categoryFeeds.map((h) => h.termId === o ? { ...h, ...u } : h)
894
+ }));
895
+ }
896
+ function ae(o) {
897
+ p((u) => ({
898
+ ...u,
899
+ categoryFeeds: u.categoryFeeds.filter((f) => f.termId !== o)
900
+ }));
901
+ }
902
+ function ne(o) {
903
+ o && (r.categoryFeeds.some((u) => u.termId === o) || p((u) => ({
904
+ ...u,
905
+ categoryFeeds: [...u.categoryFeeds, { termId: o, itemCount: 20, addToFooter: !1 }]
906
+ })));
907
+ }
908
+ function se(o) {
909
+ const u = new Set(r.site.excludedTermIds);
910
+ u.has(o) ? u.delete(o) : u.add(o), L({ excludedTermIds: [...u] });
911
+ }
912
+ async function ie() {
913
+ m(!0);
914
+ try {
915
+ const o = await W({ prevConfig: e, nextConfig: r });
916
+ await a(o.nextConfig);
917
+ const u = {
918
+ ...n,
919
+ pluginConfigs: { ...n.pluginConfigs, "flexweg-rss": o.nextConfig }
920
+ };
921
+ try {
922
+ const [f, h] = await Promise.all([
923
+ N({ type: "post" }),
924
+ N({ type: "page" })
925
+ ]);
926
+ await H(u, f, h, s);
927
+ } catch (f) {
928
+ console.error("[flexweg-rss] menu.json republish failed:", f);
929
+ }
930
+ x.success(t("actions.saved"));
931
+ } finally {
932
+ m(!1);
933
+ }
934
+ }
935
+ async function re() {
936
+ if (!n.baseUrl) {
937
+ x.error(t("baseUrlMissing"));
938
+ return;
939
+ }
940
+ C(!0);
941
+ try {
942
+ await E({ settings: n }), x.success(t("actions.xslUploaded"));
943
+ } catch (o) {
944
+ console.error("[flexweg-rss] xsl upload failed:", o), x.error(t("actions.xslFailed"));
945
+ } finally {
946
+ C(!1);
947
+ }
948
+ }
949
+ async function oe() {
950
+ if (!n.baseUrl) {
951
+ x.error(t("baseUrlMissing"));
952
+ return;
953
+ }
954
+ k(!0);
955
+ try {
956
+ const o = await W({ prevConfig: e, nextConfig: r });
957
+ await a(o.nextConfig), await E({ settings: n });
958
+ const [u, f] = await Promise.all([
959
+ N({ type: "post" }),
960
+ N({ type: "page" })
961
+ ]), h = await ee({
962
+ posts: u,
963
+ terms: s,
964
+ media: d,
965
+ settings: n,
966
+ config: o.nextConfig
967
+ });
968
+ await a(h.nextConfig);
969
+ const le = {
970
+ ...n,
971
+ pluginConfigs: { ...n.pluginConfigs, "flexweg-rss": h.nextConfig }
972
+ };
973
+ try {
974
+ await H(le, u, f, s);
975
+ } catch (de) {
976
+ console.error("[flexweg-rss] menu.json republish failed:", de);
977
+ }
978
+ x.success(
979
+ t("actions.regenerated", { count: h.result.uploaded.length + 1 })
980
+ );
981
+ } catch (o) {
982
+ console.error("[flexweg-rss] regeneration failed:", o), x.error(t("actions.regenerateFailed"));
983
+ } finally {
984
+ k(!1);
985
+ }
986
+ }
987
+ return /* @__PURE__ */ g("div", { className: "space-y-6", children: [
988
+ /* @__PURE__ */ l("p", { className: "text-sm text-surface-600 dark:text-surface-300", children: t("description") }),
989
+ /* @__PURE__ */ g("section", { className: "card p-4 space-y-4", children: [
990
+ /* @__PURE__ */ l("h2", { className: "font-semibold", children: t("sections.site") }),
991
+ /* @__PURE__ */ g("label", { className: "flex items-center gap-2", children: [
992
+ /* @__PURE__ */ l(
993
+ "input",
994
+ {
995
+ type: "checkbox",
996
+ checked: r.site.enabled,
997
+ onChange: (o) => L({ enabled: o.target.checked })
998
+ }
999
+ ),
1000
+ /* @__PURE__ */ l("span", { className: "text-sm font-medium", children: t("site.enabled") })
1001
+ ] }),
1002
+ /* @__PURE__ */ l("p", { className: "text-xs text-surface-500 dark:text-surface-400", children: t("site.enabledHelp") }),
1003
+ r.site.enabled && /* @__PURE__ */ g(me, { children: [
1004
+ /* @__PURE__ */ g("div", { children: [
1005
+ /* @__PURE__ */ l("label", { className: "label", children: t("site.itemCount") }),
1006
+ /* @__PURE__ */ l(
1007
+ "input",
1008
+ {
1009
+ type: "number",
1010
+ className: "input max-w-xs",
1011
+ min: 1,
1012
+ max: 100,
1013
+ value: r.site.itemCount,
1014
+ onChange: (o) => L({
1015
+ itemCount: Math.max(1, Math.min(100, Number.parseInt(o.target.value, 10) || 20))
1016
+ })
1017
+ }
1018
+ ),
1019
+ /* @__PURE__ */ l("p", { className: "text-xs text-surface-500 mt-1 dark:text-surface-400", children: t("site.itemCountHelp") })
1020
+ ] }),
1021
+ /* @__PURE__ */ g("div", { children: [
1022
+ /* @__PURE__ */ l("p", { className: "label", children: t("site.excluded") }),
1023
+ /* @__PURE__ */ l("p", { className: "text-xs text-surface-500 mb-2 dark:text-surface-400", children: t("site.excludedHelp") }),
1024
+ i.length === 0 ? /* @__PURE__ */ l("p", { className: "text-xs italic text-surface-500 dark:text-surface-400", children: t("category.none") }) : /* @__PURE__ */ l("ul", { className: "space-y-1", children: i.map((o) => /* @__PURE__ */ l("li", { children: /* @__PURE__ */ g("label", { className: "flex items-center gap-2", children: [
1025
+ /* @__PURE__ */ l(
1026
+ "input",
1027
+ {
1028
+ type: "checkbox",
1029
+ checked: r.site.excludedTermIds.includes(o.id),
1030
+ onChange: () => se(o.id)
1031
+ }
1032
+ ),
1033
+ /* @__PURE__ */ l("span", { className: "text-sm", children: o.name }),
1034
+ /* @__PURE__ */ g("span", { className: "text-xs text-surface-400", children: [
1035
+ "/",
1036
+ o.slug
1037
+ ] })
1038
+ ] }) }, o.id)) })
1039
+ ] }),
1040
+ /* @__PURE__ */ g("label", { className: "flex items-center gap-2 pt-2 border-t border-surface-200 dark:border-surface-700", children: [
1041
+ /* @__PURE__ */ l(
1042
+ "input",
1043
+ {
1044
+ type: "checkbox",
1045
+ checked: r.site.addToFooter,
1046
+ onChange: (o) => L({ addToFooter: o.target.checked })
1047
+ }
1048
+ ),
1049
+ /* @__PURE__ */ l("span", { className: "text-sm font-medium", children: t("site.addToFooter") })
1050
+ ] })
1051
+ ] })
1052
+ ] }),
1053
+ /* @__PURE__ */ g("section", { className: "card p-4 space-y-4", children: [
1054
+ /* @__PURE__ */ l("h2", { className: "font-semibold", children: t("sections.categoryFeeds") }),
1055
+ r.categoryFeeds.length === 0 ? /* @__PURE__ */ l("p", { className: "text-sm italic text-surface-500 dark:text-surface-400", children: t("category.none") }) : /* @__PURE__ */ l("ul", { className: "space-y-3", children: r.categoryFeeds.map((o) => {
1056
+ const u = i.find((h) => h.id === o.termId), f = u ? D(u.slug) : o.lastPublishedPath ?? "";
1057
+ return /* @__PURE__ */ g(
1058
+ "li",
1059
+ {
1060
+ className: "border border-surface-200 dark:border-surface-700 rounded-lg p-3 space-y-2",
1061
+ children: [
1062
+ /* @__PURE__ */ g("div", { className: "flex items-center justify-between gap-2", children: [
1063
+ /* @__PURE__ */ g("div", { className: "min-w-0", children: [
1064
+ /* @__PURE__ */ l("p", { className: "font-medium", children: u ? u.name : `(missing: ${o.termId})` }),
1065
+ f && /* @__PURE__ */ l("p", { className: "text-xs text-surface-500 dark:text-surface-400", children: t("category.pathHint", { path: f }) })
1066
+ ] }),
1067
+ /* @__PURE__ */ g(
1068
+ "button",
1069
+ {
1070
+ type: "button",
1071
+ className: "btn-ghost",
1072
+ onClick: () => ae(o.termId),
1073
+ children: [
1074
+ /* @__PURE__ */ l(Ge, { className: "h-4 w-4" }),
1075
+ t("category.remove")
1076
+ ]
1077
+ }
1078
+ )
1079
+ ] }),
1080
+ /* @__PURE__ */ g("div", { className: "flex flex-wrap items-center gap-3", children: [
1081
+ /* @__PURE__ */ g("label", { className: "flex items-center gap-2 text-sm", children: [
1082
+ /* @__PURE__ */ l("span", { children: t("category.itemCount") }),
1083
+ /* @__PURE__ */ l(
1084
+ "input",
1085
+ {
1086
+ type: "number",
1087
+ className: "input w-24",
1088
+ min: 1,
1089
+ max: 100,
1090
+ value: o.itemCount,
1091
+ onChange: (h) => X(o.termId, {
1092
+ itemCount: Math.max(
1093
+ 1,
1094
+ Math.min(100, Number.parseInt(h.target.value, 10) || 20)
1095
+ )
1096
+ })
1097
+ }
1098
+ )
1099
+ ] }),
1100
+ /* @__PURE__ */ g("label", { className: "flex items-center gap-2 text-sm", children: [
1101
+ /* @__PURE__ */ l(
1102
+ "input",
1103
+ {
1104
+ type: "checkbox",
1105
+ checked: o.addToFooter,
1106
+ onChange: (h) => X(o.termId, { addToFooter: h.target.checked })
1107
+ }
1108
+ ),
1109
+ /* @__PURE__ */ l("span", { children: t("category.addToFooter") })
1110
+ ] })
1111
+ ] })
1112
+ ]
1113
+ },
1114
+ o.termId
1115
+ );
1116
+ }) }),
1117
+ /* @__PURE__ */ g("div", { className: "pt-2 border-t border-surface-200 dark:border-surface-700", children: [
1118
+ /* @__PURE__ */ l("p", { className: "label", children: t("category.addLabel") }),
1119
+ /* @__PURE__ */ l(
1120
+ Oe,
1121
+ {
1122
+ options: P,
1123
+ placeholder: t("category.placeholder"),
1124
+ addLabel: t("category.add"),
1125
+ onAdd: ne
1126
+ }
1127
+ )
1128
+ ] })
1129
+ ] }),
1130
+ /* @__PURE__ */ g("section", { className: "card p-4 space-y-3", children: [
1131
+ /* @__PURE__ */ l("h2", { className: "font-semibold", children: t("sections.actions") }),
1132
+ /* @__PURE__ */ g("div", { className: "flex flex-wrap gap-2", children: [
1133
+ /* @__PURE__ */ g("button", { type: "button", className: "btn-primary", onClick: ie, disabled: c, children: [
1134
+ /* @__PURE__ */ l(A, { className: c ? "h-4 w-4 animate-spin" : "hidden" }),
1135
+ /* @__PURE__ */ l($e, { className: c ? "hidden" : "h-4 w-4" }),
1136
+ /* @__PURE__ */ l("span", { children: t(c ? "actions.saving" : "actions.save") })
1137
+ ] }),
1138
+ /* @__PURE__ */ l(
1139
+ "button",
1140
+ {
1141
+ type: "button",
1142
+ className: "btn-ghost",
1143
+ onClick: re,
1144
+ disabled: b,
1145
+ children: /* @__PURE__ */ g("span", { className: "inline-flex items-center justify-center gap-1.5", children: [
1146
+ /* @__PURE__ */ l(A, { className: "h-4 w-4 animate-spin " + (b ? "" : "hidden") }),
1147
+ /* @__PURE__ */ l(De, { className: "h-4 w-4 " + (b ? "hidden" : "") }),
1148
+ /* @__PURE__ */ l("span", { children: t(b ? "actions.uploadingXsl" : "actions.uploadXsl") })
1149
+ ] })
1150
+ }
1151
+ ),
1152
+ /* @__PURE__ */ l(
1153
+ "button",
1154
+ {
1155
+ type: "button",
1156
+ className: "btn-secondary",
1157
+ onClick: oe,
1158
+ disabled: y,
1159
+ children: /* @__PURE__ */ g("span", { className: "inline-flex items-center justify-center gap-1.5", children: [
1160
+ /* @__PURE__ */ l(A, { className: "h-4 w-4 animate-spin " + (y ? "" : "hidden") }),
1161
+ /* @__PURE__ */ l(Xe, { className: "h-4 w-4 " + (y ? "hidden" : "") }),
1162
+ /* @__PURE__ */ l("span", { children: t(y ? "actions.forceRegenerating" : "actions.forceRegenerate") })
1163
+ ] })
1164
+ }
1165
+ )
1166
+ ] })
1167
+ ] })
1168
+ ] });
1169
+ }
1170
+ function Oe({
1171
+ options: e,
1172
+ placeholder: a,
1173
+ addLabel: t,
1174
+ onAdd: n
1175
+ }) {
1176
+ const [s, i] = T(null);
1177
+ return /* @__PURE__ */ g("div", { className: "flex flex-wrap items-start gap-2", children: [
1178
+ /* @__PURE__ */ l(
1179
+ fe,
1180
+ {
1181
+ className: "flex-1 min-w-[260px]",
1182
+ options: e,
1183
+ value: s,
1184
+ onSelect: i,
1185
+ placeholder: a
1186
+ }
1187
+ ),
1188
+ /* @__PURE__ */ g(
1189
+ "button",
1190
+ {
1191
+ type: "button",
1192
+ className: "btn-secondary",
1193
+ disabled: !s,
1194
+ onClick: () => {
1195
+ s && (n(s), i(null));
1196
+ },
1197
+ children: [
1198
+ /* @__PURE__ */ l(je, { className: "h-4 w-4" }),
1199
+ t
1200
+ ]
1201
+ }
1202
+ )
1203
+ ] });
1204
+ }
1205
+ const Be = `# Flexweg RSS
1206
+
1207
+ Publishes RSS 2.0 feeds so readers can follow your site in any feed reader (Feedly, NetNewsWire, Inoreader, …).
1208
+
1209
+ ## What it does
1210
+
1211
+ Generates and maintains:
1212
+
1213
+ - **A site-wide feed** at \`/rss.xml\` — every published post in reverse chronological order, capped at the most recent 50 entries.
1214
+ - **Optional per-category feeds** at \`/<category-slug>/<category-slug>.xml\` — each contains only posts whose primary category matches.
1215
+
1216
+ Each feed is served alongside an XSL stylesheet so opening the URL in a browser shows a friendly preview rather than raw XML. Posts with a hero image get an \`<enclosure>\` element so feed readers display the artwork.
1217
+
1218
+ Item descriptions use the post's excerpt when set, otherwise an automatic plain-text summary truncated to ~300 characters.
1219
+
1220
+ The feeds re-publish only when an affected post changes — not on every publish action elsewhere on the site. A category feed only regenerates when one of its own posts is added/removed/edited.
1221
+
1222
+ ## Settings
1223
+
1224
+ Reachable via **Plugins → Configure**.
1225
+
1226
+ - **Excluded categories** — pick categories whose posts shouldn't appear in the **site-wide** feed (they still appear in their own category feed). Useful for "drafts in public" or admin-only categories.
1227
+ - **Per-category feeds** — pick which categories get their own feed. Each entry has an *Add to footer menu* checkbox: when enabled, the footer auto-grows a link to the feed (no MenusPage edit needed).
1228
+ - **Force regenerate** — rebuilds every active feed from scratch. Run this after a bulk content reorganization or when a feed seems out of sync.
1229
+
1230
+ ## Footer integration
1231
+
1232
+ Feeds with *Add to footer* checked appear at the end of the existing footer menu items. Each entry's label comes from the plugin (RSS for the site feed, "RSS — &lt;category&gt;" for category feeds). To use a custom label or a different position, leave the checkbox unchecked and add the URL by hand in **Menus** → Footer.
1233
+
1234
+ ## Requirements
1235
+
1236
+ \`Settings → Site → Public site URL\` must be set — RSS feed URLs require an absolute origin. Empty base URL = silent no-op, same as the sitemap plugin.
1237
+
1238
+ ## When to disable it
1239
+
1240
+ Disable if your site doesn't have a blog cadence (RSS only makes sense for chronologically updated content), or if you're publishing through a separate platform (Substack, Medium, …) that already provides feeds.
1241
+ `, F = "flexweg-rss";
1242
+ function j(e) {
1243
+ const a = (e.pluginConfigs ?? {})[F];
1244
+ return { ..._, ...a ?? {} };
1245
+ }
1246
+ async function M(e, a) {
1247
+ if (!a.settings.baseUrl) {
1248
+ console.warn("[flexweg-rss] skipping regeneration — baseUrl not configured");
1249
+ return;
1250
+ }
1251
+ const t = j(a.settings);
1252
+ if (!(!t.site.enabled && t.categoryFeeds.length === 0))
1253
+ try {
1254
+ const n = await Ie({
1255
+ post: e,
1256
+ posts: a.posts,
1257
+ terms: a.terms,
1258
+ media: a.media,
1259
+ settings: a.settings,
1260
+ config: t
1261
+ });
1262
+ JSON.stringify(n.nextConfig) !== JSON.stringify(t) && await B(F, n.nextConfig);
1263
+ } catch (n) {
1264
+ console.error("[flexweg-rss] regeneration failed:", n);
1265
+ }
1266
+ }
1267
+ function Ke(e, a) {
1268
+ const t = j(a.settings), n = (a.settings.baseUrl || "").replace(/\/+$/, "");
1269
+ if (!n) return e;
1270
+ const s = [], i = $.getResource($.language, F, "footerLabels") ?? V.footerLabels;
1271
+ t.site.enabled && t.site.addToFooter && s.push({
1272
+ id: "rss-site",
1273
+ label: i.site,
1274
+ href: S(n, w)
1275
+ });
1276
+ for (const d of t.categoryFeeds) {
1277
+ if (!d.addToFooter) continue;
1278
+ const r = a.terms.find((c) => c.id === d.termId && c.type === "category");
1279
+ if (!r) continue;
1280
+ const p = S(n, D(r.slug));
1281
+ O(r), s.push({
1282
+ id: `rss-cat-${r.id}`,
1283
+ // i18next interpolation is only available through t(), and at this
1284
+ // point we don't hold a t() instance — so a tiny manual replace
1285
+ // keeps the dependency footprint flat.
1286
+ label: i.category.replace("{{name}}", r.name),
1287
+ href: p
1288
+ });
1289
+ }
1290
+ return s.length === 0 ? e : { ...e, footer: [...e.footer, ...s] };
1291
+ }
1292
+ const Qe = {
1293
+ id: F,
1294
+ name: "Flexweg RSS",
1295
+ version: "1.0.0",
1296
+ author: "Flexweg",
1297
+ description: "Generates an RSS 2.0 feed at /rss.xml plus optional per-category feeds, and can list them in the site footer.",
1298
+ readme: Be,
1299
+ i18n: { en: V, fr: xe, de: Fe, es: ve, nl: we, pt: Re, ko: Ce },
1300
+ settings: {
1301
+ navLabelKey: "title",
1302
+ defaultConfig: _,
1303
+ component: We
1304
+ },
1305
+ register(e) {
1306
+ e.addAction("publish.complete", async (a, t) => {
1307
+ await M(a, t);
1308
+ }), e.addAction("post.unpublished", async (a, t) => {
1309
+ await M(a, t);
1310
+ }), e.addAction("post.deleted", async (a, t) => {
1311
+ await M(a, t);
1312
+ }), e.addFilter("menu.json.resolved", (a, ...t) => {
1313
+ const n = t[0];
1314
+ return n ? Ke(a, n) : a;
1315
+ }), e.registerRegenerationTarget({
1316
+ id: F,
1317
+ labelKey: "regenerationTarget.label",
1318
+ descriptionKey: "regenerationTarget.description",
1319
+ priority: 210,
1320
+ run: async (a, t) => {
1321
+ if (!a.settings.baseUrl) {
1322
+ t({ level: "warn", message: "[flexweg-rss] skipped — site URL not set." });
1323
+ return;
1324
+ }
1325
+ const n = j(a.settings);
1326
+ t({ level: "info", message: "Regenerating RSS stylesheet…" }), await E({ settings: a.settings }), t({ level: "info", message: "Regenerating RSS feeds…" });
1327
+ const s = await ee({
1328
+ posts: a.posts,
1329
+ terms: a.terms,
1330
+ media: a.media,
1331
+ settings: a.settings,
1332
+ config: n
1333
+ });
1334
+ JSON.stringify(s.nextConfig) !== JSON.stringify(n) && await B(F, s.nextConfig);
1335
+ try {
1336
+ const i = {
1337
+ ...a.settings,
1338
+ pluginConfigs: { ...a.settings.pluginConfigs, [F]: s.nextConfig }
1339
+ };
1340
+ await H(i, a.posts, a.pages, a.terms);
1341
+ } catch (i) {
1342
+ t({ level: "warn", message: `menu.json republish failed: ${i.message}` });
1343
+ }
1344
+ t({
1345
+ level: "success",
1346
+ message: `RSS: ${s.result.uploaded.length + 1} file(s) uploaded.`
1347
+ });
1348
+ }
1349
+ });
1350
+ }
1351
+ };
1352
+ export {
1353
+ Qe as manifest
1354
+ };