feat: outline SVG icons + version + workspaces in user menu
- _icons.html: 25 outline SVG icons (Feather/Lucide style, stroke=currentColor)
- base.html: toutes les icônes emoji remplacées par {{ fd_icon() }}
- Sidebar sections, empty states, user menu, footer links, context menu
- JS getSvgIcon() helper pour rendu dynamique
- User menu: ajout version (FlowDeck v4.0.3) + liste workspaces locaux
- Entre Switch workspace et Log out, avec ✓ sur le workspace actif
- dashboard.py: _sidebar_data enrichi (app_version, local_workspaces)
- CSS: .page-icon-svg, .um-version, .um-section-label, .um-item svg
- Dark + light theme compatible (stroke:currentColor)
- 143 tests passent
This commit is contained in:
+60
-32
@@ -140,6 +140,8 @@
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% from '_icons.html' import fd_icon %}
|
||||
|
||||
<!-- ═══════════ SIDEBAR Notion ═══════════ -->
|
||||
<aside class="sidebar"
|
||||
:class="{ collapsed: sidebarCollapsed, 'mobile-open': mobileSidebarOpen }"
|
||||
@@ -172,10 +174,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="um-sep"></div>
|
||||
<a href="/settings" class="um-item" @click="workspaceMenuOpen=false">⚙️ Settings</a>
|
||||
<a href="/workspaces" class="um-item" @click="workspaceMenuOpen=false">🔄 Switch workspace</a>
|
||||
<a href="/settings" class="um-item" @click="workspaceMenuOpen=false">{{ fd_icon("settings", 16) }} Settings</a>
|
||||
<a href="/workspaces" class="um-item" @click="workspaceMenuOpen=false">{{ fd_icon("refresh", 16) }} Switch workspace</a>
|
||||
{% if local_workspaces %}
|
||||
<div class="um-sep"></div>
|
||||
<a href="/auth/logout" class="um-item um-danger" @click="workspaceMenuOpen=false">🚪 Log out</a>
|
||||
<div class="um-section-label">Workspaces</div>
|
||||
{% for ws in local_workspaces %}
|
||||
<a href="/local-workspace?ws={{ ws.id }}" class="um-item" @click="workspaceMenuOpen=false"
|
||||
style="padding-left:24px;font-size:13px;">
|
||||
<span class="um-ws-dot" style="width:8px;height:8px;border-radius:50%;background:var(--accent);display:inline-block;margin-right:8px;flex-shrink:0;"></span>{{ ws.name }}
|
||||
{% if active_ws_name == ws.name %}
|
||||
<span style="margin-left:auto;color:var(--accent);">✓</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="um-sep"></div>
|
||||
<div class="um-version">FlowDeck v{{ app_version }}</div>
|
||||
<a href="/auth/logout" class="um-item um-danger" @click="workspaceMenuOpen=false">{{ fd_icon("log-out", 16) }} Log out</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -203,15 +219,15 @@
|
||||
<div class="sidebar-section-title">
|
||||
<span class="chevron" :class="{ open: sectionsOpen.workspace }" x-text="sectionsOpen.workspace ? '▼' : '▶'"></span>
|
||||
{% if has_active_workspace %}
|
||||
<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>
|
||||
<span><span style="vertical-align:sub;margin-right:4px;">{{ fd_icon("folder",16) }}</span>{{ active_ws_name if active_ws_name else "" }}{% if gitea_workspace or (workspace_key and "/" in workspace_key) %} {{ fd_icon("link",12) }}{% endif %}</span>
|
||||
{% else %}
|
||||
<span>📁 No workspace open</span>
|
||||
<span><span style="vertical-align:sub;margin-right:4px;">{{ fd_icon("folder",16) }}</span>No workspace open</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if has_active_workspace %}
|
||||
<div class="sidebar-section-actions">
|
||||
<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>
|
||||
<button class="section-action-btn" title="New Page" @click.stop="window.FlowDeck.createPage()">{{ fd_icon("file",16) }}</button>
|
||||
<button class="section-action-btn" title="New Folder" @click.stop="window.FlowDeck.showCreateFolderModal()">{{ fd_icon("folder",16) }}</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -222,13 +238,13 @@
|
||||
{% if workspace_pages %}
|
||||
{{ render_workspace_tree(workspace_pages) }}
|
||||
{% else %}
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon">📄</span><span class="page-name text-dim">No pages yet</span></li>
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("file",16) }}</span><span class="page-name text-dim">No pages yet</span></li>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon">📁</span><span class="page-name text-dim">Open a workspace to see your files</span></li>
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("folder",16) }}</span><span class="page-name text-dim">Open a workspace to see your files</span></li>
|
||||
{% endif %}
|
||||
<li class="sidebar-item" style="margin-top:4px;border-top:1px solid var(--border);padding-top:8px;" onclick="window.location='/workspaces'">
|
||||
<span class="page-icon">⚙</span><span class="page-name text-dim">Manage Workspaces</span>
|
||||
<span class="page-icon page-icon-svg">{{ fd_icon("settings",16) }}</span><span class="page-name text-dim">Manage Workspaces</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -243,7 +259,7 @@
|
||||
<span>🔗 Repository (Gitea)</span>
|
||||
</div>
|
||||
<div class="sidebar-section-actions">
|
||||
<button class="section-action-btn" title="Refresh" @click.stop="if(window._gwData)window._gwData.refreshTree()">🔄</button>
|
||||
<button class="section-action-btn" title="Refresh" @click.stop="if(window._gwData)window._gwData.refreshTree()">{{ fd_icon("refresh",16) }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-section-items" x-show="sectionsOpen.gitea" x-transition>
|
||||
@@ -268,7 +284,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-section-items" x-show="sectionsOpen.meetings" x-transition>
|
||||
<ul class="sidebar-items"><li class="sidebar-item empty-hint"><span class="page-icon">📅</span><span class="page-name text-dim">No meetings</span></li></ul>
|
||||
<ul class="sidebar-items"><li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("calendar",16) }}</span><span class="page-name text-dim">No meetings</span></li></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -280,7 +296,7 @@
|
||||
<span>Recents</span>
|
||||
</div>
|
||||
<div class="sidebar-section-actions">
|
||||
<a href="/library?tab=Recents" class="library-link-btn" title="View in Library" @click.stop>📚</a>
|
||||
<a href="/library?tab=Recents" class="library-link-btn" title="View in Library" @click.stop>{{ fd_icon("book",14) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-section-items" x-show="sectionsOpen.recents" x-transition>
|
||||
@@ -289,7 +305,7 @@
|
||||
{{ render_tree_item(page) }}
|
||||
{% endfor %}
|
||||
{% if not recent_pages %}
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon">📄</span><span class="page-name text-dim">No recent pages</span></li>
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("clock",16) }}</span><span class="page-name text-dim">No recent pages</span></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -303,7 +319,7 @@
|
||||
<span>Favorites</span>
|
||||
</div>
|
||||
<div class="sidebar-section-actions">
|
||||
<a href="/library?tab=Favorites" class="library-link-btn" title="View in Library" @click.stop>📚</a>
|
||||
<a href="/library?tab=Favorites" class="library-link-btn" title="View in Library" @click.stop>{{ fd_icon("book",14) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-section-items" x-show="sectionsOpen.favorites" x-transition>
|
||||
@@ -312,7 +328,7 @@
|
||||
{{ render_tree_item(page) }}
|
||||
{% endfor %}
|
||||
{% if not favorite_pages %}
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon">⭐</span><span class="page-name text-dim">No favorites yet</span></li>
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("star",16) }}</span><span class="page-name text-dim">No favorites yet</span></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -324,7 +340,7 @@
|
||||
<div class="sidebar-section-title"><span>Agents</span></div>
|
||||
</div>
|
||||
<div class="sidebar-section-items" x-show="sectionsOpen.agents" x-transition>
|
||||
<ul class="sidebar-items"><li class="sidebar-item empty-hint"><span class="page-icon">🤖</span><span class="page-name text-dim">No agents configured</span></li></ul>
|
||||
<ul class="sidebar-items"><li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("bot",16) }}</span><span class="page-name text-dim">No agents configured</span></li></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -336,7 +352,7 @@
|
||||
<span>Shared</span>
|
||||
</div>
|
||||
<div class="sidebar-section-actions">
|
||||
<a href="/library?tab=Shared" class="library-link-btn" title="View in Library" @click.stop>📚</a>
|
||||
<a href="/library?tab=Shared" class="library-link-btn" title="View in Library" @click.stop>{{ fd_icon("book",14) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-section-items" x-show="sectionsOpen.shared" x-transition>
|
||||
@@ -345,7 +361,7 @@
|
||||
{{ render_tree_item(page) }}
|
||||
{% endfor %}
|
||||
{% if not shared_pages %}
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon">👥</span><span class="page-name text-dim">No shared pages</span></li>
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("users",16) }}</span><span class="page-name text-dim">No shared pages</span></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -359,7 +375,7 @@
|
||||
<span>Published</span>
|
||||
</div>
|
||||
<div class="sidebar-section-actions">
|
||||
<a href="/library?tab=Published" class="library-link-btn" title="View in Library" @click.stop>📚</a>
|
||||
<a href="/library?tab=Published" class="library-link-btn" title="View in Library" @click.stop>{{ fd_icon("book",14) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-section-items" x-show="sectionsOpen.published" x-transition>
|
||||
@@ -368,7 +384,7 @@
|
||||
{{ render_tree_item(page) }}
|
||||
{% endfor %}
|
||||
{% if not published_pages %}
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon">🌐</span><span class="page-name text-dim">No published pages</span></li>
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("globe",16) }}</span><span class="page-name text-dim">No published pages</span></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -383,7 +399,7 @@
|
||||
<span>Private</span>
|
||||
</div>
|
||||
<div class="sidebar-section-actions">
|
||||
<a href="/library?tab=Private" class="library-link-btn" title="View in Library" @click.stop>📚</a>
|
||||
<a href="/library?tab=Private" class="library-link-btn" title="View in Library" @click.stop>{{ fd_icon("book",14) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-section-items" x-show="sectionsOpen.private" x-transition>
|
||||
@@ -392,7 +408,7 @@
|
||||
{{ render_tree_item(page) }}
|
||||
{% endfor %}
|
||||
{% if not private_pages %}
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon">📄</span><span class="page-name text-dim">No pages yet</span></li>
|
||||
<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">{{ fd_icon("file",16) }}</span><span class="page-name text-dim">No pages yet</span></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -403,16 +419,16 @@
|
||||
<!-- Bottom links (always visible, no section wrapper) -->
|
||||
<div class="sidebar-static-links" style="padding: 4px 12px;">
|
||||
<a href="/library" class="sidebar-item" style="text-decoration:none;color:inherit;display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:6px;cursor:pointer;transition:background 150ms;">
|
||||
<span class="page-icon">📚</span><span class="page-name" style="font-size:14px;">Library</span>
|
||||
<span class="page-icon page-icon-svg">{{ fd_icon("book",16) }}</span><span class="page-name" style="font-size:14px;display:flex;align-items:center;gap:4px;">Library</span>
|
||||
</a>
|
||||
<a href="/my-tasks" class="sidebar-item" style="text-decoration:none;color:inherit;display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:6px;cursor:pointer;transition:background 150ms;">
|
||||
<span class="page-icon">✅</span><span class="page-name" style="font-size:14px;">My Tasks</span>
|
||||
<span class="page-icon page-icon-svg">{{ fd_icon("check-square",16) }}</span><span class="page-name" style="font-size:14px;display:flex;align-items:center;gap:4px;">My Tasks</span>
|
||||
</a>
|
||||
<a href="/trash" class="sidebar-item" style="text-decoration:none;color:inherit;display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:6px;cursor:pointer;transition:background 150ms;">
|
||||
<span class="page-icon">🗑️</span><span class="page-name" style="font-size:14px;">Trash</span>
|
||||
<span class="page-icon page-icon-svg">{{ fd_icon("trash",16) }}</span><span class="page-name" style="font-size:14px;display:flex;align-items:center;gap:4px;">Trash</span>
|
||||
</a>
|
||||
<a href="/help" class="sidebar-item" style="text-decoration:none;color:inherit;display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:6px;cursor:pointer;transition:background 150ms;">
|
||||
<span class="page-icon">❓</span><span class="page-name" style="font-size:14px;">Help</span>
|
||||
<span class="page-icon page-icon-svg">{{ fd_icon("help-circle",16) }}</span><span class="page-name" style="font-size:14px;display:flex;align-items:center;gap:4px;">Help</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -420,7 +436,7 @@
|
||||
<div class="sidebar-footer">
|
||||
<div style="display:flex; gap:6px;">
|
||||
<button class="sidebar-footer-btn" style="flex:1; justify-content:center; background:var(--bg-hover); border-radius:20px;">
|
||||
<span style="font-size:14px;">💬</span> New chat
|
||||
<span style="margin-right:4px;">{{ fd_icon("message-square",14) }}</span>New chat
|
||||
<span class="shortcut">Ctrl+O</span>
|
||||
</button>
|
||||
{% if has_active_workspace %}
|
||||
@@ -581,6 +597,18 @@
|
||||
});
|
||||
})();
|
||||
|
||||
// ── Global SVG icon helper (used by JS-rendered elements) ─────
|
||||
function getSvgIcon(name, size) {
|
||||
size = size || 16;
|
||||
var svgs = {
|
||||
folder: '<svg width="'+size+'" height="'+size+'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>',
|
||||
file: '<svg width="'+size+'" height="'+size+'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>',
|
||||
star: '<svg width="'+size+'" height="'+size+'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>',
|
||||
trash: '<svg width="'+size+'" height="'+size+'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>'
|
||||
};
|
||||
return svgs[name] || '';
|
||||
}
|
||||
|
||||
// ── Global FlowDeck namespace ────────────────────────────────
|
||||
// All "New Page" and "New Folder" buttons across the app call
|
||||
// these functions for consistent behaviour.
|
||||
@@ -885,10 +913,10 @@
|
||||
const favList = document.querySelector('.sidebar-items[data-section="favorites"]');
|
||||
if (!favList) return;
|
||||
if (items.length === 0) {
|
||||
favList.innerHTML = '<li class="sidebar-item empty-hint"><span class="page-icon">⭐</span><span class="page-name text-dim">No favorites yet</span></li>';
|
||||
favList.innerHTML = '<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">'+getSvgIcon('star',16)+'</span><span class="page-name text-dim">No favorites yet</span></li>';
|
||||
} else {
|
||||
favList.innerHTML = items.map(function(item) {
|
||||
const icon = item.icon || '📄';
|
||||
var icon = item.icon || '<span class="page-icon-svg">'+getSvgIcon('file',14)+'</span>';
|
||||
const name = (item.title || 'Untitled');
|
||||
const safeName = name.replace(/'/g, "\\'").replace(/"/g, '"');
|
||||
const url = item.url || ('/pages/' + item.id);
|
||||
@@ -1154,13 +1182,13 @@
|
||||
}
|
||||
container.innerHTML = '';
|
||||
if (!this.giteaTree.length) {
|
||||
container.innerHTML = '<li class="sidebar-item empty-hint"><span class="page-icon">📄</span><span class="page-name text-dim">No files yet</span></li>';
|
||||
container.innerHTML = '<li class="sidebar-item empty-hint"><span class="page-icon page-icon-svg">'+getSvgIcon('file',16)+'</span><span class="page-name text-dim">No files yet</span></li>';
|
||||
return;
|
||||
}
|
||||
this.giteaTree.forEach(function(item) {
|
||||
var li = document.createElement('li');
|
||||
li.className = 'sidebar-item';
|
||||
var icon = item.type === 'dir' ? '📁' : '📄';
|
||||
var icon = item.type === 'dir' ? '<span class="page-icon-svg">'+getSvgIcon('folder',14)+'</span>' : '<span class="page-icon-svg">'+getSvgIcon('file',14)+'</span>';
|
||||
li.innerHTML = '<span class="page-icon">' + icon + '</span><span class="page-name">' + item.name + '</span>';
|
||||
container.appendChild(li);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user