@commonpub/layer 0.7.5 → 0.7.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commonpub/layer",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"files": [
|
|
@@ -50,15 +50,15 @@
|
|
|
50
50
|
"vue": "^3.4.0",
|
|
51
51
|
"vue-router": "^4.3.0",
|
|
52
52
|
"zod": "^4.3.6",
|
|
53
|
-
"@commonpub/config": "0.9.0",
|
|
54
53
|
"@commonpub/auth": "0.5.0",
|
|
55
|
-
"@commonpub/docs": "0.6.2",
|
|
56
54
|
"@commonpub/editor": "0.7.0",
|
|
55
|
+
"@commonpub/config": "0.9.0",
|
|
57
56
|
"@commonpub/explainer": "0.7.4",
|
|
58
|
-
"@commonpub/
|
|
57
|
+
"@commonpub/docs": "0.6.2",
|
|
59
58
|
"@commonpub/learning": "0.5.0",
|
|
60
|
-
"@commonpub/
|
|
61
|
-
"@commonpub/
|
|
59
|
+
"@commonpub/protocol": "0.9.7",
|
|
60
|
+
"@commonpub/schema": "0.9.4",
|
|
61
|
+
"@commonpub/server": "2.27.6",
|
|
62
62
|
"@commonpub/ui": "0.8.5"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { provide } from 'vue';
|
|
2
3
|
import type { BlockTuple } from '@commonpub/editor';
|
|
3
|
-
import { BlockCanvas, EditorShell, useBlockEditor, type BlockTypeGroup } from '@commonpub/editor/vue';
|
|
4
|
+
import { BlockCanvas, EditorShell, useBlockEditor, UPLOAD_HANDLER_KEY, type BlockTypeGroup } from '@commonpub/editor/vue';
|
|
4
5
|
import type { PageTreeItem } from '../../../components/editors/DocsPageTree.vue';
|
|
5
6
|
|
|
6
7
|
definePageMeta({ layout: false, middleware: 'auth' });
|
|
@@ -9,6 +10,15 @@ const route = useRoute();
|
|
|
9
10
|
const siteSlug = computed(() => route.params.siteSlug as string);
|
|
10
11
|
const { show: toast } = useToast();
|
|
11
12
|
|
|
13
|
+
// Provide upload handler to block components (ImageBlock, GalleryBlock)
|
|
14
|
+
provide(UPLOAD_HANDLER_KEY, async (file: File) => {
|
|
15
|
+
const formData = new FormData();
|
|
16
|
+
formData.append('file', file);
|
|
17
|
+
formData.append('purpose', 'content');
|
|
18
|
+
const res = await $fetch<{ url: string; width?: number | null; height?: number | null }>('/api/files/upload', { method: 'POST', body: formData });
|
|
19
|
+
return { url: res.url, width: res.width ?? null, height: res.height ?? null };
|
|
20
|
+
});
|
|
21
|
+
|
|
12
22
|
// ═══ DATA FETCHING ═══
|
|
13
23
|
const { data: site, refresh: refreshSite } = await useFetch<{ id: string; name: string; slug: string; description: string; ownerId: string; versions?: Array<{ id: string; version: string; isDefault: boolean }> }>(() => `/api/docs/${siteSlug.value}`);
|
|
14
24
|
|
|
@@ -190,20 +190,24 @@ useHead({
|
|
|
190
190
|
|
|
191
191
|
<!-- Replies -->
|
|
192
192
|
<div class="cpub-replies-section">
|
|
193
|
-
<h3 v-if="replies.length" class="cpub-replies-title">{{ repliesData?.total ?? 0 }}
|
|
193
|
+
<h3 v-if="replies.length" class="cpub-replies-title">{{ repliesData?.total ?? 0 }} Replies</h3>
|
|
194
194
|
<div v-for="reply in replies" :key="reply.id" class="cpub-reply">
|
|
195
195
|
<div class="cpub-reply-author">
|
|
196
196
|
<div class="cpub-reply-avatar">
|
|
197
|
-
<img v-if="reply.author?.avatarUrl" :src="reply.author.
|
|
198
|
-
<span v-else>{{ (reply.author?.displayName || reply.
|
|
197
|
+
<img v-if="reply.author?.avatarUrl || reply.remoteActorAvatarUrl" :src="(reply.author?.avatarUrl || reply.remoteActorAvatarUrl)!" :alt="reply.author?.displayName || reply.remoteActorName || 'User'" class="cpub-reply-avatar-img" />
|
|
198
|
+
<span v-else>{{ (reply.author?.displayName || reply.remoteActorName || 'U').charAt(0).toUpperCase() }}</span>
|
|
199
199
|
</div>
|
|
200
200
|
<NuxtLink v-if="reply.author" :to="`/u/${reply.author.username}`" class="cpub-reply-author-name">{{ reply.author.displayName || reply.author.username }}</NuxtLink>
|
|
201
|
+
<span v-else class="cpub-reply-author-name cpub-reply-remote">
|
|
202
|
+
{{ reply.remoteActorName || 'Remote user' }}
|
|
203
|
+
<i class="fa-solid fa-globe" style="font-size: 9px; opacity: 0.5; margin-left: 2px"></i>
|
|
204
|
+
</span>
|
|
201
205
|
<span class="cpub-post-sep">·</span>
|
|
202
206
|
<time class="cpub-post-time">{{ formatDate(reply.createdAt) }}</time>
|
|
203
207
|
</div>
|
|
204
208
|
<div class="cpub-reply-content"><MentionText :text="reply.content" /></div>
|
|
205
209
|
<div class="cpub-reply-actions">
|
|
206
|
-
<button v-if="isAuthenticated" class="cpub-reply-btn" @click="replyingTo = reply.id; replyContent = `@${reply.author
|
|
210
|
+
<button v-if="isAuthenticated && reply.author" class="cpub-reply-btn" @click="replyingTo = reply.id; replyContent = `@${reply.author.username} `">
|
|
207
211
|
<i class="fa-solid fa-reply"></i> Reply
|
|
208
212
|
</button>
|
|
209
213
|
</div>
|
|
@@ -213,10 +217,14 @@ useHead({
|
|
|
213
217
|
<div v-for="child in reply.replies" :key="child.id" class="cpub-reply cpub-reply-nested">
|
|
214
218
|
<div class="cpub-reply-author">
|
|
215
219
|
<div class="cpub-reply-avatar">
|
|
216
|
-
<img v-if="child.author?.avatarUrl" :src="child.author.
|
|
217
|
-
<span v-else>{{ (child.author?.displayName || child.
|
|
220
|
+
<img v-if="child.author?.avatarUrl || child.remoteActorAvatarUrl" :src="(child.author?.avatarUrl || child.remoteActorAvatarUrl)!" :alt="child.author?.displayName || child.remoteActorName || 'User'" class="cpub-reply-avatar-img" />
|
|
221
|
+
<span v-else>{{ (child.author?.displayName || child.remoteActorName || 'U').charAt(0).toUpperCase() }}</span>
|
|
218
222
|
</div>
|
|
219
223
|
<NuxtLink v-if="child.author" :to="`/u/${child.author.username}`" class="cpub-reply-author-name">{{ child.author.displayName || child.author.username }}</NuxtLink>
|
|
224
|
+
<span v-else class="cpub-reply-author-name cpub-reply-remote">
|
|
225
|
+
{{ child.remoteActorName || 'Remote user' }}
|
|
226
|
+
<i class="fa-solid fa-globe" style="font-size: 9px; opacity: 0.5; margin-left: 2px"></i>
|
|
227
|
+
</span>
|
|
220
228
|
<span class="cpub-post-sep">·</span>
|
|
221
229
|
<time class="cpub-post-time">{{ formatDate(child.createdAt) }}</time>
|
|
222
230
|
</div>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Component } from 'vue';
|
|
3
|
+
import { provide } from 'vue';
|
|
3
4
|
import type { BlockTuple } from '@commonpub/editor';
|
|
4
|
-
import { BlockCanvas, useBlockEditor } from '@commonpub/editor/vue';
|
|
5
|
+
import { BlockCanvas, useBlockEditor, UPLOAD_HANDLER_KEY, SEARCH_PRODUCTS_KEY } from '@commonpub/editor/vue';
|
|
5
6
|
import { isExplainerDocument, createEmptyDocument } from '@commonpub/explainer';
|
|
6
7
|
import type { ExplainerDocument } from '@commonpub/explainer';
|
|
7
8
|
import { ExplainerSectionEditor } from '@commonpub/explainer/vue';
|
|
@@ -93,6 +94,20 @@ const { errors: publishErrors, showErrors: showPublishErrors, validate, dismiss:
|
|
|
93
94
|
getBlockTuples: getContentForSave as () => BlockTuple[],
|
|
94
95
|
});
|
|
95
96
|
|
|
97
|
+
// --- Provide upload + search handlers to block components via inject ---
|
|
98
|
+
provide(UPLOAD_HANDLER_KEY, async (file: File) => {
|
|
99
|
+
const formData = new FormData();
|
|
100
|
+
formData.append('file', file);
|
|
101
|
+
formData.append('purpose', 'content');
|
|
102
|
+
const res = await $fetch<{ url: string; width?: number | null; height?: number | null }>('/api/files/upload', { method: 'POST', body: formData });
|
|
103
|
+
return { url: res.url, width: res.width ?? null, height: res.height ?? null };
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
provide(SEARCH_PRODUCTS_KEY, async (query: string) => {
|
|
107
|
+
const res = await $fetch<{ items: Array<{ id: string; name: string; slug: string; description: string | null; category: string | null; imageUrl: string | null; purchaseUrl: string | null }> }>(`/api/products?q=${encodeURIComponent(query)}&limit=10`);
|
|
108
|
+
return res.items ?? [];
|
|
109
|
+
});
|
|
110
|
+
|
|
96
111
|
// --- Specialized editor component map ---
|
|
97
112
|
const editorMap: Record<string, Component> = {
|
|
98
113
|
article: resolveComponent('EditorsArticleEditor') as Component,
|