@commonpub/layer 0.7.2 → 0.7.4
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/CommentSection.vue +4 -4
- package/components/ContentAttachments.vue +4 -4
- package/components/DiscussionItem.vue +1 -1
- package/components/FeedItem.vue +1 -1
- package/components/MessageThread.vue +1 -1
- package/components/NotificationItem.vue +1 -1
- package/components/editors/ArticleEditor.vue +11 -12
- package/components/editors/BlogEditor.vue +17 -18
- package/components/editors/ExplainerEditor.vue +13 -14
- package/components/editors/ProjectEditor.vue +17 -18
- package/components/hub/HubHero.vue +2 -2
- package/components/hub/HubLayout.vue +1 -1
- package/components/hub/HubProducts.vue +2 -2
- package/components/hub/HubSidebarCard.vue +2 -2
- package/components/views/ArticleView.vue +15 -15
- package/components/views/BlogView.vue +14 -14
- package/components/views/ExplainerView.vue +11 -11
- package/components/views/ProjectView.vue +36 -36
- package/composables/useMarkdownImport.ts +1 -1
- package/package.json +9 -9
- package/pages/admin/theme.vue +1 -1
- package/pages/authorize_interaction.vue +1 -1
- package/pages/docs/[siteSlug]/edit.vue +4 -4
- package/pages/federated-hubs/[id]/index.vue +6 -6
- package/pages/federated-hubs/[id]/posts/[postId].vue +5 -5
- package/pages/hubs/[slug]/index.vue +6 -6
- package/pages/hubs/[slug]/posts/[postId].vue +6 -6
- package/pages/hubs/index.vue +2 -2
- package/pages/mirror/[id].vue +2 -2
- package/pages/u/[username]/[type]/[slug]/edit.vue +2 -1
- package/server/api/docs/[siteSlug]/search.get.ts +2 -1
- package/theme/components.css +2 -2
- package/theme/layouts.css +2 -2
- package/theme/prose.css +4 -4
- package/components/editors/BlockCanvas.vue +0 -487
- package/components/editors/BlockInsertZone.vue +0 -84
- package/components/editors/BlockPicker.vue +0 -285
- package/components/editors/BlockWrapper.vue +0 -192
- package/components/editors/EditorBlocks.vue +0 -248
- package/components/editors/EditorSection.vue +0 -81
- package/components/editors/EditorShell.vue +0 -196
- package/components/editors/EditorTagInput.vue +0 -114
- package/components/editors/EditorVisibility.vue +0 -110
- package/components/editors/blocks/BuildStepBlock.vue +0 -102
- package/components/editors/blocks/CalloutBlock.vue +0 -122
- package/components/editors/blocks/CheckpointBlock.vue +0 -27
- package/components/editors/blocks/CodeBlock.vue +0 -177
- package/components/editors/blocks/DividerBlock.vue +0 -22
- package/components/editors/blocks/DownloadsBlock.vue +0 -41
- package/components/editors/blocks/EmbedBlock.vue +0 -20
- package/components/editors/blocks/GalleryBlock.vue +0 -236
- package/components/editors/blocks/HeadingBlock.vue +0 -96
- package/components/editors/blocks/ImageBlock.vue +0 -271
- package/components/editors/blocks/MarkdownBlock.vue +0 -258
- package/components/editors/blocks/MathBlock.vue +0 -37
- package/components/editors/blocks/PartsListBlock.vue +0 -358
- package/components/editors/blocks/QuizBlock.vue +0 -47
- package/components/editors/blocks/QuoteBlock.vue +0 -101
- package/components/editors/blocks/SectionHeaderBlock.vue +0 -130
- package/components/editors/blocks/SliderBlock.vue +0 -318
- package/components/editors/blocks/TextBlock.vue +0 -201
- package/components/editors/blocks/ToolListBlock.vue +0 -70
- package/components/editors/blocks/VideoBlock.vue +0 -22
- package/composables/useBlockEditor.ts +0 -187
|
@@ -266,7 +266,7 @@ async function deleteComment(id: string): Promise<void> {
|
|
|
266
266
|
color: var(--text-faint);
|
|
267
267
|
background: var(--surface2);
|
|
268
268
|
padding: 1px 6px;
|
|
269
|
-
border:
|
|
269
|
+
border: var(--border-width-default) solid var(--border2);
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
.cpub-comment-form {
|
|
@@ -290,7 +290,7 @@ async function deleteComment(id: string): Promise<void> {
|
|
|
290
290
|
gap: 8px;
|
|
291
291
|
padding: 6px 10px;
|
|
292
292
|
background: var(--surface2);
|
|
293
|
-
border:
|
|
293
|
+
border: var(--border-width-default) solid var(--border);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
.cpub-cancel-reply {
|
|
@@ -313,7 +313,7 @@ async function deleteComment(id: string): Promise<void> {
|
|
|
313
313
|
margin-bottom: 12px;
|
|
314
314
|
padding: 8px 12px;
|
|
315
315
|
background: var(--accent-bg);
|
|
316
|
-
border:
|
|
316
|
+
border: var(--border-width-default) solid var(--accent-border);
|
|
317
317
|
border-radius: 8px;
|
|
318
318
|
}
|
|
319
319
|
.cpub-comment-fed-notice i { color: var(--accent); font-size: 11px; }
|
|
@@ -461,6 +461,6 @@ async function deleteComment(id: string): Promise<void> {
|
|
|
461
461
|
margin-bottom: 12px;
|
|
462
462
|
padding: 8px 12px;
|
|
463
463
|
background: var(--green-bg, rgba(34, 197, 94, 0.08));
|
|
464
|
-
border:
|
|
464
|
+
border: var(--border-width-default) solid var(--green-border, rgba(34, 197, 94, 0.2));
|
|
465
465
|
}
|
|
466
466
|
</style>
|
|
@@ -57,7 +57,7 @@ const safeAttachments = computed(() =>
|
|
|
57
57
|
.cpub-attachments {
|
|
58
58
|
margin: 28px 0;
|
|
59
59
|
padding-top: 16px;
|
|
60
|
-
border-top:
|
|
60
|
+
border-top: var(--border-width-default) solid var(--border);
|
|
61
61
|
}
|
|
62
62
|
.cpub-attachments-label {
|
|
63
63
|
font-size: 10px;
|
|
@@ -77,7 +77,7 @@ const safeAttachments = computed(() =>
|
|
|
77
77
|
align-items: center;
|
|
78
78
|
gap: 8px;
|
|
79
79
|
padding: 8px 12px;
|
|
80
|
-
border:
|
|
80
|
+
border: var(--border-width-default) solid var(--border);
|
|
81
81
|
background: var(--surface);
|
|
82
82
|
text-decoration: none;
|
|
83
83
|
color: var(--text-dim);
|
|
@@ -92,7 +92,7 @@ const safeAttachments = computed(() =>
|
|
|
92
92
|
width: 40px;
|
|
93
93
|
height: 40px;
|
|
94
94
|
object-fit: cover;
|
|
95
|
-
border:
|
|
95
|
+
border: var(--border-width-default) solid var(--border2);
|
|
96
96
|
}
|
|
97
97
|
.cpub-attachment-icon {
|
|
98
98
|
width: 32px;
|
|
@@ -101,7 +101,7 @@ const safeAttachments = computed(() =>
|
|
|
101
101
|
align-items: center;
|
|
102
102
|
justify-content: center;
|
|
103
103
|
background: var(--surface2);
|
|
104
|
-
border:
|
|
104
|
+
border: var(--border-width-default) solid var(--border2);
|
|
105
105
|
color: var(--text-faint);
|
|
106
106
|
font-size: 14px;
|
|
107
107
|
}
|
package/components/FeedItem.vue
CHANGED
|
@@ -271,7 +271,7 @@ const formattedDate = computed((): string => {
|
|
|
271
271
|
|
|
272
272
|
.cpub-feed-stat-btn {
|
|
273
273
|
background: none;
|
|
274
|
-
border:
|
|
274
|
+
border: var(--border-width-default) solid transparent;
|
|
275
275
|
cursor: pointer;
|
|
276
276
|
padding: 2px 8px;
|
|
277
277
|
border-radius: 4px;
|
|
@@ -103,7 +103,7 @@ const iconMap: Record<string, string> = {
|
|
|
103
103
|
align-items: center;
|
|
104
104
|
justify-content: center;
|
|
105
105
|
background: var(--surface);
|
|
106
|
-
border:
|
|
106
|
+
border: var(--border-width-default) solid var(--border);
|
|
107
107
|
border-radius: 50%;
|
|
108
108
|
font-size: 8px;
|
|
109
109
|
color: var(--text-dim);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type
|
|
3
|
-
import type { BlockTypeGroup } from './BlockPicker.vue';
|
|
2
|
+
import { BlockCanvas, EditorBlocks, EditorSection, EditorTagInput, EditorVisibility, type BlockEditor, type BlockTypeGroup } from '@commonpub/editor/vue';
|
|
4
3
|
|
|
5
4
|
const props = defineProps<{
|
|
6
5
|
blockEditor: BlockEditor;
|
|
@@ -201,7 +200,7 @@ const canvasMaxWidth = computed(() => {
|
|
|
201
200
|
|
|
202
201
|
<!-- Modules tab -->
|
|
203
202
|
<div v-if="activeLeftTab === 'modules'" class="cpub-ae-left-body">
|
|
204
|
-
<
|
|
203
|
+
<EditorBlocks :groups="blockTypes" :block-editor="blockEditor" />
|
|
205
204
|
</div>
|
|
206
205
|
|
|
207
206
|
<!-- Structure tab -->
|
|
@@ -267,7 +266,7 @@ const canvasMaxWidth = computed(() => {
|
|
|
267
266
|
<!-- Scrollable canvas -->
|
|
268
267
|
<div class="cpub-ae-canvas">
|
|
269
268
|
<div class="cpub-ae-canvas-inner" :style="{ maxWidth: canvasMaxWidth }">
|
|
270
|
-
<
|
|
269
|
+
<BlockCanvas :block-editor="blockEditor" :block-types="blockTypes" />
|
|
271
270
|
</div>
|
|
272
271
|
</div>
|
|
273
272
|
|
|
@@ -297,7 +296,7 @@ const canvasMaxWidth = computed(() => {
|
|
|
297
296
|
</div>
|
|
298
297
|
<div class="cpub-ae-right-body">
|
|
299
298
|
<!-- Content / Metadata -->
|
|
300
|
-
<
|
|
299
|
+
<EditorSection title="Content" icon="fa-pen-nib" :open="openSections.content" @toggle="toggleSection('content')">
|
|
301
300
|
<div class="cpub-ep-field">
|
|
302
301
|
<label class="cpub-ep-flabel">Slug</label>
|
|
303
302
|
<input class="cpub-ep-input" type="text" :value="metadata.slug" placeholder="auto-generated" @input="updateMeta('slug', ($event.target as HTMLInputElement).value)">
|
|
@@ -331,22 +330,22 @@ const canvasMaxWidth = computed(() => {
|
|
|
331
330
|
</div>
|
|
332
331
|
</template>
|
|
333
332
|
</div>
|
|
334
|
-
</
|
|
333
|
+
</EditorSection>
|
|
335
334
|
|
|
336
335
|
<!-- SEO -->
|
|
337
|
-
<
|
|
336
|
+
<EditorSection title="SEO" icon="fa-magnifying-glass" :open="openSections.seo" @toggle="toggleSection('seo')">
|
|
338
337
|
<div class="cpub-ep-field">
|
|
339
338
|
<label class="cpub-ep-flabel">Meta Description</label>
|
|
340
339
|
<textarea class="cpub-ep-textarea" rows="3" :value="metadata.seoDescription as string" placeholder="Search engine description..." @input="updateMeta('seoDescription', ($event.target as HTMLTextAreaElement).value)" />
|
|
341
340
|
<span class="cpub-ep-hint">{{ ((metadata.seoDescription as string) || '').length }}/160</span>
|
|
342
341
|
</div>
|
|
343
|
-
</
|
|
342
|
+
</EditorSection>
|
|
344
343
|
|
|
345
344
|
<!-- Publishing -->
|
|
346
|
-
<
|
|
345
|
+
<EditorSection title="Publishing" icon="fa-rocket" :open="openSections.publishing" @toggle="toggleSection('publishing')">
|
|
347
346
|
<div class="cpub-ep-field">
|
|
348
347
|
<label class="cpub-ep-flabel">Visibility</label>
|
|
349
|
-
<
|
|
348
|
+
<EditorVisibility :model-value="visibility" @update:model-value="onVisibilityUpdate" />
|
|
350
349
|
</div>
|
|
351
350
|
<div class="cpub-ep-field">
|
|
352
351
|
<label class="cpub-ep-flabel">Category</label>
|
|
@@ -362,9 +361,9 @@ const canvasMaxWidth = computed(() => {
|
|
|
362
361
|
</div>
|
|
363
362
|
<div class="cpub-ep-field">
|
|
364
363
|
<label class="cpub-ep-flabel">Tags</label>
|
|
365
|
-
<
|
|
364
|
+
<EditorTagInput :tags="tags" @update:tags="onTagsUpdate" />
|
|
366
365
|
</div>
|
|
367
|
-
</
|
|
366
|
+
</EditorSection>
|
|
368
367
|
</div>
|
|
369
368
|
</aside>
|
|
370
369
|
</div>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type
|
|
3
|
-
import type { BlockTypeGroup } from './BlockPicker.vue';
|
|
2
|
+
import { BlockCanvas, EditorBlocks, EditorSection, EditorTagInput, EditorVisibility, type BlockEditor, type BlockTypeGroup } from '@commonpub/editor/vue';
|
|
4
3
|
|
|
5
4
|
const props = defineProps<{
|
|
6
5
|
blockEditor: BlockEditor;
|
|
@@ -149,7 +148,7 @@ const canvasMaxWidth = computed(() => {
|
|
|
149
148
|
|
|
150
149
|
<!-- LEFT: Block Library -->
|
|
151
150
|
<aside class="cpub-be-library" :class="{ 'cpub-be-sidebar-open': mobileLeftOpen }" aria-label="Block library">
|
|
152
|
-
<
|
|
151
|
+
<EditorBlocks :groups="blockTypes" :block-editor="blockEditor" />
|
|
153
152
|
</aside>
|
|
154
153
|
|
|
155
154
|
<!-- CENTER: Canvas with toolbar, cover, title, subtitle, byline, blocks -->
|
|
@@ -231,7 +230,7 @@ const canvasMaxWidth = computed(() => {
|
|
|
231
230
|
</div>
|
|
232
231
|
|
|
233
232
|
<!-- Block editor canvas -->
|
|
234
|
-
<
|
|
233
|
+
<BlockCanvas :block-editor="blockEditor" :block-types="blockTypes" />
|
|
235
234
|
</div>
|
|
236
235
|
|
|
237
236
|
<!-- Word count bar -->
|
|
@@ -247,7 +246,7 @@ const canvasMaxWidth = computed(() => {
|
|
|
247
246
|
<aside class="cpub-be-right" :class="{ 'cpub-be-sidebar-open': mobileRightOpen }" aria-label="Blog properties">
|
|
248
247
|
<div class="cpub-be-right-body">
|
|
249
248
|
<!-- Meta -->
|
|
250
|
-
<
|
|
249
|
+
<EditorSection title="Meta" icon="fa-tag" :open="openSections.meta" @toggle="toggleSection('meta')">
|
|
251
250
|
<div class="cpub-ep-field">
|
|
252
251
|
<label class="cpub-ep-flabel">Slug</label>
|
|
253
252
|
<input class="cpub-ep-input cpub-ep-input-mono" type="text" :value="metadata.slug" placeholder="auto-generated" @input="updateMeta('slug', ($event.target as HTMLInputElement).value)">
|
|
@@ -266,21 +265,21 @@ const canvasMaxWidth = computed(() => {
|
|
|
266
265
|
</div>
|
|
267
266
|
<div class="cpub-ep-field">
|
|
268
267
|
<label class="cpub-ep-flabel">Tags</label>
|
|
269
|
-
<
|
|
268
|
+
<EditorTagInput :tags="tags" @update:tags="onTagsUpdate" />
|
|
270
269
|
</div>
|
|
271
|
-
</
|
|
270
|
+
</EditorSection>
|
|
272
271
|
|
|
273
272
|
<!-- Excerpt / Description -->
|
|
274
|
-
<
|
|
273
|
+
<EditorSection title="Excerpt" icon="fa-align-left" :open="openSections.excerpt" @toggle="toggleSection('excerpt')">
|
|
275
274
|
<div class="cpub-ep-field">
|
|
276
275
|
<label class="cpub-ep-flabel">Custom Excerpt</label>
|
|
277
276
|
<textarea class="cpub-ep-textarea" rows="3" :value="(metadata.description as string) || ''" placeholder="Short description shown in feed previews..." @input="updateMeta('description', ($event.target as HTMLTextAreaElement).value)" />
|
|
278
277
|
<span class="cpub-ep-hint cpub-ep-hint-right">{{ ((metadata.description as string) || '').length }} / 300</span>
|
|
279
278
|
</div>
|
|
280
|
-
</
|
|
279
|
+
</EditorSection>
|
|
281
280
|
|
|
282
281
|
<!-- SEO Preview -->
|
|
283
|
-
<
|
|
282
|
+
<EditorSection title="SEO Preview" icon="fa-brands fa-google" :open="openSections.seo" @toggle="toggleSection('seo')">
|
|
284
283
|
<div class="cpub-be-seo-card">
|
|
285
284
|
<div class="cpub-be-seo-url">
|
|
286
285
|
<span class="cpub-be-seo-favicon">C</span>
|
|
@@ -294,13 +293,13 @@ const canvasMaxWidth = computed(() => {
|
|
|
294
293
|
<textarea class="cpub-ep-textarea" rows="3" :value="(metadata.seoDescription as string) || ''" placeholder="Search engine description..." @input="updateMeta('seoDescription', ($event.target as HTMLTextAreaElement).value)" />
|
|
295
294
|
<span class="cpub-ep-hint cpub-ep-hint-right">{{ ((metadata.seoDescription as string) || '').length }}/160</span>
|
|
296
295
|
</div>
|
|
297
|
-
</
|
|
296
|
+
</EditorSection>
|
|
298
297
|
|
|
299
298
|
<!-- Publishing -->
|
|
300
|
-
<
|
|
299
|
+
<EditorSection title="Publishing" icon="fa-globe" :open="openSections.publishing" @toggle="toggleSection('publishing')">
|
|
301
300
|
<div class="cpub-ep-field">
|
|
302
301
|
<label class="cpub-ep-flabel">Visibility</label>
|
|
303
|
-
<
|
|
302
|
+
<EditorVisibility :model-value="(metadata.visibility as string) || 'public'" @update:model-value="(v: string) => updateMeta('visibility', v)" />
|
|
304
303
|
</div>
|
|
305
304
|
<div class="cpub-ep-field">
|
|
306
305
|
<label class="cpub-be-schedule-row">
|
|
@@ -319,10 +318,10 @@ const canvasMaxWidth = computed(() => {
|
|
|
319
318
|
<label class="cpub-ep-flabel">Series <span class="cpub-ep-optional">(optional)</span></label>
|
|
320
319
|
<input class="cpub-ep-input" type="text" :value="metadata.series" placeholder="e.g. Home Lab Chronicles" @input="updateMeta('series', ($event.target as HTMLInputElement).value)">
|
|
321
320
|
</div>
|
|
322
|
-
</
|
|
321
|
+
</EditorSection>
|
|
323
322
|
|
|
324
323
|
<!-- Author -->
|
|
325
|
-
<
|
|
324
|
+
<EditorSection title="Author" icon="fa-user" :open="openSections.author" @toggle="toggleSection('author')">
|
|
326
325
|
<div class="cpub-be-author-row">
|
|
327
326
|
<div class="cpub-be-author-av">{{ authorInitials }}</div>
|
|
328
327
|
<div class="cpub-be-author-info">
|
|
@@ -331,10 +330,10 @@ const canvasMaxWidth = computed(() => {
|
|
|
331
330
|
</div>
|
|
332
331
|
<span class="cpub-be-author-badge">You</span>
|
|
333
332
|
</div>
|
|
334
|
-
</
|
|
333
|
+
</EditorSection>
|
|
335
334
|
|
|
336
335
|
<!-- Social -->
|
|
337
|
-
<
|
|
336
|
+
<EditorSection title="Social" icon="fa-share-nodes" :open="openSections.social" @toggle="toggleSection('social')">
|
|
338
337
|
<div class="cpub-ep-field">
|
|
339
338
|
<label class="cpub-ep-flabel">Open Graph Image</label>
|
|
340
339
|
<div class="cpub-be-og-thumb">
|
|
@@ -344,7 +343,7 @@ const canvasMaxWidth = computed(() => {
|
|
|
344
343
|
</div>
|
|
345
344
|
</div>
|
|
346
345
|
</div>
|
|
347
|
-
</
|
|
346
|
+
</EditorSection>
|
|
348
347
|
</div>
|
|
349
348
|
</aside>
|
|
350
349
|
</div>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type
|
|
3
|
-
import type { BlockTypeGroup } from './BlockPicker.vue';
|
|
2
|
+
import { BlockCanvas, EditorBlocks, EditorSection, EditorTagInput, EditorVisibility, type BlockEditor, type BlockTypeGroup } from '@commonpub/editor/vue';
|
|
4
3
|
|
|
5
4
|
const props = defineProps<{
|
|
6
5
|
blockEditor: BlockEditor;
|
|
@@ -203,7 +202,7 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
203
202
|
</div>
|
|
204
203
|
|
|
205
204
|
<div v-if="activeLeftTab === 'modules'" class="cpub-ee-left-body">
|
|
206
|
-
<
|
|
205
|
+
<EditorBlocks :groups="blockTypes" :block-editor="blockEditor" />
|
|
207
206
|
</div>
|
|
208
207
|
|
|
209
208
|
<div v-else-if="activeLeftTab === 'structure'" class="cpub-ee-left-body" style="padding: 10px;">
|
|
@@ -299,7 +298,7 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
299
298
|
|
|
300
299
|
<div class="cpub-ee-canvas">
|
|
301
300
|
<div class="cpub-ee-canvas-inner" :style="{ maxWidth: canvasMaxWidth }">
|
|
302
|
-
<
|
|
301
|
+
<BlockCanvas :block-editor="blockEditor" :block-types="blockTypes" />
|
|
303
302
|
</div>
|
|
304
303
|
</div>
|
|
305
304
|
|
|
@@ -318,7 +317,7 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
318
317
|
<!-- RIGHT: Properties -->
|
|
319
318
|
<aside class="cpub-ee-right" :class="{ 'cpub-ee-sidebar-open': mobileRightOpen }" aria-label="Explainer properties">
|
|
320
319
|
<div class="cpub-ee-right-body">
|
|
321
|
-
<
|
|
320
|
+
<EditorSection title="Content" icon="fa-sliders" :open="openSections.section" @toggle="toggleSection('section')">
|
|
322
321
|
<div class="cpub-ep-field">
|
|
323
322
|
<label class="cpub-ep-flabel">Slug</label>
|
|
324
323
|
<input class="cpub-ep-input" type="text" :value="metadata.slug" placeholder="auto-generated" @input="updateMeta('slug', ($event.target as HTMLInputElement).value)">
|
|
@@ -333,27 +332,27 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
333
332
|
</div>
|
|
334
333
|
<div class="cpub-ep-field">
|
|
335
334
|
<label class="cpub-ep-flabel">Tags</label>
|
|
336
|
-
<
|
|
335
|
+
<EditorTagInput :tags="tags" @update:tags="onTagsUpdate" />
|
|
337
336
|
</div>
|
|
338
|
-
</
|
|
337
|
+
</EditorSection>
|
|
339
338
|
|
|
340
|
-
<
|
|
339
|
+
<EditorSection title="Difficulty" icon="fa-gauge-high" :open="openSections.difficulty" @toggle="toggleSection('difficulty')">
|
|
341
340
|
<select class="cpub-ep-select" :value="metadata.difficulty || 'beginner'" @change="updateMeta('difficulty', ($event.target as HTMLSelectElement).value)">
|
|
342
341
|
<option value="beginner">Beginner</option>
|
|
343
342
|
<option value="intermediate">Intermediate</option>
|
|
344
343
|
<option value="advanced">Advanced</option>
|
|
345
344
|
</select>
|
|
346
|
-
</
|
|
345
|
+
</EditorSection>
|
|
347
346
|
|
|
348
|
-
<
|
|
349
|
-
<
|
|
350
|
-
</
|
|
347
|
+
<EditorSection title="Visibility" icon="fa-eye" :open="openSections.visibility" @toggle="toggleSection('visibility')">
|
|
348
|
+
<EditorVisibility :model-value="visibility" @update:model-value="onVisibilityUpdate" />
|
|
349
|
+
</EditorSection>
|
|
351
350
|
|
|
352
|
-
<
|
|
351
|
+
<EditorSection title="Cover Image" icon="fa-image" :open="openSections.cover" @toggle="toggleSection('cover')">
|
|
353
352
|
<div class="cpub-ep-field">
|
|
354
353
|
<input class="cpub-ep-input" type="url" :value="metadata.coverImageUrl" placeholder="https://..." @input="updateMeta('coverImageUrl', ($event.target as HTMLInputElement).value)">
|
|
355
354
|
</div>
|
|
356
|
-
</
|
|
355
|
+
</EditorSection>
|
|
357
356
|
</div>
|
|
358
357
|
</aside>
|
|
359
358
|
</div>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type
|
|
3
|
-
import type { BlockTypeGroup } from './BlockPicker.vue';
|
|
2
|
+
import { BlockCanvas, EditorBlocks, EditorSection, EditorTagInput, EditorVisibility, type BlockEditor, type BlockTypeGroup } from '@commonpub/editor/vue';
|
|
4
3
|
|
|
5
4
|
const props = defineProps<{
|
|
6
5
|
blockEditor: BlockEditor;
|
|
@@ -151,7 +150,7 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
151
150
|
|
|
152
151
|
<!-- LEFT: Block Library -->
|
|
153
152
|
<aside class="cpub-pe-library" :class="{ 'cpub-pe-sidebar-open': mobileLeftOpen }" aria-label="Block library">
|
|
154
|
-
<
|
|
153
|
+
<EditorBlocks :groups="blockTypes" :block-editor="blockEditor" />
|
|
155
154
|
</aside>
|
|
156
155
|
|
|
157
156
|
<!-- CENTER: Canvas with toolbar -->
|
|
@@ -202,7 +201,7 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
202
201
|
placeholder="Project title..."
|
|
203
202
|
/>
|
|
204
203
|
|
|
205
|
-
<
|
|
204
|
+
<BlockCanvas :block-editor="blockEditor" :block-types="blockTypes" />
|
|
206
205
|
</div>
|
|
207
206
|
</div>
|
|
208
207
|
|
|
@@ -217,7 +216,7 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
217
216
|
<!-- RIGHT: Settings Panel -->
|
|
218
217
|
<aside class="cpub-pe-settings" :class="{ 'cpub-pe-sidebar-open': mobileRightOpen }" aria-label="Project settings">
|
|
219
218
|
<div class="cpub-pe-settings-body">
|
|
220
|
-
<
|
|
219
|
+
<EditorSection title="Project Meta" icon="fa-sliders" :open="openSections.meta" @toggle="toggleSection('meta')">
|
|
221
220
|
<div class="cpub-ep-field">
|
|
222
221
|
<label class="cpub-ep-flabel">Slug</label>
|
|
223
222
|
<input class="cpub-ep-input" type="text" :value="metadata.slug" placeholder="project-url-slug" @input="updateMeta('slug', ($event.target as HTMLInputElement).value)">
|
|
@@ -246,17 +245,17 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
246
245
|
<label class="cpub-ep-flabel">Description</label>
|
|
247
246
|
<textarea class="cpub-ep-textarea" rows="3" :value="metadata.description as string" placeholder="Brief project description..." @input="updateMeta('description', ($event.target as HTMLTextAreaElement).value)" />
|
|
248
247
|
</div>
|
|
249
|
-
</
|
|
248
|
+
</EditorSection>
|
|
250
249
|
|
|
251
|
-
<
|
|
252
|
-
<
|
|
253
|
-
</
|
|
250
|
+
<EditorSection title="Tags" icon="fa-tag" :open="openSections.tags" @toggle="toggleSection('tags')">
|
|
251
|
+
<EditorTagInput :tags="tags" @update:tags="onTagsUpdate" />
|
|
252
|
+
</EditorSection>
|
|
254
253
|
|
|
255
|
-
<
|
|
256
|
-
<
|
|
257
|
-
</
|
|
254
|
+
<EditorSection title="Visibility" icon="fa-eye" :open="openSections.visibility" @toggle="toggleSection('visibility')">
|
|
255
|
+
<EditorVisibility :model-value="visibility" @update:model-value="onVisibilityUpdate" />
|
|
256
|
+
</EditorSection>
|
|
258
257
|
|
|
259
|
-
<
|
|
258
|
+
<EditorSection title="Cover Image" icon="fa-image" :open="openSections.cover" @toggle="toggleSection('cover')">
|
|
260
259
|
<div class="cpub-pe-cover" :class="{ 'has-image': !!coverImageUrl }">
|
|
261
260
|
<template v-if="coverImageUrl">
|
|
262
261
|
<img :src="coverImageUrl" alt="Cover image" class="cpub-pe-cover-img" />
|
|
@@ -282,17 +281,17 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
282
281
|
</div>
|
|
283
282
|
</template>
|
|
284
283
|
</div>
|
|
285
|
-
</
|
|
284
|
+
</EditorSection>
|
|
286
285
|
|
|
287
|
-
<
|
|
286
|
+
<EditorSection title="SEO" icon="fa-magnifying-glass" :open="openSections.seo" @toggle="toggleSection('seo')">
|
|
288
287
|
<div class="cpub-pe-field">
|
|
289
288
|
<label class="cpub-pe-flabel">Meta Description</label>
|
|
290
289
|
<textarea class="cpub-pe-textarea" rows="3" :value="metadata.seoDescription as string" placeholder="Search engine description (recommended 50-160 chars)" @input="updateMeta('seoDescription', ($event.target as HTMLTextAreaElement).value)" />
|
|
291
290
|
<span class="cpub-pe-hint">{{ ((metadata.seoDescription as string) || '').length }}/160</span>
|
|
292
291
|
</div>
|
|
293
|
-
</
|
|
292
|
+
</EditorSection>
|
|
294
293
|
|
|
295
|
-
<
|
|
294
|
+
<EditorSection title="Checklist" icon="fa-circle-check" :open="openSections.checklist" @toggle="toggleSection('checklist')">
|
|
296
295
|
<div class="cpub-pe-checklist">
|
|
297
296
|
<div v-for="item in checklist" :key="item.label" class="cpub-pe-check-item" :class="{ pass: item.pass }">
|
|
298
297
|
<i :class="item.pass ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" :style="{ color: item.pass ? 'var(--green)' : 'var(--text-faint)' }"></i>
|
|
@@ -302,7 +301,7 @@ const blockCount = computed(() => props.blockEditor.blocks.value.length);
|
|
|
302
301
|
<div class="cpub-pe-checklist-summary">
|
|
303
302
|
{{ checklistDone }}/{{ checklist.length }} complete
|
|
304
303
|
</div>
|
|
305
|
-
</
|
|
304
|
+
</EditorSection>
|
|
306
305
|
</div>
|
|
307
306
|
</aside>
|
|
308
307
|
</div>
|
|
@@ -66,7 +66,7 @@ const isCompanyHub = computed(() => hubType.value === 'company');
|
|
|
66
66
|
background: linear-gradient(135deg, var(--accent) 0%, #006b6b 50%, var(--accent-border) 100%);
|
|
67
67
|
position: relative;
|
|
68
68
|
overflow: hidden;
|
|
69
|
-
border-bottom:
|
|
69
|
+
border-bottom: var(--border-width-default) solid var(--border);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.cpub-hub-banner-pattern {
|
|
@@ -87,7 +87,7 @@ const isCompanyHub = computed(() => hubType.value === 'company');
|
|
|
87
87
|
|
|
88
88
|
.cpub-hub-meta-bar {
|
|
89
89
|
background: var(--surface);
|
|
90
|
-
border-bottom:
|
|
90
|
+
border-bottom: var(--border-width-default) solid var(--border);
|
|
91
91
|
padding: 20px 0;
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -45,7 +45,7 @@ const activeTab = defineModel<string>('activeTab', { required: true });
|
|
|
45
45
|
<style scoped>
|
|
46
46
|
.cpub-hub-tabs {
|
|
47
47
|
background: var(--surface);
|
|
48
|
-
border-bottom:
|
|
48
|
+
border-bottom: var(--border-width-default) solid var(--border);
|
|
49
49
|
position: sticky;
|
|
50
50
|
top: 48px;
|
|
51
51
|
z-index: 90;
|
|
@@ -36,7 +36,7 @@ defineProps<{
|
|
|
36
36
|
|
|
37
37
|
.cpub-product-card {
|
|
38
38
|
background: var(--surface);
|
|
39
|
-
border:
|
|
39
|
+
border: var(--border-width-default) solid var(--border);
|
|
40
40
|
border-radius: 12px;
|
|
41
41
|
padding: 16px;
|
|
42
42
|
display: flex;
|
|
@@ -55,7 +55,7 @@ defineProps<{
|
|
|
55
55
|
width: 48px;
|
|
56
56
|
height: 48px;
|
|
57
57
|
background: var(--surface2);
|
|
58
|
-
border:
|
|
58
|
+
border: var(--border-width-default) solid var(--border);
|
|
59
59
|
border-radius: 10px;
|
|
60
60
|
display: flex;
|
|
61
61
|
align-items: center;
|
|
@@ -14,7 +14,7 @@ defineProps<{
|
|
|
14
14
|
<style scoped>
|
|
15
15
|
.cpub-sb-card {
|
|
16
16
|
background: var(--surface);
|
|
17
|
-
border:
|
|
17
|
+
border: var(--border-width-default) solid var(--border);
|
|
18
18
|
padding: 18px;
|
|
19
19
|
box-shadow: var(--shadow-sm);
|
|
20
20
|
margin-bottom: 16px;
|
|
@@ -30,6 +30,6 @@ defineProps<{
|
|
|
30
30
|
color: var(--text-dim);
|
|
31
31
|
margin-bottom: 14px;
|
|
32
32
|
padding-bottom: 8px;
|
|
33
|
-
border-bottom:
|
|
33
|
+
border-bottom: var(--border-width-default) solid var(--border);
|
|
34
34
|
}
|
|
35
35
|
</style>
|