Files
notas-online/web/index.html
T
lgcosta 7e89207b70 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)
2026-11-04 11:54:15 -03:00

271 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<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>">
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
<!-- Diálogo de login (token) -->
<div id="login-overlay" class="overlay hidden">
<form id="login-form" class="dialog" autocomplete="off">
<h1>Notas</h1>
<p class="dialog-hint">Digite o token de acesso (variável <code>NOTES_TOKEN</code> do servidor).</p>
<input id="login-token" type="password" placeholder="token de acesso" autofocus required>
<p id="login-error" class="dialog-error hidden"></p>
<div class="dialog-actions">
<button type="submit" id="login-submit" class="btn btn-primary">Entrar</button>
</div>
</form>
</div>
<!-- Diálogo de Preferências do editor -->
<div id="prefs-overlay" class="overlay hidden">
<form id="prefs-form" class="dialog dialog-wide" autocomplete="off">
<h1>Preferências do editor</h1>
<fieldset>
<legend>Exibição</legend>
<label class="row"><input type="checkbox" id="pf-gutter"> Mostrar números de linha</label>
<label class="row"><input type="checkbox" id="pf-wrap"> Quebrar linha automaticamente</label>
<label class="row"><input type="checkbox" id="pf-colmark"> Marcar coluna
<input type="number" id="pf-col" min="20" max="320" step="1" class="num"> (padrão 80)</label>
<label class="row"><input type="checkbox" id="pf-hl"> Colorir sintaxe (Markdown e HTML)</label>
</fieldset>
<fieldset>
<legend>Fonte e tema</legend>
<label class="row">Fonte
<select id="pf-font">
<option value="mono">DejaVu Sans Mono (padrão)</option>
<option value="ubuntu">Ubuntu Mono</option>
<option value="cascadia">Cascadia Code</option>
<option value="liberation">Liberation Mono</option>
<option value="courier">Courier New</option>
<option value="system">Monoespaçada do sistema</option>
</select>
</label>
<label class="row">Tamanho <input type="number" id="pf-size" min="10" max="32" step="1" class="num"> px</label>
<label class="row">Esquema de cores
<select id="pf-theme">
<option value="claro">Claro (Greybird — padrão)</option>
<option value="escuro">Escuro (Adwaita)</option>
<option value="solar-claro">Solarizado claro</option>
<option value="solar-escuro">Solarizado escuro</option>
</select>
</label>
</fieldset>
<div class="dialog-actions">
<button type="button" id="pf-reset" class="btn">Restaurar padrões</button>
<span class="st-grow"></span>
<button type="button" id="pf-cancel" class="btn">Cancelar</button>
<button type="submit" class="btn btn-primary">Aplicar</button>
</div>
</form>
</div>
<!-- Modal genérico (Sobre / confirmar) -->
<div id="modal-overlay" class="overlay hidden">
<div class="dialog">
<h1 id="modal-title"></h1>
<div id="modal-body"></div>
<div class="dialog-actions">
<button type="button" id="modal-ok" class="btn btn-primary">OK</button>
<button type="button" id="modal-cancel" class="btn hidden">Cancelar</button>
</div>
</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) -->
<header id="menubar" role="menubar">
<div class="menu-brand">
<button type="button" class="menu-btn" id="btn-sidebar" title="Mostrar/ocultar barra lateral">☰</button>
<span class="brand-title">Notas</span>
</div>
<div class="menu" data-menu="arquivo">
<button type="button" class="menu-btn">Arquivo</button>
<ul class="menu-dropdown">
<li data-action="new-note">Nova nota<span class="accel">Ctrl+N</span></li>
<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>
<div class="menu" data-menu="editar">
<button type="button" class="menu-btn">Editar</button>
<ul class="menu-dropdown">
<li data-action="undo">Desfazer<span class="accel">Ctrl+Z</span></li>
<li data-action="redo">Refazer<span class="accel">Ctrl+Y</span></li>
<li class="menu-sep"></li>
<li data-action="select-all">Selecionar tudo<span class="accel">Ctrl+A</span></li>
<li class="menu-sep"></li>
<li data-action="prefs">Preferências…<span class="accel">Ctrl+,</span></li>
</ul>
</div>
<div class="menu" data-menu="exibir">
<button type="button" class="menu-btn">Exibir</button>
<ul class="menu-dropdown">
<li data-action="toggle-sidebar">Barra lateral<span class="accel">F9</span></li>
<li data-action="toggle-gutter">Números de linha</li>
<li data-action="toggle-wrap">Quebrar linha</li>
<li class="menu-sep"></li>
<li data-action="toggle-edit">Modo edição<span class="accel">Ctrl+E</span></li>
<li data-action="toggle-preview">Modo visualização<span class="accel">Ctrl+R</span></li>
<li class="menu-sep"></li>
<li data-action="toggle-versions">Painel de versões<span class="accel">Ctrl+H</span></li>
</ul>
</div>
<div class="menu" data-menu="ajuda">
<button type="button" class="menu-btn">Ajuda</button>
<ul class="menu-dropdown">
<li data-action="about">Sobre o Notas</li>
<li data-action="logout">Sair (esquecer token)</li>
</ul>
</div>
</header>
<!-- Barra de ferramentas -->
<div id="toolbar">
<div class="tb-group">
<button type="button" class="btn btn-icon" id="btn-new" title="Nova nota (Ctrl+N)">✚<span class="tb-label">Nova</span></button>
<button type="button" class="btn btn-icon" id="btn-save" title="Salvar (Ctrl+S)">💾<span class="tb-label">Salvar</span></button>
<button type="button" class="btn btn-icon" id="btn-export" title="Baixar arquivo">⬇<span class="tb-label">Baixar</span></button>
</div>
<span class="tb-sep"></span>
<div class="tb-group" id="fmt-tools">
<button type="button" class="btn btn-icon fmt" data-fmt="bold" title="Negrito (Ctrl+B)"><b>B</b></button>
<button type="button" class="btn btn-icon fmt" data-fmt="italic" title="Itálico (Ctrl+I)"><i>I</i></button>
<button type="button" class="btn btn-icon fmt" data-fmt="strike" title="Tachado"><s>S</s></button>
<button type="button" class="btn btn-icon fmt" data-fmt="h1" title="Título 1">H1</button>
<button type="button" class="btn btn-icon fmt" data-fmt="h2" title="Título 2">H2</button>
<button type="button" class="btn btn-icon fmt" data-fmt="list-ul" title="Lista com marcadores">≡•</button>
<button type="button" class="btn btn-icon fmt" data-fmt="list-ol" title="Lista numerada">≡1</button>
<button type="button" class="btn btn-icon fmt" data-fmt="quote" title="Citação">❝</button>
<button type="button" class="btn btn-icon fmt" data-fmt="link" title="Link (Ctrl+K)">🔗</button>
<button type="button" class="btn btn-icon fmt" data-fmt="code" title="Código `inline`">&lt;/&gt;</button>
<button type="button" class="btn btn-icon fmt" data-fmt="block" title="Bloco de código">{ }</button>
</div>
<span class="tb-sep"></span>
<div class="tb-group">
<label class="tb-caption" for="format-select">Formato</label>
<select id="format-select" title="Formato da nota">
<option value="txt">Texto puro .txt</option>
<option value="md" selected>Markdown .md</option>
<option value="html">HTML .html</option>
</select>
</div>
<span class="tb-grow"></span>
<div class="tb-group">
<div class="segmented" id="mode-toggle">
<button type="button" class="seg active" data-mode="edit" title="Editar (Ctrl+E)">Editar</button>
<button type="button" class="seg" data-mode="preview" title="Visualizar (Ctrl+R)">Preview</button>
</div>
<button type="button" class="btn btn-icon" id="btn-versions" title="Painel de versões (Ctrl+H)">🕘<span class="tb-label">Versões</span></button>
</div>
</div>
<!-- Corpo: sidebar + editor + painel de versões -->
<main id="layout">
<aside id="sidebar">
<div class="side-section">
<input id="search-input" type="search" placeholder="Buscar notas…" autocomplete="off">
</div>
<div class="side-section">
<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>
<div id="tag-cloud"><span class="muted">nenhuma tag ainda</span></div>
</div>
<div class="side-section side-notes">
<div class="side-listhead">
<span class="side-label">Notas</span>
<span id="notes-count" class="muted"></span>
</div>
<ul id="notes-list"></ul>
</div>
</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">
<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">
<div id="gutter" aria-hidden="true"><div id="gutter-inner"></div></div>
<div id="code-area">
<div id="hl" aria-hidden="true"></div>
<div id="col-marker" class="hidden" aria-hidden="true"></div>
<textarea id="editor" spellcheck="false" placeholder="Comece a escrever… use #tags para organizar (ex.: #linux #devops)"></textarea>
</div>
</div>
<div id="preview" class="hidden"></div>
<iframe id="preview-frame" class="hidden" sandbox="allow-scripts" title="Pré-visualização HTML"></iframe>
</div>
</section>
<aside id="versions-panel" class="hidden">
<div class="panel-head">
<span class="side-label">Histórico de revisões</span>
<button type="button" class="btn btn-icon" id="btn-close-versions" title="Fechar painel">✕</button>
</div>
<ul id="revisions-list"></ul>
<div id="version-detail" class="hidden">
<div class="diff-head">
<span id="version-info"></span>
<button type="button" class="btn btn-primary btn-small" id="btn-restore">Restaurar</button>
</div>
<pre id="version-diff"></pre>
</div>
<div class="panel-foot">
<input id="rev-message" type="text" placeholder="Mensagem da próxima revisão (opcional)" maxlength="500" autocomplete="off">
<input id="author-input" type="text" placeholder="autor (ex.: lg)" maxlength="80" autocomplete="off">
</div>
</aside>
</main>
<!-- Barra de status (estilo Mousepad) -->
<footer id="statusbar">
<span id="st-file" class="st-item">sem nota aberta</span>
<span id="st-format" class="st-item">.txt</span>
<span id="st-pos" class="st-item">Ln 1, Col 1</span>
<span class="st-grow"></span>
<span id="st-stats" class="st-item">0 caracteres</span>
<span id="st-saved" class="st-item">—</span>
</footer>
<div id="toast" class="hidden"></div>
</div>
<div id="backdrop" class="hidden"></div>
<script src="/js/app.js"></script>
</body>
</html>