Files
lgcosta 1b9ce995ba feat(ui): reordenar abas com arraste, botão 🗂 Arquivos na toolbar, lixeira na lista
- Abas: HTML5 drag-and-drop — arrastar uma aba sobre outra mostra marca de
  inserção (borda accent à esquerda/direita conforme o lado) e solta reordenando;
  soltar na área vazia da aba-barra move para o fim; ordem persiste em
  localStorage (passa a valer após o debounce de 400ms de persistTabs);
  ✕ da aba não inicia arraste (dragstart cancelado)
- Toolbar: botão 🗂 Arquivos abre o gerenciador no modo normal (não seletor),
  equivalente ao Ctrl+J
- Lista de notas (sidebar): botão 🗑 em cada linha abre o mesmo diálogo de
  confirmação do menu e exclui a nota mesmo sem aba aberta; se houver aba da
  nota, ela é fechada e a aba vizinha ativada; no mobile o botão fica sempre
  visível (alvo 32px, sem depender de hover)
- deleteNote() agora delega para deleteNoteById(n) (reuso do fluxo com
  presença/persistTabs)
- sw v15
2026-11-05 12:45:34 -03:00

356 lines
17 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="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#3584e4">
<meta name="mobile-web-app-capable" content="yes">
<meta name="application-name" content="Notas">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Notas">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
<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>
<label class="row"><input type="checkbox" id="pf-autosave"> Autosave — salva após período de inatividade
<select id="pf-autosave-delay" class="compact">
<option value="5">5 s</option>
<option value="10" selected>10 s</option>
<option value="30">30 s</option>
<option value="60">60 s</option>
</select>
</label>
<label class="row">Tecla Tab
<select id="pf-tabmode" class="compact">
<option value="spaces">insere espaços</option>
<option value="tab">insere tabulação (\t)</option>
</select>
<input type="number" id="pf-tabsize" min="1" max="16" step="1" class="num"
title="Quantidade de espaços inseridos — e largura exibida do caractere \t"> espaços
</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">
<optgroup label="Nativos">
<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>
</optgroup>
<optgroup label="iTerm2 — escuros">
<option value="dracula">Dracula</option>
<option value="nord">Nord</option>
<option value="one-dark">Atom One Dark</option>
<option value="monokai">Monokai Pro</option>
<option value="gruvbox-dark">Gruvbox Dark</option>
<option value="tokyo-night">Tokyo Night</option>
<option value="catppuccin-mocha">Catppuccin Mocha</option>
<option value="tomorrow-night">Tomorrow Night</option>
<option value="ayu-mirage">Ayu Mirage</option>
<option value="kanagawa-wave">Kanagawa Wave</option>
</optgroup>
<optgroup label="iTerm2 — claros">
<option value="catppuccin-latte">Catppuccin Latte</option>
<option value="gruvbox-light">Gruvbox Light</option>
<option value="one-light">Atom One Light</option>
<option value="nord-light">Nord Light</option>
<option value="carbon-paper">Carbon Paper</option>
<option value="tomorrow">Tomorrow</option>
<option value="ayu-light">Ayu Light</option>
</optgroup>
</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>
<!-- Gerenciador de arquivos / anexos -->
<div id="files-overlay" class="files-overlay hidden">
<div class="files-head">
<span class="files-title">Arquivos</span>
<div id="files-filters" class="files-filters"></div>
<input id="files-search" type="search" placeholder="Buscar…" autocomplete="off">
<button type="button" class="btn btn-primary btn-small" id="files-upload-btn">⬆ Enviar</button>
<button type="button" class="btn btn-small" id="files-newfolder">+ Pasta</button>
<button type="button" class="btn btn-icon btn-small" id="files-close" title="Fechar">✕</button>
</div>
<div class="files-body">
<aside id="files-tree" class="files-tree"></aside>
<section id="files-list" class="files-list dropzone">
<div id="files-empty" class="files-empty muted">Solte arquivos aqui ou use “⬆ Enviar”.</div>
</section>
</div>
<input type="file" id="files-input" multiple class="hidden">
</div>
<!-- Lightbox de preview -->
<div id="lightbox" class="overlay hidden">
<div class="lightbox-box">
<div id="lightbox-body" class="lightbox-body"></div>
<div class="dialog-actions">
<a id="lightbox-dl" class="btn btn-primary" download>Baixar</a>
<button type="button" id="lightbox-close" class="btn">Fechar</button>
</div>
</div>
</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 data-action="files">Gerenciador de arquivos…<span class="accel">Ctrl+J</span></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-wysiwyg">Modo WYSIWYG<span class="accel">Ctrl+M</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="install" class="hidden">Instalar Aplicativo…</li>
<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>
<button type="button" class="btn btn-icon" id="btn-attach" title="Inserir anexo (📎)">📎<span class="tb-label">Anexo</span></button>
<button type="button" class="btn btn-icon" id="btn-files" title="Gerenciador de arquivos (Ctrl+J)">🗂<span class="tb-label">Arquivos</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="wysiwyg" title="WYSIWYG (Ctrl+M)">WYSIWYG</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">
<span id="presence-badge" class="presence-badge hidden" title="Outras sessões com esta nota aberta"></span>
<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>
<div id="wysiwyg" class="hidden" contenteditable="true" spellcheck="false"></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>