feat: layout mobile, abas múltiplas e categorias em árvore

- Responsivo ≤760px: sidebar vira gaveta com backdrop, painel de versões em tela
  cheia, toolbar/menu rolam sem rótulos, alvos de toque, dvh + safe-area, fonte
  do editor ≥16px (anti-zoom iOS)
- Abas: várias notas abertas com estado sujo por aba, ✕ com confirmação,
  clique do meio, Alt+W / Alt+←/→ / Alt+1..9; abas e rascunhos sobrevivem à
  sessão via localStorage
- Categorias: modelo vira árvore (categories.json) com notas em múltiplas
  categorias; explorador no sidebar com expandir/renomear/mover/excluir
  (sem ciclos, GC de vínculos) e seletor multi-marca no editor; filtro por
  pasta recursivo por padrão; migração automática do campo único legado
- API: POST/PUT/DELETE /api/categories; notas aceitam categories: [ids]
- Testes: store (árvore, ciclo, migração, GC) e api (CRUD de categorias)
This commit is contained in:
lgcosta committed 2026-11-04 11:54:15 -03:00
1 parent 9bc5de4c56
commit 7e89207b70
10 files changed
+1737 -489

No files matched your search

+23 -7
View File
@@ -2,7 +2,7 @@
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="color-scheme" content="light">
<title>Notas — editor estilo Mousepad</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='2' fill='%233584e4'/><path d='M3 4h10M3 7h10M3 10h7' stroke='white' stroke-width='1.5' stroke-linecap='round'/></svg>">
@@ -78,6 +78,13 @@
</div>
</div>
<!-- Menu de contexto das categorias (Nova subpasta / Renomear / Excluir) -->
<ul id="ctx-menu" class="ctx-menu hidden">
<li data-ctx="sub">Nova subpasta…</li>
<li data-ctx="rename">Renomear…</li>
<li data-ctx="delete" class="ctx-danger">Excluir…</li>
</ul>
<div id="app">
<!-- Barra de menus (estilo Mousepad) -->
@@ -93,6 +100,7 @@
<li data-action="save">Salvar<span class="accel">Ctrl+S</span></li>
<li data-action="export">Baixar arquivo (.txt/.md/.html)…</li>
<li class="menu-sep"></li>
<li data-action="close-tab">Fechar aba<span class="accel">Alt+W</span></li>
<li data-action="delete-note">Excluir esta nota…</li>
</ul>
</div>
@@ -177,11 +185,11 @@
<input id="search-input" type="search" placeholder="Buscar notas…" autocomplete="off">
</div>
<div class="side-section">
<label class="side-label" for="category-select">Categoria</label>
<select id="category-select">
<option value="*">Todas as categorias</option>
<option value="">Sem categoria</option>
</select>
<div class="side-listhead">
<span class="side-label">Categorias</span>
<button type="button" class="btn btn-small" id="btn-new-cat" title="Nova categoria raiz">+ pasta</button>
</div>
<div id="cat-tree"></div>
</div>
<div class="side-section">
<span class="side-label">Tags</span>
@@ -197,9 +205,15 @@
</aside>
<section id="editor-pane">
<!-- Abas de arquivos abertos -->
<div id="tabbar" role="tablist">
<div id="tabs"></div>
<button type="button" class="btn btn-icon tab-new" id="btn-tab-new" title="Nova aba (Ctrl+N)">✚</button>
</div>
<div id="note-meta">
<input id="note-title" type="text" placeholder="Título da nota" maxlength="200" autocomplete="off">
<input id="note-category" type="text" placeholder="categoria (opcional)" maxlength="100" autocomplete="off" class="meta-cat">
<button type="button" class="btn" id="btn-cats" title="Atribuir categorias">🗀 Sem categoria</button>
<div id="cat-picker" class="cat-picker hidden"></div>
</div>
<div id="editor-host">
<div id="editor-wrap">
@@ -249,6 +263,8 @@
<div id="toast" class="hidden"></div>
</div>
<div id="backdrop" class="hidden"></div>
<script src="/js/app.js"></script>
</body>
</html>