Add a marked extension that tokenizes math delimiters and renders them
with KaTeX: `$$ ... $$` / `\[ ... \]` as block, `$ ... $` / `\( ... \)`
and mid-paragraph `$$ ... $$` as inline.
Inline `$ ... $` requires a non-space after the opening `$`, no newline
inside, and a non-digit after the closing `$`, so prose like "$100 から
$200" is left alone; `\$` still escapes a literal dollar sign. Parse
errors fall back to KaTeX's red inline message instead of throwing, and
math whose closing delimiter has not streamed in yet stays plain text.
DOMPurify strips `semantics` and `annotation` by default, which would
leak the raw TeX out of KaTeX's MathML output, so both are allowed
explicitly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tabs can now be dragged to reorder within a group and to move between
sections in one gesture: dropping on a tab's left/right half inserts
before/after it and adopts that tab's section, while a dedicated zone
past the last group ungroups and appends at the end. A blue indicator
line shows the drop position while dragging.
core/notes.reorder() takes the full new page-id order computed
client-side (NotePanel.vue has the reactive list already) rather than
recomputing positions server-side, validates it against the current
page-id set, sets the moved page's section, then renumbers position.
POST /api/notes/reorder exposes it.
Also adds docs/notes-guide.md, an end-user walkthrough of the shared
notes panel (pages, sections, drag-and-drop, AI interaction) — the
existing docs were all developer-facing specs with no operating
instructions for the feature itself.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Notes can now be clustered into named sections in the tab bar: each tab
gets a 🏷 button to assign it to a section, and clicking a section's
label renames (or, given an empty name, ungroups) every page in it.
note_pages gains a `section` column. Since the app has no migration
framework, database.py adds the column to existing SQLite files at
startup via PRAGMA table_info + ALTER TABLE. core/notes.set_section()
keeps same-section pages contiguous by renumbering position whenever a
page's section changes, so the tab bar and the AI-facing title list
(now grouped under "## section" headings) can both treat "adjacent" as
"same group" without a separate ordering field.
Also fixes a bug in the tab rename input: ref="renameInput" sits inside
a nested v-for, which Vue 3 binds as an array rather than a single
element, so renameInput.value?.select() was throwing on every rename.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds a note_pages table backing a tabbed notes panel where the user and
the model can both read and write. Only page titles (plus a short
preview) are injected into the system prompt on every turn; full page
content is kept out of context and fetched on demand via a read_note
tool, with write_note letting the model append to, replace, or create a
page.
Generalizes llm.resolve_web_search into resolve_tool_calls so the
tool-calling loop handles web_search and the new note tools through one
path. The chat stream emits a notes_updated SSE event when the model
edits a note so the frontend panel can refresh; local edits are tracked
as dirty and never clobbered by that refresh.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Caddy now owns ports 80/443 and reverse-proxies to the frontend
container (which was publishing 80 itself, conflicting with Caddy),
handling Let's Encrypt certificate issuance/renewal automatically.
Fix the Caddyfile's upstream name (it referenced a nonexistent "app"
service instead of "frontend") and update the Lightsail deploy guide's
HTTPS section to match this setup instead of the old manual-certbot
approach.
- Add a password-protected /api/settings endpoint (HTTP Basic auth,
backend/app/core/auth.py) backed by a DB-stored allowlist of
connection settings (OPENAI_API_KEY, ANTHROPIC_API_KEY, AWS creds,
OLLAMA_API_BASE, TAVILY_API_KEY, LOCAL_KB_EMBEDDING_MODEL). Saved
values override .env and apply immediately via os.environ, so
Lightsail deployments no longer need SSH + `.env` edits to rotate
keys. Values are always returned masked to the frontend.
- Replace ModelSelector.vue's hardcoded model list with a DB-backed
/api/models CRUD API (read is public, writes reuse the settings
password) so the chat model dropdown can be edited from the GUI;
seed it once with the previous hardcoded presets on first boot.
- Fix local_knowledge.py reading LOCAL_KB_EMBEDDING_MODEL once at
import time instead of per-call, which would have silently ignored
GUI/DB overrides for the embedding model.
- Add SettingsPanel.vue (API keys + model management UI) and document
the new SETTINGS_PASSWORD env var in .env.example and the Lightsail
deploy guide.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add backend/frontend Dockerfiles, nginx reverse proxy config, and
docker-compose.yml to run the app on AWS Lightsail; document the setup
in docs/deploy-lightsail.md. Fix a .gitignore path anchoring bug that
left backend/knowledge_base/web_search/ untracked instead of ignored.
- Summarize web search results into structured Markdown via LLM before
saving them to the local knowledge base, and support regenerating
existing entries from their source URLs (backend/app/core/memory_kb).
- Downgrade vite to ^6 to match @vitejs/plugin-vue's peer dependency
range, fixing a failing `npm ci` (found while testing the Docker build).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Split backend/app/core into weather/, discord/, memory_kb/, search/
subpackages by responsibility, keeping config.py and llm.py as the
shared foundation; updated all import sites accordingly.
- Add local Markdown knowledge base support (local_knowledge.py,
knowledge_base/fgo.md, LOCAL_KB_* settings) that embeds and injects
relevant docs into the system prompt by topic similarity.
- Add ruff and pyright as backend dev dependencies with basic config
in pyproject.toml for linting and type checking.
- Document local machine quirks in CLAUDE.md (Postgres on port 8080,
SSLKEYLOGFILE breaking litellm/pyright via OPENSSL_Uplink crashes).
- Add backend/run_dev.ps1 dev launcher and docs/discord-bot.md.