@commonpub/layer 0.4.6 → 0.4.8

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,47 +19,61 @@ function toggleRight(): void {
19
19
  </script>
20
20
 
21
21
  <template>
22
- <div class="cpub-editor-shell-inner">
23
- <!-- Mobile sidebar toggles -->
24
- <div class="cpub-editor-mobile-toggles">
25
- <button v-if="showLeftSidebar" class="cpub-editor-toggle-btn" aria-label="Toggle blocks panel" @click="toggleLeft">
26
- <i class="fa-solid fa-layer-group"></i>
27
- </button>
28
- <button v-if="showRightSidebar" class="cpub-editor-toggle-btn" aria-label="Toggle properties panel" @click="toggleRight">
29
- <i class="fa-solid fa-sliders"></i>
30
- </button>
22
+ <div class="cpub-editor-shell-wrapper">
23
+ <div class="cpub-editor-shell-inner">
24
+ <!-- Mobile sidebar toggles -->
25
+ <div class="cpub-editor-mobile-toggles">
26
+ <button v-if="showLeftSidebar" class="cpub-editor-toggle-btn" aria-label="Toggle blocks panel" @click="toggleLeft">
27
+ <i class="fa-solid fa-layer-group"></i>
28
+ </button>
29
+ <button v-if="showRightSidebar" class="cpub-editor-toggle-btn" aria-label="Toggle properties panel" @click="toggleRight">
30
+ <i class="fa-solid fa-sliders"></i>
31
+ </button>
32
+ </div>
33
+
34
+ <!-- Left sidebar -->
35
+ <aside
36
+ v-if="showLeftSidebar"
37
+ class="cpub-editor-left"
38
+ :class="{ 'cpub-editor-sidebar-open': leftOpen }"
39
+ aria-label="Editor sidebar"
40
+ >
41
+ <slot name="left" />
42
+ </aside>
43
+
44
+ <!-- Overlay for mobile sidebars -->
45
+ <div v-if="leftOpen || rightOpen" class="cpub-editor-overlay" @click="leftOpen = false; rightOpen = false" />
46
+
47
+ <div class="cpub-editor-center">
48
+ <slot />
49
+ </div>
50
+
51
+ <!-- Right sidebar -->
52
+ <aside
53
+ v-if="showRightSidebar"
54
+ class="cpub-editor-right"
55
+ :class="{ 'cpub-editor-sidebar-open': rightOpen }"
56
+ aria-label="Properties"
57
+ >
58
+ <slot name="right" />
59
+ </aside>
31
60
  </div>
32
61
 
33
- <!-- Left sidebar -->
34
- <aside
35
- v-if="showLeftSidebar"
36
- class="cpub-editor-left"
37
- :class="{ 'cpub-editor-sidebar-open': leftOpen }"
38
- aria-label="Editor sidebar"
39
- >
40
- <slot name="left" />
41
- </aside>
42
-
43
- <!-- Overlay for mobile sidebars -->
44
- <div v-if="leftOpen || rightOpen" class="cpub-editor-overlay" @click="leftOpen = false; rightOpen = false" />
45
-
46
- <div class="cpub-editor-center">
47
- <slot />
62
+ <!-- Status bar -->
63
+ <div v-if="$slots.status" class="cpub-editor-status-bar">
64
+ <slot name="status" />
48
65
  </div>
49
-
50
- <!-- Right sidebar -->
51
- <aside
52
- v-if="showRightSidebar"
53
- class="cpub-editor-right"
54
- :class="{ 'cpub-editor-sidebar-open': rightOpen }"
55
- aria-label="Properties"
56
- >
57
- <slot name="right" />
58
- </aside>
59
66
  </div>
60
67
  </template>
61
68
 
62
69
  <style scoped>
70
+ .cpub-editor-shell-wrapper {
71
+ display: flex;
72
+ flex-direction: column;
73
+ flex: 1;
74
+ overflow: hidden;
75
+ }
76
+
63
77
  .cpub-editor-shell-inner {
64
78
  display: flex;
65
79
  flex: 1;
@@ -67,6 +81,20 @@ function toggleRight(): void {
67
81
  position: relative;
68
82
  }
69
83
 
84
+ .cpub-editor-status-bar {
85
+ height: 28px;
86
+ flex-shrink: 0;
87
+ background: var(--surface);
88
+ border-top: var(--border-width-default) solid var(--border);
89
+ display: flex;
90
+ align-items: center;
91
+ padding: 0 12px;
92
+ gap: 14px;
93
+ font-family: var(--font-mono);
94
+ font-size: 10px;
95
+ color: var(--text-faint);
96
+ }
97
+
70
98
  .cpub-editor-left {
71
99
  width: 220px;
72
100
  flex-shrink: 0;
@@ -1,13 +1,23 @@
1
1
  <script setup lang="ts">
2
2
  import type { ContentViewData } from '../../composables/useEngagement';
3
3
  import type { BlockTuple } from '@commonpub/editor';
4
+ import { isExplainerDocument } from '@commonpub/explainer';
5
+ import type { ExplainerDocument } from '@commonpub/explainer';
6
+ import { ScrollViewer } from '@commonpub/explainer/vue';
4
7
 
5
8
  const props = defineProps<{
6
9
  content: ContentViewData;
7
10
  federatedId?: string;
8
11
  }>();
9
12
 
13
+ // Detect V2 ExplainerDocument format vs legacy BlockTuple[] format
14
+ const isV2Format = computed(() => isExplainerDocument(props.content?.content));
15
+ const v2Document = computed<ExplainerDocument | null>(() =>
16
+ isV2Format.value ? (props.content.content as unknown as ExplainerDocument) : null,
17
+ );
18
+
10
19
  const blocks = computed<BlockTuple[]>(() => {
20
+ if (isV2Format.value) return []; // V2 uses its own viewer
11
21
  const raw = props.content?.content;
12
22
  if (!Array.isArray(raw)) return [];
13
23
  return raw as BlockTuple[];
@@ -154,7 +164,11 @@ onUnmounted(() => { document.removeEventListener('keydown', onKeydown); });
154
164
  </script>
155
165
 
156
166
  <template>
157
- <div class="cpub-explainer-view">
167
+ <!-- V2 Scroll Viewer for ExplainerDocument format -->
168
+ <ScrollViewer v-if="isV2Format && v2Document" :document="v2Document" />
169
+
170
+ <!-- Legacy slide-deck viewer for BlockTuple[] format -->
171
+ <div v-else class="cpub-explainer-view">
158
172
  <!-- PROGRESS BAR -->
159
173
  <div class="cpub-progress-line">
160
174
  <div class="cpub-progress-line-fill" :style="{ width: progressPct + '%' }"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonpub/layer",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -51,14 +51,15 @@
51
51
  "vue-router": "^4.3.0",
52
52
  "zod": "^4.3.6",
53
53
  "@commonpub/config": "0.8.0",
54
- "@commonpub/auth": "0.5.0",
55
54
  "@commonpub/docs": "0.5.2",
56
55
  "@commonpub/editor": "0.5.0",
57
- "@commonpub/server": "2.22.0",
58
- "@commonpub/ui": "0.8.4",
59
56
  "@commonpub/learning": "0.5.0",
57
+ "@commonpub/explainer": "0.5.2",
58
+ "@commonpub/schema": "0.8.13",
60
59
  "@commonpub/protocol": "0.9.5",
61
- "@commonpub/schema": "0.8.13"
60
+ "@commonpub/ui": "0.8.4",
61
+ "@commonpub/server": "2.22.1",
62
+ "@commonpub/auth": "0.5.0"
62
63
  },
63
64
  "devDependencies": {
64
65
  "@testing-library/jest-dom": "^6.9.1",
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { TocEntry } from '@commonpub/docs';
3
+ import type { BlockTuple } from '@commonpub/editor';
3
4
 
4
5
  const route = useRoute();
5
6
  const siteSlug = computed(() => route.params.siteSlug as string);
@@ -12,19 +13,29 @@ const { data: site } = useLazyFetch(() => `/api/docs/${siteSlug.value}`);
12
13
  const { data: nav } = useLazyFetch(() => `/api/docs/${siteSlug.value}/nav`);
13
14
  const { data: pages } = useLazyFetch(() => `/api/docs/${siteSlug.value}/pages`);
14
15
 
15
- // Fetch the rendered page (server-side markdown rendering)
16
+ // Fetch the rendered page (server-side markdown rendering or block content)
16
17
  interface RenderedPage {
17
18
  id: string;
18
19
  title: string;
19
20
  slug: string;
20
- content: string;
21
+ content: string | BlockTuple[];
21
22
  sortOrder: number;
22
23
  parentId: string | null;
23
- html: string;
24
+ html: string | null;
24
25
  toc: TocEntry[];
25
26
  frontmatter: { title?: string; description?: string };
27
+ format?: 'blocks' | 'markdown';
26
28
  }
27
29
 
30
+ const isBlockContent = computed(() =>
31
+ renderedPage.value?.format === 'blocks' || Array.isArray(renderedPage.value?.content),
32
+ );
33
+
34
+ const blockContent = computed<BlockTuple[]>(() => {
35
+ if (!renderedPage.value || !isBlockContent.value) return [];
36
+ return renderedPage.value.content as BlockTuple[];
37
+ });
38
+
28
39
  const { data: renderedPage, pending: pagePending, error: pageError, refresh: refreshPage } = useLazyFetch<RenderedPage>(
29
40
  () => `/api/docs/${siteSlug.value}/pages/${pagePath.value}`,
30
41
  { key: `doc-page-${siteSlug.value}-${pagePath.value}` },
@@ -89,7 +100,7 @@ const activeHeadingId = ref('');
89
100
  // Scroll spy for TOC
90
101
  function setupScrollSpy(): void {
91
102
  if (!import.meta.client) return;
92
- const headings = document.querySelectorAll('.docs-content h2[id], .docs-content h3[id]');
103
+ const headings = document.querySelectorAll('.docs-content h2[id], .docs-content h3[id], .docs-content .cpub-block-heading[id]');
93
104
  if (!headings.length) return;
94
105
  const observer = new IntersectionObserver(
95
106
  (entries) => {
@@ -197,7 +208,7 @@ useSeoMeta({
197
208
  <aside class="docs-sidebar" :class="{ open: sidebarOpen }" aria-label="Documentation navigation">
198
209
  <div class="docs-sidebar-header">
199
210
  <NuxtLink :to="`/docs/${siteSlug}`" class="docs-sidebar-title">{{ site.name }}</NuxtLink>
200
- <NuxtLink v-if="isOwner" :to="`/docs/${siteSlug}/edit`" class="docs-edit-link" aria-label="Edit docs">
211
+ <NuxtLink v-if="isOwner" :to="`/docs/${siteSlug}/edit?page=${pagePath}`" class="docs-edit-link" aria-label="Edit this page">
201
212
  <i class="fa-solid fa-pen"></i>
202
213
  </NuxtLink>
203
214
  </div>
@@ -301,7 +312,10 @@ useSeoMeta({
301
312
  <h1 class="docs-page-title">{{ renderedPage.title }}</h1>
302
313
 
303
314
  <!-- Rendered Content -->
304
- <div class="docs-content cpub-prose" v-html="renderedPage.html" />
315
+ <div v-if="isBlockContent" class="docs-content cpub-prose">
316
+ <BlocksBlockContentRenderer :blocks="blockContent" />
317
+ </div>
318
+ <div v-else class="docs-content cpub-prose" v-html="renderedPage.html" />
305
319
 
306
320
  <!-- Prev / Next -->
307
321
  <div class="docs-prev-next" v-if="prevNextLinks.prev || prevNextLinks.next">