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 CHANGED
@@ -1,7 +1,7 @@
1
1
  export default defineAppConfig({
2
2
  ui: {
3
3
  colors: {
4
- primary: 'indigo',
4
+ primary: 'blue',
5
5
  secondary: 'violet',
6
6
  neutral: 'slate',
7
7
  },
@@ -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(event: Event) {
97
- if (running.value && port.value) {
98
- event.preventDefault();
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
- :as="running && port ? 'a' : 'div'"
107
- :href="running && port ? `http://localhost:${port}` : undefined"
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="{ 'terminal-card--running': running, 'cursor-default': !running }"
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(event: Event) {
97
- if (running.value && port.value) {
98
- event.preventDefault();
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
- { 'list-item--running': running, 'cursor-pointer': running && port },
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
+ }
@@ -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
- startPolling();
89
- window.addEventListener('beforeunload', handleBeforeUnload);
90
+ if (!deployMode.value) {
91
+ startPolling();
92
+ window.addEventListener('beforeunload', handleBeforeUnload);
93
+ }
90
94
  window.addEventListener('keydown', handleKeydown);
91
95
 
92
96
  try {
93
- const response = await fetch('/api/presentations');
94
- if (response.ok) {
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
- stopPolling();
104
- stopAllServers();
105
- window.removeEventListener('beforeunload', handleBeforeUnload);
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
- id: 'actions',
283
- label: 'Actions',
284
- items: [
285
- {
286
- label: 'New',
287
- suffix: 'Create a new presentation',
288
- icon: 'i-lucide-plus',
289
- onSelect: handleCreateCommand,
290
- },
291
- {
292
- label: 'Import',
293
- suffix: 'Import existing Sli.dev presentation(s)',
294
- icon: 'i-lucide-import',
295
- onSelect: handleImportCommand,
296
- },
297
- {
298
- label: 'Toggle theme',
299
- suffix: colorMode.value === 'dark' ? 'Switch to light mode' : 'Switch to dark mode',
300
- icon: colorMode.value === 'dark' ? 'i-lucide-sun' : 'i-lucide-moon',
301
- onSelect: handleToggleThemeCommand,
302
- },
303
- {
304
- label: 'Toggle view',
305
- suffix: viewMode.value === 'grid' ? 'Switch to list layout' : 'Switch to grid layout',
306
- icon: viewMode.value === 'grid' ? 'i-lucide-list' : 'i-lucide-layout-grid',
307
- onSelect: handleToggleViewModeCommand,
308
- },
309
- ],
310
- },
311
- {
312
- id: 'presentations',
313
- label: 'Present',
314
- items: presentations.value.map(
315
- (p): CommandPaletteItem => ({
316
- label: `Present > ${p.title}`,
317
- suffix: 'Start dev server and open in browser',
318
- icon: 'i-lucide-play',
319
- onSelect: () => handlePresentCommand(p),
320
- }),
321
- ),
322
- },
323
- {
324
- id: 'export',
325
- label: 'Export',
326
- items: presentations.value.map(
327
- (p): CommandPaletteItem => ({
328
- label: `Export > ${p.title}`,
329
- suffix: 'Export to PDF',
330
- icon: 'i-lucide-download',
331
- onSelect: () => handleExportCommand(p),
332
- }),
333
- ),
334
- },
335
- {
336
- id: 'edit',
337
- label: 'Edit',
338
- items: presentations.value.map(
339
- (p): CommandPaletteItem => ({
340
- label: `Edit > ${p.title}`,
341
- suffix: 'Open in VS Code',
342
- icon: 'i-lucide-pencil',
343
- onSelect: () => handleEditCommand(p),
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="isImportDialogOpen = true">
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="isDialogOpen = true">
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="isDialogOpen = true">
482
+ <UButton class="font-mono" @click="handleCreateCommand">
447
483
  <template #leading>
448
484
  <span class="opacity-70">$</span>
449
485
  </template>