var _regGiteaWs = () => { Alpine.data('giteaWorkspace', () => { const params = new URLSearchParams(window.location.search); const owner = params.get('owner') || ''; const repo = params.get('repo') || ''; // Forge is carried by the page route (/gitea-workspace vs /github-workspace); // the sidebar cookie prefix follows it so refresh APIs resolve the mirror. const forge = (window.location.pathname || '').indexOf('/github-workspace') === 0 ? 'github' : 'gitea'; return { owner, repo, forge, apiBase() { return '/api/' + this.forge + '/projects/' + this.owner + '/' + this.repo; }, // ── Library-style toolbar (same as /local-workspace) ── viewMode: localStorage.getItem('fd_default_view') || 'tree', viewMenuOpen: false, sortOpen: false, filterOpen: false, searchOpen: false, sortBy: '', filterType: '', searchQuery: '', showFile: false, showEditor: false, showPrivate: false, privatePages: [], editingPrivate: null, editingPrivateTitle: '', editingPrivateContent: '', filePath: '', fileContent: '', fileSize: 0, fileSha: '', fileLoading: false, fileLanguage: 'plaintext', fileLanguageLabel: 'Plain Text', editContent: '', commitMessage: 'Update via FlowDeck', commitHistory: JSON.parse(localStorage.getItem('fd_commit_msgs') || '[]'), // File tree browser treeItems: [], sortedTreeItems: [], filteredTreeItems: [], treeLoading: false, treeError: false, treeErrorText: '', folderStack: [], currentPath: '', // Context menu gwCtx: { visible: false, x: 0, y: 0, item: null }, _sortTree() { var items = [...this.treeItems]; var by = this.sortBy || ''; var typeRank = function(t) { return (t === 'folder' || t === 'dir') ? 0 : 1; }; items.sort(function(a, b) { if (by === 'name-desc') return (b.name || '').localeCompare(a.name || ''); if (by === 'type') { var d = typeRank(a.type) - typeRank(b.type); return d !== 0 ? d : (a.name || '').localeCompare(b.name || ''); } // default name-asc: folders first var td = typeRank(a.type) - typeRank(b.type); if (td !== 0) return td; return (a.name || '').localeCompare(b.name || ''); }); this.sortedTreeItems = items; this.doFilter(); }, toggleViewMenu() { this.viewMenuOpen = !this.viewMenuOpen; this.sortOpen = false; this.filterOpen = false; }, toggleSortMenu() { this.sortOpen = !this.sortOpen; this.viewMenuOpen = false; this.filterOpen = false; }, toggleFilterMenu() { this.filterOpen = !this.filterOpen; this.viewMenuOpen = false; this.sortOpen = false; }, toggleSearch() { this.searchOpen = !this.searchOpen; if (!this.searchOpen) { this.searchQuery = ''; this.doFilter(); } }, doSort() { this._sortTree(); }, filterByType(t) { this.filterType = (this.filterType === t ? '' : t); this.doFilter(); }, doFilter() { var q = (this.searchQuery || '').toLowerCase(); var ft = this.filterType || ''; this.filteredTreeItems = (this.sortedTreeItems || []).filter(function(it) { if (ft === 'folder' && it.type !== 'folder') return false; if (ft && ft !== 'folder') { var ext = (it.name || '').split('.').pop().toLowerCase(); var kind = (/^(png|jpe?g|gif|svg|webp|ico|bmp)$/.test(ext)) ? 'image' : (/^(pdf)$/.test(ext)) ? 'pdf' : (/^(js|jsx|ts|tsx|py|rb|rs|go|java|kt|c|cpp|h|cs|php|swift|r|lua|dart|sh|sql)$/.test(ext)) ? 'code' : (/^(md|txt|json|ya?ml|toml|ini|csv)$/.test(ext)) ? 'text' : 'page'; if (kind !== ft && !(ft === 'page' && it.type === 'file' && kind === 'page')) { // 'page' filter = generic files; keep kind match only if (ft === 'page' && it.type !== 'file') return false; else if (ft !== 'page') return false; } } if (q && (it.name || '').toLowerCase().indexOf(q) < 0 && (it.path || '').toLowerCase().indexOf(q) < 0) return false; return true; }); }, createFolderAtRoot() { var name = prompt('Folder name:'); if (!name || !name.trim()) return; this.createNewFolder(name.trim(), this.currentPath || ''); }, async createNewFolder(name, basePath) { var path = (basePath ? basePath.replace(/\/$/, '') + '/' : '') + name + '/.gitkeep'; try { var r = await fetch(this.apiBase() + '/file', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCsrf() }, body: JSON.stringify({ path: path, content: '', message: 'Create folder ' + name + ' via FlowDeck' }) }); if (r.ok) await this.refreshTree(); else window.showToast('Create folder failed', 'error'); } catch (e) { window.showToast('Create folder failed', 'error'); } }, init() { // Expose globally for header to access window._gwData = this; // Set cookie for sidebar (forge-prefixed so Workspace mirror resolves) document.cookie = 'flowdeck_workspace=' + this.forge + ':' + this.owner + ':' + this.repo + ';path=/;SameSite=Lax'; // Load sidebar tree (into gitea section) this.loadSidebarTree(); // Load main content tree this.loadMainTree(''); // Listen for sidebar clicks on Gitea items this.setupSidebarClicks(); }, async loadMainTree(path) { this.treeLoading = true; this.treeError = false; this.treeErrorText = ''; this.currentPath = path; var forgeLabel = this.forge === 'github' ? 'GitHub' : 'Gitea'; try { var url = this.apiBase() + '/tree'; if (path) url += '?path=' + encodeURIComponent(path); var r = await fetch(url); if (!r.ok) { this.treeItems = []; this.sortedTreeItems = []; this.filteredTreeItems = []; this.treeError = true; this.treeErrorText = r.status === 401 ? 'Connect your ' + forgeLabel + ' account (Settings → Integrations) to browse this repository.' : 'Repository API answered HTTP ' + r.status + '.'; return; } var d = await r.json(); this.treeItems = d.tree || []; this._sortTree(); } catch { this.treeItems = []; this.sortedTreeItems = []; this.filteredTreeItems = []; this.treeError = true; this.treeErrorText = 'Could not reach the repository — the server may be restarting. Retry in a moment.'; } finally { this.treeLoading = false; } }, drillDown(path) { this.folderStack.push(path.split('/').pop()); this.loadMainTree(path); }, navigateToFolder(idx) { // Truncate stack and rebuild path this.folderStack = this.folderStack.slice(0, idx + 1); var path = this.folderStack.join('/'); this.loadMainTree(path); }, navigateToRoot() { this.folderStack = []; this.loadMainTree(''); }, openGwContext(ev, item) { this.gwCtx = { visible: true, x: ev.clientX, y: ev.clientY, item: item }; }, gwRename() { this.gwCtx.visible = false; var item = this.gwCtx.item; if (!item) return; var newName = prompt('Rename:', item.name); if (!newName || !newName.trim() || newName.trim() === item.name) return; var self = this; var oldPath = item.path; var newPath = oldPath.replace(/[^/]+$/, newName.trim()); fetch(this.apiBase() + '/file', { method: 'PUT', headers: {'X-CSRF-Token': getCsrf(), 'Content-Type': 'application/json' }, body: JSON.stringify({ path: oldPath, new_path: newPath, message: 'Rename ' + oldPath + ' to ' + newPath }) }).then(function(r){ return r.json(); }) .then(function(d){ if (d.status === 'ok') { self.refreshTree(); } else { window.showToast('Rename failed', 'error'); } }) .catch(function(){ window.showToast('Rename failed', 'error'); }); }, gwDelete() { this.gwCtx.visible = false; var item = this.gwCtx.item; if (!item) return; if (!confirm('Delete ' + item.name + '?')) return; var self = this; fetch(this.apiBase() + '/file?path=' + encodeURIComponent(item.path) + '&sha=' + (item.sha || '') + '&message=Delete ' + item.path, {headers: {'X-CSRF-Token': getCsrf()}, method: 'DELETE' }).then(function(r){ if (r.ok) { self.refreshTree(); } else { window.showToast('Delete failed', 'error'); } }).catch(function(){ window.showToast('Delete failed', 'error'); }); }, _sidebarHint(msg) { var container = document.getElementById('sidebar-gitea-items'); if (container) container.innerHTML = ''; }, async loadSidebarTree() { var forgeLabel = this.forge === 'github' ? 'GitHub' : 'Gitea'; try { var r = await fetch(this.apiBase() + '/tree'); if (!r.ok) { // API error (e.g. forge account not linked) — hint instead of // leaving "Loading..." stuck in the Repository section. this._sidebarHint(r.status === 401 ? 'Connect ' + forgeLabel + ' to browse files' : 'Could not load repository tree (HTTP ' + r.status + ')'); return; } var d = await r.json(); var items = d.tree || []; var container = document.getElementById('sidebar-gitea-items'); if (!container) return; // Ensure gitea section is visible if (window.appState && window.appState.sectionsOpen && !window.appState.sectionsOpen.gitea) { window.appState.sectionsOpen.gitea = true; } container.innerHTML = ''; // Same DOM contract as Workspace-local (_workspace_tree_macro): // .sidebar-item.ws-tree-item + chevron + .page-icon/.page-name. var html = ''; var sorted = [...items].sort(function(a, b) { var at = (a.type === 'folder' || a.type === 'dir') ? 0 : 1; var bt = (b.type === 'folder' || b.type === 'dir') ? 0 : 1; if (at !== bt) return at - bt; return (a.name || '').localeCompare(b.name || ''); }); for (var i = 0; i < sorted.length; i++) { var item = sorted[i]; var isF = item.type === 'folder' || item.type === 'dir'; var safe = (item.name || '').replace(/'/g, "\\'"); var chev = isF ? '' : ''; var icon = isF ? getSvgIcon('folder',14) : getSvgIcon('file',14); var label = isF ? '' + item.name + '' : '' + item.name + ''; html += ''; } // Add Private Pages link html += '
  • '; html += ''; container.innerHTML = html; // Attach sidebar clicks once (loadSidebarTree runs on every refresh — // re-attaching would stack duplicate global listeners). this.setupSidebarClicks(); } catch(e) { // Network failure (server restarting, offline…) — hint in place of // "Loading..." so the Repository section never looks broken. this._sidebarHint('Could not reach the repository — retry in a moment'); } }, setupSidebarClicks() { if (this._sidebarClicksBound) return; this._sidebarClicksBound = true; var self = this; document.addEventListener('click', function(e) { var el = e.target.closest('.sidebar-item[data-gitea-path]'); if (!el) return; var path = el.getAttribute('data-gitea-path'); var type = el.getAttribute('data-gitea-type'); // If clicking the checkbox, let its own handler deal with selection if (e.target.classList.contains('gitea-checkbox')) return; // If clicking the inline rename input, don't navigate if (e.target.classList.contains('inline-rename-input')) return; // If Shift or Ctrl/Meta is pressed, use multi-selection if (e.shiftKey || e.ctrlKey || e.metaKey) { e.preventDefault(); e.stopPropagation(); self.selectItem(path, el, e); return; } // Normal click: navigate (open file/folder) e.preventDefault(); e.stopPropagation(); // Update visual "active" state document.querySelectorAll('.sidebar-item.active').forEach(function(si) { si.classList.remove('active'); }); el.classList.add('active'); if (type === 'folder') { self.loadSubdir(path, el); } else { self.openFile(path, el.getAttribute('data-gitea-sha')); } }); // Listen for custom delete event on gitea sidebar items document.addEventListener('gitea-delete', function(e) { var el = e.target; var path = el.getAttribute('data-gitea-path'); if (!path) return; if (!confirm('Delete ' + path + ' from ' + self.owner + '/' + self.repo + '?')) return; var sha = el.getAttribute('data-gitea-sha') || ''; fetch(self.apiBase() + '/file?path=' + encodeURIComponent(path) + '&sha=' + encodeURIComponent(sha) + '&message=' + encodeURIComponent('Delete via FlowDeck'), {headers: {'X-CSRF-Token': getCsrf()}, method: 'DELETE', }).then(function(r) { if (r.ok) self.refreshTree(); }).catch(function() {}); }); }, async loadSubdir(path, el) { var self = this; // Check if already loaded var ul = el.querySelector('ul'); if (ul) { ul.style.display = ul.style.display === 'none' ? '' : 'none'; return; } try { var r = await fetch(this.apiBase() + '/tree?path=' + encodeURIComponent(path)); if (!r.ok) return; var d = await r.json(); var children = d.tree || []; ul = document.createElement('ul'); ul.className = 'sidebar-items'; ul.style.cssText = 'list-style:none;margin:0;padding:0;'; var depth = parseInt(el.getAttribute('data-depth') || '0', 10) + 1; children.forEach(function(child) { var isF = child.type === 'folder' || child.type === 'dir'; var icon = isF ? getSvgIcon('folder',14) : getSvgIcon('file',14); var li = document.createElement('li'); li.className = 'sidebar-item ws-tree-item'; li.setAttribute('data-gitea-path', child.path); li.setAttribute('data-gitea-type', isF ? 'folder' : 'file'); li.setAttribute('data-gitea-sha', child.sha || ''); li.setAttribute('data-is-folder', isF ? 'true' : 'false'); li.setAttribute('data-depth', String(depth)); li.setAttribute('data-name', (child.name || '').replace(/'/g, "\\'")); li.style.cssText = 'padding-left:' + (4 + depth * 12) + 'px;'; // Checkbox var cb = document.createElement('input'); cb.type = 'checkbox'; cb.className = 'gitea-checkbox'; cb.style.cssText = 'flex-shrink:0;margin:0;display:none;'; cb.addEventListener('click', function(ev) { ev.stopPropagation(); self.selectItem(child.path, li, ev); }); li.appendChild(cb); // Chevron (folders) — same as workspace-local if (isF) { var chev = document.createElement('button'); chev.className = 'tree-chevron'; chev.setAttribute('data-repo-toggle', child.path); chev.title = 'Toggle folder'; chev.textContent = '▶'; li.appendChild(chev); } else { var sp = document.createElement('span'); sp.style.cssText = 'width:12px;flex-shrink:0;'; li.appendChild(sp); } // Icon var iconSpan = document.createElement('span'); iconSpan.className = 'page-icon'; iconSpan.innerHTML = icon; li.appendChild(iconSpan); // Name var nameSpan = document.createElement('span'); nameSpan.className = 'page-name' + (isF ? ' tree-folder-link' : ''); nameSpan.textContent = child.name; if (isF) nameSpan.title = 'Open folder'; nameSpan.addEventListener('dblclick', function(ev) { ev.preventDefault(); ev.stopPropagation(); self.startInlineRename(nameSpan, child.path, li); }); li.appendChild(nameSpan); ul.appendChild(li); }); el.appendChild(ul); var chevBtn = el.querySelector('[data-repo-toggle]'); if (chevBtn) chevBtn.classList.add('open'); } catch { /* volontaire */ } }, async openFile(path, sha) { this.filePath = path; this.fileSha = sha || ''; this.showEditor = false; this.showFile = true; this.fileLoading = true; this.fileLanguage = this.getLanguage(path); try { var r = await fetch(this.apiBase() + '/file?path=' + encodeURIComponent(path)); if (r.ok) { var d = await r.json(); this.fileContent = d.content || ''; this.fileSize = d.content ? d.content.length : 0; } else { this.fileContent = '[Error loading file]'; } } catch { this.fileContent = '[Error loading file]'; } this.fileLoading = false; // Highlight after Alpine renders (FDHighlight covers ~230 file types; // legacy Prism kept as a last-resort fallback). var self = this; this.$nextTick(function() { var block = self.$refs.codeBlock; if (!block || !block.textContent) return; if (window.FDHighlight) { try { FDHighlight.highlightBlock(block, self.fileLanguage); self.fileLanguageLabel = FDHighlight.labelFor(self.fileLanguage); } catch (e) { /* plain text */ } } else if (typeof Prism !== 'undefined') { Prism.highlightElement(block); } }); }, getLanguage(path) { if (window.FDHighlight) { try { return FDHighlight.langForFile(path); } catch (e) { /* fall through */ } } var ext = (path || '').split('.').pop().toLowerCase(); var map = { js:'javascript', jsx:'javascript', ts:'typescript', tsx:'typescript', py:'python', rb:'ruby', rs:'rust', go:'go', java:'java', kt:'kotlin', c:'c', cpp:'c', h:'c', hpp:'c', cs:'csharp', html:'markup', htm:'markup', xml:'markup', svg:'markup', css:'css', scss:'css', json:'json', yaml:'yaml', yml:'yaml', toml:'toml', ini:'ini', md:'markdown', sql:'sql', sh:'bash', bash:'bash', zsh:'bash', php:'php', swift:'swift', r:'r', lua:'lua', dart:'dart', dockerfile:'docker', makefile:'makefile', cmake:'cmake', }; return map[ext] || 'text'; }, openEditor() { this.editContent = this.fileContent; this.showEditor = true; }, async saveFile() { if (!this.filePath) return; try { var r = await fetch(this.apiBase() + '/file', { method: 'PUT', headers: {'Content-Type': 'application/json', 'X-CSRF-Token': this.getCsrfToken()}, body: JSON.stringify({ path: this.filePath, content: this.editContent, message: this.commitMessage, sha: this.fileSha, }) }); if (r.ok) { this.fileContent = this.editContent; this.showEditor = false; if (!this.commitHistory.includes(this.commitMessage)) { this.commitHistory.unshift(this.commitMessage); if (this.commitHistory.length > 10) this.commitHistory.pop(); localStorage.setItem('fd_commit_msgs', JSON.stringify(this.commitHistory)); } } else { var d = await r.json(); window.showToast('Failed to save: ' + (d.error || 'Unknown error'), 'error'); } } catch { window.showToast('Network error', 'error'); } }, async deleteCurrentFile() { if (!this.filePath) return; if (!confirm('Delete ' + this.filePath + ' from ' + this.owner + '/' + this.repo + '?')) return; try { var r = await fetch(this.apiBase() + '/file?path=' + encodeURIComponent(this.filePath) + '&sha=' + encodeURIComponent(this.fileSha) + '&message=' + encodeURIComponent('Delete via FlowDeck'), {headers: {'X-CSRF-Token': getCsrf()}, method: 'DELETE', }); if (r.ok) { this.showFile = false; this.filePath = ''; await this.refreshTree(); } } catch { /* volontaire */ } }, async refreshTree() { // Reload main tree and sidebar tree without full page reload this.loadMainTree(this.currentPath); this.loadSidebarTree(); }, // ── A20 ph3 : new Date / x-html interdits sous build CSP ── closePrivateEdit() { this.editingPrivate = null; this.editingPrivateTitle = ''; this.editingPrivateContent = ''; }, fmtGwDate(pp) { return pp.updated_at ? new Date(pp.updated_at + 'Z').toLocaleString() : ''; }, bindGwIcon(el, item) { Alpine.effect(() => { el.innerHTML = item.type === 'folder' ? getSvgIcon('folder', 16) : getSvgIcon('file', 16); }); }, formatSize(bytes) { if (!bytes) return ''; if (bytes < 1024) return bytes + ' B'; if (bytes < 1048576) return (bytes/1024).toFixed(1) + ' KB'; return (bytes/1048576).toFixed(1) + ' MB'; }, // ── Private Pages ── async loadPrivatePages() { try { var r = await fetch(this.apiBase() + '/private-pages'); if (r.ok) { var d = await r.json(); this.privatePages = d.pages || []; } } catch { /* volontaire */ } }, newPrivate() { this.editingPrivate = 'new'; this.editingPrivateTitle = ''; this.editingPrivateContent = ''; }, async openPrivate(id) { try { var r = await fetch(this.apiBase() + '/private-pages/' + id); if (r.ok) { var d = await r.json(); this.editingPrivate = id; this.editingPrivateTitle = d.page.title; this.editingPrivateContent = d.page.content; } } catch { /* volontaire */ } }, async savePrivate() { var url = this.apiBase() + '/private-pages'; var method = 'POST'; if (this.editingPrivate !== 'new') { url += '/' + this.editingPrivate; method = 'PUT'; } try { var r = await fetch(url, { method, headers: {'Content-Type':'application/json','X-CSRF-Token': this.getCsrfToken ? this.getCsrfToken() : ''}, body: JSON.stringify({title: this.editingPrivateTitle, content: this.editingPrivateContent}) }); if (r.ok) { this.editingPrivate = null; this.editingPrivateTitle = ''; this.editingPrivateContent = ''; await this.loadPrivatePages(); } } catch { /* volontaire */ } }, // Create new file showNewFile: false, newFilePath: '', newFileContent: '', newFileMsg: 'Create via FlowDeck', async createNewFile() { if (!this.newFilePath.trim()) return; try { var r = await fetch(this.apiBase() + '/file', { method: 'PUT', headers: {'Content-Type':'application/json', 'X-CSRF-Token': this.getCsrfToken()}, body: JSON.stringify({path: this.newFilePath.trim(), content: this.newFileContent, message: this.newFileMsg || 'Create via FlowDeck'}) }); if (r.ok) { this.showNewFile = false; this.newFilePath = ''; this.newFileContent = ''; this.newFileMsg = 'Create via FlowDeck'; await this.loadSidebarTree(); } else { var d = await r.json(); window.showToast('Failed: ' + (d.error || d.detail || 'Unknown error'), 'error'); } } catch(e) { window.showToast('Error: ' + e.message, 'error'); } }, // Upload files triggerUpload() { document.getElementById('gitea-upload-input').click(); }, async doUpload(ev) { var files = ev.target.files; if (!files.length) return; for (var i = 0; i < files.length; i++) { var form = new FormData(); form.append('file', files[i]); form.append('message', 'Upload ' + files[i].name + ' via FlowDeck'); try { var r = await fetch(this.apiBase() + '/upload', { method: 'POST', headers: {'X-CSRF-Token': this.getCsrfToken()}, body: form }); if (!r.ok) { var d = await r.json(); console.error('Upload failed:', d.error); } } catch(e) { console.error('Upload error:', e); } } await this.loadSidebarTree(); ev.target.value = ''; }, async deletePrivate(id) { if (!confirm('Delete this private page?')) return; try { var r = await fetch(this.apiBase() + '/private-pages/' + id, { method: 'DELETE', headers: {'X-CSRF-Token': this.getCsrfToken ? this.getCsrfToken() : ''} }); if (r.ok) await this.loadPrivatePages(); } catch { /* volontaire */ } }, getCsrfToken() { return document.cookie.split('; ').find(function(c) { return c.startsWith('csrf_token='); })?.split('=')[1] || ''; }, // ── Multi-selection ── multiSelect: false, selectedPaths: [], lastClickedPath: null, toggleMultiSelect() { this.multiSelect = !this.multiSelect; var cbs = document.querySelectorAll('.gitea-checkbox'); var self = this; cbs.forEach(function(cb) { cb.style.display = self.multiSelect ? '' : 'none'; }); if (!this.multiSelect) { // Clear selection when leaving multi-select mode cbs.forEach(function(cb) { cb.checked = false; }); this.selectedPaths = []; this.lastClickedPath = null; // Remove selected class document.querySelectorAll('.sidebar-item.gitea-selected').forEach(function(el) { el.classList.remove('gitea-selected'); }); } }, selectItem(path, li, ev) { if (ev.shiftKey && this.lastClickedPath) { // Range select var all = Array.from(document.querySelectorAll('.sidebar-item[data-gitea-path]')); var startIdx = all.findIndex(function(el) { return el.getAttribute('data-gitea-path') === this.lastClickedPath; }.bind(this)); var endIdx = all.findIndex(function(el) { return el.getAttribute('data-gitea-path') === path; }.bind(this)); if (startIdx >= 0 && endIdx >= 0) { var lo = Math.min(startIdx, endIdx); var hi = Math.max(startIdx, endIdx); this.selectedPaths = []; for (var i = lo; i <= hi; i++) { this.selectedPaths.push(all[i].getAttribute('data-gitea-path')); all[i].classList.add('gitea-selected'); var cb = all[i].querySelector('.gitea-checkbox'); if (cb) cb.checked = true; } } this.multiSelect = true; this.toggleMultiSelect(); // ensure checkboxes are visible } else if (ev.ctrlKey || ev.metaKey) { // Toggle single var idx = this.selectedPaths.indexOf(path); if (idx >= 0) { this.selectedPaths.splice(idx, 1); li.classList.remove('gitea-selected'); var cb = li.querySelector('.gitea-checkbox'); if (cb) cb.checked = false; } else { this.selectedPaths.push(path); li.classList.add('gitea-selected'); var cb = li.querySelector('.gitea-checkbox'); if (cb) cb.checked = true; } this.multiSelect = this.selectedPaths.length > 0; this.toggleMultiSelect(); } else { // Normal select — clear multi-selection this.selectedPaths = [path]; this.lastClickedPath = path; document.querySelectorAll('.sidebar-item.gitea-selected').forEach(function(el) { el.classList.remove('gitea-selected'); }); li.classList.add('gitea-selected', 'active'); var cb = li.querySelector('.gitea-checkbox'); if (cb) cb.checked = true; } }, isSelected(path) { return this.selectedPaths.indexOf(path) >= 0; }, // ── Inline rename ── startInlineRename(nameSpan, path, li) { var originalName = nameSpan.textContent; var input = document.createElement('input'); input.type = 'text'; input.value = originalName; input.className = 'inline-rename-input'; input.style.cssText = 'flex:1;min-width:0;padding:2px 6px;background:var(--bg-tertiary);border:1px solid var(--accent);border-radius:4px;color:var(--text);font-size:13px;outline:none;'; nameSpan.replaceWith(input); input.focus(); input.select(); var self = this; var finish = function() { self.finishInlineRename(input, originalName, path, li); }; var cancel = function() { var span = document.createElement('span'); span.className = 'page-name'; span.textContent = originalName; input.replaceWith(span); }; input.addEventListener('blur', finish); input.addEventListener('keydown', function(e) { if (e.key === 'Enter') finish(); if (e.key === 'Escape') { e.stopPropagation(); cancel(); } }); }, finishInlineRename(input, originalName, path, li) { if (input._renaming) return; // guard against double-fire input._renaming = true; var newName = input.value.trim(); if (!newName || newName === originalName) { var span = document.createElement('span'); span.className = 'page-name'; span.textContent = originalName; input.replaceWith(span); return; } var self = this; // Construct new path by replacing the last segment var parts = path.split('/'); parts.pop(); var newPath = (parts.length > 0 ? parts.join('/') + '/' : '') + newName; fetch(this.apiBase() + '/rename', { method: 'POST', headers: {'Content-Type': 'application/json', 'X-CSRF-Token': this.getCsrfToken()}, body: JSON.stringify({old_path: path, new_path: newPath, new_name: newName}) }).then(function(r) { if (r.ok) { self.refreshTree(); } else { var span = document.createElement('span'); span.className = 'page-name'; span.textContent = originalName; input.replaceWith(span); window.showToast('Rename failed', 'error'); } }).catch(function() { var span = document.createElement('span'); span.className = 'page-name'; span.textContent = originalName; input.replaceWith(span); }); }, }; }); }; if (window.Alpine) { _regGiteaWs(); fdRefreshXData('giteaWorkspace'); } else { document.addEventListener('alpine:init', _regGiteaWs); }