Skip to content

Commit c857f3b

Browse files
Add share button with URL-encoded markdown using pako compression
Co-authored-by: ThisIs-Developer <109382325+ThisIs-Developer@users.noreply.github.com>
1 parent c06fffc commit c857f3b

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

index.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454

5555
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>
5656
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js"></script>
57+
<script src="https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb" crossorigin="anonymous"></script>
5758
</head>
5859
<body>
5960
<div class="app-container">
@@ -119,6 +120,10 @@ <h1 class="h4 mb-0 me-2">Markdown Viewer</h1>
119120
<i class="bi bi-clipboard"></i> Copy
120121
</button>
121122

123+
<button id="share-button" class="tool-button" title="Share via URL">
124+
<i class="bi bi-share"></i> Share
125+
</button>
126+
122127
<button id="theme-toggle" class="tool-button" title="Toggle Dark Mode">
123128
<i class="bi bi-moon"></i>
124129
</button>
@@ -191,6 +196,10 @@ <h5>Menu</h5>
191196
<i class="bi bi-clipboard me-2"></i> Copy
192197
</button>
193198

199+
<button id="mobile-share-button" class="mobile-menu-item" title="Share via URL">
200+
<i class="bi bi-share me-2"></i> Share
201+
</button>
202+
194203
<button id="mobile-theme-toggle" class="mobile-menu-item" title="Toggle Dark Mode">
195204
<i class="bi bi-moon me-2"></i> Dark Mode
196205
</button>

script.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ document.addEventListener("DOMContentLoaded", function () {
5757
const mobileExportPdf = document.getElementById("mobile-export-pdf");
5858
const mobileCopyMarkdown = document.getElementById("mobile-copy-markdown");
5959
const mobileThemeToggle = document.getElementById("mobile-theme-toggle");
60+
const shareButton = document.getElementById("share-button");
61+
const mobileShareButton = document.getElementById("mobile-share-button");
6062

6163
// Check dark mode preference first for proper initialization
6264
const prefersDarkMode =
@@ -692,6 +694,7 @@ This is a fully client-side application. Your content never leaves your browser
692694
mobileExportHtml.addEventListener("click", () => exportHtml.click());
693695
mobileExportPdf.addEventListener("click", () => exportPdf.click());
694696
mobileCopyMarkdown.addEventListener("click", () => copyMarkdownButton.click());
697+
mobileShareButton.addEventListener("click", () => shareButton.click());
695698
mobileThemeToggle.addEventListener("click", () => {
696699
themeToggle.click();
697700
mobileThemeToggle.innerHTML = themeToggle.innerHTML + " Toggle Dark Mode";
@@ -1526,6 +1529,81 @@ This is a fully client-side application. Your content never leaves your browser
15261529
}, 2000);
15271530
}
15281531

1532+
// ============================================
1533+
// Share via URL (pako compression + base64url)
1534+
// ============================================
1535+
1536+
const MAX_SHARE_URL_LENGTH = 32000;
1537+
1538+
function encodeMarkdownForShare(text) {
1539+
const compressed = pako.deflate(new TextEncoder().encode(text));
1540+
const chunkSize = 0x8000;
1541+
let binary = '';
1542+
for (let i = 0; i < compressed.length; i += chunkSize) {
1543+
binary += String.fromCharCode.apply(null, compressed.subarray(i, i + chunkSize));
1544+
}
1545+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
1546+
}
1547+
1548+
function decodeMarkdownFromShare(encoded) {
1549+
const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
1550+
const binary = atob(base64);
1551+
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
1552+
return new TextDecoder().decode(pako.inflate(bytes));
1553+
}
1554+
1555+
shareButton.addEventListener("click", function () {
1556+
const markdownText = markdownEditor.value;
1557+
let encoded;
1558+
try {
1559+
encoded = encodeMarkdownForShare(markdownText);
1560+
} catch (e) {
1561+
console.error("Share encoding failed:", e);
1562+
alert("Failed to encode content for sharing: " + e.message);
1563+
return;
1564+
}
1565+
1566+
const shareUrl = window.location.origin + window.location.pathname + '#share=' + encoded;
1567+
if (shareUrl.length > MAX_SHARE_URL_LENGTH) {
1568+
alert("The document is too large to share via URL. Please reduce content size and try again.");
1569+
return;
1570+
}
1571+
1572+
window.location.hash = 'share=' + encoded;
1573+
1574+
const originalText = shareButton.innerHTML;
1575+
if (navigator.clipboard && window.isSecureContext) {
1576+
navigator.clipboard.writeText(shareUrl).then(() => {
1577+
shareButton.innerHTML = '<i class="bi bi-check-lg"></i> Copied!';
1578+
setTimeout(() => { shareButton.innerHTML = originalText; }, 2000);
1579+
}).catch(() => {
1580+
shareButton.innerHTML = '<i class="bi bi-link-45deg"></i> Linked!';
1581+
setTimeout(() => { shareButton.innerHTML = originalText; }, 2000);
1582+
});
1583+
} else {
1584+
shareButton.innerHTML = '<i class="bi bi-link-45deg"></i> Linked!';
1585+
setTimeout(() => { shareButton.innerHTML = originalText; }, 2000);
1586+
}
1587+
});
1588+
1589+
function loadFromShareHash() {
1590+
if (typeof pako === 'undefined') return;
1591+
const hash = window.location.hash;
1592+
if (!hash.startsWith('#share=')) return;
1593+
const encoded = hash.slice('#share='.length);
1594+
if (!encoded) return;
1595+
try {
1596+
const decoded = decodeMarkdownFromShare(encoded);
1597+
markdownEditor.value = decoded;
1598+
renderMarkdown();
1599+
} catch (e) {
1600+
console.error("Failed to load shared content:", e);
1601+
alert("The shared URL could not be decoded. It may be corrupted or incomplete.");
1602+
}
1603+
}
1604+
1605+
loadFromShareHash();
1606+
15291607
const dropEvents = ["dragenter", "dragover", "dragleave", "drop"];
15301608

15311609
dropEvents.forEach((eventName) => {

0 commit comments

Comments
 (0)