From f7a4dc33d620ab281abdbe2a775564044f750734 Mon Sep 17 00:00:00 2001 From: Baivab Sarkar Date: Wed, 12 Aug 2026 00:13:46 +0530 Subject: [PATCH 01/17] feat(toolbar): consolidate document actions --- desktop-app/resources/index.html | 103 ++++++----- desktop-app/resources/styles.css | 195 +++++++-------------- index.html | 103 ++++++----- styles.css | 195 +++++++-------------- tests/e2e/status-toolbar-followup.spec.js | 41 +++-- tests/e2e/tab-split-sidebar-update.spec.js | 69 +++----- tests/e2e/ui-toolbar-redesign.spec.js | 73 +++----- 7 files changed, 313 insertions(+), 466 deletions(-) diff --git a/desktop-app/resources/index.html b/desktop-app/resources/index.html index ef73df36..2f753352 100644 --- a/desktop-app/resources/index.html +++ b/desktop-app/resources/index.html @@ -53,11 +53,18 @@

Markdown Viewer - Online Markd - - diff --git a/desktop-app/resources/styles.css b/desktop-app/resources/styles.css index fd95dc38..1abefbe9 100644 --- a/desktop-app/resources/styles.css +++ b/desktop-app/resources/styles.css @@ -2218,10 +2218,6 @@ body.document-sidebar-collapsed .document-sidebar { gap: 5px; } -.header-right .header-command-btn .btn-text { - display: inline; -} - .header-command-btn { width: auto; padding-inline: 10px; @@ -2235,6 +2231,19 @@ body.document-sidebar-collapsed .document-sidebar { display: inline-flex; } +.header-icon-dropdown .dropdown-toggle::after { + display: none; +} + +.header-toolbar-divider { + width: 1px; + height: 20px; + flex: 0 0 1px; + margin-inline: 2px; + background: var(--border-color); + opacity: 0.72; +} + .app-command-menu.header-new-menu, .app-command-menu.header-export-menu { width: max-content; @@ -2283,6 +2292,59 @@ body.document-sidebar-collapsed .document-sidebar { line-height: 1; } +.header-icon-command.is-active, +.header-icon-command.is-active:hover, +.header-icon-command.is-live-active, +.header-icon-command.is-live-active:hover, +.header-icon-command.sync-active, +.header-icon-command.sync-active:hover { + border-color: color-mix(in srgb, var(--accent-color) 28%, var(--border-color)); + background: color-mix(in srgb, var(--accent-color) 10%, transparent); + color: var(--accent-color); +} + +.header-icon-command#review-toggle:has(.review-toolbar-count:not([hidden])) { + width: auto; + padding-inline: 6px; +} + +.header-view-toolbar { + flex: 0 0 auto; +} + +.header-view-toolbar .view-toggle-btn { + width: 27px; + min-width: 27px; + height: 26px; + padding: 0; + border: 0; + border-radius: 7px; + background: transparent; + color: var(--text-secondary); + box-shadow: none; +} + +.header-view-toolbar .view-toggle-btn:hover, +.header-view-toolbar .view-toggle-btn:focus-visible { + border: 0; + background: var(--toolbar-control-hover); + color: var(--text-color); +} + +.header-view-toolbar .view-toggle-btn.is-active, +.header-view-toolbar .view-toggle-btn.is-active:hover { + border: 0; + background: var(--segmented-surface); + color: var(--accent-color); + box-shadow: var(--segmented-shadow); +} + +.header-view-toolbar .view-toggle-btn:disabled, +.header-view-toolbar .view-toggle-btn[aria-disabled="true"] { + background: transparent; + box-shadow: none; +} + .settings-dropdown { display: inline-flex; } @@ -2800,46 +2862,10 @@ body.document-sidebar-collapsed .document-sidebar { flex-shrink: 0; } -.markdown-toolbar-group--utilities { - position: sticky; - z-index: 6; - inset-inline-end: 0; - margin-inline-start: auto; - border-right: 0; - background: var(--toolbar-surface); -} - -.markdown-toolbar-group--document { - gap: 2px; - padding-inline-start: 6px; -} - .workspace-format-actions { padding-inline: 5px; } -.markdown-toolbar-group--document .tool-button { - flex: 0 0 auto; - width: 30px; - height: 30px; - min-width: 30px; - padding: 0; - border-color: transparent; - border-radius: 7px; - background: transparent; -} - -.markdown-toolbar-group--document .tool-button:hover, -.markdown-toolbar-group--document .tool-button:focus-visible { - border-color: transparent; - background: var(--toolbar-control-hover); -} - -.markdown-toolbar-group--document #review-toggle:has(.review-toolbar-count:not([hidden])) { - width: auto; - padding-inline: 5px; -} - .document-actions-menu { width: 280px; min-width: 280px; @@ -2860,14 +2886,6 @@ body.document-sidebar-collapsed .document-sidebar { z-index: var(--layer-menu); } -.document-command-divider { - width: 1px; - height: 18px; - margin-inline: 4px; - background: var(--border-color); - opacity: 0.75; -} - .markdown-view-toolbar { display: inline-flex; align-items: center; @@ -2879,44 +2897,6 @@ body.document-sidebar-collapsed .document-sidebar { background: var(--segmented-bg); } -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn { - width: 27px; - height: 26px; - min-width: 27px; - border: 0; - border-radius: 7px; - background: transparent; - color: var(--text-secondary); - box-shadow: none; -} - -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn:hover, -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn:focus-visible { - border: 0; - background: var(--toolbar-control-hover); - color: var(--text-color); -} - -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn.is-active, -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn.is-active:hover { - border: 0; - background: var(--segmented-surface); - color: var(--accent-color); - box-shadow: var(--segmented-shadow); -} - -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn:disabled, -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn[aria-disabled="true"] { - background: transparent; - box-shadow: none; -} - -.markdown-toolbar-group--utilities .tool-button { - flex: 0 0 auto; - height: 30px; - min-width: 30px; -} - .markdown-toolbar-group:first-child { padding-left: 0; } @@ -3017,17 +2997,6 @@ body.document-sidebar-collapsed .document-sidebar { gap: 5px; } -.markdown-tool-select--insert { - width: 30px; - min-width: 30px; - gap: 0; - padding: 0; -} - -.markdown-tool-select--insert .lucide-ellipsis { - font-size: var(--ui-icon-size-lg); -} - .markdown-tool-select-label { overflow: hidden; max-width: 48px; @@ -3680,12 +3649,6 @@ a:focus { min-width: 44px; } - .markdown-tool-select--insert { - width: 30px !important; - min-width: 30px; - padding-inline: 0 !important; - } - .markdown-format-toolbar { height: 40px !important; } @@ -3699,23 +3662,6 @@ a:focus { height: 40px !important; } - .markdown-toolbar-group--document .tool-button { - width: 30px; - height: 30px; - min-width: 30px; - padding: 0; - } - - .markdown-view-toolbar { - height: 32px; - } - - .markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn { - width: 27px; - height: 26px; - min-width: 27px; - } - .document-action-item { min-height: 44px; } @@ -4061,15 +4007,6 @@ a:focus { scroll-padding-inline: 6px; } - .markdown-toolbar-group--utilities { - position: static; - margin-inline-start: 0; - } - - .markdown-tool-select--insert { - min-width: 30px; - } - .markdown-tool-menu-item, .tab-menu-item, .header-command-menu .app-menu-item { diff --git a/index.html b/index.html index b57ca427..12513f01 100644 --- a/index.html +++ b/index.html @@ -146,11 +146,18 @@

Markdown Viewer - Online Markd - - diff --git a/styles.css b/styles.css index fd95dc38..1abefbe9 100644 --- a/styles.css +++ b/styles.css @@ -2218,10 +2218,6 @@ body.document-sidebar-collapsed .document-sidebar { gap: 5px; } -.header-right .header-command-btn .btn-text { - display: inline; -} - .header-command-btn { width: auto; padding-inline: 10px; @@ -2235,6 +2231,19 @@ body.document-sidebar-collapsed .document-sidebar { display: inline-flex; } +.header-icon-dropdown .dropdown-toggle::after { + display: none; +} + +.header-toolbar-divider { + width: 1px; + height: 20px; + flex: 0 0 1px; + margin-inline: 2px; + background: var(--border-color); + opacity: 0.72; +} + .app-command-menu.header-new-menu, .app-command-menu.header-export-menu { width: max-content; @@ -2283,6 +2292,59 @@ body.document-sidebar-collapsed .document-sidebar { line-height: 1; } +.header-icon-command.is-active, +.header-icon-command.is-active:hover, +.header-icon-command.is-live-active, +.header-icon-command.is-live-active:hover, +.header-icon-command.sync-active, +.header-icon-command.sync-active:hover { + border-color: color-mix(in srgb, var(--accent-color) 28%, var(--border-color)); + background: color-mix(in srgb, var(--accent-color) 10%, transparent); + color: var(--accent-color); +} + +.header-icon-command#review-toggle:has(.review-toolbar-count:not([hidden])) { + width: auto; + padding-inline: 6px; +} + +.header-view-toolbar { + flex: 0 0 auto; +} + +.header-view-toolbar .view-toggle-btn { + width: 27px; + min-width: 27px; + height: 26px; + padding: 0; + border: 0; + border-radius: 7px; + background: transparent; + color: var(--text-secondary); + box-shadow: none; +} + +.header-view-toolbar .view-toggle-btn:hover, +.header-view-toolbar .view-toggle-btn:focus-visible { + border: 0; + background: var(--toolbar-control-hover); + color: var(--text-color); +} + +.header-view-toolbar .view-toggle-btn.is-active, +.header-view-toolbar .view-toggle-btn.is-active:hover { + border: 0; + background: var(--segmented-surface); + color: var(--accent-color); + box-shadow: var(--segmented-shadow); +} + +.header-view-toolbar .view-toggle-btn:disabled, +.header-view-toolbar .view-toggle-btn[aria-disabled="true"] { + background: transparent; + box-shadow: none; +} + .settings-dropdown { display: inline-flex; } @@ -2800,46 +2862,10 @@ body.document-sidebar-collapsed .document-sidebar { flex-shrink: 0; } -.markdown-toolbar-group--utilities { - position: sticky; - z-index: 6; - inset-inline-end: 0; - margin-inline-start: auto; - border-right: 0; - background: var(--toolbar-surface); -} - -.markdown-toolbar-group--document { - gap: 2px; - padding-inline-start: 6px; -} - .workspace-format-actions { padding-inline: 5px; } -.markdown-toolbar-group--document .tool-button { - flex: 0 0 auto; - width: 30px; - height: 30px; - min-width: 30px; - padding: 0; - border-color: transparent; - border-radius: 7px; - background: transparent; -} - -.markdown-toolbar-group--document .tool-button:hover, -.markdown-toolbar-group--document .tool-button:focus-visible { - border-color: transparent; - background: var(--toolbar-control-hover); -} - -.markdown-toolbar-group--document #review-toggle:has(.review-toolbar-count:not([hidden])) { - width: auto; - padding-inline: 5px; -} - .document-actions-menu { width: 280px; min-width: 280px; @@ -2860,14 +2886,6 @@ body.document-sidebar-collapsed .document-sidebar { z-index: var(--layer-menu); } -.document-command-divider { - width: 1px; - height: 18px; - margin-inline: 4px; - background: var(--border-color); - opacity: 0.75; -} - .markdown-view-toolbar { display: inline-flex; align-items: center; @@ -2879,44 +2897,6 @@ body.document-sidebar-collapsed .document-sidebar { background: var(--segmented-bg); } -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn { - width: 27px; - height: 26px; - min-width: 27px; - border: 0; - border-radius: 7px; - background: transparent; - color: var(--text-secondary); - box-shadow: none; -} - -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn:hover, -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn:focus-visible { - border: 0; - background: var(--toolbar-control-hover); - color: var(--text-color); -} - -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn.is-active, -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn.is-active:hover { - border: 0; - background: var(--segmented-surface); - color: var(--accent-color); - box-shadow: var(--segmented-shadow); -} - -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn:disabled, -.markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn[aria-disabled="true"] { - background: transparent; - box-shadow: none; -} - -.markdown-toolbar-group--utilities .tool-button { - flex: 0 0 auto; - height: 30px; - min-width: 30px; -} - .markdown-toolbar-group:first-child { padding-left: 0; } @@ -3017,17 +2997,6 @@ body.document-sidebar-collapsed .document-sidebar { gap: 5px; } -.markdown-tool-select--insert { - width: 30px; - min-width: 30px; - gap: 0; - padding: 0; -} - -.markdown-tool-select--insert .lucide-ellipsis { - font-size: var(--ui-icon-size-lg); -} - .markdown-tool-select-label { overflow: hidden; max-width: 48px; @@ -3680,12 +3649,6 @@ a:focus { min-width: 44px; } - .markdown-tool-select--insert { - width: 30px !important; - min-width: 30px; - padding-inline: 0 !important; - } - .markdown-format-toolbar { height: 40px !important; } @@ -3699,23 +3662,6 @@ a:focus { height: 40px !important; } - .markdown-toolbar-group--document .tool-button { - width: 30px; - height: 30px; - min-width: 30px; - padding: 0; - } - - .markdown-view-toolbar { - height: 32px; - } - - .markdown-toolbar-group--document .markdown-view-toolbar .view-toggle-btn { - width: 27px; - height: 26px; - min-width: 27px; - } - .document-action-item { min-height: 44px; } @@ -4061,15 +4007,6 @@ a:focus { scroll-padding-inline: 6px; } - .markdown-toolbar-group--utilities { - position: static; - margin-inline-start: 0; - } - - .markdown-tool-select--insert { - min-width: 30px; - } - .markdown-tool-menu-item, .tab-menu-item, .header-command-menu .app-menu-item { diff --git a/tests/e2e/status-toolbar-followup.spec.js b/tests/e2e/status-toolbar-followup.spec.js index d8c97873..f2b3c9f0 100644 --- a/tests/e2e/status-toolbar-followup.spec.js +++ b/tests/e2e/status-toolbar-followup.spec.js @@ -5,27 +5,44 @@ test.beforeEach(async ({ page }) => { await openApp(page); }); -test('header and formatting toolbar expose the requested actions in order', async ({ page }) => { +test('header consolidates icon document actions in the requested order', async ({ page }) => { await page.setViewportSize({ width: 1440, height: 900 }); const header = page.locator('.header-right'); await expect(page.locator('.app-header h1')).toBeVisible(); expect(await page.locator('.app-header h1').evaluate(title => title.getBoundingClientRect().width)).toBeGreaterThan(115); expect(await page.locator('#document-sidebar').evaluate(sidebar => sidebar.getBoundingClientRect().width)).toBeGreaterThanOrEqual(220); - for (const selector of ['#importDropdown', '#share-button', '#live-share-button', '#header-about-button', '#workspaceSettingsDropdown']) { + for (const selector of ['#importDropdown', '#copy-markdown-button', '#toggle-sync', '#review-toggle', '#share-button', '#live-share-button', '#exportDropdown', '#header-about-button', '#workspaceSettingsDropdown']) { await expect(header.locator(selector)).toBeVisible(); } await expect(header.locator('[aria-label="Report an issue"]')).toBeVisible(); - await expect(header.locator('#exportDropdown')).toBeVisible(); - await expect(header.locator('#toggle-sync, #copy-markdown-button')).toHaveCount(0); + await expect(header.locator('.header-view-toolbar .view-toggle-btn')).toHaveCount(3); + await expect(header.locator('#importDropdown .btn-text, #share-button .btn-text, #live-share-button .btn-text, #exportDropdown .btn-text')).toHaveCount(0); + + const headerOrder = await header.locator(':scope > button, :scope > a, :scope > .dropdown > button').evaluateAll(elements => + elements.map(element => element.id || element.getAttribute('aria-label')) + ); + expect(headerOrder).toEqual([ + 'importDropdown', + 'copy-markdown-button', + 'toggle-sync', + 'review-toggle', + 'share-button', + 'live-share-button', + 'exportDropdown', + 'Report an issue', + 'header-about-button', + 'workspaceSettingsDropdown' + ]); const toolbar = page.locator('#markdown-format-toolbar'); await expect(toolbar.locator('.workspace-format-actions [data-md-action="find"]')).toBeVisible(); await expect(toolbar.locator('.workspace-format-actions [data-md-action="fullscreen"]')).toBeVisible(); await expect(toolbar.locator('.workspace-format-actions [data-md-action="find"] i')).toHaveClass(/lucide-search/); await expect(toolbar.locator('.workspace-format-actions [data-md-action="fullscreen"] i')).toHaveClass(/lucide-maximize/); - await expect(toolbar.locator('#review-toggle')).toBeVisible(); - await expect(toolbar.locator('#toggle-sync, #copy-markdown-button')).toHaveCount(2); + await expect(toolbar.locator('#review-toggle, #toggle-sync, #copy-markdown-button')).toHaveCount(0); await expect(toolbar.locator('#exportDropdown')).toHaveCount(0); + await expect(toolbar.locator('.markdown-tool-select--insert, [data-toolbar-menu="insert"]')).toHaveCount(0); + await expect(toolbar.locator('.markdown-toolbar-group--advanced > .markdown-tool-btn')).toHaveCount(9); await toolbar.locator('.workspace-format-actions [data-md-action="find"]').click(); await expect(page.locator('#find-replace-modal')).toBeVisible(); @@ -39,13 +56,13 @@ test('header and formatting toolbar expose the requested actions in order', asyn await fullscreen.click(); await expect.poll(() => page.evaluate(() => Boolean(document.fullscreenElement))).toBe(false); - await toolbar.locator('#toggle-sync').click(); - await expect(toolbar.locator('#toggle-sync')).toHaveAttribute('aria-pressed', 'false'); - await toolbar.locator('#toggle-sync').click(); - await expect(toolbar.locator('#toggle-sync')).toHaveAttribute('aria-pressed', 'true'); + await header.locator('#toggle-sync').click(); + await expect(header.locator('#toggle-sync')).toHaveAttribute('aria-pressed', 'false'); + await header.locator('#toggle-sync').click(); + await expect(header.locator('#toggle-sync')).toHaveAttribute('aria-pressed', 'true'); await stubClipboard(page); - await toolbar.locator('#copy-markdown-button').click(); - await expect(toolbar.locator('#copy-markdown-button')).toHaveAttribute('aria-label', 'Copied'); + await header.locator('#copy-markdown-button').click(); + await expect(header.locator('#copy-markdown-button')).toHaveAttribute('aria-label', 'Copied'); await expect.poll(() => page.evaluate(() => window.__copiedText)).not.toBe(''); await header.locator('#importDropdown').click(); diff --git a/tests/e2e/tab-split-sidebar-update.spec.js b/tests/e2e/tab-split-sidebar-update.spec.js index 2879b8d4..734a8061 100644 --- a/tests/e2e/tab-split-sidebar-update.spec.js +++ b/tests/e2e/tab-split-sidebar-update.spec.js @@ -119,7 +119,7 @@ test('split view uses one combined tab and offers only edit or preview modes', a await expect(page.locator('.view-toolbar [data-view-mode="split"]')).toBeEnabled(); }); -test('header keeps global actions concise and settings groups workspace preferences', async ({ page }) => { +test('header groups document actions before application preferences', async ({ page }) => { const header = page.locator('.header-right'); await expect(header.locator('#importDropdown')).toBeVisible(); await expect(header.locator('#share-button')).toBeVisible(); @@ -128,41 +128,39 @@ test('header keeps global actions concise and settings groups workspace preferen await expect(header.locator('#header-about-button')).toBeVisible(); await expect(header.locator('#workspaceSettingsDropdown')).toBeVisible(); await expect(header.locator('#exportDropdown')).toBeVisible(); - await expect(header.locator('#toggle-sync, #review-toggle, #copy-markdown-button')).toHaveCount(0); + await expect(header.locator('#toggle-sync, #review-toggle, #copy-markdown-button')).toHaveCount(3); + await expect(header.locator('.header-view-toolbar .view-toggle-btn')).toHaveCount(3); const headerActions = await header.locator(':scope > button, :scope > a, :scope > .dropdown > button').evaluateAll(elements => elements.map(element => element.id || element.getAttribute('aria-label')) ); expect(headerActions).toEqual([ 'importDropdown', - 'exportDropdown', + 'copy-markdown-button', + 'toggle-sync', + 'review-toggle', 'share-button', 'live-share-button', + 'exportDropdown', 'Report an issue', 'header-about-button', 'workspaceSettingsDropdown' ]); const formatToolbar = page.locator('#markdown-format-toolbar'); - await expect(formatToolbar.locator('#toggle-sync')).toBeVisible(); - await expect(formatToolbar.locator('#copy-markdown-button')).toBeVisible(); - await expect(formatToolbar.locator('#review-toggle')).toBeVisible(); + await expect(formatToolbar.locator('#toggle-sync, #copy-markdown-button, #review-toggle')).toHaveCount(0); await expect(formatToolbar.locator('#exportDropdown')).toHaveCount(0); await expect(formatToolbar.locator('#documentActionsDropdown')).toHaveCount(0); await expect(page.locator('.app-brand-logo')).toHaveCount(0); - const utilityOrder = await formatToolbar.locator('.markdown-toolbar-group--utilities').evaluate(group => - Array.from(group.children).map(element => element.id || element.className) - ); - expect(utilityOrder.slice(0, 4)).toEqual(['toggle-sync', 'copy-markdown-button', 'review-toggle', 'document-command-divider']); - await formatToolbar.locator('#toggle-sync').click(); - await expect(formatToolbar.locator('#toggle-sync')).toHaveAttribute('aria-pressed', 'false'); - await expect(formatToolbar.locator('#toggle-sync')).toHaveAttribute('aria-label', 'Enable synchronized scrolling'); - await formatToolbar.locator('#toggle-sync').click(); - await expect(formatToolbar.locator('#toggle-sync')).toHaveAttribute('aria-pressed', 'true'); + await header.locator('#toggle-sync').click(); + await expect(header.locator('#toggle-sync')).toHaveAttribute('aria-pressed', 'false'); + await expect(header.locator('#toggle-sync')).toHaveAttribute('aria-label', 'Enable synchronized scrolling'); + await header.locator('#toggle-sync').click(); + await expect(header.locator('#toggle-sync')).toHaveAttribute('aria-pressed', 'true'); await stubClipboard(page); - await formatToolbar.locator('#copy-markdown-button').click(); - await expect(formatToolbar.locator('#copy-markdown-button')).toHaveAttribute('aria-label', 'Copied'); - await expect(formatToolbar.locator('#copy-markdown-button i')).toHaveClass(/lucide-check/); + await header.locator('#copy-markdown-button').click(); + await expect(header.locator('#copy-markdown-button')).toHaveAttribute('aria-label', 'Copied'); + await expect(header.locator('#copy-markdown-button i')).toHaveClass(/lucide-check/); await expect.poll(() => page.evaluate(() => window.__copiedText)).not.toBe(''); const initialTabCount = await page.locator('#tab-list .tab-item').count(); @@ -188,14 +186,8 @@ test('header keeps global actions concise and settings groups workspace preferen await expect(settings.locator('#theme-toggle')).toContainText('Appearance'); await page.locator('#workspaceSettingsDropdown').click(); - const insertToggle = formatToolbar.locator('.markdown-tool-select--insert'); - await expect(insertToggle).toHaveAttribute('aria-label', 'More tools'); - await expect(insertToggle).not.toContainText('Insert'); - await expect(formatToolbar.locator('[data-md-action="diagram"]')).toHaveCount(1); - await expect(formatToolbar.locator('[data-toolbar-menu="insert"] [data-md-action="diagram"]')).toHaveCount(0); - await insertToggle.press('Enter'); - await expect(formatToolbar.locator('[data-toolbar-menu="insert"]')).toHaveClass(/open/); - await page.keyboard.press('Escape'); + await expect(formatToolbar.locator('.markdown-tool-select--insert, [data-toolbar-menu="insert"]')).toHaveCount(0); + await expect(formatToolbar.locator('.markdown-toolbar-group--advanced > .markdown-tool-btn')).toHaveCount(9); await header.locator('#exportDropdown').click(); const exportMenu = page.locator('[aria-labelledby="exportDropdown"]'); @@ -203,8 +195,8 @@ test('header keeps global actions concise and settings groups workspace preferen await expect(exportMenu).not.toHaveClass(/toolbar-portal-menu/); await expect(exportMenu).toBeInViewport(); - await expect(formatToolbar.locator('.markdown-view-toolbar .view-toggle-btn')).toHaveCount(3); - expect(await formatToolbar.locator('.markdown-view-toolbar .view-toggle-btn').evaluateAll(buttons => + await expect(header.locator('.header-view-toolbar .view-toggle-btn')).toHaveCount(3); + expect(await header.locator('.header-view-toolbar .view-toggle-btn').evaluateAll(buttons => buttons.map(button => button.getAttribute('data-view-mode')) )).toEqual(['editor', 'split', 'preview']); const activeStyle = await page.locator('.view-toggle-btn.is-active').evaluate(button => ({ @@ -257,14 +249,15 @@ test('closing a tab keeps the document in Files and reopening restores the tab', await expect(reopenedRow).toHaveCount(0); }); -test('format toolbar consolidates heading, case, alignment, and insert actions', async ({ page }) => { +test('format toolbar keeps compact selectors and exposes advanced insert actions', async ({ page }) => { await expect(page.locator('[data-md-action="clear-formatting"]')).toHaveCount(0); await expect(page.locator('[data-md-action="help"]')).toHaveCount(0); await expect(page.locator('[data-md-action="info"]')).toBeVisible(); await expect(page.locator('[data-toolbar-menu-toggle="heading"]')).toBeVisible(); await expect(page.locator('[data-toolbar-menu-toggle="case"]')).toBeVisible(); await expect(page.locator('[data-toolbar-menu-toggle="alignment"]')).toBeVisible(); - await expect(page.locator('[data-toolbar-menu-toggle="insert"]')).toBeVisible(); + await expect(page.locator('[data-toolbar-menu-toggle="insert"], [data-toolbar-menu="insert"]')).toHaveCount(0); + await expect(page.locator('.markdown-toolbar-group--advanced > .markdown-tool-btn')).toHaveCount(9); await setEditorContent(page, 'toolbar heading'); await page.locator('[data-toolbar-menu-toggle="heading"]').click(); @@ -450,19 +443,9 @@ test('toolbar stays usable on phone and landscape widths', async ({ page }) => { await page.locator('[data-toolbar-menu-toggle="heading"]').click(); await expect(page.locator('[data-toolbar-menu="heading"]')).toBeVisible(); await page.keyboard.press('Escape'); - await page.locator('#markdown-format-toolbar').evaluate(toolbar => { toolbar.scrollLeft = toolbar.scrollWidth; }); - const utilityPlacement = await page.evaluate(() => { - const toolbar = document.querySelector('#markdown-format-toolbar').getBoundingClientRect(); - const utilities = document.querySelector('.markdown-toolbar-group--utilities').getBoundingClientRect(); - return { - toolbarLeft: Math.round(toolbar.left), - toolbarRight: Math.round(toolbar.right), - utilitiesRight: Math.round(utilities.right), - utilitiesLeft: Math.round(utilities.left) - }; - }); - expect(utilityPlacement.utilitiesRight).toBeLessThanOrEqual(utilityPlacement.toolbarRight); - expect(utilityPlacement.utilitiesLeft).toBeGreaterThanOrEqual(utilityPlacement.toolbarLeft); + const toolbar = page.locator('#markdown-format-toolbar'); + await toolbar.evaluate(element => { element.scrollLeft = element.scrollWidth; }); + await expect(toolbar.locator('.markdown-toolbar-group--advanced [data-md-action="alert"]')).toBeInViewport(); expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true); await page.setViewportSize({ width: 844, height: 390 }); diff --git a/tests/e2e/ui-toolbar-redesign.spec.js b/tests/e2e/ui-toolbar-redesign.spec.js index eb8f19c3..314148e9 100644 --- a/tests/e2e/ui-toolbar-redesign.spec.js +++ b/tests/e2e/ui-toolbar-redesign.spec.js @@ -71,8 +71,7 @@ test('shared application overlays use compact type, spacing, and surface styling await expect(page.locator('.about-support')).toContainText('Developed and maintained by ThisIs-Developer.'); await page.locator('#about-modal-close').click(); - await page.locator('.markdown-tool-select--insert').click(); - await page.locator('[data-toolbar-menu="insert"] [data-md-action="alert"]').click(); + await page.locator('.markdown-toolbar-group--advanced [data-md-action="alert"]').click(); await expect(page.locator('#alert-modal')).toHaveClass(/is-visible/); await expect(page.locator('#alert-modal .alert-option').first()).toBeFocused(); await page.keyboard.press('Escape'); @@ -110,36 +109,33 @@ test('every application dialog uses the shared alert modal shell', async ({ page expect(dialogShells.filter(dialog => !dialog.sharedShell || !dialog.header || !dialog.close || !dialog.footer || dialog.padding !== '0px')).toEqual([]); }); -test('toolbar groups preserve actions while simplifying Insert and promoting diagrams', async ({ page }) => { +test('header orders document actions and formatting toolbar exposes advanced tools', async ({ page }) => { await page.setViewportSize({ width: 1440, height: 900 }); + const header = page.locator('.header-right'); const toolbar = page.locator('#markdown-format-toolbar'); - const utilityOrder = await toolbar.locator('.markdown-toolbar-group--utilities').evaluate(group => - Array.from(group.children).map(element => element.id || element.className) + const documentActionOrder = await header.locator(':scope > button, :scope > .dropdown > button').evaluateAll(elements => + elements.map(element => element.id) ); - expect(utilityOrder.slice(0, 4)).toEqual([ - 'toggle-sync', + expect(documentActionOrder.slice(0, 7)).toEqual([ + 'importDropdown', 'copy-markdown-button', + 'toggle-sync', 'review-toggle', - 'document-command-divider' + 'share-button', + 'live-share-button', + 'exportDropdown' ]); - const insertToggle = toolbar.locator('.markdown-tool-select--insert'); - await expect(insertToggle).toHaveText(''); - await expect(insertToggle).toHaveAttribute('title', 'More tools'); - await expect(insertToggle.locator('.lucide-ellipsis')).toHaveCount(1); - await expect(toolbar.locator('[data-md-action="diagram"]')).toHaveCount(1); - await expect(toolbar.locator('[data-toolbar-menu="insert"] [data-md-action="diagram"]')).toHaveCount(0); - - await insertToggle.press('Enter'); - const insertMenu = toolbar.locator('[data-toolbar-menu="insert"]'); - await expect(insertMenu).toHaveClass(/open/); - await expect(insertMenu.locator('.markdown-tool-menu-item').first()).toBeFocused(); - await page.keyboard.press('Escape'); - await expect(insertMenu).not.toHaveClass(/open/); - await expect(insertToggle).toBeFocused(); + const advancedActions = await toolbar.locator('.markdown-toolbar-group--advanced > .markdown-tool-btn').evaluateAll(buttons => + buttons.map(button => button.getAttribute('data-md-action')) + ); + expect(advancedActions).toEqual([ + 'diagram', 'reference', 'code-block', 'terminal-block', 'horizontal-rule', + 'date-time', 'emoji', 'symbols', 'alert' + ]); + await expect(toolbar.locator('.markdown-tool-select--insert, [data-toolbar-menu="insert"]')).toHaveCount(0); - await insertToggle.click(); - await insertMenu.locator('[data-md-action="emoji"]').click(); + await toolbar.locator('[data-md-action="emoji"]').click(); await expect(page.locator('#emoji-modal')).toBeVisible(); await page.locator('#emoji-modal-search').press('Escape'); await expect(page.locator('#emoji-modal')).toBeHidden(); @@ -227,7 +223,7 @@ test('shared interface roles use the application type and icon scale', async ({ expect(sizing.mobileIcons).toEqual(['14px']); }); -test('New, Export, and formatting menus share one visual system and keyboard dismissal', async ({ page }) => { +test('New and Export menus share one visual system and keyboard dismissal', async ({ page }) => { await page.setViewportSize({ width: 1440, height: 900 }); await page.locator('#importDropdown').click(); @@ -250,16 +246,7 @@ test('New, Export, and formatting menus share one visual system and keyboard dis expect(exportSurface).toEqual(newSurface); await page.keyboard.press('Escape'); - const insertToggle = page.locator('.markdown-tool-select--insert'); - await insertToggle.click(); - const insertMenu = page.locator('[data-toolbar-menu="insert"]'); - const insertSurface = await insertMenu.evaluate(menu => { - const style = getComputedStyle(menu); - return [style.backgroundColor, style.borderColor, style.borderRadius, style.boxShadow]; - }); - expect(insertSurface).toEqual(newSurface); - await page.locator('#markdown-editor').click(); - await expect(insertMenu).not.toHaveClass(/open/); + await expect(page.locator('.markdown-tool-select--insert, [data-toolbar-menu="insert"]')).toHaveCount(0); }); test('toolbar uses theme surfaces and remains usable at desktop and phone widths', async ({ page }) => { @@ -269,8 +256,8 @@ test('toolbar uses theme surfaces and remains usable at desktop and phone widths const desktopSizing = await page.evaluate(() => { const toolbar = document.querySelector('#markdown-format-toolbar'); const formatButton = toolbar.querySelector('.markdown-tool-btn'); - const utilityButton = toolbar.querySelector('.markdown-toolbar-group--document .tool-button'); - const viewButton = toolbar.querySelector('.markdown-view-toolbar .view-toggle-btn'); + const utilityButton = document.querySelector('.header-right #copy-markdown-button'); + const viewButton = document.querySelector('.header-view-toolbar .view-toggle-btn'); return { toolbarHeight: toolbar.getBoundingClientRect().height, formatButton: [formatButton.getBoundingClientRect().width, formatButton.getBoundingClientRect().height], @@ -294,14 +281,6 @@ test('toolbar uses theme surfaces and remains usable at desktop and phone widths const button = toolbar.querySelector('.markdown-tool-btn'); return [button.getBoundingClientRect().width, button.getBoundingClientRect().height]; })(), - utilityButton: (() => { - const button = toolbar.querySelector('.markdown-toolbar-group--document .tool-button'); - return [button.getBoundingClientRect().width, button.getBoundingClientRect().height]; - })(), - viewButton: (() => { - const button = toolbar.querySelector('.markdown-view-toolbar .view-toggle-btn'); - return [button.getBoundingClientRect().width, button.getBoundingClientRect().height]; - })(), minimumTarget: Math.min(...buttons.map(button => button.getBoundingClientRect().height)) }; }); @@ -309,8 +288,8 @@ test('toolbar uses theme surfaces and remains usable at desktop and phone widths expect(mobileSizing.toolbarScrollable).toBe(true); expect(mobileSizing.toolbarHeight).toBe(desktopSizing.toolbarHeight); expect(mobileSizing.formatButton).toEqual(desktopSizing.formatButton); - expect(mobileSizing.utilityButton).toEqual(desktopSizing.utilityButton); - expect(mobileSizing.viewButton).toEqual(desktopSizing.viewButton); + expect(desktopSizing.utilityButton).toEqual([30, 30]); + expect(desktopSizing.viewButton).toEqual([27, 26]); expect(mobileSizing.minimumTarget).toBeGreaterThanOrEqual(26); await page.evaluate(() => { document.documentElement.style.fontSize = '20px'; }); From 8904d47d7f860512dcaccfe9f89048f7b65e899f Mon Sep 17 00:00:00 2001 From: Baivab Sarkar Date: Wed, 12 Aug 2026 01:00:10 +0530 Subject: [PATCH 02/17] feat(theme): add icon switch controls --- assets/lucide-icons.css | 1 + desktop-app/resources/index.html | 8 +++---- desktop-app/resources/js/script.js | 22 +++++++++++++------- desktop-app/resources/styles.css | 31 ++++++++++++++++++++++++++++ index.html | 8 +++---- script.js | 22 +++++++++++++------- styles.css | 31 ++++++++++++++++++++++++++++ tests/e2e/responsive-storage.spec.js | 20 +++++++++++++++++- 8 files changed, 118 insertions(+), 25 deletions(-) diff --git a/assets/lucide-icons.css b/assets/lucide-icons.css index dda69892..73a0a336 100644 --- a/assets/lucide-icons.css +++ b/assets/lucide-icons.css @@ -133,6 +133,7 @@ .lucide-star-filled { --lucide-icon: url("data:image/svg+xml;base64,PCEtLSBAbGljZW5zZSBsdWNpZGUtc3RhdGljIHYxLjI1LjAgLSBJU0MgLS0+IDxzdmcgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtc3RhciIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiID4gPHBhdGggZD0iTTExLjUyNSAyLjI5NWEuNTMuNTMgMCAwIDEgLjk1IDBsMi4zMSA0LjY3OWEyLjEyMyAyLjEyMyAwIDAgMCAxLjU5NSAxLjE2bDUuMTY2Ljc1NmEuNTMuNTMgMCAwIDEgLjI5NC45MDRsLTMuNzM2IDMuNjM4YTIuMTIzIDIuMTIzIDAgMCAwLS42MTEgMS44NzhsLjg4MiA1LjE0YS41My41MyAwIDAgMS0uNzcxLjU2bC00LjYxOC0yLjQyOGEyLjEyMiAyLjEyMiAwIDAgMC0xLjk3MyAwTDYuMzk2IDIxLjAxYS41My41MyAwIDAgMS0uNzctLjU2bC44ODEtNS4xMzlhMi4xMjIgMi4xMjIgMCAwIDAtLjYxMS0xLjg3OUwyLjE2IDkuNzk1YS41My41MyAwIDAgMSAuMjk0LS45MDZsNS4xNjUtLjc1NWEyLjEyMiAyLjEyMiAwIDAgMCAxLjU5Ny0xLjE2eiIgLz4gPC9zdmc+"); } .lucide-strikethrough { --lucide-icon: url("data:image/svg+xml;base64,PCEtLSBAbGljZW5zZSBsdWNpZGUtc3RhdGljIHYxLjI1LjAgLSBJU0MgLS0+IDxzdmcgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtc3RyaWtldGhyb3VnaCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPiA8cGF0aCBkPSJNMTYgNEg5YTMgMyAwIDAgMC0yLjgzIDQiIC8+IDxwYXRoIGQ9Ik0xNCAxMmE0IDQgMCAwIDEgMCA4SDYiIC8+IDxsaW5lIHgxPSI0IiB4Mj0iMjAiIHkxPSIxMiIgeTI9IjEyIiAvPiA8L3N2Zz4="); } .lucide-sun { --lucide-icon: url("data:image/svg+xml;base64,PCEtLSBAbGljZW5zZSBsdWNpZGUtc3RhdGljIHYxLjI1LjAgLSBJU0MgLS0+IDxzdmcgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtc3VuIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiA+IDxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjQiIC8+IDxwYXRoIGQ9Ik0xMiAydjIiIC8+IDxwYXRoIGQ9Ik0xMiAyMHYyIiAvPiA8cGF0aCBkPSJtNC45MyA0LjkzIDEuNDEgMS40MSIgLz4gPHBhdGggZD0ibTE3LjY2IDE3LjY2IDEuNDEgMS40MSIgLz4gPHBhdGggZD0iTTIgMTJoMiIgLz4gPHBhdGggZD0iTTIwIDEyaDIiIC8+IDxwYXRoIGQ9Im02LjM0IDE3LjY2LTEuNDEgMS40MSIgLz4gPHBhdGggZD0ibTE5LjA3IDQuOTMtMS40MSAxLjQxIiAvPiA8L3N2Zz4="); } +.lucide-sun-medium { --lucide-icon: url("data:image/svg+xml;base64,PCEtLSBAbGljZW5zZSBsdWNpZGUtc3RhdGljIHYxLjI1LjAgLSBJU0MgLS0+IDxzdmcgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtc3VuLW1lZGl1bSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPiA8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSI0IiAvPiA8cGF0aCBkPSJNMTIgM3YxIiAvPiA8cGF0aCBkPSJNMTIgMjB2MSIgLz4gPHBhdGggZD0iTTMgMTJoMSIgLz4gPHBhdGggZD0iTTIwIDEyaDEiIC8+IDxwYXRoIGQ9Im0xOC4zNjQgNS42MzYtLjcwNy43MDciIC8+IDxwYXRoIGQ9Im02LjM0MyAxNy42NTctLjcwNy43MDciIC8+IDxwYXRoIGQ9Im01LjYzNiA1LjYzNi43MDcuNzA3IiAvPiA8cGF0aCBkPSJtMTcuNjU3IDE3LjY1Ny43MDcuNzA3IiAvPiA8L3N2Zz4="); } .lucide-table-2 { --lucide-icon: url("data:image/svg+xml;base64,PCEtLSBAbGljZW5zZSBsdWNpZGUtc3RhdGljIHYxLjI1LjAgLSBJU0MgLS0+IDxzdmcgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtdGFibGUtMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPiA8cGF0aCBkPSJNOSAzSDVhMiAyIDAgMCAwLTIgMnY0bTYtNmgxMGEyIDIgMCAwIDEgMiAydjRNOSAzdjE4bTAgMGgxMGEyIDIgMCAwIDAgMi0yVjlNOSAyMUg1YTIgMiAwIDAgMS0yLTJWOW0wIDBoMTgiIC8+IDwvc3ZnPg=="); } .lucide-trash-2 { --lucide-icon: url("data:image/svg+xml;base64,PCEtLSBAbGljZW5zZSBsdWNpZGUtc3RhdGljIHYxLjI1LjAgLSBJU0MgLS0+IDxzdmcgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtdHJhc2gtMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPiA8cGF0aCBkPSJNMTAgMTF2NiIgLz4gPHBhdGggZD0iTTE0IDExdjYiIC8+IDxwYXRoIGQ9Ik0xOSA2djE0YTIgMiAwIDAgMS0yIDJIN2EyIDIgMCAwIDEtMi0yVjYiIC8+IDxwYXRoIGQ9Ik0zIDZoMTgiIC8+IDxwYXRoIGQ9Ik04IDZWNGEyIDIgMCAwIDEgMi0yaDRhMiAyIDAgMCAxIDIgMnYyIiAvPiA8L3N2Zz4="); } .lucide-triangle-alert { --lucide-icon: url("data:image/svg+xml;base64,PCEtLSBAbGljZW5zZSBsdWNpZGUtc3RhdGljIHYxLjI1LjAgLSBJU0MgLS0+IDxzdmcgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtdHJpYW5nbGUtYWxlcnQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiID4gPHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIiAvPiA8cGF0aCBkPSJNMTIgOXY0IiAvPiA8cGF0aCBkPSJNMTIgMTdoLjAxIiAvPiA8L3N2Zz4="); } diff --git a/desktop-app/resources/index.html b/desktop-app/resources/index.html index 2f753352..de933f41 100644 --- a/desktop-app/resources/index.html +++ b/desktop-app/resources/index.html @@ -122,8 +122,8 @@

Markdown Viewer - Online Markd
Workspace settings
-