Add Gitea markdown rendering and Edit/Preview tabs

- New /api/render-markdown proxy to Gitea (configured via config.yaml)
- lib/config.mjs loads config.yaml with yaml-js
- Edit/Preview tabs on the body field in the task dialog
- Title and body rendered as markdown inline in the task list
- Gitea markup+chroma CSS extracted and vendored to public/gitea-markup.css
- Markdown cached in memory per text string

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 18:19:29 +00:00
parent 1100720b03
commit 444b51794a
12 changed files with 369 additions and 12 deletions

View File

@@ -4,6 +4,7 @@ process.on('uncaughtException', (err) => { console.error('[uncaughtException
import express from 'express';
import { next_id } from './lib/ids.mjs';
import { list_tasks, get_task, set_task, delete_task } from './lib/storage.mjs';
import { get_config } from './lib/config.mjs';
const app = express();
app.use(express.json());
@@ -15,6 +16,31 @@ const BIND_ADDRESS = process.env.BIND_ADDRESS ?? 'localhost';
function ok(res, data = {}) { res.json({ ok: true, ...data }); }
function fail(res, msg, status = 400) { res.status(status).json({ ok: false, error: msg }); }
// ---------------------------------------------------------------------------
// Markdown rendering proxy
// ---------------------------------------------------------------------------
app.post('/api/render-markdown', async (req, res) => {
const { text, mode = 'markdown' } = req.body;
if (typeof text !== 'string') { return fail(res, 'text is required'); }
const config = get_config();
const gitea_url = config.gitea?.url;
const token = config.gitea?.token;
if (!gitea_url) { return fail(res, 'Gitea not configured (missing config.yaml)'); }
const headers = { 'Content-Type': 'application/json' };
if (token) { headers['Authorization'] = `token ${token}`; }
const gitea_res = await fetch(`${gitea_url}/api/v1/markdown`, {
method: 'POST',
headers,
body: JSON.stringify({ Text: text, Mode: mode }),
});
if (!gitea_res.ok) {
return fail(res, `Gitea error: ${gitea_res.status} ${gitea_res.statusText}`);
}
const html = await gitea_res.text();
ok(res, { html });
});
// ---------------------------------------------------------------------------
// Tasks
// ---------------------------------------------------------------------------