slidev-workspace 0.1.7 → 0.1.8

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/dist/index.d.ts CHANGED
@@ -1,23 +1,5 @@
1
1
  import * as vue0 from "vue";
2
2
 
3
- //#region src/preview/composables/useSlides.d.ts
4
- interface SlideData$1 {
5
- title: string;
6
- url: string;
7
- description: string;
8
- image: string;
9
- author: string;
10
- date: string;
11
- theme?: string;
12
- transition?: string;
13
- class?: string;
14
- }
15
- declare function useSlides(): {
16
- slides: vue0.ComputedRef<SlideData$1[]>;
17
- slidesCount: vue0.ComputedRef<number>;
18
- loadSlidesData: () => Promise<void>;
19
- };
20
- //#endregion
21
3
  //#region src/types/slide.d.ts
22
4
  interface SlideFrontmatter {
23
5
  theme?: string;
@@ -37,7 +19,6 @@ interface SlideFrontmatter {
37
19
  };
38
20
  author?: string;
39
21
  date?: string;
40
- [key: string]: any;
41
22
  }
42
23
  interface SlideInfo {
43
24
  id: string;
@@ -48,7 +29,6 @@ interface SlideInfo {
48
29
  content: string;
49
30
  }
50
31
  interface SlideData {
51
- id: string;
52
32
  title: string;
53
33
  url: string;
54
34
  description: string;
@@ -58,13 +38,15 @@ interface SlideData {
58
38
  theme?: string;
59
39
  transition?: string;
60
40
  class?: string;
61
- sourceDir: string;
62
- path: string;
63
- fullPath: string;
64
- frontmatter: SlideFrontmatter;
65
- content: string;
66
41
  }
67
42
  //#endregion
43
+ //#region src/preview/composables/useSlides.d.ts
44
+ declare function useSlides(): {
45
+ slides: vue0.ComputedRef<SlideData[]>;
46
+ slidesCount: vue0.ComputedRef<number>;
47
+ loadSlidesData: () => Promise<void>;
48
+ };
49
+ //#endregion
68
50
  //#region src/types/config.d.ts
69
51
  interface SlidevWorkspaceConfig {
70
52
  slidesDir: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slidev-workspace",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "A workspace tool for managing multiple Slidev presentations with API-based content management",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,7 +35,7 @@
35
35
  "access": "public"
36
36
  },
37
37
  "dependencies": {
38
- "yaml": "^2.8.0",
38
+ "@tailwindcss/vite": "^4.1.11",
39
39
  "@vitejs/plugin-vue": "^6.0.0",
40
40
  "@vueuse/core": "^13.5.0",
41
41
  "class-variance-authority": "^0.7.1",
@@ -45,29 +45,24 @@
45
45
  "tw-animate-css": "^1.3.5",
46
46
  "vite": "npm:rolldown-vite@latest",
47
47
  "vue": "^3.5.17",
48
- "@tailwindcss/vite": "^4.1.11"
48
+ "yaml": "^2.8.0"
49
49
  },
50
50
  "devDependencies": {
51
- "tsdown": "^0.11.9",
52
- "typescript": "^5.8.3",
53
- "vitest": "^3.1.3",
54
- "vue-tsc": "^3.0.3",
51
+ "@prettier/plugin-oxc": "^0.0.4",
55
52
  "@tsconfig/node22": "^22.0.2",
56
53
  "@types/node": "^22.15.32",
57
- "@vue/eslint-config-prettier": "^10.2.0",
58
- "@vue/eslint-config-typescript": "^14.5.1",
59
54
  "@vue/tsconfig": "^0.7.0",
60
- "autoprefixer": "^10.4.21",
61
- "eslint": "^9.29.0",
62
- "eslint-plugin-oxlint": "~1.1.0",
63
- "eslint-plugin-vue": "~10.2.0",
64
55
  "jiti": "^2.4.2",
65
56
  "npm-run-all2": "^8.0.4",
66
57
  "oxlint": "~1.1.0",
67
58
  "postcss": "^8.5.6",
68
59
  "prettier": "3.5.3",
69
60
  "tailwindcss": "^4.1.11",
70
- "vite-plugin-vue-devtools": "^7.7.7"
61
+ "tsdown": "^0.11.9",
62
+ "typescript": "^5.8.3",
63
+ "vite-plugin-vue-devtools": "^7.7.7",
64
+ "vitest": "^3.1.3",
65
+ "vue-tsc": "^3.0.3"
71
66
  },
72
67
  "scripts": {
73
68
  "build": "tsdown",
@@ -75,7 +70,7 @@
75
70
  "dev:watch": "tsdown --watch",
76
71
  "test": "vitest",
77
72
  "typecheck": "vue-tsc --noEmit",
78
- "lint": "eslint . --fix",
73
+ "lint": "oxlint",
79
74
  "format": "prettier --write src/"
80
75
  }
81
76
  }
@@ -1,4 +1,4 @@
1
- @import 'tailwindcss';
1
+ @import "tailwindcss";
2
2
  @import "tw-animate-css";
3
3
 
4
4
  @custom-variant dark (&:is(.dark *));
@@ -1,5 +1,8 @@
1
1
  <template>
2
- <Card class="group hover:shadow-lg transition-all duration-200 cursor-pointer" @click="$emit('click')">
2
+ <Card
3
+ class="group hover:shadow-lg transition-all duration-200 cursor-pointer"
4
+ @click="$emit('click')"
5
+ >
3
6
  <div class="relative overflow-hidden rounded-t-lg">
4
7
  <img
5
8
  :src="image || '/placeholder.svg'"
@@ -9,24 +12,36 @@
9
12
  </div>
10
13
 
11
14
  <CardHeader class="pb-2">
12
- <CardTitle class="text-lg line-clamp-2 group-hover:text-primary transition-colors">
15
+ <CardTitle
16
+ class="text-lg line-clamp-2 group-hover:text-primary transition-colors"
17
+ >
13
18
  {{ title }}
14
19
  </CardTitle>
15
20
  </CardHeader>
16
21
 
17
22
  <CardContent class="space-y-3">
18
- <CardDescription class="line-clamp-3 text-sm h-[40px]">{{ description }}</CardDescription>
23
+ <CardDescription class="line-clamp-3 text-sm h-[40px]">{{
24
+ description
25
+ }}</CardDescription>
19
26
 
20
27
  <div class="flex flex-wrap gap-1 mb-2">
21
- <span v-if="theme" class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-blue-100 text-blue-800">
28
+ <span
29
+ v-if="theme"
30
+ class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-blue-100 text-blue-800"
31
+ >
22
32
  {{ theme }}
23
33
  </span>
24
- <span v-if="sourceDir" class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-green-100 text-green-800">
25
- {{ sourceDir.split('/').pop() }}
34
+ <span
35
+ v-if="sourceDir"
36
+ class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-green-100 text-green-800"
37
+ >
38
+ {{ sourceDir.split("/").pop() }}
26
39
  </span>
27
40
  </div>
28
41
 
29
- <div class="flex items-center justify-between text-xs text-muted-foreground pt-2 border-t">
42
+ <div
43
+ class="flex items-center justify-between text-xs text-muted-foreground pt-2 border-t"
44
+ >
30
45
  <div class="flex items-center gap-1">
31
46
  <User class="h-3 w-3" />
32
47
  <span>{{ author }}</span>
@@ -41,22 +56,28 @@
41
56
  </template>
42
57
 
43
58
  <script setup lang="ts">
44
- import { Calendar, User } from 'lucide-vue-next'
59
+ import { Calendar, User } from "lucide-vue-next";
45
60
 
46
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
61
+ import {
62
+ Card,
63
+ CardContent,
64
+ CardDescription,
65
+ CardHeader,
66
+ CardTitle,
67
+ } from "@/components/ui/card";
47
68
 
48
69
  defineProps<{
49
- title: string
50
- image?: string
51
- description?: string
52
- url: string
53
- author: string
54
- date: string
55
- theme?: string
56
- sourceDir?: string
57
- }>()
70
+ title: string;
71
+ image?: string;
72
+ description?: string;
73
+ url: string;
74
+ author: string;
75
+ date: string;
76
+ theme?: string;
77
+ sourceDir?: string;
78
+ }>();
58
79
 
59
80
  defineEmits<{
60
- click: []
61
- }>()
81
+ click: [];
82
+ }>();
62
83
  </script>
@@ -47,13 +47,6 @@
47
47
  />
48
48
  </div>
49
49
  </div>
50
-
51
- <!-- Slide Detail Modal -->
52
- <SlideDetail
53
- v-if="selectedSlide"
54
- :slide="selectedSlide"
55
- @close="closeSlide"
56
- />
57
50
  </div>
58
51
  </template>
59
52
 
@@ -62,27 +55,25 @@ import { ref, computed } from "vue";
62
55
  import { useSlides } from "../composables/useSlides";
63
56
  import { Input } from "../components/ui/input";
64
57
  import SlideCard from "./SlideCard.vue";
65
- import SlideDetail from "./SlideDetail.vue";
58
+ import type { SlideData } from "../../types/slide";
66
59
 
67
60
  const searchTerm = ref("");
68
61
  const { slides, slidesCount } = useSlides();
69
62
 
70
63
  const filteredSlides = computed(() => {
71
64
  if (!searchTerm.value) return slides.value;
72
- return slides.value.filter((slide) =>
73
- slide.title.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
74
- slide.description.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
75
- slide.author.toLowerCase().includes(searchTerm.value.toLowerCase())
65
+ return slides.value.filter(
66
+ (slide) =>
67
+ slide.title.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
68
+ slide.description
69
+ .toLowerCase()
70
+ .includes(searchTerm.value.toLowerCase()) ||
71
+ slide.author.toLowerCase().includes(searchTerm.value.toLowerCase()),
76
72
  );
77
73
  });
78
74
 
79
- const selectedSlide = ref(null);
80
-
81
- const openSlide = (slide: any) => {
82
- selectedSlide.value = slide;
83
- };
84
-
85
- const closeSlide = () => {
86
- selectedSlide.value = null;
75
+ const openSlide = (slide: SlideData) => {
76
+ const url = `${window.location.href}/${slide.url}`;
77
+ window.open(url, "_blank");
87
78
  };
88
79
  </script>
@@ -14,8 +14,8 @@ import type { VariantProps } from "class-variance-authority";
14
14
 
15
15
  interface Props {
16
16
  as?: string;
17
- variant?: VariantProps<typeof buttonVariants>['variant'];
18
- size?: VariantProps<typeof buttonVariants>['size'];
17
+ variant?: VariantProps<typeof buttonVariants>["variant"];
18
+ size?: VariantProps<typeof buttonVariants>["size"];
19
19
  }
20
20
 
21
21
  const props = withDefaults(defineProps<Props>(), {
@@ -26,7 +26,7 @@ export const buttonVariants = cva(
26
26
  variant: "default",
27
27
  size: "default",
28
28
  },
29
- }
29
+ },
30
30
  );
31
31
 
32
32
  export type ButtonVariants = VariantProps<typeof buttonVariants>;
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
- import type { HTMLAttributes } from 'vue'
3
- import { cn } from '@/lib/utils'
2
+ import type { HTMLAttributes } from "vue";
3
+ import { cn } from "@/lib/utils";
4
4
 
5
5
  const props = defineProps<{
6
- class?: HTMLAttributes['class']
7
- }>()
6
+ class?: HTMLAttributes["class"];
7
+ }>();
8
8
  </script>
9
9
 
10
10
  <template>
@@ -1,16 +1,21 @@
1
1
  <script setup lang="ts">
2
- import type { HTMLAttributes } from 'vue'
3
- import { cn } from '@/lib/utils'
2
+ import type { HTMLAttributes } from "vue";
3
+ import { cn } from "@/lib/utils";
4
4
 
5
5
  const props = defineProps<{
6
- class?: HTMLAttributes['class']
7
- }>()
6
+ class?: HTMLAttributes["class"];
7
+ }>();
8
8
  </script>
9
9
 
10
10
  <template>
11
11
  <div
12
12
  data-slot="card-action"
13
- :class="cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', props.class)"
13
+ :class="
14
+ cn(
15
+ 'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
16
+ props.class,
17
+ )
18
+ "
14
19
  >
15
20
  <slot />
16
21
  </div>
@@ -1,17 +1,14 @@
1
1
  <script setup lang="ts">
2
- import type { HTMLAttributes } from 'vue'
3
- import { cn } from '@/lib/utils'
2
+ import type { HTMLAttributes } from "vue";
3
+ import { cn } from "@/lib/utils";
4
4
 
5
5
  const props = defineProps<{
6
- class?: HTMLAttributes['class']
7
- }>()
6
+ class?: HTMLAttributes["class"];
7
+ }>();
8
8
  </script>
9
9
 
10
10
  <template>
11
- <div
12
- data-slot="card-content"
13
- :class="cn('px-6 pb-6', props.class)"
14
- >
11
+ <div data-slot="card-content" :class="cn('px-6 pb-6', props.class)">
15
12
  <slot />
16
13
  </div>
17
14
  </template>
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
- import type { HTMLAttributes } from 'vue'
3
- import { cn } from '@/lib/utils'
2
+ import type { HTMLAttributes } from "vue";
3
+ import { cn } from "@/lib/utils";
4
4
 
5
5
  const props = defineProps<{
6
- class?: HTMLAttributes['class']
7
- }>()
6
+ class?: HTMLAttributes["class"];
7
+ }>();
8
8
  </script>
9
9
 
10
10
  <template>
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
- import type { HTMLAttributes } from 'vue'
3
- import { cn } from '@/lib/utils'
2
+ import type { HTMLAttributes } from "vue";
3
+ import { cn } from "@/lib/utils";
4
4
 
5
5
  const props = defineProps<{
6
- class?: HTMLAttributes['class']
7
- }>()
6
+ class?: HTMLAttributes["class"];
7
+ }>();
8
8
  </script>
9
9
 
10
10
  <template>
@@ -1,14 +1,17 @@
1
1
  <script setup lang="ts">
2
- import type { HTMLAttributes } from 'vue'
3
- import { cn } from '@/lib/utils'
2
+ import type { HTMLAttributes } from "vue";
3
+ import { cn } from "@/lib/utils";
4
4
 
5
5
  const props = defineProps<{
6
- class?: HTMLAttributes['class']
7
- }>()
6
+ class?: HTMLAttributes["class"];
7
+ }>();
8
8
  </script>
9
9
 
10
10
  <template>
11
- <div data-slot="card-header" :class="cn('@container/card-header gap-1.5 pt-6 px-6', props.class)">
11
+ <div
12
+ data-slot="card-header"
13
+ :class="cn('@container/card-header gap-1.5 pt-6 px-6', props.class)"
14
+ >
12
15
  <slot />
13
16
  </div>
14
17
  </template>
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
- import type { HTMLAttributes } from 'vue'
3
- import { cn } from '@/lib/utils'
2
+ import type { HTMLAttributes } from "vue";
3
+ import { cn } from "@/lib/utils";
4
4
 
5
5
  const props = defineProps<{
6
- class?: HTMLAttributes['class']
7
- }>()
6
+ class?: HTMLAttributes["class"];
7
+ }>();
8
8
  </script>
9
9
 
10
10
  <template>
@@ -1,7 +1,7 @@
1
- export { default as Card } from './Card.vue'
2
- export { default as CardAction } from './CardAction.vue'
3
- export { default as CardContent } from './CardContent.vue'
4
- export { default as CardDescription } from './CardDescription.vue'
5
- export { default as CardFooter } from './CardFooter.vue'
6
- export { default as CardHeader } from './CardHeader.vue'
7
- export { default as CardTitle } from './CardTitle.vue'
1
+ export { default as Card } from "./Card.vue";
2
+ export { default as CardAction } from "./CardAction.vue";
3
+ export { default as CardContent } from "./CardContent.vue";
4
+ export { default as CardDescription } from "./CardDescription.vue";
5
+ export { default as CardFooter } from "./CardFooter.vue";
6
+ export { default as CardHeader } from "./CardHeader.vue";
7
+ export { default as CardTitle } from "./CardTitle.vue";
@@ -1,33 +1,35 @@
1
1
  <script setup lang="ts">
2
- import type { HTMLAttributes } from 'vue'
3
- import { useVModel } from '@vueuse/core'
4
- import { cn } from '@/lib/utils'
2
+ import type { HTMLAttributes } from "vue";
3
+ import { useVModel } from "@vueuse/core";
4
+ import { cn } from "@/lib/utils";
5
5
 
6
6
  const props = defineProps<{
7
- defaultValue?: string | number
8
- modelValue?: string | number
9
- class?: HTMLAttributes['class']
10
- }>()
7
+ defaultValue?: string | number;
8
+ modelValue?: string | number;
9
+ class?: HTMLAttributes["class"];
10
+ }>();
11
11
 
12
12
  const emits = defineEmits<{
13
- (e: 'update:modelValue', payload: string | number): void
14
- }>()
13
+ (e: "update:modelValue", payload: string | number): void;
14
+ }>();
15
15
 
16
- const modelValue = useVModel(props, 'modelValue', emits, {
16
+ const modelValue = useVModel(props, "modelValue", emits, {
17
17
  passive: true,
18
18
  defaultValue: props.defaultValue,
19
- })
19
+ });
20
20
  </script>
21
21
 
22
22
  <template>
23
23
  <input
24
24
  v-model="modelValue"
25
25
  data-slot="input"
26
- :class="cn(
27
- 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
28
- 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
29
- 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
30
- props.class,
31
- )"
32
- >
26
+ :class="
27
+ cn(
28
+ 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
29
+ 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
30
+ 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
31
+ props.class,
32
+ )
33
+ "
34
+ />
33
35
  </template>
@@ -1 +1 @@
1
- export { default as Input } from './Input.vue'
1
+ export { default as Input } from "./Input.vue";
@@ -1,17 +1,5 @@
1
1
  import { computed, ref } from "vue";
2
- import type { SlideInfo } from "../../types/slide.js";
3
-
4
- export interface SlideData {
5
- title: string;
6
- url: string;
7
- description: string;
8
- image: string;
9
- author: string;
10
- date: string;
11
- theme?: string;
12
- transition?: string;
13
- class?: string;
14
- }
2
+ import type { SlideData, SlideInfo } from "../../types/slide.js";
15
3
 
16
4
  export function useSlides() {
17
5
  const slidesData = ref<SlideInfo[]>([]);
@@ -1,12 +1,12 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Slidev Workspace</title>
7
- </head>
8
- <body>
9
- <div id="app"></div>
10
- <script type="module" src="/main.ts"></script>
11
- </body>
12
- </html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Slidev Workspace</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -1,6 +1,6 @@
1
- import { type ClassValue, clsx } from 'clsx'
2
- import { twMerge } from 'tailwind-merge'
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
3
 
4
4
  export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs))
5
+ return twMerge(clsx(inputs));
6
6
  }
@@ -1,6 +1,6 @@
1
- import './assets/main.css'
1
+ import "./assets/main.css";
2
2
 
3
- import { createApp } from 'vue'
4
- import App from './App.vue'
3
+ import { createApp } from "vue";
4
+ import App from "./App.vue";
5
5
 
6
- createApp(App).mount('#app')
6
+ createApp(App).mount("#app");
@@ -1,165 +0,0 @@
1
- <template>
2
- <div
3
- class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"
4
- @click="$emit('close')"
5
- >
6
- <div
7
- class="bg-white rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto"
8
- @click.stop
9
- >
10
- <div class="p-6">
11
- <div class="flex items-start justify-between mb-6">
12
- <div class="flex-1">
13
- <h1 class="text-2xl font-bold mb-2">{{ slide.title }}</h1>
14
- <div class="flex items-center gap-4 text-sm text-muted-foreground">
15
- <div class="flex items-center gap-1">
16
- <User class="h-4 w-4" />
17
- <span>{{ slide.author }}</span>
18
- </div>
19
- <div class="flex items-center gap-1">
20
- <Calendar class="h-4 w-4" />
21
- <span>{{ slide.date }}</span>
22
- </div>
23
- <div v-if="slide.theme" class="flex items-center gap-1">
24
- <Palette class="h-4 w-4" />
25
- <span>{{ slide.theme }}</span>
26
- </div>
27
- </div>
28
- </div>
29
- <Button variant="ghost" size="sm" @click="$emit('close')">
30
- <X class="h-4 w-4" />
31
- </Button>
32
- </div>
33
-
34
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
35
- <!-- Preview Image -->
36
- <div class="space-y-4">
37
- <div
38
- class="aspect-video bg-gradient-to-br from-gray-100 to-gray-200 rounded-lg overflow-hidden"
39
- >
40
- <img
41
- :src="slide.image"
42
- :alt="slide.title"
43
- class="w-full h-full object-cover"
44
- @error="handleImageError"
45
- />
46
- </div>
47
-
48
- <!-- Slide Actions -->
49
- <div class="flex gap-2">
50
- <Button @click="openInSlidev" class="flex-1">
51
- <ExternalLink class="h-4 w-4 mr-2" />
52
- Open in Slidev
53
- </Button>
54
- <Button variant="outline" @click="copyPath">
55
- <Copy class="h-4 w-4 mr-2" />
56
- Copy Path
57
- </Button>
58
- </div>
59
- </div>
60
-
61
- <!-- Slide Information -->
62
- <div class="space-y-6">
63
- <div>
64
- <h3 class="font-semibold mb-2">Description</h3>
65
- <p class="text-muted-foreground">{{ slide.description }}</p>
66
- </div>
67
-
68
- <div>
69
- <h3 class="font-semibold mb-2">Frontmatter</h3>
70
- <div class="bg-gray-50 rounded-lg p-4">
71
- <pre class="text-sm overflow-x-auto">{{
72
- formatFrontmatter(slide.frontmatter)
73
- }}</pre>
74
- </div>
75
- </div>
76
-
77
- <div>
78
- <h3 class="font-semibold mb-2">File Information</h3>
79
- <div class="space-y-2 text-sm">
80
- <div class="flex justify-between">
81
- <span class="text-muted-foreground">ID:</span>
82
- <code class="bg-gray-100 px-2 py-1 rounded">{{
83
- slide.id
84
- }}</code>
85
- </div>
86
- <div class="flex justify-between">
87
- <span class="text-muted-foreground">Path:</span>
88
- <code class="bg-gray-100 px-2 py-1 rounded">{{
89
- slide.path
90
- }}</code>
91
- </div>
92
- <div class="flex justify-between">
93
- <span class="text-muted-foreground">Source:</span>
94
- <span>{{
95
- slide.sourceDir?.split("/").pop() || "Unknown"
96
- }}</span>
97
- </div>
98
- <div class="flex justify-between">
99
- <span class="text-muted-foreground">Full Path:</span>
100
- <code class="bg-gray-100 px-2 py-1 rounded text-xs">{{
101
- slide.fullPath
102
- }}</code>
103
- </div>
104
- </div>
105
- </div>
106
-
107
- <div v-if="slide.content">
108
- <h3 class="font-semibold mb-2">Content Preview</h3>
109
- <div class="bg-gray-50 rounded-lg p-4 max-h-60 overflow-y-auto">
110
- <pre class="text-sm whitespace-pre-wrap"
111
- >{{ slide.content.substring(0, 500)
112
- }}{{ slide.content.length > 500 ? "..." : "" }}</pre
113
- >
114
- </div>
115
- </div>
116
- </div>
117
- </div>
118
- </div>
119
- </div>
120
- </div>
121
- </template>
122
-
123
- <script setup lang="ts">
124
- import {
125
- X,
126
- User,
127
- Calendar,
128
- Palette,
129
- ExternalLink,
130
- Copy,
131
- } from "lucide-vue-next";
132
- import { computed } from "vue";
133
- import { Button } from "@/components/ui/button";
134
- import type { SlideData } from "../../types/slide";
135
-
136
- const props = defineProps<{
137
- slide: SlideData;
138
- }>();
139
-
140
- defineEmits<{
141
- close: [];
142
- }>();
143
-
144
- const slidevUrl = computed(() => {
145
- return `${window.location.href}/${props.slide.url}`;
146
- });
147
-
148
- const formatFrontmatter = (frontmatter: any) => {
149
- return JSON.stringify(frontmatter, null, 2);
150
- };
151
-
152
- const handleImageError = (event: Event) => {
153
- const target = event.target as HTMLImageElement;
154
- target.src = "https://cover.sli.dev";
155
- };
156
-
157
- const openInSlidev = () => {
158
- window.open(slidevUrl.value, "_blank");
159
- };
160
-
161
- const copyPath = () => {
162
- navigator.clipboard.writeText(slidevUrl.value);
163
- // You could add a toast notification here
164
- };
165
- </script>
@@ -1,65 +0,0 @@
1
- import slidesData from "slidev:content";
2
- import type { SlideData, SlideInfo } from "../../types/slide.js";
3
-
4
- export function getSlides(): SlideData[] {
5
- try {
6
- if (slidesData && slidesData.length > 0) {
7
- return (slidesData as SlideInfo[]).map((slide) => ({
8
- id: slide.id,
9
- title: slide.frontmatter.title || slide.path,
10
- url: slide.path,
11
- description:
12
- slide.frontmatter.info ||
13
- slide.frontmatter.seoMeta?.ogDescription ||
14
- "No description available",
15
- image:
16
- slide.frontmatter.background ||
17
- slide.frontmatter.seoMeta?.ogImage ||
18
- "https://cover.sli.dev",
19
- author: slide.frontmatter.author || "Unknown Author",
20
- date: slide.frontmatter.date || new Date().toISOString().split("T")[0],
21
- theme: slide.frontmatter.theme,
22
- transition: slide.frontmatter.transition,
23
- class: slide.frontmatter.class,
24
- sourceDir: slide.sourceDir,
25
- path: slide.path,
26
- fullPath: slide.fullPath,
27
- frontmatter: slide.frontmatter,
28
- content: slide.content,
29
- }));
30
- }
31
- } catch (error) {
32
- console.error("Error reading slides frontmatter:", error);
33
- }
34
-
35
- return [];
36
- }
37
-
38
- export function getSlideById(id: string): SlideData | null {
39
- const slides = getSlides();
40
- return slides.find((slide) => slide.id === id) || null;
41
- }
42
-
43
- export function getSlidesBySourceDir(sourceDir: string): SlideData[] {
44
- const slides = getSlides();
45
- return slides.filter((slide) => slide.sourceDir === sourceDir);
46
- }
47
-
48
- export function searchSlides(query: string): SlideData[] {
49
- const slides = getSlides();
50
- const lowerQuery = query.toLowerCase();
51
-
52
- return slides.filter(
53
- (slide) =>
54
- slide.title.toLowerCase().includes(lowerQuery) ||
55
- slide.description.toLowerCase().includes(lowerQuery) ||
56
- slide.author.toLowerCase().includes(lowerQuery) ||
57
- (slide.theme && slide.theme.toLowerCase().includes(lowerQuery))
58
- );
59
- }
60
-
61
- if (import.meta.hot) {
62
- import.meta.hot.accept("slidev:content", (_newSlidesData: any) => {
63
- console.log("Slides data updated");
64
- });
65
- }