Skip to content

Add download button, tab drag-drop, keyboard shortcuts#1

Open
afar1 wants to merge 1 commit intoneedle-tools:mainfrom
afar1:feature/download-tabs-shortcuts
Open

Add download button, tab drag-drop, keyboard shortcuts#1
afar1 wants to merge 1 commit intoneedle-tools:mainfrom
afar1:feature/download-tabs-shortcuts

Conversation

@afar1
Copy link
Copy Markdown

@afar1 afar1 commented Feb 8, 2026

Summary

  • Download markdown button (↓) to save current page as .md file
  • Tab drag and drop reordering
  • Cmd+W to close tab, Cmd+N/T for new tab with URL bar auto-focus
  • Fix HTML extraction to capture all article sections (not just first)
  • SPA URL tracking for proper address bar updates

Test plan

  • Open a page, click ↓ to download markdown
  • Drag tabs to reorder them
  • Cmd+W closes current tab
  • Cmd+T/N opens new tab and focuses URL bar
  • Load a multi-article page (e.g. fieldtheory.dev) and verify all sections convert

- Download markdown button to save page as .md file
- Tab drag and drop reordering
- Cmd+W close tab, Cmd+N/T new tab with URL focus
- Fix extraction to capture all article sections
- SPA URL tracking for proper address bar updates
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds several user-facing features to improve the browsing experience: a download button for saving markdown files, tab drag-and-drop reordering, additional keyboard shortcuts (Cmd+W to close tabs, Cmd+N for new tabs with URL bar auto-focus), fixes HTML extraction to capture all article sections on multi-article pages, and implements SPA URL tracking to keep the address bar synchronized with single-page application navigation.

Changes:

  • Download markdown button (↓) with smart filename generation from URL or title
  • Drag-and-drop tab reordering with visual feedback (opacity and border indicators)
  • Keyboard shortcuts: Cmd+W closes active tab, Cmd+N/T creates new tab with URL bar focus
  • Removed article tag fallback in HTML extraction to capture all sections instead of just the first article
  • SPA URL tracking via history API hooks and polling to update address bar on client-side navigation

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/toolbar-svelte/lib/rpc.ts Added downloadMarkdown function with filename sanitization; removed article tag matching fallback to fix multi-article extraction
src/toolbar-svelte/App.svelte Added drag-drop handlers and state; implemented Cmd+W/N/T shortcuts; added SPA URL tracking in preload script; added download button UI
src/bun/index.ts Removed article tag matching for consistency; added URL update in refetchForViewMode for redirect handling; whitespace cleanup

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +447 to +459
function handleTabDrop(e: DragEvent, targetTabId: number) {
e.preventDefault();
if (draggedTabId !== null && draggedTabId !== targetTabId) {
// Reorder tabs locally
const tabs = [...appStore.tabs];
const draggedIndex = tabs.findIndex(t => t.id === draggedTabId);
const targetIndex = tabs.findIndex(t => t.id === targetTabId);

if (draggedIndex !== -1 && targetIndex !== -1) {
const [draggedTab] = tabs.splice(draggedIndex, 1);
tabs.splice(targetIndex, 0, draggedTab);
appStore.setTabs(tabs);
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tab reordering only updates the local frontend state but does not persist the new order to the backend. When loadTabs is called (e.g., after creating/closing tabs or receiving tab updates from the backend), the tab order will revert to the backend order, losing the user's drag-drop reordering. Consider adding a reorderTabs RPC call to sync the new order with the backend tabs array, or track tab order separately from the tabs array.

Copilot uses AI. Check for mistakes.
filename = `${content.title.replace(/[^a-z0-9]/gi, "-").toLowerCase()}.md`;
}
} catch {}

Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filename generation could produce empty or invalid filenames in edge cases. For example, if pathname consists only of slashes (like "///") and hostname is empty/invalid, pathPart could be empty. Similarly, if title contains only special characters, the sanitized filename could be empty. Consider adding a fallback to ensure a valid filename is always generated, such as defaulting to "download.md" if the generated filename is empty after sanitization.

Suggested change
// Ensure we always have a valid, non-empty filename
filename = filename.trim();
if (!filename || filename === ".md") {
filename = "download.md";
}

Copilot uses AI. Check for mistakes.
"history.pushState = function() { origPush.apply(this, arguments); checkUrl(); };",
"history.replaceState = function() { origReplace.apply(this, arguments); checkUrl(); };",
"window.addEventListener('popstate', checkUrl);",
"setInterval(checkUrl, 500);",
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SPA URL tracking uses a 500ms polling interval as a fallback to detect URL changes. While this ensures URL changes are caught even if history methods aren't used, it could be wasteful. Consider increasing the interval (e.g., 1000ms or 2000ms) to reduce unnecessary polling, since the primary detection mechanism (history.pushState/replaceState hooks and popstate event) should handle most SPA navigation cases.

Suggested change
"setInterval(checkUrl, 500);",
"setInterval(checkUrl, 2000);",

Copilot uses AI. Check for mistakes.
class="download-btn electrobun-webkit-app-region-no-drag"
disabled={!appStore.currentContent?.markdown}
onclick={() => downloadMarkdown()}
title="Download Markdown"
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The download button should include an aria-label attribute for better accessibility. The down arrow symbol (↓) might not be clearly announced by screen readers. Consider adding aria-label="Download Markdown" to complement the title attribute.

Suggested change
title="Download Markdown"
title="Download Markdown"
aria-label="Download Markdown"

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants