@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.
@@ -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 -4px 12px rgba(0, 0, 0, 0.1);
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: 4px 4px 0 var(--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
- --hero-border: rgba(255, 255, 255, 0.15);
143
- --hero-surface: rgba(255, 255, 255, 0.06);
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
- el.scrollIntoView({ behavior: 'smooth', block: 'start' });
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.5",
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/explainer": "0.7.12",
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/config": "0.12.0"
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: rgba(224, 64, 48, 0.03);
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: 12px;
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
- const next = (userConnections.get(userId) ?? 1) - 1;
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> {