@commonpub/layer 0.73.0 → 0.73.2

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.
@@ -19,8 +19,12 @@ const BUILTIN_COOKIES: CookieDefinition[] = [
19
19
  },
20
20
  {
21
21
  name: 'cpub-color-scheme',
22
- category: 'functional',
23
- description: 'Remembers your light/dark mode preference across visits.',
22
+ // Essential, not functional: it's a preference the user explicitly
23
+ // requests by pressing the Light/Dark toggle (consent-exempt class:
24
+ // stores no identifier, does no tracking). When it was consent-gated,
25
+ // anyone on "Essential only" lost their theme on every refresh.
26
+ category: 'essential',
27
+ description: 'Remembers your light/dark mode preference. Set only when you use the theme toggle.',
24
28
  duration: '1 year',
25
29
  },
26
30
  ];
@@ -8,8 +8,11 @@ import { THEME_TO_FAMILY, FAMILY_VARIANTS } from '../utils/themeConfig';
8
8
  * between light and dark mode within that theme's family. The server middleware
9
9
  * resolves the correct theme on every request — no theme-selection cookie needed.
10
10
  *
11
- * The dark mode preference cookie (`cpub-color-scheme`) is only persisted
12
- * when the user has accepted functional cookies via the consent banner.
11
+ * The dark mode preference cookie (`cpub-color-scheme`) is an ESSENTIAL
12
+ * preference cookie (set only by the user pressing the toggle, no
13
+ * identifier) — always persisted, no consent gate. It used to be gated on
14
+ * functional consent, which silently dropped the preference on refresh for
15
+ * anyone who chose "Essential only".
13
16
  *
14
17
  * Custom themes (`cpub-custom-*`) and code-registered themes pass through —
15
18
  * the user's cookie toggle is recorded but the server picks the actual variant
@@ -35,15 +38,12 @@ export function useTheme(): {
35
38
  path: '/',
36
39
  sameSite: 'lax',
37
40
  });
38
- const { allowsFunctional } = useCookieConsent();
39
-
40
41
  function setDarkMode(dark: boolean): void {
41
42
  isDark.value = dark;
42
43
 
43
- // Only persist to cookie if user consented to functional cookies
44
- if (allowsFunctional.value) {
45
- schemeCookie.value = dark ? 'dark' : 'light';
46
- }
44
+ // Always persist pressing the toggle IS the consent for this
45
+ // preference cookie (registered as essential in useCookieConsent).
46
+ schemeCookie.value = dark ? 'dark' : 'light';
47
47
 
48
48
  // Custom light/dark PAIR: both variants' tokens are injected (scoped to
49
49
  // their data-theme attr), so flip the attribute client-side for an instant
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonpub/layer",
3
- "version": "0.73.0",
3
+ "version": "0.73.2",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -53,17 +53,17 @@
53
53
  "vue": "^3.4.0",
54
54
  "vue-router": "^4.3.0",
55
55
  "zod": "^4.3.6",
56
- "@commonpub/docs": "0.6.3",
57
- "@commonpub/config": "0.22.0",
58
- "@commonpub/editor": "0.7.11",
59
56
  "@commonpub/auth": "0.8.0",
57
+ "@commonpub/docs": "0.6.3",
60
58
  "@commonpub/learning": "0.5.2",
61
- "@commonpub/schema": "0.40.1",
62
- "@commonpub/explainer": "0.7.15",
59
+ "@commonpub/config": "0.22.1",
60
+ "@commonpub/editor": "0.7.11",
63
61
  "@commonpub/protocol": "0.13.0",
62
+ "@commonpub/schema": "0.40.1",
63
+ "@commonpub/theme-studio": "0.6.1",
64
64
  "@commonpub/server": "2.84.1",
65
65
  "@commonpub/ui": "0.13.1",
66
- "@commonpub/theme-studio": "0.6.1"
66
+ "@commonpub/explainer": "0.7.15"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@testing-library/jest-dom": "^6.9.1",
package/pages/privacy.vue CHANGED
@@ -67,7 +67,7 @@ const { federation: federationEnabled } = useFeatures();
67
67
  <ul>
68
68
  <li><strong>Session cookie</strong> (<code>better-auth.session_token</code>): strictly necessary, authenticates your login session. HttpOnly, secure, 7-day expiry.</li>
69
69
  <li><strong>Consent cookie</strong> (<code>cpub-consent</code>): strictly necessary, stores your cookie consent choice.</li>
70
- <li><strong>Color scheme</strong> (<code>cpub-color-scheme</code>): functional, remembers your light/dark mode preference. Set only with your consent.</li>
70
+ <li><strong>Color scheme</strong> (<code>cpub-color-scheme</code>): strictly necessary preference, remembers your light/dark mode choice. Set only when you use the theme toggle. No identifier, no tracking.</li>
71
71
  </ul>
72
72
  <p>We do not use any advertising or tracking cookies. Your instance operator may add analytics cookies, these require your explicit consent. For the full list of cookies and to manage your preferences, visit our <NuxtLink to="/cookies">Cookie Policy</NuxtLink>.</p>
73
73
  </section>
@@ -119,14 +119,19 @@ export default defineEventHandler(async (event): Promise<{ items: unknown[]; tot
119
119
  // items the instance's own homepage shows. Delegate to listContent (same
120
120
  // merge, same pagination invariants, items already in ContentCard's shape)
121
121
  // whenever the request uses only filters listContent supports. Search-only
122
- // filters (author, date range, multiple tags) and a configured Meilisearch
123
- // keep the dedicated local path: federated rows aren't indexed and don't
124
- // carry those fields. `resolveContentQuery` pins status=published +
122
+ // filters (author, date range, multiple tags) keep the dedicated local
123
+ // path: federated rows aren't indexed and don't carry those fields.
124
+ // NOTE this branch deliberately OUTRANKS Meilisearch when federation is on:
125
+ // meili only ever indexes LOCAL content, so on a mirror-heavy instance a
126
+ // configured-but-mostly-empty index would shadow the merge and return 0
127
+ // (exactly what happened on commonpub.io, whose compose stack sets
128
+ // MEILI_URL — the first ship of this fix gated on `!meiliClient` and was
129
+ // inert there). `resolveContentQuery` pins status=published +
125
130
  // visibility=public so this path can never widen what search exposes.
126
131
  const tagList = params.tags?.split(',').map((t) => t.trim()).filter(Boolean) ?? [];
127
132
  const usesSearchOnlyFilters = !!(params.author || params.dateFrom || params.dateTo || tagList.length > 1);
128
133
  const CONTENT_TYPES = new Set(['project', 'article', 'blog', 'explainer']);
129
- if (!meiliClient && config.features.seamlessFederation && !usesSearchOnlyFilters) {
134
+ if (config.features.seamlessFederation && !usesSearchOnlyFilters) {
130
135
  const raw: ContentFilters = {
131
136
  search: q,
132
137
  type: params.type && CONTENT_TYPES.has(params.type) ? (params.type as ContentFilters['type']) : undefined,