Preferences inputs WIP

This commit is contained in:
nicwands
2026-03-26 15:32:13 -04:00
parent 75309cab9c
commit 3c0184cf63
7 changed files with 1885 additions and 146 deletions

View File

@@ -3,6 +3,8 @@ import { electronApp, optimizer, is } from "@electron-toolkit/utils";
import { app, ipcMain, BrowserWindow, shell } from "electron"; import { app, ipcMain, BrowserWindow, shell } from "electron";
import filesystemPlugin from "@takerofnotes/plugin-filesystem"; import filesystemPlugin from "@takerofnotes/plugin-filesystem";
import supabasePlugin from "@takerofnotes/plugin-supabase"; import supabasePlugin from "@takerofnotes/plugin-supabase";
import s3Plugin from "@takerofnotes/plugin-s3";
import postgresPlugin from "@takerofnotes/plugin-postgre-sql";
import sodium from "libsodium-wrappers"; import sodium from "libsodium-wrappers";
import { v4 } from "uuid"; import { v4 } from "uuid";
import { Index } from "flexsearch"; import { Index } from "flexsearch";
@@ -323,10 +325,8 @@ const initPluginManager = (runtime, plugins, config) => {
registry.register(plugin); registry.register(plugin);
} }
const manager = createPluginManager(registry); const manager = createPluginManager(registry);
manager.setActivePlugin( const activeConfig = config.adapters?.[config.activeAdapter] || {};
config.activeAdapter, manager.setActivePlugin(config.activeAdapter, activeConfig);
config.adapters[config.activeAdapter]
);
return manager; return manager;
}; };
const initConfigManager = async (runtime) => { const initConfigManager = async (runtime) => {
@@ -439,7 +439,12 @@ app.whenReady().then(async () => {
const { pluginManager, configManager } = await initializeCore( const { pluginManager, configManager } = await initializeCore(
"electron-main", "electron-main",
{ {
plugins: [filesystemPlugin, supabasePlugin] plugins: [
filesystemPlugin,
s3Plugin,
postgresPlugin,
supabasePlugin
]
} }
); );
ipcMain.handle("pluginManager:call", async (_, method, ...args) => { ipcMain.handle("pluginManager:call", async (_, method, ...args) => {

1820
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,8 @@
"@fuzzco/font-loader": "^1.0.2", "@fuzzco/font-loader": "^1.0.2",
"@takerofnotes/plugin-browser": "^0.2.0", "@takerofnotes/plugin-browser": "^0.2.0",
"@takerofnotes/plugin-filesystem": "^0.3.0", "@takerofnotes/plugin-filesystem": "^0.3.0",
"@takerofnotes/plugin-postgre-sql": "^0.1.0",
"@takerofnotes/plugin-s3": "^0.2.0",
"@takerofnotes/plugin-supabase": "^0.2.0", "@takerofnotes/plugin-supabase": "^0.2.0",
"@tiptap/extension-code-block-lowlight": "^3.20.0", "@tiptap/extension-code-block-lowlight": "^3.20.0",
"@tiptap/extension-document": "^3.19.0", "@tiptap/extension-document": "^3.19.0",

View File

@@ -56,9 +56,7 @@ export const initializeCore = async (runtime, { plugins }) => {
const config = await configManager.loadConfig() const config = await configManager.loadConfig()
const pluginManager = initPluginManager(runtime, plugins, config) const pluginManager = initPluginManager(runtime, plugins, config)
// -------------------------
// NotesAPI bootstrap // NotesAPI bootstrap
// -------------------------
let notesAPI = null let notesAPI = null
let initPromise = null let initPromise = null

View File

@@ -3,6 +3,8 @@ import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import { app, shell, BrowserWindow, ipcMain } from 'electron' import { app, shell, BrowserWindow, ipcMain } from 'electron'
import filesystemPlugin from '@takerofnotes/plugin-filesystem' import filesystemPlugin from '@takerofnotes/plugin-filesystem'
import supabasePlugin from '@takerofnotes/plugin-supabase' import supabasePlugin from '@takerofnotes/plugin-supabase'
import s3Plugin from '@takerofnotes/plugin-s3'
import postgresPlugin from '@takerofnotes/plugin-postgre-sql'
import { initializeCore } from '../core/index.js' import { initializeCore } from '../core/index.js'
import { join } from 'path' import { join } from 'path'
@@ -78,7 +80,12 @@ app.whenReady().then(async () => {
const { pluginManager, configManager } = await initializeCore( const { pluginManager, configManager } = await initializeCore(
'electron-main', 'electron-main',
{ {
plugins: [filesystemPlugin, supabasePlugin], plugins: [
filesystemPlugin,
s3Plugin,
postgresPlugin,
supabasePlugin,
],
}, },
) )

View File

@@ -1,7 +1,7 @@
import { useEnvironment } from '@/composables/useEnvironment' import { useEnvironment } from '@/composables/useEnvironment'
import { initializeCore } from '@core/index.js' import { initializeCore } from '@core/index.js'
import supabasePlugin from '@takerofnotes/plugin-supabase'
import browserPlugin from '@takerofnotes/plugin-browser' import browserPlugin from '@takerofnotes/plugin-browser'
import supabasePlugin from '@takerofnotes/plugin-supabase'
export const initCore = async (app) => { export const initCore = async (app) => {
const environment = useEnvironment() const environment = useEnvironment()
@@ -10,7 +10,7 @@ export const initCore = async (app) => {
const runtime = environment === 'electron' ? 'electron-renderer' : 'web' const runtime = environment === 'electron' ? 'electron-renderer' : 'web'
// Plugins that are valid for web (electron uses IPC) // Plugins that are valid for web (electron uses IPC)
const plugins = [supabasePlugin, browserPlugin] const plugins = [browserPlugin, supabasePlugin]
const core = await initializeCore(runtime, { const core = await initializeCore(runtime, {
plugins, plugins,

View File

@@ -1,43 +1,18 @@
<template> <template>
<main class="preferences layout-block"> <main class="preferences layout-block">
<h1 class="mono">Storage Plugin</h1> <h1 class="title">Preferences</h1>
<div v-for="plugin in plugins" class="plugin" :key="plugin.id"> <div class="sections">
<input <preferences-encryption ref="encryption" />
v-model="selectedPluginId"
name="plugins"
type="radio"
:id="plugin.id"
:value="plugin.id"
/>
<div class="info">
<p class="name bold">{{ plugin.name }}</p>
<p class="description">{{ plugin.description }}</p>
<div v-if="plugin.configSchema.length" class="config"> <suspense @resolve="ready = true">
<div <preferences-storage ref="storage" />
v-for="field in plugin.configSchema" </suspense>
class="config-field"
:key="field.key"
>
<label :for="field.key">
{{ field.label }} {{ field.required ? '*' : '' }}
</label>
<input
v-model="config.adapters[plugin.id][field.key]"
:id="field.key"
:type="field.type"
:placeholder="field.default"
:required="field.required"
/>
</div>
</div>
</div>
</div> </div>
<p v-if="validationError" class="error">{{ validationError }}</p> <p v-if="validationError" class="error">{{ validationError }}</p>
<button @click="save" class="save-btn"> <button @click="save" class="save-btn" :disabled="!ready">
<svg-spinner v-if="saving" /> <svg-spinner v-if="saving" />
<span v-else-if="saved">Saved</span> <span v-else-if="saved">Saved</span>
<span v-else>Save</span> <span v-else>Save</span>
@@ -46,74 +21,39 @@
</template> </template>
<script setup> <script setup>
import PreferencesEncryption from '@/components/preferences/Encryption.vue'
import PreferencesStorage from '@/components/preferences/Storage.vue'
import SvgSpinner from '@/components/svg/Spinner.vue' import SvgSpinner from '@/components/svg/Spinner.vue'
import usePlugins from '@/composables/usePlugins' import { ref } from 'vue'
import useConfig from '@/composables/useConfig'
import { ref, computed } from 'vue'
const { plugins, setActivePlugin, testPlugin } = await usePlugins()
const { config, ensureConfig } = useConfig()
await ensureConfig()
const normalizeConfig = () => {
if (!config.value.adapters) {
config.value.adapters = {}
}
for (const plugin of plugins.value) {
if (!config.value.adapters[plugin.id]) {
config.value.adapters[plugin.id] = {}
}
for (const field of plugin.configSchema) {
if (config.value.adapters[plugin.id][field.key] === undefined) {
config.value.adapters[plugin.id][field.key] =
field.default ?? ''
}
}
}
}
normalizeConfig()
const selectedPluginId = ref(config.value.activeAdapter)
const validationError = ref('')
const selectedPlugin = computed(() => {
return plugins.value.find((p) => p.id === selectedPluginId.value)
})
const ready = ref(false)
const saving = ref(false) const saving = ref(false)
const saved = ref(false) const saved = ref(false)
const validationError = ref('')
// Sections
const storage = ref()
const encryption = ref()
const save = async () => { const save = async () => {
saving.value = true saving.value = true
saved.value = false
validationError.value = '' validationError.value = ''
const plugin = selectedPlugin.value try {
const adapterConfig = config.value.adapters[plugin.id] || {} await storage.value.validate()
await encryption.value.validate()
if (plugin && plugin.configSchema.length) { saved.value = true
// Check required fields setTimeout(() => {
for (const field of plugin.configSchema) { saved.value = false
if (field.required && !adapterConfig[field.key]) { }, 2000)
validationError.value = `Please fill in all required fields for ${plugin.name}` } catch (error) {
return console.error(error.message)
} validationError.value = error.message
} } finally {
saving.value = false
// Test connection
// const testResult = await testPlugin(plugin.id, adapterConfig)
// console.log(testResult)
// if (!testResult) {
// validationError.value = `Failed to connect to ${plugin.name}`
// }
} }
await setActivePlugin(selectedPluginId.value, adapterConfig)
saving.value = false
saved.value = true
setTimeout(() => {
saved.value = false
}, 2000)
} }
</script> </script>
@@ -122,55 +62,18 @@ const save = async () => {
padding-top: var(--nav-height); padding-top: var(--nav-height);
padding-bottom: 60px; padding-bottom: 60px;
h1 { .title {
margin-bottom: 25px;
}
.sections {
display: flex;
flex-direction: column;
gap: 30px;
}
.section-title {
margin-bottom: 20px; margin-bottom: 20px;
} }
.plugin {
display: flex;
margin-bottom: 16px;
}
input[type='radio'] {
display: block;
flex-shrink: 0;
width: 10px;
height: 10px;
margin-right: 10px;
border: 1px solid var(--theme-fg);
cursor: pointer;
&:checked {
background-color: var(--theme-fg);
}
}
.info {
.description {
color: var(--grey-100);
margin-top: 6px;
}
}
.config {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 16px;
}
.config-field {
display: flex;
flex-direction: column;
gap: 4px;
input {
width: 100%;
border: 1px solid var(--grey-100);
border-radius: 0.2em;
padding: 0.2em 0.5em;
}
}
.error { .error {
color: red; color: red;
margin-top: 16px; margin-top: 16px;
@@ -193,6 +96,10 @@ const save = async () => {
&:hover { &:hover {
color: var(--theme-accent); color: var(--theme-accent);
} }
&:disabled {
opacity: 0.5;
cursor: default;
}
} }
} }
</style> </style>