Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73349444d6 | ||
|
|
e9e0abe380 | ||
|
|
e843b7662d | ||
|
|
cc3ba79df0 | ||
|
|
2609d73bbd | ||
|
|
1c753a6f8f |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -35,6 +35,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Run platform build
|
- name: Run platform build
|
||||||
run: npm run ${{ matrix.script }}
|
run: npm run ${{ matrix.script }}
|
||||||
|
env:
|
||||||
|
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||||
|
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": ["dbaeumer.vscode-eslint"]
|
"recommendations": ["dbaeumer.vscode-eslint"]
|
||||||
}
|
}
|
||||||
|
|||||||
3
bin/generateEncryptionKey.js
Normal file
3
bin/generateEncryptionKey.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import crypto from 'crypto'
|
||||||
|
|
||||||
|
console.log(crypto.randomBytes(32).toString('hex'))
|
||||||
@@ -24,9 +24,7 @@ mac:
|
|||||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
||||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||||
notarize: false
|
notarize: true
|
||||||
cscLink: ${{ secrets.CSC_LINK }}
|
|
||||||
cscKeyPassword: ${{ secrets.CSC_KEY_PASSWORD }}
|
|
||||||
dmg:
|
dmg:
|
||||||
artifactName: ${name}-${version}.${ext}
|
artifactName: ${name}-${version}.${ext}
|
||||||
linux:
|
linux:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import fs from "fs/promises";
|
|||||||
import path, { join } from "path";
|
import path, { join } from "path";
|
||||||
import { Index } from "flexsearch";
|
import { Index } from "flexsearch";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
import sodium from "libsodium-wrappers";
|
||||||
import __cjs_mod__ from "node:module";
|
import __cjs_mod__ from "node:module";
|
||||||
const __filename = import.meta.filename;
|
const __filename = import.meta.filename;
|
||||||
const __dirname = import.meta.dirname;
|
const __dirname = import.meta.dirname;
|
||||||
@@ -88,23 +89,100 @@ class PluginConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
class NotesAPI {
|
class NotesAPI {
|
||||||
constructor(adapter) {
|
constructor(adapter, encryptionKey = null) {
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
throw new Error("NotesAPI requires a storage adapter");
|
throw new Error("NotesAPI requires a storage adapter");
|
||||||
}
|
}
|
||||||
this.adapter = adapter;
|
this.adapter = adapter;
|
||||||
this.notesCache = /* @__PURE__ */ new Map();
|
this.notesCache = /* @__PURE__ */ new Map();
|
||||||
|
this.encryptionKey = encryptionKey || process.env.NOTES_ENCRYPTION_KEY;
|
||||||
|
this._sodiumReady = false;
|
||||||
this.index = new Index({
|
this.index = new Index({
|
||||||
tokenize: "tolerant",
|
tokenize: "tolerant",
|
||||||
resolution: 9
|
resolution: 9
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
async _initSodium() {
|
||||||
|
if (!this._sodiumReady) {
|
||||||
|
await sodium.ready;
|
||||||
|
this._sodiumReady = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_encrypt(note) {
|
||||||
|
if (!this.encryptionKey) {
|
||||||
|
throw new Error("Encryption key not set");
|
||||||
|
}
|
||||||
|
const key = Buffer.from(this.encryptionKey, "hex");
|
||||||
|
if (key.length !== 32) {
|
||||||
|
throw new Error(
|
||||||
|
"Encryption key must be 64 hex characters (32 bytes)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
|
||||||
|
const message = JSON.stringify(note);
|
||||||
|
const ciphertext = sodium.crypto_secretbox_easy(
|
||||||
|
Buffer.from(message),
|
||||||
|
nonce,
|
||||||
|
key
|
||||||
|
);
|
||||||
|
const combined = Buffer.concat([nonce, ciphertext]);
|
||||||
|
return combined.toString("base64");
|
||||||
|
}
|
||||||
|
_decrypt(encryptedData) {
|
||||||
|
if (!this.encryptionKey) {
|
||||||
|
throw new Error("Encryption key not set");
|
||||||
|
}
|
||||||
|
const key = Buffer.from(this.encryptionKey, "hex");
|
||||||
|
if (key.length !== 32) {
|
||||||
|
throw new Error(
|
||||||
|
"Encryption key must be 64 hex characters (32 bytes)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let combined;
|
||||||
|
try {
|
||||||
|
combined = Buffer.from(encryptedData, "base64");
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("Invalid encrypted data: not valid base64");
|
||||||
|
}
|
||||||
|
if (combined.length < sodium.crypto_secretbox_NONCEBYTES + sodium.crypto_secretbox_MACBYTES) {
|
||||||
|
throw new Error("Invalid encrypted data: too short");
|
||||||
|
}
|
||||||
|
const nonce = combined.slice(0, sodium.crypto_secretbox_NONCEBYTES);
|
||||||
|
const ciphertext = combined.slice(sodium.crypto_secretbox_NONCEBYTES);
|
||||||
|
let decrypted;
|
||||||
|
try {
|
||||||
|
decrypted = sodium.crypto_secretbox_open_easy(
|
||||||
|
ciphertext,
|
||||||
|
nonce,
|
||||||
|
key
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("Decryption failed: wrong key or corrupted data");
|
||||||
|
}
|
||||||
|
if (!decrypted) {
|
||||||
|
throw new Error("Decryption failed: no data returned");
|
||||||
|
}
|
||||||
|
const decryptedStr = Buffer.from(decrypted).toString("utf8");
|
||||||
|
try {
|
||||||
|
return JSON.parse(decryptedStr);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`Decryption succeeded but invalid JSON: ${decryptedStr}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
async init() {
|
async init() {
|
||||||
|
await this._initSodium();
|
||||||
await this.adapter.init();
|
await this.adapter.init();
|
||||||
const notes = await this.adapter.getAll();
|
const encryptedNotes = await this.adapter.getAll();
|
||||||
for (const note of notes) {
|
for (const encryptedNote of encryptedNotes) {
|
||||||
this.notesCache.set(note.id, note);
|
try {
|
||||||
this.index.add(note.id, note.title + "\n" + note.content);
|
const note = this._decrypt(encryptedNote.data || encryptedNote);
|
||||||
|
this.notesCache.set(note.id, note);
|
||||||
|
this.index.add(note.id, note.title + "\n" + note.content);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to decrypt note:", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* -----------------------
|
/* -----------------------
|
||||||
@@ -136,9 +214,13 @@ class NotesAPI {
|
|||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
content
|
content
|
||||||
};
|
};
|
||||||
|
const encryptedNote = {
|
||||||
|
id: note.id,
|
||||||
|
data: this._encrypt(note)
|
||||||
|
};
|
||||||
this.notesCache.set(id, note);
|
this.notesCache.set(id, note);
|
||||||
this.index.add(id, note.title + "\n" + content);
|
this.index.add(id, note.title + "\n" + content);
|
||||||
await this.adapter.create(note);
|
await this.adapter.create(encryptedNote);
|
||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
async deleteNote(id) {
|
async deleteNote(id) {
|
||||||
@@ -151,8 +233,12 @@ class NotesAPI {
|
|||||||
if (!note) throw new Error("Note not found");
|
if (!note) throw new Error("Note not found");
|
||||||
note.content = content;
|
note.content = content;
|
||||||
note.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
note.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
||||||
|
const encryptedNote = {
|
||||||
|
id: note.id,
|
||||||
|
data: this._encrypt(note)
|
||||||
|
};
|
||||||
this.index.update(id, note.title + "\n" + content);
|
this.index.update(id, note.title + "\n" + content);
|
||||||
await this.adapter.update(note);
|
await this.adapter.update(encryptedNote);
|
||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
async updateNoteMetadata(id, updates = {}) {
|
async updateNoteMetadata(id, updates = {}) {
|
||||||
@@ -171,8 +257,12 @@ class NotesAPI {
|
|||||||
note.category = updates.category;
|
note.category = updates.category;
|
||||||
}
|
}
|
||||||
note.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
note.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
||||||
|
const encryptedNote = {
|
||||||
|
id: note.id,
|
||||||
|
data: this._encrypt(note)
|
||||||
|
};
|
||||||
this.index.update(id, note.title + "\n" + note.content);
|
this.index.update(id, note.title + "\n" + note.content);
|
||||||
await this.adapter.update(note);
|
await this.adapter.update(encryptedNote);
|
||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
search(query) {
|
search(query) {
|
||||||
@@ -235,19 +325,34 @@ app.whenReady().then(async () => {
|
|||||||
const registry = new PluginRegistry();
|
const registry = new PluginRegistry();
|
||||||
registry.register(filesystemPlugin);
|
registry.register(filesystemPlugin);
|
||||||
registry.register(supabasePlugin);
|
registry.register(supabasePlugin);
|
||||||
await new PluginConfig(filesystemPlugin).load();
|
const config = await new PluginConfig(filesystemPlugin).load();
|
||||||
const plugin = registry.get(supabasePlugin.id);
|
const plugin = registry.get(config.activeAdapter);
|
||||||
const adapter = plugin.createAdapter({
|
const adapter = plugin.createAdapter(config.adapterConfig);
|
||||||
supabaseKey: process.env.SUPABASE_KEY,
|
const notesAPI = new NotesAPI(
|
||||||
supabaseUrl: process.env.SUPABASE_URL
|
adapter,
|
||||||
});
|
"729a0d21d783654c68f1a0123e2a0e986350de536b5324f1f35876ea12ffeaf5"
|
||||||
const notesAPI = new NotesAPI(adapter);
|
);
|
||||||
await notesAPI.init();
|
await notesAPI.init();
|
||||||
ipcMain.handle("notesAPI:call", (_, method, args) => {
|
const broadcastNoteChange = (event, data) => {
|
||||||
|
BrowserWindow.getAllWindows().forEach((win) => {
|
||||||
|
win.webContents.send(event, data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
ipcMain.handle("notesAPI:call", async (_, method, args) => {
|
||||||
if (!notesAPI[method]) {
|
if (!notesAPI[method]) {
|
||||||
throw new Error("Invalid method");
|
throw new Error("Invalid method");
|
||||||
}
|
}
|
||||||
return notesAPI[method](...args);
|
const result = await notesAPI[method](...args);
|
||||||
|
if (method === "createNote") {
|
||||||
|
broadcastNoteChange("note-created", result);
|
||||||
|
} else if (method === "updateNote") {
|
||||||
|
broadcastNoteChange("note-updated", result);
|
||||||
|
} else if (method === "updateNoteMetadata") {
|
||||||
|
broadcastNoteChange("note-updated", result);
|
||||||
|
} else if (method === "deleteNote") {
|
||||||
|
broadcastNoteChange("note-deleted", { id: args[0] });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
electronApp.setAppUserModelId("com.electron");
|
electronApp.setAppUserModelId("com.electron");
|
||||||
app.on("browser-window-created", (_, window) => {
|
app.on("browser-window-created", (_, window) => {
|
||||||
|
|||||||
@@ -2,6 +2,15 @@ import { contextBridge, ipcRenderer } from "electron";
|
|||||||
const api = {
|
const api = {
|
||||||
openNoteWindow: (noteId) => {
|
openNoteWindow: (noteId) => {
|
||||||
ipcRenderer.send("open-note-window", noteId);
|
ipcRenderer.send("open-note-window", noteId);
|
||||||
|
},
|
||||||
|
onNoteCreated: (callback) => {
|
||||||
|
ipcRenderer.on("note-created", (_, data) => callback(data));
|
||||||
|
},
|
||||||
|
onNoteUpdated: (callback) => {
|
||||||
|
ipcRenderer.on("note-updated", (_, data) => callback(data));
|
||||||
|
},
|
||||||
|
onNoteDeleted: (callback) => {
|
||||||
|
ipcRenderer.on("note-deleted", (_, data) => callback(data));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const notesAPI = {
|
const notesAPI = {
|
||||||
|
|||||||
@@ -180,11 +180,7 @@ meter {
|
|||||||
all: revert;
|
all: revert;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lily-cursor {
|
.menu {
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-header {
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,13 +300,13 @@ html.has-scroll-smooth .hide-on-smooth-scroll {
|
|||||||
grid-gap: var(--layout-column-gap);
|
grid-gap: var(--layout-column-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
html:not(.dev),
|
html,
|
||||||
html:not(.dev) * {
|
html * {
|
||||||
scrollbar-width: none !important;
|
scrollbar-width: none !important;
|
||||||
-ms-overflow-style: none !important;
|
-ms-overflow-style: none !important;
|
||||||
}
|
}
|
||||||
html:not(.dev)::-webkit-scrollbar,
|
html::-webkit-scrollbar,
|
||||||
html:not(.dev) *::-webkit-scrollbar {
|
html *::-webkit-scrollbar {
|
||||||
width: 0 !important;
|
width: 0 !important;
|
||||||
height: 0 !important;
|
height: 0 !important;
|
||||||
}
|
}
|
||||||
@@ -445,6 +441,10 @@ pre {
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
color: var(--theme-bg);
|
color: var(--theme-bg);
|
||||||
background: var(--theme-accent);
|
background: var(--theme-accent);
|
||||||
@@ -493,6 +493,223 @@ pre {
|
|||||||
transform: translateY(100%);
|
transform: translateY(100%);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
}/* Breakpoints */
|
||||||
|
@keyframes flip-r {
|
||||||
|
50% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-l {
|
||||||
|
50% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-d {
|
||||||
|
50% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-u {
|
||||||
|
50% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.nav {
|
||||||
|
padding-top: 2.5423728814vw;
|
||||||
|
color: var(--grey-100);
|
||||||
|
}/* Breakpoints */
|
||||||
|
@keyframes flip-r {
|
||||||
|
50% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-l {
|
||||||
|
50% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-d {
|
||||||
|
50% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-u {
|
||||||
|
50% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.menu {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--theme-bg);
|
||||||
|
border-bottom: 1px solid var(--grey-100);
|
||||||
|
}
|
||||||
|
.menu .menu-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-top: 0.8474576271vw;
|
||||||
|
padding-bottom: 2.8248587571vw;
|
||||||
|
}
|
||||||
|
.menu .menu-wrap .menu-item {
|
||||||
|
display: block;
|
||||||
|
padding: 4.5197740113vw 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.menu .menu-wrap .menu-item:not(:last-child) {
|
||||||
|
border-bottom: 1px dashed currentColor;
|
||||||
|
}
|
||||||
|
.menu .menu-wrap .menu-item:hover {
|
||||||
|
color: var(--theme-accent);
|
||||||
|
}
|
||||||
|
.menu.menu-enter-active, .menu.menu-leave-active {
|
||||||
|
transition: transform 300ms var(--ease-out-expo);
|
||||||
|
}
|
||||||
|
.menu.menu-enter-from, .menu.menu-leave-to {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}/* Breakpoints */
|
||||||
|
@keyframes flip-r {
|
||||||
|
50% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-l {
|
||||||
|
50% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-d {
|
||||||
|
50% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-u {
|
||||||
|
50% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.scroll-bar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 2.2598870056vw;
|
||||||
|
will-change: transform;
|
||||||
|
border-left: 1px solid var(--grey-100);
|
||||||
|
}
|
||||||
|
.scroll-bar .inner {
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.scroll-bar .inner .handle {
|
||||||
|
width: 100%;
|
||||||
|
height: 109.604519774vw;
|
||||||
|
background: var(--grey-100);
|
||||||
|
border-radius: 20px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
will-change: transform;
|
||||||
|
}/* Breakpoints */
|
||||||
|
@keyframes flip-r {
|
||||||
|
50% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-l {
|
||||||
|
50% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-d {
|
||||||
|
50% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-u {
|
||||||
|
50% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
min-height: calc(100 * var(--vh));
|
min-height: calc(100 * var(--vh));
|
||||||
@@ -547,7 +764,8 @@ pre {
|
|||||||
}
|
}
|
||||||
.category-row {
|
.category-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 7.3446327684vw 1fr;
|
grid-template-columns: 7.3446327684vw 1fr auto;
|
||||||
|
align-items: flex-start;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 1.4124293785vw 0 4.2372881356vw;
|
padding: 1.4124293785vw 0 4.2372881356vw;
|
||||||
@@ -555,6 +773,10 @@ pre {
|
|||||||
}
|
}
|
||||||
.category-row .index {
|
.category-row .index {
|
||||||
margin-top: 5.3672316384vw;
|
margin-top: 5.3672316384vw;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: 3.3898305085vw;
|
||||||
}
|
}
|
||||||
.category-row .title {
|
.category-row .title {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -570,16 +792,48 @@ pre {
|
|||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.category-row .category-input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-size: 8.4745762712vw;
|
||||||
|
}
|
||||||
|
.category-row .category-input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.category-row .edit-button,
|
||||||
|
.category-row .save-button {
|
||||||
|
color: var(--grey-100);
|
||||||
|
cursor: pointer;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
.category-row .edit-button {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.category-row.editable:hover .edit-button {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
.category-row::after {
|
.category-row::after {
|
||||||
content: "----------------------------------------";
|
content: "----------------------------------------";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: 3.3898305085vw;
|
||||||
}
|
}
|
||||||
.category-row.router-link-exact-active {
|
.category-row.router-link-exact-active, .category-row.editable {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
.category-row:hover:not(.router-link-exact-active) {
|
.category-row:hover:not(.router-link-exact-active):not(.editable) {
|
||||||
color: var(--theme-accent);
|
color: var(--theme-accent);
|
||||||
}/* Breakpoints */
|
}/* Breakpoints */
|
||||||
@keyframes flip-r {
|
@keyframes flip-r {
|
||||||
@@ -687,10 +941,10 @@ pre {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.directory {
|
main.directory {
|
||||||
padding-top: 5.0847457627vw;
|
padding-top: 5.0847457627vw;
|
||||||
}
|
}
|
||||||
.directory .label {
|
main.directory .label {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
margin: 4.802259887vw 0 6.7796610169vw;
|
margin: 4.802259887vw 0 6.7796610169vw;
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
@@ -698,7 +952,7 @@ pre {
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-size: 3.3898305085vw;
|
font-size: 3.3898305085vw;
|
||||||
}
|
}
|
||||||
.directory .notes {
|
main.directory .notes {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 3.9548022599vw;
|
gap: 3.9548022599vw;
|
||||||
@@ -743,27 +997,27 @@ pre {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.editor {
|
main.editor {
|
||||||
padding-top: 2.2598870056vw;
|
padding-top: 2.2598870056vw;
|
||||||
padding-bottom: 5.6497175141vw;
|
padding-bottom: 5.6497175141vw;
|
||||||
}
|
}
|
||||||
.editor h1 {
|
main.editor h1 {
|
||||||
font-weight: 700 !important;
|
font-weight: 700 !important;
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-size: 3.3898305085vw;
|
font-size: 3.3898305085vw;
|
||||||
}
|
}
|
||||||
.editor h1:first-child {
|
main.editor h1:first-child {
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
font-size: 3.3898305085vw;
|
font-size: 3.3898305085vw;
|
||||||
font-weight: 400 !important;
|
font-weight: 400 !important;
|
||||||
}
|
}
|
||||||
.editor h1:first-child:first-child::first-letter {
|
main.editor h1:first-child:first-child::first-letter {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 11.8644067797vw;
|
font-size: 11.8644067797vw;
|
||||||
}
|
}
|
||||||
.editor h1.is-editor-empty:first-child::before {
|
main.editor h1.is-editor-empty:first-child::before {
|
||||||
color: var(--grey-100);
|
color: var(--grey-100);
|
||||||
content: attr(data-placeholder);
|
content: attr(data-placeholder);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -771,56 +1025,135 @@ pre {
|
|||||||
font-size: 3.3898305085vw;
|
font-size: 3.3898305085vw;
|
||||||
font-weight: 400 !important;
|
font-weight: 400 !important;
|
||||||
}
|
}
|
||||||
.editor h1.is-editor-empty:first-child::before:first-child::first-letter {
|
main.editor h1.is-editor-empty:first-child::before:first-child::first-letter {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 11.8644067797vw;
|
font-size: 11.8644067797vw;
|
||||||
}
|
}
|
||||||
.editor p strong {
|
main.editor p strong {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.editor p em {
|
main.editor p em {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
.editor hr::before {
|
main.editor hr::before {
|
||||||
content: "----------------------------------------";
|
content: "----------------------------------------";
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-size: 3.3898305085vw;
|
font-size: 3.3898305085vw;
|
||||||
}
|
}
|
||||||
.editor ul {
|
main.editor ul {
|
||||||
list-style-type: disc;
|
list-style-type: disc;
|
||||||
}
|
}
|
||||||
.editor ul li {
|
main.editor ul li {
|
||||||
display: list-item;
|
display: list-item;
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
.editor ol {
|
main.editor ol {
|
||||||
list-style-type: decimal;
|
list-style-type: decimal;
|
||||||
}
|
}
|
||||||
.editor ol li {
|
main.editor ol li {
|
||||||
display: list-item;
|
display: list-item;
|
||||||
margin-left: 1.5em;
|
margin-left: 1.5em;
|
||||||
}
|
}
|
||||||
.editor ol li::marker {
|
main.editor ol li::marker {
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-size: 3.3898305085vw;
|
font-size: 3.3898305085vw;
|
||||||
}
|
}
|
||||||
.editor a {
|
main.editor a {
|
||||||
color: var(--theme-link);
|
color: var(--theme-link);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.editor .editor-wrap > div {
|
main.editor code {
|
||||||
|
border: 1px solid var(--grey-100);
|
||||||
|
color: var(--theme-accent);
|
||||||
|
padding: 0.2em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
main.editor pre code {
|
||||||
|
display: block;
|
||||||
|
color: inherit;
|
||||||
|
padding: 1em;
|
||||||
|
/* Code styling */
|
||||||
|
}
|
||||||
|
main.editor pre code .hljs-comment,
|
||||||
|
main.editor pre code .hljs-quote {
|
||||||
|
color: #616161;
|
||||||
|
}
|
||||||
|
main.editor pre code .hljs-variable,
|
||||||
|
main.editor pre code .hljs-template-variable,
|
||||||
|
main.editor pre code .hljs-attribute,
|
||||||
|
main.editor pre code .hljs-tag,
|
||||||
|
main.editor pre code .hljs-name,
|
||||||
|
main.editor pre code .hljs-regexp,
|
||||||
|
main.editor pre code .hljs-link,
|
||||||
|
main.editor pre code .hljs-name,
|
||||||
|
main.editor pre code .hljs-selector-id,
|
||||||
|
main.editor pre code .hljs-selector-class {
|
||||||
|
color: #f98181;
|
||||||
|
}
|
||||||
|
main.editor pre code .hljs-number,
|
||||||
|
main.editor pre code .hljs-meta,
|
||||||
|
main.editor pre code .hljs-built_in,
|
||||||
|
main.editor pre code .hljs-builtin-name,
|
||||||
|
main.editor pre code .hljs-literal,
|
||||||
|
main.editor pre code .hljs-type,
|
||||||
|
main.editor pre code .hljs-params {
|
||||||
|
color: #fbbc88;
|
||||||
|
}
|
||||||
|
main.editor pre code .hljs-string,
|
||||||
|
main.editor pre code .hljs-symbol,
|
||||||
|
main.editor pre code .hljs-bullet {
|
||||||
|
color: #b9f18d;
|
||||||
|
}
|
||||||
|
main.editor pre code .hljs-title,
|
||||||
|
main.editor pre code .hljs-section {
|
||||||
|
color: #faf594;
|
||||||
|
}
|
||||||
|
main.editor pre code .hljs-keyword,
|
||||||
|
main.editor pre code .hljs-selector-tag {
|
||||||
|
color: #70cff8;
|
||||||
|
}
|
||||||
|
main.editor pre code .hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
main.editor pre code .hljs-strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
main.editor blockquote {
|
||||||
|
border-left: 4px solid var(--grey-100);
|
||||||
|
padding-left: 0.5em;
|
||||||
|
}
|
||||||
|
main.editor s {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
main.editor s::after {
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 0.2824858757vw;
|
||||||
|
background: currentColor;
|
||||||
|
}
|
||||||
|
main.editor mark {
|
||||||
|
background: var(--theme-accent);
|
||||||
|
color: var(--theme-bg);
|
||||||
|
padding: 0.2em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
main.editor .editor-wrap > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 5.6497175141vw;
|
gap: 5.6497175141vw;
|
||||||
}
|
}
|
||||||
.editor .editor-wrap > div:focus {
|
main.editor .editor-wrap > div:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
.editor .bubble-menu {
|
main.editor .bubble-menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1.4124293785vw;
|
gap: 1.4124293785vw;
|
||||||
border: 1px solid var(--grey-100);
|
border: 1px solid var(--grey-100);
|
||||||
@@ -828,16 +1161,16 @@ pre {
|
|||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
background: var(--theme-bg);
|
background: var(--theme-bg);
|
||||||
}
|
}
|
||||||
.editor .bubble-menu button {
|
main.editor .bubble-menu button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0.2em;
|
padding: 0.2em;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
}
|
}
|
||||||
.editor .bubble-menu button:hover {
|
main.editor .bubble-menu button:hover {
|
||||||
background: var(--grey-100);
|
background: var(--grey-100);
|
||||||
color: var(--theme-bg);
|
color: var(--theme-bg);
|
||||||
}
|
}
|
||||||
.editor .bubble-menu button.active {
|
main.editor .bubble-menu button.active {
|
||||||
background: var(--theme-fg);
|
background: var(--theme-fg);
|
||||||
color: var(--theme-bg);
|
color: var(--theme-bg);
|
||||||
}/* Breakpoints */
|
}/* Breakpoints */
|
||||||
@@ -881,17 +1214,67 @@ pre {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.category .back {
|
main.category .back {
|
||||||
display: block;
|
display: block;
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
margin-top: 2.5423728814vw;
|
margin-top: 2.5423728814vw;
|
||||||
}
|
}
|
||||||
.category .category-row {
|
main.category .category-row {
|
||||||
margin-top: 1.1299435028vw;
|
margin-top: 1.1299435028vw;
|
||||||
}
|
}
|
||||||
.category .notes {
|
main.category .notes {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 3.9548022599vw;
|
gap: 3.9548022599vw;
|
||||||
margin-top: 2.5423728814vw;
|
margin-top: 2.5423728814vw;
|
||||||
|
}
|
||||||
|
main.category .new-note {
|
||||||
|
display: block;
|
||||||
|
margin: 14.1242937853vw auto 0;
|
||||||
|
}/* Breakpoints */
|
||||||
|
@keyframes flip-r {
|
||||||
|
50% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-l {
|
||||||
|
50% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-d {
|
||||||
|
50% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes flip-u {
|
||||||
|
50% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
51% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main.instructions .back-link {
|
||||||
|
opacity: 0.25;
|
||||||
|
display: block;
|
||||||
|
margin-top: 2.5423728814vw;
|
||||||
|
margin-bottom: 3.9548022599vw;
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
87793
out/renderer/assets/index-GKstRZpo.js
Normal file
87793
out/renderer/assets/index-GKstRZpo.js
Normal file
File diff suppressed because one or more lines are too long
@@ -8,8 +8,8 @@
|
|||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
|
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
|
||||||
/>
|
/>
|
||||||
<script type="module" crossorigin src="./assets/index-Dv1qfmwr.js"></script>
|
<script type="module" crossorigin src="./assets/index-GKstRZpo.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="./assets/index-CZWw79gc.css">
|
<link rel="stylesheet" crossorigin href="./assets/index-D9ZUihqb.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
313
package-lock.json
generated
313
package-lock.json
generated
@@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "app.takerofnotes.com",
|
"name": "takerofnotes-app",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "app.takerofnotes.com",
|
"name": "takerofnotes-app",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-toolkit/preload": "^3.0.2",
|
"@electron-toolkit/preload": "^3.0.2",
|
||||||
"@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.2.0",
|
||||||
"@takerofnotes/plugin-supabase": "^0.1.0",
|
"@takerofnotes/plugin-supabase": "^0.1.0",
|
||||||
|
"@tiptap/extension-code-block-lowlight": "^3.20.0",
|
||||||
"@tiptap/extension-document": "^3.19.0",
|
"@tiptap/extension-document": "^3.19.0",
|
||||||
"@tiptap/extension-image": "^3.19.0",
|
"@tiptap/extension-highlight": "^3.20.0",
|
||||||
"@tiptap/extension-table": "^3.19.0",
|
"@tiptap/extension-list": "^3.20.0",
|
||||||
"@tiptap/markdown": "^3.19.0",
|
|
||||||
"@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",
|
||||||
@@ -27,7 +27,9 @@
|
|||||||
"flexsearch": "^0.8.212",
|
"flexsearch": "^0.8.212",
|
||||||
"gsap": "^3.14.2",
|
"gsap": "^3.14.2",
|
||||||
"lenis": "^1.3.17",
|
"lenis": "^1.3.17",
|
||||||
|
"libsodium-wrappers": "^0.8.2",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
|
"lowlight": "^3.3.0",
|
||||||
"sass": "^1.97.3",
|
"sass": "^1.97.3",
|
||||||
"sass-embedded": "^1.97.3",
|
"sass-embedded": "^1.97.3",
|
||||||
"tempus": "^1.0.0-dev.17",
|
"tempus": "^1.0.0-dev.17",
|
||||||
@@ -2343,19 +2345,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@takerofnotes/plugin-filesystem": {
|
"node_modules/@takerofnotes/plugin-filesystem": {
|
||||||
"version": "0.1.1",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@takerofnotes/plugin-filesystem/-/plugin-filesystem-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@takerofnotes/plugin-filesystem/-/plugin-filesystem-0.2.0.tgz",
|
||||||
"integrity": "sha512-u3L6HLxN/+t7PTtzzRA2uzgaVh/O1vasngU/tQeC6JsTnlIrypWFVbAlTS5zT9Km4y+8w6C16eEq7tcN8+5lPg==",
|
"integrity": "sha512-BP7HBN0SKAqBiv5pDtXpyVmkW9UrOnPXpKeThTwQTIShHyN5aPaD8ZEyv8+vfTSs3qnXWGplNdEPVjbQmc27+Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@takerofnotes/plugin-sdk": "^0.1.0",
|
"@takerofnotes/plugin-sdk": "^0.3.1"
|
||||||
"gray-matter": "^4.0.3"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@takerofnotes/plugin-sdk": {
|
"node_modules/@takerofnotes/plugin-sdk": {
|
||||||
"version": "0.1.0",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@takerofnotes/plugin-sdk/-/plugin-sdk-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@takerofnotes/plugin-sdk/-/plugin-sdk-0.3.1.tgz",
|
||||||
"integrity": "sha512-ofhwwiQ59kNMEg2vvYoNq5JdXHB9/6TkDsbyroM5nsP/VPUvJSQ5g0UWCYBhteSzJ36iFUB+LtUwVt6gOXDClw==",
|
"integrity": "sha512-9GfPKyu1n52N00zYlLK32wdmGdc2uSd0jTj6UEixauW0TXn/7hD6SLpLRGSXcfZOJGGoi3iQk4MfjNsthe2ucw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
@@ -2464,17 +2465,34 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-code-block": {
|
"node_modules/@tiptap/extension-code-block": {
|
||||||
"version": "3.19.0",
|
"version": "3.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.20.0.tgz",
|
||||||
"integrity": "sha512-b/2qR+tMn8MQb+eaFYgVk4qXnLNkkRYmwELQ8LEtEDQPxa5Vl7J3eu8+4OyoIFhZrNDZvvoEp80kHMCP8sI6rg==",
|
"integrity": "sha512-lBbmNek14aCjrHcBcq3PRqWfNLvC6bcRa2Osc6e/LtmXlcpype4f6n+Yx+WZ+f2uUh0UmDRCz7BEyUETEsDmlQ==",
|
||||||
"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-code-block-lowlight": {
|
||||||
|
"version": "3.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-3.20.0.tgz",
|
||||||
|
"integrity": "sha512-9lN9rn07lOWkLnByT5C1axtq56MHpOI7MpLaCmX3p+x1bDl6Uvixm6AoBdTLfZUmUYeEFBsf7t5cR+QepMbkiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@tiptap/core": "^3.20.0",
|
||||||
|
"@tiptap/extension-code-block": "^3.20.0",
|
||||||
|
"@tiptap/pm": "^3.20.0",
|
||||||
|
"highlight.js": "^11",
|
||||||
|
"lowlight": "^2 || ^3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-document": {
|
"node_modules/@tiptap/extension-document": {
|
||||||
@@ -2558,6 +2576,19 @@
|
|||||||
"@tiptap/core": "^3.19.0"
|
"@tiptap/core": "^3.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tiptap/extension-highlight": {
|
||||||
|
"version": "3.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.20.0.tgz",
|
||||||
|
"integrity": "sha512-ANL1wFz0s1ScNR4uBfO0s6Sz+qqGp2u6ynrCVk6TCT3d10CQ+gD1gSDTrVRC3CtlMKtHHH4fYrFAq284+J0gKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@tiptap/core": "^3.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||||
"version": "3.19.0",
|
"version": "3.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.19.0.tgz",
|
||||||
@@ -2572,19 +2603,6 @@
|
|||||||
"@tiptap/pm": "^3.19.0"
|
"@tiptap/pm": "^3.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-image": {
|
|
||||||
"version": "3.19.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-3.19.0.tgz",
|
|
||||||
"integrity": "sha512-/rGl8nBziBPVJJ/9639eQWFDKcI3RQsDM3s+cqYQMFQfMqc7sQB9h4o4sHCBpmKxk3Y0FV/0NjnjLbBVm8OKdQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/core": "^3.19.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-italic": {
|
"node_modules/@tiptap/extension-italic": {
|
||||||
"version": "3.19.0",
|
"version": "3.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.19.0.tgz",
|
||||||
@@ -2694,20 +2712,6 @@
|
|||||||
"@tiptap/core": "^3.19.0"
|
"@tiptap/core": "^3.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-table": {
|
|
||||||
"version": "3.19.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.19.0.tgz",
|
|
||||||
"integrity": "sha512-Lg8DlkkDUMYE/CcGOxoCWF98B2i7VWh+AGgqlF+XWrHjhlKHfENLRXm1a0vWuyyP3NknRYILoaaZ1s7QzmXKRA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/core": "^3.19.0",
|
|
||||||
"@tiptap/pm": "^3.19.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-text": {
|
"node_modules/@tiptap/extension-text": {
|
||||||
"version": "3.19.0",
|
"version": "3.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.19.0.tgz",
|
||||||
@@ -2748,23 +2752,6 @@
|
|||||||
"@tiptap/pm": "^3.19.0"
|
"@tiptap/pm": "^3.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/markdown": {
|
|
||||||
"version": "3.19.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/markdown/-/markdown-3.19.0.tgz",
|
|
||||||
"integrity": "sha512-Pnfacq2FHky1rqwmGwEmUJxuZu8VZ8XjaJIqsQC34S3CQWiOU+PukC9In2odzcooiVncLWT9s97jKuYpbmF1tQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"marked": "^17.0.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/core": "^3.19.0",
|
|
||||||
"@tiptap/pm": "^3.19.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/pm": {
|
"node_modules/@tiptap/pm": {
|
||||||
"version": "3.20.0",
|
"version": "3.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.0.tgz",
|
||||||
@@ -2890,6 +2877,15 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/hast": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/http-cache-semantics": {
|
"node_modules/@types/http-cache-semantics": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
|
||||||
@@ -2970,6 +2966,12 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/unist": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/verror": {
|
"node_modules/@types/verror": {
|
||||||
"version": "1.10.11",
|
"version": "1.10.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
|
||||||
@@ -4422,6 +4424,15 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dequal": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
@@ -4439,6 +4450,19 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/devlop": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dequal": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dir-compare": {
|
"node_modules/dir-compare": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz",
|
||||||
@@ -5105,19 +5129,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esprima": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"bin": {
|
|
||||||
"esparse": "bin/esparse.js",
|
|
||||||
"esvalidate": "bin/esvalidate.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/estree-walker": {
|
"node_modules/estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
@@ -5137,18 +5148,6 @@
|
|||||||
"integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
|
"integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/extend-shallow": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"is-extendable": "^0.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/extract-zip": {
|
"node_modules/extract-zip": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||||
@@ -5625,49 +5624,6 @@
|
|||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/gray-matter": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"js-yaml": "^3.13.1",
|
|
||||||
"kind-of": "^6.0.2",
|
|
||||||
"section-matter": "^1.0.0",
|
|
||||||
"strip-bom-string": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gray-matter/node_modules/argparse": {
|
|
||||||
"version": "1.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"sprintf-js": "~1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gray-matter/node_modules/js-yaml": {
|
|
||||||
"version": "3.14.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
|
|
||||||
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"argparse": "^1.0.7",
|
|
||||||
"esprima": "^4.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"js-yaml": "bin/js-yaml.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gray-matter/node_modules/sprintf-js": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/gsap": {
|
"node_modules/gsap": {
|
||||||
"version": "3.14.2",
|
"version": "3.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz",
|
||||||
@@ -5738,6 +5694,15 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/highlight.js": {
|
||||||
|
"version": "11.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||||
|
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hookable": {
|
"node_modules/hookable": {
|
||||||
"version": "5.5.3",
|
"version": "5.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||||
@@ -5930,15 +5895,6 @@
|
|||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-extendable": {
|
|
||||||
"version": "0.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
|
||||||
"integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-extglob": {
|
"node_modules/is-extglob": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
@@ -6155,15 +6111,6 @@
|
|||||||
"json-buffer": "3.0.1"
|
"json-buffer": "3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/kind-of": {
|
|
||||||
"version": "6.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
|
||||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lazy-val": {
|
"node_modules/lazy-val": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
|
||||||
@@ -6196,6 +6143,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/libsodium": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-TsnGYMoZtpweT+kR+lOv5TVsnJ/9U0FZOsLFzFOMWmxqOAYXjX3fsrPAW+i1LthgDKXJnI9A8dWEanT1tnJKIw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/libsodium-wrappers": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-VFLmfxkxo+U9q60tjcnSomQBRx2UzlRjKWJqvB4K1pUqsMQg4cu3QXA2nrcsj9A1qRsnJBbi2Ozx1hsiDoCkhw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"libsodium": "^0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/linkify-it": {
|
"node_modules/linkify-it": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||||
@@ -6273,6 +6235,21 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lowlight": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hast": "^3.0.0",
|
||||||
|
"devlop": "^1.0.0",
|
||||||
|
"highlight.js": "~11.11.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||||
@@ -6359,18 +6336,6 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/marked": {
|
|
||||||
"version": "17.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.2.tgz",
|
|
||||||
"integrity": "sha512-s5HZGFQea7Huv5zZcAGhJLT3qLpAfnY7v7GWkICUr0+Wd5TFEtdlRR2XUL5Gg+RH7u2Df595ifrxR03mBaw7gA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"marked": "bin/marked.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 20"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/matcher": {
|
"node_modules/matcher": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
||||||
@@ -8121,19 +8086,6 @@
|
|||||||
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/section-matter": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"extend-shallow": "^2.0.1",
|
|
||||||
"kind-of": "^6.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "6.3.1",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
@@ -8416,15 +8368,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/strip-bom-string": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sumchecker": {
|
"node_modules/sumchecker": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "app.takerofnotes.com",
|
"name": "takerofnotes-app",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "An Electron application with Vue",
|
"description": "An Electron application with Vue",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
@@ -28,12 +28,12 @@
|
|||||||
"@electron-toolkit/preload": "^3.0.2",
|
"@electron-toolkit/preload": "^3.0.2",
|
||||||
"@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.2.0",
|
||||||
"@takerofnotes/plugin-supabase": "^0.1.0",
|
"@takerofnotes/plugin-supabase": "^0.1.0",
|
||||||
|
"@tiptap/extension-code-block-lowlight": "^3.20.0",
|
||||||
"@tiptap/extension-document": "^3.19.0",
|
"@tiptap/extension-document": "^3.19.0",
|
||||||
"@tiptap/extension-image": "^3.19.0",
|
"@tiptap/extension-highlight": "^3.20.0",
|
||||||
"@tiptap/extension-table": "^3.19.0",
|
"@tiptap/extension-list": "^3.20.0",
|
||||||
"@tiptap/markdown": "^3.19.0",
|
|
||||||
"@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",
|
||||||
@@ -43,7 +43,9 @@
|
|||||||
"flexsearch": "^0.8.212",
|
"flexsearch": "^0.8.212",
|
||||||
"gsap": "^3.14.2",
|
"gsap": "^3.14.2",
|
||||||
"lenis": "^1.3.17",
|
"lenis": "^1.3.17",
|
||||||
|
"libsodium-wrappers": "^0.8.2",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
|
"lowlight": "^3.3.0",
|
||||||
"sass": "^1.97.3",
|
"sass": "^1.97.3",
|
||||||
"sass-embedded": "^1.97.3",
|
"sass-embedded": "^1.97.3",
|
||||||
"tempus": "^1.0.0-dev.17",
|
"tempus": "^1.0.0-dev.17",
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { Index } from 'flexsearch'
|
import { Index } from 'flexsearch'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import sodium from 'libsodium-wrappers'
|
||||||
|
|
||||||
export default class NotesAPI {
|
export default class NotesAPI {
|
||||||
constructor(adapter) {
|
constructor(adapter, encryptionKey = null) {
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
throw new Error('NotesAPI requires a storage adapter')
|
throw new Error('NotesAPI requires a storage adapter')
|
||||||
}
|
}
|
||||||
|
|
||||||
this.adapter = adapter
|
this.adapter = adapter
|
||||||
this.notesCache = new Map()
|
this.notesCache = new Map()
|
||||||
|
this.encryptionKey = encryptionKey || process.env.NOTES_ENCRYPTION_KEY
|
||||||
|
this._sodiumReady = false
|
||||||
|
|
||||||
this.index = new Index({
|
this.index = new Index({
|
||||||
tokenize: 'tolerant',
|
tokenize: 'tolerant',
|
||||||
@@ -16,14 +19,109 @@ export default class NotesAPI {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _initSodium() {
|
||||||
|
if (!this._sodiumReady) {
|
||||||
|
await sodium.ready
|
||||||
|
this._sodiumReady = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_encrypt(note) {
|
||||||
|
if (!this.encryptionKey) {
|
||||||
|
throw new Error('Encryption key not set')
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = Buffer.from(this.encryptionKey, 'hex')
|
||||||
|
if (key.length !== 32) {
|
||||||
|
throw new Error(
|
||||||
|
'Encryption key must be 64 hex characters (32 bytes)',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES)
|
||||||
|
const message = JSON.stringify(note)
|
||||||
|
|
||||||
|
const ciphertext = sodium.crypto_secretbox_easy(
|
||||||
|
Buffer.from(message),
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
)
|
||||||
|
|
||||||
|
const combined = Buffer.concat([nonce, ciphertext])
|
||||||
|
return combined.toString('base64')
|
||||||
|
}
|
||||||
|
|
||||||
|
_decrypt(encryptedData) {
|
||||||
|
if (!this.encryptionKey) {
|
||||||
|
throw new Error('Encryption key not set')
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = Buffer.from(this.encryptionKey, 'hex')
|
||||||
|
if (key.length !== 32) {
|
||||||
|
throw new Error(
|
||||||
|
'Encryption key must be 64 hex characters (32 bytes)',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let combined
|
||||||
|
try {
|
||||||
|
combined = Buffer.from(encryptedData, 'base64')
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Invalid encrypted data: not valid base64')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
combined.length <
|
||||||
|
sodium.crypto_secretbox_NONCEBYTES +
|
||||||
|
sodium.crypto_secretbox_MACBYTES
|
||||||
|
) {
|
||||||
|
throw new Error('Invalid encrypted data: too short')
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonce = combined.slice(0, sodium.crypto_secretbox_NONCEBYTES)
|
||||||
|
const ciphertext = combined.slice(sodium.crypto_secretbox_NONCEBYTES)
|
||||||
|
|
||||||
|
let decrypted
|
||||||
|
try {
|
||||||
|
decrypted = sodium.crypto_secretbox_open_easy(
|
||||||
|
ciphertext,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Decryption failed: wrong key or corrupted data')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decrypted) {
|
||||||
|
throw new Error('Decryption failed: no data returned')
|
||||||
|
}
|
||||||
|
|
||||||
|
const decryptedStr = Buffer.from(decrypted).toString('utf8')
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(decryptedStr)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`Decryption succeeded but invalid JSON: ${decryptedStr}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
await this._initSodium()
|
||||||
await this.adapter.init()
|
await this.adapter.init()
|
||||||
|
|
||||||
const notes = await this.adapter.getAll()
|
const encryptedNotes = await this.adapter.getAll()
|
||||||
|
|
||||||
for (const note of notes) {
|
for (const encryptedNote of encryptedNotes) {
|
||||||
this.notesCache.set(note.id, note)
|
try {
|
||||||
this.index.add(note.id, note.title + '\n' + note.content)
|
const note = this._decrypt(encryptedNote.data || encryptedNote)
|
||||||
|
|
||||||
|
this.notesCache.set(note.id, note)
|
||||||
|
this.index.add(note.id, note.title + '\n' + note.content)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to decrypt note:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,10 +163,15 @@ export default class NotesAPI {
|
|||||||
content,
|
content,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const encryptedNote = {
|
||||||
|
id: note.id,
|
||||||
|
data: this._encrypt(note),
|
||||||
|
}
|
||||||
|
|
||||||
this.notesCache.set(id, note)
|
this.notesCache.set(id, note)
|
||||||
this.index.add(id, note.title + '\n' + content)
|
this.index.add(id, note.title + '\n' + content)
|
||||||
|
|
||||||
await this.adapter.create(note)
|
await this.adapter.create(encryptedNote)
|
||||||
|
|
||||||
return note
|
return note
|
||||||
}
|
}
|
||||||
@@ -87,9 +190,14 @@ export default class NotesAPI {
|
|||||||
note.content = content
|
note.content = content
|
||||||
note.updatedAt = new Date().toISOString()
|
note.updatedAt = new Date().toISOString()
|
||||||
|
|
||||||
|
const encryptedNote = {
|
||||||
|
id: note.id,
|
||||||
|
data: this._encrypt(note),
|
||||||
|
}
|
||||||
|
|
||||||
this.index.update(id, note.title + '\n' + content)
|
this.index.update(id, note.title + '\n' + content)
|
||||||
|
|
||||||
await this.adapter.update(note)
|
await this.adapter.update(encryptedNote)
|
||||||
|
|
||||||
return note
|
return note
|
||||||
}
|
}
|
||||||
@@ -115,9 +223,14 @@ export default class NotesAPI {
|
|||||||
|
|
||||||
note.updatedAt = new Date().toISOString()
|
note.updatedAt = new Date().toISOString()
|
||||||
|
|
||||||
|
const encryptedNote = {
|
||||||
|
id: note.id,
|
||||||
|
data: this._encrypt(note),
|
||||||
|
}
|
||||||
|
|
||||||
this.index.update(id, note.title + '\n' + note.content)
|
this.index.update(id, note.title + '\n' + note.content)
|
||||||
|
|
||||||
await this.adapter.update(note)
|
await this.adapter.update(encryptedNote)
|
||||||
|
|
||||||
return note
|
return note
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,24 +82,48 @@ app.whenReady().then(async () => {
|
|||||||
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 plugin = registry.get(supabasePlugin.id)
|
// const plugin = registry.get(supabasePlugin.id)
|
||||||
// const adapter = plugin.createAdapter(config.adapterConfig)
|
const adapter = plugin.createAdapter(config.adapterConfig)
|
||||||
const adapter = plugin.createAdapter({
|
// const adapter = plugin.createAdapter({
|
||||||
supabaseKey: process.env.SUPABASE_KEY,
|
// supabaseKey: process.env.SUPABASE_KEY,
|
||||||
supabaseUrl: process.env.SUPABASE_URL,
|
// supabaseUrl: process.env.SUPABASE_URL,
|
||||||
})
|
// })
|
||||||
|
|
||||||
// Init Notes API
|
// Init Notes API
|
||||||
const notesAPI = new NotesAPI(adapter)
|
const notesAPI = new NotesAPI(
|
||||||
|
adapter,
|
||||||
|
'729a0d21d783654c68f1a0123e2a0e986350de536b5324f1f35876ea12ffeaf5',
|
||||||
|
)
|
||||||
await notesAPI.init()
|
await notesAPI.init()
|
||||||
|
|
||||||
|
// Broadcast note changes to all windows
|
||||||
|
const broadcastNoteChange = (event, data) => {
|
||||||
|
BrowserWindow.getAllWindows().forEach((win) => {
|
||||||
|
win.webContents.send(event, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Handle Notes API
|
// Handle Notes API
|
||||||
ipcMain.handle('notesAPI:call', (_, method, args) => {
|
ipcMain.handle('notesAPI:call', async (_, method, args) => {
|
||||||
if (!notesAPI[method]) {
|
if (!notesAPI[method]) {
|
||||||
throw new Error('Invalid method')
|
throw new Error('Invalid method')
|
||||||
}
|
}
|
||||||
return notesAPI[method](...args)
|
|
||||||
|
const result = await notesAPI[method](...args)
|
||||||
|
|
||||||
|
// Broadcast changes to all windows
|
||||||
|
if (method === 'createNote') {
|
||||||
|
broadcastNoteChange('note-created', result)
|
||||||
|
} else if (method === 'updateNote') {
|
||||||
|
broadcastNoteChange('note-updated', result)
|
||||||
|
} else if (method === 'updateNoteMetadata') {
|
||||||
|
broadcastNoteChange('note-updated', result)
|
||||||
|
} else if (method === 'deleteNote') {
|
||||||
|
broadcastNoteChange('note-deleted', { id: args[0] })
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
electronApp.setAppUserModelId('com.electron')
|
electronApp.setAppUserModelId('com.electron')
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ const api = {
|
|||||||
openNoteWindow: (noteId) => {
|
openNoteWindow: (noteId) => {
|
||||||
ipcRenderer.send('open-note-window', noteId)
|
ipcRenderer.send('open-note-window', noteId)
|
||||||
},
|
},
|
||||||
|
onNoteCreated: (callback) => {
|
||||||
|
ipcRenderer.on('note-created', (_, data) => callback(data))
|
||||||
|
},
|
||||||
|
onNoteUpdated: (callback) => {
|
||||||
|
ipcRenderer.on('note-updated', (_, data) => callback(data))
|
||||||
|
},
|
||||||
|
onNoteDeleted: (callback) => {
|
||||||
|
ipcRenderer.on('note-deleted', (_, data) => callback(data))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement notes API
|
// Implement notes API
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Electron</title>
|
<title>Electron</title>
|
||||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
|
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
|
||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,18 +1,84 @@
|
|||||||
<template>
|
<template>
|
||||||
<router-link class="category-row" :to="`/category/${category}`">
|
<component
|
||||||
|
:class="['category-row', { editable }]"
|
||||||
|
:to="`/category/${category}`"
|
||||||
|
:is="wrapper"
|
||||||
|
>
|
||||||
<span class="index">{{ String(index + 1).padStart(2, '0') }}.</span>
|
<span class="index">{{ String(index + 1).padStart(2, '0') }}.</span>
|
||||||
<span class="title h1">{{ category }}</span>
|
<form v-if="isEditing" @submit.prevent="onSave">
|
||||||
</router-link>
|
<input
|
||||||
|
v-model="categoryInput"
|
||||||
|
class="category-input"
|
||||||
|
type="text"
|
||||||
|
ref="input"
|
||||||
|
@blur="onSave"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
<span v-else class="title h1">{{ categoryInput }}</span>
|
||||||
|
|
||||||
|
<button v-if="isEditing" class="save-button" @click="onSave">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button v-else-if="editable" class="edit-button" @click="onEdit">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({ index: Number, category: String })
|
import { computed, ref, onMounted } from 'vue'
|
||||||
|
import { RouterLink } from 'vue-router'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
index: Number,
|
||||||
|
category: {
|
||||||
|
type: String,
|
||||||
|
default: () => '',
|
||||||
|
},
|
||||||
|
editable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['edited'])
|
||||||
|
|
||||||
|
const isEditing = ref(false)
|
||||||
|
const categoryInput = ref('')
|
||||||
|
const input = ref()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
categoryInput.value = props.category
|
||||||
|
|
||||||
|
if (categoryInput.value === '') {
|
||||||
|
onEdit()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onEdit = async () => {
|
||||||
|
isEditing.value = true
|
||||||
|
await new Promise((res) => setTimeout(res, 300))
|
||||||
|
|
||||||
|
input.value?.focus()
|
||||||
|
}
|
||||||
|
const onSave = async () => {
|
||||||
|
isEditing.value = false
|
||||||
|
emit('edited', categoryInput.value)
|
||||||
|
await new Promise((res) => setTimeout(res, 300))
|
||||||
|
|
||||||
|
input.value?.blur()
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = computed(() => {
|
||||||
|
return props.editable ? 'div' : RouterLink
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.category-row {
|
.category-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: size-vw(26px) 1fr;
|
grid-template-columns: size-vw(26px) 1fr auto;
|
||||||
|
align-items: flex-start;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: size-vw(5px) 0 size-vw(15px);
|
padding: size-vw(5px) 0 size-vw(15px);
|
||||||
@@ -20,22 +86,52 @@ const props = defineProps({ index: Number, category: String })
|
|||||||
|
|
||||||
.index {
|
.index {
|
||||||
margin-top: size-vw(19px);
|
margin-top: size-vw(19px);
|
||||||
|
@include p;
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@include line-clamp(2);
|
@include line-clamp(2);
|
||||||
}
|
}
|
||||||
|
.category-input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
@include h1;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.edit-button,
|
||||||
|
.save-button {
|
||||||
|
color: var(--grey-100);
|
||||||
|
cursor: pointer;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
.edit-button {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
&.editable:hover {
|
||||||
|
.edit-button {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
&::after {
|
&::after {
|
||||||
content: '----------------------------------------';
|
content: '----------------------------------------';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@include p;
|
||||||
}
|
}
|
||||||
&.router-link-exact-active {
|
&.router-link-exact-active,
|
||||||
|
&.editable {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
&:hover:not(.router-link-exact-active) {
|
&:hover:not(.router-link-exact-active):not(.editable) {
|
||||||
color: var(--theme-accent);
|
color: var(--theme-accent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
<div class="menu-wrap layout-block-inner">
|
<div class="menu-wrap layout-block-inner">
|
||||||
<new-note class="menu-item" @noteOpened="closeMenu" />
|
<new-note class="menu-item" @noteOpened="closeMenu" />
|
||||||
<button class="menu-item">+ New Capitulum</button>
|
<router-link class="menu-item" to="/category"
|
||||||
|
>+ New Capitulum</router-link
|
||||||
|
>
|
||||||
<button class="menu-item">Change Theme</button>
|
<button class="menu-item">Change Theme</button>
|
||||||
<router-link class="menu-item" to="/instructions"
|
<router-link class="menu-item" to="/instructions"
|
||||||
>Instructio</router-link
|
>Instructio</router-link
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default () => {
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const menuOpen = computed(() => route.query.menuOpen === 'true')
|
const menuOpen = computed(() => route.query?.menuOpen === 'true')
|
||||||
|
|
||||||
const closeMenu = () => {
|
const closeMenu = () => {
|
||||||
router.push({
|
router.push({
|
||||||
|
|||||||
@@ -2,8 +2,30 @@ import { ref } from 'vue'
|
|||||||
|
|
||||||
const categories = ref([])
|
const categories = ref([])
|
||||||
const searchResults = ref([])
|
const searchResults = ref([])
|
||||||
|
const notesChangeCount = ref(0)
|
||||||
|
|
||||||
|
let initialized = false
|
||||||
|
|
||||||
|
function setupListeners() {
|
||||||
|
if (initialized || typeof window === 'undefined') return
|
||||||
|
initialized = true
|
||||||
|
|
||||||
|
window.api.onNoteCreated(() => {
|
||||||
|
notesChangeCount.value++
|
||||||
|
})
|
||||||
|
|
||||||
|
window.api.onNoteUpdated(() => {
|
||||||
|
notesChangeCount.value++
|
||||||
|
})
|
||||||
|
|
||||||
|
window.api.onNoteDeleted(() => {
|
||||||
|
notesChangeCount.value++
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
setupListeners()
|
||||||
|
|
||||||
/* -------------------------
|
/* -------------------------
|
||||||
Initialization
|
Initialization
|
||||||
--------------------------*/
|
--------------------------*/
|
||||||
@@ -46,6 +68,16 @@ export default () => {
|
|||||||
return note
|
return note
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateCategory(category, update) {
|
||||||
|
const notes = await loadCategoryNotes(category)
|
||||||
|
|
||||||
|
notes.forEach(async (note) => {
|
||||||
|
await updateNoteMetadata(note.id, { category: update })
|
||||||
|
})
|
||||||
|
|
||||||
|
await loadCategories()
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------
|
/* -------------------------
|
||||||
Search
|
Search
|
||||||
--------------------------*/
|
--------------------------*/
|
||||||
@@ -61,6 +93,7 @@ export default () => {
|
|||||||
return {
|
return {
|
||||||
categories,
|
categories,
|
||||||
searchResults,
|
searchResults,
|
||||||
|
notesChangeCount,
|
||||||
|
|
||||||
loadCategories,
|
loadCategories,
|
||||||
loadCategoryNotes,
|
loadCategoryNotes,
|
||||||
@@ -69,6 +102,7 @@ export default () => {
|
|||||||
createNote,
|
createNote,
|
||||||
updateNoteContent,
|
updateNoteContent,
|
||||||
updateNoteMetadata,
|
updateNoteMetadata,
|
||||||
|
updateCategory,
|
||||||
|
|
||||||
search,
|
search,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ 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 CreateCategory from '@/views/CreateCategory.vue'
|
||||||
import Category from '@/views/Category.vue'
|
import Category from '@/views/Category.vue'
|
||||||
import Instructions from '@/views/Instructions.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', name: 'create-category', component: CreateCategory },
|
||||||
{ path: '/category/:id', name: 'category', component: Category },
|
{ path: '/category/:id', name: 'category', component: Category },
|
||||||
{ path: '/instructions', name: 'instructions', component: Instructions },
|
{ path: '/instructions', name: 'instructions', component: Instructions },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
- The "html" element is excluded, otherwise a bug in Chrome breaks the CSS hyphens property (https://github.com/elad2412/the-new-css-reset/issues/36)
|
- The "html" element is excluded, otherwise a bug in Chrome breaks the CSS hyphens property (https://github.com/elad2412/the-new-css-reset/issues/36)
|
||||||
*/
|
*/
|
||||||
*:where(
|
*:where(
|
||||||
:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)
|
:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)
|
||||||
) {
|
) {
|
||||||
all: unset;
|
all: unset;
|
||||||
display: revert;
|
display: revert;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,12 @@
|
|||||||
<main class="category layout-block">
|
<main class="category layout-block">
|
||||||
<router-link class="back" to="/"><- Go Back</router-link>
|
<router-link class="back" to="/"><- Go Back</router-link>
|
||||||
|
|
||||||
<category-row :index="categoryIndex" :category="id" />
|
<category-row
|
||||||
|
:index="categoryIndex"
|
||||||
|
:category="id"
|
||||||
|
editable
|
||||||
|
@edited="onCategoryEdited"
|
||||||
|
/>
|
||||||
|
|
||||||
<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" />
|
||||||
@@ -13,26 +18,44 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute, useRouter } 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'
|
import NewNote from '@/components/NewNote.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const id = route.params.id
|
const id = route.params?.id
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const { categories, loadCategoryNotes } = useNotes()
|
const { categories, loadCategoryNotes, updateCategory, notesChangeCount } =
|
||||||
|
useNotes()
|
||||||
|
|
||||||
const notes = ref()
|
const notes = ref()
|
||||||
|
|
||||||
|
async function refreshNotes() {
|
||||||
|
if (id) {
|
||||||
|
notes.value = await loadCategoryNotes(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
notes.value = await loadCategoryNotes(id)
|
await refreshNotes()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(notesChangeCount, async () => {
|
||||||
|
await refreshNotes()
|
||||||
|
})
|
||||||
|
|
||||||
|
const onCategoryEdited = async (editedCategory) => {
|
||||||
|
await updateCategory(id, editedCategory)
|
||||||
|
|
||||||
|
router.push({ name: 'category', params: { id: editedCategory } })
|
||||||
|
}
|
||||||
|
|
||||||
const categoryIndex = computed(() => {
|
const categoryIndex = computed(() => {
|
||||||
return categories.value?.findIndex((category) => category === id) || 0
|
return categories.value?.findIndex((category) => category === id) || 1
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
16
src/renderer/src/views/CreateCategory.vue
Normal file
16
src/renderer/src/views/CreateCategory.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<main class="create-category layout-block-inner">
|
||||||
|
<category-row :index="1" editable @edited="onCategoryEdited" />
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import CategoryRow from '@/components/CategoryRow.vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const onCategoryEdited = (name) => {
|
||||||
|
router.push({ name: 'category', params: { id: name } })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -17,17 +17,26 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import useNotes from '@/composables/useNotes'
|
import useNotes from '@/composables/useNotes'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import CategoryRow from '@/components/CategoryRow.vue'
|
import CategoryRow from '@/components/CategoryRow.vue'
|
||||||
import NoteRow from '@/components/NoteRow.vue'
|
import NoteRow from '@/components/NoteRow.vue'
|
||||||
|
|
||||||
const { categories, loadCategories, loadCategoryNotes } = useNotes()
|
const { categories, loadCategories, loadCategoryNotes, notesChangeCount } =
|
||||||
|
useNotes()
|
||||||
|
|
||||||
const notes = ref()
|
const notes = ref()
|
||||||
|
|
||||||
onMounted(async () => {
|
async function refreshNotes() {
|
||||||
await loadCategories()
|
await loadCategories()
|
||||||
notes.value = await loadCategoryNotes()
|
notes.value = await loadCategoryNotes()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await refreshNotes()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(notesChangeCount, async () => {
|
||||||
|
await refreshNotes()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,12 @@
|
|||||||
>
|
>
|
||||||
Italic
|
Italic
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
@click="editor.chain().focus().toggleHighlight().run()"
|
||||||
|
:class="{ active: editor.isActive('highlight') }"
|
||||||
|
>
|
||||||
|
Highlight
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</bubble-menu>
|
</bubble-menu>
|
||||||
|
|
||||||
@@ -24,12 +30,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
|
import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
|
||||||
import { Editor, EditorContent } from '@tiptap/vue-3'
|
import { Editor, EditorContent } from '@tiptap/vue-3'
|
||||||
import { Markdown } from '@tiptap/markdown'
|
|
||||||
import Image from '@tiptap/extension-image'
|
|
||||||
import Document from '@tiptap/extension-document'
|
import Document from '@tiptap/extension-document'
|
||||||
import { Placeholder } from '@tiptap/extensions'
|
import { Placeholder } from '@tiptap/extensions'
|
||||||
import StarterKit from '@tiptap/starter-kit'
|
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 { BubbleMenu } from '@tiptap/vue-3/menus'
|
||||||
|
import { all, createLowlight } from 'lowlight'
|
||||||
import useNotes from '@/composables/useNotes'
|
import useNotes from '@/composables/useNotes'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import _debounce from 'lodash/debounce'
|
import _debounce from 'lodash/debounce'
|
||||||
@@ -46,9 +54,9 @@ const CustomDocument = Document.extend({
|
|||||||
const editor = shallowRef()
|
const editor = shallowRef()
|
||||||
|
|
||||||
const updateNote = _debounce(async ({ editor }) => {
|
const updateNote = _debounce(async ({ editor }) => {
|
||||||
const markdown = editor.getMarkdown()
|
const json = editor.getJSON()
|
||||||
|
|
||||||
await updateNoteContent(id, markdown)
|
await updateNoteContent(id, json)
|
||||||
|
|
||||||
updateTitle(editor)
|
updateTitle(editor)
|
||||||
}, 300)
|
}, 300)
|
||||||
@@ -73,6 +81,9 @@ onMounted(async () => {
|
|||||||
const note = await loadNote(id)
|
const note = await loadNote(id)
|
||||||
lastTitle = note.title
|
lastTitle = note.title
|
||||||
|
|
||||||
|
// Lowlight setup
|
||||||
|
const lowlight = createLowlight(all)
|
||||||
|
|
||||||
editor.value = new Editor({
|
editor.value = new Editor({
|
||||||
extensions: [
|
extensions: [
|
||||||
CustomDocument,
|
CustomDocument,
|
||||||
@@ -90,11 +101,15 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
Markdown.configure({}),
|
TaskList,
|
||||||
Image,
|
TaskItem,
|
||||||
|
Highlight,
|
||||||
|
CodeBlockLowlight.configure({
|
||||||
|
lowlight,
|
||||||
|
enableTabIndentation: true,
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
content: note.content,
|
content: note.content || [],
|
||||||
contentType: 'markdown',
|
|
||||||
onUpdate: updateNote,
|
onUpdate: updateNote,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -156,6 +171,94 @@ main.editor {
|
|||||||
color: var(--theme-link);
|
color: var(--theme-link);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
code {
|
||||||
|
border: 1px solid var(--grey-100);
|
||||||
|
color: var(--theme-accent);
|
||||||
|
padding: 0.2em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
display: block;
|
||||||
|
color: inherit;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
/* Code styling */
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #616161;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-tag,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-link,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-selector-class {
|
||||||
|
color: #f98181;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-meta,
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-builtin-name,
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-params {
|
||||||
|
color: #fbbc88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet {
|
||||||
|
color: #b9f18d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-section {
|
||||||
|
color: #faf594;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-selector-tag {
|
||||||
|
color: #70cff8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid var(--grey-100);
|
||||||
|
padding-left: 0.5em;
|
||||||
|
}
|
||||||
|
s {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: ' ';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: size-vw(1px);
|
||||||
|
background: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mark {
|
||||||
|
background: var(--theme-accent);
|
||||||
|
color: var(--theme-bg);
|
||||||
|
padding: 0.2em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
.editor-wrap {
|
.editor-wrap {
|
||||||
> div {
|
> div {
|
||||||
|
|||||||
Reference in New Issue
Block a user