diff --git a/README.md b/README.md index 570dce3..d4afcfe 100644 --- a/README.md +++ b/README.md @@ -117,11 +117,20 @@ ocupa a tela inteira; menus e toolbar rolam horizontalmente com botões só de guardado em **cookie HttpOnly/SameSite=Lax** (30 dias). A API também aceita `Authorization: Bearer ` para scripts. - **Responsivo para celular** (≤760px): barra lateral vira gaveta com backdrop (☰ fecha ao - tocar uma nota), painel de versões ocupa a tela toda, toolbar e menus rolam horizontalmente - sem rótulos, **dropdowns dos menus posicionados como `fixed`** (o `overflow-x` da menubar não - os corta mais em telas pequenas), alvos de toque ampliados, diálogos em largura total, fonte - do editor com mínimo de 16px (evita zoom automático do iOS), altura em `dvh` e - `safe-area-inset` para notch. + tocar uma nota), painel de versões ocupa a tela toda, **toolbar quebra em 3 linhas + organizadas** — ① ações de arquivo + formato + versões, ② alternador de modo em largura + total, ③ ferramentas de formatação distribuídas (cabe em 393px **e** 320px sem corte) —, + alvos de toque ≥40px, diálogos em largura total, fonte do editor com mínimo de 16px (evita + zoom automático do iOS), altura em `dvh` e `safe-area-inset` para notch; **dropdowns dos + menus em `position:fixed`** (o `overflow-x` da menubar não os corta mais) e menus rolam + horizontalmente sem rótulos. +- **Botões "rich"** (linguagem visual do Rich Button/spell.sh, mas em CSS puro — o componente + deles é React+Tailwind, incompatível com o build-free + CSP do app): gradiente vertical com + brilho de vidro, reflexo interno superior, sombra dupla e glow no primário, micro-interações + de hover (eleva) / active (pressiona) / focus ring. Tudo derivado dos tokens com + `color-mix()` + `light-dark()`, então **os 21 temas** (claros e escuros) ganham botões + coerentes automaticamente; o mesmo vale para o controle segmentado Editar/WYSIWYG/Preview, + selects da toolbar e chips. - **Estilo Mousepad**: tema claro Greybird-like, menus nativos (Arquivo/Editar/Exibir/Ajuda), atalhos `Ctrl+S`, `Ctrl+N`, `Ctrl+E/M/R` (edição/wysiwyg/preview), `Ctrl+H` (histórico), `F9` (barra lateral), `Ctrl+B/I/K` (formatação), `Ctrl+,` (preferências), `Alt+W`/`Alt+←/→`/ diff --git a/web/css/app.css b/web/css/app.css index a57c639..4e76dce 100644 --- a/web/css/app.css +++ b/web/css/app.css @@ -1,6 +1,7 @@ /* Notas — tema claro sóbrio inspirado no Mousepad (XFCE/Greybird). */ :root { + color-scheme: light; --bg: #f6f5f4; --panel: #ffffff; --panel-2: #f0eeeb; @@ -119,7 +120,7 @@ code { font-family: var(--mono); font-size: 0.92em; } white-space: nowrap; } .menu-dropdown li:hover { background: var(--accent); color: #fff; } -.menu-dropdown li:hover .accel { color: #e5effb; } +.menu-dropdown li:hover .accel { color: color-mix(in oklab, var(--accent), white 78%); } .menu-dropdown .accel { color: var(--muted); font-size: 11px; } .menu-sep { height: 0; padding: 0; margin: 4px 8px; border-top: 1px solid var(--border-soft); cursor: default; } .menu-sep:hover { background: none; } @@ -141,49 +142,158 @@ code { font-family: var(--mono); font-size: 0.92em; } .tb-grow { flex: 1; } .tb-label { font-size: 12px; margin-left: 4px; } +/* --------------------------------------------------------------------------- + Sistema de botões "rich" (mesma linguagem visual do Rich Button do + spell.sh — gradientes, brilho interno, glow e micro-interações — porém 100% + CSS puro, sem React/Tailwind, derivado dos tokens de tema via color-mix + + light-dark, portanto compatível com os 21 temas e com CSP estrita). + --------------------------------------------------------------------------- */ .btn { + position: relative; + isolation: isolate; font: inherit; font-size: 13px; color: var(--fg); - background: var(--panel-2); - border: 1px solid var(--border); - border-radius: 3px; - padding: 3px 10px; + border: 1px solid color-mix(in oklab, var(--border) 70%, var(--fg) 30%); + border-radius: 8px; + padding: 4px 12px; cursor: pointer; + background: + linear-gradient(180deg, + color-mix(in oklab, var(--panel-2), white 55%) 0%, + var(--panel-2) 48%, + color-mix(in oklab, var(--panel-2), var(--fg) 10%) 100%); + box-shadow: + inset 0 1px 0 light-dark(rgba(255, 255, 255, .85), rgba(255, 255, 255, .14)), + inset 0 -1px 0 color-mix(in oklab, var(--panel-2), var(--fg) 14%), + 0 1px 2px rgba(0, 0, 0, .12), + 0 2px 6px -2px rgba(0, 0, 0, .10); + text-shadow: 0 1px 0 light-dark(rgba(255, 255, 255, .7), rgba(0, 0, 0, .35)); + transition: transform .12s ease, box-shadow .16s ease, filter .16s ease; } -.btn:hover { background: #e6e2dd; } -.btn:active { border-color: var(--accent-dark); } -.btn:disabled { opacity: .45; cursor: default; } -.btn-icon { display: inline-flex; align-items: center; padding: 3px 8px; } -.btn-primary { background: var(--accent); border-color: var(--accent-dark); color: #fff; } -.btn-primary:hover { background: var(--accent-dark); } -.btn-danger { background: var(--danger); border-color: #9c1720; color: #fff; } -.btn-small { font-size: 12px; padding: 2px 8px; } -.fmt { min-width: 30px; justify-content: center; font-size: 13px; } +.btn::after { /* reflexo superior estilo "vidro" */ + content: ''; + position: absolute; + inset: 1px 1px 50% 1px; + z-index: -1; + border-radius: 7px 7px 10px 10px; + background: linear-gradient(180deg, light-dark(rgba(255, 255, 255, .5), rgba(255, 255, 255, .07)), transparent); + pointer-events: none; +} +.btn:hover { + transform: translateY(-1px); + filter: brightness(1.04) saturate(1.05); + box-shadow: + inset 0 1px 0 light-dark(rgba(255, 255, 255, .9), rgba(255, 255, 255, .18)), + inset 0 -1px 0 color-mix(in oklab, var(--panel-2), var(--fg) 16%), + 0 3px 8px -1px rgba(0, 0, 0, .18), + 0 6px 16px -6px rgba(0, 0, 0, .14); +} +.btn:active { + transform: translateY(1px) scale(.98); + filter: brightness(.97); + box-shadow: + inset 0 2px 5px rgba(0, 0, 0, .22), + inset 0 -1px 0 light-dark(rgba(255, 255, 255, .5), rgba(255, 255, 255, .08)); +} +.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } +.btn:disabled { + opacity: .45; + cursor: default; + transform: none; + filter: saturate(.4); + box-shadow: inset 0 1px 0 light-dark(rgba(255, 255, 255, .6), rgba(255, 255, 255, .1)); +} +.btn-icon { display: inline-flex; align-items: center; justify-content: center; padding: 4px 10px; } +.btn-primary { + color: #fff; + text-shadow: 0 1px 1px rgba(0, 0, 0, .25); + border-color: color-mix(in oklab, var(--accent), black 28%); + background: + linear-gradient(180deg, + color-mix(in oklab, var(--accent), white 26%) 0%, + var(--accent) 48%, + color-mix(in oklab, var(--accent), black 14%) 100%); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, .38), + inset 0 -1px 0 color-mix(in oklab, var(--accent), black 24%), + 0 2px 8px -1px color-mix(in oklab, var(--accent) 55%, transparent), + 0 1px 2px rgba(0, 0, 0, .18); +} +.btn-primary::after { background: linear-gradient(180deg, rgba(255, 255, 255, .28), transparent); } +.btn-primary:hover { + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, .45), + inset 0 -1px 0 color-mix(in oklab, var(--accent), black 26%), + 0 4px 14px -2px color-mix(in oklab, var(--accent) 65%, transparent), + 0 2px 4px rgba(0, 0, 0, .2); +} +.btn-danger { + color: #fff; + text-shadow: 0 1px 1px rgba(0, 0, 0, .25); + border-color: color-mix(in oklab, var(--danger), black 26%); + background: + linear-gradient(180deg, + color-mix(in oklab, var(--danger), white 24%) 0%, + var(--danger) 48%, + color-mix(in oklab, var(--danger), black 12%) 100%); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, .35), + inset 0 -1px 0 color-mix(in oklab, var(--danger), black 22%), + 0 2px 8px -1px color-mix(in oklab, var(--danger) 50%, transparent); +} +.btn-danger::after { background: linear-gradient(180deg, rgba(255, 255, 255, .25), transparent); } +.btn-small { font-size: 12px; padding: 2px 9px; border-radius: 6px; } +.fmt { min-width: 32px; justify-content: center; font-size: 13px; } #fmt-tools.disabled { opacity: .45; pointer-events: none; } #toolbar select { font: inherit; font-size: 13px; color: var(--fg); - background: var(--panel); - border: 1px solid var(--border); - border-radius: 3px; - padding: 3px 6px; + background: + linear-gradient(180deg, + color-mix(in oklab, var(--panel), white 40%) 0%, + var(--panel) 50%, + color-mix(in oklab, var(--panel), var(--fg) 6%) 100%); + border: 1px solid color-mix(in oklab, var(--border) 70%, var(--fg) 30%); + border-radius: 8px; + padding: 4px 8px; + box-shadow: inset 0 1px 0 light-dark(rgba(255, 255, 255, .8), rgba(255, 255, 255, .12)), 0 1px 2px rgba(0, 0, 0, .1); } -.segmented { display: inline-flex; border: 1px solid var(--border); border-radius: 4px; overflow: hidden; } +.segmented { + display: inline-flex; + border: 1px solid color-mix(in oklab, var(--border) 70%, var(--fg) 30%); + border-radius: 9px; + overflow: hidden; + background: color-mix(in oklab, var(--panel-2), var(--fg) 6%); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, .12); + padding: 2px; + gap: 2px; +} .segmented .seg { border: 0; - background: var(--panel-2); + background: transparent; font: inherit; font-size: 12px; - padding: 4px 12px; + padding: 4px 14px; cursor: pointer; - color: var(--fg); + color: var(--muted); + border-radius: 7px; + transition: background .15s ease, color .15s ease, box-shadow .15s ease; +} +.segmented .seg:hover { color: var(--fg); background: color-mix(in oklab, var(--panel), transparent 35%); } +.segmented .seg.active { + background: + linear-gradient(180deg, + color-mix(in oklab, var(--accent), white 22%) 0%, + var(--accent) 55%, + color-mix(in oklab, var(--accent), black 10%) 100%); + color: #fff; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .35), 0 1px 4px color-mix(in oklab, var(--accent) 50%, transparent); + text-shadow: 0 1px 1px rgba(0, 0, 0, .25); } -.segmented .seg + .seg { border-left: 1px solid var(--border); } -.segmented .seg.active { background: var(--accent); color: #fff; } /* ---------------------------------------------------------------- layout */ #layout { display: flex; flex: 1 1 auto; min-height: 0; } @@ -233,7 +343,7 @@ body.sidebar-off #sidebar { display: none; } .tag-chip:hover { border-color: var(--accent); } .tag-chip.active { background: var(--accent); border-color: var(--accent-dark); color: #fff; } .tag-chip .tag-n { color: var(--muted); margin-left: 3px; font-size: 10px; } -.tag-chip.active .tag-n { color: #dbe9fb; } +.tag-chip.active .tag-n { color: color-mix(in oklab, var(--accent), white 75%); } .side-notes { flex: 1 1 auto; border-bottom: 0; display: flex; flex-direction: column; } .side-listhead { display: flex; justify-content: space-between; align-items: baseline; } @@ -244,8 +354,8 @@ body.sidebar-off #sidebar { display: none; } border-bottom: 1px solid var(--border-soft); cursor: pointer; } -#notes-list li:hover { background: #e9e6e2; } -#notes-list li.active { background: #cfe2fa; box-shadow: inset 3px 0 0 var(--accent); } +#notes-list li:hover { background: color-mix(in oklab, var(--panel-2), var(--fg) 9%); } +#notes-list li.active { background: color-mix(in oklab, var(--panel), var(--accent) 24%); box-shadow: inset 3px 0 0 var(--accent); } .n-title { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .n-sub { font-size: 11px; color: var(--muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .n-ext { color: var(--accent-dark); font-family: var(--mono); font-size: 10px; } @@ -415,8 +525,8 @@ body.no-gutter #gutter { display: none; } .panel-head .side-label { margin: 0; } #revisions-list { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 0 1 40%; } #revisions-list li { padding: 7px 10px; border-bottom: 1px solid var(--border-soft); cursor: pointer; } -#revisions-list li:hover { background: #e9e6e2; } -#revisions-list li.active { background: #cfe2fa; box-shadow: inset 3px 0 0 var(--accent); } +#revisions-list li:hover { background: color-mix(in oklab, var(--panel-2), var(--fg) 9%); } +#revisions-list li.active { background: color-mix(in oklab, var(--panel), var(--accent) 24%); box-shadow: inset 3px 0 0 var(--accent); } .rev-top { display: flex; justify-content: space-between; font-size: 12px; gap: 8px; } .rev-hash { font-family: var(--mono); color: var(--accent-dark); font-size: 11px; } .rev-msg { font-size: 12px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } @@ -594,8 +704,8 @@ body.no-gutter #gutter { display: none; } font-size: 13px; color: var(--fg); } -.tree-row:hover { background: #e9e6e2; } -.tree-row.active { background: #cfe2fa; box-shadow: inset 3px 0 0 var(--accent); } +.tree-row:hover { background: color-mix(in oklab, var(--panel-2), var(--fg) 9%); } +.tree-row.active { background: color-mix(in oklab, var(--panel), var(--accent) 24%); box-shadow: inset 3px 0 0 var(--accent); } .tree-caret { border: 0; background: none; @@ -625,7 +735,7 @@ body.no-gutter #gutter { display: none; } visibility: hidden; } .tree-row:hover .tree-menu, .tree-row.active .tree-menu { visibility: visible; } -.tree-menu:hover { background: #dcd7d2; color: var(--fg); } +.tree-menu:hover { background: color-mix(in oklab, var(--panel-2), var(--fg) 14%); color: var(--fg); } /* ------------------------------------------------- popover de categorias */ #note-meta { position: relative; } @@ -692,11 +802,28 @@ body.no-gutter #gutter { display: none; } .menu-btn { padding: 0 9px; } .menu-dropdown li { padding: 9px 14px; } - #toolbar { overflow-x: auto; scrollbar-width: none; padding: 0 6px; } - #toolbar::-webkit-scrollbar { display: none; } + #toolbar { + height: auto; + min-height: 0; + flex-wrap: wrap; + align-content: flex-start; + row-gap: 6px; + padding: 6px 8px 8px; + overflow: visible; + } + #toolbar .tb-sep, #toolbar .tb-grow { display: none; } .tb-label, .tb-caption { display: none; } - .btn-icon { padding: 6px 9px; min-height: 32px; } - .segmented .seg { padding: 7px 12px; } + #toolbar .tb-group { display: contents; } + #toolbar #fmt-tools { display: flex; } /* grupo de formatação permanece unido */ + /* linha 1: ações de arquivo, botão de versões e seletor de formato */ + #btn-new, #btn-save, #btn-export, #btn-versions { min-height: 40px; } + #toolbar select { min-height: 40px; } + /* linha 2: alternador de modo ocupa a largura toda */ + #mode-toggle { order: 40; flex: 1 1 100%; } + #mode-toggle .seg { flex: 1; min-height: 38px; font-size: 13px; } + /* linha 3: ferramentas de formatação distribuídas, alvos de toque */ + #fmt-tools { order: 50; flex: 1 1 100%; min-width: 0; flex-wrap: wrap; justify-content: space-between; } + #fmt-tools .fmt { flex: 1 1 0; min-width: 0; min-height: 40px; margin: 1px; padding: 4px 2px; } /* sidebar vira gaveta */ #sidebar { @@ -746,6 +873,7 @@ body.no-gutter #gutter { display: none; } /* ---------------------------------------------------- temas / esquemas */ html[data-theme="escuro"] { + color-scheme: dark; --bg: #232323; --panel: #1e1e1e; --panel-2: #262626; @@ -788,6 +916,7 @@ html[data-theme="escuro"] #notes-list li:hover, html[data-theme="escuro"] #revisions-list li:hover { background: #303030; } html[data-theme="solar-claro"] { + color-scheme: light; --bg: #eee8d5; --panel: #fdf6e3; --panel-2: #f4efdc; @@ -825,6 +954,7 @@ html[data-theme="solar-claro"] { } html[data-theme="solar-escuro"] { + color-scheme: dark; --bg: #073642; --panel: #002b36; --panel-2: #0a3d4a; diff --git a/web/sw.js b/web/sw.js index fd0b563..3547150 100644 --- a/web/sw.js +++ b/web/sw.js @@ -1,7 +1,7 @@ /* Service Worker do Notas — caches versionados. * BUMP de CACHE em todo deploy que mude shell/assets (o activate limpa os antigos). */ -const CACHE = 'notas-shell-v4'; +const CACHE = 'notas-shell-v8'; const SHELL = [ '/', '/css/app.css',