theme switcher

This commit is contained in:
nicwands
2026-03-10 11:11:22 -04:00
parent 23054d4981
commit 77b8ad2dcd
21 changed files with 88962 additions and 92 deletions

View File

@@ -14,16 +14,20 @@
import { ref, computed, onMounted } from 'vue'
import loadFonts from '@fuzzco/font-loader'
import { useWindowSize } from '@vueuse/core'
import Menu from '@/components/menu/Index.vue'
import Menu from '@/components/Menu.vue'
import Nav from '@/components/Nav.vue'
import ScrollBar from './components/ScrollBar.vue'
import useState from '@/composables/useState'
import ScrollBar from '@/components/ScrollBar.vue'
const { height } = useWindowSize()
// Theme state
const { theme } = useState()
const classes = computed(() => [
'container',
{ 'fonts-ready': !fontsLoading.value },
'theme-dark',
`theme-${theme.value}`,
])
const fontsLoading = ref(true)

View File

@@ -8,7 +8,7 @@
<router-link class="menu-item" to="/category"
>+ New Capitulum</router-link
>
<button class="menu-item">Change Theme</button>
<theme-switcher class="menu-item" />
<router-link class="menu-item" to="/instructions"
>Instructio</router-link
>
@@ -21,9 +21,10 @@
<script setup>
import NewNote from '@/components/NewNote.vue'
import ThemeSwitcher from '@/components/ThemeSwitcher.vue'
import { onClickOutside } from '@vueuse/core'
import { ref, watch } from 'vue'
import Nav from '../Nav.vue'
import Nav from './Nav.vue'
import useMenu from '@/composables/useMenu'
import { useRoute } from 'vue-router'
@@ -64,7 +65,6 @@ const openNewCategory = () => {}
padding-bottom: 10px;
.menu-item {
display: block;
padding: 16px 0;
text-align: center;

View File

@@ -0,0 +1,48 @@
<template>
<div class="theme-switcher">
<span>Change Theme</span>
<button
v-for="(value, key) in themes"
:class="[`theme-${key}`, { active: theme === key }]"
@click="setTheme(key)"
/>
</div>
</template>
<script setup>
import { themes } from '@/libs/theme'
import useState from '@/composables/useState'
const { theme } = useState()
const setTheme = (value) => {
theme.value = value
}
</script>
<style lang="scss">
.theme-switcher {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
button {
background: var(--theme-bg);
display: block;
width: 14px;
height: 14px;
border-radius: 50%;
border: none;
cursor: pointer;
&.active {
border: 1px solid var(--theme-fg);
}
}
&:hover {
color: var(--theme-fg) !important;
}
}
</style>

View File

@@ -1,62 +1,40 @@
<template>
<main v-if="editor" class="editor layout-block">
<bubble-menu :editor="editor">
<div class="bubble-menu">
<button
@click="editor.chain().focus().toggleBold().run()"
:class="{ active: editor.isActive('bold') }"
>
Bold
</button>
<button
@click="editor.chain().focus().toggleItalic().run()"
:class="{ active: editor.isActive('italic') }"
>
Italic
</button>
<button
@click="editor.chain().focus().toggleHighlight().run()"
:class="{ active: editor.isActive('highlight') }"
>
Highlight
</button>
</div>
</bubble-menu>
<div v-if="editor" class="note-editor">
<editor-content :editor="editor" class="editor-wrap" />
</main>
<editor-menu :editor="editor" />
</div>
</template>
<script setup>
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
import { TaskList, TaskItem } from '@tiptap/extension-list'
import { Highlight } from '@tiptap/extension-highlight'
import { Editor, EditorContent } from '@tiptap/vue-3'
import Document from '@tiptap/extension-document'
import { Placeholder } from '@tiptap/extensions'
import StarterKit from '@tiptap/starter-kit'
import { TaskList, TaskItem } from '@tiptap/extension-list'
import { Highlight } from '@tiptap/extension-highlight'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import { BubbleMenu } from '@tiptap/vue-3/menus'
import { all, createLowlight } from 'lowlight'
import useNotes from '@/composables/useNotes'
import { useRoute } from 'vue-router'
import StarterKit from '@tiptap/starter-kit'
import _debounce from 'lodash/debounce'
import EditorMenu from './Menu.vue'
const route = useRoute()
const id = route.params.id
const { loadNote, updateNoteContent, updateNoteMetadata } = useNotes()
const CustomDocument = Document.extend({
content: 'heading block*',
const props = defineProps({
id: {
type: String,
required: true,
},
})
const editor = shallowRef()
const { loadNote, updateNoteContent, updateNoteMetadata } = useNotes()
const updateNote = _debounce(async ({ editor }) => {
const json = editor.getJSON()
await updateNoteContent(id, json)
await updateNoteContent(props.id, json)
updateTitle(editor)
}, 300)
@@ -74,16 +52,21 @@ const updateTitle = _debounce(async (editor) => {
if (newTitle === lastTitle) return
lastTitle = newTitle
await updateNoteMetadata(id, { title: newTitle })
await updateNoteMetadata(props.id, { title: newTitle })
}, 300)
onMounted(async () => {
const note = await loadNote(id)
const note = await loadNote(props.id)
lastTitle = note.title
// Lowlight setup
const lowlight = createLowlight(all)
// Force note format
const CustomDocument = Document.extend({
content: 'heading block*',
})
editor.value = new Editor({
extensions: [
CustomDocument,
@@ -119,10 +102,7 @@ onBeforeUnmount(() => {
</script>
<style lang="scss">
main.editor {
padding-top: 8px;
padding-bottom: 20px;
.note-editor {
h1 {
font-weight: 700 !important;
@include p;
@@ -143,9 +123,8 @@ main.editor {
p em {
font-style: italic;
}
hr::before {
content: '----------------------------------------';
@include p;
hr {
border: 1px dashed currentColor;
}
ul {
list-style-type: disc;
@@ -181,7 +160,7 @@ main.editor {
code {
border: 1px solid var(--grey-100);
color: var(--theme-accent);
padding: 0.2em;
padding: 0 0.2em;
border-radius: 0.2em;
}
pre code {
@@ -263,8 +242,7 @@ main.editor {
mark {
background: var(--theme-accent);
color: var(--theme-bg);
padding: 0.2em;
border-radius: 0.2em;
padding: 0 0.2em;
}
ul[data-type='taskList'] {
@@ -325,29 +303,5 @@ main.editor {
}
}
}
.bubble-menu {
display: flex;
gap: 5px;
border: 1px solid var(--grey-100);
color: var(--grey-100);
border-radius: 0.2em;
background: var(--theme-bg);
button {
cursor: pointer;
padding: 0.2em;
border-radius: 0.2em;
&:hover {
background: var(--grey-100);
color: var(--theme-bg);
}
&.active {
background: var(--theme-fg);
color: var(--theme-bg);
}
}
}
}
</style>

View File

@@ -0,0 +1,12 @@
<template>
<div class="note-find"></div>
</template>
<script setup>
const props = defineProps({})
</script>
<style lang="scss">
.note-find {
}
</style>

View File

@@ -0,0 +1,61 @@
<template>
<bubble-menu v-if="editor" :editor="editor">
<div class="note-menu">
<button
@click="editor.chain().focus().toggleBold().run()"
:class="{ active: editor.isActive('bold') }"
>
Bold
</button>
<button
@click="editor.chain().focus().toggleItalic().run()"
:class="{ active: editor.isActive('italic') }"
>
Italic
</button>
<button
@click="editor.chain().focus().toggleHighlight().run()"
:class="{ active: editor.isActive('highlight') }"
>
Highlight
</button>
</div>
</bubble-menu>
</template>
<script setup>
import { BubbleMenu } from '@tiptap/vue-3/menus'
const props = defineProps({
editor: {
type: Object,
required: true,
},
})
</script>
<style lang="scss">
.note-menu {
display: flex;
gap: 5px;
border: 1px solid var(--grey-100);
color: var(--grey-100);
border-radius: 0.2em;
background: var(--theme-bg);
button {
cursor: pointer;
padding: 0.2em;
border-radius: 0.2em;
&:hover {
background: var(--grey-100);
color: var(--theme-bg);
}
&.active {
background: var(--theme-fg);
color: var(--theme-bg);
}
}
}
</style>

View File

@@ -0,0 +1,7 @@
import { createGlobalState, useStorage } from '@vueuse/core'
export default createGlobalState(() => {
const theme = useStorage('app-theme', 'dark')
return { theme }
})

View File

@@ -0,0 +1,17 @@
# Instructions
This will be the instructions for the application.
---
## Getting Started
1. Install the application by running `npm install` in the project directory.
2. Start the application by running `npm start` in the project directory.
3. Open the application in your browser at `http://localhost:3000`.
## Usage
1. Create a new note by clicking the "New Note" button.
2. Edit an existing note by clicking on it.
3. Delete a note by clicking the "Delete" button.

View File

@@ -7,18 +7,18 @@ const colors = {
}
const themes = {
light: {
bg: colors.white,
fg: colors.black,
accent: colors.green,
link: colors.blue,
},
dark: {
bg: colors.black,
fg: colors.white,
accent: colors.green,
link: colors.blue,
},
light: {
bg: colors.white,
fg: colors.black,
accent: colors.green,
link: colors.blue,
},
}
export { colors, themes }

View File

@@ -1,14 +1,14 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import Directory from '@/views/Directory.vue'
import Editor from '@/views/Editor.vue'
import Note from '@/views/Note.vue'
import CreateCategory from '@/views/CreateCategory.vue'
import Category from '@/views/Category.vue'
import Instructions from '@/views/Instructions.vue'
const routes = [
{ path: '/', name: 'directory', component: Directory },
{ path: '/note/:id', name: 'note', component: Editor },
{ path: '/note/:id', name: 'note', component: Note },
{ path: '/category', name: 'create-category', component: CreateCategory },
{ path: '/category/:id', name: 'category', component: Category },
{ path: '/instructions', name: 'instructions', component: Instructions },

View File

@@ -46,7 +46,8 @@ p,
a,
button,
input,
pre {
pre,
span {
@include p;
}
.bold {
@@ -64,9 +65,9 @@ button {
// Text selection
::selection {
color: var(--theme-bg);
background: var(--theme-accent);
background: var(--theme-fg);
}
::-moz-selection {
color: var(--theme-bg);
background: var(--theme-accent);
background: var(--theme-fg);
}

View File

@@ -0,0 +1,29 @@
<template>
<main class="note layout-block">
<note-editor :id="id" />
</main>
</template>
<script setup>
import { watchEffect } from 'vue'
import { useMagicKeys } from '@vueuse/core'
import { useRoute } from 'vue-router'
import NoteEditor from '@/components/note/Editor.vue'
const route = useRoute()
const id = route.params.id
const { ctrl, f } = useMagicKeys()
watchEffect(() => {
if (ctrl.value && f.value) {
console.log('find')
}
})
</script>
<style lang="scss">
main.note {
padding-top: 8px;
padding-bottom: 20px;
}
</style>