@commonpub/layer 0.5.0 → 0.5.2

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.5.0",
3
+ "version": "0.5.2",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -51,14 +51,14 @@
51
51
  "vue-router": "^4.3.0",
52
52
  "zod": "^4.3.6",
53
53
  "@commonpub/auth": "0.5.0",
54
- "@commonpub/config": "0.8.0",
55
54
  "@commonpub/docs": "0.6.0",
55
+ "@commonpub/config": "0.8.0",
56
56
  "@commonpub/editor": "0.5.0",
57
- "@commonpub/explainer": "0.6.0",
58
- "@commonpub/learning": "0.5.0",
57
+ "@commonpub/explainer": "0.6.2",
58
+ "@commonpub/schema": "0.8.16",
59
59
  "@commonpub/protocol": "0.9.6",
60
- "@commonpub/schema": "0.8.15",
61
- "@commonpub/server": "2.23.1",
60
+ "@commonpub/server": "2.24.0",
61
+ "@commonpub/learning": "0.5.0",
62
62
  "@commonpub/ui": "0.8.4"
63
63
  },
64
64
  "devDependencies": {
@@ -73,6 +73,7 @@ const selectedPage = computed<DocsPage | null>(() =>
73
73
 
74
74
  // Page properties (right panel)
75
75
  const pageSlug = ref('');
76
+ const pageStatus = ref<'draft' | 'published'>('draft');
76
77
  const savingPage = ref(false);
77
78
  const autoSaveTimer = ref<ReturnType<typeof setTimeout> | null>(null);
78
79
  const autoSaveStatus = ref<'idle' | 'saving' | 'saved' | 'error'>('idle');
@@ -110,6 +111,7 @@ async function selectPage(pageId: string): Promise<void> {
110
111
 
111
112
  // Load properties
112
113
  pageSlug.value = page.slug ?? '';
114
+ pageStatus.value = ((page as Record<string, unknown>).status as 'draft' | 'published') || 'draft';
113
115
  isDirty.value = false;
114
116
  autoSaveStatus.value = 'idle';
115
117
 
@@ -146,6 +148,36 @@ async function saveCurrentPage(): Promise<void> {
146
148
  }
147
149
 
148
150
  // Autosave: debounce 5 seconds for docs (shorter than article 30s)
151
+ async function publishPage(): Promise<void> {
152
+ if (!selectedPageId.value) return;
153
+ try {
154
+ await $fetch(`/api/docs/${siteSlug.value}/pages/${selectedPageId.value}`, {
155
+ method: 'PUT',
156
+ body: { status: 'published' },
157
+ });
158
+ pageStatus.value = 'published';
159
+ await refreshPages();
160
+ toast('Page published', 'success');
161
+ } catch {
162
+ toast('Failed to publish', 'error');
163
+ }
164
+ }
165
+
166
+ async function unpublishPage(): Promise<void> {
167
+ if (!selectedPageId.value) return;
168
+ try {
169
+ await $fetch(`/api/docs/${siteSlug.value}/pages/${selectedPageId.value}`, {
170
+ method: 'PUT',
171
+ body: { status: 'draft' },
172
+ });
173
+ pageStatus.value = 'draft';
174
+ await refreshPages();
175
+ toast('Page unpublished', 'success');
176
+ } catch {
177
+ toast('Failed to unpublish', 'error');
178
+ }
179
+ }
180
+
149
181
  function scheduleAutoSave(): void {
150
182
  if (autoSaveTimer.value) clearTimeout(autoSaveTimer.value);
151
183
  autoSaveTimer.value = setTimeout(() => {
@@ -499,22 +531,66 @@ async function createVersion(): Promise<void> {
499
531
  </div>
500
532
  </template>
501
533
 
502
- <!-- RIGHT: Page properties -->
534
+ <!-- RIGHT: Blocks + Properties -->
503
535
  <template #right>
504
- <div v-if="selectedPage" class="cpub-docs-props">
505
- <h3 class="cpub-docs-props-heading">Page Properties</h3>
506
-
507
- <div class="cpub-docs-field">
508
- <label class="cpub-docs-field-label">Slug</label>
509
- <input v-model="pageSlug" class="cpub-docs-field-input" placeholder="page-slug" />
536
+ <div v-if="selectedPage" class="cpub-docs-right">
537
+ <!-- Block palette -->
538
+ <div class="cpub-docs-right-section">
539
+ <h3 class="cpub-docs-props-heading">Blocks</h3>
540
+ <div class="cpub-docs-block-list">
541
+ <template v-for="group in blockTypes" :key="group.name">
542
+ <button
543
+ v-for="block in group.blocks"
544
+ :key="block.type + (block.attrs?.variant || '')"
545
+ class="cpub-docs-block-btn"
546
+ @click="blockEditor.addBlock(block.type, block.attrs)"
547
+ >
548
+ <i :class="`fa-solid ${block.icon}`" class="cpub-docs-block-icon" />
549
+ <span>{{ block.label }}</span>
550
+ </button>
551
+ </template>
552
+ </div>
510
553
  </div>
511
554
 
512
- <div class="cpub-docs-field">
513
- <label class="cpub-docs-field-label">Parent</label>
514
- <div class="cpub-docs-field-value">
515
- {{ selectedPage?.parentId ? pages.find(p => p.id === selectedPage!.parentId)?.title ?? 'Unknown' : 'Top level' }}
555
+ <!-- Page properties -->
556
+ <div class="cpub-docs-right-section">
557
+ <h3 class="cpub-docs-props-heading">Properties</h3>
558
+
559
+ <div class="cpub-docs-field">
560
+ <label class="cpub-docs-field-label">Slug</label>
561
+ <input v-model="pageSlug" class="cpub-docs-field-input" placeholder="page-slug" />
562
+ </div>
563
+
564
+ <div class="cpub-docs-field">
565
+ <label class="cpub-docs-field-label">Status</label>
566
+ <div class="cpub-docs-status-row">
567
+ <span class="cpub-docs-status-badge" :class="pageStatus === 'published' ? 'cpub-docs-status-published' : 'cpub-docs-status-draft'">
568
+ {{ pageStatus === 'published' ? 'Published' : 'Draft' }}
569
+ </span>
570
+ <button
571
+ v-if="pageStatus !== 'published'"
572
+ class="cpub-docs-publish-btn"
573
+ @click="publishPage"
574
+ >
575
+ <i class="fa-solid fa-globe" /> Publish
576
+ </button>
577
+ <button
578
+ v-else
579
+ class="cpub-docs-unpublish-btn"
580
+ @click="unpublishPage"
581
+ >
582
+ Unpublish
583
+ </button>
584
+ </div>
585
+ </div>
586
+
587
+ <div class="cpub-docs-field">
588
+ <label class="cpub-docs-field-label">Parent</label>
589
+ <div class="cpub-docs-field-value">
590
+ {{ selectedPage?.parentId ? pages.find(p => p.id === selectedPage!.parentId)?.title ?? 'Unknown' : 'Top level' }}
591
+ </div>
592
+ <span class="cpub-docs-field-hint">Drag pages in the tree to change hierarchy</span>
516
593
  </div>
517
- <span class="cpub-docs-field-hint">Drag pages in the tree to change hierarchy</span>
518
594
  </div>
519
595
  </div>
520
596
  </template>
@@ -838,6 +914,117 @@ async function createVersion(): Promise<void> {
838
914
  line-height: 1.3;
839
915
  }
840
916
 
917
+ /* Right panel layout */
918
+ .cpub-docs-right {
919
+ display: flex;
920
+ flex-direction: column;
921
+ gap: 0;
922
+ }
923
+
924
+ .cpub-docs-right-section {
925
+ padding: 12px 0;
926
+ border-bottom: var(--border-width-default) solid var(--border2);
927
+ }
928
+
929
+ .cpub-docs-right-section:last-child {
930
+ border-bottom: none;
931
+ }
932
+
933
+ /* Block palette */
934
+ .cpub-docs-block-list {
935
+ display: flex;
936
+ flex-direction: column;
937
+ gap: 2px;
938
+ }
939
+
940
+ .cpub-docs-block-btn {
941
+ display: flex;
942
+ align-items: center;
943
+ gap: 8px;
944
+ padding: 5px 8px;
945
+ background: none;
946
+ border: var(--border-width-default) solid transparent;
947
+ color: var(--text-dim);
948
+ font-size: 11px;
949
+ cursor: pointer;
950
+ text-align: left;
951
+ width: 100%;
952
+ }
953
+
954
+ .cpub-docs-block-btn:hover {
955
+ background: var(--surface2);
956
+ border-color: var(--border);
957
+ color: var(--text);
958
+ }
959
+
960
+ .cpub-docs-block-icon {
961
+ width: 14px;
962
+ font-size: 10px;
963
+ color: var(--text-faint);
964
+ text-align: center;
965
+ }
966
+
967
+ /* Status badge + publish */
968
+ .cpub-docs-status-row {
969
+ display: flex;
970
+ align-items: center;
971
+ gap: 8px;
972
+ }
973
+
974
+ .cpub-docs-status-badge {
975
+ font-family: var(--font-mono);
976
+ font-size: 9px;
977
+ font-weight: 700;
978
+ text-transform: uppercase;
979
+ letter-spacing: 0.06em;
980
+ padding: 2px 8px;
981
+ }
982
+
983
+ .cpub-docs-status-draft {
984
+ color: var(--yellow, #d4a017);
985
+ background: var(--yellow-bg, rgba(245, 158, 11, 0.08));
986
+ border: var(--border-width-default) solid var(--yellow-border, rgba(245, 158, 11, 0.25));
987
+ }
988
+
989
+ .cpub-docs-status-published {
990
+ color: var(--green, #2a9d5c);
991
+ background: var(--green-bg, rgba(34, 197, 94, 0.08));
992
+ border: var(--border-width-default) solid var(--green-border, rgba(34, 197, 94, 0.25));
993
+ }
994
+
995
+ .cpub-docs-publish-btn {
996
+ padding: 3px 10px;
997
+ background: var(--accent);
998
+ border: var(--border-width-default) solid var(--accent);
999
+ color: var(--color-text-inverse, #fff);
1000
+ font-family: var(--font-mono);
1001
+ font-size: 9px;
1002
+ font-weight: 700;
1003
+ cursor: pointer;
1004
+ display: flex;
1005
+ align-items: center;
1006
+ gap: 5px;
1007
+ }
1008
+
1009
+ .cpub-docs-publish-btn:hover {
1010
+ opacity: 0.85;
1011
+ }
1012
+
1013
+ .cpub-docs-unpublish-btn {
1014
+ padding: 3px 10px;
1015
+ background: none;
1016
+ border: var(--border-width-default) solid var(--border);
1017
+ color: var(--text-faint);
1018
+ font-family: var(--font-mono);
1019
+ font-size: 9px;
1020
+ cursor: pointer;
1021
+ }
1022
+
1023
+ .cpub-docs-unpublish-btn:hover {
1024
+ color: var(--yellow, #d4a017);
1025
+ border-color: var(--yellow, #d4a017);
1026
+ }
1027
+
841
1028
  .cpub-docs-field-value {
842
1029
  font-size: 12px;
843
1030
  color: var(--text-dim);