feat: Add "open in new tab" link to history entries

Each history entry now has a third action: a real anchor (rendered via
the Button's href, target="_blank") linking to the editor with that
entry's serialized state. Being a normal link, users can also copy it or
open it in a new tab via the context menu.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sidharth Vinod
2026-06-08 20:03:31 +05:30
co-authored by Claude Opus 4.8
parent 64e8822e32
commit 8dd9cb49a0
2 changed files with 34 additions and 0 deletions
+15
View File
@@ -2,6 +2,7 @@
import Card from '$lib/components/Card/Card.svelte';
import type { HistoryEntry, HistoryType, State, Tab } from '$lib/types';
import { notify, prompt } from '$lib/util/notify';
import { serializeState } from '$lib/util/serde';
import { inputStateStore } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import dayjs from 'dayjs';
@@ -14,6 +15,7 @@
import UploadIcon from '~icons/material-symbols/upload-rounded';
import HistoryIcon from '~icons/mdi/clock-outline';
import GitAltIcon from '~icons/mdi/git';
import OpenInNewIcon from '~icons/material-symbols/open-in-new-rounded';
import { Button } from '../ui/button';
import { Separator } from '../ui/separator';
import {
@@ -99,6 +101,10 @@
const restoreHistoryItem = (state: State): void => {
inputStateStore.set({ ...state, updateDiagram: true });
};
// Absolute editor URL for an entry, so the link can be opened in a new tab or copied.
const entryUrl = (state: State): string =>
`${window.location.origin}${window.location.pathname}#${serializeState(state)}`;
</script>
<Card onselect={tabSelectHandler} isOpen isClosable={false} {tabs} activeTabID={historyState.mode}>
@@ -160,6 +166,15 @@
<span class="text-sm whitespace-nowrap text-primary-foreground/50">
{dayjs(time).fromNow()}
</span>
<Button
href={entryUrl(state)}
target="_blank"
rel="noopener"
size="icon"
variant="ghost"
title="Open in new tab">
<OpenInNewIcon />
</Button>
<Button
size="icon"
variant="ghost"
+19
View File
@@ -67,6 +67,25 @@ test.describe('History', () => {
await expect(page.locator('#view')).toContainText('NewYear');
});
test('each entry has a copyable link that opens it in a new tab', async ({ page }) => {
await page.evaluate(
(manual) => localStorage.setItem('manualHistoryStore', manual),
JSON.stringify(manualHistory)
);
await page.reload();
await openHistory(page);
// It is a real link (so it can be copied / opened in a new tab), not a button.
const link = page.getByRole('link', { name: 'Open in new tab' }).first();
await expect(link).toHaveAttribute('target', '_blank');
const href = await link.getAttribute('href');
expect(href).toContain('/edit#pako:');
// Following it loads that entry's diagram.
await page.goto(href ?? '');
await expect(page.locator('#view')).toContainText('Halloween');
});
test('keeps the active tab highlighted when switching modes', async ({ page }) => {
await openHistory(page);
const saved = page.getByRole('tab', { name: 'Saved' });