v2.8.0 WIP: create/upload files + commit history + labels sync
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

- POST /api/gitea/projects/{owner}/{repo}/upload (binary upload)
- get_file_commits() in GiteaClient
- GET /api/gitea/projects/{owner}/{repo}/commits (per-file history)
- POST .../sync-labels (Gitea labels → FlowDeck tags)
- New file form + upload button in gitea_workspace.html topbar
This commit is contained in:
2026-07-13 22:33:47 -04:00
parent f25c8ebaf0
commit 6f15ad3ad4
9 changed files with 689 additions and 27 deletions
+67
View File
@@ -529,6 +529,8 @@
contextMenu: { visible: false, x: 0, y: 0, pageId: null, pageName: '' },
contextActions: {},
showNewPageMenu: false,
selectedItems: [],
lastClickedIndex: -1,
// ── Workspace tree context menu ──
wsCtx: { visible: false, x: 0, y: 0, id: null, name: '', isFolder: false },
@@ -1027,6 +1029,71 @@
window.addEventListener('pageshow', function(e) {
if (e.persisted) applySavedTheme();
});
// ═══════════ GLOBAL KEYBOARD SHORTCUTS ═══════════
document.addEventListener('keydown', function(e) {
var tag = (document.activeElement && document.activeElement.tagName) || '';
var editable = tag === 'INPUT' || tag === 'TEXTAREA' || (document.activeElement && document.activeElement.isContentEditable);
// Don't intercept when typing in inputs unless it's Escape
if (editable && e.key !== 'Escape') return;
// Don't intercept when a modal prompt is open
if (document.querySelector('.modal-overlay:not([style*="display:none"])')) return;
var appEl = document.querySelector('[x-data]');
var data = appEl && appEl.__x ? appEl.__x.$data : null;
if (e.ctrlKey && e.key === 'n') {
e.preventDefault();
if (data && data.newPageInWorkspace) data.newPageInWorkspace();
else if (data && data.newPage) data.newPage('Private');
}
else if (e.key === 'Enter' && !editable) {
var activeSidebar = document.querySelector('.sidebar-item.active');
if (activeSidebar) {
e.preventDefault();
activeSidebar.click();
}
}
else if (e.key === 'F2') {
e.preventDefault();
// Trigger rename: dispatch dblclick on active item's name span
var nameSpan = document.querySelector('.sidebar-item.active .page-name');
if (nameSpan) {
nameSpan.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }));
} else if (data && data.wsCtx && data.wsCtx.id) {
// Workspace tree item: trigger rename via context menu
data.wsCtxAction('rename');
}
}
else if (e.key === 'Delete' && !editable) {
e.preventDefault();
var delItem = document.querySelector('.sidebar-item.active');
if (delItem && delItem.dataset.pageId && data) {
data.contextMenu.pageId = delItem.dataset.pageId;
data.contextMenu.pageName = delItem.dataset.pageName || delItem.querySelector('.page-name')?.textContent || '';
data.contextAction('delete');
} else if (delItem && delItem.dataset.giteaPath) {
// Gitea file: dispatch custom event
delItem.dispatchEvent(new CustomEvent('gitea-delete', { bubbles: true }));
}
}
else if (e.key === 'Escape') {
// Close card modal
if (typeof closeModal === 'function') closeModal();
// Close context menus
if (data) {
if (data.contextMenu) data.contextMenu.visible = false;
if (data.wsCtx) data.wsCtx.visible = false;
if (data.showNewPageMenu) data.showNewPageMenu = false;
}
}
else if (e.ctrlKey && e.key === 's') {
e.preventDefault();
// Try to find and click a save/commit button
var saveBtn = document.querySelector('.btn-primary, button:contains("Save"), button:contains("Commit")');
if (saveBtn) saveBtn.click();
}
});
</script>
<script src="/static/js/app.js?v=2.4.6" defer></script>
{% block scripts %}{% endblock %}