supaslidev 0.1.5 → 0.2.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.
Files changed (67) hide show
  1. package/app/app.config.ts +9 -0
  2. package/app/assets/css/main.css +90 -0
  3. package/app/components/AppHeader.vue +429 -0
  4. package/app/components/CreatePresentationDialog.vue +236 -0
  5. package/app/components/EmptyState.vue +37 -0
  6. package/app/components/ImportPresentationDialog.vue +865 -0
  7. package/app/components/PresentationCard.vue +343 -0
  8. package/app/components/PresentationListItem.vue +242 -0
  9. package/app/composables/useServers.ts +148 -0
  10. package/app/layouts/default.vue +49 -0
  11. package/app/pages/index.vue +542 -0
  12. package/dist/cli/index.js +183707 -127
  13. package/dist/config.d.ts +8 -0
  14. package/dist/config.js +16 -0
  15. package/dist/index.d.ts +21 -0
  16. package/dist/index.js +3 -0
  17. package/dist/module.d.ts +6 -0
  18. package/dist/module.js +9168 -0
  19. package/dist/prompt.js +847 -0
  20. package/nuxt.config.ts +53 -0
  21. package/package.json +26 -19
  22. package/server/api/export/[id].post.ts +67 -0
  23. package/server/api/open-editor/[id].post.ts +28 -0
  24. package/server/api/presentations/import.post.ts +139 -0
  25. package/server/api/presentations/index.get.ts +18 -0
  26. package/server/api/presentations/index.post.ts +175 -0
  27. package/server/api/presentations/upload.post.ts +174 -0
  28. package/server/api/presentations/validate.post.ts +14 -0
  29. package/server/api/servers/[id].delete.ts +15 -0
  30. package/server/api/servers/[id].post.ts +17 -0
  31. package/server/api/servers/index.delete.ts +5 -0
  32. package/server/api/servers/index.get.ts +5 -0
  33. package/server/api/servers/stop-all.post.ts +5 -0
  34. package/server/plugins/generate.ts +12 -0
  35. package/server/plugins/shutdown.ts +16 -0
  36. package/server/routes/exports/[...path].get.ts +25 -0
  37. package/server/utils/config.ts +13 -0
  38. package/server/utils/process-manager.ts +119 -0
  39. package/src/cli/commands/create.ts +125 -0
  40. package/src/cli/commands/deploy.ts +90 -0
  41. package/src/cli/commands/dev.ts +116 -0
  42. package/src/cli/commands/export.ts +63 -0
  43. package/src/cli/commands/import.ts +178 -0
  44. package/src/cli/commands/present.ts +111 -0
  45. package/src/cli/index.ts +87 -0
  46. package/src/cli/utils.ts +94 -0
  47. package/src/config.ts +21 -0
  48. package/src/index.ts +2 -0
  49. package/src/module.ts +12 -0
  50. package/src/shared/catalog.ts +94 -0
  51. package/src/shared/copy.ts +28 -0
  52. package/src/shared/index.ts +29 -0
  53. package/{scripts/generate-presentations.mjs → src/shared/presentations.ts} +23 -46
  54. package/src/shared/types.ts +29 -0
  55. package/src/shared/validation.ts +111 -0
  56. package/dist/assets/index-BJzGy4Im.js +0 -49
  57. package/dist/assets/index-CVzsY-on.css +0 -1
  58. package/dist/index.html +0 -24
  59. package/server/api.js +0 -1207
  60. /package/{dist → public}/apple-touch-icon.png +0 -0
  61. /package/{dist → public}/favicon-96x96.png +0 -0
  62. /package/{dist → public}/favicon.ico +0 -0
  63. /package/{dist → public}/favicon.svg +0 -0
  64. /package/{dist → public}/site.webmanifest +0 -0
  65. /package/{dist → public}/ssl-logo.png +0 -0
  66. /package/{dist → public}/web-app-manifest-192x192.png +0 -0
  67. /package/{dist → public}/web-app-manifest-512x512.png +0 -0
@@ -0,0 +1,9 @@
1
+ export default defineAppConfig({
2
+ ui: {
3
+ colors: {
4
+ primary: 'indigo',
5
+ secondary: 'violet',
6
+ neutral: 'slate',
7
+ },
8
+ },
9
+ });
@@ -0,0 +1,90 @@
1
+ @import 'tailwindcss';
2
+ @import '@nuxt/ui';
3
+
4
+ @theme {
5
+ --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
6
+ }
7
+
8
+ :root {
9
+ --supaslidev-border: #d1d5db;
10
+ --supaslidev-header-bg: rgba(0, 0, 0, 0.02);
11
+ --supaslidev-text-muted: #6b7280;
12
+ }
13
+
14
+ .dark {
15
+ --supaslidev-border: #4b5563;
16
+ --supaslidev-header-bg: rgba(255, 255, 255, 0.03);
17
+ --supaslidev-text-muted: #9ca3af;
18
+ }
19
+
20
+ *,
21
+ *::before,
22
+ *::after {
23
+ transition:
24
+ background-color 0.2s ease,
25
+ border-color 0.2s ease,
26
+ color 0.2s ease;
27
+ }
28
+
29
+ .template-grid {
30
+ display: grid;
31
+ grid-template-columns: repeat(3, 1fr);
32
+ gap: 0.75rem;
33
+ }
34
+
35
+ .card-enter-active {
36
+ transition: all 0.4s ease-out;
37
+ }
38
+
39
+ .card-leave-active {
40
+ transition: all 0.3s ease-in;
41
+ }
42
+
43
+ .card-enter-from {
44
+ opacity: 0;
45
+ transform: translateY(20px) scale(0.95);
46
+ }
47
+
48
+ .card-leave-to {
49
+ opacity: 0;
50
+ transform: translateY(-10px) scale(0.95);
51
+ }
52
+
53
+ .card-move {
54
+ transition: transform 0.4s ease;
55
+ }
56
+
57
+ .list-enter-active {
58
+ transition: all 0.3s ease-out;
59
+ }
60
+
61
+ .list-leave-active {
62
+ transition: all 0.2s ease-in;
63
+ }
64
+
65
+ .list-enter-from {
66
+ opacity: 0;
67
+ transform: translateX(-20px);
68
+ }
69
+
70
+ .list-leave-to {
71
+ opacity: 0;
72
+ transform: translateX(20px);
73
+ }
74
+
75
+ .list-move {
76
+ transition: transform 0.3s ease;
77
+ }
78
+
79
+ .view-fade-enter-active {
80
+ transition: opacity 0.25s ease-out;
81
+ }
82
+
83
+ .view-fade-leave-active {
84
+ transition: opacity 0.2s ease-in;
85
+ }
86
+
87
+ .view-fade-enter-from,
88
+ .view-fade-leave-to {
89
+ opacity: 0;
90
+ }
@@ -0,0 +1,429 @@
1
+ <script setup lang="ts">
2
+ const version = useRuntimeConfig().public.supaslidevVersion || '0.0.0';
3
+
4
+ interface CommandOption {
5
+ label: string;
6
+ description?: string;
7
+ onSelect: () => void;
8
+ }
9
+
10
+ const { commands } = defineProps<{
11
+ commands: CommandOption[];
12
+ }>();
13
+
14
+ const emit = defineEmits<{
15
+ 'open-command-palette': [];
16
+ 'execute-command': [command: string];
17
+ }>();
18
+
19
+ const colorMode = useColorMode();
20
+ const isDark = computed({
21
+ get: () => colorMode.value === 'dark',
22
+ set: (value: boolean) => {
23
+ colorMode.preference = value ? 'dark' : 'light';
24
+ },
25
+ });
26
+
27
+ const isMac = computed(() => {
28
+ if (typeof navigator === 'undefined') return true;
29
+ return navigator.platform.toUpperCase().indexOf('MAC') >= 0;
30
+ });
31
+
32
+ const inputValue = ref('');
33
+ const inputRef = ref<HTMLInputElement | null>(null);
34
+ const isFocused = ref(false);
35
+ const selectedIndex = ref(0);
36
+
37
+ const filteredCommands = computed(() => {
38
+ if (!inputValue.value.trim()) return [];
39
+
40
+ const query = inputValue.value.toLowerCase();
41
+ return commands.filter((cmd) => cmd.label.toLowerCase().includes(query));
42
+ });
43
+
44
+ const showDropdown = computed(() => isFocused.value && filteredCommands.value.length > 0);
45
+
46
+ const ghostText = computed(() => {
47
+ if (!inputValue.value.trim()) return '';
48
+
49
+ const query = inputValue.value.toLowerCase();
50
+ const match = commands.find((cmd) => cmd.label.toLowerCase().startsWith(query));
51
+
52
+ if (match) {
53
+ return match.label.slice(inputValue.value.length);
54
+ }
55
+ return '';
56
+ });
57
+
58
+ function handleInputKeydown(event: KeyboardEvent) {
59
+ if (event.key === 'Tab' && ghostText.value) {
60
+ event.preventDefault();
61
+ inputValue.value = inputValue.value + ghostText.value;
62
+ } else if (event.key === 'ArrowDown' && showDropdown.value) {
63
+ event.preventDefault();
64
+ selectedIndex.value = Math.min(selectedIndex.value + 1, filteredCommands.value.length - 1);
65
+ } else if (event.key === 'ArrowUp' && showDropdown.value) {
66
+ event.preventDefault();
67
+ selectedIndex.value = Math.max(selectedIndex.value - 1, 0);
68
+ } else if (event.key === 'Enter') {
69
+ event.preventDefault();
70
+ if (showDropdown.value) {
71
+ const selected = filteredCommands.value[selectedIndex.value];
72
+ if (selected) {
73
+ selected.onSelect();
74
+ inputValue.value = '';
75
+ inputRef.value?.blur();
76
+ }
77
+ } else if (inputValue.value.trim()) {
78
+ emit('execute-command', inputValue.value.trim());
79
+ inputValue.value = '';
80
+ inputRef.value?.blur();
81
+ }
82
+ } else if (event.key === 'Escape') {
83
+ inputValue.value = '';
84
+ inputRef.value?.blur();
85
+ }
86
+ }
87
+
88
+ function selectOption(option: CommandOption) {
89
+ option.onSelect();
90
+ inputValue.value = '';
91
+ inputRef.value?.blur();
92
+ }
93
+
94
+ function handleInputChange() {
95
+ selectedIndex.value = 0;
96
+ }
97
+
98
+ function handleHeaderClick() {
99
+ inputRef.value?.focus();
100
+ }
101
+
102
+ function focusInput() {
103
+ nextTick(() => {
104
+ inputRef.value?.focus();
105
+ });
106
+ }
107
+
108
+ defineExpose({ focusInput, inputRef });
109
+ </script>
110
+
111
+ <template>
112
+ <header class="header">
113
+ <div class="header-terminal">
114
+ <div class="terminal-dots">
115
+ <span class="terminal-dot terminal-dot--close" />
116
+ <span class="terminal-dot terminal-dot--minimize" />
117
+ <span class="terminal-dot terminal-dot--maximize" />
118
+ </div>
119
+
120
+ <div class="header-content" @click="handleHeaderClick">
121
+ <div class="header-left">
122
+ <div class="logo">
123
+ <img src="/ssl-logo.png" alt="Supaslidev" class="logo-image" />
124
+ <h1 class="logo-title">Supaslidev</h1>
125
+ <span class="logo-version">{{ version }}</span>
126
+ <span class="logo-symbol">$</span>
127
+ <span v-if="!isFocused && !inputValue" class="logo-cursor" />
128
+ </div>
129
+
130
+ <div class="input-wrapper">
131
+ <input
132
+ ref="inputRef"
133
+ v-model="inputValue"
134
+ type="text"
135
+ class="terminal-input"
136
+ placeholder="Type a command..."
137
+ @keydown="handleInputKeydown"
138
+ @input="handleInputChange"
139
+ @focus="isFocused = true"
140
+ @blur="isFocused = false"
141
+ />
142
+ <span v-if="ghostText" class="ghost-text"
143
+ >{{ inputValue }}<span class="ghost-suffix">{{ ghostText }}</span></span
144
+ >
145
+ </div>
146
+ </div>
147
+
148
+ <div class="header-right" @click.stop>
149
+ <div class="command-shortcut">
150
+ <UKbd size="sm" class="command-kbd">{{ isMac ? '⌘' : 'Ctrl' }}</UKbd>
151
+ <UKbd size="sm" class="command-kbd">K</UKbd>
152
+ </div>
153
+
154
+ <UButton
155
+ :icon="isDark ? 'i-lucide-sun' : 'i-lucide-moon'"
156
+ color="neutral"
157
+ variant="ghost"
158
+ size="md"
159
+ class="theme-toggle"
160
+ :title="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
161
+ @click="isDark = !isDark"
162
+ />
163
+ </div>
164
+ </div>
165
+
166
+ <div v-if="showDropdown" class="dropdown">
167
+ <button
168
+ v-for="(option, index) in filteredCommands"
169
+ :key="option.label"
170
+ class="dropdown-item"
171
+ :class="{ 'dropdown-item--selected': index === selectedIndex }"
172
+ @mousedown.prevent="selectOption(option)"
173
+ @mouseenter="selectedIndex = index"
174
+ >
175
+ <span class="dropdown-item-label">{{ option.label }}</span>
176
+ <span v-if="option.description" class="dropdown-item-description">{{
177
+ option.description
178
+ }}</span>
179
+ </button>
180
+ </div>
181
+ </div>
182
+ </header>
183
+ </template>
184
+
185
+ <style scoped>
186
+ .header {
187
+ margin-bottom: 2rem;
188
+ }
189
+
190
+ .header-terminal {
191
+ background: var(--ui-bg);
192
+ border: 1px solid var(--supaslidev-border);
193
+ border-radius: 0.75rem;
194
+ overflow: hidden;
195
+ }
196
+
197
+ .terminal-dots {
198
+ display: flex;
199
+ gap: 0.375rem;
200
+ padding: 0.75rem 1rem;
201
+ background: var(--supaslidev-header-bg);
202
+ border-bottom: 1px solid var(--supaslidev-border);
203
+ }
204
+
205
+ .terminal-dot {
206
+ width: 12px;
207
+ height: 12px;
208
+ border-radius: 50%;
209
+ transition: box-shadow 0.3s ease;
210
+ }
211
+
212
+ .terminal-dot--close {
213
+ background: #ff5f56;
214
+ }
215
+
216
+ .terminal-dot--minimize {
217
+ background: #ffbd2e;
218
+ }
219
+
220
+ .terminal-dot--maximize {
221
+ background: #27c93f;
222
+ }
223
+
224
+ .header-terminal:hover .terminal-dot--close {
225
+ box-shadow: 0 0 8px #ff5f56;
226
+ }
227
+
228
+ .header-terminal:hover .terminal-dot--minimize {
229
+ box-shadow: 0 0 8px #ffbd2e;
230
+ }
231
+
232
+ .header-terminal:hover .terminal-dot--maximize {
233
+ box-shadow: 0 0 8px #27c93f;
234
+ }
235
+
236
+ .header-content {
237
+ display: flex;
238
+ align-items: center;
239
+ justify-content: space-between;
240
+ padding: 1rem 1.5rem;
241
+ gap: 1rem;
242
+ cursor: pointer;
243
+ transition: background-color 0.2s ease;
244
+ }
245
+
246
+ .header-content:hover {
247
+ background: var(--ui-bg-elevated);
248
+ }
249
+
250
+ .header-content:focus {
251
+ outline: none;
252
+ background: var(--ui-bg-elevated);
253
+ }
254
+
255
+ .header-left {
256
+ display: flex;
257
+ align-items: center;
258
+ gap: 0.5rem;
259
+ }
260
+
261
+ .logo {
262
+ display: flex;
263
+ align-items: center;
264
+ gap: 0.5rem;
265
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
266
+ }
267
+
268
+ .logo-image {
269
+ width: 24px;
270
+ height: 24px;
271
+ object-fit: contain;
272
+ }
273
+
274
+ .logo-title {
275
+ margin: 0;
276
+ font-size: 0.875rem;
277
+ font-weight: 600;
278
+ background: linear-gradient(90deg, #31a3a5, #f5ca35, #2ba4a9, #239d9f);
279
+ background-size: 300% 100%;
280
+ -webkit-background-clip: text;
281
+ -webkit-text-fill-color: transparent;
282
+ background-clip: text;
283
+ animation: gradient-shift 3s ease infinite;
284
+ }
285
+
286
+ @keyframes gradient-shift {
287
+ 0% {
288
+ background-position: 0% 50%;
289
+ }
290
+ 50% {
291
+ background-position: 100% 50%;
292
+ }
293
+ 100% {
294
+ background-position: 0% 50%;
295
+ }
296
+ }
297
+
298
+ .logo-version {
299
+ font-size: 0.8rem;
300
+ font-weight: 400;
301
+ }
302
+
303
+ .logo-symbol {
304
+ color: var(--ui-text);
305
+ font-weight: 600;
306
+ font-size: 1rem;
307
+ }
308
+
309
+ .logo-cursor {
310
+ width: 8px;
311
+ height: 1.25rem;
312
+ background: var(--ui-primary);
313
+ animation: blink 1s step-end infinite;
314
+ }
315
+
316
+ .input-wrapper {
317
+ position: relative;
318
+ flex: 1;
319
+ min-width: 0;
320
+ }
321
+
322
+ .terminal-input {
323
+ width: 100%;
324
+ background: transparent;
325
+ border: none;
326
+ outline: none;
327
+ color: var(--ui-text);
328
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
329
+ caret-color: var(--ui-primary);
330
+ }
331
+
332
+ .terminal-input::placeholder {
333
+ color: var(--ui-text-muted);
334
+ }
335
+
336
+ .ghost-text {
337
+ position: absolute;
338
+ top: 0;
339
+ left: 0;
340
+ pointer-events: none;
341
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
342
+ color: transparent;
343
+ white-space: pre;
344
+ }
345
+
346
+ .ghost-suffix {
347
+ color: white;
348
+ opacity: 0.5;
349
+ margin-left: 1px;
350
+ }
351
+
352
+ @keyframes blink {
353
+ 0%,
354
+ 100% {
355
+ opacity: 1;
356
+ }
357
+ 50% {
358
+ opacity: 0;
359
+ }
360
+ }
361
+
362
+ .header-right {
363
+ display: flex;
364
+ align-items: center;
365
+ gap: 0.75rem;
366
+ }
367
+
368
+ .command-shortcut {
369
+ display: flex;
370
+ align-items: center;
371
+ gap: 0.25rem;
372
+ }
373
+
374
+ .command-kbd {
375
+ opacity: 0.85;
376
+ }
377
+
378
+ .theme-toggle {
379
+ transition: all 0.2s ease;
380
+ }
381
+
382
+ .theme-toggle:hover {
383
+ box-shadow: 0 0 20px rgba(99, 102, 241, 0.2);
384
+ }
385
+
386
+ .dropdown {
387
+ border-top: 1px solid var(--supaslidev-border);
388
+ max-height: 240px;
389
+ overflow-y: auto;
390
+ }
391
+
392
+ .dropdown-item {
393
+ display: flex;
394
+ justify-content: space-between;
395
+ align-items: center;
396
+ gap: 1rem;
397
+ width: 100%;
398
+ padding: 0.75rem 1.5rem;
399
+ text-align: left;
400
+ background: transparent;
401
+ border: none;
402
+ color: var(--ui-text);
403
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
404
+ cursor: pointer;
405
+ transition: background-color 0.15s ease;
406
+ }
407
+
408
+ .dropdown-item:hover,
409
+ .dropdown-item--selected {
410
+ background: var(--ui-bg-elevated);
411
+ }
412
+
413
+ .dropdown-item--selected {
414
+ color: var(--ui-primary);
415
+ }
416
+
417
+ .dropdown-item-label {
418
+ flex-shrink: 0;
419
+ }
420
+
421
+ .dropdown-item-description {
422
+ color: var(--ui-text-muted);
423
+ font-size: 0.75rem;
424
+ text-align: right;
425
+ overflow: hidden;
426
+ text-overflow: ellipsis;
427
+ white-space: nowrap;
428
+ }
429
+ </style>