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:
co-authored by
Claude Opus 4.8
parent
64e8822e32
commit
8dd9cb49a0
@@ -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"
|
||||
|
||||
@@ -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' });
|
||||
|
||||
Reference in New Issue
Block a user