diff --git a/scripts/verify-ui.mjs b/scripts/verify-ui.mjs
index eb3686f..00f5237 100644
--- a/scripts/verify-ui.mjs
+++ b/scripts/verify-ui.mjs
@@ -367,7 +367,7 @@ try {
check('encryption lands on the result page', onResult)
const shareLinkGone = await page2.evaluate(() => !window.__sb.text().includes('Copy Share Link'))
- check('Copy Share Link button is removed', shareLinkGone)
+ check('share link not offered for local-only encryption (no pastebin link)', shareLinkGone)
await page2.evaluate(() => window.__sb.clickButton('Decrypt'))
const resultDecryptDialog = await page2.evaluate(() => window.__sb.waitForText('Decryption Key'))
@@ -463,9 +463,73 @@ try {
await shot(page2, '9-open-plain-paste')
check('opening a plain paste loads its content into the editor', opened)
}
+
} else {
console.log('⏭ PASTEBIN_API_KEY not set — skipping the live paste-link checks')
}
+
+ // ── 6. Copy Share Link → securebin.org viewer URL (offline via seeded history)
+ await sw.evaluate(async (encText) => {
+ await chrome.storage.local.set({
+ history: [{
+ id: 'sharetest1',
+ action: 'Encrypt to Pastebin',
+ pastebinLink: 'https://pastebin.com/SHARE123',
+ key: 'share-verify-pass',
+ encText,
+ encMode: 'AES-GCM',
+ keyLength: 16,
+ date: Date.now(),
+ title: 'Share Test 8814',
+ format: 'text',
+ privacy: '1',
+ expiry: 'N',
+ }],
+ })
+ }, ciphertext)
+
+ const page3 = await browser.newPage()
+ await page3.goto('https://pastebin.com/doc_api', { waitUntil: 'networkidle2' })
+ await page3.evaluate(pageHelpers)
+ const sl = await injectAndSend('https://pastebin.com/doc_api', { type: 'SB_OPEN' })
+ check('panel opens for the share-link flow', !sl.error, sl.error)
+
+ await page3.evaluate(async () => {
+ const start = Date.now()
+ while (Date.now() - start < 4000) {
+ const nav = window.__sb.buttons().find(b => b.title === 'Pastes')
+ if (nav) { nav.click(); return }
+ await new Promise(r => setTimeout(r, 100))
+ }
+ })
+ // History rows are labeled by their pastebin link
+ const historyShown = await page3.evaluate(() => window.__sb.waitForText('SHARE123'))
+ check('seeded history item appears', historyShown)
+
+ await page3.evaluate(() => {
+ const leaf = [...(window.__sb.root()?.querySelectorAll('*') ?? [])]
+ .reverse()
+ .find(el => el.childElementCount === 0 && (el.textContent ?? '').includes('SHARE123'))
+ leaf?.click()
+ })
+ const shareBtnShown = await page3.evaluate(() => window.__sb.waitForText('Copy Share Link'))
+ check('result page offers Copy Share Link', shareBtnShown)
+
+ try {
+ await browser.defaultBrowserContext().overridePermissions('https://pastebin.com', ['clipboard-read', 'clipboard-write', 'clipboard-sanitized-write'])
+ } catch {
+ await browser.defaultBrowserContext().overridePermissions('https://pastebin.com', ['clipboard-read', 'clipboard-write'])
+ }
+ await page3.bringToFront()
+ await page3.evaluate(() => window.__sb.clickButton('Copy Share Link'))
+ await new Promise(r => setTimeout(r, 400))
+ const copied = await page3.evaluate(() => navigator.clipboard.readText().catch(() => ''))
+ await shot(page3, '10-share-link')
+ check(
+ 'share link is the securebin.org viewer URL with the key in the fragment',
+ copied === 'https://securebin.org/SHARE123#key=share-verify-pass',
+ copied || 'clipboard empty',
+ )
} finally {
await browser.close()
}
diff --git a/src/routes/Result.tsx b/src/routes/Result.tsx
index 1d293f3..e7b5215 100644
--- a/src/routes/Result.tsx
+++ b/src/routes/Result.tsx
@@ -28,6 +28,7 @@ export default function Result() {
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
const [deleting, setDeleting] = useState(false)
const [deleteError, setDeleteError] = useState('')
+ const [shareCopied, setShareCopied] = useState(false)
const [contentCopied, setContentCopied] = useState(false)
const [postDropdownOpen, setPostDropdownOpen] = useState(false)
@@ -90,6 +91,18 @@ export default function Result() {
navigate('/home')
}
+ const handleCopyShareLink = async () => {
+ if (!hasPastebin || !item.key) return
+ // Opens in the securebin.org viewer (same path as pastebin.com); the
+ // passkey rides in the fragment, which browsers never send to servers —
+ // decryption happens in the recipient's browser
+ const pasteKey = extractPasteKey(item.pastebinLink)
+ const shareLink = `https://securebin.org/${pasteKey}#key=${encodeURIComponent(item.key)}`
+ await navigator.clipboard.writeText(shareLink)
+ setShareCopied(true)
+ setTimeout(() => setShareCopied(false), 2000)
+ }
+
// Load draft content into editor with the chosen action, then navigate to editor
const handleOpenInEditor = (action?: EditorAction) => {
const text = item.encText ?? ''
@@ -202,6 +215,27 @@ export default function Result() {
{/* Passkey */}
{item.key &&
+ Link + passkey in one URL — decrypts on securebin.org, no extension needed. Anyone with the link can read the paste. +
+Actions