From c06fffc9941fdc32783ba9aec3dc12cac9a17758 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 04:43:58 +0000 Subject: [PATCH 1/3] Initial plan From c857f3b5ac62a98b0afc45098aa8daf3d89b0ee9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 04:49:11 +0000 Subject: [PATCH 2/3] Add share button with URL-encoded markdown using pako compression Co-authored-by: ThisIs-Developer <109382325+ThisIs-Developer@users.noreply.github.com> --- index.html | 9 +++++++ script.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/index.html b/index.html index acdf8c0..73d4118 100644 --- a/index.html +++ b/index.html @@ -54,6 +54,7 @@ +
@@ -119,6 +120,10 @@

Markdown Viewer

Copy + + @@ -191,6 +196,10 @@
Menu
Copy + + diff --git a/script.js b/script.js index 16ab0e2..fd6261c 100644 --- a/script.js +++ b/script.js @@ -57,6 +57,8 @@ document.addEventListener("DOMContentLoaded", function () { const mobileExportPdf = document.getElementById("mobile-export-pdf"); const mobileCopyMarkdown = document.getElementById("mobile-copy-markdown"); const mobileThemeToggle = document.getElementById("mobile-theme-toggle"); + const shareButton = document.getElementById("share-button"); + const mobileShareButton = document.getElementById("mobile-share-button"); // Check dark mode preference first for proper initialization const prefersDarkMode = @@ -692,6 +694,7 @@ This is a fully client-side application. Your content never leaves your browser mobileExportHtml.addEventListener("click", () => exportHtml.click()); mobileExportPdf.addEventListener("click", () => exportPdf.click()); mobileCopyMarkdown.addEventListener("click", () => copyMarkdownButton.click()); + mobileShareButton.addEventListener("click", () => shareButton.click()); mobileThemeToggle.addEventListener("click", () => { themeToggle.click(); mobileThemeToggle.innerHTML = themeToggle.innerHTML + " Toggle Dark Mode"; @@ -1526,6 +1529,81 @@ This is a fully client-side application. Your content never leaves your browser }, 2000); } + // ============================================ + // Share via URL (pako compression + base64url) + // ============================================ + + const MAX_SHARE_URL_LENGTH = 32000; + + function encodeMarkdownForShare(text) { + const compressed = pako.deflate(new TextEncoder().encode(text)); + const chunkSize = 0x8000; + let binary = ''; + for (let i = 0; i < compressed.length; i += chunkSize) { + binary += String.fromCharCode.apply(null, compressed.subarray(i, i + chunkSize)); + } + return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); + } + + function decodeMarkdownFromShare(encoded) { + const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/'); + const binary = atob(base64); + const bytes = Uint8Array.from(binary, c => c.charCodeAt(0)); + return new TextDecoder().decode(pako.inflate(bytes)); + } + + shareButton.addEventListener("click", function () { + const markdownText = markdownEditor.value; + let encoded; + try { + encoded = encodeMarkdownForShare(markdownText); + } catch (e) { + console.error("Share encoding failed:", e); + alert("Failed to encode content for sharing: " + e.message); + return; + } + + const shareUrl = window.location.origin + window.location.pathname + '#share=' + encoded; + if (shareUrl.length > MAX_SHARE_URL_LENGTH) { + alert("The document is too large to share via URL. Please reduce content size and try again."); + return; + } + + window.location.hash = 'share=' + encoded; + + const originalText = shareButton.innerHTML; + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(shareUrl).then(() => { + shareButton.innerHTML = ' Copied!'; + setTimeout(() => { shareButton.innerHTML = originalText; }, 2000); + }).catch(() => { + shareButton.innerHTML = ' Linked!'; + setTimeout(() => { shareButton.innerHTML = originalText; }, 2000); + }); + } else { + shareButton.innerHTML = ' Linked!'; + setTimeout(() => { shareButton.innerHTML = originalText; }, 2000); + } + }); + + function loadFromShareHash() { + if (typeof pako === 'undefined') return; + const hash = window.location.hash; + if (!hash.startsWith('#share=')) return; + const encoded = hash.slice('#share='.length); + if (!encoded) return; + try { + const decoded = decodeMarkdownFromShare(encoded); + markdownEditor.value = decoded; + renderMarkdown(); + } catch (e) { + console.error("Failed to load shared content:", e); + alert("The shared URL could not be decoded. It may be corrupted or incomplete."); + } + } + + loadFromShareHash(); + const dropEvents = ["dragenter", "dragover", "dragleave", "drop"]; dropEvents.forEach((eventName) => { From 92c2fdf0094ceade0b1523c34576f95b387cec29 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 04:53:48 +0000 Subject: [PATCH 3/3] Fix invalid Docker tag on PR events by using static sha- prefix Co-authored-by: ThisIs-Developer <109382325+ThisIs-Developer@users.noreply.github.com> --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index b489d2f..6a5177a 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -39,7 +39,7 @@ jobs: tags: | type=ref,event=branch type=ref,event=pr - type=sha,prefix={{branch}}- + type=sha,prefix=sha- type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Docker image