eudaimon.io
Motivation
It seems that perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
Eudaimonio is my Personal Knowledge Management System (PKMS). Similar to Obsidian or Zettrl, it visualizes notes and their links to other notes as a graph. The reason why I want to invest time into this project, is because I hope that it will help me in all areas of my life, from cooking, by storing all of my recipies, to learning new skills, by writting, summerizing and reviewing, to use it as a journal, so I don’t forget where I started from, to sharing those ideas with others.
While implementing, I tried to keep it as simple as possible, following the the quote of Antoine de Saint-Exupery. For this reason, I want to take this opportunity and learn how to write simple and maintanable code.
Concept
Behind the scene of eudaimon.io is a collection of HTML files, which viewed on a web browser on any device. The links between those files are parsed and visualized as a graph.
Using HTML files as notes enables me to share them with a web server.
Graph Visualization
One part of eudaimon.io is the visualization of notes as a graph.
A force directed algorithm is used to draw the graph. It simulates a physical system, by imagining the edges as springs pulling and pushing the connected vertices to an optimal distance. At the same time all vertices are repelling each other, as if they have the same electromagnetic charge. This leads to a visually pleasing embedding, with few edge crossings and evenly spaced vertices.
The design for nodes and edges are kept as simple as possible. Nodes are represented by a rectangle containing text and edges are straght lines connecting the rectangles.
Drawing Edges
The overall goal for the graph view is to reduce cluttering to a minimum. For edges this means that they need to be as short as possible. The algorithm used to connect two rectangles with the shortest line is described here.
private drawEdge(vertexA: DrawingVertex, vertexB: DrawingVertex): void {
try {
const dividendX =
vertexA.left * vertexB.left - vertexA.right * vertexB.right;
const divisorX =
vertexA.left + vertexB.left - (vertexA.right + vertexB.right);
const x = dividendX / divisorX;
const dividendY =
vertexA.top * vertexB.top - vertexA.bottom * vertexB.bottom;
const divisorY =
vertexA.top + vertexB.top - (vertexA.bottom + vertexB.bottom);
const y = dividendY / divisorY;
const optimalPoint = new Point(x, y);
const line = this.getDrawingEdge(vertexA, vertexB);
const anchorA = optimalPoint.copy();
const anchorB = optimalPoint.copy();
if (vertexA.right < vertexB.left) {
anchorA.x = vertexA.right;
anchorB.x = vertexB.left;
} else if (vertexA.left > vertexB.right) {
anchorA.x = vertexA.left;
anchorB.x = vertexB.right;
}
if (vertexA.top > vertexB.bottom) {
anchorA.y = vertexA.top;
anchorB.y = vertexB.bottom;
} else if (vertexA.bottom < vertexB.top) {
anchorA.y = vertexA.bottom;
anchorB.y = vertexB.top;
}
line.pointA = anchorA;
line.pointB = anchorB;
} catch {
throw new Error(
`Error drawing edge from ${vertexA.toString()} to ${vertexB.toString()}:`
);
}
}
systemd
-
Copy the
eudaimondbinary to/usr/local/bin -
Create a service file under
/etc/systemd/system/eudaimond.service -
Enable the service with
systemctl
Build the binary with GOOS=linux GOARCH=arm64 go build -o build/eudaimond
|
[Unit]
Description=eudaimond
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/eudaimond
WorkingDirectory=/home/pi/.eudaimond
Restart=always
User=pi
[Install]
WantedBy=multi-user.target
Backend
Reporting Errors
type ProblemDetail struct {
Type string `json:"type"` // Primary identifier for the problem type. Should be an absolute and resolvable URI, that points to a human readable documentation for the problem type
Status int `json:"status"` // The same status code used in the HTTP response.
Title string `json:"title"` // Short, human-readable summary of the problem type
Detail string `json:"detail"` // Human-readable explanation about how to correct the problem.
Instance string `json:"instance"` // Unique identifier of the problem occurance for the server
}
Features
-
Think of sticky notes. These are nodes in the graph view that cannot be clicked to open a HTML file, but rather display all there is to it. I think a title and text with a maximum length should be sufficient.
-
Mark the shortest path between two nodes with thick or colorful lines. You could select these two lines with
SHIFTand mouse clicks. This could also be used to link to notes, without having a link in the document itself. -
Create a nice icon for the Website.
-
Add timestamp information to desaturate the vertices in the graph view that have not been visited for a long time. These vertices should also have a weaker pull to the center, in the hope that all important notes are then in one spot. This should make it more visible where I am currently working on.
-
Make todo’s checkable. When I showed Haruka the todo site, she instictively wanted to press on it. Would be a great feature.
-
Redirect from eudaimon.io to www.eudaimon.io
-
Write about your algorithm to draw edges
-
I don’t want to graph to always look different for each reload. Save the positions of the vertices persistently. Maybe the embedding algorithm should be in the backend. This way you can avoid the periodic position updates via REST API.
-
Set up DNS server, so that I can reach my raspberry pi with
home.eudaimon.io-
Set up a DNS sink at the same time
-
-
Set up TLS encryption, so that the browser does not complain about an insecure connection.
-
Press
SPACEto recenter the graph view. Adjust the zoom as well. -
Implementing sub-graphs. eudaimonio should parse HTML files and find all the links. It would be nice, if it would keep HTML files in different directories separate. This way, the graph view can stay small.
-
Protect sub-graphs with a password.
-