theme switcher
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
48
src/renderer/src/components/ThemeSwitcher.vue
Normal file
48
src/renderer/src/components/ThemeSwitcher.vue
Normal 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>
|
||||
@@ -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>
|
||||
12
src/renderer/src/components/note/Find.vue
Normal file
12
src/renderer/src/components/note/Find.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="note-find"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.note-find {
|
||||
}
|
||||
</style>
|
||||
61
src/renderer/src/components/note/Menu.vue
Normal file
61
src/renderer/src/components/note/Menu.vue
Normal 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>
|
||||
7
src/renderer/src/composables/useState.js
Normal file
7
src/renderer/src/composables/useState.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createGlobalState, useStorage } from '@vueuse/core'
|
||||
|
||||
export default createGlobalState(() => {
|
||||
const theme = useStorage('app-theme', 'dark')
|
||||
|
||||
return { theme }
|
||||
})
|
||||
17
src/renderer/src/content/instructions.md
Normal file
17
src/renderer/src/content/instructions.md
Normal 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.
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
29
src/renderer/src/views/Note.vue
Normal file
29
src/renderer/src/views/Note.vue
Normal 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>
|
||||
Reference in New Issue
Block a user