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

2
.env
View File

@@ -1 +1,3 @@
VITE_DEV_SERVER_URL=http://localhost:5173 VITE_DEV_SERVER_URL=http://localhost:5173
SUPABASE_KEY='sb_secret_xYDyNcB7Q46ZYhDISeXRHQ_95l-N33m'
SUPABASE_URL='https://jmuadfpmmejfvkwosaqh.supabase.co'

1
.gitignore vendored
View File

@@ -7,6 +7,7 @@ yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
.env
node_modules node_modules
.DS_Store .DS_Store
dist dist

View File

@@ -1,4 +1,4 @@
# app.takerofnotes.com # Taker of Notes App
An Electron application with Vue An Electron application with Vue

View File

@@ -1,6 +1,8 @@
import "dotenv/config";
import { electronApp, optimizer, is } from "@electron-toolkit/utils"; import { electronApp, optimizer, is } from "@electron-toolkit/utils";
import { app, BrowserWindow, ipcMain, 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 fs from "fs/promises"; import fs from "fs/promises";
import path, { join } from "path"; import path, { join } from "path";
import { Index } from "flexsearch"; import { Index } from "flexsearch";
@@ -227,22 +229,18 @@ function createNoteWindow(noteId) {
} }
} }
app.whenReady().then(async () => { 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();
});
ipcMain.on("open-note-window", (_, noteId) => { ipcMain.on("open-note-window", (_, noteId) => {
createNoteWindow(noteId); createNoteWindow(noteId);
}); });
const registry = new PluginRegistry(); const registry = new PluginRegistry();
registry.register(filesystemPlugin); registry.register(filesystemPlugin);
const config = await new PluginConfig(filesystemPlugin).load(); registry.register(supabasePlugin);
const plugin = registry.get(config.activeAdapter); await new PluginConfig(filesystemPlugin).load();
const adapter = plugin.createAdapter(config.adapterConfig); const plugin = registry.get(supabasePlugin.id);
const adapter = plugin.createAdapter({
supabaseKey: process.env.SUPABASE_KEY,
supabaseUrl: process.env.SUPABASE_URL
});
const notesAPI = new NotesAPI(adapter); const notesAPI = new NotesAPI(adapter);
await notesAPI.init(); await notesAPI.init();
ipcMain.handle("notesAPI:call", (_, method, args) => { ipcMain.handle("notesAPI:call", (_, method, args) => {
@@ -251,6 +249,14 @@ app.whenReady().then(async () => {
} }
return notesAPI[method](...args); 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", () => { app.on("window-all-closed", () => {
if (process.platform !== "darwin") { if (process.platform !== "darwin") {

203
package-lock.json generated
View File

@@ -13,6 +13,7 @@
"@electron-toolkit/utils": "^4.0.0", "@electron-toolkit/utils": "^4.0.0",
"@fuzzco/font-loader": "^1.0.2", "@fuzzco/font-loader": "^1.0.2",
"@takerofnotes/plugin-filesystem": "^0.1.1", "@takerofnotes/plugin-filesystem": "^0.1.1",
"@takerofnotes/plugin-supabase": "^0.1.0",
"@tiptap/extension-document": "^3.19.0", "@tiptap/extension-document": "^3.19.0",
"@tiptap/extension-image": "^3.19.0", "@tiptap/extension-image": "^3.19.0",
"@tiptap/extension-table": "^3.19.0", "@tiptap/extension-table": "^3.19.0",
@@ -20,6 +21,7 @@
"@tiptap/starter-kit": "^3.19.0", "@tiptap/starter-kit": "^3.19.0",
"@tiptap/vue-3": "^3.19.0", "@tiptap/vue-3": "^3.19.0",
"@vueuse/core": "^14.2.1", "@vueuse/core": "^14.2.1",
"dotenv": "^17.3.1",
"electron-updater": "^6.3.9", "electron-updater": "^6.3.9",
"fecha": "^4.2.3", "fecha": "^4.2.3",
"flexsearch": "^0.8.212", "flexsearch": "^0.8.212",
@@ -2248,6 +2250,86 @@
"url": "https://github.com/sindresorhus/is?sponsor=1" "url": "https://github.com/sindresorhus/is?sponsor=1"
} }
}, },
"node_modules/@supabase/auth-js": {
"version": "2.98.0",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.98.0.tgz",
"integrity": "sha512-GBH361T0peHU91AQNzOlIrjUZw9TZbB9YDRiyFgk/3Kvr3/Z1NWUZ2athWTfHhwNNi8IrW00foyFxQD9IO/Trg==",
"license": "MIT",
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/functions-js": {
"version": "2.98.0",
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.98.0.tgz",
"integrity": "sha512-N/xEyiNU5Org+d+PNCpv+TWniAXRzxIURxDYsS/m2I/sfAB/HcM9aM2Dmf5edj5oWb9GxID1OBaZ8NMmPXL+Lg==",
"license": "MIT",
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/postgrest-js": {
"version": "2.98.0",
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.98.0.tgz",
"integrity": "sha512-v6e9WeZuJijzUut8HyXu6gMqWFepIbaeaMIm1uKzei4yLg9bC9OtEW9O14LE/9ezqNbSAnSLO5GtOLFdm7Bpkg==",
"license": "MIT",
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/realtime-js": {
"version": "2.98.0",
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.98.0.tgz",
"integrity": "sha512-rOWt28uGyFipWOSd+n0WVMr9kUXiWaa7J4hvyLCIHjRFqWm1z9CaaKAoYyfYMC1Exn3WT8WePCgiVhlAtWC2yw==",
"license": "MIT",
"dependencies": {
"@types/phoenix": "^1.6.6",
"@types/ws": "^8.18.1",
"tslib": "2.8.1",
"ws": "^8.18.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/storage-js": {
"version": "2.98.0",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.98.0.tgz",
"integrity": "sha512-tzr2mG+v7ILSAZSfZMSL9OPyIH4z1ikgQ8EcQTKfMRz4EwmlFt3UnJaGzSOxyvF5b+fc9So7qdSUWTqGgeLokQ==",
"license": "MIT",
"dependencies": {
"iceberg-js": "^0.8.1",
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/supabase-js": {
"version": "2.98.0",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.98.0.tgz",
"integrity": "sha512-Ohc97CtInLwZyiSASz7tT9/Abm/vqnIbO9REp+PivVUII8UZsuI3bngRQnYgJdFoOIwvaEII1fX1qy8x0CyNiw==",
"license": "MIT",
"dependencies": {
"@supabase/auth-js": "2.98.0",
"@supabase/functions-js": "2.98.0",
"@supabase/postgrest-js": "2.98.0",
"@supabase/realtime-js": "2.98.0",
"@supabase/storage-js": "2.98.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@szmarczak/http-timer": { "node_modules/@szmarczak/http-timer": {
"version": "4.0.6", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
@@ -2279,17 +2361,36 @@
"zod": "^4.3.6" "zod": "^4.3.6"
} }
}, },
"node_modules/@takerofnotes/plugin-supabase": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@takerofnotes/plugin-supabase/-/plugin-supabase-0.1.0.tgz",
"integrity": "sha512-NXmiyOAjVHXSFyzOPwljngrjwe1KZEzFiQjImI5XbjxtMJiAg6TFSbqLAnZaIZqg2WpAcBdZKy941WmkXJfNXA==",
"license": "MIT",
"dependencies": {
"@supabase/supabase-js": "^2.98.0",
"@takerofnotes/plugin-sdk": "^0.2.0"
}
},
"node_modules/@takerofnotes/plugin-supabase/node_modules/@takerofnotes/plugin-sdk": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@takerofnotes/plugin-sdk/-/plugin-sdk-0.2.0.tgz",
"integrity": "sha512-6Ll+EnO/9B51jf3tKXGkByj9lRTodYJogIuCJ1xh7uTxorJ5tcpBXv49dtXNYmGYEtpVt80G26/kQzm3J54DoA==",
"license": "MIT",
"dependencies": {
"zod": "^4.3.6"
}
},
"node_modules/@tiptap/core": { "node_modules/@tiptap/core": {
"version": "3.19.0", "version": "3.20.0",
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.19.0.tgz", "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.20.0.tgz",
"integrity": "sha512-bpqELwPW+DG8gWiD8iiFtSl4vIBooG5uVJod92Qxn3rA9nFatyXRr4kNbMJmOZ66ezUvmCjXVe/5/G4i5cyzKA==", "integrity": "sha512-aC9aROgia/SpJqhsXFiX9TsligL8d+oeoI8W3u00WI45s0VfsqjgeKQLDLF7Tu7hC+7F02teC84SAHuup003VQ==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/pm": "^3.19.0" "@tiptap/pm": "^3.20.0"
} }
}, },
"node_modules/@tiptap/extension-blockquote": { "node_modules/@tiptap/extension-blockquote": {
@@ -2515,17 +2616,17 @@
} }
}, },
"node_modules/@tiptap/extension-list": { "node_modules/@tiptap/extension-list": {
"version": "3.19.0", "version": "3.20.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.19.0.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.20.0.tgz",
"integrity": "sha512-N6nKbFB2VwMsPlCw67RlAtYSK48TAsAUgjnD+vd3ieSlIufdQnLXDFUP6hFKx9mwoUVUgZGz02RA6bkxOdYyTw==", "integrity": "sha512-+V0/gsVWAv+7vcY0MAe6D52LYTIicMSHw00wz3ISZgprSb2yQhJ4+4gurOnUrQ4Du3AnRQvxPROaofwxIQ66WQ==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^3.19.0", "@tiptap/core": "^3.20.0",
"@tiptap/pm": "^3.19.0" "@tiptap/pm": "^3.20.0"
} }
}, },
"node_modules/@tiptap/extension-list-item": { "node_modules/@tiptap/extension-list-item": {
@@ -2665,9 +2766,9 @@
} }
}, },
"node_modules/@tiptap/pm": { "node_modules/@tiptap/pm": {
"version": "3.19.0", "version": "3.20.0",
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.19.0.tgz", "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.0.tgz",
"integrity": "sha512-789zcnM4a8OWzvbD2DL31d0wbSm9BVeO/R7PLQwLIGysDI3qzrcclyZ8yhqOEVuvPitRRwYLq+mY14jz7kY4cw==", "integrity": "sha512-jn+2KnQZn+b+VXr8EFOJKsnjVNaA4diAEr6FOazupMt8W8ro1hfpYtZ25JL87Kao/WbMze55sd8M8BDXLUKu1A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"prosemirror-changeset": "^2.3.0", "prosemirror-changeset": "^2.3.0",
@@ -2842,6 +2943,12 @@
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
} }
}, },
"node_modules/@types/phoenix": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz",
"integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==",
"license": "MIT"
},
"node_modules/@types/plist": { "node_modules/@types/plist": {
"version": "3.0.5", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz",
@@ -2877,6 +2984,15 @@
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/yauzl": { "node_modules/@types/yauzl": {
"version": "2.10.3", "version": "2.10.3",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
@@ -3326,6 +3442,19 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/app-builder-lib/node_modules/dotenv": {
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/app-builder-lib/node_modules/fs-extra": { "node_modules/app-builder-lib/node_modules/fs-extra": {
"version": "10.1.0", "version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -4435,10 +4564,9 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "16.6.1", "version": "17.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz",
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==",
"dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@@ -4463,6 +4591,19 @@
"url": "https://dotenvx.com" "url": "https://dotenvx.com"
} }
}, },
"node_modules/dotenv-expand/node_modules/dotenv": {
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -5683,6 +5824,15 @@
"node": ">= 14" "node": ">= 14"
} }
}, },
"node_modules/iceberg-js": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
"integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
"license": "MIT",
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/iconv-corefoundation": { "node_modules/iconv-corefoundation": {
"version": "1.1.7", "version": "1.1.7",
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
@@ -9461,6 +9611,27 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xmlbuilder": { "node_modules/xmlbuilder": {
"version": "15.1.1", "version": "15.1.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",

View File

@@ -29,6 +29,7 @@
"@electron-toolkit/utils": "^4.0.0", "@electron-toolkit/utils": "^4.0.0",
"@fuzzco/font-loader": "^1.0.2", "@fuzzco/font-loader": "^1.0.2",
"@takerofnotes/plugin-filesystem": "^0.1.1", "@takerofnotes/plugin-filesystem": "^0.1.1",
"@takerofnotes/plugin-supabase": "^0.1.0",
"@tiptap/extension-document": "^3.19.0", "@tiptap/extension-document": "^3.19.0",
"@tiptap/extension-image": "^3.19.0", "@tiptap/extension-image": "^3.19.0",
"@tiptap/extension-table": "^3.19.0", "@tiptap/extension-table": "^3.19.0",
@@ -36,6 +37,7 @@
"@tiptap/starter-kit": "^3.19.0", "@tiptap/starter-kit": "^3.19.0",
"@tiptap/vue-3": "^3.19.0", "@tiptap/vue-3": "^3.19.0",
"@vueuse/core": "^14.2.1", "@vueuse/core": "^14.2.1",
"dotenv": "^17.3.1",
"electron-updater": "^6.3.9", "electron-updater": "^6.3.9",
"fecha": "^4.2.3", "fecha": "^4.2.3",
"flexsearch": "^0.8.212", "flexsearch": "^0.8.212",

View File

@@ -1,6 +1,8 @@
import 'dotenv/config'
import { electronApp, optimizer, is } from '@electron-toolkit/utils' 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 PluginRegistry from './core/PluginRegistry.js' import PluginRegistry from './core/PluginRegistry.js'
import PluginConfig from './core/PluginConfig.js' import PluginConfig from './core/PluginConfig.js'
import NotesAPI from './core/NotesAPI.js' import NotesAPI from './core/NotesAPI.js'
@@ -64,18 +66,6 @@ function createNoteWindow(noteId) {
} }
app.whenReady().then(async () => { 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 // Open note in new window
ipcMain.on('open-note-window', (_, noteId) => { ipcMain.on('open-note-window', (_, noteId) => {
createNoteWindow(noteId) createNoteWindow(noteId)
@@ -86,13 +76,19 @@ app.whenReady().then(async () => {
// Register built-in plugins // Register built-in plugins
registry.register(filesystemPlugin) registry.register(filesystemPlugin)
registry.register(supabasePlugin)
// Pull plugin config // Pull plugin config
const config = await new PluginConfig(filesystemPlugin).load() const config = await new PluginConfig(filesystemPlugin).load()
// Create instance of active adapter // Create instance of active adapter
const plugin = registry.get(config.activeAdapter) // const plugin = registry.get(config.activeAdapter)
const adapter = plugin.createAdapter(config.adapterConfig) 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 // Init Notes API
const notesAPI = new NotesAPI(adapter) const notesAPI = new NotesAPI(adapter)
@@ -105,6 +101,18 @@ app.whenReady().then(async () => {
} }
return notesAPI[method](...args) 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', () => { app.on('window-all-closed', () => {

View File

@@ -1,22 +1,22 @@
<template> <template>
<lenis root :options="{ duration: 1 }"> <div :class="classes" :style="styles">
<div :class="classes" :style="styles"> <Nav />
<Nav />
<router-view :key="$route.fullPath" /> <router-view :key="$route.fullPath" />
<Menu /> <Menu />
</div>
</lenis> <ScrollBar />
</div>
</template> </template>
<script setup> <script setup>
import Lenis from './components/Lenis.vue'
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import loadFonts from '@fuzzco/font-loader' import loadFonts from '@fuzzco/font-loader'
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
import Menu from '@/components/menu/Index.vue' import Menu from '@/components/menu/Index.vue'
import Nav from '@/components/Nav.vue' import Nav from '@/components/Nav.vue'
import ScrollBar from './components/ScrollBar.vue'
const { height } = useWindowSize() const { height } = useWindowSize()

View File

@@ -5,25 +5,31 @@
</template> </template>
<script setup> <script setup>
import { useRoute, useRouter } from 'vue-router' import useMenu from '@/composables/useMenu'
import { onMounted } from 'vue'
const route = useRoute() const { menuOpen, closeMenu, openMenu } = useMenu()
const router = useRouter()
const toggleMenu = () => { const toggleMenu = () => {
const state = route.query.menuOpen if (menuOpen.value) {
closeMenu()
if (state === 'true') {
router.push({ query: { menuOpen: false } })
} else { } 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> </script>
<style lang="scss"> <style lang="scss">
.nav { .nav {
margin-top: size-vw(9px); padding-top: size-vw(9px);
color: var(--grey-100); color: var(--grey-100);
} }
</style> </style>

View File

@@ -6,13 +6,19 @@
import useOpenNote from '@/composables/useOpenNote' import useOpenNote from '@/composables/useOpenNote'
import useNotes from '@/composables/useNotes' import useNotes from '@/composables/useNotes'
const props = defineProps({ category: String })
const emit = defineEmits(['noteOpened']) const emit = defineEmits(['noteOpened'])
const { createNote } = useNotes() const { createNote } = useNotes()
const { openNote } = useOpenNote() const { openNote } = useOpenNote()
const onClick = async () => { const onClick = async () => {
const note = await createNote({}, '') const note = await createNote(
{
category: props.category ?? null,
},
'',
)
openNote(note.id) openNote(note.id)
emit('noteOpened') 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> <template>
<transition name="menu"> <transition name="menu">
<div v-if="open" class="menu" ref="container"> <div v-if="menuOpen" class="menu" ref="container">
<Nav /> <Nav />
<div class="menu-wrap layout-block-inner"> <div class="menu-wrap layout-block-inner">
<new-note @noteOpened="closeMenu" /> <new-note class="menu-item" @noteOpened="closeMenu" />
<button>+ New Capitulum</button> <button class="menu-item">+ New Capitulum</button>
<button>Change Theme</button> <button class="menu-item">Change Theme</button>
<button>Instructio</button> <router-link class="menu-item" to="/instructions"
<button>Import</button> >Instructio</router-link
<button>Export</button> >
<button class="menu-item">Import</button>
<button class="menu-item">Export</button>
</div> </div>
</div> </div>
</transition> </transition>
@@ -18,30 +20,30 @@
<script setup> <script setup>
import NewNote from '@/components/NewNote.vue' import NewNote from '@/components/NewNote.vue'
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from '@vueuse/core'
import { computed, ref } from 'vue' import { ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import Nav from '../Nav.vue' import Nav from '../Nav.vue'
import useMenu from '@/composables/useMenu'
import { useRoute } from 'vue-router'
const container = ref() const container = ref()
const route = useRoute() const { menuOpen, closeMenu } = useMenu()
const router = useRouter()
const closeMenu = () => {
router.push({
query: {
menuOpen: false,
},
})
}
// Close on click outside
onClickOutside(container, () => { onClickOutside(container, () => {
if (!open.value) return
closeMenu() closeMenu()
}) })
const open = computed(() => route.query.menuOpen === 'true') // Close on route change
const route = useRoute()
watch(
() => route.path,
() => {
closeMenu()
},
)
const openNewCategory = () => {}
</script> </script>
<style lang="scss"> <style lang="scss">
@@ -59,13 +61,17 @@ const open = computed(() => route.query.menuOpen === 'true')
padding-top: size-vw(3px); padding-top: size-vw(3px);
padding-bottom: size-vw(10px); padding-bottom: size-vw(10px);
button { .menu-item {
display: block;
padding: size-vw(16px) 0; padding: size-vw(16px) 0;
text-align: center; text-align: center;
&:not(:last-child) { &:not(:last-child) {
border-bottom: 1px dashed currentColor; 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 Directory from '@/views/Directory.vue'
import Editor from '@/views/Editor.vue' import Editor from '@/views/Editor.vue'
import Category from '@/views/Category.vue' import Category from '@/views/Category.vue'
import Instructions from '@/views/Instructions.vue'
const routes = [ const routes = [
{ path: '/', name: 'directory', component: Directory }, { path: '/', name: 'directory', component: Directory },
{ path: '/note/:id', name: 'note', component: Editor }, { path: '/note/:id', name: 'note', component: Editor },
{ path: '/category/:id', name: 'category', component: Category }, { path: '/category/:id', name: 'category', component: Category },
{ path: '/instructions', name: 'instructions', component: Instructions },
] ]
export const router = createRouter({ export const router = createRouter({

View File

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

View File

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

View File

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

View File

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

View File

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