Unify all New Page / New Folder buttons across the app
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

- Add global window.FlowDeck namespace with createPage() and showCreateFolderModal()
- createPage() creates a Notion-style page via /board/api/pages (same as sidebar bottom-right button)
- showCreateFolderModal() opens a global themed modal (dark/light) instead of browser prompt()
- All New Page buttons now call window.FlowDeck.createPage()
- All New Folder buttons now call window.FlowDeck.showCreateFolderModal()
- Global New Folder modal uses existing .flowdeck-modal CSS classes for theme consistency
- Updated Ctrl+N keyboard shortcut to use unified createPage()
This commit is contained in:
2026-07-19 21:40:39 -04:00
parent 74883688ef
commit 9100da74f3
4 changed files with 101 additions and 20 deletions
+89 -8
View File
@@ -123,8 +123,8 @@
<span>{% if gitea_workspace %}📁 {{ active_ws_name }}{% elif workspace_key and '/' in workspace_key %}🔗{% else %}📁{% endif %} {{ active_ws_name if not gitea_workspace else '' }}</span>
</div>
<div class="sidebar-section-actions">
<button class="section-action-btn" title="New Page" @click.stop="newPageInWorkspace()">📄</button>
<button class="section-action-btn" title="New Folder" @click.stop="newFolderInWorkspace()">📁</button>
<button class="section-action-btn" title="New Page" @click.stop="window.FlowDeck.createPage()">📄</button>
<button class="section-action-btn" title="New Folder" @click.stop="window.FlowDeck.showCreateFolderModal()">📁</button>
</div>
</div>
{% from '_workspace_tree_macro.html' import render_workspace_tree %}
@@ -170,7 +170,7 @@
<span>Meetings</span>
</div>
<div class="sidebar-section-actions">
<button class="section-action-btn" title="Add" @click.stop="newPage('Meetings')">+</button>
<button class="section-action-btn" title="Add" @click.stop="window.FlowDeck.createPage()">+</button>
</div>
</div>
<div class="sidebar-section-items" x-show="sectionsOpen.meetings" x-transition>
@@ -332,7 +332,7 @@
<span style="font-size:14px;">💬</span> New chat
<span class="shortcut">Ctrl+O</span>
</button>
<button class="sidebar-footer-btn" style="width:36px; justify-content:center; background:var(--bg-hover); border-radius:8px;" @click="newPage('Private')" title="New page">
<button class="sidebar-footer-btn" style="width:36px; justify-content:center; background:var(--bg-hover); border-radius:8px;" onclick="window.FlowDeck.createPage()" title="New page">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><line x1="9" y1="15" x2="15" y2="15"/></svg>
</button>
</div>
@@ -425,10 +425,10 @@
@click.outside="wsCtx.visible = false">
<template x-if="wsCtx.isFolder">
<div class="context-menu-section">
<div class="context-menu-item" @click="wsCtxAction('new-file')">
<div class="context-menu-item" onclick="window.FlowDeck.createPage()">
<span class="menu-icon">📄</span> New File
</div>
<div class="context-menu-item" @click="wsCtxAction('new-folder')">
<div class="context-menu-item" onclick="window.FlowDeck.showCreateFolderModal()">
<span class="menu-icon">📁</span> New Folder
</div>
</div>
@@ -488,6 +488,70 @@
});
})();
// ── Global FlowDeck namespace ────────────────────────────────
// All "New Page" and "New Folder" buttons across the app call
// these functions for consistent behaviour.
window.FlowDeck = {
getCsrfToken: function() {
var m = document.cookie.match(/csrf_token=([^;]+)/);
return m ? m[1] : '';
},
/** Create a new Notion-style page (same as sidebar bottom-right button). */
createPage: function() {
var project = (window.appState && window.appState.workspaceKey) || '';
var url = '/board/api/pages?section=Private&project=' + encodeURIComponent(project);
fetch(url, {
method: 'POST',
headers: { 'X-CSRF-Token': this.getCsrfToken() }
})
.then(function(r) { if (!r.ok) throw new Error('CSRF or API error'); return r.json(); })
.then(function(data) { window.location.href = '/pages/' + data.id; })
.catch(function(err) {
if (window.appState && window.appState.toast) window.appState.toast('Failed to create page: ' + err.message, 'error');
else alert('Failed to create page: ' + err.message);
});
},
/** Show the global New Folder modal. */
showCreateFolderModal: function() {
var modal = document.getElementById('fd-global-folder-modal');
if (!modal) return;
modal.style.display = 'flex';
var input = document.getElementById('fd-global-folder-input');
if (input) { input.value = ''; setTimeout(function() { input.focus(); }, 100); }
},
/** Called by the global New Folder modal's Create button. */
_confirmCreateFolder: function() {
var input = document.getElementById('fd-global-folder-input');
var name = (input && input.value || '').trim();
if (!name) return;
var modal = document.getElementById('fd-global-folder-modal');
if (modal) modal.style.display = 'none';
var self = this;
fetch('/api/local-workspace/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': this.getCsrfToken() },
body: JSON.stringify({ name: name, type: 'folder' })
})
.then(function(r) { if (!r.ok) throw new Error('Failed'); return r.json(); })
.then(function() {
// Refresh sidebar tree
if (window.appState && window.appState.refreshSidebarTree) window.appState.refreshSidebarTree();
// Notify workspace page
document.dispatchEvent(new CustomEvent('flowdeck:workspace-changed'));
if (window._wsData && window._wsData._reloadAfterAction) window._wsData._reloadAfterAction();
// Show toast
if (window.appState && window.appState.toast) window.appState.toast('Folder created', 'success');
})
.catch(function(err) {
if (window.appState && window.appState.toast) window.appState.toast('Failed: ' + err.message, 'error');
else alert('Failed to create folder: ' + err.message);
});
}
};
function appState() {
return {
init() {
@@ -1164,8 +1228,7 @@
if (e.ctrlKey && e.key === 'n') {
e.preventDefault();
if (data && data.newPageInWorkspace) data.newPageInWorkspace();
else if (data && data.newPage) data.newPage('Private');
window.FlowDeck.createPage();
}
else if (e.key === 'Enter' && !editable) {
var activeSidebar = document.querySelector('.sidebar-item.active');
@@ -1237,5 +1300,23 @@
.flowdeck-modal-btn.confirm:hover{background:#2C8CEB;}
</style>
<!-- Global New Folder Modal -->
<div id="fd-global-folder-modal" class="flowdeck-modal-overlay" style="display:none" onclick="if(event.target===this)this.style.display='none'">
<div class="flowdeck-modal">
<div class="flowdeck-modal-header">
<h3>📁 New Folder</h3>
<button class="flowdeck-modal-close" onclick="document.getElementById('fd-global-folder-modal').style.display='none'">&times;</button>
</div>
<div class="flowdeck-modal-body">
<p>Enter a name for the new folder.</p>
<input class="flowdeck-modal-input" id="fd-global-folder-input" type="text" placeholder="Folder name" onkeydown="if(event.key==='Enter')window.FlowDeck._confirmCreateFolder()">
</div>
<div class="flowdeck-modal-footer">
<button class="flowdeck-modal-btn cancel" onclick="document.getElementById('fd-global-folder-modal').style.display='none'">Cancel</button>
<button class="flowdeck-modal-btn confirm" onclick="window.FlowDeck._confirmCreateFolder()">Create</button>
</div>
</div>
</div>
</body>
</html>