supaslidev 0.2.1 → 0.3.0
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/app/app.config.ts +1 -1
- package/app/components/PresentationCard.vue +30 -30
- package/app/components/PresentationListItem.vue +26 -4
- package/app/composables/useDeployMode.ts +18 -0
- package/app/pages/index.vue +115 -79
- package/dist/cli/index.js +16939 -13045
- package/dist/module.d.ts +3 -3
- package/dist/module.js +2 -2
- package/nuxt.config.ts +1 -0
- package/package.json +6 -6
- package/server/api/presentations/import.post.ts +2 -7
- package/server/api/presentations/upload.post.ts +2 -7
- package/src/cli/commands/deploy.ts +236 -63
- package/src/cli/commands/import.ts +3 -0
- package/src/cli/index.ts +15 -10
- package/src/cli/utils.ts +0 -47
- package/src/module.ts +4 -1
- package/src/shared/catalog.ts +5 -19
- package/src/shared/index.ts +1 -2
package/app/app.config.ts
CHANGED
|
@@ -5,6 +5,9 @@ const props = defineProps<{
|
|
|
5
5
|
presentation: Presentation;
|
|
6
6
|
}>();
|
|
7
7
|
|
|
8
|
+
const { deployMode, showDeployDemoToast } = useDeployMode();
|
|
9
|
+
const deployBasePath = computed(() => (import.meta.env.BASE_URL || '/').replace(/\/$/, ''));
|
|
10
|
+
|
|
8
11
|
const {
|
|
9
12
|
isRunning,
|
|
10
13
|
getPort,
|
|
@@ -33,6 +36,11 @@ async function handleDev(event: Event) {
|
|
|
33
36
|
event.preventDefault();
|
|
34
37
|
event.stopPropagation();
|
|
35
38
|
|
|
39
|
+
if (deployMode.value) {
|
|
40
|
+
showDeployDemoToast();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
if (loading.value.dev) return;
|
|
37
45
|
|
|
38
46
|
loading.value.dev = true;
|
|
@@ -57,6 +65,11 @@ async function handleExport(event: Event) {
|
|
|
57
65
|
event.preventDefault();
|
|
58
66
|
event.stopPropagation();
|
|
59
67
|
|
|
68
|
+
if (deployMode.value) {
|
|
69
|
+
showDeployDemoToast();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
if (loading.value.export) return;
|
|
61
74
|
|
|
62
75
|
loading.value.export = true;
|
|
@@ -78,6 +91,11 @@ async function handleEdit(event: Event) {
|
|
|
78
91
|
event.preventDefault();
|
|
79
92
|
event.stopPropagation();
|
|
80
93
|
|
|
94
|
+
if (deployMode.value) {
|
|
95
|
+
showDeployDemoToast();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
81
99
|
if (loading.value.edit) return;
|
|
82
100
|
|
|
83
101
|
loading.value.edit = true;
|
|
@@ -93,9 +111,10 @@ async function handleEdit(event: Event) {
|
|
|
93
111
|
}
|
|
94
112
|
}
|
|
95
113
|
|
|
96
|
-
function handleCardClick(
|
|
97
|
-
if (
|
|
98
|
-
|
|
114
|
+
function handleCardClick() {
|
|
115
|
+
if (deployMode.value) {
|
|
116
|
+
window.open(`${deployBasePath.value}/presentations/${props.presentation.id}/`, '_blank');
|
|
117
|
+
} else if (running.value && port.value) {
|
|
99
118
|
window.open(`http://localhost:${port.value}`, '_blank');
|
|
100
119
|
}
|
|
101
120
|
}
|
|
@@ -103,13 +122,14 @@ function handleCardClick(event: Event) {
|
|
|
103
122
|
|
|
104
123
|
<template>
|
|
105
124
|
<UCard
|
|
106
|
-
|
|
107
|
-
:
|
|
108
|
-
:target="running && port ? '_blank' : undefined"
|
|
109
|
-
:rel="running && port ? 'noopener noreferrer' : undefined"
|
|
110
|
-
:title="running && port ? `Open ${presentation.title}` : undefined"
|
|
125
|
+
as="div"
|
|
126
|
+
:title="deployMode || (running && port) ? `Open ${presentation.title}` : undefined"
|
|
111
127
|
class="card terminal-card group transition-all duration-300"
|
|
112
|
-
:class="{
|
|
128
|
+
:class="{
|
|
129
|
+
'terminal-card--running': !deployMode && running,
|
|
130
|
+
'cursor-pointer': deployMode || (running && port),
|
|
131
|
+
'cursor-default': !deployMode && !running,
|
|
132
|
+
}"
|
|
113
133
|
:ui="{
|
|
114
134
|
root: 'overflow-hidden',
|
|
115
135
|
header: 'p-0 sm:px-0 bg-[var(--supaslidev-header-bg)]',
|
|
@@ -228,30 +248,10 @@ function handleCardClick(event: Event) {
|
|
|
228
248
|
</template>
|
|
229
249
|
edit
|
|
230
250
|
</UButton>
|
|
231
|
-
|
|
232
|
-
<!-- TODO: Re-enable when deploy functionality is implemented
|
|
233
|
-
<UButton
|
|
234
|
-
color="neutral"
|
|
235
|
-
variant="soft"
|
|
236
|
-
size="sm"
|
|
237
|
-
class="flex-1 terminal-btn font-mono"
|
|
238
|
-
:loading="loading.deploy"
|
|
239
|
-
:disabled="loading.deploy"
|
|
240
|
-
@click="handleDeploy"
|
|
241
|
-
>
|
|
242
|
-
<template #leading>
|
|
243
|
-
<span class="terminal-prompt-symbol">$</span>
|
|
244
|
-
</template>
|
|
245
|
-
deploy
|
|
246
|
-
<template #trailing>
|
|
247
|
-
<UKbd size="xs" class="terminal-kbd">P</UKbd>
|
|
248
|
-
</template>
|
|
249
|
-
</UButton>
|
|
250
|
-
-->
|
|
251
251
|
</div>
|
|
252
252
|
|
|
253
253
|
<div
|
|
254
|
-
v-if="running && port"
|
|
254
|
+
v-if="!deployMode && running && port"
|
|
255
255
|
class="terminal-status font-mono text-xs text-[var(--ui-text-muted)] flex items-center gap-2 pt-3 border-t border-[var(--ui-border-muted)]"
|
|
256
256
|
>
|
|
257
257
|
<span class="text-[var(--ui-success)] animate-pulse">●</span>
|
|
@@ -5,6 +5,9 @@ const props = defineProps<{
|
|
|
5
5
|
presentation: Presentation;
|
|
6
6
|
}>();
|
|
7
7
|
|
|
8
|
+
const { deployMode, showDeployDemoToast } = useDeployMode();
|
|
9
|
+
const deployBasePath = computed(() => (import.meta.env.BASE_URL || '/').replace(/\/$/, ''));
|
|
10
|
+
|
|
8
11
|
const {
|
|
9
12
|
isRunning,
|
|
10
13
|
getPort,
|
|
@@ -33,6 +36,11 @@ async function handleDev(event: Event) {
|
|
|
33
36
|
event.preventDefault();
|
|
34
37
|
event.stopPropagation();
|
|
35
38
|
|
|
39
|
+
if (deployMode.value) {
|
|
40
|
+
showDeployDemoToast();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
if (loading.value.dev) return;
|
|
37
45
|
|
|
38
46
|
loading.value.dev = true;
|
|
@@ -57,6 +65,11 @@ async function handleExport(event: Event) {
|
|
|
57
65
|
event.preventDefault();
|
|
58
66
|
event.stopPropagation();
|
|
59
67
|
|
|
68
|
+
if (deployMode.value) {
|
|
69
|
+
showDeployDemoToast();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
if (loading.value.export) return;
|
|
61
74
|
|
|
62
75
|
loading.value.export = true;
|
|
@@ -78,6 +91,11 @@ async function handleEdit(event: Event) {
|
|
|
78
91
|
event.preventDefault();
|
|
79
92
|
event.stopPropagation();
|
|
80
93
|
|
|
94
|
+
if (deployMode.value) {
|
|
95
|
+
showDeployDemoToast();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
81
99
|
if (loading.value.edit) return;
|
|
82
100
|
|
|
83
101
|
loading.value.edit = true;
|
|
@@ -93,9 +111,10 @@ async function handleEdit(event: Event) {
|
|
|
93
111
|
}
|
|
94
112
|
}
|
|
95
113
|
|
|
96
|
-
function handleRowClick(
|
|
97
|
-
if (
|
|
98
|
-
|
|
114
|
+
function handleRowClick() {
|
|
115
|
+
if (deployMode.value) {
|
|
116
|
+
window.open(`${deployBasePath.value}/presentations/${props.presentation.id}/`, '_blank');
|
|
117
|
+
} else if (running.value && port.value) {
|
|
99
118
|
window.open(`http://localhost:${port.value}`, '_blank');
|
|
100
119
|
}
|
|
101
120
|
}
|
|
@@ -113,7 +132,10 @@ function handleOpen(event: Event) {
|
|
|
113
132
|
<div
|
|
114
133
|
:class="[
|
|
115
134
|
'list-item-v font-mono flex items-center gap-3 px-4 py-3 rounded-lg transition-all duration-200',
|
|
116
|
-
{
|
|
135
|
+
{
|
|
136
|
+
'list-item--running': !deployMode && running,
|
|
137
|
+
'cursor-pointer': deployMode || (running && port),
|
|
138
|
+
},
|
|
117
139
|
]"
|
|
118
140
|
@click="handleRowClick"
|
|
119
141
|
>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function useDeployMode() {
|
|
2
|
+
const config = useRuntimeConfig();
|
|
3
|
+
const deployMode = computed(() => !!config.public.deployMode);
|
|
4
|
+
|
|
5
|
+
function showDeployDemoToast() {
|
|
6
|
+
const toast = useToast();
|
|
7
|
+
toast.add({
|
|
8
|
+
title: 'Dev Mode Only',
|
|
9
|
+
description:
|
|
10
|
+
'This functionality is only available in dev mode. It is shown here for demo purposes.',
|
|
11
|
+
color: 'warning',
|
|
12
|
+
icon: 'i-lucide-info',
|
|
13
|
+
duration: 0,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return { deployMode, showDeployDemoToast };
|
|
18
|
+
}
|
package/app/pages/index.vue
CHANGED
|
@@ -11,8 +11,10 @@ const {
|
|
|
11
11
|
openInEditor,
|
|
12
12
|
waitForServerReady,
|
|
13
13
|
} = useServers();
|
|
14
|
+
const { deployMode, showDeployDemoToast } = useDeployMode();
|
|
14
15
|
const toast = useToast();
|
|
15
16
|
const colorMode = useColorMode();
|
|
17
|
+
const deployBasePath = computed(() => (import.meta.env.BASE_URL || '/').replace(/\/$/, ''));
|
|
16
18
|
|
|
17
19
|
function handleExportError(message: string) {
|
|
18
20
|
toast.add({
|
|
@@ -85,13 +87,25 @@ function handleBeforeUnload() {
|
|
|
85
87
|
const presentations = ref<Presentation[]>([]);
|
|
86
88
|
|
|
87
89
|
onMounted(async () => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
if (!deployMode.value) {
|
|
91
|
+
startPolling();
|
|
92
|
+
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
93
|
+
}
|
|
90
94
|
window.addEventListener('keydown', handleKeydown);
|
|
91
95
|
|
|
92
96
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
let response: Response | undefined;
|
|
98
|
+
|
|
99
|
+
if (deployMode.value) {
|
|
100
|
+
response = await fetch(`${deployBasePath.value}/presentations.json`);
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
response = await fetch(`${deployBasePath.value}/api/presentations`);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
response = await fetch('/api/presentations');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (response?.ok) {
|
|
95
109
|
presentations.value = await response.json();
|
|
96
110
|
}
|
|
97
111
|
} catch {
|
|
@@ -100,9 +114,11 @@ onMounted(async () => {
|
|
|
100
114
|
});
|
|
101
115
|
|
|
102
116
|
onUnmounted(() => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
117
|
+
if (!deployMode.value) {
|
|
118
|
+
stopPolling();
|
|
119
|
+
stopAllServers();
|
|
120
|
+
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
121
|
+
}
|
|
106
122
|
window.removeEventListener('keydown', handleKeydown);
|
|
107
123
|
});
|
|
108
124
|
const searchQuery = ref('');
|
|
@@ -123,6 +139,10 @@ watch(viewMode, (newMode) => {
|
|
|
123
139
|
|
|
124
140
|
async function handlePresentCommand(presentation: Presentation) {
|
|
125
141
|
isCommandPaletteOpen.value = false;
|
|
142
|
+
if (deployMode.value) {
|
|
143
|
+
window.open(`${deployBasePath.value}/presentations/${presentation.id}/`, '_blank');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
126
146
|
const result = await startServer(presentation.id);
|
|
127
147
|
if (result.success && result.port) {
|
|
128
148
|
const isReady = await waitForServerReady(result.port);
|
|
@@ -134,6 +154,10 @@ async function handlePresentCommand(presentation: Presentation) {
|
|
|
134
154
|
|
|
135
155
|
async function handleExportCommand(presentation: Presentation) {
|
|
136
156
|
isCommandPaletteOpen.value = false;
|
|
157
|
+
if (deployMode.value) {
|
|
158
|
+
showDeployDemoToast();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
137
161
|
const result = await exportPresentation(presentation.id);
|
|
138
162
|
if (result.success && result.pdfPath) {
|
|
139
163
|
window.open(result.pdfPath, '_blank');
|
|
@@ -144,6 +168,10 @@ async function handleExportCommand(presentation: Presentation) {
|
|
|
144
168
|
|
|
145
169
|
async function handleEditCommand(presentation: Presentation) {
|
|
146
170
|
isCommandPaletteOpen.value = false;
|
|
171
|
+
if (deployMode.value) {
|
|
172
|
+
showDeployDemoToast();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
147
175
|
const result = await openInEditor(presentation.id);
|
|
148
176
|
if (!result.success && result.error) {
|
|
149
177
|
toast.add({
|
|
@@ -157,11 +185,19 @@ async function handleEditCommand(presentation: Presentation) {
|
|
|
157
185
|
|
|
158
186
|
function handleCreateCommand() {
|
|
159
187
|
isCommandPaletteOpen.value = false;
|
|
188
|
+
if (deployMode.value) {
|
|
189
|
+
showDeployDemoToast();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
160
192
|
isDialogOpen.value = true;
|
|
161
193
|
}
|
|
162
194
|
|
|
163
195
|
function handleImportCommand() {
|
|
164
196
|
isCommandPaletteOpen.value = false;
|
|
197
|
+
if (deployMode.value) {
|
|
198
|
+
showDeployDemoToast();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
165
201
|
isImportDialogOpen.value = true;
|
|
166
202
|
}
|
|
167
203
|
|
|
@@ -277,74 +313,74 @@ function handleExecuteCommand(command: string) {
|
|
|
277
313
|
});
|
|
278
314
|
}
|
|
279
315
|
|
|
280
|
-
const commandPaletteGroups = computed<CommandPaletteGroup[]>(() =>
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
316
|
+
const commandPaletteGroups = computed<CommandPaletteGroup[]>(() => {
|
|
317
|
+
const actionItems: CommandPaletteItem[] = [
|
|
318
|
+
{
|
|
319
|
+
label: 'New',
|
|
320
|
+
suffix: 'Create a new presentation',
|
|
321
|
+
icon: 'i-lucide-plus',
|
|
322
|
+
onSelect: handleCreateCommand,
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
label: 'Import',
|
|
326
|
+
suffix: 'Import existing Sli.dev presentation(s)',
|
|
327
|
+
icon: 'i-lucide-import',
|
|
328
|
+
onSelect: handleImportCommand,
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
label: 'Toggle theme',
|
|
332
|
+
suffix: colorMode.value === 'dark' ? 'Switch to light mode' : 'Switch to dark mode',
|
|
333
|
+
icon: colorMode.value === 'dark' ? 'i-lucide-sun' : 'i-lucide-moon',
|
|
334
|
+
onSelect: handleToggleThemeCommand,
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
label: 'Toggle view',
|
|
338
|
+
suffix: viewMode.value === 'grid' ? 'Switch to list layout' : 'Switch to grid layout',
|
|
339
|
+
icon: viewMode.value === 'grid' ? 'i-lucide-list' : 'i-lucide-layout-grid',
|
|
340
|
+
onSelect: handleToggleViewModeCommand,
|
|
341
|
+
},
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
return [
|
|
345
|
+
{ id: 'actions', label: 'Actions', items: actionItems },
|
|
346
|
+
{
|
|
347
|
+
id: 'presentations',
|
|
348
|
+
label: 'Present',
|
|
349
|
+
items: presentations.value.map(
|
|
350
|
+
(p: Presentation): CommandPaletteItem => ({
|
|
351
|
+
label: `Present > ${p.title}`,
|
|
352
|
+
suffix: deployMode.value ? 'Open presentation' : 'Start dev server and open in browser',
|
|
353
|
+
icon: 'i-lucide-play',
|
|
354
|
+
onSelect: () => handlePresentCommand(p),
|
|
355
|
+
}),
|
|
356
|
+
),
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: 'export',
|
|
360
|
+
label: 'Export',
|
|
361
|
+
items: presentations.value.map(
|
|
362
|
+
(p: Presentation): CommandPaletteItem => ({
|
|
363
|
+
label: `Export > ${p.title}`,
|
|
364
|
+
suffix: 'Export to PDF',
|
|
365
|
+
icon: 'i-lucide-download',
|
|
366
|
+
onSelect: () => handleExportCommand(p),
|
|
367
|
+
}),
|
|
368
|
+
),
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
id: 'edit',
|
|
372
|
+
label: 'Edit',
|
|
373
|
+
items: presentations.value.map(
|
|
374
|
+
(p: Presentation): CommandPaletteItem => ({
|
|
375
|
+
label: `Edit > ${p.title}`,
|
|
376
|
+
suffix: 'Open in VS Code',
|
|
377
|
+
icon: 'i-lucide-pencil',
|
|
378
|
+
onSelect: () => handleEditCommand(p),
|
|
379
|
+
}),
|
|
380
|
+
),
|
|
381
|
+
},
|
|
382
|
+
];
|
|
383
|
+
});
|
|
348
384
|
|
|
349
385
|
const filteredPresentations = computed(() => {
|
|
350
386
|
if (!searchQuery.value.trim()) {
|
|
@@ -377,7 +413,7 @@ const commandOptions = computed(() => {
|
|
|
377
413
|
presentations.value.forEach((p) => {
|
|
378
414
|
options.push({
|
|
379
415
|
label: `Present > ${p.title}`,
|
|
380
|
-
description: 'Start dev server and open in browser',
|
|
416
|
+
description: deployMode.value ? 'Open presentation' : 'Start dev server and open in browser',
|
|
381
417
|
onSelect: () => handlePresentCommand(p),
|
|
382
418
|
});
|
|
383
419
|
options.push({
|
|
@@ -412,13 +448,13 @@ const commandOptions = computed(() => {
|
|
|
412
448
|
}}
|
|
413
449
|
</p>
|
|
414
450
|
<div class="flex items-center gap-3">
|
|
415
|
-
<UButton variant="outline" class="font-mono" @click="
|
|
451
|
+
<UButton variant="outline" class="font-mono" @click="handleImportCommand">
|
|
416
452
|
<template #leading>
|
|
417
453
|
<span class="opacity-70">$</span>
|
|
418
454
|
</template>
|
|
419
455
|
import
|
|
420
456
|
</UButton>
|
|
421
|
-
<UButton class="btn-new font-mono" @click="
|
|
457
|
+
<UButton class="btn-new font-mono" @click="handleCreateCommand">
|
|
422
458
|
<template #leading>
|
|
423
459
|
<span class="opacity-70">$</span>
|
|
424
460
|
</template>
|
|
@@ -443,7 +479,7 @@ const commandOptions = computed(() => {
|
|
|
443
479
|
title="No presentations yet"
|
|
444
480
|
description="Create your first presentation to get started with supaslidev."
|
|
445
481
|
>
|
|
446
|
-
<UButton class="font-mono" @click="
|
|
482
|
+
<UButton class="font-mono" @click="handleCreateCommand">
|
|
447
483
|
<template #leading>
|
|
448
484
|
<span class="opacity-70">$</span>
|
|
449
485
|
</template>
|