first commit

This commit is contained in:
Ravi Krishnan 2026-05-12 10:12:29 +09:30
commit 598a5be4a8
7 changed files with 1733 additions and 0 deletions

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Manex
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

70
README.md Normal file
View file

@ -0,0 +1,70 @@
# Manex Brain
A local AI brain for your Obsidian vault. Indexes all your notes and answers questions privately using a local Apple Silicon MLX model — no cloud, no API keys, no data leaves your machine.
## Requirements
- **macOS with Apple Silicon** (M1 or later)
- **Python 3** (via Homebrew: `brew install python`)
- **Obsidian 1.5.0** or later
## How It Works
On first load, the plugin:
1. Creates a Python virtual environment at `~/.obsidian-study-room/venv`
2. Installs [mlx-lm](https://github.com/ml-explore/mlx-lm) into the venv (~2 min on first run)
3. Downloads and starts `mlx-community/Qwen3-4B-4bit` (~2.3 GB, downloaded once)
4. Indexes all your vault notes in the background for vault-wide semantic search
After that, the server starts automatically every time Obsidian opens.
## Features
- **Fully local** — inference runs on-device via Apple's MLX framework; nothing is sent to the cloud
- **Vault-wide RAG** — all notes are indexed; relevant chunks are automatically included in every answer
- **Active note context** — the currently open note is semantically embedded and prioritised
- **Graph-aware** — linked notes, backlinks, and shared-tag notes are included as context
- **Clickable sources** — note links in answers open the corresponding note in Obsidian
- **Chat memory** — conversation history is maintained within a session
- **Automatic re-indexing** — notes are re-indexed on create, edit, rename, or delete
## Usage
1. Open Obsidian — the MLX server starts automatically in the background
2. Click the brain icon in the ribbon, or run **Open Manex Brain panel** from the command palette
3. Open any note and start asking questions — the plugin automatically uses it as context
4. Click any note link in an answer to open that note
## Commands
| Command | Description |
|---|---|
| Open Manex Brain panel | Opens the chat sidebar |
| Open Manex Brain web app | Opens manex.app |
| Ask Study Room about current note | Focuses the panel on the current note |
## Settings
| Setting | Description |
|---|---|
| Include frontmatter | Whether to include YAML frontmatter when reading notes |
## Privacy
All processing is local. The plugin:
- Makes no network requests except to `http://localhost:8080` (the local MLX server)
- Stores the vault index at `~/.obsidian-study-room/vault-index.json` (text only, no embeddings)
- Does not collect, transmit, or store any user data
## Troubleshooting
**"MLX server not available"** — The model may still be downloading or loading. Wait 25 minutes on first run; the panel will update automatically once the server is ready.
**Python not found** — Install Python via Homebrew: `brew install python`
**Slow first response** — Normal. The model (~2.3 GB) is downloaded and loaded into memory once; subsequent responses are fast.
## Support
Visit [manex.app](https://manex.app) to learn more or support development.

1272
main.js Normal file

File diff suppressed because it is too large Load diff

11
manifest.json Normal file
View file

@ -0,0 +1,11 @@
{
"id": "manex-brain",
"name": "Manex Brain",
"version": "1.0.0",
"minAppVersion": "1.5.0",
"description": "Local AI brain for your Obsidian vault. Indexes all your notes and answers questions privately using a local Apple Silicon MLX model — no cloud, no API keys.",
"author": "Manex",
"authorUrl": "https://manex.app",
"fundingUrl": "https://manex.app",
"isDesktopOnly": true
}

20
package.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "manex-brain",
"version": "1.0.0",
"description": "Local AI brain for your Obsidian vault using Apple Silicon MLX.",
"main": "main.js",
"scripts": {
"check": "node --check main.js",
"pack": "zip -r manex-brain.zip manifest.json versions.json main.js styles.css README.md LICENSE"
},
"keywords": [
"obsidian",
"ai",
"mlx",
"local-ai",
"knowledge-management"
],
"author": "Manex",
"license": "MIT",
"devDependencies": {}
}

336
styles.css Normal file
View file

@ -0,0 +1,336 @@
.manex-study-room-panel {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.manex-scroll-area {
display: flex;
flex: 1;
flex-direction: column;
gap: 14px;
min-height: 0;
overflow-y: auto;
padding: 14px 20px 8px;
}
.manex-panel-header h2 {
margin: 0 0 6px;
}
.manex-panel-header p,
.manex-empty span,
.manex-context-card span {
color: var(--text-muted);
line-height: 1.45;
}
.manex-panel-header p {
margin: 0;
}
.manex-vault-status {
margin-top: 4px;
color: var(--text-faint);
font-size: var(--font-ui-smaller);
}
.manex-workspace-status,
.manex-settings-status {
display: grid;
gap: 4px;
margin-top: 10px;
border: 1px solid var(--background-modifier-border);
border-radius: 12px;
padding: 10px 12px;
background: var(--background-secondary);
}
.manex-workspace-status span,
.manex-settings-status strong {
font-weight: 700;
}
.manex-workspace-status small,
.manex-settings-status p {
margin: 0;
color: var(--text-muted);
line-height: 1.35;
}
.manex-workspace-status button {
justify-self: start;
margin-top: 4px;
}
.manex-entitlement-card {
display: grid;
gap: 4px;
margin-top: 10px;
border: 1px solid color-mix(in srgb, var(--interactive-accent) 28%, var(--background-modifier-border));
border-radius: 12px;
padding: 10px 12px;
background: color-mix(in srgb, var(--interactive-accent) 8%, var(--background-secondary));
}
.manex-entitlement-card.is-active {
border-color: color-mix(in srgb, var(--color-green) 45%, var(--background-modifier-border));
background: color-mix(in srgb, var(--color-green) 8%, var(--background-secondary));
}
.manex-entitlement-card span {
font-weight: 800;
}
.manex-entitlement-card small {
color: var(--text-muted);
line-height: 1.35;
}
.manex-entitlement-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 4px;
}
.manex-entitlement-actions button {
white-space: nowrap;
}
.manex-settings-entitlement {
border-color: color-mix(in srgb, var(--interactive-accent) 30%, var(--background-modifier-border));
}
.manex-settings-entitlement.is-active {
border-color: color-mix(in srgb, var(--color-green) 45%, var(--background-modifier-border));
}
.manex-chat-toolbar .setting-item {
align-items: flex-start;
border: 1px solid var(--background-modifier-border);
border-radius: 12px;
padding: 12px;
}
.manex-chat-toolbar .setting-item-control {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: flex-start;
}
.manex-chat-toolbar button,
.manex-message-actions button,
.manex-chat-form button {
white-space: nowrap;
}
.manex-context-card {
display: grid;
gap: 4px;
border: 1px solid var(--background-modifier-border);
border-radius: 12px;
padding: 10px 12px;
background: var(--background-secondary);
}
.manex-context-card .manex-graph-context {
display: block;
margin-top: 4px;
color: var(--text-muted);
font-size: var(--font-ui-smaller);
line-height: 1.35;
}
.manex-chat-log {
display: flex;
flex: 1;
flex-direction: column;
gap: 12px;
min-height: 220px;
overflow: auto;
padding-right: 2px;
}
.manex-empty {
display: grid;
gap: 6px;
border: 1px dashed var(--background-modifier-border);
border-radius: 14px;
padding: 18px;
text-align: left;
}
.manex-message {
border: 1px solid var(--background-modifier-border);
border-radius: 14px;
padding: 12px;
background: var(--background-primary);
}
.manex-message-user {
align-self: flex-end;
max-width: 92%;
background: var(--interactive-accent);
color: var(--text-on-accent);
}
.manex-message-assistant {
align-self: flex-start;
max-width: 96%;
}
.manex-message.is-error {
border-color: var(--text-error);
}
.manex-message.is-error .manex-upgrade-box {
border-color: color-mix(in srgb, var(--interactive-accent) 36%, var(--background-modifier-border));
}
.manex-message-label {
margin-bottom: 6px;
color: var(--text-muted);
font-size: var(--font-ui-smaller);
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
}
.manex-message-user .manex-message-label {
color: color-mix(in srgb, var(--text-on-accent) 72%, transparent);
}
.manex-message-text {
line-height: 1.5;
overflow-wrap: anywhere;
word-break: normal;
}
.manex-message-text > :first-child {
margin-top: 0;
}
.manex-message-text > :last-child {
margin-bottom: 0;
}
.manex-message-text h1,
.manex-message-text h2,
.manex-message-text h3 {
margin: 12px 0 6px;
font-size: var(--font-ui-medium);
line-height: 1.25;
}
.manex-message-text p,
.manex-message-text ul,
.manex-message-text ol {
margin: 6px 0;
}
.manex-message-text li {
margin: 4px 0;
}
.manex-message-meta {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
}
.manex-message-meta span {
border: 1px solid var(--background-modifier-border);
border-radius: 999px;
padding: 2px 8px;
color: var(--text-muted);
font-size: var(--font-ui-smaller);
line-height: 1.4;
}
.manex-sources {
display: grid;
gap: 6px;
margin-top: 12px;
border-top: 1px solid var(--background-modifier-border);
padding-top: 10px;
}
.manex-sources span {
color: var(--text-muted);
font-size: var(--font-ui-smaller);
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
}
.manex-sources p {
margin: 0;
color: var(--text-muted);
font-size: var(--font-ui-small);
line-height: 1.4;
}
.manex-message-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
}
.manex-upgrade-box {
display: grid;
gap: 6px;
margin-top: 12px;
border: 1px solid var(--background-modifier-border);
border-radius: 12px;
padding: 10px;
background: color-mix(in srgb, var(--interactive-accent) 8%, var(--background-secondary));
}
.manex-upgrade-box p {
margin: 0;
color: var(--text-muted);
}
.manex-upgrade-box button {
justify-self: start;
}
.manex-chat-form {
display: grid;
flex-shrink: 0;
gap: 8px;
border-top: 1px solid var(--background-modifier-border);
padding: 10px 20px 16px;
background: var(--background-primary);
}
.manex-chat-form textarea {
min-height: 72px;
resize: vertical;
}
.manex-chat-form button {
justify-self: end;
}
.manex-actions {
display: grid;
gap: 10px;
}
.manex-actions .setting-item {
border: 1px solid var(--background-modifier-border);
border-radius: 10px;
padding: 12px;
}
.manex-memory-textarea {
width: 100%;
min-height: 160px;
}

3
versions.json Normal file
View file

@ -0,0 +1,3 @@
{
"1.0.0": "1.5.0"
}