Skip to content
Merged
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
39 changes: 31 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ cd oolongd
go build
```

TODO: Docker image, Nix and Homebrew packages?
<!-- TODO: Docker image, Nix and Homebrew packages? -->

## Usage

Expand All @@ -27,10 +27,10 @@ oolong
```

The service will run in the background, and the API will be accessible on port 11975.
The graph can be opened by navigating to [http://localhost:11975](http://localhost:11975), and clicking on note nodes will open the file in an editor of your choice (as defined in config).

To view the constructed graph and use the web editor see [oolong-web](https://github.com/oolong-sh/oolong-web).

(Serving up a copy of the graph from the backend is a WIP)
To use the Oolong web editor see [oolong-web](https://github.com/oolong-sh/oolong-web). (The web editor also includes a graph integration)

## Configuration

Expand All @@ -41,20 +41,37 @@ Oolong looks for a configuration file at `~/.config/oolong.toml`

| Option | Description | Recommended |
|--------|-------------|---------|
| `ngram_range` | Range of NGram sizes to use for keyword linking | `[1, 2, 3]` |
| `note_directories` | List of directories to use with Oolong | `["~/notes"]` |
| `ignored_directories` | Subdirectories to exclude from reading and linking | `[".git"]` |
| `allowed_extensions` | Whitelist of file extensions to use in linking | `[".md", ".txt", ".mdx", ".tex", ".typ"]` |
| `open_command` | Command to run when clicking a graph node | `["code"]` (See below for more details) |
| `ngram_range` | Range of NGram sizes to use for keyword linking | `[1, 2, 3]` |
| `stop_words` | Additional stop words to exclude from keyword extraction | `[]` |


The `open_command` option is used by the graph to allow you to open a clicked note in an editor of your choice.

For example, to open a note in VSCode use `open_command = ["code"]`

To use your system default editor:
- Linux: `open_command = ["xdg-open"]`
- MacOS: `open_command = ["open"]`
- Windows: `open_command = ["start"]`

For more situations where you want to run a more complex command, separate consecutive arguments:
- `open_command = ["tmux", "neww", "-c", "shell", "nvim"]` (opens Neovim in a new tmux window in the active session)

<!-- TODO: example using a script -->


**Graph Settings** (required):

| Option | Description | Recommended |
|--------|-------------|-------------|
| min_node_weight | Minimum NGram weight to allow to show up in the graph | `2.0` (Increase to a larger number for large note directories) |
| max_node_weight | Maximum size of a node in the graph (larger values are clamped to this size) | `10.0` |
| min_link_weight | The minimum allowed link strength between a note and NGram | `0.1` (Increase to a larger number (0.2-0.3) for larger note directories) |
| default_mode | Default graph mode (2d/3d) | `"3d"` |


**Cloud Synchronization Settings** (optional):
Expand All @@ -75,9 +92,6 @@ Oolong looks for a configuration file at `~/.config/oolong.toml`
### Example Configuration

```toml
# Range of NGram sizes to use for keyword linking
ngram_range = [ 1, 2, 3 ]

# List of directories to read into Oolong
note_directories = [
"~/notes",
Expand All @@ -99,16 +113,25 @@ allowed_extensions = [
".typ",
]

# Command to run when open endpoint it called (a note node is clicked on the graph)
# Note: All arguments MUST be separated into separate strings (see config for more details)
open_command = [ "code" ]


# Range of NGram sizes to use for keyword linking
ngram_range = [ 1, 2, 3 ]

# Extra stop words to exclude from NGram generation
stop_words = [
"hello",
]


# graph settings (required)
[graph]
min_node_weight = 8.0
max_node_weight = 12.0
min_link_weight = 0.2
default_mode = "3d"

# optional plugins section (not currently recommended)
[plugins]
Expand Down
22 changes: 15 additions & 7 deletions examples/oolong.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# Range of NGram sizes to use for keyword linking
ngram_range = [ 1, 2, 3 ]

# List of directories to read into Oolong
note_directories = [
"~/notes",
Expand All @@ -21,19 +18,25 @@ allowed_extensions = [
".typ",
]

# Command to run when open endpoint it called (a note node is clicked on the graph)
open_command = [ "code" ]
# Note: All arguments MUST be separated into separate strings
# (more complicated commands should likely be written into a script)
# i.e. 'open_command = ["tmux", "neww", "-c", "shell", "nvim"]'

# Range of NGram sizes to use for keyword linking
ngram_range = [ 1, 2, 3 ]

# Additional stop words
stop_words = [
"hello",
]

# List of plugins (lua files) to load -- not recommended
[plugins]
plugin_paths = [ "./scripts/daily_note.lua" ]

[graph]
min_node_weight = 4.0
max_node_weight = 80.0
min_link_weight = 0.1
default_mode = "3d"

#
# NOTE: do not include the following section if you do not want to use cloud sync
Expand All @@ -43,3 +46,8 @@ host = "127.0.0.1" # replace with your server hostname/ip
user = "your_username" # replace with your server username
port = 22 # server ssh port
private_key_path = "/home/<your_username>/.ssh/<your_private_key_file>" # replace with your private key path


# List of plugins (lua files) to load -- not recommended
# [plugins]
# plugin_paths = [ "./scripts/daily_note.lua" ]
15 changes: 0 additions & 15 deletions index.html

This file was deleted.

20 changes: 0 additions & 20 deletions index.js

This file was deleted.

18 changes: 18 additions & 0 deletions internal/api/config_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,21 @@ func handleGetNoteDirsConfig(w http.ResponseWriter, r *http.Request) {

json.NewEncoder(w).Encode(config.NotesDirPaths())
}

func handleGetGraphView(w http.ResponseWriter, r *http.Request) {
log.Println("Request received:", r.Method, r.URL, r.Host)
w.Header().Set("Content-Type", "application/json")

if err := checkOrigin(w, r); err != nil {
log.Println(err)
http.Error(w, fmt.Sprintln(err), 500)
return
}

mode := config.GraphMode()
if mode == "" {
mode = "2d"
}

json.NewEncoder(w).Encode(mode)
}
5 changes: 4 additions & 1 deletion internal/api/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,29 @@ import (
"net/http"
)

// DOC:
// spawn the oolong api server
func SpawnServer() {
mux := http.NewServeMux()

// TODO: add some sort of JWT system for better security

// graph endpoints
mux.HandleFunc("GET /graph", handleGetGraph)
mux.Handle("/", http.FileServer(http.Dir("./static")))

// config endpoints
mux.HandleFunc("GET /config", handleGetConfig)
mux.HandleFunc("GET /config/graph", handleGetGraphConfig)
mux.HandleFunc("GET /config/note-dirs", handleGetNoteDirsConfig)
mux.HandleFunc("GET /config/default-graph-mode", handleGetGraphView)

// note endpoints
mux.HandleFunc("GET /notes", handleGetNotes)
mux.HandleFunc("GET /note", handleGetNote)
mux.HandleFunc("POST /note", handleCreateNote)
mux.HandleFunc("PUT /note", handleUpdateNote)
mux.HandleFunc("DELETE /note", handleDeleteNote)
mux.HandleFunc("GET /open/note", handleOpenNote)

// start server
log.Println("Starting server on :11975...")
Expand Down
38 changes: 37 additions & 1 deletion internal/api/note_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package api

import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"

"github.com/oolong-sh/oolong/internal/config"
"github.com/oolong-sh/oolong/internal/state"
)

Expand Down Expand Up @@ -58,7 +61,7 @@ func handleGetNote(w http.ResponseWriter, r *http.Request) {
b, err := os.ReadFile(path)
if err != nil {
log.Println(err)
http.Error(w, "Could not read file '"+path+"'", 500)
http.Error(w, "Could not read file '"+path+"'", 400)
return
}

Expand Down Expand Up @@ -169,3 +172,36 @@ func handleDeleteNote(w http.ResponseWriter, r *http.Request) {
// NOTE: this function may need to call the update function due to files no longer existing
// - check this case in state, this may require substantial logic missing there
}

// 'GET /open/note?path=/path/to/note.md' opens the specified not file using the command specified in oolong.toml
func handleOpenNote(w http.ResponseWriter, r *http.Request) {
log.Println("Request received:", r.Method, r.URL, r.Host)

path := r.URL.Query().Get("path")
if path == "" {
http.Error(w, "Path parameter not specified ", http.StatusBadRequest)
return
}

if e, err := exists(path); err != nil {
log.Println(err)
http.Error(w, "Error checking path", 500)
return
} else if !e {
log.Printf("File %s does not exist.\n", path)
http.Error(w, "Note file not found exists", 400)
return
}

// open file in editor (use command defined in config)
openCommand := append(config.OpenCommand(), path)
cmd := exec.Command(openCommand[0], openCommand[1:]...)
if errors.Is(cmd.Err, exec.ErrDot) {
cmd.Err = nil
}

if err := cmd.Run(); err != nil {
log.Println(err)
http.Error(w, "Error opening file in editor.", 500)
}
}
14 changes: 12 additions & 2 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ type OolongSyncConfig sync.SyncConfig

type OolongConfig struct {
NotesDirPaths []string `toml:"note_directories"`
NGramRange []int `toml:"ngram_range"`
AllowedExtensions []string `toml:"allowed_extensions"`
IgnoreDirectories []string `toml:"ignored_directories"`
StopWords []string `toml:"stop_words"`
OpenCommand []string `toml:"open_command"`

// TODO: move these things to a "linker" config section
NGramRange []int `toml:"ngram_range"`
StopWords []string `toml:"stop_words"`

PluginsConfig OolongPluginConfig `toml:"plugins"`
GraphConfig OolongGraphConfig `toml:"graph"`
Expand All @@ -31,17 +34,24 @@ type OolongGraphConfig struct {
MinNodeWeight float64 `toml:"min_node_weight"`
MaxNodeWeight float64 `toml:"max_node_weight"`
MinLinkWeight float64 `toml:"min_link_weight"`
DefaultMode string `toml:"default_mode"`
}

type OolongEditorConfig struct {
// TODO: web editor related config (themes?)
}

func Config() *OolongConfig { return &cfg }

func NotesDirPaths() []string { return cfg.NotesDirPaths }
func OpenCommand() []string { return cfg.OpenCommand }
func NGramRange() []int { return cfg.NGramRange }
func AllowedExtensions() []string { return cfg.AllowedExtensions }
func PluginPaths() []string { return cfg.PluginsConfig.PluginPaths }
func IgnoredDirectories() []string { return cfg.IgnoreDirectories }
func StopWords() []string { return cfg.StopWords }
func WeightThresholds() OolongGraphConfig { return cfg.GraphConfig }
func GraphMode() string { return cfg.GraphConfig.DefaultMode }
func SyncConfig() OolongSyncConfig { return cfg.SyncConfig }

// TODO: file watcher for config file, reload on change
Expand Down
1 change: 1 addition & 0 deletions internal/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func SerializeGraph(keywordMap map[string]keywords.Keyword, notes []notes.Note)
for _, keyword := range keywordMap {
// Only add nodes above the minimum threshold
if keyword.Weight >= minThresh {
// TODO: clamp weights == min to 1 after filtering
clampedWeight := clamp(keyword.Weight, minThresh, upperBound)
nodes = append(nodes, NodeJSON{
ID: keyword.Keyword,
Expand Down
Loading
Loading