@commonpub/layer 0.21.5 → 0.21.7
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/CookieConsent.vue +1 -1
- package/components/RemoteFollowDialog.vue +2 -2
- package/components/contest/ContestHero.vue +5 -2
- package/components/editors/MarkdownImportDialog.vue +5 -2
- package/components/views/ProjectView.vue +4 -1
- package/package.json +6 -6
- package/pages/docs/[siteSlug]/edit.vue +3 -3
- package/server/api/realtime/stream.get.ts +18 -3
|
@@ -34,7 +34,7 @@ const visible = computed(() => !hasConsented.value && hasNonEssentialCookies.val
|
|
|
34
34
|
z-index: var(--z-toast);
|
|
35
35
|
background: var(--surface);
|
|
36
36
|
border-top: var(--border-width-default) solid var(--border);
|
|
37
|
-
box-shadow: 0 -
|
|
37
|
+
box-shadow: 0 -2px 0 var(--border);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
.cpub-consent-inner {
|
|
@@ -79,14 +79,14 @@ useFocusTrap(dialogRef, () => open.value, close);
|
|
|
79
79
|
<style scoped>
|
|
80
80
|
.cpub-rfd-overlay {
|
|
81
81
|
position: fixed; inset: 0; z-index: 9999;
|
|
82
|
-
background: rgba(0, 0, 0, 0.5); display: flex;
|
|
82
|
+
background: var(--color-surface-overlay, rgba(0, 0, 0, 0.5)); display: flex;
|
|
83
83
|
align-items: center; justify-content: center;
|
|
84
84
|
padding: 16px;
|
|
85
85
|
}
|
|
86
86
|
.cpub-rfd-dialog {
|
|
87
87
|
background: var(--bg); border: var(--border-width-default) solid var(--border);
|
|
88
88
|
width: 100%; max-width: 420px; padding: 24px;
|
|
89
|
-
box-shadow:
|
|
89
|
+
box-shadow: var(--shadow-md);
|
|
90
90
|
}
|
|
91
91
|
.cpub-rfd-header {
|
|
92
92
|
display: flex; align-items: center; justify-content: space-between;
|
|
@@ -139,8 +139,11 @@ const isEnded = computed(() => c.value?.status === 'completed' || c.value?.statu
|
|
|
139
139
|
--hero-bg: var(--text);
|
|
140
140
|
--hero-text: var(--color-text-inverse);
|
|
141
141
|
--hero-text-dim: var(--text-faint);
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
/* Alpha of the hero foreground so the structure lines/surfaces track
|
|
143
|
+
the inverted hero in both themes (white-on-dark in light mode,
|
|
144
|
+
dark-on-light in dark mode) instead of vanishing white-on-white. */
|
|
145
|
+
--hero-border: color-mix(in srgb, var(--hero-text) 18%, transparent);
|
|
146
|
+
--hero-surface: color-mix(in srgb, var(--hero-text) 7%, transparent);
|
|
144
147
|
position: relative; overflow: hidden; background: var(--hero-bg); padding: 56px 0 48px;
|
|
145
148
|
}
|
|
146
149
|
.cpub-hero-pattern { position: absolute; inset: 0; }
|
|
@@ -10,6 +10,9 @@ const emit = defineEmits<{
|
|
|
10
10
|
import: [md: string, mode: 'append' | 'replace'];
|
|
11
11
|
}>();
|
|
12
12
|
|
|
13
|
+
const dialogRef = ref<HTMLElement | null>(null);
|
|
14
|
+
useFocusTrap(dialogRef, () => props.show, () => emit('close'));
|
|
15
|
+
|
|
13
16
|
const activeTab = ref<'paste' | 'file'>('paste');
|
|
14
17
|
const markdownText = ref('');
|
|
15
18
|
const mode = ref<'append' | 'replace'>('append');
|
|
@@ -58,9 +61,9 @@ async function readFile(file: File): Promise<void> {
|
|
|
58
61
|
<template>
|
|
59
62
|
<Teleport to="body">
|
|
60
63
|
<div v-if="show" class="md-import-overlay" @click.self="emit('close')">
|
|
61
|
-
<div class="md-import-dialog">
|
|
64
|
+
<div ref="dialogRef" class="md-import-dialog" role="dialog" aria-modal="true" aria-labelledby="md-import-title">
|
|
62
65
|
<div class="md-import-header">
|
|
63
|
-
<h2><i class="fa-brands fa-markdown"></i> Import Markdown</h2>
|
|
66
|
+
<h2 id="md-import-title"><i class="fa-brands fa-markdown"></i> Import Markdown</h2>
|
|
64
67
|
<button class="md-import-close" @click="emit('close')"><i class="fa-solid fa-xmark"></i></button>
|
|
65
68
|
</div>
|
|
66
69
|
|
|
@@ -227,7 +227,10 @@ const tocActiveId = ref('');
|
|
|
227
227
|
function scrollToHeading(id: string): void {
|
|
228
228
|
const el = document.getElementById(id);
|
|
229
229
|
if (el) {
|
|
230
|
-
|
|
230
|
+
// CSS scroll-behavior is reduced-motion-gated in base.css, but the JS
|
|
231
|
+
// smooth option ignores that — honour the preference explicitly.
|
|
232
|
+
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
233
|
+
el.scrollIntoView({ behavior: reduceMotion ? 'auto' : 'smooth', block: 'start' });
|
|
231
234
|
tocActiveId.value = id;
|
|
232
235
|
}
|
|
233
236
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commonpub/layer",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"files": [
|
|
@@ -50,16 +50,16 @@
|
|
|
50
50
|
"vue": "^3.4.0",
|
|
51
51
|
"vue-router": "^4.3.0",
|
|
52
52
|
"zod": "^4.3.6",
|
|
53
|
-
"@commonpub/auth": "0.6.0",
|
|
54
|
-
"@commonpub/editor": "0.7.9",
|
|
55
53
|
"@commonpub/docs": "0.6.3",
|
|
54
|
+
"@commonpub/editor": "0.7.9",
|
|
55
|
+
"@commonpub/explainer": "0.7.13",
|
|
56
56
|
"@commonpub/protocol": "0.9.10",
|
|
57
|
+
"@commonpub/config": "0.12.0",
|
|
57
58
|
"@commonpub/learning": "0.5.2",
|
|
58
|
-
"@commonpub/
|
|
59
|
+
"@commonpub/server": "2.53.1",
|
|
59
60
|
"@commonpub/ui": "0.8.5",
|
|
60
|
-
"@commonpub/server": "2.53.0",
|
|
61
61
|
"@commonpub/schema": "0.16.0",
|
|
62
|
-
"@commonpub/
|
|
62
|
+
"@commonpub/auth": "0.6.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@testing-library/jest-dom": "^6.9.1",
|
|
@@ -1229,7 +1229,7 @@ async function createVersion(): Promise<void> {
|
|
|
1229
1229
|
position: fixed;
|
|
1230
1230
|
inset: 0;
|
|
1231
1231
|
z-index: 10000;
|
|
1232
|
-
background: rgba(0, 0, 0, 0.5);
|
|
1232
|
+
background: var(--color-surface-overlay, rgba(0, 0, 0, 0.5));
|
|
1233
1233
|
display: flex;
|
|
1234
1234
|
align-items: flex-start;
|
|
1235
1235
|
justify-content: center;
|
|
@@ -1402,7 +1402,7 @@ async function createVersion(): Promise<void> {
|
|
|
1402
1402
|
}
|
|
1403
1403
|
|
|
1404
1404
|
.cpub-settings-danger {
|
|
1405
|
-
background:
|
|
1405
|
+
background: var(--red-bg);
|
|
1406
1406
|
}
|
|
1407
1407
|
|
|
1408
1408
|
.cpub-settings-danger .cpub-settings-section-title {
|
|
@@ -1410,7 +1410,7 @@ async function createVersion(): Promise<void> {
|
|
|
1410
1410
|
}
|
|
1411
1411
|
|
|
1412
1412
|
.cpub-settings-danger-text {
|
|
1413
|
-
font-size:
|
|
1413
|
+
font-size: var(--text-xs);
|
|
1414
1414
|
color: var(--text-dim);
|
|
1415
1415
|
margin-bottom: 10px;
|
|
1416
1416
|
}
|
|
@@ -47,6 +47,23 @@ export default defineEventHandler(async (event) => {
|
|
|
47
47
|
}
|
|
48
48
|
userConnections.set(userId, current + 1);
|
|
49
49
|
|
|
50
|
+
// Decrement exactly once on disconnect. Registered at handler scope (not
|
|
51
|
+
// inside ReadableStream.start) because start() is invoked lazily when the
|
|
52
|
+
// runtime begins pulling the stream — if the client aborts before that,
|
|
53
|
+
// start() never runs and an in-start cleanup would never fire, leaking
|
|
54
|
+
// the slot permanently and eventually 429-locking the user for the
|
|
55
|
+
// process lifetime. cleanup() also calls release(); the guard makes it
|
|
56
|
+
// idempotent so there is no double-decrement.
|
|
57
|
+
let released = false;
|
|
58
|
+
const release = (): void => {
|
|
59
|
+
if (released) return;
|
|
60
|
+
released = true;
|
|
61
|
+
const next = (userConnections.get(userId) ?? 1) - 1;
|
|
62
|
+
if (next <= 0) userConnections.delete(userId);
|
|
63
|
+
else userConnections.set(userId, next);
|
|
64
|
+
};
|
|
65
|
+
event.node.req.on('close', release);
|
|
66
|
+
|
|
50
67
|
const encoder = new TextEncoder();
|
|
51
68
|
const stream = new ReadableStream({
|
|
52
69
|
async start(controller) {
|
|
@@ -65,9 +82,7 @@ export default defineEventHandler(async (event) => {
|
|
|
65
82
|
unsubscribe = null;
|
|
66
83
|
}
|
|
67
84
|
try { controller.close(); } catch { /* already closed */ }
|
|
68
|
-
|
|
69
|
-
if (next <= 0) userConnections.delete(userId);
|
|
70
|
-
else userConnections.set(userId, next);
|
|
85
|
+
release();
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
async function sendCounts(): Promise<void> {
|