Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Fixed

- Keep Read Only note controls visible while disabling content changes, fit reader actions to the available width, and expose available actions in maximized desktop note windows.
- Open nested reminder calendars and time pickers above their parent form, and dismiss only the top overlay on Escape.
- Use consistent rounded page headers and the theme button foreground for banner contrast.

---

## [1.2.0-dev01] - 2026-10-20 (pre-release)
Expand Down
2 changes: 1 addition & 1 deletion scripts/backdrop-click.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ test('it follows the latest close action and lets go when removed', () => {

test('the note editor and every popup close only on a deliberate backdrop click', () => {
const editor = read('../src/components/notes/NoteEditor.svelte');
assert.match(editor, /class="editor-backdrop" class:inline use:backdropClick=\{\(\) => \{ if \(!inline\) close\(\); \}\}/);
assert.match(editor, /class="editor-backdrop" class:inline[^\n]*use:backdropClick=\{\(\) => \{ if \(!inline\) close\(\); \}\}/);
assert.doesNotMatch(editor, /class="editor-backdrop"[^\n]*on:click/);
const pop = read('../src/components/notes/Popover.svelte');
assert.match(pop, /class="pop-backdrop" class:narrow use:backdropClick=\{closeOutside\} use:closeOnBack=\{close\}/);
Expand Down
32 changes: 32 additions & 0 deletions scripts/overlay-layer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { overlayLayer, isTopOverlay } from '../src/lib/overlay-layer.js';

globalThis.getComputedStyle = node => ({ zIndex: node.baseline });
const node = baseline => ({ baseline, style: { zIndex: '' } });

test('a calendar with a lower CSS z-index opens above its reminder popover', () => {
const popover = node('300'), calendar = node('200');
const parent = overlayLayer(popover), child = overlayLayer(calendar);
assert.ok(Number(calendar.style.zIndex) > Number(popover.style.zIndex));
assert.equal(isTopOverlay(popover), false);
assert.equal(isTopOverlay(calendar), true);
child.destroy();
assert.equal(isTopOverlay(popover), true);
assert.equal(calendar.style.zIndex, '');
parent.destroy();
assert.equal(isTopOverlay(popover), false);
});
test('higher baseline layers and nested layers retain their ordering', () => {
const reminder = node('300'), time = node('9999'), child = node('200');
const a = overlayLayer(reminder), b = overlayLayer(time), c = overlayLayer(child);
assert.equal(time.style.zIndex, '9999');
assert.equal(child.style.zIndex, '10000');
a.destroy(); // Account/navigation teardown can remove a parent first.
assert.equal(isTopOverlay(child), true);
c.destroy(); assert.equal(isTopOverlay(time), true);
b.destroy();
const reopened = node('200'), d = overlayLayer(reopened);
assert.equal(reopened.style.zIndex, '200');
d.destroy();
});
5 changes: 4 additions & 1 deletion scripts/read-only.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,10 @@ test('the note opens for reading: content guarded, ticking and color kept, Edit
// Turned on, what's typed is saved first.
assert.match(ed, /if \(next\) await flushAll\(\);\s*readOnlyNote = next;/);
assert.match(ed, /checkable=\{!baseLocked\}/);
assert.equal((ed.match(/\{#if !baseLocked( && !tightBar)?\}\s*<button class="(icon-btn|sheet-item)"[^\n]*openColor/g) || []).length, 4, 'color stays on every bar');
assert.equal((ed.match(/\{#if !baseLocked\}\s*<button class="icon-btn"[^\n]*openColor/g) || []).length, 2, 'color stays on the compact and full editor bars');
assert.match(ed, /!baseLocked \? \[\{ key: 'color',[^\n]*run: openColor/, 'the responsive reader keeps color available on Read Only notes');
assert.match(ed, /\{#each readerVisible as action \(action\.key\)\}/);
assert.match(ed, /\{#each readerOverflow as action \(action\.key\)\}/);
assert.match(ed, /role="menuitemcheckbox" aria-checked=\{readOnlyNote\} on:click=\{\(\) => \{ moreOpen = false; toggleReadOnly\(\); \}\}/);
const cl = read('../src/components/notes/ChecklistEditor.svelte');
assert.match(cl, /function toggle\(item\) \{\s*if \(!checkable\) return;/);
Expand Down
26 changes: 26 additions & 0 deletions scripts/toolbar-fit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { visibleToolbarCount } from '../src/lib/toolbar-fit.js';

test('wide note panes show all eligible actions', () => {
assert.equal(visibleToolbarCount(1280, 19), 19);
assert.equal(visibleToolbarCount(960, 15), 15);
});

test('shrinking panes progressively move actions into More', () => {
const counts = [1000, 900, 800, 700, 600].map(width => visibleToolbarCount(width, 19));
for (let i = 1; i < counts.length; i++) assert.ok(counts[i] <= counts[i - 1]);
assert.ok(new Set(counts).size >= 4);
});

test('phone toolbars reserve room for touch-sized More and Close buttons', () => {
assert.equal(visibleToolbarCount(360, 19), 4);
assert.equal(visibleToolbarCount(320, 19), 3);
assert.equal(visibleToolbarCount(180, 19), 0);
});

test('the count remains bounded when actions disappear or before layout measures', () => {
assert.equal(visibleToolbarCount(0, 19), 0);
assert.equal(visibleToolbarCount(1280, 0), 0);
assert.equal(visibleToolbarCount(360, 2), 2);
});
2 changes: 1 addition & 1 deletion src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@
// editor or dialog is open, where the shortcut would pull focus away.
async function _searchShortcut(e) {
if (!(e.ctrlKey || e.metaKey) || e.altKey || e.shiftKey || e.key.toLowerCase() !== 'k') return;
if (document.querySelector('.editor-backdrop, [role="dialog"][aria-modal="true"]')) return;
if (document.querySelector('.editor-backdrop, dialog[open], [role="dialog"][aria-modal="true"]')) return;
e.preventDefault();
const onNotes = $location === '/' || /^\/(notes|shared|archive|trash|reminders|label\/)/.test($location);
if (!onNotes) { await push('/'); await new Promise(r => setTimeout(r, 60)); }
Expand Down
Loading
Loading