@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.
Files changed (64) hide show
  1. package/components/CommentSection.vue +4 -4
  2. package/components/ContentAttachments.vue +4 -4
  3. package/components/DiscussionItem.vue +1 -1
  4. package/components/FeedItem.vue +1 -1
  5. package/components/MessageThread.vue +1 -1
  6. package/components/NotificationItem.vue +1 -1
  7. package/components/editors/ArticleEditor.vue +11 -12
  8. package/components/editors/BlogEditor.vue +17 -18
  9. package/components/editors/ExplainerEditor.vue +13 -14
  10. package/components/editors/ProjectEditor.vue +17 -18
  11. package/components/hub/HubHero.vue +2 -2
  12. package/components/hub/HubLayout.vue +1 -1
  13. package/components/hub/HubProducts.vue +2 -2
  14. package/components/hub/HubSidebarCard.vue +2 -2
  15. package/components/views/ArticleView.vue +15 -15
  16. package/components/views/BlogView.vue +14 -14
  17. package/components/views/ExplainerView.vue +11 -11
  18. package/components/views/ProjectView.vue +36 -36
  19. package/composables/useMarkdownImport.ts +1 -1
  20. package/package.json +9 -9
  21. package/pages/admin/theme.vue +1 -1
  22. package/pages/authorize_interaction.vue +1 -1
  23. package/pages/docs/[siteSlug]/edit.vue +4 -4
  24. package/pages/federated-hubs/[id]/index.vue +6 -6
  25. package/pages/federated-hubs/[id]/posts/[postId].vue +5 -5
  26. package/pages/hubs/[slug]/index.vue +6 -6
  27. package/pages/hubs/[slug]/posts/[postId].vue +6 -6
  28. package/pages/hubs/index.vue +2 -2
  29. package/pages/mirror/[id].vue +2 -2
  30. package/pages/u/[username]/[type]/[slug]/edit.vue +2 -1
  31. package/server/api/docs/[siteSlug]/search.get.ts +2 -1
  32. package/theme/components.css +2 -2
  33. package/theme/layouts.css +2 -2
  34. package/theme/prose.css +4 -4
  35. package/components/editors/BlockCanvas.vue +0 -487
  36. package/components/editors/BlockInsertZone.vue +0 -84
  37. package/components/editors/BlockPicker.vue +0 -285
  38. package/components/editors/BlockWrapper.vue +0 -192
  39. package/components/editors/EditorBlocks.vue +0 -248
  40. package/components/editors/EditorSection.vue +0 -81
  41. package/components/editors/EditorShell.vue +0 -196
  42. package/components/editors/EditorTagInput.vue +0 -114
  43. package/components/editors/EditorVisibility.vue +0 -110
  44. package/components/editors/blocks/BuildStepBlock.vue +0 -102
  45. package/components/editors/blocks/CalloutBlock.vue +0 -122
  46. package/components/editors/blocks/CheckpointBlock.vue +0 -27
  47. package/components/editors/blocks/CodeBlock.vue +0 -177
  48. package/components/editors/blocks/DividerBlock.vue +0 -22
  49. package/components/editors/blocks/DownloadsBlock.vue +0 -41
  50. package/components/editors/blocks/EmbedBlock.vue +0 -20
  51. package/components/editors/blocks/GalleryBlock.vue +0 -236
  52. package/components/editors/blocks/HeadingBlock.vue +0 -96
  53. package/components/editors/blocks/ImageBlock.vue +0 -271
  54. package/components/editors/blocks/MarkdownBlock.vue +0 -258
  55. package/components/editors/blocks/MathBlock.vue +0 -37
  56. package/components/editors/blocks/PartsListBlock.vue +0 -358
  57. package/components/editors/blocks/QuizBlock.vue +0 -47
  58. package/components/editors/blocks/QuoteBlock.vue +0 -101
  59. package/components/editors/blocks/SectionHeaderBlock.vue +0 -130
  60. package/components/editors/blocks/SliderBlock.vue +0 -318
  61. package/components/editors/blocks/TextBlock.vue +0 -201
  62. package/components/editors/blocks/ToolListBlock.vue +0 -70
  63. package/components/editors/blocks/VideoBlock.vue +0 -22
  64. package/composables/useBlockEditor.ts +0 -187
@@ -1,285 +0,0 @@
1
- <script setup lang="ts">
2
- /**
3
- * Block type picker — appears when clicking an insert zone.
4
- * Shows available block types grouped by category, with search.
5
- */
6
- export interface BlockTypeDef {
7
- type: string;
8
- label: string;
9
- icon: string;
10
- description?: string;
11
- attrs?: Record<string, unknown>;
12
- }
13
-
14
- export interface BlockTypeGroup {
15
- name: string;
16
- blocks: BlockTypeDef[];
17
- }
18
-
19
- const props = defineProps<{
20
- groups: BlockTypeGroup[];
21
- visible: boolean;
22
- }>();
23
-
24
- const emit = defineEmits<{
25
- select: [type: string, attrs?: Record<string, unknown>];
26
- close: [];
27
- }>();
28
-
29
- const search = ref('');
30
- const selectedIndex = ref(0);
31
- const pickerRef = ref<HTMLElement | null>(null);
32
-
33
- const flatBlocks = computed(() => {
34
- return props.groups.flatMap((g) => g.blocks);
35
- });
36
-
37
- const filteredBlocks = computed(() => {
38
- const q = search.value.toLowerCase();
39
- if (!q) return flatBlocks.value;
40
- return flatBlocks.value.filter(
41
- (b) => b.label.toLowerCase().includes(q) || b.type.toLowerCase().includes(q),
42
- );
43
- });
44
-
45
- watch(() => props.visible, (v) => {
46
- if (v) {
47
- search.value = '';
48
- selectedIndex.value = 0;
49
- nextTick(() => {
50
- (pickerRef.value?.querySelector('.cpub-picker-search') as HTMLInputElement)?.focus();
51
- });
52
- }
53
- });
54
-
55
- watch(search, () => {
56
- selectedIndex.value = 0;
57
- });
58
-
59
- function handleKeydown(event: KeyboardEvent): void {
60
- if (event.key === 'Escape') {
61
- event.preventDefault();
62
- emit('close');
63
- return;
64
- }
65
- if (event.key === 'ArrowDown') {
66
- event.preventDefault();
67
- selectedIndex.value = Math.min(selectedIndex.value + 1, filteredBlocks.value.length - 1);
68
- return;
69
- }
70
- if (event.key === 'ArrowUp') {
71
- event.preventDefault();
72
- selectedIndex.value = Math.max(selectedIndex.value - 1, 0);
73
- return;
74
- }
75
- if (event.key === 'Enter') {
76
- event.preventDefault();
77
- const block = filteredBlocks.value[selectedIndex.value];
78
- if (block) {
79
- emit('select', block.type, block.attrs);
80
- }
81
- return;
82
- }
83
- }
84
-
85
- function selectBlock(block: BlockTypeDef): void {
86
- emit('select', block.type, block.attrs);
87
- }
88
-
89
- // Close on click outside
90
- function handleClickOutside(event: MouseEvent): void {
91
- if (pickerRef.value && !pickerRef.value.contains(event.target as Node)) {
92
- emit('close');
93
- }
94
- }
95
-
96
- onMounted(() => {
97
- document.addEventListener('mousedown', handleClickOutside);
98
- });
99
-
100
- onUnmounted(() => {
101
- document.removeEventListener('mousedown', handleClickOutside);
102
- });
103
- </script>
104
-
105
- <template>
106
- <div v-if="visible" ref="pickerRef" class="cpub-picker" @keydown="handleKeydown">
107
- <div class="cpub-picker-header">
108
- <i class="fa-solid fa-magnifying-glass cpub-picker-search-icon"></i>
109
- <input
110
- v-model="search"
111
- type="text"
112
- class="cpub-picker-search"
113
- placeholder="Search blocks..."
114
- aria-label="Search block types"
115
- />
116
- </div>
117
- <div class="cpub-picker-body">
118
- <template v-if="filteredBlocks.length > 0">
119
- <button
120
- v-for="(block, i) in filteredBlocks"
121
- :key="block.type + (block.attrs?.variant ?? '')"
122
- :data-block="block.type"
123
- class="cpub-picker-item"
124
- :class="{ 'cpub-picker-item--active': i === selectedIndex }"
125
- @mouseenter="selectedIndex = i"
126
- @click="selectBlock(block)"
127
- >
128
- <span class="cpub-picker-icon"><i :class="['fa-solid', block.icon]"></i></span>
129
- <span class="cpub-picker-text">
130
- <span class="cpub-picker-label">{{ block.label }}</span>
131
- <span v-if="block.description" class="cpub-picker-desc">{{ block.description }}</span>
132
- </span>
133
- </button>
134
- </template>
135
- <div v-else class="cpub-picker-empty">
136
- No blocks match "{{ search }}"
137
- </div>
138
- </div>
139
- </div>
140
- </template>
141
-
142
- <style scoped>
143
- .cpub-picker {
144
- position: absolute;
145
- left: 50%;
146
- transform: translateX(-50%);
147
- z-index: 100;
148
- background: var(--surface);
149
- border: var(--border-width-default) solid var(--border);
150
- box-shadow: var(--shadow-lg);
151
- min-width: 260px;
152
- max-width: 340px;
153
- max-height: 360px;
154
- display: flex;
155
- flex-direction: column;
156
- }
157
-
158
- .cpub-picker-header {
159
- display: flex;
160
- align-items: center;
161
- padding: 8px 10px;
162
- gap: 8px;
163
- border-bottom: var(--border-width-default) solid var(--border);
164
- flex-shrink: 0;
165
- }
166
-
167
- .cpub-picker-search-icon {
168
- font-size: 10px;
169
- color: var(--text-faint);
170
- flex-shrink: 0;
171
- }
172
-
173
- .cpub-picker-search {
174
- background: transparent;
175
- border: none;
176
- outline: none;
177
- font-size: 12px;
178
- color: var(--text);
179
- width: 100%;
180
- font-family: var(--font-sans);
181
- }
182
-
183
- .cpub-picker-search::placeholder {
184
- color: var(--text-faint);
185
- }
186
-
187
- .cpub-picker-body {
188
- overflow-y: auto;
189
- flex: 1;
190
- padding: 4px;
191
- }
192
-
193
- .cpub-picker-item {
194
- display: flex;
195
- align-items: center;
196
- gap: 10px;
197
- width: 100%;
198
- padding: 7px 10px;
199
- background: transparent;
200
- border: none;
201
- text-align: left;
202
- cursor: pointer;
203
- transition: background 0.08s;
204
- color: var(--text);
205
- font-size: 12px;
206
- }
207
-
208
- .cpub-picker-item:hover,
209
- .cpub-picker-item--active {
210
- background: var(--accent-bg);
211
- }
212
-
213
- .cpub-picker-icon {
214
- width: 26px;
215
- height: 26px;
216
- background: var(--surface2);
217
- border: var(--border-width-default) solid var(--border2);
218
- display: flex;
219
- align-items: center;
220
- justify-content: center;
221
- font-size: 10px;
222
- color: var(--text-dim);
223
- flex-shrink: 0;
224
- }
225
-
226
- .cpub-picker-item--active .cpub-picker-icon,
227
- .cpub-picker-item:hover .cpub-picker-icon {
228
- background: var(--accent-bg);
229
- border-color: var(--accent-border);
230
- color: var(--accent);
231
- }
232
-
233
- .cpub-picker-text {
234
- display: flex;
235
- flex-direction: column;
236
- min-width: 0;
237
- }
238
-
239
- .cpub-picker-label {
240
- font-size: 12px;
241
- font-weight: 500;
242
- }
243
-
244
- .cpub-picker-desc {
245
- font-size: 10px;
246
- color: var(--text-faint);
247
- font-family: var(--font-mono);
248
- }
249
-
250
- .cpub-picker-empty {
251
- padding: 16px;
252
- text-align: center;
253
- font-size: 11px;
254
- color: var(--text-faint);
255
- }
256
-
257
- /* Per-block-type icon colors */
258
- [data-block="heading"] .cpub-picker-icon { color: var(--teal); background: color-mix(in srgb, var(--teal) 10%, transparent); }
259
- [data-block="text"] .cpub-picker-icon,
260
- [data-block="paragraph"] .cpub-picker-icon { color: var(--text-dim); background: var(--surface2); }
261
- [data-block="image"] .cpub-picker-icon { color: #38bdf8; background: rgba(56, 189, 248, 0.08); }
262
- [data-block="code"] .cpub-picker-icon,
263
- [data-block="code_block"] .cpub-picker-icon { color: #c084fc; background: rgba(192, 132, 252, 0.08); }
264
- [data-block="callout"] .cpub-picker-icon { color: #fbbf24; background: rgba(251, 191, 36, 0.08); }
265
- [data-block="quote"] .cpub-picker-icon,
266
- [data-block="blockquote"] .cpub-picker-icon { color: #94a3b8; background: rgba(148, 163, 184, 0.08); }
267
- [data-block="embed"] .cpub-picker-icon { color: #f472b6; background: rgba(244, 114, 182, 0.08); }
268
- [data-block="video"] .cpub-picker-icon { color: #fb923c; background: rgba(251, 146, 60, 0.08); }
269
- [data-block="divider"] .cpub-picker-icon,
270
- [data-block="horizontal_rule"] .cpub-picker-icon,
271
- [data-block="horizontalRule"] .cpub-picker-icon { color: var(--text-faint); background: var(--surface2); }
272
- [data-block="gallery"] .cpub-picker-icon { color: #2dd4bf; background: rgba(45, 212, 191, 0.08); }
273
- [data-block="quiz"] .cpub-picker-icon { color: #4ade80; background: rgba(74, 222, 128, 0.08); }
274
- [data-block="slider"] .cpub-picker-icon,
275
- [data-block="interactiveSlider"] .cpub-picker-icon { color: #818cf8; background: rgba(129, 140, 248, 0.08); }
276
- [data-block="math"] .cpub-picker-icon,
277
- [data-block="mathNotation"] .cpub-picker-icon { color: #e879f9; background: rgba(232, 121, 249, 0.08); }
278
- [data-block="markdown"] .cpub-picker-icon { color: var(--text-dim); background: var(--surface2); }
279
- [data-block="buildStep"] .cpub-picker-icon { color: var(--accent); background: var(--accent-bg); }
280
- [data-block="partsList"] .cpub-picker-icon { color: #fb7185; background: rgba(251, 113, 133, 0.08); }
281
- [data-block="toolList"] .cpub-picker-icon { color: #a78bfa; background: rgba(167, 139, 250, 0.08); }
282
- [data-block="downloads"] .cpub-picker-icon { color: #22d3ee; background: rgba(34, 211, 238, 0.08); }
283
- [data-block="sectionHeader"] .cpub-picker-icon { color: var(--accent); background: var(--accent-bg); }
284
- [data-block="checkpoint"] .cpub-picker-icon { color: #34d399; background: rgba(52, 211, 153, 0.08); }
285
- </style>
@@ -1,192 +0,0 @@
1
- <script setup lang="ts">
2
- /**
3
- * Block wrapper — wraps every content block with:
4
- * - Drag handle (left, appears on hover)
5
- * - Block controls (top-right: move, clone, delete)
6
- * - Selected state (accent outline)
7
- * - Click-to-select
8
- */
9
- import type { EditorBlock } from '../../composables/useBlockEditor';
10
-
11
- const props = defineProps<{
12
- block: EditorBlock;
13
- selected: boolean;
14
- }>();
15
-
16
- const emit = defineEmits<{
17
- select: [];
18
- delete: [];
19
- duplicate: [];
20
- 'move-up': [];
21
- 'move-down': [];
22
- 'drag-start': [event: DragEvent];
23
- 'drag-end': [event: DragEvent];
24
- }>();
25
-
26
- function onDragStart(event: DragEvent): void {
27
- event.dataTransfer?.setData('text/plain', props.block.id);
28
- event.dataTransfer!.effectAllowed = 'move';
29
- emit('drag-start', event);
30
- }
31
-
32
- function onDragEnd(event: DragEvent): void {
33
- emit('drag-end', event);
34
- }
35
- </script>
36
-
37
- <template>
38
- <div
39
- class="cpub-block-wrap"
40
- :class="{ 'cpub-block-wrap--selected': selected }"
41
- @click.stop="emit('select')"
42
- >
43
- <!-- Drag handle (left side) -->
44
- <div class="cpub-block-handle">
45
- <button
46
- class="cpub-handle-btn"
47
- title="Drag to reorder"
48
- draggable="true"
49
- @dragstart="onDragStart"
50
- @dragend="onDragEnd"
51
- >
52
- <i class="fa-solid fa-grip-vertical"></i>
53
- </button>
54
- </div>
55
-
56
- <!-- Block controls (top-right, shown on hover) -->
57
- <div class="cpub-block-controls">
58
- <button class="cpub-block-ctrl" title="Move up" @click.stop="emit('move-up')">
59
- <i class="fa-solid fa-arrow-up"></i>
60
- </button>
61
- <button class="cpub-block-ctrl" title="Move down" @click.stop="emit('move-down')">
62
- <i class="fa-solid fa-arrow-down"></i>
63
- </button>
64
- <button class="cpub-block-ctrl" title="Duplicate" @click.stop="emit('duplicate')">
65
- <i class="fa-solid fa-copy"></i>
66
- </button>
67
- <button class="cpub-block-ctrl cpub-block-ctrl--danger" title="Delete" @click.stop="emit('delete')">
68
- <i class="fa-solid fa-trash"></i>
69
- </button>
70
- </div>
71
-
72
- <!-- Block content -->
73
- <div class="cpub-block-inner">
74
- <slot />
75
- </div>
76
- </div>
77
- </template>
78
-
79
- <style scoped>
80
- .cpub-block-wrap {
81
- position: relative;
82
- border: var(--border-width-default) solid transparent;
83
- transition: border-color 0.12s;
84
- }
85
-
86
- .cpub-block-wrap:hover {
87
- border-color: var(--border2);
88
- }
89
-
90
- .cpub-block-wrap--selected {
91
- border-color: var(--accent);
92
- box-shadow: 0 0 0 2px var(--accent-bg);
93
- }
94
-
95
- /* Drag handle — left side */
96
- .cpub-block-handle {
97
- position: absolute;
98
- left: -36px;
99
- top: 50%;
100
- transform: translateY(-50%);
101
- display: flex;
102
- flex-direction: column;
103
- gap: 2px;
104
- opacity: 0;
105
- transition: opacity 0.12s;
106
- }
107
-
108
- .cpub-block-wrap:hover .cpub-block-handle,
109
- .cpub-block-wrap--selected .cpub-block-handle {
110
- opacity: 1;
111
- }
112
-
113
- .cpub-handle-btn {
114
- width: 28px;
115
- height: 28px;
116
- display: flex;
117
- align-items: center;
118
- justify-content: center;
119
- background: var(--surface);
120
- border: var(--border-width-default) solid var(--border2);
121
- color: var(--text-faint);
122
- cursor: grab;
123
- font-size: 11px;
124
- padding: 0;
125
- }
126
-
127
- .cpub-handle-btn:hover {
128
- border-color: var(--border);
129
- color: var(--text-dim);
130
- background: var(--surface2);
131
- }
132
-
133
- .cpub-handle-btn:active {
134
- cursor: grabbing;
135
- }
136
-
137
- /* Block controls — top-right, offset above the block */
138
- .cpub-block-controls {
139
- --ctrl-surface: rgba(255, 255, 255, 0.15);
140
- position: absolute;
141
- top: -30px;
142
- right: 0;
143
- display: flex;
144
- gap: 0;
145
- background: var(--text);
146
- padding: 2px;
147
- opacity: 0;
148
- transition: opacity 0.12s;
149
- z-index: 10;
150
- }
151
-
152
- .cpub-block-wrap:hover .cpub-block-controls,
153
- .cpub-block-wrap--selected .cpub-block-controls {
154
- opacity: 1;
155
- }
156
-
157
- .cpub-block-ctrl {
158
- width: 26px;
159
- height: 26px;
160
- display: flex;
161
- align-items: center;
162
- justify-content: center;
163
- background: transparent;
164
- border: none;
165
- color: var(--surface3);
166
- cursor: pointer;
167
- font-size: 10px;
168
- padding: 0;
169
- transition: background 0.1s, color 0.1s;
170
- }
171
-
172
- .cpub-block-ctrl:hover {
173
- background: var(--ctrl-surface);
174
- color: var(--surface);
175
- }
176
-
177
- .cpub-block-ctrl--danger:hover {
178
- background: var(--red);
179
- color: var(--surface);
180
- }
181
-
182
- /* Block inner */
183
- .cpub-block-inner {
184
- min-height: 20px;
185
- }
186
-
187
- /* Touch devices: only show controls on selected block since there's no hover */
188
- @media (hover: none) {
189
- .cpub-block-wrap--selected .cpub-block-handle { opacity: 1; }
190
- .cpub-block-wrap--selected .cpub-block-controls { opacity: 1; }
191
- }
192
- </style>