+
diff --git a/src/routes/my-notes/+page.svelte b/src/routes/my-notes/+page.svelte
new file mode 100644
index 0000000..0163a77
--- /dev/null
+++ b/src/routes/my-notes/+page.svelte
@@ -0,0 +1,276 @@
+
+
+
+
+
+
+
+
+
My Notes
+ {#if loading}
+
Loading…
+ {:else if error}
+
{error}
+ {:else if filteredEvents.length === 0}
+
No notes found.
+ {:else}
+
+ {#each filteredEvents as event}
+ -
+
+
{getTitle(event)}
+
+
+ {#if showTags[event.id]}
+
+ {#each getTags(event) as tag}
+
+ {tag[0]}:
+ {tag[1]}
+
+ {/each}
+
+ {/if}
+
+ {event.created_at
+ ? new Date(event.created_at * 1000).toLocaleString()
+ : ""}
+
+
+ {@html renderedContent[event.id] || ""}
+
+
+ {/each}
+
+ {/if}
+
+
diff --git a/tests/e2e/my_notes_layout.pw.spec.ts b/tests/e2e/my_notes_layout.pw.spec.ts
new file mode 100644
index 0000000..0a17d75
--- /dev/null
+++ b/tests/e2e/my_notes_layout.pw.spec.ts
@@ -0,0 +1,103 @@
+import { test, expect } from '@playwright/test';
+
+// Utility to check for horizontal scroll bar
+async function hasHorizontalScroll(page, selector) {
+ return await page.evaluate((sel) => {
+ const el = document.querySelector(sel);
+ if (!el) return false;
+ return el.scrollWidth > el.clientWidth;
+ }, selector);
+}
+
+test.describe('My Notes Layout', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/my-notes');
+ await page.waitForSelector('h1:text("My Notes")');
+ });
+
+ test('no horizontal scroll bar for all tag type and tag filter combinations', async ({ page }) => {
+ // Helper to check scroll for current state
+ async function assertNoScroll() {
+ const hasScroll = await hasHorizontalScroll(page, 'main, body, html');
+ expect(hasScroll).toBeFalsy();
+ }
+
+ // Check default (no tag type selected)
+ await assertNoScroll();
+
+ // Get all tag type buttons
+ const tagTypeButtons = await page.locator('aside button').all();
+ // Only consider tag type buttons (first N)
+ const tagTypeCount = await page.locator('aside > div.flex.flex-wrap.gap-2.mb-6 > button').count();
+ // For each single tag type
+ for (let i = 0; i < tagTypeCount; i++) {
+ // Click tag type button
+ await tagTypeButtons[i].click();
+ await page.waitForTimeout(100); // Wait for UI update
+ await assertNoScroll();
+ // Get tag filter buttons (after tag type buttons)
+ const tagFilterButtons = await page.locator('aside > div.flex.flex-wrap.gap-2.mb-4 > button').all();
+ // Try all single tag filter selections
+ for (let j = 0; j < tagFilterButtons.length; j++) {
+ await tagFilterButtons[j].click();
+ await page.waitForTimeout(100);
+ await assertNoScroll();
+ // Deselect
+ await tagFilterButtons[j].click();
+ await page.waitForTimeout(50);
+ }
+ // Try all pairs of tag filter selections
+ for (let j = 0; j < tagFilterButtons.length; j++) {
+ for (let k = j + 1; k < tagFilterButtons.length; k++) {
+ await tagFilterButtons[j].click();
+ await tagFilterButtons[k].click();
+ await page.waitForTimeout(100);
+ await assertNoScroll();
+ // Deselect
+ await tagFilterButtons[j].click();
+ await tagFilterButtons[k].click();
+ await page.waitForTimeout(50);
+ }
+ }
+ // Deselect tag type
+ await tagTypeButtons[i].click();
+ await page.waitForTimeout(100);
+ }
+
+ // Try all pairs of tag type selections (multi-select)
+ for (let i = 0; i < tagTypeCount; i++) {
+ for (let j = i + 1; j < tagTypeCount; j++) {
+ await tagTypeButtons[i].click();
+ await tagTypeButtons[j].click();
+ await page.waitForTimeout(100);
+ await assertNoScroll();
+ // Get tag filter buttons for this combination
+ const tagFilterButtons = await page.locator('aside > div.flex.flex-wrap.gap-2.mb-4 > button').all();
+ // Try all single tag filter selections
+ for (let k = 0; k < tagFilterButtons.length; k++) {
+ await tagFilterButtons[k].click();
+ await page.waitForTimeout(100);
+ await assertNoScroll();
+ await tagFilterButtons[k].click();
+ await page.waitForTimeout(50);
+ }
+ // Try all pairs of tag filter selections
+ for (let k = 0; k < tagFilterButtons.length; k++) {
+ for (let l = k + 1; l < tagFilterButtons.length; l++) {
+ await tagFilterButtons[k].click();
+ await tagFilterButtons[l].click();
+ await page.waitForTimeout(100);
+ await assertNoScroll();
+ await tagFilterButtons[k].click();
+ await tagFilterButtons[l].click();
+ await page.waitForTimeout(50);
+ }
+ }
+ // Deselect tag types
+ await tagTypeButtons[i].click();
+ await tagTypeButtons[j].click();
+ await page.waitForTimeout(100);
+ }
+ }
+ });
+});
\ No newline at end of file