@commonpub/layer 0.72.0 → 0.72.1
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/components/ContentCard.vue +2 -0
- package/components/ContentPicker.vue +2 -0
- package/components/ImportUrlModal.vue +2 -0
- package/components/PublishErrorsModal.vue +2 -0
- package/components/RemoteFollowDialog.vue +2 -0
- package/components/ShareToHubModal.vue +2 -0
- package/components/editors/MarkdownImportDialog.vue +2 -0
- package/layouts/default.vue +4 -2
- package/package.json +7 -7
- package/server/utils/instanceTheme.ts +18 -3
- package/theme/layouts.css +3 -0
|
@@ -141,6 +141,8 @@ function formatCount(n: number | undefined): string {
|
|
|
141
141
|
border: var(--border-width-default) solid var(--border);
|
|
142
142
|
overflow: hidden;
|
|
143
143
|
transition: transform 0.15s, box-shadow 0.15s;
|
|
144
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
145
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
.cpub-cc:hover {
|
|
@@ -114,6 +114,8 @@ useFocusTrap(dialogRef, () => props.open, close);
|
|
|
114
114
|
background: var(--surface);
|
|
115
115
|
border: var(--border-width-default) solid var(--border);
|
|
116
116
|
box-shadow: var(--shadow-xl);
|
|
117
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
118
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
117
119
|
width: 520px;
|
|
118
120
|
max-width: 90vw;
|
|
119
121
|
max-height: 70vh;
|
|
@@ -193,6 +193,8 @@ useFocusTrap(dialogRef, () => props.show, handleClose);
|
|
|
193
193
|
display: flex;
|
|
194
194
|
flex-direction: column;
|
|
195
195
|
max-height: 80vh;
|
|
196
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
197
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
196
198
|
}
|
|
197
199
|
|
|
198
200
|
.cpub-import-header {
|
|
@@ -52,6 +52,8 @@ useFocusTrap(dialogRef, () => props.show, () => emit('dismiss'));
|
|
|
52
52
|
max-width: 420px;
|
|
53
53
|
width: 100%;
|
|
54
54
|
box-shadow: var(--shadow-md);
|
|
55
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
56
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
.cpub-publish-errors-title {
|
|
@@ -87,6 +87,8 @@ useFocusTrap(dialogRef, () => open.value, close);
|
|
|
87
87
|
background: var(--bg); border: var(--border-width-default) solid var(--border);
|
|
88
88
|
width: 100%; max-width: 420px; padding: 24px;
|
|
89
89
|
box-shadow: var(--shadow-md);
|
|
90
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
91
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
90
92
|
}
|
|
91
93
|
.cpub-rfd-header {
|
|
92
94
|
display: flex; align-items: center; justify-content: space-between;
|
|
@@ -100,6 +100,8 @@ async function handleShare(): Promise<void> {
|
|
|
100
100
|
background: var(--surface);
|
|
101
101
|
border: var(--border-width-default) solid var(--border);
|
|
102
102
|
box-shadow: var(--shadow-lg);
|
|
103
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
104
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
103
105
|
padding: 24px;
|
|
104
106
|
max-width: 420px;
|
|
105
107
|
width: 90vw;
|
|
@@ -150,6 +150,8 @@ async function readFile(file: File): Promise<void> {
|
|
|
150
150
|
box-shadow: var(--shadow-xl);
|
|
151
151
|
display: flex; flex-direction: column;
|
|
152
152
|
max-height: 80vh;
|
|
153
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
154
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
.md-import-header {
|
package/layouts/default.vue
CHANGED
|
@@ -323,6 +323,8 @@ const userUsername = computed(() => user.value?.username ?? '');
|
|
|
323
323
|
background: var(--surface); border: var(--border-width-default) solid var(--border);
|
|
324
324
|
box-shadow: var(--shadow-md); z-index: 200; display: flex; flex-direction: column; padding: 4px 0;
|
|
325
325
|
margin-top: 4px;
|
|
326
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
327
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
326
328
|
}
|
|
327
329
|
:deep(.cpub-nav-panel-item) {
|
|
328
330
|
display: flex; align-items: center; gap: 8px; padding: 8px 14px;
|
|
@@ -363,7 +365,7 @@ const userUsername = computed(() => user.value?.username ?? '');
|
|
|
363
365
|
.cpub-user-avatar { width: 28px; height: 28px; border-radius: 50%; background: var(--purple-bg); border: var(--border-width-default) solid var(--purple); display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700; color: var(--purple); font-family: var(--font-mono); overflow: hidden; }
|
|
364
366
|
.cpub-user-avatar-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
|
|
365
367
|
.cpub-user-menu-wrapper { position: relative; }
|
|
366
|
-
.cpub-user-dropdown { position: absolute; top: calc(100% + 6px); right: 0; min-width: 180px; background: var(--surface); border: var(--border-width-default) solid var(--border); box-shadow: var(--shadow-md); z-index: 200; display: flex; flex-direction: column; padding: 4px 0; }
|
|
368
|
+
.cpub-user-dropdown { position: absolute; top: calc(100% + 6px); right: 0; min-width: 180px; background: var(--surface); border: var(--border-width-default) solid var(--border); box-shadow: var(--shadow-md); z-index: 200; display: flex; flex-direction: column; padding: 4px 0; -webkit-backdrop-filter: var(--surface-backdrop, none); backdrop-filter: var(--surface-backdrop, none); }
|
|
367
369
|
.cpub-dropdown-item { display: flex; align-items: center; gap: 8px; padding: 8px 16px; font-size: 12px; color: var(--text-dim); text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit; width: 100%; text-align: left; transition: all 0.15s; }
|
|
368
370
|
.cpub-dropdown-item:hover { background: var(--surface2); color: var(--text); }
|
|
369
371
|
.cpub-dropdown-item i { width: 14px; text-align: center; font-size: 11px; }
|
|
@@ -375,7 +377,7 @@ const userUsername = computed(() => user.value?.username ?? '');
|
|
|
375
377
|
|
|
376
378
|
.cpub-mobile-toggle { display: none; width: 32px; height: 32px; background: none; border: var(--border-width-default) solid transparent; color: var(--text-dim); font-size: 16px; cursor: pointer; align-items: center; justify-content: center; }
|
|
377
379
|
.cpub-mobile-menu { display: none; position: fixed; inset: 0; top: var(--cpub-topbar-height, 48px); z-index: 99; background: var(--color-surface-overlay-light); }
|
|
378
|
-
:deep(.cpub-mobile-nav) { background: var(--surface); border-bottom: var(--border-width-default) solid var(--border); padding: 8px 0; display: flex; flex-direction: column; box-shadow: var(--shadow-md); }
|
|
380
|
+
:deep(.cpub-mobile-nav) { background: var(--surface); border-bottom: var(--border-width-default) solid var(--border); padding: 8px 0; display: flex; flex-direction: column; box-shadow: var(--shadow-md); -webkit-backdrop-filter: var(--surface-backdrop, none); backdrop-filter: var(--surface-backdrop, none); }
|
|
379
381
|
:deep(.cpub-mobile-link) { display: flex; align-items: center; gap: 10px; padding: 10px 20px; font-size: 13px; color: var(--text-dim); text-decoration: none; transition: background 0.1s; }
|
|
380
382
|
:deep(.cpub-mobile-link:hover) { background: var(--surface2); color: var(--text); }
|
|
381
383
|
:deep(.cpub-mobile-link i) { width: 16px; text-align: center; font-size: 12px; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commonpub/layer",
|
|
3
|
-
"version": "0.72.
|
|
3
|
+
"version": "0.72.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"files": [
|
|
@@ -55,15 +55,15 @@
|
|
|
55
55
|
"zod": "^4.3.6",
|
|
56
56
|
"@commonpub/auth": "0.8.0",
|
|
57
57
|
"@commonpub/config": "0.21.0",
|
|
58
|
-
"@commonpub/server": "2.84.1",
|
|
59
|
-
"@commonpub/ui": "0.13.0",
|
|
60
|
-
"@commonpub/learning": "0.5.2",
|
|
61
|
-
"@commonpub/theme-studio": "0.6.0",
|
|
62
58
|
"@commonpub/docs": "0.6.3",
|
|
63
|
-
"@commonpub/schema": "0.40.0",
|
|
64
59
|
"@commonpub/editor": "0.7.11",
|
|
65
60
|
"@commonpub/protocol": "0.13.0",
|
|
66
|
-
"@commonpub/
|
|
61
|
+
"@commonpub/schema": "0.40.0",
|
|
62
|
+
"@commonpub/theme-studio": "0.6.1",
|
|
63
|
+
"@commonpub/ui": "0.13.1",
|
|
64
|
+
"@commonpub/explainer": "0.7.15",
|
|
65
|
+
"@commonpub/server": "2.84.1",
|
|
66
|
+
"@commonpub/learning": "0.5.2"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@testing-library/jest-dom": "^6.9.1",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Users only toggle light/dark within that family.
|
|
4
4
|
|
|
5
5
|
import { eq } from 'drizzle-orm';
|
|
6
|
-
import { instanceSettings } from '@commonpub/schema';
|
|
6
|
+
import { instanceSettings, isSafeBgImageValue } from '@commonpub/schema';
|
|
7
7
|
import {
|
|
8
8
|
getCustomTokenOverrides,
|
|
9
9
|
listCustomThemes,
|
|
@@ -15,6 +15,21 @@ import { THEME_TO_FAMILY, FAMILY_VARIANTS, IS_DARK, VALID_THEME_IDS } from '../.
|
|
|
15
15
|
|
|
16
16
|
const CACHE_TTL = 60_000; // 1 minute, admin changes propagate fast
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Sink-side guard for the one token whose VALUE can fetch when rendered:
|
|
20
|
+
* `--bg-image` feeds `background-image`, so a `url(...)` is a beacon/exfil
|
|
21
|
+
* channel. The themes POST/PUT already reject unsafe values, but the token
|
|
22
|
+
* map has other write paths (the generic admin settings route writes
|
|
23
|
+
* `instance_settings` keys wholesale), so the render sink enforces the same
|
|
24
|
+
* allowlist: a non-gradient bg-image is dropped, never injected.
|
|
25
|
+
*/
|
|
26
|
+
export function sanitizeRenderTokens(tokens: Record<string, string>): Record<string, string> {
|
|
27
|
+
const v = tokens['bg-image'];
|
|
28
|
+
if (v === undefined || isSafeBgImageValue(v)) return tokens;
|
|
29
|
+
const { 'bg-image': _dropped, ...rest } = tokens;
|
|
30
|
+
return rest;
|
|
31
|
+
}
|
|
32
|
+
|
|
18
33
|
interface CachedThemeState {
|
|
19
34
|
/** The admin's chosen default theme (built-in id, custom data-attr, or registered id) */
|
|
20
35
|
defaultTheme: string;
|
|
@@ -138,7 +153,7 @@ export async function resolveThemeContext(
|
|
|
138
153
|
const sib = state.customByAttr.get(sibAttr);
|
|
139
154
|
if (sib) members.push({ attr: sibAttr, rec: sib });
|
|
140
155
|
}
|
|
141
|
-
themeVariants = members.map((m) => ({ attr: m.attr, tokens: m.rec.tokens }));
|
|
156
|
+
themeVariants = members.map((m) => ({ attr: m.attr, tokens: sanitizeRenderTokens(m.rec.tokens) }));
|
|
142
157
|
const lightM = members.find((m) => !m.rec.isDark);
|
|
143
158
|
const darkM = members.find((m) => m.rec.isDark);
|
|
144
159
|
if (members.length === 2 && lightM && darkM) {
|
|
@@ -167,7 +182,7 @@ export async function resolveThemeContext(
|
|
|
167
182
|
instanceTheme: admin,
|
|
168
183
|
isDark,
|
|
169
184
|
themeVariants,
|
|
170
|
-
overrides: { ...state.tokenOverrides },
|
|
185
|
+
overrides: sanitizeRenderTokens({ ...state.tokenOverrides }),
|
|
171
186
|
pair,
|
|
172
187
|
fontHref,
|
|
173
188
|
};
|
package/theme/layouts.css
CHANGED
|
@@ -283,6 +283,9 @@
|
|
|
283
283
|
border: var(--border-width-default) solid var(--border);
|
|
284
284
|
overflow: hidden;
|
|
285
285
|
transition: transform var(--transition-fast), box-shadow var(--transition-fast);
|
|
286
|
+
/* Glass treatment hook — `none` default is a true no-op. */
|
|
287
|
+
-webkit-backdrop-filter: var(--surface-backdrop, none);
|
|
288
|
+
backdrop-filter: var(--surface-backdrop, none);
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
.cpub-card:hover {
|