Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
node_modules/
.emdash/
.turbo/
.playwright-cli/
.playwright-cli/
.yarn/install-state.gz
dist/
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed .yarn/install-state.gz
Binary file not shown.
8 changes: 2 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Monorepo Summary

This repository contains a monorepo managed by [Turborepo](https://turborepo.dev/docs). It implements a web-based note-taking application called "Astronote" that is strongly influenced by a MacOS application called [The Archive](https://zettelkasten.de/the-archive/).
This repository contains a monorepo managed by [Turborepo](https://turborepo.dev/docs). It implements a note-taking application called "Astronote."

## The Frontend Application

Expand Down Expand Up @@ -30,8 +30,4 @@ The remaining area of the screen (to the right of the sidebar) is the "content v

- Views the content of a selected note.
- Creates new notes
- Edits existing notes

### Mobile Support

Currently, we are only interested in desktop support. We are not interested in implementing mobile device support.
- Edits existing notes
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
# Astronote

Yet another web-based note taking application.
Yet another note taking application[^1].

## Quick Start

```
```sh
# This will install dependencies and launch the API and frontend application in development mode.
yarn
yarn dev
```
```

## Packages

This project is structured as a [monorepo](https://monorepo.tools/) managed with [Turborepo](https://turborepo.dev/). It contains several packages:

### Applications

- [apps/web-app](apps/web-app) - React-based UI.
- [apps/api](apps/api) - REST API. This layer is intentionally minimal. It validates requests and forwards them to the `domain` package (see below).
- [apps/desktop](apps/desktop) - A standalone, [Electron](https://www.electronjs.org/)-based variant that runs on the desktop (no REST API is required).

### Libraries

- [packages/astronote-client](packages/astronote-client) - Exports an API for use by the web app or the desktop app. The web variant routes requests to the REST API. The desktop variant routes requests directly to a local package (`packages/domain`).
- [packjages/domain](packages/domain) - Business logic lives here.
- [packages/repository](packages/repository) - Database code lives here.
- [packages/types](packages/types) - Exports shared [Zod](https://zod.dev/) validation functions and inferred TypeScript types.
- [packages/eslint-config](packages/eslint-config) - Shared [ESLint](https://eslint.org/) config.
- [packages/typescript-config](packages/typescript-config/) - Shared [TypeScript](https://www.typescriptlang.org/) config.

[^1]: This one's the best, though.
4 changes: 3 additions & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
"@repo/domain": "*",
"@repo/repository": "*",
"@repo/types": "*",
"better-sqlite3": "^12.6.2",
"cors": "^2.8.5",
"express": "^5.1.0"
"express": "^5.1.0",
"knex": "^3.1.0"
},
"devDependencies": {
"@repo/typescript-config": "*",
Expand Down
10 changes: 8 additions & 2 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import path from "node:path";
import express from "express";
import cors from "cors";
import { initDatabase, seedDatabase } from "@repo/repository";
import knex from "knex";
import { init, seedDatabase } from "@repo/repository";
import { notesRouter } from "./routes/notes";
import { tagsRouter } from "./routes/tags";
import { collectionsRouter } from "./routes/collections";
Expand All @@ -13,7 +14,12 @@ const PORT = process.env.PORT ?? 3009;
const DB_PATH = process.env.DB_PATH ?? path.join(process.cwd(), "astronote.db");

async function main() {
await initDatabase(DB_PATH);
const db = knex({
client: "better-sqlite3",
connection: { filename: DB_PATH },
useNullAsDefault: true,
});
await init(db);
await seedDatabase();

const app = express();
Expand Down
28 changes: 28 additions & 0 deletions apps/desktop/electron-builder.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json",
"appId": "com.astronote.desktop",
"productName": "Astronote",
"directories": {
"output": "release"
},
"files": [
"dist/**/*",
"node_modules/**/*",
"package.json"
],
"extraResources": [
{
"from": "../web-app/dist",
"to": "renderer"
}
],
"mac": {
"target": "dmg"
},
"win": {
"target": "nsis"
},
"linux": {
"target": "AppImage"
}
}
31 changes: 31 additions & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@repo/desktop",
"version": "0.0.0",
"private": true,
"type": "module",
"main": "dist/main.js",
"scripts": {
"dev": "tsx src/dev.ts",
"build": "vite build --config vite.main.config.ts && vite build --config vite.preload.config.ts",
"rebuild": "electron-rebuild -o better-sqlite3",
"postinstall": "yarn rebuild",
"start": "electron dist/main.js",
"check-types": "tsc --noEmit",
"package": "electron-builder"
},
"dependencies": {
"@repo/domain": "*",
"@repo/repository": "*",
"@repo/types": "*",
"better-sqlite3": "^12.6.2"
},
"devDependencies": {
"@electron/rebuild": "^4.0.1",
"@repo/typescript-config": "*",
"electron": "^35.1.2",
"electron-builder": "^26.0.12",
"tsx": "^4.19.4",
"typescript": "^5.9.2",
"vite": "^6.3.5"
}
}
37 changes: 37 additions & 0 deletions apps/desktop/src/dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { spawn } from "node:child_process";
import { build } from "vite";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import path from "node:path";

const require = createRequire(import.meta.url);
const root = path.dirname(fileURLToPath(import.meta.url));
const desktopRoot = path.resolve(root, "..");

async function start() {
// Build main and preload
await build({
configFile: path.join(desktopRoot, "vite.main.config.ts"),
mode: "development",
});
await build({
configFile: path.join(desktopRoot, "vite.preload.config.ts"),
mode: "development",
});

// Launch Electron pointing at the Vite dev server
const electronPath = require("electron") as unknown as string;
const child = spawn(electronPath, [path.join(desktopRoot, "dist", "main.js")], {
stdio: "inherit",
env: {
...process.env,
VITE_DEV_SERVER_URL: "http://localhost:5173",
},
});

child.on("close", (code) => {
process.exit(code ?? 0);
});
}

start();
92 changes: 92 additions & 0 deletions apps/desktop/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { app, BrowserWindow, ipcMain } from "electron";
import path from "node:path";
import knex from "knex";
import { init, seedDatabase } from "@repo/repository";
import * as domain from "@repo/domain";

let mainWindow: BrowserWindow | null = null;

async function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(import.meta.dirname, "preload.cjs"),
contextIsolation: true,
sandbox: false,
},
});

if (process.env["VITE_DEV_SERVER_URL"]) {
await mainWindow.loadURL(process.env["VITE_DEV_SERVER_URL"]);
} else if (app.isPackaged) {
await mainWindow.loadFile(
path.join(process.resourcesPath, "renderer", "index.html"),
);
} else {
await mainWindow.loadFile(
path.join(import.meta.dirname, "..", "..", "web-app", "dist", "index.html"),
);
}
}

function registerIpcHandlers() {
// Settings
ipcMain.handle("fetchSettings", () => domain.getSettings());
ipcMain.handle("updateSettings", (_e, updates) => domain.updateSettings(updates));
ipcMain.handle("resetAll", () => domain.resetAll());

// Tags
ipcMain.handle("fetchTags", () => domain.listTags());

// Collections
ipcMain.handle("fetchCollections", () => domain.listCollections());
ipcMain.handle("createCollection", (_e, name) => domain.createCollection(name));
ipcMain.handle("deleteCollection", (_e, id) => domain.deleteCollection(id));
ipcMain.handle("setDefaultCollection", async (_e, id) => {
await domain.setDefaultCollection(id);
return domain.listCollections();
});

// Notes
ipcMain.handle("fetchNotes", (_e, params) =>
domain.listNotes(params?.q, params?.tags, params?.collectionId),
);
ipcMain.handle("createNote", (_e, params) =>
domain.createNote({
title: params.title,
content: params.content,
collectionId: params.collectionId ?? undefined,
}),
);
ipcMain.handle("updateNote", (_e, id, updates) => domain.updateNote(id, updates));
ipcMain.handle("deleteNote", (_e, id) => domain.archiveNote(id));
ipcMain.handle("addTag", (_e, noteId, tag) => domain.addTag(noteId, tag));
ipcMain.handle("removeTag", (_e, noteId, tag) => domain.removeTag(noteId, tag));
}

app.whenReady().then(async () => {
const dbPath = path.join(app.getPath("userData"), "astronote.db");
const db = knex({
client: "better-sqlite3",
connection: { filename: dbPath },
useNullAsDefault: true,
});
await init(db);
await seedDatabase();

registerIpcHandlers();
await createWindow();

app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});

app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
25 changes: 25 additions & 0 deletions apps/desktop/src/preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { contextBridge, ipcRenderer } from "electron";

contextBridge.exposeInMainWorld("electronAPI", {
// Settings
fetchSettings: () => ipcRenderer.invoke("fetchSettings"),
updateSettings: (updates: unknown) => ipcRenderer.invoke("updateSettings", updates),
resetAll: () => ipcRenderer.invoke("resetAll"),

// Tags
fetchTags: () => ipcRenderer.invoke("fetchTags"),

// Collections
fetchCollections: () => ipcRenderer.invoke("fetchCollections"),
createCollection: (name: string) => ipcRenderer.invoke("createCollection", name),
deleteCollection: (id: number) => ipcRenderer.invoke("deleteCollection", id),
setDefaultCollection: (id: number) => ipcRenderer.invoke("setDefaultCollection", id),

// Notes
fetchNotes: (params?: unknown) => ipcRenderer.invoke("fetchNotes", params),
createNote: (params: unknown) => ipcRenderer.invoke("createNote", params),
updateNote: (id: string, updates: unknown) => ipcRenderer.invoke("updateNote", id, updates),
deleteNote: (id: string) => ipcRenderer.invoke("deleteNote", id),
addTag: (noteId: string, tag: string) => ipcRenderer.invoke("addTag", noteId, tag),
removeTag: (noteId: string, tag: string) => ipcRenderer.invoke("removeTag", noteId, tag),
});
8 changes: 8 additions & 0 deletions apps/desktop/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "@repo/typescript-config/node.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
23 changes: 23 additions & 0 deletions apps/desktop/vite.main.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineConfig } from "vite";
import path from "node:path";

export default defineConfig({
build: {
outDir: "dist",
lib: {
entry: path.resolve(__dirname, "src/main.ts"),
formats: ["es"],
fileName: () => "main.js",
},
rollupOptions: {
external: [
"electron",
/^node:/,
"better-sqlite3",
"knex",
],
},
minify: false,
emptyOutDir: false,
},
});
18 changes: 18 additions & 0 deletions apps/desktop/vite.preload.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig } from "vite";
import path from "node:path";

export default defineConfig({
build: {
outDir: "dist",
lib: {
entry: path.resolve(__dirname, "src/preload.ts"),
formats: ["cjs"],
fileName: () => "preload.cjs",
},
rollupOptions: {
external: ["electron"],
},
minify: false,
emptyOutDir: false,
},
});
1 change: 1 addition & 0 deletions apps/web-app/dist/assets/DesktopClient-DYc3tr51.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

Loading