supaslidev 0.3.1 → 0.3.3

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,9 +1,9 @@
1
1
  export default defineAppConfig({
2
2
  ui: {
3
3
  colors: {
4
- primary: 'blue',
4
+ primary: 'teal',
5
5
  secondary: 'violet',
6
- neutral: 'slate',
6
+ neutral: 'stone',
7
7
  },
8
8
  },
9
9
  });
@@ -6,17 +6,40 @@
6
6
  }
7
7
 
8
8
  :root {
9
+ --ui-primary: var(--color-teal-700);
10
+ --ui-success: var(--color-green-700);
9
11
  --supaslidev-border: #d1d5db;
10
12
  --supaslidev-header-bg: rgba(0, 0, 0, 0.02);
11
13
  --supaslidev-text-muted: #6b7280;
12
14
  }
13
15
 
14
16
  .dark {
17
+ --ui-primary: var(--color-teal-500);
18
+ --ui-success: var(--color-green-500);
15
19
  --supaslidev-border: #4b5563;
16
20
  --supaslidev-header-bg: rgba(255, 255, 255, 0.03);
17
21
  --supaslidev-text-muted: #9ca3af;
18
22
  }
19
23
 
24
+ a,
25
+ button,
26
+ [role='button'],
27
+ select,
28
+ summary {
29
+ cursor: pointer;
30
+ }
31
+
32
+ .settings-icon {
33
+ transition:
34
+ transform 0.3s ease,
35
+ color 0.3s ease;
36
+ }
37
+
38
+ .settings-btn:hover .settings-icon {
39
+ transform: rotate(45deg);
40
+ color: var(--ui-primary);
41
+ }
42
+
20
43
  *,
21
44
  *::before,
22
45
  *::after {
@@ -16,13 +16,7 @@ const emit = defineEmits<{
16
16
  'execute-command': [command: string];
17
17
  }>();
18
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
- });
19
+ const router = useRouter();
26
20
 
27
21
  const isMac = computed(() => {
28
22
  if (typeof navigator === 'undefined') return true;
@@ -152,13 +146,15 @@ defineExpose({ focusInput, inputRef });
152
146
  </div>
153
147
 
154
148
  <UButton
155
- :icon="isDark ? 'i-lucide-sun' : 'i-lucide-moon'"
149
+ icon="i-lucide-settings"
156
150
  color="neutral"
157
151
  variant="ghost"
158
152
  size="md"
159
- class="theme-toggle"
160
- :title="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
161
- @click="isDark = !isDark"
153
+ class="settings-btn"
154
+ aria-label="Open settings"
155
+ title="Settings"
156
+ :ui="{ leadingIcon: 'settings-icon' }"
157
+ @click="router.push('/settings')"
162
158
  />
163
159
  </div>
164
160
  </div>
@@ -375,14 +371,6 @@ defineExpose({ focusInput, inputRef });
375
371
  opacity: 0.85;
376
372
  }
377
373
 
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
374
  .dropdown {
387
375
  border-top: 1px solid var(--supaslidev-border);
388
376
  max-height: 240px;
@@ -215,7 +215,7 @@ function handleCardClick() {
215
215
  <template v-if="!loading.dev" #leading>
216
216
  <span class="terminal-prompt-symbol">$</span>
217
217
  </template>
218
- {{ running ? 'stop' : 'dev' }}
218
+ {{ running ? 'stop' : 'present' }}
219
219
  </UButton>
220
220
 
221
221
  <UButton
@@ -0,0 +1,37 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ colors: readonly { name: string; value: string }[];
4
+ modelValue: string | undefined;
5
+ name: string;
6
+ label: string;
7
+ }>();
8
+
9
+ const emit = defineEmits<{
10
+ 'update:modelValue': [value: string];
11
+ }>();
12
+ </script>
13
+
14
+ <template>
15
+ <fieldset
16
+ class="flex items-center gap-4 rounded-xl w-fit p-2 -m-2 has-[input:focus-visible]:outline-[0.375rem] has-[input:focus-visible]:outline-double has-[input:focus-visible]:outline-primary has-[input:focus-visible]:shadow-[0_0_0_0.25rem_white]"
17
+ >
18
+ <legend class="sr-only">{{ label }}</legend>
19
+ <label
20
+ v-for="color in colors"
21
+ :key="color.name"
22
+ class="size-6 rounded-full cursor-pointer transition-transform duration-150 hover:scale-110 has-checked:outline-2 has-checked:outline-black dark:has-checked:outline-white has-checked:outline-offset-2"
23
+ :style="{ backgroundColor: color.value }"
24
+ :title="color.name"
25
+ >
26
+ <input
27
+ type="radio"
28
+ :name="name"
29
+ class="sr-only"
30
+ :checked="modelValue === color.name"
31
+ :aria-label="color.name"
32
+ :value="color.name"
33
+ @change="emit('update:modelValue', color.name)"
34
+ />
35
+ </label>
36
+ </fieldset>
37
+ </template>
@@ -0,0 +1,78 @@
1
+ const ACCENT_COLORS = ['blue', 'green', 'red', 'orange', 'teal', 'indigo', 'violet'] as const;
2
+ const NEUTRAL_COLORS = ['slate', 'gray', 'zinc', 'neutral', 'stone'] as const;
3
+
4
+ type AccentColor = (typeof ACCENT_COLORS)[number];
5
+ type NeutralColor = (typeof NEUTRAL_COLORS)[number];
6
+
7
+ interface AppSettings {
8
+ accentColor: AccentColor;
9
+ neutralColor: NeutralColor;
10
+ }
11
+
12
+ const STORAGE_KEY = 'supaslidev-settings';
13
+
14
+ const DEFAULT_SETTINGS: AppSettings = {
15
+ accentColor: 'teal',
16
+ neutralColor: 'stone',
17
+ };
18
+
19
+ function loadSettings(): AppSettings {
20
+ if (import.meta.server) return { ...DEFAULT_SETTINGS };
21
+ try {
22
+ const raw = localStorage.getItem(STORAGE_KEY);
23
+ if (raw) {
24
+ const parsed = JSON.parse(raw) as Partial<AppSettings>;
25
+ return {
26
+ accentColor:
27
+ parsed.accentColor && ACCENT_COLORS.includes(parsed.accentColor as AccentColor)
28
+ ? (parsed.accentColor as AccentColor)
29
+ : DEFAULT_SETTINGS.accentColor,
30
+ neutralColor:
31
+ parsed.neutralColor && NEUTRAL_COLORS.includes(parsed.neutralColor as NeutralColor)
32
+ ? (parsed.neutralColor as NeutralColor)
33
+ : DEFAULT_SETTINGS.neutralColor,
34
+ };
35
+ }
36
+ } catch {
37
+ // Ignore localStorage errors
38
+ }
39
+ return { ...DEFAULT_SETTINGS };
40
+ }
41
+
42
+ const _settings = ref<AppSettings>(loadSettings());
43
+ let watcherInitialized = false;
44
+
45
+ function useSettings() {
46
+ if (!watcherInitialized) {
47
+ watcherInitialized = true;
48
+ watch(
49
+ _settings,
50
+ (value) => {
51
+ try {
52
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
53
+ } catch (error) {
54
+ console.error(`Failed to persist settings to "${STORAGE_KEY}":`, error, value);
55
+ }
56
+ updateAppConfig({
57
+ ui: { colors: { primary: value.accentColor, neutral: value.neutralColor } },
58
+ });
59
+ },
60
+ { deep: true },
61
+ );
62
+ }
63
+
64
+ // Apply on first use
65
+ updateAppConfig({
66
+ ui: {
67
+ colors: {
68
+ primary: _settings.value.accentColor,
69
+ neutral: _settings.value.neutralColor,
70
+ },
71
+ },
72
+ });
73
+
74
+ return { settings: _settings };
75
+ }
76
+
77
+ export { ACCENT_COLORS, DEFAULT_SETTINGS, NEUTRAL_COLORS, useSettings };
78
+ export type { AccentColor, NeutralColor, AppSettings };
@@ -1,3 +1,9 @@
1
+ <script setup lang="ts">
2
+ import { useSettings } from '../composables/useSettings';
3
+
4
+ useSettings();
5
+ </script>
6
+
1
7
  <template>
2
8
  <UApp>
3
9
  <div class="min-h-screen bg-default flex flex-col">
@@ -13,7 +13,6 @@ const {
13
13
  } = useServers();
14
14
  const { deployMode, showDeployDemoToast } = useDeployMode();
15
15
  const toast = useToast();
16
- const colorMode = useColorMode();
17
16
  const deployBasePath = computed(() => (import.meta.env.BASE_URL || '/').replace(/\/$/, ''));
18
17
 
19
18
  function handleExportError(message: string) {
@@ -203,9 +202,11 @@ function handleImportCommand() {
203
202
  isImportDialogOpen.value = true;
204
203
  }
205
204
 
206
- function handleToggleThemeCommand() {
205
+ const router = useRouter();
206
+
207
+ function handleSettingsCommand() {
207
208
  isCommandPaletteOpen.value = false;
208
- colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark';
209
+ router.push('/settings');
209
210
  }
210
211
 
211
212
  function handleToggleViewModeCommand() {
@@ -330,10 +331,10 @@ const commandPaletteGroups = computed<CommandPaletteGroup[]>(() => {
330
331
  onSelect: handleImportCommand,
331
332
  },
332
333
  {
333
- label: 'Toggle theme',
334
- suffix: colorMode.value === 'dark' ? 'Switch to light mode' : 'Switch to dark mode',
335
- icon: colorMode.value === 'dark' ? 'i-lucide-sun' : 'i-lucide-moon',
336
- onSelect: handleToggleThemeCommand,
334
+ label: 'Settings',
335
+ suffix: 'Customize appearance',
336
+ icon: 'i-lucide-settings',
337
+ onSelect: handleSettingsCommand,
337
338
  },
338
339
  {
339
340
  label: 'Toggle view',
@@ -401,9 +402,9 @@ const commandOptions = computed(() => {
401
402
  onSelect: handleImportCommand,
402
403
  },
403
404
  {
404
- label: 'Toggle theme',
405
- description: colorMode.value === 'dark' ? 'Switch to light mode' : 'Switch to dark mode',
406
- onSelect: handleToggleThemeCommand,
405
+ label: 'Settings',
406
+ description: 'Customize appearance',
407
+ onSelect: handleSettingsCommand,
407
408
  },
408
409
  {
409
410
  label: 'Toggle view',
@@ -0,0 +1,127 @@
1
+ <script setup lang="ts">
2
+ import type { AccentColor, NeutralColor } from '../composables/useSettings';
3
+ import {
4
+ ACCENT_COLORS,
5
+ DEFAULT_SETTINGS,
6
+ NEUTRAL_COLORS,
7
+ useSettings,
8
+ } from '../composables/useSettings';
9
+
10
+ const router = useRouter();
11
+ const colorMode = useColorMode();
12
+ const { settings } = useSettings();
13
+
14
+ const ACCENT_HEX: Record<string, string> = {
15
+ blue: '#3b82f6',
16
+ green: '#22c55e',
17
+ red: '#ef4444',
18
+ orange: '#f97316',
19
+ teal: '#14b8a6',
20
+ indigo: '#6366f1',
21
+ violet: '#8b5cf6',
22
+ };
23
+
24
+ const NEUTRAL_HEX: Record<string, string> = {
25
+ slate: '#64748b',
26
+ gray: '#6b7280',
27
+ zinc: '#71717a',
28
+ neutral: '#737373',
29
+ stone: '#78716c',
30
+ };
31
+
32
+ const accentColorSwatches = ACCENT_COLORS.map((name: string) => ({
33
+ name,
34
+ value: ACCENT_HEX[name] ?? '#000000',
35
+ }));
36
+
37
+ const neutralColorSwatches = NEUTRAL_COLORS.map((name: string) => ({
38
+ name,
39
+ value: NEUTRAL_HEX[name] ?? '#000000',
40
+ }));
41
+
42
+ const themeOptions = [
43
+ { label: 'System', value: 'system' },
44
+ { label: 'Light', value: 'light' },
45
+ { label: 'Dark', value: 'dark' },
46
+ ];
47
+ </script>
48
+
49
+ <template>
50
+ <div>
51
+ <header class="mb-10">
52
+ <div class="flex items-center justify-between gap-4 mb-4">
53
+ <h1 class="text-2xl sm:text-3xl font-semibold font-mono">$ settings</h1>
54
+ <UButton
55
+ color="neutral"
56
+ variant="ghost"
57
+ icon="i-lucide-arrow-left"
58
+ class="cursor-pointer"
59
+ @click="router.push('/')"
60
+ >
61
+ Back
62
+ </UButton>
63
+ </div>
64
+ <p class="text-sm font-mono" style="color: var(--supaslidev-text-muted)">
65
+ Customize the dashboard appearance.
66
+ </p>
67
+ </header>
68
+
69
+ <div class="max-w-2xl space-y-8">
70
+ <section>
71
+ <h2
72
+ class="text-xs uppercase tracking-wider mb-4 font-mono"
73
+ style="color: var(--supaslidev-text-muted)"
74
+ >
75
+ Appearance
76
+ </h2>
77
+ <div class="settings-card space-y-6">
78
+ <div class="space-y-2">
79
+ <label for="theme-select" class="block text-sm font-medium font-mono"> Theme </label>
80
+ <USelect
81
+ id="theme-select"
82
+ v-model="colorMode.preference"
83
+ :items="themeOptions"
84
+ value-key="value"
85
+ class="max-w-48 cursor-pointer"
86
+ />
87
+ </div>
88
+
89
+ <div class="space-y-3">
90
+ <span class="block text-sm font-medium font-mono"> Accent color </span>
91
+ <SettingsColorPicker
92
+ :colors="accentColorSwatches"
93
+ :model-value="settings.accentColor"
94
+ label="Accent color"
95
+ name="accent-color"
96
+ @update:model-value="
97
+ settings.accentColor = ($event ?? DEFAULT_SETTINGS.accentColor) as AccentColor
98
+ "
99
+ />
100
+ </div>
101
+
102
+ <div class="space-y-3">
103
+ <span class="block text-sm font-medium font-mono"> Background shade </span>
104
+ <SettingsColorPicker
105
+ :colors="neutralColorSwatches"
106
+ :model-value="settings.neutralColor"
107
+ label="Background shade"
108
+ name="background-shade"
109
+ @update:model-value="
110
+ settings.neutralColor = ($event ?? DEFAULT_SETTINGS.neutralColor) as NeutralColor
111
+ "
112
+ />
113
+ </div>
114
+ </div>
115
+ </section>
116
+ </div>
117
+ </div>
118
+ </template>
119
+
120
+ <style scoped>
121
+ .settings-card {
122
+ background: var(--ui-bg-elevated);
123
+ border: 1px solid var(--supaslidev-border);
124
+ border-radius: 0.75rem;
125
+ padding: 1.5rem;
126
+ }
127
+ </style>
package/dist/cli/index.js CHANGED
@@ -9028,7 +9028,7 @@ var require_dist$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
9028
9028
  exports.visitAsync = visit.visitAsync;
9029
9029
  }));
9030
9030
  //#endregion
9031
- //#region ../../node_modules/.pnpm/tsdown@0.21.6_typescript@6.0.2_vue-tsc@3.2.6_typescript@6.0.2_/node_modules/tsdown/esm-shims.js
9031
+ //#region ../../node_modules/.pnpm/tsdown@0.21.6_oxc-resolver@11.19.1_typescript@6.0.2_vue-tsc@3.2.6_typescript@6.0.2_/node_modules/tsdown/esm-shims.js
9032
9032
  var getFilename, getDirname, __dirname, __filename;
9033
9033
  var init_esm_shims = __esmMin((() => {
9034
9034
  getFilename = () => fileURLToPath(import.meta.url);
@@ -186210,7 +186210,7 @@ var require_ts_morph = /* @__PURE__ */ __commonJSMin(((exports) => {
186210
186210
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
186211
186211
  require_dist$2();
186212
186212
  require_ts_morph();
186213
- const CLI_VERSION = "0.3.1";
186213
+ const CLI_VERSION = "0.3.3";
186214
186214
  const PACKAGE_NAME = "@supaslidev/cli";
186215
186215
  const CACHE_DIR = join(tmpdir(), "supaslidev-cli");
186216
186216
  const CACHE_FILE = join(CACHE_DIR, "version-cache.json");
@@ -186654,7 +186654,7 @@ async function create(options = {}) {
186654
186654
  console.log(import_picocolors.default.cyan("Next steps:"));
186655
186655
  console.log(` ${import_picocolors.default.dim("$")} cd ${projectName}`);
186656
186656
  if (!runInstall) console.log(` ${import_picocolors.default.dim("$")} pnpm install`);
186657
- console.log(` ${import_picocolors.default.dim("$")} pnpm dev ${presentationName}`);
186657
+ console.log(` ${import_picocolors.default.dim("$")} pnpm dev`);
186658
186658
  console.log("");
186659
186659
  } catch (error) {
186660
186660
  spinner.stop("Failed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supaslidev",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "CLI toolkit for managing Supaslidev presentations",
5
5
  "keywords": [
6
6
  "slidev",
@@ -64,7 +64,7 @@
64
64
  "typescript": "^6.0.0",
65
65
  "vitest": "^4.0.0",
66
66
  "vue-tsc": "^3.0.0",
67
- "create-supaslidev": "^0.3.1"
67
+ "create-supaslidev": "^0.3.3"
68
68
  },
69
69
  "scripts": {
70
70
  "dev": "nuxt dev",
@@ -4,7 +4,7 @@ import { existsSync, readFileSync } from 'node:fs';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { findProjectRoot } from '../utils.js';
6
6
 
7
- export function findSupaslidevPackageRoot(): string {
7
+ function findSupaslidevPackageRoot(): string {
8
8
  let dir = dirname(fileURLToPath(import.meta.url));
9
9
 
10
10
  while (dir !== dirname(dir)) {