diff --git a/src/runtime/components/Story.vue b/src/runtime/components/Story.vue
index 3bfacdc..6997852 100644
--- a/src/runtime/components/Story.vue
+++ b/src/runtime/components/Story.vue
@@ -10,11 +10,10 @@
>
- {{ title }}
+ {{ title || story?.pascalName }}
@@ -23,7 +22,7 @@
:class="tvStory().actions({ class: [classes?.actions, storyClasses.actions] })"
>
-
@@ -50,11 +49,12 @@
+
+
diff --git a/src/runtime/components/elements/Icon.vue b/src/runtime/components/elements/Icon.vue
new file mode 100644
index 0000000..7c83d2b
--- /dev/null
+++ b/src/runtime/components/elements/Icon.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
diff --git a/src/runtime/components/nav/NavFolder.vue b/src/runtime/components/nav/NavFolder.vue
new file mode 100644
index 0000000..744510e
--- /dev/null
+++ b/src/runtime/components/nav/NavFolder.vue
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
diff --git a/src/runtime/components/nav/NavItem.vue b/src/runtime/components/nav/NavItem.vue
new file mode 100644
index 0000000..fd6f391
--- /dev/null
+++ b/src/runtime/components/nav/NavItem.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+ {{ title }}
+
+ {{ Object.values(story.variants ?? {}).length || 1 }}
+
+
+
+
+
+
+
diff --git a/src/runtime/components/nav/NavRoot.vue b/src/runtime/components/nav/NavRoot.vue
new file mode 100644
index 0000000..92e071c
--- /dev/null
+++ b/src/runtime/components/nav/NavRoot.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
diff --git a/src/runtime/components/nav/types.ts b/src/runtime/components/nav/types.ts
new file mode 100644
index 0000000..5bd142d
--- /dev/null
+++ b/src/runtime/components/nav/types.ts
@@ -0,0 +1,8 @@
+import type { BedtimeStory } from '../../../types/module'
+
+export interface NavFolderData {
+ title: string
+ path: string
+ folders: NavFolderData[]
+ stories: BedtimeStory[]
+}
diff --git a/src/runtime/composables/useOptions.ts b/src/runtime/composables/useOptions.ts
new file mode 100644
index 0000000..41362d6
--- /dev/null
+++ b/src/runtime/composables/useOptions.ts
@@ -0,0 +1,37 @@
+import { onMounted, watch } from 'vue'
+import { useState } from 'nuxt/app'
+
+interface Options {
+ tree: boolean
+}
+
+const KEY = 'bedtime-options'
+
+export function useOptions() {
+ // initialize with default values for consistent ssr
+ const options = useState(KEY, () => ({
+ tree: true,
+ }))
+
+ // client-side localstorage handling
+ if (import.meta.client) {
+ onMounted(() => {
+ const stored = localStorage.getItem(KEY)
+ if (stored) {
+ try {
+ options.value = JSON.parse(stored)
+ }
+ catch (err: unknown) {
+ console.error('Failed to parse stored options:', err)
+ }
+ }
+ })
+
+ // save changes to localstorage
+ watch(options, (values) => {
+ localStorage.setItem(KEY, JSON.stringify(values))
+ }, { deep: true })
+ }
+
+ return options
+}
diff --git a/src/runtime/composables/useScrollfix.ts b/src/runtime/composables/useScrollfix.ts
new file mode 100644
index 0000000..9b29421
--- /dev/null
+++ b/src/runtime/composables/useScrollfix.ts
@@ -0,0 +1,38 @@
+import { onMounted, onUpdated, type Ref } from 'vue'
+// @ts-expect-error resolved at runtime
+import { onBeforeRouteLeave, onBeforeRouteUpdate, type RouteLocationNormalized } from '#vue-router'
+// @ts-expect-error resolved at runtime
+import { useState } from '#imports'
+
+export function useFixScroll(ref: Ref) {
+ const id = ref.value?.id || ref.value?.className
+ if (ref.value && !id) {
+ console.warn('UseScrollFix requires that the fixed element have a unique class or id')
+ }
+
+ const lastScrollTop = useState(`bedtime-${id}-scroll-top`, () => 0)
+ const saveScrollPosition = () => {
+ if (ref.value) {
+ lastScrollTop.value = ref.value.scrollTop
+ }
+ }
+ const restoreScrollPosition = () => {
+ if (ref.value) {
+ ref.value.scrollTop = lastScrollTop.value
+ }
+ }
+
+ onBeforeRouteLeave((_to: RouteLocationNormalized, _from: RouteLocationNormalized) => {
+ saveScrollPosition()
+ })
+ onBeforeRouteUpdate((_to: RouteLocationNormalized, _from: RouteLocationNormalized) => {
+ saveScrollPosition()
+ })
+
+ onMounted(() => {
+ restoreScrollPosition()
+ })
+ onUpdated(() => {
+ restoreScrollPosition()
+ })
+}
diff --git a/src/runtime/pages/story-view.vue b/src/runtime/pages/story-view.vue
index fdf8e1a..c40b8ea 100644
--- a/src/runtime/pages/story-view.vue
+++ b/src/runtime/pages/story-view.vue
@@ -4,27 +4,29 @@
:data-bedtime-theme="theme"
>
-