test supabase plugin

This commit is contained in:
nicwands
2026-02-27 17:33:11 -05:00
parent a69d12b2f1
commit d7d4ba1a57
21 changed files with 597 additions and 101 deletions

View File

@@ -1,6 +1,8 @@
import 'dotenv/config'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import { app, shell, BrowserWindow, ipcMain } from 'electron'
import filesystemPlugin from '@takerofnotes/plugin-filesystem'
import supabasePlugin from '@takerofnotes/plugin-supabase'
import PluginRegistry from './core/PluginRegistry.js'
import PluginConfig from './core/PluginConfig.js'
import NotesAPI from './core/NotesAPI.js'
@@ -64,18 +66,6 @@ function createNoteWindow(noteId) {
}
app.whenReady().then(async () => {
electronApp.setAppUserModelId('com.electron')
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// Open note in new window
ipcMain.on('open-note-window', (_, noteId) => {
createNoteWindow(noteId)
@@ -86,13 +76,19 @@ app.whenReady().then(async () => {
// Register built-in plugins
registry.register(filesystemPlugin)
registry.register(supabasePlugin)
// Pull plugin config
const config = await new PluginConfig(filesystemPlugin).load()
// Create instance of active adapter
const plugin = registry.get(config.activeAdapter)
const adapter = plugin.createAdapter(config.adapterConfig)
// const plugin = registry.get(config.activeAdapter)
const plugin = registry.get(supabasePlugin.id)
// const adapter = plugin.createAdapter(config.adapterConfig)
const adapter = plugin.createAdapter({
supabaseKey: process.env.SUPABASE_KEY,
supabaseUrl: process.env.SUPABASE_URL,
})
// Init Notes API
const notesAPI = new NotesAPI(adapter)
@@ -105,6 +101,18 @@ app.whenReady().then(async () => {
}
return notesAPI[method](...args)
})
electronApp.setAppUserModelId('com.electron')
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', () => {

View File

@@ -1,22 +1,22 @@
<template>
<lenis root :options="{ duration: 1 }">
<div :class="classes" :style="styles">
<Nav />
<div :class="classes" :style="styles">
<Nav />
<router-view :key="$route.fullPath" />
<router-view :key="$route.fullPath" />
<Menu />
</div>
</lenis>
<Menu />
<ScrollBar />
</div>
</template>
<script setup>
import Lenis from './components/Lenis.vue'
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 Nav from '@/components/Nav.vue'
import ScrollBar from './components/ScrollBar.vue'
const { height } = useWindowSize()

View File

@@ -5,25 +5,31 @@
</template>
<script setup>
import { useRoute, useRouter } from 'vue-router'
import useMenu from '@/composables/useMenu'
import { onMounted } from 'vue'
const route = useRoute()
const router = useRouter()
const { menuOpen, closeMenu, openMenu } = useMenu()
const toggleMenu = () => {
const state = route.query.menuOpen
if (state === 'true') {
router.push({ query: { menuOpen: false } })
if (menuOpen.value) {
closeMenu()
} else {
router.push({ query: { menuOpen: true } })
openMenu()
}
}
onMounted(() => {
// Initialize menu state or perform any other necessary setup
// Example: Check if the user is logged in and update menu accordingly
// if (isLoggedIn()) {
// openMenu()
// }
})
</script>
<style lang="scss">
.nav {
margin-top: size-vw(9px);
padding-top: size-vw(9px);
color: var(--grey-100);
}
</style>

View File

@@ -6,13 +6,19 @@
import useOpenNote from '@/composables/useOpenNote'
import useNotes from '@/composables/useNotes'
const props = defineProps({ category: String })
const emit = defineEmits(['noteOpened'])
const { createNote } = useNotes()
const { openNote } = useOpenNote()
const onClick = async () => {
const note = await createNote({}, '')
const note = await createNote(
{
category: props.category ?? null,
},
'',
)
openNote(note.id)
emit('noteOpened')
}

View File

@@ -0,0 +1,97 @@
<template>
<div v-if="showScrollBar" class="scroll-bar">
<div class="inner" ref="inner">
<div class="handle" ref="handle" />
</div>
</div>
</template>
<script setup>
import { ref, onBeforeUnmount, computed, watch } from 'vue'
import { useElementSize, useWindowScroll, useWindowSize } from '@vueuse/core'
import Tempus from 'tempus'
import { useDragControls } from '@/composables/useDragControls'
const inner = ref()
const handle = ref()
// Sizing
const { height: wHeight } = useWindowSize()
const { y: scrollY } = useWindowScroll()
const { height: documentHeight } = useElementSize(document.documentElement)
const { height: innerHeight } = useElementSize(inner)
const { height: handleHeight } = useElementSize(handle)
// Drag controls
// useDragControls({
// target: handle,
// onUpdate: ({ y }) => {
// console.log(y.value)
// },
// })
const updateScrollbar = () => {
const scrollHeight = documentHeight.value
const viewportHeight = wHeight.value
const limit = scrollHeight - viewportHeight
if (limit <= 0) return
const progress = scrollY.value / limit
if (handle.value) {
handle.value.style.transform = `translate3d(0,${
progress * (innerHeight.value - handleHeight.value)
}px,0)`
}
}
const showScrollBar = computed(() => {
return documentHeight.value > wHeight.value
})
// RAF
const removeRaf = ref()
const addRaf = () => (removeRaf.value = Tempus.add(updateScrollbar))
onBeforeUnmount(() => {
removeRaf.value?.()
})
watch(
showScrollBar,
(show) => {
if (show) {
addRaf()
} else {
removeRaf.value?.()
}
},
{ immediate: true },
)
</script>
<style lang="scss">
.scroll-bar {
position: fixed;
top: 0;
right: 0;
bottom: 0;
width: size-vw(8px);
will-change: transform;
border-left: 1px solid var(--grey-100);
.inner {
height: 100%;
position: relative;
.handle {
width: 100%;
height: size-vw(388px);
background: var(--grey-100);
border-radius: 20px;
position: absolute;
right: 0;
will-change: transform;
}
}
}
</style>

View File

@@ -1,15 +1,17 @@
<template>
<transition name="menu">
<div v-if="open" class="menu" ref="container">
<div v-if="menuOpen" class="menu" ref="container">
<Nav />
<div class="menu-wrap layout-block-inner">
<new-note @noteOpened="closeMenu" />
<button>+ New Capitulum</button>
<button>Change Theme</button>
<button>Instructio</button>
<button>Import</button>
<button>Export</button>
<new-note class="menu-item" @noteOpened="closeMenu" />
<button class="menu-item">+ New Capitulum</button>
<button class="menu-item">Change Theme</button>
<router-link class="menu-item" to="/instructions"
>Instructio</router-link
>
<button class="menu-item">Import</button>
<button class="menu-item">Export</button>
</div>
</div>
</transition>
@@ -18,30 +20,30 @@
<script setup>
import NewNote from '@/components/NewNote.vue'
import { onClickOutside } from '@vueuse/core'
import { computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ref, watch } from 'vue'
import Nav from '../Nav.vue'
import useMenu from '@/composables/useMenu'
import { useRoute } from 'vue-router'
const container = ref()
const route = useRoute()
const router = useRouter()
const closeMenu = () => {
router.push({
query: {
menuOpen: false,
},
})
}
const { menuOpen, closeMenu } = useMenu()
// Close on click outside
onClickOutside(container, () => {
if (!open.value) return
closeMenu()
})
const open = computed(() => route.query.menuOpen === 'true')
// Close on route change
const route = useRoute()
watch(
() => route.path,
() => {
closeMenu()
},
)
const openNewCategory = () => {}
</script>
<style lang="scss">
@@ -59,13 +61,17 @@ const open = computed(() => route.query.menuOpen === 'true')
padding-top: size-vw(3px);
padding-bottom: size-vw(10px);
button {
.menu-item {
display: block;
padding: size-vw(16px) 0;
text-align: center;
&:not(:last-child) {
border-bottom: 1px dashed currentColor;
}
&:hover {
color: var(--theme-accent);
}
}
}

View File

@@ -0,0 +1,77 @@
import { useEventListener } from '@vueuse/core'
import { onMounted, ref, unref } from 'vue'
export function useDragControls(options = {}) {
const { target = null, onUpdate = () => {} } = options
const x = ref(0)
const y = ref(0)
const isDragging = ref(false)
let bounds = null
const updateBounds = () => {
if (target) {
bounds = unref(target).getBoundingClientRect()
} else {
bounds = new DOMRect(0, 0, window.innerWidth, window.innerHeight)
}
}
const updatePosition = (clientX, clientY) => {
if (!bounds) return
const relativeX = (clientX - bounds.left) / bounds.width
const relativeY = (clientY - bounds.top) / bounds.height
// Clamp between 0 and 1
x.value = Math.min(1, Math.max(0, relativeX))
y.value = Math.min(1, Math.max(0, relativeY))
onUpdate?.({ x, y })
}
const start = (e) => {
isDragging.value = true
updateBounds()
move(e)
}
const move = (e) => {
if (!isDragging.value) return
if (e instanceof MouseEvent) {
updatePosition(e.clientX, e.clientY)
} else {
const touch = e.touches[0]
if (touch) updatePosition(touch.clientX, touch.clientY)
}
}
const end = () => {
isDragging.value = false
}
onMounted(() => {
const el = target ?? window
// Mouse
useEventListener(el, 'mousedown', start)
useEventListener(window, 'mousemove', move)
useEventListener(window, 'mouseup', end)
// Touch
useEventListener(el, 'touchstart', start, { passive: true })
useEventListener(window, 'touchmove', move, { passive: true })
useEventListener(window, 'touchend', end)
useEventListener(window, 'touchcancel', end)
// Resize
useEventListener(window, 'resize', updateBounds)
})
return {
x,
y,
isDragging,
}
}

View File

@@ -0,0 +1,29 @@
import { useRoute, useRouter } from 'vue-router'
import _without from 'lodash/without'
import { computed } from 'vue'
export default () => {
const route = useRoute()
const router = useRouter()
const menuOpen = computed(() => route.query.menuOpen === 'true')
const closeMenu = () => {
router.push({
query: _without(route.query, 'menuOpen'),
})
}
const openMenu = () => {
router.push({
query: {
menuOpen: true,
},
})
}
return {
menuOpen,
closeMenu,
openMenu,
}
}

View File

@@ -3,11 +3,13 @@ import { createRouter, createWebHistory } from 'vue-router'
import Directory from '@/views/Directory.vue'
import Editor from '@/views/Editor.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: '/category/:id', name: 'category', component: Category },
{ path: '/instructions', name: 'instructions', component: Instructions },
]
export const router = createRouter({

View File

@@ -1,14 +1,12 @@
html {
&:not(.dev) {
&,
* {
scrollbar-width: none !important;
-ms-overflow-style: none !important;
&,
* {
scrollbar-width: none !important;
-ms-overflow-style: none !important;
&::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
}
&::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
}
}
}

View File

@@ -57,6 +57,10 @@ pre {
min-height: 100vh;
}
button {
cursor: pointer;
}
// Text selection
::selection {
color: var(--theme-bg);

View File

@@ -7,6 +7,8 @@
<div class="notes">
<note-row v-for="note in notes" :note="note" :key="note.id" />
</div>
<new-note :category="id" />
</main>
</template>
@@ -15,7 +17,8 @@ import { computed, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import useNotes from '@/composables/useNotes'
import NoteRow from '@/components/NoteRow.vue'
import CategoryRow from '../components/CategoryRow.vue'
import CategoryRow from '@/components/CategoryRow.vue'
import NewNote from '@/components/NewNote.vue'
const route = useRoute()
const id = route.params.id
@@ -34,7 +37,7 @@ const categoryIndex = computed(() => {
</script>
<style lang="scss">
.category {
main.category {
.back {
display: block;
opacity: 0.25;
@@ -49,5 +52,9 @@ const categoryIndex = computed(() => {
gap: size-vw(14px);
margin-top: size-vw(9px);
}
.new-note {
display: block;
margin: size-vw(50px) auto 0;
}
}
</style>

View File

@@ -32,7 +32,7 @@ onMounted(async () => {
</script>
<style lang="scss">
.directory {
main.directory {
padding-top: size-vw(18px);
.label {

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="editor" class="editor layout-block">
<main v-if="editor" class="editor layout-block">
<bubble-menu :editor="editor">
<div class="bubble-menu">
<button
@@ -18,7 +18,7 @@
</bubble-menu>
<editor-content :editor="editor" class="editor-wrap" />
</div>
</main>
</template>
<script setup>
@@ -90,7 +90,7 @@ onMounted(async () => {
}
},
}),
Markdown,
Markdown.configure({}),
Image,
],
content: note.content,
@@ -104,7 +104,7 @@ onBeforeUnmount(() => {
</script>
<style lang="scss">
.editor {
main.editor {
padding-top: size-vw(8px);
padding-bottom: size-vw(20px);

View File

@@ -0,0 +1,74 @@
<template>
<main class="instructions layout-block-inner">
<router-link class="back-link" to="/"><- Go Back</router-link>
<p>
Medieval Translation Nota = Note Capitulum = Category Intructio =
Instructions Tabula = Index/Overview *This can be disabled via
toolbar -------------------------------------------- Program Key
Commands cmd + s = save cmd + t = new capitulum cmd + n = new nota
cmd + x = close window dbl click = change name / open nota paste
hyperlink twice = activated url
-------------------------------------------- Text Markdowns cmd + b
= Bold cmd + u = underline --- = ---------- (ruled line break)
/*text*/ = Desaturated text
</p>
<p>
Medieval Translation Nota = Note Capitulum = Category Intructio =
Instructions Tabula = Index/Overview *This can be disabled via
toolbar -------------------------------------------- Program Key
Commands cmd + s = save cmd + t = new capitulum cmd + n = new nota
cmd + x = close window dbl click = change name / open nota paste
hyperlink twice = activated url
-------------------------------------------- Text Markdowns cmd + b
= Bold cmd + u = underline --- = ---------- (ruled line break)
/*text*/ = Desaturated text
</p>
<p>
Medieval Translation Nota = Note Capitulum = Category Intructio =
Instructions Tabula = Index/Overview *This can be disabled via
toolbar -------------------------------------------- Program Key
Commands cmd + s = save cmd + t = new capitulum cmd + n = new nota
cmd + x = close window dbl click = change name / open nota paste
hyperlink twice = activated url
-------------------------------------------- Text Markdowns cmd + b
= Bold cmd + u = underline --- = ---------- (ruled line break)
/*text*/ = Desaturated text
</p>
<p>
Medieval Translation Nota = Note Capitulum = Category Intructio =
Instructions Tabula = Index/Overview *This can be disabled via
toolbar -------------------------------------------- Program Key
Commands cmd + s = save cmd + t = new capitulum cmd + n = new nota
cmd + x = close window dbl click = change name / open nota paste
hyperlink twice = activated url
-------------------------------------------- Text Markdowns cmd + b
= Bold cmd + u = underline --- = ---------- (ruled line break)
/*text*/ = Desaturated text
</p>
<p>
Medieval Translation Nota = Note Capitulum = Category Intructio =
Instructions Tabula = Index/Overview *This can be disabled via
toolbar -------------------------------------------- Program Key
Commands cmd + s = save cmd + t = new capitulum cmd + n = new nota
cmd + x = close window dbl click = change name / open nota paste
hyperlink twice = activated url
-------------------------------------------- Text Markdowns cmd + b
= Bold cmd + u = underline --- = ---------- (ruled line break)
/*text*/ = Desaturated text
</p>
</main>
</template>
<script setup></script>
<style lang="scss">
main.instructions {
.back-link {
opacity: 0.25;
display: block;
margin-top: size-vw(9px);
margin-bottom: size-vw(14px);
}
}
</style>