@rmdes/indiekit-endpoint-site-config 1.0.0-beta.2 → 1.0.0-beta.3

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.
package/index.js CHANGED
@@ -7,7 +7,6 @@ import { brandingRouter } from "./lib/controllers/branding.js";
7
7
  import { homepageRouter } from "./lib/controllers/homepage.js";
8
8
  import { blogRouter } from "./lib/controllers/blog.js";
9
9
  import { navigationRouter } from "./lib/controllers/navigation.js";
10
- import { featuresRouter } from "./lib/controllers/features.js";
11
10
  import { apiRouter } from "./lib/controllers/api.js";
12
11
 
13
12
  import { getSiteConfig } from "./lib/storage/get-site-config.js";
@@ -78,7 +77,6 @@ export default class SiteConfigEndpoint {
78
77
  protectedRouter.use("/homepage", homepageRouter(Indiekit));
79
78
  protectedRouter.use("/blog", blogRouter(Indiekit));
80
79
  protectedRouter.use("/navigation", navigationRouter(Indiekit));
81
- protectedRouter.use("/features", featuresRouter(Indiekit));
82
80
 
83
81
  this.routes = protectedRouter;
84
82
 
@@ -47,7 +47,6 @@ export function renderSiteJson(config) {
47
47
  identity: config.identity,
48
48
  branding: config.branding,
49
49
  navigation: config.navigation,
50
- features: config.features,
51
50
  updatedAt: config.updatedAt,
52
51
  };
53
52
  const replacer = (key, value) => (PRIVATE_KEYS.has(key) ? undefined : value);
@@ -57,7 +57,7 @@ export async function maybeBackfillIdentity(Indiekit) {
57
57
  if (!homepageDoc || !hasRichIdentity(homepageDoc.identity)) return false;
58
58
 
59
59
  // Copy the rich identity over. saveSiteConfig handles the deepMerge + replaceOne
60
- // so we preserve any other siteConfig keys (branding, navigation, features).
60
+ // so we preserve any other siteConfig keys (branding, navigation).
61
61
  await saveSiteConfig(Indiekit, { identity: homepageDoc.identity }, "backfill-from-homepage");
62
62
 
63
63
  console.log("[site-config] backfilled identity from homepageConfig (one-time migration)");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Default values for siteConfig collection (identity, branding, navigation, features).
2
+ * Default values for siteConfig collection (identity, branding, navigation).
3
3
  * Singleton _id: "primary".
4
4
  *
5
5
  * Schema v3 (unification):
@@ -7,7 +7,8 @@
7
7
  * - layout subtree removed entirely (replaced by homepageConfig + navigation)
8
8
  * - navigation.items[] added (was siteConfig.layout.navItems[])
9
9
  * - branding subtree unchanged from v2 (Path D)
10
- * - features subtree unchanged from v2
10
+ * - features subtree DROPPED in v3.x (was unused; per-plugin config lives in
11
+ * each plugin's own admin UI, plugin loadout lives in plugins.yaml).
11
12
  *
12
13
  * Frozen for immutability — never mutate this object directly.
13
14
  * @module storage/defaults-site
@@ -77,13 +78,6 @@ export const DEFAULTS_SITE = Object.freeze({
77
78
  navigation: Object.freeze({
78
79
  items: Object.freeze([]),
79
80
  }),
80
- features: Object.freeze({
81
- webmentions: true,
82
- syndication: true,
83
- activitypub: false,
84
- search: true,
85
- rss: true,
86
- }),
87
81
  });
88
82
 
89
83
  // Legacy export name for any code path that hasn't been updated yet.
package/locales/en.json CHANGED
@@ -7,8 +7,7 @@
7
7
  "branding": "Branding",
8
8
  "homepage": "Homepage",
9
9
  "blog": "Blog",
10
- "navigation": "Navigation",
11
- "features": "Feature Flags"
10
+ "navigation": "Navigation"
12
11
  },
13
12
  "common": {
14
13
  "save": "Save",
@@ -218,10 +217,6 @@
218
217
  "sidebarEnabled": "Sidebar enabled",
219
218
  "sidebarSide": "Sidebar side",
220
219
  "navItems": "Navigation items"
221
- },
222
- "features": {
223
- "title": "Feature Flags",
224
- "empty": "No plugins declare a feature flag yet."
225
220
  }
226
221
  }
227
222
  }
package/locales/fr.json CHANGED
@@ -7,8 +7,7 @@
7
7
  "branding": "Identité visuelle",
8
8
  "homepage": "Accueil",
9
9
  "blog": "Blog",
10
- "navigation": "Navigation",
11
- "features": "Fonctionnalités"
10
+ "navigation": "Navigation"
12
11
  },
13
12
  "common": {
14
13
  "save": "Enregistrer",
@@ -218,10 +217,6 @@
218
217
  "sidebarEnabled": "Barre latérale activée",
219
218
  "sidebarSide": "Côté de la barre latérale",
220
219
  "navItems": "Éléments de navigation"
221
- },
222
- "features": {
223
- "title": "Fonctionnalités",
224
- "empty": "Aucun plugin ne déclare encore de drapeau de fonctionnalité."
225
220
  }
226
221
  }
227
222
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-site-config",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.3",
4
4
  "type": "module",
5
- "description": "Site identity, branding, layout, and feature flag configuration for Indiekit",
5
+ "description": "Site identity, branding, and navigation configuration for Indiekit",
6
6
  "main": "index.js",
7
7
  "exports": {
8
8
  ".": "./index.js"
@@ -1,5 +1,5 @@
1
1
  {# Tab navigation for site-config admin - server-rendered URL tabs #}
2
- {# Expects: activeTab (string, one of identity|branding|homepage|blog|navigation|features) #}
2
+ {# Expects: activeTab (string, one of identity|branding|homepage|blog|navigation) #}
3
3
  <style>
4
4
  .sc-tab-nav {
5
5
  display: flex;
@@ -31,8 +31,7 @@
31
31
  {key: 'branding', href: '/site-config/branding'},
32
32
  {key: 'homepage', href: '/site-config/homepage'},
33
33
  {key: 'blog', href: '/site-config/blog'},
34
- {key: 'navigation', href: '/site-config/navigation'},
35
- {key: 'features', href: '/site-config/features'}
34
+ {key: 'navigation', href: '/site-config/navigation'}
36
35
  ] %}
37
36
  {% for tab in tabs %}
38
37
  <a href="{{ tab.href }}"
@@ -1,52 +0,0 @@
1
- import express from "express";
2
- import { getSiteConfig } from "../storage/get-site-config.js";
3
- import { saveSiteConfig } from "../storage/save-site-config.js";
4
- import { writeSiteJson } from "../render/write-site-json.js";
5
-
6
- /**
7
- * Discover feature flags from loaded plugins by reading `plugin.featureFlag`.
8
- * Plugins without the capability are skipped.
9
- */
10
- export function discoverFlags(Indiekit) {
11
- const plugins = Indiekit.config?.plugins || [];
12
- return plugins
13
- .map((p) => p.featureFlag)
14
- .filter(Boolean)
15
- .sort((a, b) => (a.category || "").localeCompare(b.category || ""));
16
- }
17
-
18
- export function featuresRouter(Indiekit) {
19
- const router = express.Router();
20
-
21
- router.get("/", async (req, res, next) => {
22
- try {
23
- const config = await getSiteConfig(Indiekit);
24
- const flags = discoverFlags(Indiekit);
25
- res.render("site-config-features", {
26
- config,
27
- activeTab: "features",
28
- flags,
29
- });
30
- } catch (error) {
31
- next(error);
32
- }
33
- });
34
-
35
- router.post("/", async (req, res, next) => {
36
- try {
37
- const flags = discoverFlags(Indiekit);
38
- const features = {};
39
- for (const flag of flags) {
40
- features[flag.key] = req.body[`feature_${flag.key}`] === "on";
41
- }
42
- const userIdent = Indiekit.config?.publication?.me || "unknown";
43
- const updated = await saveSiteConfig(Indiekit, { features }, userIdent);
44
- await writeSiteJson(updated);
45
- res.redirect("/site-config/features?saved=1");
46
- } catch (error) {
47
- next(error);
48
- }
49
- });
50
-
51
- return router;
52
- }
@@ -1,166 +0,0 @@
1
- {% extends "document.njk" %}
2
-
3
- {% block title %}{{ __('siteConfig.features.title') }}{% endblock %}
4
-
5
- {% block content %}
6
- <style>
7
- .sc-dashboard {
8
- display: flex;
9
- flex-direction: column;
10
- gap: var(--space-xl, 2rem);
11
- }
12
-
13
- .sc-section {
14
- background: var(--color-offset, #f5f5f5);
15
- border-radius: var(--border-radius-small, 0.5rem);
16
- padding: var(--space-m, 1.5rem);
17
- }
18
-
19
- .sc-section h2 {
20
- font: var(--font-heading, bold 1.25rem/1.4 sans-serif);
21
- margin-block-end: var(--space-s, 0.75rem);
22
- padding-block-end: var(--space-xs, 0.5rem);
23
- border-block-end: 1px solid var(--color-outline-variant, #ddd);
24
- }
25
-
26
- .sc-section__desc {
27
- color: var(--color-on-offset, #666);
28
- font: var(--font-body, 0.875rem/1.5 sans-serif);
29
- margin-block-end: var(--space-s, 0.75rem);
30
- }
31
-
32
- .hp-success {
33
- background: var(--color-success-container, #d4edda);
34
- border: 1px solid var(--color-success, #28a745);
35
- border-radius: var(--border-radius-small, 0.25rem);
36
- padding: var(--space-s, 0.75rem);
37
- margin-block-end: var(--space-m, 1rem);
38
- }
39
-
40
- /* Empty state */
41
- .sc-empty-state {
42
- text-align: center;
43
- padding: var(--space-xl, 2rem) var(--space-m, 1rem);
44
- color: var(--color-on-offset, #888);
45
- font: var(--font-body, 0.875rem/1.5 sans-serif);
46
- background: var(--color-offset, #f5f5f5);
47
- border: 1px dashed var(--color-outline-variant, #ddd);
48
- border-radius: var(--border-radius-small, 0.5rem);
49
- }
50
-
51
- .sc-empty-state p {
52
- margin: 0;
53
- }
54
-
55
- /* Flag rows */
56
- .sc-flag-row {
57
- display: flex;
58
- align-items: flex-start;
59
- gap: var(--space-s, 0.75rem);
60
- padding: var(--space-s, 0.75rem) 0;
61
- border-bottom: 1px solid var(--color-outline-variant, #eee);
62
- }
63
-
64
- .sc-flag-row:last-child {
65
- border-bottom: none;
66
- padding-bottom: 0;
67
- }
68
-
69
- .sc-flag-row:first-child {
70
- padding-top: 0;
71
- }
72
-
73
- .sc-flag-label {
74
- display: flex;
75
- flex-direction: column;
76
- gap: 0.125rem;
77
- flex: 1;
78
- }
79
-
80
- .sc-flag-label strong {
81
- font: var(--font-body, 0.875rem/1.5 sans-serif);
82
- font-weight: 600;
83
- color: var(--color-on-surface, inherit);
84
- }
85
-
86
- .sc-flag-hint {
87
- font: var(--font-caption, 0.75rem/1.4 sans-serif);
88
- color: var(--color-on-offset, #888);
89
- margin: 0;
90
- }
91
-
92
- .sc-restart-badge {
93
- display: inline-flex;
94
- align-items: center;
95
- font: var(--font-caption, 0.7rem/1.4 sans-serif);
96
- font-weight: 600;
97
- color: var(--color-warning, #856404);
98
- background: color-mix(in srgb, var(--color-warning, #856404) 12%, var(--color-background, #fff));
99
- border: 1px solid color-mix(in srgb, var(--color-warning, #856404) 30%, transparent);
100
- border-radius: var(--border-radius-small, 0.25rem);
101
- padding: 0.1rem 0.4rem;
102
- white-space: nowrap;
103
- }
104
- </style>
105
-
106
- <header class="page-header">
107
- <h1 class="page-header__title">{{ __('siteConfig.title') }}</h1>
108
- </header>
109
-
110
- {% include "partials/tab-strip.njk" %}
111
-
112
- {% if request.query.saved %}
113
- <div class="hp-success">
114
- <p>{{ __('siteConfig.common.saved') }}</p>
115
- </div>
116
- {% endif %}
117
-
118
- {% if flags.length == 0 %}
119
-
120
- <div class="sc-empty-state">
121
- <p>{{ __('siteConfig.features.empty') }}</p>
122
- </div>
123
-
124
- {% else %}
125
-
126
- <form method="post" action="/site-config/features" class="sc-dashboard">
127
-
128
- {% set currentCategory = '' %}
129
- {% for flag in flags %}
130
-
131
- {% if flag.category != currentCategory %}
132
- {% set currentCategory = flag.category %}
133
- <section class="sc-section">
134
- <h2>{{ flag.category }}</h2>
135
- {% endif %}
136
-
137
- <div class="sc-flag-row">
138
- <input type="checkbox"
139
- id="feature_{{ flag.key }}"
140
- name="feature_{{ flag.key }}"
141
- {% if config.features[flag.key] %}checked{% elif flag.default %}checked{% endif %}>
142
- <label class="sc-flag-label" for="feature_{{ flag.key }}">
143
- <strong>{{ flag.label }}</strong>
144
- {% if flag.description %}
145
- <p class="sc-flag-hint">{{ flag.description }}</p>
146
- {% endif %}
147
- {% if flag.requiresRestart %}
148
- <span class="sc-restart-badge">Restart required</span>
149
- {% endif %}
150
- </label>
151
- </div>
152
-
153
- {% if loop.last or flags[loop.index].category != flag.category %}
154
- </section>
155
- {% endif %}
156
-
157
- {% endfor %}
158
-
159
- <div class="button-group">
160
- <button type="submit" class="button button--primary">{{ __('siteConfig.common.save') }}</button>
161
- </div>
162
-
163
- </form>
164
-
165
- {% endif %}
166
- {% endblock %}