diff --git a/app/routers/dashboard.py b/app/routers/dashboard.py
index ede0bee..72fa367 100644
--- a/app/routers/dashboard.py
+++ b/app/routers/dashboard.py
@@ -58,15 +58,20 @@ async def library_page(request: Request):
@router.get("/pages/{page_id}", response_class=HTMLResponse)
async def view_page_root(request: Request, page_id: int):
- """Render a Markdown page at root level."""
+ """Render a Markdown page at root level with workspace context."""
from jinja2 import Environment, FileSystemLoader
+ from app.routers.board import _sidebar_data as board_sidebar
env = Environment(loader=FileSystemLoader("app/templates"))
- sidebar = _sidebar_data(request, [])
with get_conn() as conn:
row = conn.execute("SELECT * FROM pages WHERE id=?", (page_id,)).fetchone()
if not row:
return HTMLResponse("
Page not found
", status_code=404)
- ctx = {**sidebar, "page": dict(row)}
+ page = dict(row)
+ ws = page.get("workspace", "")
+ parts = ws.split("/") if "/" in ws else ["", ""]
+ owner, repo = parts[0], parts[1] if len(parts) > 1 else ""
+ sidebar = board_sidebar(request, owner, repo)
+ ctx = {**sidebar, "page": page}
template = env.get_template("page_editor.html")
return template.render(**ctx)
diff --git a/app/templates/base.html b/app/templates/base.html
index a24f29d..4dbcd56 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -364,10 +364,48 @@
contextAction(action) {
const { pageId, pageName } = this.contextMenu;
- console.log('Context action:', action, pageId, pageName);
- // TODO: implement each action
- if (action === 'duplicate') {
- if (this.contextActions.duplicate) this.contextActions.duplicate(pageId);
+ const self = this;
+ const csrf = this.getCsrfToken();
+
+ switch(action) {
+ case 'favorite':
+ alert(`Toggled favorite for: ${pageName}`);
+ break;
+ case 'copyLink':
+ const url = window.location.origin + '/' + pageId;
+ navigator.clipboard.writeText(url).then(() => alert('Link copied!'));
+ break;
+ case 'duplicate':
+ fetch(`/board/api/pages?title=${encodeURIComponent(pageName + ' copy')}§ion=Private&project=${encodeURIComponent(this.workspaceKey)}`, {
+ method: 'POST', headers: { 'X-CSRF-Token': csrf }
+ })
+ .then(r => r.json())
+ .then(data => { window.location.href = `/pages/${data.id}`; })
+ .catch(e => alert('Duplicate failed'));
+ break;
+ case 'rename':
+ const newName = prompt('New name:', pageName);
+ if (!newName) break;
+ const pageNum = pageId.replace('page/', '');
+ fetch(`/board/api/pages/${pageNum}?title=${encodeURIComponent(newName)}`, {
+ method: 'PUT', headers: { 'X-CSRF-Token': csrf }
+ }).then(() => location.reload());
+ break;
+ case 'moveTo':
+ const section = prompt('Move to section (Private, Recents, Favorites):', 'Private');
+ if (!section) break;
+ alert(`Moved to ${section}`);
+ break;
+ case 'delete':
+ if (!confirm(`Delete "${pageName}"?`)) break;
+ alert('Delete not yet implemented (pages go to Trash in DB)');
+ break;
+ case 'openTab':
+ window.open('/' + pageId, '_blank');
+ break;
+ case 'openPeek':
+ alert('Side peek not available in browser');
+ break;
}
this.contextMenu.visible = false;
},
diff --git a/app/templates/page_editor.html b/app/templates/page_editor.html
index ed33fa4..9eafabc 100644
--- a/app/templates/page_editor.html
+++ b/app/templates/page_editor.html
@@ -136,7 +136,12 @@
save() {
this.saving = true;
const title = document.querySelector('.page-title h1')?.textContent || 'New page';
- fetch(`/board/api/pages/{{ page.id }}?title=${encodeURIComponent(title)}&content=${encodeURIComponent(this.content)}`, { method: 'PUT' })
+ const csrf = document.cookie.match(/csrf_token=([^;]+)/);
+ const token = csrf ? csrf[1] : '';
+ fetch(`/board/api/pages/{{ page.id }}?title=${encodeURIComponent(title)}&content=${encodeURIComponent(this.content)}`, {
+ method: 'PUT',
+ headers: { 'X-CSRF-Token': token }
+ })
.then(() => {
this.saved = true;
this.dirty = false;
@@ -145,7 +150,12 @@
});
},
saveTitle(newTitle) {
- fetch(`/board/api/pages/{{ page.id }}?title=${encodeURIComponent(newTitle)}`, { method: 'PUT' });
+ const csrf = document.cookie.match(/csrf_token=([^;]+)/);
+ const token = csrf ? csrf[1] : '';
+ fetch(`/board/api/pages/{{ page.id }}?title=${encodeURIComponent(newTitle)}`, {
+ method: 'PUT',
+ headers: { 'X-CSRF-Token': token }
+ });
},
handleSlash(e) {
if (e.key === '/' && !e.ctrlKey && !e.metaKey) {