@@ -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