@commonpub/layer 0.7.7 → 0.7.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonpub/layer",
3
- "version": "0.7.7",
3
+ "version": "0.7.9",
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.5.0",
54
- "@commonpub/editor": "0.7.0",
55
53
  "@commonpub/config": "0.9.0",
56
- "@commonpub/explainer": "0.7.4",
57
- "@commonpub/docs": "0.6.2",
58
- "@commonpub/learning": "0.5.0",
59
- "@commonpub/protocol": "0.9.7",
54
+ "@commonpub/editor": "0.7.2",
55
+ "@commonpub/explainer": "0.7.5",
60
56
  "@commonpub/schema": "0.9.4",
57
+ "@commonpub/auth": "0.5.0",
58
+ "@commonpub/learning": "0.5.0",
59
+ "@commonpub/ui": "0.8.5",
60
+ "@commonpub/docs": "0.6.2",
61
61
  "@commonpub/server": "2.27.6",
62
- "@commonpub/ui": "0.8.5"
62
+ "@commonpub/protocol": "0.9.7"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@testing-library/jest-dom": "^6.9.1",
@@ -476,6 +476,22 @@ async function createVersion(): Promise<void> {
476
476
  </NuxtLink>
477
477
  <span class="cpub-docs-topbar-title">{{ site?.name ?? 'Docs' }}</span>
478
478
  <div class="cpub-docs-topbar-spacer" />
479
+ <button
480
+ class="cpub-docs-toolbar-btn"
481
+ title="Undo (Ctrl+Z)"
482
+ :disabled="!blockEditor.canUndo.value"
483
+ @click="blockEditor.undo()"
484
+ >
485
+ <i class="fa-solid fa-rotate-left" />
486
+ </button>
487
+ <button
488
+ class="cpub-docs-toolbar-btn"
489
+ title="Redo (Ctrl+Shift+Z)"
490
+ :disabled="!blockEditor.canRedo.value"
491
+ @click="blockEditor.redo()"
492
+ >
493
+ <i class="fa-solid fa-rotate-right" />
494
+ </button>
479
495
  <button
480
496
  v-if="selectedPageId"
481
497
  class="cpub-docs-toolbar-btn"
@@ -404,6 +404,24 @@ async function handleUrlImport(result: ImportedContent): Promise<void> {
404
404
  <i class="fa-solid fa-exclamation-triangle"></i> Save failed
405
405
  </span>
406
406
  </div>
407
+ <div v-if="!isExplainer" class="cpub-topbar-undo-redo">
408
+ <button
409
+ class="cpub-topbar-icon-btn"
410
+ title="Undo (Ctrl+Z)"
411
+ :disabled="!blockEditor.canUndo.value"
412
+ @click="blockEditor.undo()"
413
+ >
414
+ <i class="fa-solid fa-rotate-left"></i>
415
+ </button>
416
+ <button
417
+ class="cpub-topbar-icon-btn"
418
+ title="Redo (Ctrl+Shift+Z)"
419
+ :disabled="!blockEditor.canRedo.value"
420
+ @click="blockEditor.redo()"
421
+ >
422
+ <i class="fa-solid fa-rotate-right"></i>
423
+ </button>
424
+ </div>
407
425
  <div class="cpub-mode-tabs">
408
426
  <button :class="['cpub-mode-tab', { active: mode === 'write' }]" @click="mode = 'write'">Write</button>
409
427
  <button :class="['cpub-mode-tab', { active: mode === 'preview' }]" @click="enterPreview">Preview</button>
@@ -619,6 +637,37 @@ async function handleUrlImport(result: ImportedContent): Promise<void> {
619
637
  .cpub-autosave-status--saved { color: var(--green); }
620
638
  .cpub-autosave-status--error { color: var(--red); }
621
639
 
640
+ .cpub-topbar-undo-redo {
641
+ display: flex;
642
+ align-items: center;
643
+ gap: 2px;
644
+ margin-right: 4px;
645
+ flex-shrink: 0;
646
+ }
647
+
648
+ .cpub-topbar-icon-btn {
649
+ width: 30px;
650
+ height: 30px;
651
+ display: flex;
652
+ align-items: center;
653
+ justify-content: center;
654
+ background: none;
655
+ border: var(--border-width-default) solid transparent;
656
+ color: var(--text-dim);
657
+ cursor: pointer;
658
+ font-size: 12px;
659
+ padding: 0;
660
+ }
661
+ .cpub-topbar-icon-btn:hover:not(:disabled) {
662
+ background: var(--surface2);
663
+ border-color: var(--border2);
664
+ color: var(--text);
665
+ }
666
+ .cpub-topbar-icon-btn:disabled {
667
+ opacity: 0.3;
668
+ cursor: not-allowed;
669
+ }
670
+
622
671
  .cpub-mode-tabs {
623
672
  display: flex;
624
673
  background: var(--surface2);
@@ -736,6 +785,7 @@ async function handleUrlImport(result: ImportedContent): Promise<void> {
736
785
  .cpub-editor-back { margin-left: 0; }
737
786
  .cpub-topbar-title-input { max-width: none; font-size: 12px; padding: 3px 6px; }
738
787
  .cpub-autosave-status { display: none; }
788
+ .cpub-topbar-undo-redo { display: none; }
739
789
  .cpub-mode-tabs { margin: 0 6px; padding: 1px; }
740
790
  .cpub-mode-tab { padding: 4px 10px; font-size: 10px; }
741
791
  .cpub-topbar-spacer { display: none; }
@@ -3,11 +3,13 @@ import { contentItems, users } from '@commonpub/schema';
3
3
  import { eq, and, isNull } from 'drizzle-orm';
4
4
 
5
5
  /**
6
- * New-format content AP Article endpoint.
7
- * URI: /u/{username}/{type}/{slug}
6
+ * Middleware: serve ActivityPub Article JSON-LD for content URIs.
8
7
  *
9
- * Serves Article JSON-LD when requested with AP Accept header.
10
- * Browsers see the Nuxt page instead (this handler returns nothing for non-AP requests).
8
+ * Matches /u/{username}/{type}/{slug} with AP Accept headers.
9
+ * Non-AP requests pass through to the Nuxt page renderer.
10
+ *
11
+ * This MUST be a middleware (not a server route) because a server route
12
+ * returning undefined sends HTTP 204, which prevents the Nuxt page from rendering.
11
13
  */
12
14
  export default defineEventHandler(async (event) => {
13
15
  const accept = getRequestHeader(event, 'accept') ?? '';
@@ -17,12 +19,14 @@ export default defineEventHandler(async (event) => {
17
19
 
18
20
  if (!isAPRequest) return;
19
21
 
22
+ const path = getRequestURL(event).pathname;
23
+ const match = path.match(/^\/u\/([a-zA-Z0-9_-]+)\/([a-z]+)\/([a-z0-9][a-z0-9_-]*)$/);
24
+ if (!match) return;
25
+
20
26
  const config = useConfig();
21
27
  if (!config.features.federation) return;
22
28
 
23
- const username = getRouterParam(event, 'username');
24
- const type = getRouterParam(event, 'type');
25
- const slug = getRouterParam(event, 'slug');
29
+ const [, username, type, slug] = match;
26
30
  if (!username || !type || !slug) return;
27
31
 
28
32
  const db = useDB();
@@ -51,7 +55,7 @@ export default defineEventHandler(async (event) => {
51
55
 
52
56
  setResponseHeader(event, 'content-type', 'application/activity+json');
53
57
 
54
- const article = contentToArticle(
58
+ return contentToArticle(
55
59
  {
56
60
  id: row.content.id,
57
61
  type: row.content.type,
@@ -68,6 +72,4 @@ export default defineEventHandler(async (event) => {
68
72
  { username: row.author.username, displayName: row.author.displayName ?? row.author.username },
69
73
  domain,
70
74
  );
71
-
72
- return article;
73
75
  });