@@ -665,7 +665,10 @@ document.addEventListener("DOMContentLoaded", function () {
665665 return markdown ;
666666 }
667667 resetExtendedMarkdownState ( ) ;
668- return applyFootnotes ( extractFootnoteDefinitions ( markdown ) ) ;
668+ // ✅ Replace escaped dollar signs before marked.js strips the backslash.
669+ // This prevents MathJax from treating lone $ as a math delimiter.
670+ const protectedMarkdown = markdown . replace ( / \\ \$ / g, '$' ) ;
671+ return applyFootnotes ( extractFootnoteDefinitions ( protectedMarkdown ) ) ;
669672 } ,
670673 } ,
671674 } ) ;
@@ -5009,64 +5012,151 @@ This is a fully client-side application. Your content never leaves your browser
50095012 return new TextDecoder ( ) . decode ( pako . inflate ( bytes ) ) ;
50105013 }
50115014
5012- function copyShareUrl ( btn ) {
5015+ // ============================================
5016+ // Share Modal
5017+ // ============================================
5018+
5019+ const shareModal = document . getElementById ( 'share-modal' ) ;
5020+ const shareModalCloseX = document . getElementById ( 'share-modal-close-icon' ) ;
5021+ const shareModalClose = document . getElementById ( 'share-modal-close' ) ;
5022+ const shareUrlInput = document . getElementById ( 'share-url-input' ) ;
5023+ const shareCopyBtn = document . getElementById ( 'share-copy-btn' ) ;
5024+ const shareModeView = document . getElementById ( 'share-mode-view' ) ;
5025+ const shareModeEdit = document . getElementById ( 'share-mode-edit' ) ;
5026+ const shareCardView = document . getElementById ( 'share-card-view' ) ;
5027+ const shareCardEdit = document . getElementById ( 'share-card-edit' ) ;
5028+
5029+ function buildShareUrl ( mode ) {
50135030 const markdownText = markdownEditor . value ;
50145031 let encoded ;
50155032 try {
50165033 encoded = encodeMarkdownForShare ( markdownText ) ;
50175034 } catch ( e ) {
5018- console . error ( "Share encoding failed:" , e ) ;
5019- alert ( "Failed to encode content for sharing: " + e . message ) ;
5035+ console . error ( 'Share encoding failed:' , e ) ;
5036+ return null ;
5037+ }
5038+ // mode=view → #share=<encoded> (opens preview-only)
5039+ // mode=edit → #share=<encoded>&edit=1
5040+ const base = window . location . origin + window . location . pathname + '#share=' + encoded ;
5041+ return mode === 'edit' ? base + '&edit=1' : base ;
5042+ }
5043+
5044+ function updateShareUrlField ( ) {
5045+ const mode = shareModeView . checked ? 'view' : 'edit' ;
5046+ const url = buildShareUrl ( mode ) ;
5047+ if ( ! url ) {
5048+ shareUrlInput . value = 'Error generating link.' ;
5049+ shareCopyBtn . disabled = true ;
50205050 return ;
50215051 }
5052+ const tooLarge = url . length > MAX_SHARE_URL_LENGTH ;
5053+ if ( tooLarge ) {
5054+ shareUrlInput . value = 'Document too large to share via URL.' ;
5055+ shareCopyBtn . disabled = true ;
5056+ } else {
5057+ shareUrlInput . value = url ;
5058+ shareCopyBtn . disabled = false ;
5059+ }
5060+ }
50225061
5023- const shareUrl = window . location . origin + window . location . pathname + '#share=' + encoded ;
5024- const tooLarge = shareUrl . length > MAX_SHARE_URL_LENGTH ;
5062+ function openShareModal ( ) {
5063+ // Reset to view-only by default each time
5064+ shareModeView . checked = true ;
5065+ syncShareCardStyles ( ) ;
5066+ updateShareUrlField ( ) ;
5067+ shareModal . style . display = '' ;
5068+ requestAnimationFrame ( ( ) => {
5069+ shareModal . classList . add ( 'is-visible' ) ;
5070+ shareModal . setAttribute ( 'aria-hidden' , 'false' ) ;
5071+ } ) ;
5072+ }
50255073
5026- const originalHTML = btn . innerHTML ;
5027- const copiedHTML = '<i class="bi bi-check-lg"></i> Copied!' ;
5074+ function closeShareModal ( ) {
5075+ shareModal . classList . remove ( 'is-visible' ) ;
5076+ shareModal . setAttribute ( 'aria-hidden' , 'true' ) ;
5077+ shareModal . addEventListener ( 'transitionend' , function handler ( ) {
5078+ shareModal . style . display = 'none' ;
5079+ shareModal . removeEventListener ( 'transitionend' , handler ) ;
5080+ } ) ;
5081+ }
5082+
5083+ function syncShareCardStyles ( ) {
5084+ if ( shareModeView . checked ) {
5085+ shareCardView . classList . add ( 'is-selected' ) ;
5086+ shareCardEdit . classList . remove ( 'is-selected' ) ;
5087+ } else {
5088+ shareCardEdit . classList . add ( 'is-selected' ) ;
5089+ shareCardView . classList . remove ( 'is-selected' ) ;
5090+ }
5091+ }
5092+
5093+ shareModeView . addEventListener ( 'change' , function ( ) {
5094+ syncShareCardStyles ( ) ;
5095+ updateShareUrlField ( ) ;
5096+ } ) ;
5097+ shareModeEdit . addEventListener ( 'change' , function ( ) {
5098+ syncShareCardStyles ( ) ;
5099+ updateShareUrlField ( ) ;
5100+ } ) ;
5101+
5102+ shareCopyBtn . addEventListener ( 'click' , function ( ) {
5103+ const url = shareUrlInput . value ;
5104+ if ( ! url || shareCopyBtn . disabled ) return ;
50285105
50295106 function onCopied ( ) {
5030- if ( ! tooLarge ) {
5031- window . location . hash = 'share=' + encoded ;
5032- }
5033- btn . innerHTML = copiedHTML ;
5034- setTimeout ( ( ) => { btn . innerHTML = originalHTML ; } , 2000 ) ;
5107+ const orig = shareCopyBtn . innerHTML ;
5108+ shareCopyBtn . innerHTML = '<i class="bi bi-check-lg"></i>' ;
5109+ setTimeout ( ( ) => { shareCopyBtn . innerHTML = orig ; } , 2000 ) ;
50355110 }
50365111
50375112 if ( navigator . clipboard && window . isSecureContext ) {
5038- navigator . clipboard . writeText ( shareUrl ) . then ( onCopied ) . catch ( ( ) => {
5039- // clipboard.writeText failed; nothing further to do in secure context
5040- } ) ;
5113+ navigator . clipboard . writeText ( url ) . then ( onCopied ) . catch ( ( ) => { } ) ;
50415114 } else {
50425115 try {
5043- const tempInput = document . createElement ( " textarea" ) ;
5044- tempInput . value = shareUrl ;
5045- document . body . appendChild ( tempInput ) ;
5046- tempInput . select ( ) ;
5047- document . execCommand ( " copy" ) ;
5048- document . body . removeChild ( tempInput ) ;
5116+ const tmp = document . createElement ( ' textarea' ) ;
5117+ tmp . value = url ;
5118+ document . body . appendChild ( tmp ) ;
5119+ tmp . select ( ) ;
5120+ document . execCommand ( ' copy' ) ;
5121+ document . body . removeChild ( tmp ) ;
50495122 onCopied ( ) ;
5050- } catch ( _ ) {
5051- // copy failed silently
5052- }
5123+ } catch ( _ ) { }
50535124 }
5054- }
5125+ } ) ;
5126+
5127+ shareModalCloseX . addEventListener ( 'click' , closeShareModal ) ;
5128+ shareModalClose . addEventListener ( 'click' , closeShareModal ) ;
5129+ shareModal . addEventListener ( 'click' , function ( e ) {
5130+ if ( e . target === shareModal ) closeShareModal ( ) ;
5131+ } ) ;
5132+ document . addEventListener ( 'keydown' , function ( e ) {
5133+ if ( e . key === 'Escape' && shareModal . classList . contains ( 'is-visible' ) ) closeShareModal ( ) ;
5134+ } ) ;
50555135
5056- shareButton . addEventListener ( " click" , function ( ) { copyShareUrl ( shareButton ) ; } ) ;
5057- mobileShareButton . addEventListener ( " click" , function ( ) { copyShareUrl ( mobileShareButton ) ; } ) ;
5136+ shareButton . addEventListener ( ' click' , openShareModal ) ;
5137+ mobileShareButton . addEventListener ( ' click' , openShareModal ) ;
50585138
50595139 function loadFromShareHash ( ) {
50605140 if ( typeof pako === 'undefined' ) return ;
50615141 const hash = window . location . hash ;
50625142 if ( ! hash . startsWith ( '#share=' ) ) return ;
5063- const encoded = hash . slice ( '#share=' . length ) ;
5143+
5144+ // Parse encoded content and optional &edit=1 flag.
5145+ // Hash format: #share=<encoded> or #share=<encoded>&edit=1
5146+ const rest = hash . slice ( '#share=' . length ) ;
5147+ const ampIdx = rest . indexOf ( '&' ) ;
5148+ const encoded = ampIdx === - 1 ? rest : rest . slice ( 0 , ampIdx ) ;
5149+ const params = ampIdx === - 1 ? '' : rest . slice ( ampIdx + 1 ) ;
5150+ const isEdit = params . split ( '&' ) . includes ( 'edit=1' ) ;
5151+
50645152 if ( ! encoded ) return ;
50655153 try {
50665154 const decoded = decodeMarkdownFromShare ( encoded ) ;
50675155 markdownEditor . value = decoded ;
50685156 renderMarkdown ( ) ;
50695157 saveCurrentTabState ( ) ;
5158+ // Apply the correct view mode: edit=1 → split, default → preview only
5159+ setViewMode ( isEdit ? 'split' : 'preview' ) ;
50705160 } catch ( e ) {
50715161 console . error ( "Failed to load shared content:" , e ) ;
50725162 alert ( "The shared URL could not be decoded. It may be corrupted or incomplete." ) ;
@@ -5495,4 +5585,4 @@ This is a fully client-side application. Your content never leaves your browser
54955585 container . appendChild ( toolbar ) ;
54965586 } ) ;
54975587 }
5498- } ) ;
5588+ } ) ;
0 commit comments