test supabase plugin
This commit is contained in:
@@ -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', () => {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
97
src/renderer/src/components/ScrollBar.vue
Normal file
97
src/renderer/src/components/ScrollBar.vue
Normal 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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
77
src/renderer/src/composables/useDragControls.js
Normal file
77
src/renderer/src/composables/useDragControls.js
Normal 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,
|
||||
}
|
||||
}
|
||||
29
src/renderer/src/composables/useMenu.js
Normal file
29
src/renderer/src/composables/useMenu.js
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ pre {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// Text selection
|
||||
::selection {
|
||||
color: var(--theme-bg);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -32,7 +32,7 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.directory {
|
||||
main.directory {
|
||||
padding-top: size-vw(18px);
|
||||
|
||||
.label {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
74
src/renderer/src/views/Instructions.vue
Normal file
74
src/renderer/src/views/Instructions.vue
Normal 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>
|
||||
Reference in New Issue
Block a user