retold-remote 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1732,432 +1732,122 @@ let tmpCode=this.codeJar.toString();this.codeJar.destroy();this.codeJar=this._co
1732
1732
  * Marshal code content from the data address into the view.
1733
1733
  */marshalToView(){super.marshalToView();if(this.codeJar&&this.options.CodeDataAddress){let tmpCode=this._resolveCodeContent();if(typeof tmpCode==='string'){this.codeJar.updateCode(tmpCode);this._updateLineNumbers();}}}/**
1734
1734
  * Marshal the current code content back to the data address.
1735
- */marshalFromView(){super.marshalFromView();if(this.codeJar&&this.options.CodeDataAddress){this.onCodeChange(this.codeJar.toString());}}}module.exports=PictSectionCode;module.exports.default_configuration=_DefaultConfiguration;module.exports.createHighlighter=libCreateHighlighter;},{"./Pict-Code-Highlighter.js":47,"./Pict-Section-Code-DefaultConfiguration.js":48,"pict-view":76}],50:[function(require,module,exports){// The container for all the Pict-Section-Content related code.
1736
- // The main content view class
1737
- module.exports=require('./views/Pict-View-Content.js');// The content provider (markdown parsing, HTML escaping)
1738
- module.exports.PictContentProvider=require('./providers/Pict-Provider-Content.js');},{"./providers/Pict-Provider-Content.js":51,"./views/Pict-View-Content.js":52}],51:[function(require,module,exports){const libPictProvider=require('pict-provider');const libCreateHighlighter=require('pict-section-code').createHighlighter;/**
1739
- * Content Provider for Pict Section Content
1740
- *
1741
- * A general-purpose markdown-to-HTML parser with support for:
1742
- * - Headings, paragraphs, lists, blockquotes, horizontal rules
1743
- * - Fenced code blocks with language tags (nested fence support)
1744
- * - Syntax highlighting and line numbers for code blocks (via pict-section-code)
1745
- * - Tables (GFM pipe syntax)
1746
- * - Mermaid diagram blocks
1747
- * - KaTeX math (inline and display)
1748
- * - Bold, italic, inline code, links, images
1749
- *
1750
- * Link resolution is customizable via an optional callback.
1751
- */class PictContentProvider extends libPictProvider{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}/**
1752
- * Highlight a code string using pict-section-code's syntax highlighter.
1753
- * Uses a mock element to interface with the highlighter's DOM-based API.
1754
- *
1755
- * @param {string} pCode - The raw code string
1756
- * @param {string} pLanguage - The language identifier (e.g. "javascript", "html")
1757
- * @returns {string} The syntax-highlighted HTML
1758
- */highlightCode(pCode,pLanguage){if(!pCode){return'';}let tmpHighlighter=libCreateHighlighter(pLanguage);// Create a mock element to interface with the highlighter
1759
- let tmpMockElement={textContent:pCode,innerHTML:''};tmpHighlighter(tmpMockElement);return tmpMockElement.innerHTML;}/**
1760
- * Generate line number HTML for a code block.
1761
- *
1762
- * @param {string} pCode - The raw code string
1763
- * @returns {string} HTML string with line number spans
1764
- */generateLineNumbers(pCode){if(!pCode){return'<span>1</span>';}let tmpLineCount=pCode.split('\n').length;let tmpHTML='';for(let i=1;i<=tmpLineCount;i++){tmpHTML+='<span>'+i+'</span>';}return tmpHTML;}/**
1765
- * Parse a markdown string into HTML.
1766
- *
1767
- * @param {string} pMarkdown - The raw markdown text
1768
- * @param {Function} [pLinkResolver] - Optional callback for link resolution: (pHref, pLinkText) => { href, target, rel } or null
1769
- * @returns {string} The parsed HTML
1770
- */parseMarkdown(pMarkdown,pLinkResolver){if(!pMarkdown){return'';}let tmpLines=pMarkdown.split('\n');let tmpHTML=[];let tmpInCodeBlock=false;let tmpCodeFenceLength=0;let tmpCodeLang='';let tmpCodeLines=[];let tmpInList=false;let tmpListType='';let tmpInBlockquote=false;let tmpBlockquoteLines=[];let tmpInMathBlock=false;let tmpMathLines=[];let tmpParagraphLines=[];// Helper to flush accumulated paragraph lines into a single <p> tag
1771
- let fFlushParagraph=()=>{if(tmpParagraphLines.length>0){tmpHTML.push('<p>'+tmpParagraphLines.map(pLine=>{return this.parseInline(pLine,pLinkResolver);}).join(' ')+'</p>');tmpParagraphLines=[];}};for(let i=0;i<tmpLines.length;i++){let tmpLine=tmpLines[i];// Display math blocks ($$...$$) — skip if inside a code block
1772
- if(!tmpInCodeBlock&&tmpLine.trim().match(/^\$\$/)){if(tmpInMathBlock){// End math block
1773
- tmpHTML.push('<div class="pict-content-katex-display">'+tmpMathLines.join('\n')+'</div>');tmpInMathBlock=false;tmpMathLines=[];}else{// Flush any pending paragraph
1774
- fFlushParagraph();// Close any open list or blockquote
1775
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}tmpInMathBlock=true;}continue;}if(tmpInMathBlock){tmpMathLines.push(tmpLine);continue;}// Code blocks (fenced) — track fence length so ````x```` nests around ```y```
1776
- let tmpFenceMatch=tmpLine.match(/^(`{3,})/);if(tmpFenceMatch){let tmpFenceLen=tmpFenceMatch[1].length;if(tmpInCodeBlock){// Only close if the closing fence is at least as long as the opening
1777
- if(tmpFenceLen>=tmpCodeFenceLength&&tmpLine.trim()===tmpFenceMatch[1]){// End code block
1778
- if(tmpCodeLang==='mermaid'){// Mermaid diagrams: output raw content for client-side rendering
1779
- tmpHTML.push('<pre class="mermaid">'+tmpCodeLines.join('\n')+'</pre>');}else{let tmpCodeText=tmpCodeLines.join('\n');let tmpHighlightedCode=this.highlightCode(tmpCodeText,tmpCodeLang);let tmpLineNumbersHTML=this.generateLineNumbers(tmpCodeText);tmpHTML.push('<div class="pict-content-code-wrap"><div class="pict-content-code-line-numbers">'+tmpLineNumbersHTML+'</div><pre><code class="language-'+this.escapeHTML(tmpCodeLang)+'">'+tmpHighlightedCode+'</code></pre></div>');}tmpInCodeBlock=false;tmpCodeFenceLength=0;tmpCodeLang='';tmpCodeLines=[];continue;}else{// Inner fence with fewer backticks — treat as content
1780
- tmpCodeLines.push(tmpLine);continue;}}else{// Flush any pending paragraph
1781
- fFlushParagraph();// Close any open list or blockquote
1782
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}// Start code block — record fence length
1783
- tmpCodeFenceLength=tmpFenceLen;tmpCodeLang=tmpLine.replace(/^`{3,}/,'').trim();tmpInCodeBlock=true;continue;}}if(tmpInCodeBlock){tmpCodeLines.push(tmpLine);continue;}// Blockquotes
1784
- if(tmpLine.match(/^>\s?/)){if(!tmpInBlockquote){// Flush any pending paragraph
1785
- fFlushParagraph();// Close any open list
1786
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}tmpInBlockquote=true;tmpBlockquoteLines=[];}tmpBlockquoteLines.push(tmpLine.replace(/^>\s?/,''));continue;}else if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}// Horizontal rule
1787
- if(tmpLine.match(/^(-{3,}|\*{3,}|_{3,})\s*$/)){fFlushParagraph();if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}tmpHTML.push('<hr>');continue;}// Headings
1788
- let tmpHeadingMatch=tmpLine.match(/^(#{1,6})\s+(.+)/);if(tmpHeadingMatch){fFlushParagraph();if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}let tmpLevel=tmpHeadingMatch[1].length;let tmpText=this.parseInline(tmpHeadingMatch[2],pLinkResolver);let tmpID=tmpHeadingMatch[2].toLowerCase().replace(/[^\w\s-]/g,'').replace(/\s+/g,'-');tmpHTML.push('<h'+tmpLevel+' id="'+tmpID+'">'+tmpText+'</h'+tmpLevel+'>');continue;}// Unordered list items
1789
- let tmpULMatch=tmpLine.match(/^(\s*)[-*+]\s+(.*)/);if(tmpULMatch){fFlushParagraph();if(!tmpInList||tmpListType!=='ul'){if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}tmpHTML.push('<ul>');tmpInList=true;tmpListType='ul';}tmpHTML.push('<li>'+this.parseInline(tmpULMatch[2],pLinkResolver)+'</li>');continue;}// Ordered list items
1790
- let tmpOLMatch=tmpLine.match(/^(\s*)\d+\.\s+(.*)/);if(tmpOLMatch){fFlushParagraph();if(!tmpInList||tmpListType!=='ol'){if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}tmpHTML.push('<ol>');tmpInList=true;tmpListType='ol';}tmpHTML.push('<li>'+this.parseInline(tmpOLMatch[2],pLinkResolver)+'</li>');continue;}// Close list if we've left list items
1791
- if(tmpInList&&tmpLine.trim()!==''){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}// Empty line — flush any accumulated paragraph
1792
- if(tmpLine.trim()===''){fFlushParagraph();continue;}// Table detection
1793
- if(tmpLine.match(/^\|/)&&i+1<tmpLines.length&&tmpLines[i+1].match(/^\|[\s-:|]+\|/)){fFlushParagraph();// Close any open list
1794
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}let tmpTableHTML='<table>';// Header row
1795
- let tmpHeaders=tmpLine.split('|').filter(pCell=>{return pCell.trim()!=='';});tmpTableHTML+='<thead><tr>';for(let h=0;h<tmpHeaders.length;h++){tmpTableHTML+='<th>'+this.parseInline(tmpHeaders[h].trim(),pLinkResolver)+'</th>';}tmpTableHTML+='</tr></thead>';// Skip separator row
1796
- i++;// Body rows
1797
- tmpTableHTML+='<tbody>';while(i+1<tmpLines.length&&tmpLines[i+1].match(/^\|/)){i++;let tmpCells=tmpLines[i].split('|').filter(pCell=>{return pCell.trim()!=='';});tmpTableHTML+='<tr>';for(let c=0;c<tmpCells.length;c++){tmpTableHTML+='<td>'+this.parseInline(tmpCells[c].trim(),pLinkResolver)+'</td>';}tmpTableHTML+='</tr>';}tmpTableHTML+='</tbody></table>';tmpHTML.push(tmpTableHTML);continue;}// Accumulate paragraph lines — consecutive non-blank text lines
1798
- // will be joined into a single <p> tag when flushed
1799
- tmpParagraphLines.push(tmpLine);}// Flush any remaining accumulated paragraph
1800
- fFlushParagraph();// Close any trailing open elements
1801
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver)+'</blockquote>');}if(tmpInCodeBlock){let tmpCodeText=tmpCodeLines.join('\n');let tmpHighlightedCode=this.highlightCode(tmpCodeText,tmpCodeLang);let tmpLineNumbersHTML=this.generateLineNumbers(tmpCodeText);tmpHTML.push('<div class="pict-content-code-wrap"><div class="pict-content-code-line-numbers">'+tmpLineNumbersHTML+'</div><pre><code>'+tmpHighlightedCode+'</code></pre></div>');}return tmpHTML.join('\n');}/**
1802
- * Parse inline markdown elements (bold, italic, code, links, images, KaTeX).
1803
- *
1804
- * @param {string} pText - The text to parse
1805
- * @param {Function} [pLinkResolver] - Optional callback: (pHref, pLinkText) => { href, target, rel } or null
1806
- * @returns {string} HTML with inline elements
1807
- */parseInline(pText,pLinkResolver){if(!pText){return'';}let tmpResult=pText;// Extract inline code spans into placeholders so bold/italic regexes don't mangle their contents
1808
- let tmpCodeSpans=[];tmpResult=tmpResult.replace(/`([^`]+)`/g,(pMatch,pCode)=>{let tmpIndex=tmpCodeSpans.length;tmpCodeSpans.push('<code>'+pCode+'</code>');return'\x00CODEINLINE'+tmpIndex+'\x00';});// Inline LaTeX equations ($...$) — must be processed before other inline patterns
1809
- // Match single $ delimiters that aren't adjacent to spaces (to avoid false positives with currency)
1810
- tmpResult=tmpResult.replace(/\$([^\$\s][^\$]*?[^\$\s])\$/g,'<span class="pict-content-katex-inline">$1</span>');// Also match single-character inline math like $x$
1811
- tmpResult=tmpResult.replace(/\$([^\$\s])\$/g,'<span class="pict-content-katex-inline">$1</span>');// Images
1812
- tmpResult=tmpResult.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,'<img src="$2" alt="$1">');// Links
1813
- tmpResult=tmpResult.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(pMatch,pLinkText,pHref)=>{if(typeof pLinkResolver==='function'){let tmpResolved=pLinkResolver(pHref,pLinkText);if(tmpResolved){let tmpTarget=tmpResolved.target?' target="'+tmpResolved.target+'"':'';let tmpRel=tmpResolved.rel?' rel="'+tmpResolved.rel+'"':'';return'<a href="'+tmpResolved.href+'"'+tmpTarget+tmpRel+'>'+pLinkText+'</a>';}}// Default behavior: external links open in new tab
1814
- if(pHref.match(/^https?:\/\//)){return'<a href="'+pHref+'" target="_blank" rel="noopener">'+pLinkText+'</a>';}return'<a href="'+pHref+'">'+pLinkText+'</a>';});// Bold
1815
- tmpResult=tmpResult.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');tmpResult=tmpResult.replace(/__([^_]+)__/g,'<strong>$1</strong>');// Italic
1816
- tmpResult=tmpResult.replace(/\*([^*]+)\*/g,'<em>$1</em>');tmpResult=tmpResult.replace(/_([^_]+)_/g,'<em>$1</em>');// Restore inline code spans from placeholders
1817
- tmpResult=tmpResult.replace(/\x00CODEINLINE(\d+)\x00/g,(pMatch,pIndex)=>{return tmpCodeSpans[parseInt(pIndex)];});return tmpResult;}/**
1818
- * Escape HTML special characters.
1819
- *
1820
- * @param {string} pText - The text to escape
1821
- * @returns {string} The escaped text
1822
- */escapeHTML(pText){if(!pText){return'';}return pText.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');}}module.exports=PictContentProvider;module.exports.default_configuration={ProviderIdentifier:"Pict-Content",AutoInitialize:true,AutoInitializeOrdinal:0};},{"pict-provider":46,"pict-section-code":49}],52:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"Pict-Content",DefaultRenderable:"Pict-Content-Display",DefaultDestinationAddress:"#Pict-Content-Container",AutoRender:false,CSS:/*css*/`
1823
- .pict-content {
1824
- padding: 2em 3em;
1825
- max-width: 900px;
1826
- margin: 0 auto;
1735
+ */marshalFromView(){super.marshalFromView();if(this.codeJar&&this.options.CodeDataAddress){this.onCodeChange(this.codeJar.toString());}}}module.exports=PictSectionCode;module.exports.default_configuration=_DefaultConfiguration;module.exports.createHighlighter=libCreateHighlighter;},{"./Pict-Code-Highlighter.js":47,"./Pict-Section-Code-DefaultConfiguration.js":48,"pict-view":76}],50:[function(require,module,exports){module.exports={"ViewIdentifier":"Pict-FileBrowser","DefaultRenderable":"FileBrowser-Container","DefaultDestinationAddress":"#Pict-FileBrowser-Container","AutoRender":false,// --- FileBrowser State ---
1736
+ // These are the four core state values for the file browser.
1737
+ // They live in AppData at the addresses below.
1738
+ "StateAddresses":{"Layout":"AppData.PictFileBrowser.Layout","RootLocation":"AppData.PictFileBrowser.RootLocation","CurrentLocation":"AppData.PictFileBrowser.CurrentLocation","CurrentFile":"AppData.PictFileBrowser.CurrentFile"},// Default state values
1739
+ "DefaultState":{"Layout":"browser-detail","RootLocation":"/","CurrentLocation":"","CurrentFile":null},// --- File Data ---
1740
+ // The consuming application must populate this address with file/folder data.
1741
+ // Expected format: Array of objects with at minimum { Name, Type }
1742
+ // Type: "folder" or "file"
1743
+ // Additional properties: Size, Modified, Extension, MimeType, Path, Icon, ThumbnailURL
1744
+ "FileListAddress":"AppData.PictFileBrowser.FileList","FolderTreeAddress":"AppData.PictFileBrowser.FolderTree","ChildFolderCacheAddress":"AppData.PictFileBrowser.ChildFolderCache",// --- Templates ---
1745
+ "Templates":[{"Hash":"FileBrowser-Container-Template","Template":/*html*/`
1746
+ <div class="pict-filebrowser" id="Pict-FileBrowser-Wrap">
1747
+ <div class="pict-filebrowser-browse-pane" id="Pict-FileBrowser-BrowsePane"></div>
1748
+ <div class="pict-filebrowser-main-pane">
1749
+ <div class="pict-filebrowser-list-pane" id="Pict-FileBrowser-ListPane"></div>
1750
+ <div class="pict-filebrowser-view-pane" id="Pict-FileBrowser-ViewPane"></div>
1751
+ </div>
1752
+ </div>
1753
+ `}],"Renderables":[{"RenderableHash":"FileBrowser-Container","TemplateHash":"FileBrowser-Container-Template","DestinationAddress":"#Pict-FileBrowser-Container","RenderMethod":"replace"}],// --- CSS ---
1754
+ "CSS":/*css*/`
1755
+ .pict-filebrowser {
1756
+ display: flex;
1757
+ height: 100%;
1758
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
1759
+ font-size: 14px;
1760
+ color: #3D3229;
1761
+ border: 1px solid #DDD6CA;
1762
+ border-radius: 4px;
1763
+ overflow: hidden;
1764
+ background: #FAFAF8;
1827
1765
  }
1828
- .pict-content-loading {
1766
+
1767
+ .pict-filebrowser-browse-pane {
1768
+ width: 240px;
1769
+ min-width: 180px;
1770
+ border-right: 1px solid #DDD6CA;
1771
+ overflow-y: auto;
1772
+ background: #F5F0E8;
1773
+ flex-shrink: 0;
1774
+ }
1775
+
1776
+ .pict-filebrowser-main-pane {
1829
1777
  display: flex;
1830
- align-items: center;
1831
- justify-content: center;
1832
- min-height: 200px;
1833
- color: #8A7F72;
1834
- font-size: 1em;
1778
+ flex-direction: column;
1779
+ flex: 1;
1780
+ min-width: 0;
1835
1781
  }
1836
- .pict-content h1 {
1837
- font-size: 2em;
1838
- color: #3D3229;
1839
- border-bottom: 1px solid #DDD6CA;
1840
- padding-bottom: 0.3em;
1841
- margin-top: 0;
1782
+
1783
+ .pict-filebrowser-list-pane {
1784
+ flex: 1;
1785
+ overflow-y: auto;
1786
+ overflow-x: hidden;
1842
1787
  }
1843
- .pict-content h2 {
1844
- font-size: 1.5em;
1845
- color: #3D3229;
1846
- border-bottom: 1px solid #EAE3D8;
1847
- padding-bottom: 0.25em;
1848
- margin-top: 1.5em;
1788
+
1789
+ .pict-filebrowser-view-pane {
1790
+ border-top: 1px solid #DDD6CA;
1791
+ overflow-y: auto;
1792
+ background: #FAFAF8;
1849
1793
  }
1850
- .pict-content h3 {
1851
- font-size: 1.25em;
1852
- color: #3D3229;
1853
- margin-top: 1.25em;
1794
+
1795
+ /* --- Browsing: Tree --- */
1796
+ .pict-fb-tree {
1797
+ padding: 8px 0;
1854
1798
  }
1855
- .pict-content h4, .pict-content h5, .pict-content h6 {
1856
- color: #5E5549;
1857
- margin-top: 1em;
1799
+ .pict-fb-tree-node {
1800
+ display: flex;
1801
+ align-items: center;
1802
+ padding: 4px 8px 4px 0;
1803
+ cursor: pointer;
1804
+ user-select: none;
1805
+ white-space: nowrap;
1858
1806
  }
1859
- .pict-content p {
1860
- line-height: 1.7;
1861
- color: #423D37;
1862
- margin: 0.75em 0;
1807
+ .pict-fb-tree-node:hover {
1808
+ background: #EAE3D8;
1863
1809
  }
1864
- .pict-content a {
1865
- color: #2E7D74;
1866
- text-decoration: none;
1810
+ .pict-fb-tree-node.selected {
1811
+ background: #DDD6CA;
1812
+ font-weight: 600;
1867
1813
  }
1868
- .pict-content a:hover {
1869
- text-decoration: underline;
1814
+ .pict-fb-tree-toggle {
1815
+ display: inline-block;
1816
+ width: 16px;
1817
+ text-align: center;
1818
+ flex-shrink: 0;
1819
+ color: #8A7F72;
1820
+ font-size: 10px;
1870
1821
  }
1871
- .pict-content-code-wrap {
1872
- position: relative;
1873
- font-family: 'SFMono-Regular', 'SF Mono', 'Menlo', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
1822
+ .pict-fb-tree-icon {
1823
+ margin-right: 6px;
1824
+ flex-shrink: 0;
1874
1825
  font-size: 14px;
1875
- line-height: 1.5;
1876
- border-radius: 6px;
1877
- overflow: auto;
1878
- margin: 1em 0;
1879
- background: #3D3229;
1880
1826
  }
1881
- .pict-content-code-wrap .pict-content-code-line-numbers {
1882
- position: absolute;
1883
- top: 0;
1884
- left: 0;
1885
- width: 40px;
1886
- padding: 1.25em 0;
1887
- text-align: right;
1888
- background: #342A22;
1889
- border-right: 1px solid #4A3F35;
1890
- color: #8A7F72;
1891
- font-size: 13px;
1892
- line-height: 1.5;
1893
- user-select: none;
1894
- pointer-events: none;
1895
- box-sizing: border-box;
1827
+ .pict-fb-tree-label {
1828
+ overflow: hidden;
1829
+ text-overflow: ellipsis;
1896
1830
  }
1897
- .pict-content-code-wrap .pict-content-code-line-numbers span {
1831
+ .pict-fb-tree-children {
1832
+ display: none;
1833
+ }
1834
+ .pict-fb-tree-children.expanded {
1898
1835
  display: block;
1899
- padding: 0 8px 0 0;
1900
1836
  }
1901
- .pict-content-code-wrap pre {
1902
- margin: 0;
1903
- background: #3D3229;
1904
- color: #E8E0D4;
1905
- padding: 1.25em 1.25em 1.25em 52px;
1906
- border-radius: 6px;
1907
- overflow-x: auto;
1908
- line-height: 1.5;
1909
- font-size: inherit;
1837
+
1838
+ /* --- Browsing: Search --- */
1839
+ .pict-fb-search {
1840
+ padding: 8px;
1910
1841
  }
1911
- .pict-content-code-wrap pre code {
1912
- background: none;
1913
- padding: 0;
1914
- color: inherit;
1915
- font-size: inherit;
1916
- font-family: inherit;
1917
- }
1918
- .pict-content-code-wrap .keyword { color: #C678DD; }
1919
- .pict-content-code-wrap .string { color: #98C379; }
1920
- .pict-content-code-wrap .number { color: #D19A66; }
1921
- .pict-content-code-wrap .comment { color: #7F848E; font-style: italic; }
1922
- .pict-content-code-wrap .operator { color: #56B6C2; }
1923
- .pict-content-code-wrap .punctuation { color: #E8E0D4; }
1924
- .pict-content-code-wrap .function-name { color: #61AFEF; }
1925
- .pict-content-code-wrap .property { color: #E06C75; }
1926
- .pict-content-code-wrap .tag { color: #E06C75; }
1927
- .pict-content-code-wrap .attr-name { color: #D19A66; }
1928
- .pict-content-code-wrap .attr-value { color: #98C379; }
1929
- .pict-content pre {
1930
- background: #3D3229;
1931
- color: #E8E0D4;
1932
- padding: 1.25em;
1933
- border-radius: 6px;
1934
- overflow-x: auto;
1935
- line-height: 1.5;
1936
- font-size: 0.9em;
1937
- }
1938
- .pict-content code {
1939
- background: #F0ECE4;
1940
- padding: 0.15em 0.4em;
1941
- border-radius: 3px;
1942
- font-size: 0.9em;
1943
- color: #9E6B47;
1944
- }
1945
- .pict-content pre code {
1946
- background: none;
1947
- padding: 0;
1948
- color: inherit;
1949
- font-size: inherit;
1950
- }
1951
- .pict-content blockquote {
1952
- border-left: 4px solid #2E7D74;
1953
- margin: 1em 0;
1954
- padding: 0.5em 1em;
1955
- background: #F7F5F0;
1956
- color: #5E5549;
1957
- }
1958
- .pict-content blockquote p {
1959
- margin: 0.25em 0;
1960
- }
1961
- .pict-content ul, .pict-content ol {
1962
- padding-left: 2em;
1963
- line-height: 1.8;
1964
- }
1965
- .pict-content li {
1966
- margin: 0.25em 0;
1967
- color: #423D37;
1968
- }
1969
- .pict-content hr {
1970
- border: none;
1971
- border-top: 1px solid #DDD6CA;
1972
- margin: 2em 0;
1973
- }
1974
- .pict-content table {
1975
- width: 100%;
1976
- border-collapse: collapse;
1977
- margin: 1em 0;
1978
- }
1979
- .pict-content table th {
1980
- background: #F5F0E8;
1981
- border: 1px solid #DDD6CA;
1982
- padding: 0.6em 0.8em;
1983
- text-align: left;
1984
- font-weight: 600;
1985
- color: #3D3229;
1986
- }
1987
- .pict-content table td {
1988
- border: 1px solid #DDD6CA;
1989
- padding: 0.5em 0.8em;
1990
- color: #423D37;
1991
- }
1992
- .pict-content table tr:nth-child(even) {
1993
- background: #F7F5F0;
1994
- }
1995
- .pict-content img {
1996
- max-width: 100%;
1997
- height: auto;
1998
- }
1999
- .pict-content pre.mermaid {
2000
- background: #fff;
2001
- color: #3D3229;
2002
- text-align: center;
2003
- padding: 1em;
2004
- }
2005
- .pict-content .pict-content-katex-display {
2006
- text-align: center;
2007
- margin: 1em 0;
2008
- padding: 0.5em;
2009
- overflow-x: auto;
2010
- }
2011
- .pict-content .pict-content-katex-inline {
2012
- display: inline;
2013
- }
2014
- `,Templates:[{Hash:"Pict-Content-Template",Template:/*html*/`
2015
- <div class="pict-content" id="Pict-Content-Body">
2016
- <div class="pict-content-loading">Loading content...</div>
2017
- </div>
2018
- `}],Renderables:[{RenderableHash:"Pict-Content-Display",TemplateHash:"Pict-Content-Template",DestinationAddress:"#Pict-Content-Container",RenderMethod:"replace"}]};class PictContentView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}/**
2019
- * Display parsed HTML content in the content area.
2020
- *
2021
- * @param {string} pHTMLContent - The HTML to display
2022
- * @param {string} [pContainerID] - The container element ID (defaults to 'Pict-Content-Body')
2023
- */displayContent(pHTMLContent,pContainerID){let tmpContainerID=pContainerID||'Pict-Content-Body';this.pict.ContentAssignment.assignContent('#'+tmpContainerID,pHTMLContent);// Scroll to top of content area
2024
- let tmpContentContainer=document.getElementById(tmpContainerID);if(tmpContentContainer&&tmpContentContainer.parentElement){tmpContentContainer.parentElement.scrollTop=0;}// Post-render: initialize Mermaid diagrams if mermaid is available
2025
- this.renderMermaidDiagrams(tmpContainerID);// Post-render: render KaTeX equations if katex is available
2026
- this.renderKaTeXEquations(tmpContainerID);}/**
2027
- * Render any Mermaid diagram blocks in the content area.
2028
- * Mermaid blocks are `<pre class="mermaid">` elements produced by parseMarkdown.
2029
- *
2030
- * @param {string} [pContainerID] - The container element ID (defaults to 'Pict-Content-Body')
2031
- */renderMermaidDiagrams(pContainerID){if(typeof mermaid==='undefined'){return;}let tmpContainerID=pContainerID||'Pict-Content-Body';let tmpContentBody=document.getElementById(tmpContainerID);if(!tmpContentBody){return;}let tmpMermaidElements=tmpContentBody.querySelectorAll('pre.mermaid');if(tmpMermaidElements.length<1){return;}// mermaid.run() will process all pre.mermaid elements in the container
2032
- try{mermaid.run({nodes:tmpMermaidElements});}catch(pError){this.log.error('Mermaid rendering error: '+pError.message);}}/**
2033
- * Render KaTeX inline and display math elements in the content area.
2034
- * Inline: `<span class="pict-content-katex-inline">`
2035
- * Display: `<div class="pict-content-katex-display">`
2036
- *
2037
- * @param {string} [pContainerID] - The container element ID (defaults to 'Pict-Content-Body')
2038
- */renderKaTeXEquations(pContainerID){if(typeof katex==='undefined'){return;}let tmpContainerID=pContainerID||'Pict-Content-Body';let tmpContentBody=document.getElementById(tmpContainerID);if(!tmpContentBody){return;}// Render inline math
2039
- let tmpInlineElements=tmpContentBody.querySelectorAll('.pict-content-katex-inline');for(let i=0;i<tmpInlineElements.length;i++){try{katex.render(tmpInlineElements[i].textContent,tmpInlineElements[i],{throwOnError:false,displayMode:false});}catch(pError){this.log.warn('KaTeX inline error: '+pError.message);}}// Render display math
2040
- let tmpDisplayElements=tmpContentBody.querySelectorAll('.pict-content-katex-display');for(let i=0;i<tmpDisplayElements.length;i++){try{katex.render(tmpDisplayElements[i].textContent,tmpDisplayElements[i],{throwOnError:false,displayMode:true});}catch(pError){this.log.warn('KaTeX display error: '+pError.message);}}}/**
2041
- * Show a loading indicator.
2042
- *
2043
- * @param {string} [pMessage] - Loading message (defaults to 'Loading content...')
2044
- * @param {string} [pContainerID] - The container element ID (defaults to 'Pict-Content-Body')
2045
- */showLoading(pMessage,pContainerID){let tmpContainerID=pContainerID||'Pict-Content-Body';let tmpMessage=pMessage||'Loading content...';this.pict.ContentAssignment.assignContent('#'+tmpContainerID,'<div class="pict-content-loading">'+tmpMessage+'</div>');}}module.exports=PictContentView;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],53:[function(require,module,exports){module.exports={"ViewIdentifier":"Pict-FileBrowser","DefaultRenderable":"FileBrowser-Container","DefaultDestinationAddress":"#Pict-FileBrowser-Container","AutoRender":false,// --- FileBrowser State ---
2046
- // These are the four core state values for the file browser.
2047
- // They live in AppData at the addresses below.
2048
- "StateAddresses":{"Layout":"AppData.PictFileBrowser.Layout","RootLocation":"AppData.PictFileBrowser.RootLocation","CurrentLocation":"AppData.PictFileBrowser.CurrentLocation","CurrentFile":"AppData.PictFileBrowser.CurrentFile"},// Default state values
2049
- "DefaultState":{"Layout":"browser-detail","RootLocation":"/","CurrentLocation":"","CurrentFile":null},// --- File Data ---
2050
- // The consuming application must populate this address with file/folder data.
2051
- // Expected format: Array of objects with at minimum { Name, Type }
2052
- // Type: "folder" or "file"
2053
- // Additional properties: Size, Modified, Extension, MimeType, Path, Icon, ThumbnailURL
2054
- "FileListAddress":"AppData.PictFileBrowser.FileList","FolderTreeAddress":"AppData.PictFileBrowser.FolderTree","ChildFolderCacheAddress":"AppData.PictFileBrowser.ChildFolderCache",// --- Templates ---
2055
- "Templates":[{"Hash":"FileBrowser-Container-Template","Template":/*html*/`
2056
- <div class="pict-filebrowser" id="Pict-FileBrowser-Wrap">
2057
- <div class="pict-filebrowser-browse-pane" id="Pict-FileBrowser-BrowsePane"></div>
2058
- <div class="pict-filebrowser-main-pane">
2059
- <div class="pict-filebrowser-list-pane" id="Pict-FileBrowser-ListPane"></div>
2060
- <div class="pict-filebrowser-view-pane" id="Pict-FileBrowser-ViewPane"></div>
2061
- </div>
2062
- </div>
2063
- `}],"Renderables":[{"RenderableHash":"FileBrowser-Container","TemplateHash":"FileBrowser-Container-Template","DestinationAddress":"#Pict-FileBrowser-Container","RenderMethod":"replace"}],// --- CSS ---
2064
- "CSS":/*css*/`
2065
- .pict-filebrowser {
2066
- display: flex;
2067
- height: 100%;
2068
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
2069
- font-size: 14px;
2070
- color: #3D3229;
2071
- border: 1px solid #DDD6CA;
2072
- border-radius: 4px;
2073
- overflow: hidden;
2074
- background: #FAFAF8;
2075
- }
2076
-
2077
- .pict-filebrowser-browse-pane {
2078
- width: 240px;
2079
- min-width: 180px;
2080
- border-right: 1px solid #DDD6CA;
2081
- overflow-y: auto;
2082
- background: #F5F0E8;
2083
- flex-shrink: 0;
2084
- }
2085
-
2086
- .pict-filebrowser-main-pane {
2087
- display: flex;
2088
- flex-direction: column;
2089
- flex: 1;
2090
- min-width: 0;
2091
- }
2092
-
2093
- .pict-filebrowser-list-pane {
2094
- flex: 1;
2095
- overflow-y: auto;
2096
- overflow-x: hidden;
2097
- }
2098
-
2099
- .pict-filebrowser-view-pane {
2100
- border-top: 1px solid #DDD6CA;
2101
- overflow-y: auto;
2102
- background: #FAFAF8;
2103
- }
2104
-
2105
- /* --- Browsing: Tree --- */
2106
- .pict-fb-tree {
2107
- padding: 8px 0;
2108
- }
2109
- .pict-fb-tree-node {
2110
- display: flex;
2111
- align-items: center;
2112
- padding: 4px 8px 4px 0;
2113
- cursor: pointer;
2114
- user-select: none;
2115
- white-space: nowrap;
2116
- }
2117
- .pict-fb-tree-node:hover {
2118
- background: #EAE3D8;
2119
- }
2120
- .pict-fb-tree-node.selected {
2121
- background: #DDD6CA;
2122
- font-weight: 600;
2123
- }
2124
- .pict-fb-tree-toggle {
2125
- display: inline-block;
2126
- width: 16px;
2127
- text-align: center;
2128
- flex-shrink: 0;
2129
- color: #8A7F72;
2130
- font-size: 10px;
2131
- }
2132
- .pict-fb-tree-icon {
2133
- margin-right: 6px;
2134
- flex-shrink: 0;
2135
- font-size: 14px;
2136
- }
2137
- .pict-fb-tree-label {
2138
- overflow: hidden;
2139
- text-overflow: ellipsis;
2140
- }
2141
- .pict-fb-tree-children {
2142
- display: none;
2143
- }
2144
- .pict-fb-tree-children.expanded {
2145
- display: block;
2146
- }
2147
-
2148
- /* --- Browsing: Search --- */
2149
- .pict-fb-search {
2150
- padding: 8px;
2151
- }
2152
- .pict-fb-search-input {
2153
- width: 100%;
2154
- box-sizing: border-box;
2155
- padding: 6px 10px;
2156
- border: 1px solid #DDD6CA;
2157
- border-radius: 4px;
2158
- font-size: 13px;
2159
- outline: none;
2160
- background: #fff;
1842
+ .pict-fb-search-input {
1843
+ width: 100%;
1844
+ box-sizing: border-box;
1845
+ padding: 6px 10px;
1846
+ border: 1px solid #DDD6CA;
1847
+ border-radius: 4px;
1848
+ font-size: 13px;
1849
+ outline: none;
1850
+ background: #fff;
2161
1851
  }
2162
1852
  .pict-fb-search-input:focus {
2163
1853
  border-color: #2E7D74;
@@ -2394,7 +2084,7 @@ let tmpDisplayElements=tmpContentBody.querySelectorAll('.pict-content-katex-disp
2394
2084
  color: #8A7F72;
2395
2085
  font-style: italic;
2396
2086
  }
2397
- `};},{}],54:[function(require,module,exports){// Pict Section FileBrowser
2087
+ `};},{}],51:[function(require,module,exports){// Pict Section FileBrowser
2398
2088
  // A composable file browser section with browsing, listing, and viewing views.
2399
2089
  // The main container view
2400
2090
  module.exports=require('./views/Pict-View-FileBrowser.js');// --- Providers (base classes for each view type) ---
@@ -2402,7 +2092,7 @@ module.exports.PictFileBrowserBrowseProvider=require('./providers/Pict-Provider-
2402
2092
  module.exports.PictViewBrowseTree=require('./views/Pict-View-FileBrowser-BrowseTree.js');module.exports.PictViewBrowseSearch=require('./views/Pict-View-FileBrowser-BrowseSearch.js');// --- Listing Views ---
2403
2093
  module.exports.PictViewListDetail=require('./views/Pict-View-FileBrowser-ListDetail.js');module.exports.PictViewListIcons=require('./views/Pict-View-FileBrowser-ListIcons.js');// --- Viewing Views ---
2404
2094
  module.exports.PictViewFileInfo=require('./views/Pict-View-FileBrowser-ViewFileInfo.js');module.exports.PictViewImageViewer=require('./views/Pict-View-FileBrowser-ViewImage.js');// --- Service (Fable service with REST endpoints + static web app) ---
2405
- module.exports.FileBrowserService=require('./services/Pict-Service-FileBrowser.js');},{"./providers/Pict-Provider-FileBrowserBrowse.js":55,"./providers/Pict-Provider-FileBrowserIcons.js":56,"./providers/Pict-Provider-FileBrowserLayout.js":57,"./providers/Pict-Provider-FileBrowserList.js":58,"./providers/Pict-Provider-FileBrowserView.js":59,"./services/Pict-Service-FileBrowser.js":60,"./views/Pict-View-FileBrowser-BrowseSearch.js":61,"./views/Pict-View-FileBrowser-BrowseTree.js":62,"./views/Pict-View-FileBrowser-ListDetail.js":63,"./views/Pict-View-FileBrowser-ListIcons.js":64,"./views/Pict-View-FileBrowser-ViewFileInfo.js":65,"./views/Pict-View-FileBrowser-ViewImage.js":66,"./views/Pict-View-FileBrowser.js":67}],55:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-Browse","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};/**
2095
+ module.exports.FileBrowserService=require('./services/Pict-Service-FileBrowser.js');},{"./providers/Pict-Provider-FileBrowserBrowse.js":52,"./providers/Pict-Provider-FileBrowserIcons.js":53,"./providers/Pict-Provider-FileBrowserLayout.js":54,"./providers/Pict-Provider-FileBrowserList.js":55,"./providers/Pict-Provider-FileBrowserView.js":56,"./services/Pict-Service-FileBrowser.js":57,"./views/Pict-View-FileBrowser-BrowseSearch.js":58,"./views/Pict-View-FileBrowser-BrowseTree.js":59,"./views/Pict-View-FileBrowser-ListDetail.js":60,"./views/Pict-View-FileBrowser-ListIcons.js":61,"./views/Pict-View-FileBrowser-ViewFileInfo.js":62,"./views/Pict-View-FileBrowser-ViewImage.js":63,"./views/Pict-View-FileBrowser.js":64}],52:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-Browse","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};/**
2406
2096
  * Base provider for browsing-type views (tree navigation, search).
2407
2097
  *
2408
2098
  * Subclass or override to customize how folder structures are resolved,
@@ -2457,7 +2147,7 @@ this.pict.manifest.setValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmp
2457
2147
  * @param {string} pKey - The option key
2458
2148
  * @param {*} pDefault - Default value
2459
2149
  * @returns {*} The option value
2460
- */getFileBrowserOption(pKey,pDefault){if(this.pict.views&&this.pict.views['Pict-FileBrowser']){let tmpOptions=this.pict.views['Pict-FileBrowser'].options;if(tmpOptions&&pKey in tmpOptions){return tmpOptions[pKey];}}return pDefault;}}module.exports=PictFileBrowserBrowseProvider;module.exports.default_configuration=_DefaultProviderConfiguration;},{"pict-provider":46}],56:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-Icons","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};// ---- Color palette (matches filebrowser CSS) ----
2150
+ */getFileBrowserOption(pKey,pDefault){if(this.pict.views&&this.pict.views['Pict-FileBrowser']){let tmpOptions=this.pict.views['Pict-FileBrowser'].options;if(tmpOptions&&pKey in tmpOptions){return tmpOptions[pKey];}}return pDefault;}}module.exports=PictFileBrowserBrowseProvider;module.exports.default_configuration=_DefaultProviderConfiguration;},{"pict-provider":46}],53:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-Icons","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};// ---- Color palette (matches filebrowser CSS) ----
2461
2151
  const _Colors={Primary:'#3D3229',Accent:'#2E7D74',Muted:'#8A7F72',Light:'#F5F0E8',WarmBeige:'#EAE3D8',TealTint:'#E0EDE9',Lavender:'#E8E0F0',AmberTint:'#F0E8D0'};// ====================================================================
2462
2152
  // RETRO / HAND-DRAWN SVG ICON SET
2463
2153
  //
@@ -2550,7 +2240,7 @@ return this.getIcon('file',tmpSize);}/**
2550
2240
  */getExtensionMap(){return Object.assign({},this._extensionMap);}/**
2551
2241
  * Inject CSS classes for icon sizing into the pict CSSMap.
2552
2242
  * Called automatically by views that use icons.
2553
- */injectCSS(){if(this._cssInjected){return;}if(this.pict&&this.pict.CSSMap){this.pict.CSSMap.addCSS('PictFileBrowserIcons','.pict-fb-svg-icon { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; }\n'+'.pict-fb-svg-icon svg { display: block; }\n'+'.pict-fb-tree-icon svg { width: 16px; height: 16px; }\n'+'.pict-fb-detail-icon svg { width: 16px; height: 16px; }\n'+'.pict-fb-icon-graphic svg { width: 36px; height: 36px; }\n'+'.pict-fb-tree-toggle svg { width: 10px; height: 10px; }\n');this._cssInjected=true;}}}module.exports=PictFileBrowserIconProvider;module.exports.default_configuration=_DefaultProviderConfiguration;module.exports.BuiltInIcons=_BuiltInIcons;module.exports.ExtensionMap=_ExtensionMap;module.exports.Colors=_Colors;},{"pict-provider":46}],57:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-Layout","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};/**
2243
+ */injectCSS(){if(this._cssInjected){return;}if(this.pict&&this.pict.CSSMap){this.pict.CSSMap.addCSS('PictFileBrowserIcons','.pict-fb-svg-icon { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; }\n'+'.pict-fb-svg-icon svg { display: block; }\n'+'.pict-fb-tree-icon svg { width: 16px; height: 16px; }\n'+'.pict-fb-detail-icon svg { width: 16px; height: 16px; }\n'+'.pict-fb-icon-graphic svg { width: 36px; height: 36px; }\n'+'.pict-fb-tree-toggle svg { width: 10px; height: 10px; }\n');this._cssInjected=true;}}}module.exports=PictFileBrowserIconProvider;module.exports.default_configuration=_DefaultProviderConfiguration;module.exports.BuiltInIcons=_BuiltInIcons;module.exports.ExtensionMap=_ExtensionMap;module.exports.Colors=_Colors;},{"pict-provider":46}],54:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-Layout","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};/**
2554
2244
  * Layout definitions.
2555
2245
  *
2556
2246
  * Each layout specifies:
@@ -2612,7 +2302,7 @@ this.layouts=JSON.parse(JSON.stringify(_BuiltInLayouts));}/**
2612
2302
  * @param {string} pKey - The option key
2613
2303
  * @param {*} pDefault - Default value
2614
2304
  * @returns {*} The option value
2615
- */getFileBrowserOption(pKey,pDefault){if(this.pict.views&&this.pict.views['Pict-FileBrowser']){let tmpOptions=this.pict.views['Pict-FileBrowser'].options;if(tmpOptions&&pKey in tmpOptions){return tmpOptions[pKey];}}return pDefault;}}module.exports=PictFileBrowserLayoutProvider;module.exports.default_configuration=_DefaultProviderConfiguration;module.exports.BuiltInLayouts=_BuiltInLayouts;},{"pict-provider":46}],58:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-List","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};/**
2305
+ */getFileBrowserOption(pKey,pDefault){if(this.pict.views&&this.pict.views['Pict-FileBrowser']){let tmpOptions=this.pict.views['Pict-FileBrowser'].options;if(tmpOptions&&pKey in tmpOptions){return tmpOptions[pKey];}}return pDefault;}}module.exports=PictFileBrowserLayoutProvider;module.exports.default_configuration=_DefaultProviderConfiguration;module.exports.BuiltInLayouts=_BuiltInLayouts;},{"pict-provider":46}],55:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-List","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};/**
2616
2306
  * Base provider for listing-type views (detail list, icon grid).
2617
2307
  *
2618
2308
  * Handles file list retrieval, sorting, filtering, and selection.
@@ -2671,7 +2361,7 @@ if(pEntry.Type==='folder'){return'\uD83D\uDCC1';}let tmpExt=(pEntry.Extension||'
2671
2361
  * @param {string} pKey - The option key
2672
2362
  * @param {*} pDefault - Default value
2673
2363
  * @returns {*} The option value
2674
- */getFileBrowserOption(pKey,pDefault){if(this.pict.views&&this.pict.views['Pict-FileBrowser']){let tmpOptions=this.pict.views['Pict-FileBrowser'].options;if(tmpOptions&&pKey in tmpOptions){return tmpOptions[pKey];}}return pDefault;}}module.exports=PictFileBrowserListProvider;module.exports.default_configuration=_DefaultProviderConfiguration;},{"pict-provider":46}],59:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-View","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};/**
2364
+ */getFileBrowserOption(pKey,pDefault){if(this.pict.views&&this.pict.views['Pict-FileBrowser']){let tmpOptions=this.pict.views['Pict-FileBrowser'].options;if(tmpOptions&&pKey in tmpOptions){return tmpOptions[pKey];}}return pDefault;}}module.exports=PictFileBrowserListProvider;module.exports.default_configuration=_DefaultProviderConfiguration;},{"pict-provider":46}],56:[function(require,module,exports){const libPictProvider=require('pict-provider');const _DefaultProviderConfiguration={"ProviderIdentifier":"Pict-FileBrowser-View","AutoInitialize":true,"AutoInitializeOrdinal":0,"AutoSolveWithApp":true,"AutoSolveOrdinal":0};/**
2675
2365
  * Base provider for viewing-type views (file info, image preview).
2676
2366
  *
2677
2367
  * Handles retrieval of the currently selected file's metadata and
@@ -2707,7 +2397,7 @@ let tmpExt=(tmpEntry.Extension||'').toLowerCase();let tmpImageExtensions=['.jpg'
2707
2397
  * @param {string} pKey - The option key
2708
2398
  * @param {*} pDefault - Default value
2709
2399
  * @returns {*} The option value
2710
- */getFileBrowserOption(pKey,pDefault){if(this.pict.views&&this.pict.views['Pict-FileBrowser']){let tmpOptions=this.pict.views['Pict-FileBrowser'].options;if(tmpOptions&&pKey in tmpOptions){return tmpOptions[pKey];}}return pDefault;}}module.exports=PictFileBrowserViewProvider;module.exports.default_configuration=_DefaultProviderConfiguration;},{"pict-provider":46}],60:[function(require,module,exports){(function(__dirname){(function(){/**
2400
+ */getFileBrowserOption(pKey,pDefault){if(this.pict.views&&this.pict.views['Pict-FileBrowser']){let tmpOptions=this.pict.views['Pict-FileBrowser'].options;if(tmpOptions&&pKey in tmpOptions){return tmpOptions[pKey];}}return pDefault;}}module.exports=PictFileBrowserViewProvider;module.exports.default_configuration=_DefaultProviderConfiguration;},{"pict-provider":46}],57:[function(require,module,exports){(function(__dirname){(function(){/**
2711
2401
  * Pict FileBrowser Service
2712
2402
  *
2713
2403
  * A Fable service that provides:
@@ -2818,7 +2508,7 @@ tmpNodes.sort((pA,pB)=>{return pA.Name.localeCompare(pB.Name);});return fCallbac
2818
2508
  *
2819
2509
  * @param {string} pRelativePath - Path relative to basePath
2820
2510
  * @param {Function} fCallback - Callback(pError, pInfo)
2821
- */getFileInfo(pRelativePath,fCallback){let tmpAbsolutePath=this.resolveSafePath(pRelativePath);if(!tmpAbsolutePath){return fCallback(new Error('Invalid path'));}libFS.stat(tmpAbsolutePath,(pError,pStats)=>{if(pError){if(pError.code==='ENOENT'){return fCallback(new Error('Path not found'));}return fCallback(pError);}let tmpName=libPath.basename(tmpAbsolutePath);let tmpInfo={Name:tmpName,Path:pRelativePath||tmpName,Type:pStats.isDirectory()?'folder':'file',Size:pStats.size,Modified:pStats.mtime,Created:pStats.birthtime};if(!pStats.isDirectory()){tmpInfo.Extension=libPath.extname(tmpName);}return fCallback(null,tmpInfo);});}}module.exports=PictFileBrowserService;module.exports.default_configuration=_DefaultServiceConfiguration;}).call(this);}).call(this,"/node_modules/pict-section-filebrowser/source/services");},{"fable-serviceproviderbase":20,"fs":2,"path":42,"url":113}],61:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-BrowseSearch","DefaultRenderable":"BrowseSearch-Container","DefaultDestinationAddress":"#Pict-FileBrowser-BrowsePane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-BrowseSearch-Container-Template","Template":/*html*/`
2511
+ */getFileInfo(pRelativePath,fCallback){let tmpAbsolutePath=this.resolveSafePath(pRelativePath);if(!tmpAbsolutePath){return fCallback(new Error('Invalid path'));}libFS.stat(tmpAbsolutePath,(pError,pStats)=>{if(pError){if(pError.code==='ENOENT'){return fCallback(new Error('Path not found'));}return fCallback(pError);}let tmpName=libPath.basename(tmpAbsolutePath);let tmpInfo={Name:tmpName,Path:pRelativePath||tmpName,Type:pStats.isDirectory()?'folder':'file',Size:pStats.size,Modified:pStats.mtime,Created:pStats.birthtime};if(!pStats.isDirectory()){tmpInfo.Extension=libPath.extname(tmpName);}return fCallback(null,tmpInfo);});}}module.exports=PictFileBrowserService;module.exports.default_configuration=_DefaultServiceConfiguration;}).call(this);}).call(this,"/node_modules/pict-section-filebrowser/source/services");},{"fable-serviceproviderbase":20,"fs":2,"path":42,"url":113}],58:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-BrowseSearch","DefaultRenderable":"BrowseSearch-Container","DefaultDestinationAddress":"#Pict-FileBrowser-BrowsePane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-BrowseSearch-Container-Template","Template":/*html*/`
2822
2512
  <div class="pict-fb-search">
2823
2513
  <input type="text" class="pict-fb-search-input"
2824
2514
  id="Pict-FileBrowser-SearchInput"
@@ -2853,7 +2543,7 @@ this._lastResults=tmpResults;}/**
2853
2543
  * Handle clicking a search result.
2854
2544
  *
2855
2545
  * @param {number} pIndex - The result index
2856
- */selectResult(pIndex){if(!this._lastResults||pIndex>=this._lastResults.length){return;}let tmpEntry=this._lastResults[pIndex];let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(tmpListProvider){tmpListProvider.openEntry(tmpEntry);}}}module.exports=PictViewFileBrowserBrowseSearch;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],62:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-BrowseTree","DefaultRenderable":"BrowseTree-Container","DefaultDestinationAddress":"#Pict-FileBrowser-BrowsePane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-BrowseTree-Container-Template","Template":/*html*/`<div class="pict-fb-tree" id="Pict-FileBrowser-Tree"></div>`},{"Hash":"FileBrowser-BrowseTree-Node-Template","Template":/*html*/`
2546
+ */selectResult(pIndex){if(!this._lastResults||pIndex>=this._lastResults.length){return;}let tmpEntry=this._lastResults[pIndex];let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(tmpListProvider){tmpListProvider.openEntry(tmpEntry);}}}module.exports=PictViewFileBrowserBrowseSearch;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],59:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-BrowseTree","DefaultRenderable":"BrowseTree-Container","DefaultDestinationAddress":"#Pict-FileBrowser-BrowsePane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-BrowseTree-Container-Template","Template":/*html*/`<div class="pict-fb-tree" id="Pict-FileBrowser-Tree"></div>`},{"Hash":"FileBrowser-BrowseTree-Node-Template","Template":/*html*/`
2857
2547
  <div class="pict-fb-tree-node{~D:Record.SelectedClass~}" style="padding-left: {~D:Record.Indent~}px;" data-path="{~D:Record.Path~}" onclick="{~D:Record.ClickHandler~}">
2858
2548
  <span class="{~D:Record.ToggleClass~}" onclick="{~D:Record.ToggleHandler~}">{~D:Record.ToggleIcon~}</span>
2859
2549
  <span class="pict-fb-tree-icon">{~D:Record.Icon~}</span>
@@ -2910,7 +2600,7 @@ let tmpCached=tmpBrowseProvider.getChildFolders(pPath);if(!tmpCached){if(typeof
2910
2600
  * Get the current location from state.
2911
2601
  *
2912
2602
  * @returns {string} The current location path
2913
- */getCurrentLocation(){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses.CurrentLocation||'AppData.PictFileBrowser.CurrentLocation';return this.pict.manifest.getValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress)||'';}}module.exports=PictViewFileBrowserBrowseTree;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],63:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-ListDetail","DefaultRenderable":"ListDetail-Container","DefaultDestinationAddress":"#Pict-FileBrowser-ListPane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-ListDetail-Container-Template","Template":/*html*/`
2603
+ */getCurrentLocation(){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses.CurrentLocation||'AppData.PictFileBrowser.CurrentLocation';return this.pict.manifest.getValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress)||'';}}module.exports=PictViewFileBrowserBrowseTree;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],60:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-ListDetail","DefaultRenderable":"ListDetail-Container","DefaultDestinationAddress":"#Pict-FileBrowser-ListPane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-ListDetail-Container-Template","Template":/*html*/`
2914
2604
  <div class="pict-fb-detail" id="Pict-FileBrowser-DetailList">
2915
2605
  <div class="pict-fb-breadcrumb" id="Pict-FileBrowser-Breadcrumb"></div>
2916
2606
  <div class="pict-fb-detail-header">
@@ -2962,7 +2652,7 @@ tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Current-Template'
2962
2652
  * Get the current location from state.
2963
2653
  *
2964
2654
  * @returns {string} The current location path
2965
- */getCurrentLocation(){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses.CurrentLocation||'AppData.PictFileBrowser.CurrentLocation';return this.pict.manifest.getValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress)||'';}}module.exports=PictViewFileBrowserListDetail;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],64:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-ListIcons","DefaultRenderable":"ListIcons-Container","DefaultDestinationAddress":"#Pict-FileBrowser-ListPane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-ListIcons-Container-Template","Template":/*html*/`
2655
+ */getCurrentLocation(){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses.CurrentLocation||'AppData.PictFileBrowser.CurrentLocation';return this.pict.manifest.getValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress)||'';}}module.exports=PictViewFileBrowserListDetail;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],61:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-ListIcons","DefaultRenderable":"ListIcons-Container","DefaultDestinationAddress":"#Pict-FileBrowser-ListPane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-ListIcons-Container-Template","Template":/*html*/`
2966
2656
  <div id="Pict-FileBrowser-IconList">
2967
2657
  <div class="pict-fb-breadcrumb" id="Pict-FileBrowser-IconBreadcrumb"></div>
2968
2658
  <div class="pict-fb-icons" id="Pict-FileBrowser-IconGrid"></div>
@@ -3000,7 +2690,7 @@ tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Segment-Template'
3000
2690
  * Get the current location from state.
3001
2691
  *
3002
2692
  * @returns {string} The current location path
3003
- */getCurrentLocation(){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses.CurrentLocation||'AppData.PictFileBrowser.CurrentLocation';return this.pict.manifest.getValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress)||'';}}module.exports=PictViewFileBrowserListIcons;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],65:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-ViewFileInfo","DefaultRenderable":"ViewFileInfo-Container","DefaultDestinationAddress":"#Pict-FileBrowser-ViewPane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-ViewFileInfo-Container-Template","Template":/*html*/`<div class="pict-fb-fileinfo" id="Pict-FileBrowser-FileInfo"></div>`},{"Hash":"FileBrowser-ViewFileInfo-Detail-Template","Template":/*html*/`
2693
+ */getCurrentLocation(){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses.CurrentLocation||'AppData.PictFileBrowser.CurrentLocation';return this.pict.manifest.getValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress)||'';}}module.exports=PictViewFileBrowserListIcons;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],62:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-ViewFileInfo","DefaultRenderable":"ViewFileInfo-Container","DefaultDestinationAddress":"#Pict-FileBrowser-ViewPane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-ViewFileInfo-Container-Template","Template":/*html*/`<div class="pict-fb-fileinfo" id="Pict-FileBrowser-FileInfo"></div>`},{"Hash":"FileBrowser-ViewFileInfo-Detail-Template","Template":/*html*/`
3004
2694
  <div class="pict-fb-fileinfo-title">{~D:Record.Name~}</div>
3005
2695
  <table class="pict-fb-fileinfo-table">
3006
2696
  <tr>
@@ -3033,7 +2723,7 @@ tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Segment-Template'
3033
2723
  * After rendering the container, populate with file info.
3034
2724
  */onAfterRender(pRenderable){this.rebuildFileInfo();this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
3035
2725
  * Rebuild the file info display.
3036
- */rebuildFileInfo(){let tmpViewProvider=this.pict.providers['Pict-FileBrowser-View'];let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];let tmpCurrentFile=tmpViewProvider?tmpViewProvider.getCurrentFile():null;if(!tmpCurrentFile){let tmpEmptyHTML=this.pict.parseTemplateByHash('FileBrowser-ViewFileInfo-Empty-Template',{});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-FileInfo',tmpEmptyHTML);return;}let tmpRecord={Name:tmpCurrentFile.Name||'Unknown',TypeDescription:tmpViewProvider?tmpViewProvider.getFileTypeDescription(tmpCurrentFile):'File',SizeFormatted:tmpListProvider?tmpListProvider.formatFileSize(tmpCurrentFile.Size):tmpCurrentFile.Size||'--',ModifiedFormatted:tmpListProvider?tmpListProvider.formatDate(tmpCurrentFile.Modified):tmpCurrentFile.Modified||'--',Extension:tmpCurrentFile.Extension||'--',Path:tmpCurrentFile.Path||'--'};let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewFileInfo-Detail-Template',tmpRecord);this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-FileInfo',tmpHTML);}}module.exports=PictViewFileBrowserViewFileInfo;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],66:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-ViewImage","DefaultRenderable":"ViewImage-Container","DefaultDestinationAddress":"#Pict-FileBrowser-ViewPane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-ViewImage-Container-Template","Template":/*html*/`<div class="pict-fb-image-viewer" id="Pict-FileBrowser-ImageViewer"></div>`},{"Hash":"FileBrowser-ViewImage-Display-Template","Template":/*html*/`<img src="{~D:Record.ImageURL~}" alt="{~D:Record.Name~}" />`},{"Hash":"FileBrowser-ViewImage-NoImage-Template","Template":/*html*/`<div class="pict-fb-image-viewer-none">{~D:Record.Message~}</div>`}],"Renderables":[{"RenderableHash":"ViewImage-Container","TemplateHash":"FileBrowser-ViewImage-Container-Template","DestinationAddress":"#Pict-FileBrowser-ViewPane","RenderMethod":"replace"}]};/**
2726
+ */rebuildFileInfo(){let tmpViewProvider=this.pict.providers['Pict-FileBrowser-View'];let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];let tmpCurrentFile=tmpViewProvider?tmpViewProvider.getCurrentFile():null;if(!tmpCurrentFile){let tmpEmptyHTML=this.pict.parseTemplateByHash('FileBrowser-ViewFileInfo-Empty-Template',{});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-FileInfo',tmpEmptyHTML);return;}let tmpRecord={Name:tmpCurrentFile.Name||'Unknown',TypeDescription:tmpViewProvider?tmpViewProvider.getFileTypeDescription(tmpCurrentFile):'File',SizeFormatted:tmpListProvider?tmpListProvider.formatFileSize(tmpCurrentFile.Size):tmpCurrentFile.Size||'--',ModifiedFormatted:tmpListProvider?tmpListProvider.formatDate(tmpCurrentFile.Modified):tmpCurrentFile.Modified||'--',Extension:tmpCurrentFile.Extension||'--',Path:tmpCurrentFile.Path||'--'};let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewFileInfo-Detail-Template',tmpRecord);this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-FileInfo',tmpHTML);}}module.exports=PictViewFileBrowserViewFileInfo;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],63:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={"ViewIdentifier":"Pict-FileBrowser-ViewImage","DefaultRenderable":"ViewImage-Container","DefaultDestinationAddress":"#Pict-FileBrowser-ViewPane","AutoRender":false,"Templates":[{"Hash":"FileBrowser-ViewImage-Container-Template","Template":/*html*/`<div class="pict-fb-image-viewer" id="Pict-FileBrowser-ImageViewer"></div>`},{"Hash":"FileBrowser-ViewImage-Display-Template","Template":/*html*/`<img src="{~D:Record.ImageURL~}" alt="{~D:Record.Name~}" />`},{"Hash":"FileBrowser-ViewImage-NoImage-Template","Template":/*html*/`<div class="pict-fb-image-viewer-none">{~D:Record.Message~}</div>`}],"Renderables":[{"RenderableHash":"ViewImage-Container","TemplateHash":"FileBrowser-ViewImage-Container-Template","DestinationAddress":"#Pict-FileBrowser-ViewPane","RenderMethod":"replace"}]};/**
3037
2727
  * Viewing view that displays an image preview for the currently selected file.
3038
2728
  *
3039
2729
  * If the selected file is an image and has a URL or ThumbnailURL, it renders
@@ -3042,7 +2732,7 @@ tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Segment-Template'
3042
2732
  * After rendering the container, populate with the image or message.
3043
2733
  */onAfterRender(pRenderable){this.rebuildImageView();this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
3044
2734
  * Rebuild the image viewer display.
3045
- */rebuildImageView(){let tmpViewProvider=this.pict.providers['Pict-FileBrowser-View'];let tmpCurrentFile=tmpViewProvider?tmpViewProvider.getCurrentFile():null;if(!tmpCurrentFile){let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewImage-NoImage-Template',{Message:'No file selected'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-ImageViewer',tmpHTML);return;}let tmpIsImage=tmpViewProvider?tmpViewProvider.isImage(tmpCurrentFile):false;if(!tmpIsImage){let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewImage-NoImage-Template',{Message:'Selected file is not an image'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-ImageViewer',tmpHTML);return;}let tmpImageURL=tmpViewProvider?tmpViewProvider.getImageURL(tmpCurrentFile):null;if(!tmpImageURL){let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewImage-NoImage-Template',{Message:'No image URL available'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-ImageViewer',tmpHTML);return;}let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewImage-Display-Template',{ImageURL:tmpImageURL,Name:tmpCurrentFile.Name||'Image'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-ImageViewer',tmpHTML);}}module.exports=PictViewFileBrowserViewImage;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],67:[function(require,module,exports){const libPictView=require('pict-view');const _DefaultConfiguration=require('../Pict-Section-FileBrowser-DefaultConfiguration.js');const libBrowseProvider=require('../providers/Pict-Provider-FileBrowserBrowse.js');const libListProvider=require('../providers/Pict-Provider-FileBrowserList.js');const libViewProvider=require('../providers/Pict-Provider-FileBrowserView.js');const libLayoutProvider=require('../providers/Pict-Provider-FileBrowserLayout.js');const libIconProvider=require('../providers/Pict-Provider-FileBrowserIcons.js');/**
2735
+ */rebuildImageView(){let tmpViewProvider=this.pict.providers['Pict-FileBrowser-View'];let tmpCurrentFile=tmpViewProvider?tmpViewProvider.getCurrentFile():null;if(!tmpCurrentFile){let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewImage-NoImage-Template',{Message:'No file selected'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-ImageViewer',tmpHTML);return;}let tmpIsImage=tmpViewProvider?tmpViewProvider.isImage(tmpCurrentFile):false;if(!tmpIsImage){let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewImage-NoImage-Template',{Message:'Selected file is not an image'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-ImageViewer',tmpHTML);return;}let tmpImageURL=tmpViewProvider?tmpViewProvider.getImageURL(tmpCurrentFile):null;if(!tmpImageURL){let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewImage-NoImage-Template',{Message:'No image URL available'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-ImageViewer',tmpHTML);return;}let tmpHTML=this.pict.parseTemplateByHash('FileBrowser-ViewImage-Display-Template',{ImageURL:tmpImageURL,Name:tmpCurrentFile.Name||'Image'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-ImageViewer',tmpHTML);}}module.exports=PictViewFileBrowserViewImage;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],64:[function(require,module,exports){const libPictView=require('pict-view');const _DefaultConfiguration=require('../Pict-Section-FileBrowser-DefaultConfiguration.js');const libBrowseProvider=require('../providers/Pict-Provider-FileBrowserBrowse.js');const libListProvider=require('../providers/Pict-Provider-FileBrowserList.js');const libViewProvider=require('../providers/Pict-Provider-FileBrowserView.js');const libLayoutProvider=require('../providers/Pict-Provider-FileBrowserLayout.js');const libIconProvider=require('../providers/Pict-Provider-FileBrowserIcons.js');/**
3046
2736
  * Main FileBrowser view.
3047
2737
  *
3048
2738
  * Renders the outer container layout (browse pane, list pane, view pane)
@@ -3065,14 +2755,326 @@ if(!this.pict.AppData.PictFileBrowser.FileList){this.pict.AppData.PictFileBrowse
3065
2755
  */onAfterRender(pRenderable){this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
3066
2756
  * Get a state value by key.
3067
2757
  *
3068
- * @param {string} pKey - One of: Layout, RootLocation, CurrentLocation, CurrentFile
3069
- * @returns {*} The state value
3070
- */getState(pKey){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses[pKey];if(!tmpAddress){return undefined;}return this.pict.manifest.getValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress);}/**
3071
- * Set a state value by key.
2758
+ * @param {string} pKey - One of: Layout, RootLocation, CurrentLocation, CurrentFile
2759
+ * @returns {*} The state value
2760
+ */getState(pKey){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses[pKey];if(!tmpAddress){return undefined;}return this.pict.manifest.getValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress);}/**
2761
+ * Set a state value by key.
2762
+ *
2763
+ * @param {string} pKey - One of: Layout, RootLocation, CurrentLocation, CurrentFile
2764
+ * @param {*} pValue - The value to set
2765
+ */setState(pKey,pValue){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses[pKey];if(!tmpAddress){return;}this.pict.manifest.setValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress,pValue);}}module.exports=PictViewFileBrowser;module.exports.default_configuration=_DefaultConfiguration;},{"../Pict-Section-FileBrowser-DefaultConfiguration.js":50,"../providers/Pict-Provider-FileBrowserBrowse.js":52,"../providers/Pict-Provider-FileBrowserIcons.js":53,"../providers/Pict-Provider-FileBrowserLayout.js":54,"../providers/Pict-Provider-FileBrowserList.js":55,"../providers/Pict-Provider-FileBrowserView.js":56,"pict-view":76}],65:[function(require,module,exports){// The container for all the Pict-Section-Content related code.
2766
+ // The main content view class
2767
+ module.exports=require('./views/Pict-View-Content.js');// The content provider (markdown parsing, HTML escaping)
2768
+ module.exports.PictContentProvider=require('./providers/Pict-Provider-Content.js');},{"./providers/Pict-Provider-Content.js":66,"./views/Pict-View-Content.js":67}],66:[function(require,module,exports){const libPictProvider=require('pict-provider');const libCreateHighlighter=require('pict-section-code').createHighlighter;/**
2769
+ * Content Provider for Pict Section Content
2770
+ *
2771
+ * A general-purpose markdown-to-HTML parser with support for:
2772
+ * - Headings, paragraphs, lists, blockquotes, horizontal rules
2773
+ * - Fenced code blocks with language tags (nested fence support)
2774
+ * - Syntax highlighting and line numbers for code blocks (via pict-section-code)
2775
+ * - Tables (GFM pipe syntax)
2776
+ * - Mermaid diagram blocks
2777
+ * - KaTeX math (inline and display)
2778
+ * - Bold, italic, inline code, links, images
2779
+ *
2780
+ * Link resolution is customizable via an optional callback.
2781
+ */class PictContentProvider extends libPictProvider{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}/**
2782
+ * Highlight a code string using pict-section-code's syntax highlighter.
2783
+ * Uses a mock element to interface with the highlighter's DOM-based API.
2784
+ *
2785
+ * @param {string} pCode - The raw code string
2786
+ * @param {string} pLanguage - The language identifier (e.g. "javascript", "html")
2787
+ * @returns {string} The syntax-highlighted HTML
2788
+ */highlightCode(pCode,pLanguage){if(!pCode){return'';}let tmpHighlighter=libCreateHighlighter(pLanguage);// Create a mock element to interface with the highlighter
2789
+ let tmpMockElement={textContent:pCode,innerHTML:''};tmpHighlighter(tmpMockElement);return tmpMockElement.innerHTML;}/**
2790
+ * Generate line number HTML for a code block.
2791
+ *
2792
+ * @param {string} pCode - The raw code string
2793
+ * @returns {string} HTML string with line number spans
2794
+ */generateLineNumbers(pCode){if(!pCode){return'<span>1</span>';}let tmpLineCount=pCode.split('\n').length;let tmpHTML='';for(let i=1;i<=tmpLineCount;i++){tmpHTML+='<span>'+i+'</span>';}return tmpHTML;}/**
2795
+ * Parse a markdown string into HTML.
2796
+ *
2797
+ * @param {string} pMarkdown - The raw markdown text
2798
+ * @param {Function} [pLinkResolver] - Optional callback for link resolution: (pHref, pLinkText) => { href, target, rel } or null
2799
+ * @param {Function} [pImageResolver] - Optional callback for image URL resolution: (pSrc, pAlt) => resolvedSrc or null
2800
+ * @returns {string} The parsed HTML
2801
+ */parseMarkdown(pMarkdown,pLinkResolver,pImageResolver){if(!pMarkdown){return'';}let tmpLines=pMarkdown.split('\n');let tmpHTML=[];let tmpInCodeBlock=false;let tmpCodeFenceLength=0;let tmpCodeLang='';let tmpCodeLines=[];let tmpInList=false;let tmpListType='';let tmpInBlockquote=false;let tmpBlockquoteLines=[];let tmpInMathBlock=false;let tmpMathLines=[];let tmpParagraphLines=[];// Helper to flush accumulated paragraph lines into a single <p> tag
2802
+ let fFlushParagraph=()=>{if(tmpParagraphLines.length>0){tmpHTML.push('<p>'+tmpParagraphLines.map(pLine=>{return this.parseInline(pLine,pLinkResolver,pImageResolver);}).join(' ')+'</p>');tmpParagraphLines=[];}};for(let i=0;i<tmpLines.length;i++){let tmpLine=tmpLines[i];// Display math blocks ($$...$$) — skip if inside a code block
2803
+ if(!tmpInCodeBlock&&tmpLine.trim().match(/^\$\$/)){if(tmpInMathBlock){// End math block
2804
+ tmpHTML.push('<div class="pict-content-katex-display">'+tmpMathLines.join('\n')+'</div>');tmpInMathBlock=false;tmpMathLines=[];}else{// Flush any pending paragraph
2805
+ fFlushParagraph();// Close any open list or blockquote
2806
+ if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver,pImageResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}tmpInMathBlock=true;}continue;}if(tmpInMathBlock){tmpMathLines.push(tmpLine);continue;}// Code blocks (fenced) — track fence length so ````x```` nests around ```y```
2807
+ let tmpFenceMatch=tmpLine.match(/^(`{3,})/);if(tmpFenceMatch){let tmpFenceLen=tmpFenceMatch[1].length;if(tmpInCodeBlock){// Only close if the closing fence is at least as long as the opening
2808
+ if(tmpFenceLen>=tmpCodeFenceLength&&tmpLine.trim()===tmpFenceMatch[1]){// End code block
2809
+ if(tmpCodeLang==='mermaid'){// Mermaid diagrams: output raw content for client-side rendering
2810
+ tmpHTML.push('<pre class="mermaid">'+tmpCodeLines.join('\n')+'</pre>');}else{let tmpCodeText=tmpCodeLines.join('\n');let tmpHighlightedCode=this.highlightCode(tmpCodeText,tmpCodeLang);let tmpLineNumbersHTML=this.generateLineNumbers(tmpCodeText);tmpHTML.push('<div class="pict-content-code-wrap"><div class="pict-content-code-line-numbers">'+tmpLineNumbersHTML+'</div><pre><code class="language-'+this.escapeHTML(tmpCodeLang)+'">'+tmpHighlightedCode+'</code></pre></div>');}tmpInCodeBlock=false;tmpCodeFenceLength=0;tmpCodeLang='';tmpCodeLines=[];continue;}else{// Inner fence with fewer backticks — treat as content
2811
+ tmpCodeLines.push(tmpLine);continue;}}else{// Flush any pending paragraph
2812
+ fFlushParagraph();// Close any open list or blockquote
2813
+ if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver,pImageResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}// Start code block — record fence length
2814
+ tmpCodeFenceLength=tmpFenceLen;tmpCodeLang=tmpLine.replace(/^`{3,}/,'').trim();tmpInCodeBlock=true;continue;}}if(tmpInCodeBlock){tmpCodeLines.push(tmpLine);continue;}// Blockquotes
2815
+ if(tmpLine.match(/^>\s?/)){if(!tmpInBlockquote){// Flush any pending paragraph
2816
+ fFlushParagraph();// Close any open list
2817
+ if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}tmpInBlockquote=true;tmpBlockquoteLines=[];}tmpBlockquoteLines.push(tmpLine.replace(/^>\s?/,''));continue;}else if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver,pImageResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}// Horizontal rule
2818
+ if(tmpLine.match(/^(-{3,}|\*{3,}|_{3,})\s*$/)){fFlushParagraph();if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}tmpHTML.push('<hr>');continue;}// Headings
2819
+ let tmpHeadingMatch=tmpLine.match(/^(#{1,6})\s+(.+)/);if(tmpHeadingMatch){fFlushParagraph();if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}let tmpLevel=tmpHeadingMatch[1].length;let tmpText=this.parseInline(tmpHeadingMatch[2],pLinkResolver,pImageResolver);let tmpID=tmpHeadingMatch[2].toLowerCase().replace(/[^\w\s-]/g,'').replace(/\s+/g,'-');tmpHTML.push('<h'+tmpLevel+' id="'+tmpID+'">'+tmpText+'</h'+tmpLevel+'>');continue;}// Unordered list items
2820
+ let tmpULMatch=tmpLine.match(/^(\s*)[-*+]\s+(.*)/);if(tmpULMatch){fFlushParagraph();if(!tmpInList||tmpListType!=='ul'){if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}tmpHTML.push('<ul>');tmpInList=true;tmpListType='ul';}tmpHTML.push('<li>'+this.parseInline(tmpULMatch[2],pLinkResolver,pImageResolver)+'</li>');continue;}// Ordered list items
2821
+ let tmpOLMatch=tmpLine.match(/^(\s*)\d+\.\s+(.*)/);if(tmpOLMatch){fFlushParagraph();if(!tmpInList||tmpListType!=='ol'){if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}tmpHTML.push('<ol>');tmpInList=true;tmpListType='ol';}tmpHTML.push('<li>'+this.parseInline(tmpOLMatch[2],pLinkResolver,pImageResolver)+'</li>');continue;}// Close list if we've left list items
2822
+ if(tmpInList&&tmpLine.trim()!==''){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}// Empty line — flush any accumulated paragraph
2823
+ if(tmpLine.trim()===''){fFlushParagraph();continue;}// Table detection
2824
+ if(tmpLine.match(/^\|/)&&i+1<tmpLines.length&&tmpLines[i+1].match(/^\|[\s-:|]+\|/)){fFlushParagraph();// Close any open list
2825
+ if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}let tmpTableHTML='<table>';// Header row
2826
+ let tmpHeaders=tmpLine.split('|').filter(pCell=>{return pCell.trim()!=='';});tmpTableHTML+='<thead><tr>';for(let h=0;h<tmpHeaders.length;h++){tmpTableHTML+='<th>'+this.parseInline(tmpHeaders[h].trim(),pLinkResolver,pImageResolver)+'</th>';}tmpTableHTML+='</tr></thead>';// Skip separator row
2827
+ i++;// Body rows
2828
+ tmpTableHTML+='<tbody>';while(i+1<tmpLines.length&&tmpLines[i+1].match(/^\|/)){i++;let tmpCells=tmpLines[i].split('|').filter(pCell=>{return pCell.trim()!=='';});tmpTableHTML+='<tr>';for(let c=0;c<tmpCells.length;c++){tmpTableHTML+='<td>'+this.parseInline(tmpCells[c].trim(),pLinkResolver,pImageResolver)+'</td>';}tmpTableHTML+='</tr>';}tmpTableHTML+='</tbody></table>';tmpHTML.push(tmpTableHTML);continue;}// Accumulate paragraph lines — consecutive non-blank text lines
2829
+ // will be joined into a single <p> tag when flushed
2830
+ tmpParagraphLines.push(tmpLine);}// Flush any remaining accumulated paragraph
2831
+ fFlushParagraph();// Close any trailing open elements
2832
+ if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver,pImageResolver)+'</blockquote>');}if(tmpInCodeBlock){let tmpCodeText=tmpCodeLines.join('\n');let tmpHighlightedCode=this.highlightCode(tmpCodeText,tmpCodeLang);let tmpLineNumbersHTML=this.generateLineNumbers(tmpCodeText);tmpHTML.push('<div class="pict-content-code-wrap"><div class="pict-content-code-line-numbers">'+tmpLineNumbersHTML+'</div><pre><code>'+tmpHighlightedCode+'</code></pre></div>');}return tmpHTML.join('\n');}/**
2833
+ * Parse inline markdown elements (bold, italic, code, links, images, KaTeX).
2834
+ *
2835
+ * @param {string} pText - The text to parse
2836
+ * @param {Function} [pLinkResolver] - Optional callback: (pHref, pLinkText) => { href, target, rel } or null
2837
+ * @param {Function} [pImageResolver] - Optional callback: (pSrc, pAlt) => resolvedSrc or null
2838
+ * @returns {string} HTML with inline elements
2839
+ */parseInline(pText,pLinkResolver,pImageResolver){if(!pText){return'';}let tmpResult=pText;// Extract inline code spans into placeholders so bold/italic regexes don't mangle their contents
2840
+ let tmpCodeSpans=[];tmpResult=tmpResult.replace(/`([^`]+)`/g,(pMatch,pCode)=>{let tmpIndex=tmpCodeSpans.length;tmpCodeSpans.push('<code>'+pCode+'</code>');return'\x00CODEINLINE'+tmpIndex+'\x00';});// Inline LaTeX equations ($...$) — must be processed before other inline patterns
2841
+ // Match single $ delimiters that aren't adjacent to spaces (to avoid false positives with currency)
2842
+ tmpResult=tmpResult.replace(/\$([^\$\s][^\$]*?[^\$\s])\$/g,'<span class="pict-content-katex-inline">$1</span>');// Also match single-character inline math like $x$
2843
+ tmpResult=tmpResult.replace(/\$([^\$\s])\$/g,'<span class="pict-content-katex-inline">$1</span>');// Images
2844
+ tmpResult=tmpResult.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(pMatch,pAlt,pSrc)=>{let tmpSrc=pSrc;if(typeof pImageResolver==='function'){let tmpResolved=pImageResolver(pSrc,pAlt);if(tmpResolved){tmpSrc=tmpResolved;}}return'<img src="'+tmpSrc+'" alt="'+pAlt+'">';});// Links
2845
+ tmpResult=tmpResult.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(pMatch,pLinkText,pHref)=>{if(typeof pLinkResolver==='function'){let tmpResolved=pLinkResolver(pHref,pLinkText);if(tmpResolved){let tmpTarget=tmpResolved.target?' target="'+tmpResolved.target+'"':'';let tmpRel=tmpResolved.rel?' rel="'+tmpResolved.rel+'"':'';return'<a href="'+tmpResolved.href+'"'+tmpTarget+tmpRel+'>'+pLinkText+'</a>';}}// Default behavior: external links open in new tab
2846
+ if(pHref.match(/^https?:\/\//)){return'<a href="'+pHref+'" target="_blank" rel="noopener">'+pLinkText+'</a>';}return'<a href="'+pHref+'">'+pLinkText+'</a>';});// Bold
2847
+ tmpResult=tmpResult.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');tmpResult=tmpResult.replace(/__([^_]+)__/g,'<strong>$1</strong>');// Italic
2848
+ tmpResult=tmpResult.replace(/\*([^*]+)\*/g,'<em>$1</em>');tmpResult=tmpResult.replace(/_([^_]+)_/g,'<em>$1</em>');// Restore inline code spans from placeholders
2849
+ tmpResult=tmpResult.replace(/\x00CODEINLINE(\d+)\x00/g,(pMatch,pIndex)=>{return tmpCodeSpans[parseInt(pIndex)];});return tmpResult;}/**
2850
+ * Escape HTML special characters.
2851
+ *
2852
+ * @param {string} pText - The text to escape
2853
+ * @returns {string} The escaped text
2854
+ */escapeHTML(pText){if(!pText){return'';}return pText.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');}}module.exports=PictContentProvider;module.exports.default_configuration={ProviderIdentifier:"Pict-Content",AutoInitialize:true,AutoInitializeOrdinal:0};},{"pict-provider":46,"pict-section-code":49}],67:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"Pict-Content",DefaultRenderable:"Pict-Content-Display",DefaultDestinationAddress:"#Pict-Content-Container",AutoRender:false,CSS:/*css*/`
2855
+ .pict-content {
2856
+ padding: 2em 3em;
2857
+ max-width: 900px;
2858
+ margin: 0 auto;
2859
+ }
2860
+ .pict-content-loading {
2861
+ display: flex;
2862
+ align-items: center;
2863
+ justify-content: center;
2864
+ min-height: 200px;
2865
+ color: #8A7F72;
2866
+ font-size: 1em;
2867
+ }
2868
+ .pict-content h1 {
2869
+ font-size: 2em;
2870
+ color: #3D3229;
2871
+ border-bottom: 1px solid #DDD6CA;
2872
+ padding-bottom: 0.3em;
2873
+ margin-top: 0;
2874
+ }
2875
+ .pict-content h2 {
2876
+ font-size: 1.5em;
2877
+ color: #3D3229;
2878
+ border-bottom: 1px solid #EAE3D8;
2879
+ padding-bottom: 0.25em;
2880
+ margin-top: 1.5em;
2881
+ }
2882
+ .pict-content h3 {
2883
+ font-size: 1.25em;
2884
+ color: #3D3229;
2885
+ margin-top: 1.25em;
2886
+ }
2887
+ .pict-content h4, .pict-content h5, .pict-content h6 {
2888
+ color: #5E5549;
2889
+ margin-top: 1em;
2890
+ }
2891
+ .pict-content p {
2892
+ line-height: 1.7;
2893
+ color: #423D37;
2894
+ margin: 0.75em 0;
2895
+ }
2896
+ .pict-content a {
2897
+ color: #2E7D74;
2898
+ text-decoration: none;
2899
+ }
2900
+ .pict-content a:hover {
2901
+ text-decoration: underline;
2902
+ }
2903
+ .pict-content-code-wrap {
2904
+ position: relative;
2905
+ font-family: 'SFMono-Regular', 'SF Mono', 'Menlo', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
2906
+ font-size: 14px;
2907
+ line-height: 1.5;
2908
+ border-radius: 6px;
2909
+ overflow: auto;
2910
+ margin: 1em 0;
2911
+ background: #3D3229;
2912
+ }
2913
+ .pict-content-code-wrap .pict-content-code-line-numbers {
2914
+ position: absolute;
2915
+ top: 0;
2916
+ left: 0;
2917
+ width: 40px;
2918
+ padding: 1.25em 0;
2919
+ text-align: right;
2920
+ background: #342A22;
2921
+ border-right: 1px solid #4A3F35;
2922
+ color: #8A7F72;
2923
+ font-size: 13px;
2924
+ line-height: 1.5;
2925
+ user-select: none;
2926
+ pointer-events: none;
2927
+ box-sizing: border-box;
2928
+ }
2929
+ .pict-content-code-wrap .pict-content-code-line-numbers span {
2930
+ display: block;
2931
+ padding: 0 8px 0 0;
2932
+ }
2933
+ .pict-content-code-wrap pre {
2934
+ margin: 0;
2935
+ background: #3D3229;
2936
+ color: #E8E0D4;
2937
+ padding: 1.25em 1.25em 1.25em 52px;
2938
+ border-radius: 6px;
2939
+ overflow-x: auto;
2940
+ line-height: 1.5;
2941
+ font-size: inherit;
2942
+ }
2943
+ .pict-content-code-wrap pre code {
2944
+ background: none;
2945
+ padding: 0;
2946
+ color: inherit;
2947
+ font-size: inherit;
2948
+ font-family: inherit;
2949
+ }
2950
+ .pict-content-code-wrap .keyword { color: #C678DD; }
2951
+ .pict-content-code-wrap .string { color: #98C379; }
2952
+ .pict-content-code-wrap .number { color: #D19A66; }
2953
+ .pict-content-code-wrap .comment { color: #7F848E; font-style: italic; }
2954
+ .pict-content-code-wrap .operator { color: #56B6C2; }
2955
+ .pict-content-code-wrap .punctuation { color: #E8E0D4; }
2956
+ .pict-content-code-wrap .function-name { color: #61AFEF; }
2957
+ .pict-content-code-wrap .property { color: #E06C75; }
2958
+ .pict-content-code-wrap .tag { color: #E06C75; }
2959
+ .pict-content-code-wrap .attr-name { color: #D19A66; }
2960
+ .pict-content-code-wrap .attr-value { color: #98C379; }
2961
+ .pict-content pre {
2962
+ background: #3D3229;
2963
+ color: #E8E0D4;
2964
+ padding: 1.25em;
2965
+ border-radius: 6px;
2966
+ overflow-x: auto;
2967
+ line-height: 1.5;
2968
+ font-size: 0.9em;
2969
+ }
2970
+ .pict-content code {
2971
+ background: #F0ECE4;
2972
+ padding: 0.15em 0.4em;
2973
+ border-radius: 3px;
2974
+ font-size: 0.9em;
2975
+ color: #9E6B47;
2976
+ }
2977
+ .pict-content pre code {
2978
+ background: none;
2979
+ padding: 0;
2980
+ color: inherit;
2981
+ font-size: inherit;
2982
+ }
2983
+ .pict-content blockquote {
2984
+ border-left: 4px solid #2E7D74;
2985
+ margin: 1em 0;
2986
+ padding: 0.5em 1em;
2987
+ background: #F7F5F0;
2988
+ color: #5E5549;
2989
+ }
2990
+ .pict-content blockquote p {
2991
+ margin: 0.25em 0;
2992
+ }
2993
+ .pict-content ul, .pict-content ol {
2994
+ padding-left: 2em;
2995
+ line-height: 1.8;
2996
+ }
2997
+ .pict-content li {
2998
+ margin: 0.25em 0;
2999
+ color: #423D37;
3000
+ }
3001
+ .pict-content hr {
3002
+ border: none;
3003
+ border-top: 1px solid #DDD6CA;
3004
+ margin: 2em 0;
3005
+ }
3006
+ .pict-content table {
3007
+ width: 100%;
3008
+ border-collapse: collapse;
3009
+ margin: 1em 0;
3010
+ }
3011
+ .pict-content table th {
3012
+ background: #F5F0E8;
3013
+ border: 1px solid #DDD6CA;
3014
+ padding: 0.6em 0.8em;
3015
+ text-align: left;
3016
+ font-weight: 600;
3017
+ color: #3D3229;
3018
+ }
3019
+ .pict-content table td {
3020
+ border: 1px solid #DDD6CA;
3021
+ padding: 0.5em 0.8em;
3022
+ color: #423D37;
3023
+ }
3024
+ .pict-content table tr:nth-child(even) {
3025
+ background: #F7F5F0;
3026
+ }
3027
+ .pict-content img {
3028
+ max-width: 100%;
3029
+ height: auto;
3030
+ }
3031
+ .pict-content pre.mermaid {
3032
+ background: #fff;
3033
+ color: #3D3229;
3034
+ text-align: center;
3035
+ padding: 1em;
3036
+ }
3037
+ .pict-content .pict-content-katex-display {
3038
+ text-align: center;
3039
+ margin: 1em 0;
3040
+ padding: 0.5em;
3041
+ overflow-x: auto;
3042
+ }
3043
+ .pict-content .pict-content-katex-inline {
3044
+ display: inline;
3045
+ }
3046
+ `,Templates:[{Hash:"Pict-Content-Template",Template:/*html*/`
3047
+ <div class="pict-content" id="Pict-Content-Body">
3048
+ <div class="pict-content-loading">Loading content...</div>
3049
+ </div>
3050
+ `}],Renderables:[{RenderableHash:"Pict-Content-Display",TemplateHash:"Pict-Content-Template",DestinationAddress:"#Pict-Content-Container",RenderMethod:"replace"}]};class PictContentView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}/**
3051
+ * Display parsed HTML content in the content area.
3052
+ *
3053
+ * @param {string} pHTMLContent - The HTML to display
3054
+ * @param {string} [pContainerID] - The container element ID (defaults to 'Pict-Content-Body')
3055
+ */displayContent(pHTMLContent,pContainerID){let tmpContainerID=pContainerID||'Pict-Content-Body';this.pict.ContentAssignment.assignContent('#'+tmpContainerID,pHTMLContent);// Scroll to top of content area
3056
+ let tmpContentContainer=document.getElementById(tmpContainerID);if(tmpContentContainer&&tmpContentContainer.parentElement){tmpContentContainer.parentElement.scrollTop=0;}// Post-render: initialize Mermaid diagrams if mermaid is available
3057
+ this.renderMermaidDiagrams(tmpContainerID);// Post-render: render KaTeX equations if katex is available
3058
+ this.renderKaTeXEquations(tmpContainerID);}/**
3059
+ * Render any Mermaid diagram blocks in the content area.
3060
+ * Mermaid blocks are `<pre class="mermaid">` elements produced by parseMarkdown.
3072
3061
  *
3073
- * @param {string} pKey - One of: Layout, RootLocation, CurrentLocation, CurrentFile
3074
- * @param {*} pValue - The value to set
3075
- */setState(pKey,pValue){let tmpStateAddresses=this.options.StateAddresses||{};let tmpAddress=tmpStateAddresses[pKey];if(!tmpAddress){return;}this.pict.manifest.setValueByHash({AppData:this.pict.AppData,Pict:this.pict},tmpAddress,pValue);}}module.exports=PictViewFileBrowser;module.exports.default_configuration=_DefaultConfiguration;},{"../Pict-Section-FileBrowser-DefaultConfiguration.js":53,"../providers/Pict-Provider-FileBrowserBrowse.js":55,"../providers/Pict-Provider-FileBrowserIcons.js":56,"../providers/Pict-Provider-FileBrowserLayout.js":57,"../providers/Pict-Provider-FileBrowserList.js":58,"../providers/Pict-Provider-FileBrowserView.js":59,"pict-view":76}],68:[function(require,module,exports){/**
3062
+ * @param {string} [pContainerID] - The container element ID (defaults to 'Pict-Content-Body')
3063
+ */renderMermaidDiagrams(pContainerID){if(typeof mermaid==='undefined'){return;}let tmpContainerID=pContainerID||'Pict-Content-Body';let tmpContentBody=document.getElementById(tmpContainerID);if(!tmpContentBody){return;}let tmpMermaidElements=tmpContentBody.querySelectorAll('pre.mermaid');if(tmpMermaidElements.length<1){return;}// mermaid.run() will process all pre.mermaid elements in the container
3064
+ try{mermaid.run({nodes:tmpMermaidElements});}catch(pError){this.log.error('Mermaid rendering error: '+pError.message);}}/**
3065
+ * Render KaTeX inline and display math elements in the content area.
3066
+ * Inline: `<span class="pict-content-katex-inline">`
3067
+ * Display: `<div class="pict-content-katex-display">`
3068
+ *
3069
+ * @param {string} [pContainerID] - The container element ID (defaults to 'Pict-Content-Body')
3070
+ */renderKaTeXEquations(pContainerID){if(typeof katex==='undefined'){return;}let tmpContainerID=pContainerID||'Pict-Content-Body';let tmpContentBody=document.getElementById(tmpContainerID);if(!tmpContentBody){return;}// Render inline math
3071
+ let tmpInlineElements=tmpContentBody.querySelectorAll('.pict-content-katex-inline');for(let i=0;i<tmpInlineElements.length;i++){try{katex.render(tmpInlineElements[i].textContent,tmpInlineElements[i],{throwOnError:false,displayMode:false});}catch(pError){this.log.warn('KaTeX inline error: '+pError.message);}}// Render display math
3072
+ let tmpDisplayElements=tmpContentBody.querySelectorAll('.pict-content-katex-display');for(let i=0;i<tmpDisplayElements.length;i++){try{katex.render(tmpDisplayElements[i].textContent,tmpDisplayElements[i],{throwOnError:false,displayMode:true});}catch(pError){this.log.warn('KaTeX display error: '+pError.message);}}}/**
3073
+ * Show a loading indicator.
3074
+ *
3075
+ * @param {string} [pMessage] - Loading message (defaults to 'Loading content...')
3076
+ * @param {string} [pContainerID] - The container element ID (defaults to 'Pict-Content-Body')
3077
+ */showLoading(pMessage,pContainerID){let tmpContainerID=pContainerID||'Pict-Content-Body';let tmpMessage=pMessage||'Loading content...';this.pict.ContentAssignment.assignContent('#'+tmpContainerID,'<div class="pict-content-loading">'+tmpMessage+'</div>');}}module.exports=PictContentView;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],68:[function(require,module,exports){/**
3076
3078
  * Pict-MDE-CodeMirror: Helper module for PictSectionMarkdownEditor
3077
3079
  *
3078
3080
  * Handles CodeMirror editor instance creation, extension configuration,
@@ -3088,22 +3090,53 @@ if(!this.pict.AppData.PictFileBrowser.FileList){this.pict.AppData.PictFileBrowse
3088
3090
  * @param {number} pSegmentIndex - The segment index
3089
3091
  * @param {string} pContent - The initial content
3090
3092
  */pView._createEditorInContainer=function _createEditorInContainer(pContainer,pSegmentIndex,pContent){let tmpCM=pView._codeMirrorModules;// Build the extensions array
3091
- let tmpExtensions=[];// Add consumer-provided extensions (e.g. basicSetup, markdown())
3093
+ let tmpExtensions=[];// Keyboard shortcuts for formatting, inter-segment navigation, and image paste handling
3094
+ // IMPORTANT: Must be added BEFORE consumer extensions (e.g. basicSetup) so that
3095
+ // our domEventHandlers fire before CM6's internal keymap handlers.
3096
+ tmpExtensions.push(tmpCM.EditorView.domEventHandlers({keydown:(pEvent,pEditorView)=>{// Ctrl/Cmd + B = bold
3097
+ if((pEvent.ctrlKey||pEvent.metaKey)&&pEvent.key==='b'){pEvent.preventDefault();pView.applyFormatting(pSegmentIndex,'bold');return true;}// Ctrl/Cmd + I = italic
3098
+ if((pEvent.ctrlKey||pEvent.metaKey)&&pEvent.key==='i'){pEvent.preventDefault();pView.applyFormatting(pSegmentIndex,'italic');return true;}// Ctrl/Cmd + E = inline code
3099
+ if((pEvent.ctrlKey||pEvent.metaKey)&&pEvent.key==='e'){pEvent.preventDefault();pView.applyFormatting(pSegmentIndex,'code');return true;}},paste:(pEvent,pEditorView)=>{// Check clipboard for image data
3100
+ let tmpItems=pEvent.clipboardData&&pEvent.clipboardData.items;if(!tmpItems){return false;}for(let i=0;i<tmpItems.length;i++){if(tmpItems[i].type.startsWith('image/')){pEvent.preventDefault();let tmpFile=tmpItems[i].getAsFile();if(tmpFile){pView._processImageFile(tmpFile,pSegmentIndex);}return true;}}return false;},drop:(pEvent,pEditorView)=>{// Intercept image file drops at the CodeMirror level to prevent
3101
+ // the browser from inserting the image as a raw DOM element.
3102
+ // Without this, both CodeMirror's default drop behavior AND the
3103
+ // container-level handler fire, causing rendering artifacts.
3104
+ if(pView._dragSourceIndex>=0){return false;// segment-reorder drag, not a file drop
3105
+ }if(!pEvent.dataTransfer||!pEvent.dataTransfer.files||pEvent.dataTransfer.files.length<1){return false;}let tmpFile=pEvent.dataTransfer.files[0];if(tmpFile.type&&tmpFile.type.startsWith('image/')){pEvent.preventDefault();pEvent.stopPropagation();pView._processImageFile(tmpFile,pSegmentIndex);// Clean up the dragover visual indicator on the container
3106
+ let tmpContainer=pEditorView.dom.closest('.pict-mde-segment-editor');if(tmpContainer){tmpContainer.classList.remove('pict-mde-image-dragover');}return true;}return false;}}));// Add consumer-provided extensions (e.g. basicSetup, markdown())
3092
3107
  if(tmpCM.extensions&&Array.isArray(tmpCM.extensions)){tmpExtensions=tmpExtensions.concat(tmpCM.extensions);}// Update listener for content changes, focus, and cursor tracking
3093
3108
  tmpExtensions.push(tmpCM.EditorView.updateListener.of(pUpdate=>{if(pUpdate.docChanged){pView._onSegmentContentChange(pSegmentIndex,pUpdate.state.doc.toString());}// Track focus changes to toggle the active class
3094
3109
  if(pUpdate.focusChanged){if(pUpdate.view.hasFocus){pView._setActiveSegment(pSegmentIndex);// Position sidebar at cursor on focus
3095
3110
  pView._updateSidebarPosition(pSegmentIndex);}else{// Small delay so clicking a sidebar button doesn't immediately deactivate
3096
3111
  setTimeout(()=>{if(pView._activeSegmentIndex===pSegmentIndex){// Check if focus moved to another segment or truly left
3097
3112
  let tmpSegEl=document.getElementById(`PictMDE-Segment-${pSegmentIndex}`);if(tmpSegEl&&!tmpSegEl.contains(document.activeElement)){pView._clearActiveSegment(pSegmentIndex);pView._resetSidebarPosition(pSegmentIndex);}}},100);}}// Track cursor/selection changes to move the sidebar
3098
- if(pUpdate.selectionSet&&pUpdate.view.hasFocus){pView._updateSidebarPosition(pSegmentIndex);}}));// Keyboard shortcuts for formatting and image paste handling
3099
- tmpExtensions.push(tmpCM.EditorView.domEventHandlers({keydown:(pEvent,pEditorView)=>{// Ctrl/Cmd + B = bold
3100
- if((pEvent.ctrlKey||pEvent.metaKey)&&pEvent.key==='b'){pEvent.preventDefault();pView.applyFormatting(pSegmentIndex,'bold');return true;}// Ctrl/Cmd + I = italic
3101
- if((pEvent.ctrlKey||pEvent.metaKey)&&pEvent.key==='i'){pEvent.preventDefault();pView.applyFormatting(pSegmentIndex,'italic');return true;}// Ctrl/Cmd + E = inline code
3102
- if((pEvent.ctrlKey||pEvent.metaKey)&&pEvent.key==='e'){pEvent.preventDefault();pView.applyFormatting(pSegmentIndex,'code');return true;}},paste:(pEvent,pEditorView)=>{// Check clipboard for image data
3103
- let tmpItems=pEvent.clipboardData&&pEvent.clipboardData.items;if(!tmpItems){return false;}for(let i=0;i<tmpItems.length;i++){if(tmpItems[i].type.startsWith('image/')){pEvent.preventDefault();let tmpFile=tmpItems[i].getAsFile();if(tmpFile){pView._processImageFile(tmpFile,pSegmentIndex);}return true;}}return false;}}));// Collapse long data URIs in image markdown so the editor is readable
3113
+ if(pUpdate.selectionSet&&pUpdate.view.hasFocus){pView._updateSidebarPosition(pSegmentIndex);}}));// Collapse long data URIs in image markdown so the editor is readable
3104
3114
  let tmpCollapseExtension=pView._buildDataURICollapseExtension();if(tmpCollapseExtension){tmpExtensions.push(tmpCollapseExtension);}// Make read-only if configured
3105
3115
  if(pView.options.ReadOnly){tmpExtensions.push(tmpCM.EditorState.readOnly.of(true));tmpExtensions.push(tmpCM.EditorView.editable.of(false));}// Allow consumer to customize extensions
3106
- tmpExtensions=pView.customConfigureExtensions(tmpExtensions,pSegmentIndex);let tmpState=tmpCM.EditorState.create({doc:pContent||'',extensions:tmpExtensions});let tmpEditorView=new tmpCM.EditorView({state:tmpState,parent:pContainer});pView._segmentEditors[pSegmentIndex]=tmpEditorView;};/**
3116
+ tmpExtensions=pView.customConfigureExtensions(tmpExtensions,pSegmentIndex);let tmpState=tmpCM.EditorState.create({doc:pContent||'',extensions:tmpExtensions});let tmpEditorView=new tmpCM.EditorView({state:tmpState,parent:pContainer});pView._segmentEditors[pSegmentIndex]=tmpEditorView;// -- Inter-segment arrow-key navigation --
3117
+ // Use a capture-phase DOM listener so we intercept ArrowDown/ArrowUp
3118
+ // BEFORE CodeMirror's internal keymap handlers process them.
3119
+ tmpEditorView.contentDOM.addEventListener('keydown',function(pEvent){if(pEvent.key!=='ArrowDown'&&pEvent.key!=='ArrowUp'){return;}// Don't interfere if a modifier key is held (selection, etc.)
3120
+ if(pEvent.shiftKey||pEvent.ctrlKey||pEvent.metaKey||pEvent.altKey){return;}let tmpState=tmpEditorView.state;let tmpCursorPos=tmpState.selection.main.head;let tmpLine=tmpState.doc.lineAt(tmpCursorPos);let tmpColumnOffset=tmpCursorPos-tmpLine.from;if(pEvent.key==='ArrowDown'){// Only navigate when cursor is on the very last line
3121
+ if(tmpLine.to<tmpState.doc.length){return;// not on last line — let CM handle it
3122
+ }// Find next segment
3123
+ let tmpOrderedIndices=pView._getOrderedSegmentIndices();let tmpLogicalIndex=pView._getLogicalIndex(pSegmentIndex);if(tmpLogicalIndex<0||tmpLogicalIndex>=tmpOrderedIndices.length-1){return;// last segment — nowhere to go
3124
+ }let tmpNextInternalIndex=tmpOrderedIndices[tmpLogicalIndex+1];let tmpNextEditor=pView._segmentEditors[tmpNextInternalIndex];if(!tmpNextEditor){return;}pEvent.preventDefault();pEvent.stopPropagation();// Focus the next editor and place cursor on the first line
3125
+ let tmpFirstLine=tmpNextEditor.state.doc.line(1);let tmpTargetCol=Math.min(tmpColumnOffset,tmpFirstLine.to-tmpFirstLine.from);tmpNextEditor.focus();tmpNextEditor.dispatch({selection:{anchor:tmpFirstLine.from+tmpTargetCol}});pView._setActiveSegment(tmpNextInternalIndex);}else if(pEvent.key==='ArrowUp'){// Only navigate when cursor is on the very first line
3126
+ if(tmpLine.number>1){return;// not on first line — let CM handle it
3127
+ }// Find previous segment
3128
+ let tmpOrderedIndices=pView._getOrderedSegmentIndices();let tmpLogicalIndex=pView._getLogicalIndex(pSegmentIndex);if(tmpLogicalIndex<=0){return;// first segment — nowhere to go
3129
+ }let tmpPrevInternalIndex=tmpOrderedIndices[tmpLogicalIndex-1];let tmpPrevEditor=pView._segmentEditors[tmpPrevInternalIndex];if(!tmpPrevEditor){return;}pEvent.preventDefault();pEvent.stopPropagation();// Focus the previous editor and place cursor on the last line
3130
+ let tmpLastLine=tmpPrevEditor.state.doc.line(tmpPrevEditor.state.doc.lines);let tmpTargetCol=Math.min(tmpColumnOffset,tmpLastLine.to-tmpLastLine.from);tmpPrevEditor.focus();tmpPrevEditor.dispatch({selection:{anchor:tmpLastLine.from+tmpTargetCol}});pView._setActiveSegment(tmpPrevInternalIndex);}},true);// <-- capture phase
3131
+ // -- Capture-phase drop listener for image files --
3132
+ // Safari processes native contenteditable drops before CodeMirror's
3133
+ // bubble-phase domEventHandlers fire, which can insert a raw <img>
3134
+ // element into the editor DOM. A capture-phase listener fires first
3135
+ // and lets us preventDefault() before the browser acts.
3136
+ tmpEditorView.contentDOM.addEventListener('drop',function(pEvent){if(pView._dragSourceIndex>=0){return;// segment-reorder drag
3137
+ }if(!pEvent.dataTransfer||!pEvent.dataTransfer.files||pEvent.dataTransfer.files.length<1){return;}let tmpFile=pEvent.dataTransfer.files[0];if(tmpFile.type&&tmpFile.type.startsWith('image/')){pEvent.preventDefault();pEvent.stopPropagation();pView._processImageFile(tmpFile,pSegmentIndex);// Clean up the dragover visual indicator
3138
+ let tmpEditorEl=document.getElementById(`PictMDE-SegmentEditor-${pSegmentIndex}`);if(tmpEditorEl){tmpEditorEl.classList.remove('pict-mde-image-dragover');}}},true);// <-- capture phase
3139
+ };/**
3107
3140
  * Build a CodeMirror extension that visually collapses long data URIs
3108
3141
  * inside markdown image syntax.
3109
3142
  *
@@ -3386,7 +3419,7 @@ pView._renderedViewGeneration++;let tmpGeneration=pView._renderedViewGeneration;
3386
3419
  let tmpContentContainer=tmpContainer.querySelector(`#${tmpRenderedViewID} .pict-content`);if(tmpContentContainer){let tmpContentID='PictMDE-RenderedViewContent';tmpContentContainer.id=tmpContentID;pView._postRenderMermaid(tmpContentID,-1,tmpGeneration);pView._postRenderKaTeX(tmpContentID);}};/**
3387
3420
  * Switch back from rendered view to the editing view: rebuild the
3388
3421
  * full editor UI from the data.
3389
- */pView._restoreEditingView=function _restoreEditingView(){let tmpContainer=pView._getContainerElement();if(!tmpContainer){return;}tmpContainer.classList.remove('pict-mde-rendered-mode');pView._buildEditorUI();};};},{"pict-section-content":50}],73:[function(require,module,exports){module.exports={"DefaultRenderable":"MarkdownEditor-Wrap","DefaultDestinationAddress":"#MarkdownEditor-Container-Div","Templates":[{"Hash":"MarkdownEditor-Container","Template":/*html*/`<div class="pict-mde" id="PictMDE-Container"></div>`},{"Hash":"MarkdownEditor-Segment","Template":/*html*/`<div class="pict-mde-segment" id="PictMDE-Segment-{~D:Record.SegmentIndex~}" data-segment-index="{~D:Record.SegmentIndex~}">
3422
+ */pView._restoreEditingView=function _restoreEditingView(){let tmpContainer=pView._getContainerElement();if(!tmpContainer){return;}tmpContainer.classList.remove('pict-mde-rendered-mode');pView._buildEditorUI();};};},{"pict-section-content":65}],73:[function(require,module,exports){module.exports={"DefaultRenderable":"MarkdownEditor-Wrap","DefaultDestinationAddress":"#MarkdownEditor-Container-Div","Templates":[{"Hash":"MarkdownEditor-Container","Template":/*html*/`<div class="pict-mde" id="PictMDE-Container"></div>`},{"Hash":"MarkdownEditor-Segment","Template":/*html*/`<div class="pict-mde-segment" id="PictMDE-Segment-{~D:Record.SegmentIndex~}" data-segment-index="{~D:Record.SegmentIndex~}">
3390
3423
  <div class="pict-mde-left-controls">
3391
3424
  <div class="pict-mde-quadrant-tl"></div>
3392
3425
  <div class="pict-mde-quadrant-bl"></div>
@@ -3554,8 +3587,9 @@ let tmpContentContainer=tmpContainer.querySelector(`#${tmpRenderedViewID} .pict-
3554
3587
  /* ---- Editor body (middle column) ---- */
3555
3588
  .pict-mde-segment-body
3556
3589
  {
3557
- flex: 1 1 auto;
3590
+ flex: 1 1 0%;
3558
3591
  min-width: 0;
3592
+ overflow: hidden;
3559
3593
  background: #FFFFFF;
3560
3594
  transition: background-color 0.15s ease;
3561
3595
  }
@@ -3613,6 +3647,13 @@ let tmpContentContainer=tmpContainer.querySelector(`#${tmpRenderedViewID} .pict-
3613
3647
  display: block;
3614
3648
  border-top: 1px solid #EDEDED;
3615
3649
  background: #FCFCFC;
3650
+ overflow: hidden;
3651
+ }
3652
+ /* Constrain images in the rich preview even if pict-section-content CSS loads late */
3653
+ .pict-mde-rich-preview img
3654
+ {
3655
+ max-width: 100%;
3656
+ height: auto;
3616
3657
  }
3617
3658
  /* Global preview toggle: hide all previews when container has class */
3618
3659
  .pict-mde.pict-mde-previews-hidden .pict-mde-rich-preview.pict-mde-has-rich-preview,
@@ -3894,6 +3935,182 @@ let tmpContentContainer=tmpContainer.querySelector(`#${tmpRenderedViewID} .pict-
3894
3935
  {
3895
3936
  display: flex;
3896
3937
  }
3938
+
3939
+ /* ============================================
3940
+ RESPONSIVE: Tablet / Phone (max-width: 768px)
3941
+ ============================================ */
3942
+ @media (max-width: 768px)
3943
+ {
3944
+ /* Prevent any horizontal overflow in the editor */
3945
+ .pict-mde
3946
+ {
3947
+ overflow-x: hidden;
3948
+ max-width: 100%;
3949
+ }
3950
+
3951
+ /* Reduce the left controls column width */
3952
+ .pict-mde-left-controls
3953
+ {
3954
+ flex: 0 0 16px;
3955
+ }
3956
+ .pict-mde-left-btn
3957
+ {
3958
+ width: 16px;
3959
+ height: 18px;
3960
+ font-size: 10px;
3961
+ }
3962
+
3963
+ /* Make left-side buttons always visible on touch (no hover) */
3964
+ .pict-mde-left-btn
3965
+ {
3966
+ opacity: 0.6;
3967
+ }
3968
+
3969
+ /* Narrow the drag handle */
3970
+ .pict-mde-drag-handle
3971
+ {
3972
+ flex: 0 0 5px;
3973
+ }
3974
+
3975
+ /* Shrink the right sidebar column */
3976
+ .pict-mde-sidebar
3977
+ {
3978
+ flex: 0 0 24px;
3979
+ }
3980
+ .pict-mde-sidebar-btn
3981
+ {
3982
+ width: 20px;
3983
+ height: 20px;
3984
+ font-size: 11px;
3985
+ }
3986
+
3987
+ /* Make right sidebar buttons always visible (touch devices) */
3988
+ .pict-mde-quadrant-tr,
3989
+ .pict-mde-quadrant-br
3990
+ {
3991
+ opacity: 0.7;
3992
+ }
3993
+ .pict-mde-segment.pict-mde-active .pict-mde-quadrant-tr,
3994
+ .pict-mde-segment.pict-mde-active .pict-mde-quadrant-br
3995
+ {
3996
+ opacity: 1;
3997
+ }
3998
+
3999
+ /* Reduce CodeMirror content padding */
4000
+ .pict-mde-segment-editor .cm-editor .cm-content
4001
+ {
4002
+ padding: 6px 6px;
4003
+ }
4004
+
4005
+ /* Reduce font size slightly for more content on screen */
4006
+ .pict-mde-segment-editor .cm-editor .cm-scroller
4007
+ {
4008
+ font-size: 13px;
4009
+ }
4010
+
4011
+ /* Add segment button: reduce left margin */
4012
+ .pict-mde-add-segment
4013
+ {
4014
+ padding-left: 21px;
4015
+ }
4016
+
4017
+ /* Rich preview: less padding */
4018
+ .pict-mde-rich-preview .pict-content
4019
+ {
4020
+ padding: 8px;
4021
+ font-size: 12px;
4022
+ }
4023
+
4024
+ /* Image previews: smaller max dimensions */
4025
+ .pict-mde-image-preview img
4026
+ {
4027
+ max-width: 120px;
4028
+ max-height: 100px;
4029
+ }
4030
+
4031
+ /* Rendered view: less padding */
4032
+ .pict-mde-rendered-view
4033
+ {
4034
+ padding: 10px 8px;
4035
+ }
4036
+ }
4037
+
4038
+ /* ============================================
4039
+ RESPONSIVE: Small phone (max-width: 480px)
4040
+ ============================================ */
4041
+ @media (max-width: 480px)
4042
+ {
4043
+ /* Wrap segment so left controls flow to the top as a horizontal bar */
4044
+ .pict-mde-segment
4045
+ {
4046
+ flex-wrap: wrap;
4047
+ }
4048
+
4049
+ /* Left controls become a horizontal toolbar at the top of the segment */
4050
+ .pict-mde-left-controls
4051
+ {
4052
+ flex: 0 0 100%;
4053
+ flex-direction: row;
4054
+ justify-content: flex-start;
4055
+ gap: 2px;
4056
+ padding: 3px 4px;
4057
+ order: -1;
4058
+ background: #F5F5F5;
4059
+ border-bottom: 1px solid #EDEDED;
4060
+ }
4061
+ .pict-mde-left-btn
4062
+ {
4063
+ width: 24px;
4064
+ height: 24px;
4065
+ font-size: 12px;
4066
+ opacity: 0.7;
4067
+ }
4068
+
4069
+ /* Left quadrants flow horizontally */
4070
+ .pict-mde-quadrant-tl,
4071
+ .pict-mde-quadrant-bl
4072
+ {
4073
+ flex-direction: row;
4074
+ gap: 2px;
4075
+ position: static;
4076
+ }
4077
+
4078
+ /* Segment body: explicit basis so it fills the wrapped row */
4079
+ .pict-mde-segment-body
4080
+ {
4081
+ flex: 1 1 calc(100% - 20px);
4082
+ }
4083
+
4084
+ /* Hide drag handle on very small screens */
4085
+ .pict-mde-drag-handle
4086
+ {
4087
+ display: none;
4088
+ }
4089
+
4090
+ /* Right sidebar: further shrink */
4091
+ .pict-mde-sidebar
4092
+ {
4093
+ flex: 0 0 20px;
4094
+ }
4095
+ .pict-mde-sidebar-btn
4096
+ {
4097
+ width: 18px;
4098
+ height: 18px;
4099
+ font-size: 10px;
4100
+ }
4101
+
4102
+ /* Add segment: no left offset since controls are at top */
4103
+ .pict-mde-add-segment
4104
+ {
4105
+ padding-left: 0;
4106
+ }
4107
+
4108
+ /* Even tighter CodeMirror padding */
4109
+ .pict-mde-segment-editor .cm-editor .cm-content
4110
+ {
4111
+ padding: 4px 4px;
4112
+ }
4113
+ }
3897
4114
  `};},{}],74:[function(require,module,exports){const libPictViewClass=require('pict-view');const libPictSectionContent=require('pict-section-content');const _DefaultConfiguration=require('./Pict-Section-MarkdownEditor-DefaultConfiguration.js');// Helper modules
3898
4115
  const libFormatting=require('./Pict-MDE-Formatting.js');const libImageHandling=require('./Pict-MDE-ImageHandling.js');const libDragAndReorder=require('./Pict-MDE-DragAndReorder.js');const libRichPreview=require('./Pict-MDE-RichPreview.js');const libCodeMirror=require('./Pict-MDE-CodeMirror.js');class PictSectionMarkdownEditor extends libPictViewClass{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_DefaultConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);this.initialRenderComplete=false;// CodeMirror prototype references (injected by consumer or found on window)
3899
4116
  this._codeMirrorModules=null;// Map of segment index to CodeMirror EditorView instance
@@ -3903,7 +4120,7 @@ this._viewCallIdentifier=false;// Currently active (focused) segment index, or -
3903
4120
  this._activeSegmentIndex=-1;// Drag state for reorder
3904
4121
  this._dragSourceIndex=-1;// Whether controls (line numbers + right sidebar) are currently visible
3905
4122
  this._controlsVisible=true;// Whether rich previews are globally visible
3906
- this._previewsVisible=false;// Set of logical segment indices where preview has been individually hidden
4123
+ this._previewsVisible=true;// Set of logical segment indices where preview has been individually hidden
3907
4124
  this._hiddenPreviewSegments={};// Debounce timers for image preview updates (keyed by segment index)
3908
4125
  this._imagePreviewTimers={};// Debounce timers for rich content preview updates (keyed by segment index)
3909
4126
  this._richPreviewTimers={};// Generation counters for mermaid async rendering (keyed by segment index)
@@ -3923,8 +4140,9 @@ libFormatting.attach(this);libImageHandling.attach(this);libDragAndReorder.attac
3923
4140
  * @param {object} [pCodeMirrorModules] - The CodeMirror modules object
3924
4141
  * @returns {boolean|void}
3925
4142
  */connectCodeMirrorModules(pCodeMirrorModules){if(pCodeMirrorModules&&typeof pCodeMirrorModules==='object'){if(typeof pCodeMirrorModules.EditorView==='function'&&typeof pCodeMirrorModules.EditorState==='function'){this._codeMirrorModules=pCodeMirrorModules;return;}}// Try to find CodeMirror modules in global scope
3926
- if(typeof window!=='undefined'){if(window.CodeMirrorModules&&typeof window.CodeMirrorModules.EditorView==='function'){this.log.trace(`PICT-MarkdownEditor Found CodeMirror modules on window.CodeMirrorModules.`);this._codeMirrorModules=window.CodeMirrorModules;return;}}this.log.error(`PICT-MarkdownEditor No CodeMirror modules found. Provide them via connectCodeMirrorModules() or set window.CodeMirrorModules.`);return false;}onAfterRender(pRenderable){// Inject CSS from all registered views
3927
- this.pict.CSSMap.injectCSS();if(!this.initialRenderComplete){this.onAfterInitialRender();this.initialRenderComplete=true;}return super.onAfterRender(pRenderable);}onAfterInitialRender(){// Resolve CodeMirror modules if not already set
4143
+ if(typeof window!=='undefined'){if(window.CodeMirrorModules&&typeof window.CodeMirrorModules.EditorView==='function'){this.log.trace(`PICT-MarkdownEditor Found CodeMirror modules on window.CodeMirrorModules.`);this._codeMirrorModules=window.CodeMirrorModules;return;}}this.log.error(`PICT-MarkdownEditor No CodeMirror modules found. Provide them via connectCodeMirrorModules() or set window.CodeMirrorModules.`);return false;}onAfterRender(pRenderable){if(!this.initialRenderComplete){this.onAfterInitialRender();this.initialRenderComplete=true;}// Inject CSS from all registered views (after onAfterInitialRender so
4144
+ // that pict-section-content's CSS is registered before injection)
4145
+ this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}onAfterInitialRender(){// Resolve CodeMirror modules if not already set
3928
4146
  if(!this._codeMirrorModules){this.connectCodeMirrorModules();}if(!this._codeMirrorModules){this.log.error(`PICT-MarkdownEditor Cannot initialize; no CodeMirror modules available.`);return false;}// Register pict-section-content's CSS for rich preview rendering.
3929
4147
  // This ensures the rendered markdown (headings, code blocks, tables, etc.)
3930
4148
  // is styled correctly inside the preview area.
@@ -4037,7 +4255,8 @@ let tmpVisible=pVisible;if(typeof pSegmentIndexOrVisible==='boolean'){tmpVisible
4037
4255
  * @param {number} pSegmentIndex - The internal segment index
4038
4256
  * @param {boolean} [pVisible] - If provided, set to this value; otherwise toggle
4039
4257
  */toggleSegmentPreview(pSegmentIndex,pVisible){// Convert internal index to logical index for persistent tracking
4040
- let tmpLogicalIndex=this._getLogicalIndex(pSegmentIndex);if(tmpLogicalIndex<0){return;}let tmpCurrentlyHidden=!!this._hiddenPreviewSegments[tmpLogicalIndex];if(typeof pVisible==='boolean'){tmpCurrentlyHidden=!pVisible;}else{tmpCurrentlyHidden=!tmpCurrentlyHidden;}if(tmpCurrentlyHidden){this._hiddenPreviewSegments[tmpLogicalIndex]=true;}else{delete this._hiddenPreviewSegments[tmpLogicalIndex];}let tmpSegmentEl=document.getElementById(`PictMDE-Segment-${pSegmentIndex}`);if(tmpSegmentEl){if(tmpCurrentlyHidden){tmpSegmentEl.classList.add('pict-mde-preview-hidden');}else{tmpSegmentEl.classList.remove('pict-mde-preview-hidden');}}}// -- Segment Data Management --
4258
+ let tmpLogicalIndex=this._getLogicalIndex(pSegmentIndex);if(tmpLogicalIndex<0){return;}let tmpCurrentlyHidden=!!this._hiddenPreviewSegments[tmpLogicalIndex];if(typeof pVisible==='boolean'){tmpCurrentlyHidden=!pVisible;}else{tmpCurrentlyHidden=!tmpCurrentlyHidden;}if(tmpCurrentlyHidden){this._hiddenPreviewSegments[tmpLogicalIndex]=true;}else{delete this._hiddenPreviewSegments[tmpLogicalIndex];}let tmpSegmentEl=document.getElementById(`PictMDE-Segment-${pSegmentIndex}`);if(tmpSegmentEl){if(tmpCurrentlyHidden){tmpSegmentEl.classList.add('pict-mde-preview-hidden');}else{tmpSegmentEl.classList.remove('pict-mde-preview-hidden');// Render preview content when making it visible
4259
+ this._updateRichPreviews(pSegmentIndex);this._updateImagePreviews(pSegmentIndex);}}}// -- Segment Data Management --
4041
4260
  /**
4042
4261
  * Get the segments array from the configured data address.
4043
4262
  *
@@ -4121,7 +4340,7 @@ this._swapHiddenPreviewState(tmpLogicalIndex,tmpLogicalIndex+1);this._buildEdito
4121
4340
  */marshalFromView(){super.marshalFromView();this._marshalAllEditorsToData();}/**
4122
4341
  * Destroy all editors and clean up.
4123
4342
  */destroy(){for(let tmpIndex in this._segmentEditors){if(this._segmentEditors[tmpIndex]){this._segmentEditors[tmpIndex].destroy();}}this._segmentEditors={};// Clear rich preview debounce timers
4124
- for(let tmpIndex in this._richPreviewTimers){clearTimeout(this._richPreviewTimers[tmpIndex]);}this._richPreviewTimers={};this._richPreviewGenerations={};}}module.exports=PictSectionMarkdownEditor;module.exports.default_configuration=_DefaultConfiguration;},{"./Pict-MDE-CodeMirror.js":68,"./Pict-MDE-DragAndReorder.js":69,"./Pict-MDE-Formatting.js":70,"./Pict-MDE-ImageHandling.js":71,"./Pict-MDE-RichPreview.js":72,"./Pict-Section-MarkdownEditor-DefaultConfiguration.js":73,"pict-section-content":50,"pict-view":76}],75:[function(require,module,exports){module.exports={"name":"pict-view","version":"1.0.67","description":"Pict View Base Class","main":"source/Pict-View.js","scripts":{"test":"npx quack test","tests":"npx quack test -g","start":"node source/Pict-View.js","coverage":"npx quack coverage","build":"npx quack build","docker-dev-build":"docker build ./ -f Dockerfile_LUXURYCode -t pict-view-image:local","docker-dev-run":"docker run -it -d --name pict-view-dev -p 30001:8080 -p 38086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/pict-view\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" pict-view-image:local","docker-dev-shell":"docker exec -it pict-view-dev /bin/bash","types":"tsc -p .","lint":"eslint source/**"},"types":"types/source/Pict-View.d.ts","repository":{"type":"git","url":"git+https://github.com/stevenvelozo/pict-view.git"},"author":"steven velozo <steven@velozo.com>","license":"MIT","bugs":{"url":"https://github.com/stevenvelozo/pict-view/issues"},"homepage":"https://github.com/stevenvelozo/pict-view#readme","devDependencies":{"@eslint/js":"^9.39.1","browser-env":"^3.3.0","eslint":"^9.39.1","pict":"^1.0.348","quackage":"^1.0.58","typescript":"^5.9.3"},"mocha":{"diff":true,"extension":["js"],"package":"./package.json","reporter":"spec","slow":"75","timeout":"5000","ui":"tdd","watch-files":["source/**/*.js","test/**/*.js"],"watch-ignore":["lib/vendor"]},"dependencies":{"fable":"^3.1.63","fable-serviceproviderbase":"^3.0.19"}};},{}],76:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');const libPackage=require('../package.json');const defaultPictViewSettings={DefaultRenderable:false,DefaultDestinationAddress:false,DefaultTemplateRecordAddress:false,ViewIdentifier:false,// If this is set to true, when the App initializes this will.
4343
+ for(let tmpIndex in this._richPreviewTimers){clearTimeout(this._richPreviewTimers[tmpIndex]);}this._richPreviewTimers={};this._richPreviewGenerations={};}}module.exports=PictSectionMarkdownEditor;module.exports.default_configuration=_DefaultConfiguration;},{"./Pict-MDE-CodeMirror.js":68,"./Pict-MDE-DragAndReorder.js":69,"./Pict-MDE-Formatting.js":70,"./Pict-MDE-ImageHandling.js":71,"./Pict-MDE-RichPreview.js":72,"./Pict-Section-MarkdownEditor-DefaultConfiguration.js":73,"pict-section-content":65,"pict-view":76}],75:[function(require,module,exports){module.exports={"name":"pict-view","version":"1.0.67","description":"Pict View Base Class","main":"source/Pict-View.js","scripts":{"test":"npx quack test","tests":"npx quack test -g","start":"node source/Pict-View.js","coverage":"npx quack coverage","build":"npx quack build","docker-dev-build":"docker build ./ -f Dockerfile_LUXURYCode -t pict-view-image:local","docker-dev-run":"docker run -it -d --name pict-view-dev -p 30001:8080 -p 38086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/pict-view\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" pict-view-image:local","docker-dev-shell":"docker exec -it pict-view-dev /bin/bash","types":"tsc -p .","lint":"eslint source/**"},"types":"types/source/Pict-View.d.ts","repository":{"type":"git","url":"git+https://github.com/stevenvelozo/pict-view.git"},"author":"steven velozo <steven@velozo.com>","license":"MIT","bugs":{"url":"https://github.com/stevenvelozo/pict-view/issues"},"homepage":"https://github.com/stevenvelozo/pict-view#readme","devDependencies":{"@eslint/js":"^9.39.1","browser-env":"^3.3.0","eslint":"^9.39.1","pict":"^1.0.348","quackage":"^1.0.58","typescript":"^5.9.3"},"mocha":{"diff":true,"extension":["js"],"package":"./package.json","reporter":"spec","slow":"75","timeout":"5000","ui":"tdd","watch-files":["source/**/*.js","test/**/*.js"],"watch-ignore":["lib/vendor"]},"dependencies":{"fable":"^3.1.63","fable-serviceproviderbase":"^3.0.19"}};},{}],76:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');const libPackage=require('../package.json');const defaultPictViewSettings={DefaultRenderable:false,DefaultDestinationAddress:false,DefaultTemplateRecordAddress:false,ViewIdentifier:false,// If this is set to true, when the App initializes this will.
4125
4344
  // After the App initializes, initialize will be called as soon as it's added.
4126
4345
  AutoInitialize:true,AutoInitializeOrdinal:0,// If this is set to true, when the App autorenders (on load) this will.
4127
4346
  // After the App initializes, render will be called as soon as it's added.
@@ -5795,93 +6014,7 @@ let tmpBrand='Documentation';if(tmpDocuserve.CoverLoaded&&tmpDocuserve.Cover&&tm
5795
6014
  * Sanitize a title string, preserving only <small> tags.
5796
6015
  */sanitizeTitle(pText){if(!pText){return'';}return this.escapeHTML(pText).replace(/&lt;small&gt;/gi,'<small>').replace(/&lt;\/small&gt;/gi,'</small>');}/**
5797
6016
  * Escape HTML special characters.
5798
- */escapeHTML(pText){if(!pText){return'';}return pText.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');}}module.exports=DocuserveTopBarView;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],93:[function(require,module,exports){arguments[4][50][0].apply(exports,arguments);},{"./providers/Pict-Provider-Content.js":94,"./views/Pict-View-Content.js":95,"dup":50}],94:[function(require,module,exports){const libPictProvider=require('pict-provider');const libCreateHighlighter=require('pict-section-code').createHighlighter;/**
5799
- * Content Provider for Pict Section Content
5800
- *
5801
- * A general-purpose markdown-to-HTML parser with support for:
5802
- * - Headings, paragraphs, lists, blockquotes, horizontal rules
5803
- * - Fenced code blocks with language tags (nested fence support)
5804
- * - Syntax highlighting and line numbers for code blocks (via pict-section-code)
5805
- * - Tables (GFM pipe syntax)
5806
- * - Mermaid diagram blocks
5807
- * - KaTeX math (inline and display)
5808
- * - Bold, italic, inline code, links, images
5809
- *
5810
- * Link resolution is customizable via an optional callback.
5811
- */class PictContentProvider extends libPictProvider{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}/**
5812
- * Highlight a code string using pict-section-code's syntax highlighter.
5813
- * Uses a mock element to interface with the highlighter's DOM-based API.
5814
- *
5815
- * @param {string} pCode - The raw code string
5816
- * @param {string} pLanguage - The language identifier (e.g. "javascript", "html")
5817
- * @returns {string} The syntax-highlighted HTML
5818
- */highlightCode(pCode,pLanguage){if(!pCode){return'';}let tmpHighlighter=libCreateHighlighter(pLanguage);// Create a mock element to interface with the highlighter
5819
- let tmpMockElement={textContent:pCode,innerHTML:''};tmpHighlighter(tmpMockElement);return tmpMockElement.innerHTML;}/**
5820
- * Generate line number HTML for a code block.
5821
- *
5822
- * @param {string} pCode - The raw code string
5823
- * @returns {string} HTML string with line number spans
5824
- */generateLineNumbers(pCode){if(!pCode){return'<span>1</span>';}let tmpLineCount=pCode.split('\n').length;let tmpHTML='';for(let i=1;i<=tmpLineCount;i++){tmpHTML+='<span>'+i+'</span>';}return tmpHTML;}/**
5825
- * Parse a markdown string into HTML.
5826
- *
5827
- * @param {string} pMarkdown - The raw markdown text
5828
- * @param {Function} [pLinkResolver] - Optional callback for link resolution: (pHref, pLinkText) => { href, target, rel } or null
5829
- * @param {Function} [pImageResolver] - Optional callback for image URL resolution: (pSrc, pAlt) => resolvedSrc or null
5830
- * @returns {string} The parsed HTML
5831
- */parseMarkdown(pMarkdown,pLinkResolver,pImageResolver){if(!pMarkdown){return'';}let tmpLines=pMarkdown.split('\n');let tmpHTML=[];let tmpInCodeBlock=false;let tmpCodeFenceLength=0;let tmpCodeLang='';let tmpCodeLines=[];let tmpInList=false;let tmpListType='';let tmpInBlockquote=false;let tmpBlockquoteLines=[];let tmpInMathBlock=false;let tmpMathLines=[];let tmpParagraphLines=[];// Helper to flush accumulated paragraph lines into a single <p> tag
5832
- let fFlushParagraph=()=>{if(tmpParagraphLines.length>0){tmpHTML.push('<p>'+tmpParagraphLines.map(pLine=>{return this.parseInline(pLine,pLinkResolver,pImageResolver);}).join(' ')+'</p>');tmpParagraphLines=[];}};for(let i=0;i<tmpLines.length;i++){let tmpLine=tmpLines[i];// Display math blocks ($$...$$) — skip if inside a code block
5833
- if(!tmpInCodeBlock&&tmpLine.trim().match(/^\$\$/)){if(tmpInMathBlock){// End math block
5834
- tmpHTML.push('<div class="pict-content-katex-display">'+tmpMathLines.join('\n')+'</div>');tmpInMathBlock=false;tmpMathLines=[];}else{// Flush any pending paragraph
5835
- fFlushParagraph();// Close any open list or blockquote
5836
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver,pImageResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}tmpInMathBlock=true;}continue;}if(tmpInMathBlock){tmpMathLines.push(tmpLine);continue;}// Code blocks (fenced) — track fence length so ````x```` nests around ```y```
5837
- let tmpFenceMatch=tmpLine.match(/^(`{3,})/);if(tmpFenceMatch){let tmpFenceLen=tmpFenceMatch[1].length;if(tmpInCodeBlock){// Only close if the closing fence is at least as long as the opening
5838
- if(tmpFenceLen>=tmpCodeFenceLength&&tmpLine.trim()===tmpFenceMatch[1]){// End code block
5839
- if(tmpCodeLang==='mermaid'){// Mermaid diagrams: output raw content for client-side rendering
5840
- tmpHTML.push('<pre class="mermaid">'+tmpCodeLines.join('\n')+'</pre>');}else{let tmpCodeText=tmpCodeLines.join('\n');let tmpHighlightedCode=this.highlightCode(tmpCodeText,tmpCodeLang);let tmpLineNumbersHTML=this.generateLineNumbers(tmpCodeText);tmpHTML.push('<div class="pict-content-code-wrap"><div class="pict-content-code-line-numbers">'+tmpLineNumbersHTML+'</div><pre><code class="language-'+this.escapeHTML(tmpCodeLang)+'">'+tmpHighlightedCode+'</code></pre></div>');}tmpInCodeBlock=false;tmpCodeFenceLength=0;tmpCodeLang='';tmpCodeLines=[];continue;}else{// Inner fence with fewer backticks — treat as content
5841
- tmpCodeLines.push(tmpLine);continue;}}else{// Flush any pending paragraph
5842
- fFlushParagraph();// Close any open list or blockquote
5843
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver,pImageResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}// Start code block — record fence length
5844
- tmpCodeFenceLength=tmpFenceLen;tmpCodeLang=tmpLine.replace(/^`{3,}/,'').trim();tmpInCodeBlock=true;continue;}}if(tmpInCodeBlock){tmpCodeLines.push(tmpLine);continue;}// Blockquotes
5845
- if(tmpLine.match(/^>\s?/)){if(!tmpInBlockquote){// Flush any pending paragraph
5846
- fFlushParagraph();// Close any open list
5847
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}tmpInBlockquote=true;tmpBlockquoteLines=[];}tmpBlockquoteLines.push(tmpLine.replace(/^>\s?/,''));continue;}else if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver,pImageResolver)+'</blockquote>');tmpInBlockquote=false;tmpBlockquoteLines=[];}// Horizontal rule
5848
- if(tmpLine.match(/^(-{3,}|\*{3,}|_{3,})\s*$/)){fFlushParagraph();if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}tmpHTML.push('<hr>');continue;}// Headings
5849
- let tmpHeadingMatch=tmpLine.match(/^(#{1,6})\s+(.+)/);if(tmpHeadingMatch){fFlushParagraph();if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}let tmpLevel=tmpHeadingMatch[1].length;let tmpText=this.parseInline(tmpHeadingMatch[2],pLinkResolver,pImageResolver);let tmpID=tmpHeadingMatch[2].toLowerCase().replace(/[^\w\s-]/g,'').replace(/\s+/g,'-');tmpHTML.push('<h'+tmpLevel+' id="'+tmpID+'">'+tmpText+'</h'+tmpLevel+'>');continue;}// Unordered list items
5850
- let tmpULMatch=tmpLine.match(/^(\s*)[-*+]\s+(.*)/);if(tmpULMatch){fFlushParagraph();if(!tmpInList||tmpListType!=='ul'){if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}tmpHTML.push('<ul>');tmpInList=true;tmpListType='ul';}tmpHTML.push('<li>'+this.parseInline(tmpULMatch[2],pLinkResolver,pImageResolver)+'</li>');continue;}// Ordered list items
5851
- let tmpOLMatch=tmpLine.match(/^(\s*)\d+\.\s+(.*)/);if(tmpOLMatch){fFlushParagraph();if(!tmpInList||tmpListType!=='ol'){if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}tmpHTML.push('<ol>');tmpInList=true;tmpListType='ol';}tmpHTML.push('<li>'+this.parseInline(tmpOLMatch[2],pLinkResolver,pImageResolver)+'</li>');continue;}// Close list if we've left list items
5852
- if(tmpInList&&tmpLine.trim()!==''){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}// Empty line — flush any accumulated paragraph
5853
- if(tmpLine.trim()===''){fFlushParagraph();continue;}// Table detection
5854
- if(tmpLine.match(/^\|/)&&i+1<tmpLines.length&&tmpLines[i+1].match(/^\|[\s-:|]+\|/)){fFlushParagraph();// Close any open list
5855
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');tmpInList=false;}let tmpTableHTML='<table>';// Header row
5856
- let tmpHeaders=tmpLine.split('|').filter(pCell=>{return pCell.trim()!=='';});tmpTableHTML+='<thead><tr>';for(let h=0;h<tmpHeaders.length;h++){tmpTableHTML+='<th>'+this.parseInline(tmpHeaders[h].trim(),pLinkResolver,pImageResolver)+'</th>';}tmpTableHTML+='</tr></thead>';// Skip separator row
5857
- i++;// Body rows
5858
- tmpTableHTML+='<tbody>';while(i+1<tmpLines.length&&tmpLines[i+1].match(/^\|/)){i++;let tmpCells=tmpLines[i].split('|').filter(pCell=>{return pCell.trim()!=='';});tmpTableHTML+='<tr>';for(let c=0;c<tmpCells.length;c++){tmpTableHTML+='<td>'+this.parseInline(tmpCells[c].trim(),pLinkResolver,pImageResolver)+'</td>';}tmpTableHTML+='</tr>';}tmpTableHTML+='</tbody></table>';tmpHTML.push(tmpTableHTML);continue;}// Accumulate paragraph lines — consecutive non-blank text lines
5859
- // will be joined into a single <p> tag when flushed
5860
- tmpParagraphLines.push(tmpLine);}// Flush any remaining accumulated paragraph
5861
- fFlushParagraph();// Close any trailing open elements
5862
- if(tmpInList){tmpHTML.push(tmpListType==='ul'?'</ul>':'</ol>');}if(tmpInBlockquote){tmpHTML.push('<blockquote>'+this.parseMarkdown(tmpBlockquoteLines.join('\n'),pLinkResolver,pImageResolver)+'</blockquote>');}if(tmpInCodeBlock){let tmpCodeText=tmpCodeLines.join('\n');let tmpHighlightedCode=this.highlightCode(tmpCodeText,tmpCodeLang);let tmpLineNumbersHTML=this.generateLineNumbers(tmpCodeText);tmpHTML.push('<div class="pict-content-code-wrap"><div class="pict-content-code-line-numbers">'+tmpLineNumbersHTML+'</div><pre><code>'+tmpHighlightedCode+'</code></pre></div>');}return tmpHTML.join('\n');}/**
5863
- * Parse inline markdown elements (bold, italic, code, links, images, KaTeX).
5864
- *
5865
- * @param {string} pText - The text to parse
5866
- * @param {Function} [pLinkResolver] - Optional callback: (pHref, pLinkText) => { href, target, rel } or null
5867
- * @param {Function} [pImageResolver] - Optional callback: (pSrc, pAlt) => resolvedSrc or null
5868
- * @returns {string} HTML with inline elements
5869
- */parseInline(pText,pLinkResolver,pImageResolver){if(!pText){return'';}let tmpResult=pText;// Extract inline code spans into placeholders so bold/italic regexes don't mangle their contents
5870
- let tmpCodeSpans=[];tmpResult=tmpResult.replace(/`([^`]+)`/g,(pMatch,pCode)=>{let tmpIndex=tmpCodeSpans.length;tmpCodeSpans.push('<code>'+pCode+'</code>');return'\x00CODEINLINE'+tmpIndex+'\x00';});// Inline LaTeX equations ($...$) — must be processed before other inline patterns
5871
- // Match single $ delimiters that aren't adjacent to spaces (to avoid false positives with currency)
5872
- tmpResult=tmpResult.replace(/\$([^\$\s][^\$]*?[^\$\s])\$/g,'<span class="pict-content-katex-inline">$1</span>');// Also match single-character inline math like $x$
5873
- tmpResult=tmpResult.replace(/\$([^\$\s])\$/g,'<span class="pict-content-katex-inline">$1</span>');// Images
5874
- tmpResult=tmpResult.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(pMatch,pAlt,pSrc)=>{let tmpSrc=pSrc;if(typeof pImageResolver==='function'){let tmpResolved=pImageResolver(pSrc,pAlt);if(tmpResolved){tmpSrc=tmpResolved;}}return'<img src="'+tmpSrc+'" alt="'+pAlt+'">';});// Links
5875
- tmpResult=tmpResult.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(pMatch,pLinkText,pHref)=>{if(typeof pLinkResolver==='function'){let tmpResolved=pLinkResolver(pHref,pLinkText);if(tmpResolved){let tmpTarget=tmpResolved.target?' target="'+tmpResolved.target+'"':'';let tmpRel=tmpResolved.rel?' rel="'+tmpResolved.rel+'"':'';return'<a href="'+tmpResolved.href+'"'+tmpTarget+tmpRel+'>'+pLinkText+'</a>';}}// Default behavior: external links open in new tab
5876
- if(pHref.match(/^https?:\/\//)){return'<a href="'+pHref+'" target="_blank" rel="noopener">'+pLinkText+'</a>';}return'<a href="'+pHref+'">'+pLinkText+'</a>';});// Bold
5877
- tmpResult=tmpResult.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');tmpResult=tmpResult.replace(/__([^_]+)__/g,'<strong>$1</strong>');// Italic
5878
- tmpResult=tmpResult.replace(/\*([^*]+)\*/g,'<em>$1</em>');tmpResult=tmpResult.replace(/_([^_]+)_/g,'<em>$1</em>');// Restore inline code spans from placeholders
5879
- tmpResult=tmpResult.replace(/\x00CODEINLINE(\d+)\x00/g,(pMatch,pIndex)=>{return tmpCodeSpans[parseInt(pIndex)];});return tmpResult;}/**
5880
- * Escape HTML special characters.
5881
- *
5882
- * @param {string} pText - The text to escape
5883
- * @returns {string} The escaped text
5884
- */escapeHTML(pText){if(!pText){return'';}return pText.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');}}module.exports=PictContentProvider;module.exports.default_configuration={ProviderIdentifier:"Pict-Content",AutoInitialize:true,AutoInitializeOrdinal:0};},{"pict-provider":46,"pict-section-code":49}],95:[function(require,module,exports){arguments[4][52][0].apply(exports,arguments);},{"dup":52,"pict-view":76}],96:[function(require,module,exports){module.exports={"Name":"Retold Content Editor","Hash":"ContentEditor","MainViewportViewIdentifier":"ContentEditor-Layout","AutoSolveAfterInitialize":true,"AutoRenderMainViewportViewAfterInitialize":false,"AutoRenderViewsAfterInitialize":false,"pict_configuration":{"Product":"ContentEditor-Pict-Application"}};},{}],97:[function(require,module,exports){const libPictApplication=require('pict-application');// File browser
6017
+ */escapeHTML(pText){if(!pText){return'';}return pText.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');}}module.exports=DocuserveTopBarView;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],93:[function(require,module,exports){arguments[4][65][0].apply(exports,arguments);},{"./providers/Pict-Provider-Content.js":94,"./views/Pict-View-Content.js":95,"dup":65}],94:[function(require,module,exports){arguments[4][66][0].apply(exports,arguments);},{"dup":66,"pict-provider":46,"pict-section-code":49}],95:[function(require,module,exports){arguments[4][67][0].apply(exports,arguments);},{"dup":67,"pict-view":76}],96:[function(require,module,exports){module.exports={"Name":"Retold Content Editor","Hash":"ContentEditor","MainViewportViewIdentifier":"ContentEditor-Layout","AutoSolveAfterInitialize":true,"AutoRenderMainViewportViewAfterInitialize":false,"AutoRenderViewsAfterInitialize":false,"pict_configuration":{"Product":"ContentEditor-Pict-Application"}};},{}],97:[function(require,module,exports){const libPictApplication=require('pict-application');// File browser
5885
6018
  const libPictSectionFileBrowser=require('pict-section-filebrowser');// Provider
5886
6019
  const libContentEditorProvider=require('./providers/Pict-Provider-ContentEditor.js');// Views
5887
6020
  const libViewLayout=require('./views/PictView-Editor-Layout.js');const libViewTopBar=require('./views/PictView-Editor-TopBar.js');const libViewMarkdownEditor=require('./views/PictView-Editor-MarkdownEditor.js');const libViewCodeEditor=require('./views/PictView-Editor-CodeEditor.js');const libViewSettingsPanel=require('./views/PictView-Editor-SettingsPanel.js');const libViewMarkdownReference=require('./views/PictView-Editor-MarkdownReference.js');const libViewTopics=require('./views/PictView-Editor-Topics.js');/**
@@ -5893,13 +6026,37 @@ const libViewLayout=require('./views/PictView-Editor-Layout.js');const libViewTo
5893
6026
  */class ContentEditorApplication extends libPictApplication{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);// Register the content editor provider
5894
6027
  this.pict.addProvider('ContentEditor-Provider',libContentEditorProvider.default_configuration,libContentEditorProvider);// Register views
5895
6028
  this.pict.addView('ContentEditor-Layout',libViewLayout.default_configuration,libViewLayout);this.pict.addView('ContentEditor-TopBar',libViewTopBar.default_configuration,libViewTopBar);this.pict.addView('ContentEditor-MarkdownEditor',libViewMarkdownEditor.default_configuration,libViewMarkdownEditor);this.pict.addView('ContentEditor-CodeEditor',libViewCodeEditor.default_configuration,libViewCodeEditor);this.pict.addView('ContentEditor-SettingsPanel',libViewSettingsPanel.default_configuration,libViewSettingsPanel);this.pict.addView('ContentEditor-MarkdownReference',libViewMarkdownReference.default_configuration,libViewMarkdownReference);this.pict.addView('ContentEditor-Topics',libViewTopics.default_configuration,libViewTopics);// Register the file browser -- override destination and layout for sidebar use
5896
- let tmpFileBrowserConfig=JSON.parse(JSON.stringify(libPictSectionFileBrowser.default_configuration));tmpFileBrowserConfig.DefaultDestinationAddress='#ContentEditor-Sidebar-Container';tmpFileBrowserConfig.DefaultState.Layout='list-only';this.pict.addView('Pict-FileBrowser',tmpFileBrowserConfig,libPictSectionFileBrowser);// Register the list detail sub-view for the file list pane
5897
- this.pict.addView('Pict-FileBrowser-ListDetail',libPictSectionFileBrowser.PictViewListDetail.default_configuration,libPictSectionFileBrowser.PictViewListDetail);}onAfterInitializeAsync(fCallback){// Expose the pict instance as window.pict for inline onclick handlers
6029
+ let tmpFileBrowserConfig=JSON.parse(JSON.stringify(libPictSectionFileBrowser.default_configuration));tmpFileBrowserConfig.DefaultDestinationAddress='#ContentEditor-Sidebar-Container';tmpFileBrowserConfig.DefaultState.Layout='list-only';this.pict.addView('Pict-FileBrowser',tmpFileBrowserConfig,libPictSectionFileBrowser);// Register the list detail sub-view for the file list pane.
6030
+ // Override templates to:
6031
+ // - Add a "create folder" button to the breadcrumb bar
6032
+ // - Add a hover-visible "+" insert button on each file row
6033
+ let tmpListDetailConfig=JSON.parse(JSON.stringify(libPictSectionFileBrowser.PictViewListDetail.default_configuration));for(let i=0;i<tmpListDetailConfig.Templates.length;i++){if(tmpListDetailConfig.Templates[i].Hash==='FileBrowser-ListDetail-Container-Template'){tmpListDetailConfig.Templates[i].Template=/*html*/`
6034
+ <div class="pict-fb-detail" id="Pict-FileBrowser-DetailList">
6035
+ <div class="pict-fb-breadcrumb-bar">
6036
+ <div class="pict-fb-breadcrumb" id="Pict-FileBrowser-Breadcrumb"></div>
6037
+ <button class="pict-fb-breadcrumb-addfolder" onclick="pict.PictApplication.promptNewFolder()" title="New folder">+</button>
6038
+ </div>
6039
+ <div class="pict-fb-detail-header">
6040
+ <div class="pict-fb-detail-header-cell pict-fb-detail-col-name" onclick="pict.views['{~D:Record.ViewHash~}'].sortBy('Name')">Name</div>
6041
+ <div class="pict-fb-detail-header-cell pict-fb-detail-col-size" onclick="pict.views['{~D:Record.ViewHash~}'].sortBy('Size')">Size</div>
6042
+ <div class="pict-fb-detail-header-cell pict-fb-detail-col-modified" onclick="pict.views['{~D:Record.ViewHash~}'].sortBy('Modified')">Modified</div>
6043
+ </div>
6044
+ <div id="Pict-FileBrowser-DetailRows"></div>
6045
+ </div>
6046
+ `;}if(tmpListDetailConfig.Templates[i].Hash==='FileBrowser-ListDetail-Row-Template'){tmpListDetailConfig.Templates[i].Template=/*html*/`
6047
+ <div class="pict-fb-detail-row{~D:Record.SelectedClass~}" data-index="{~D:Record.Index~}" data-name="{~D:Record.Name~}" onclick="{~D:Record.ClickHandler~}" ondblclick="{~D:Record.DblClickHandler~}">
6048
+ <span class="pict-fb-detail-icon">{~D:Record.Icon~}</span>
6049
+ <span class="pict-fb-detail-name">{~D:Record.Name~}</span>
6050
+ <span class="pict-fb-detail-size">{~D:Record.SizeFormatted~}</span>
6051
+ <span class="pict-fb-detail-modified">{~D:Record.ModifiedFormatted~}</span>
6052
+ <button class="pict-fb-insert-btn" onclick="event.stopPropagation(); pict.PictApplication.insertFileReference(this.parentElement.getAttribute('data-name'))" title="Insert into editor">+</button>
6053
+ </div>
6054
+ `;}}this.pict.addView('Pict-FileBrowser-ListDetail',tmpListDetailConfig,libPictSectionFileBrowser.PictViewListDetail);}onAfterInitializeAsync(fCallback){// Expose the pict instance as window.pict for inline onclick handlers
5898
6055
  // (pict-section-filebrowser templates reference pict.views[...])
5899
6056
  if(typeof window!=='undefined'){window.pict=this.pict;}// Initialize application state
5900
6057
  this.pict.AppData.ContentEditor={CurrentFile:'',ActiveEditor:'markdown',// 'markdown' or 'code'
5901
6058
  IsDirty:false,IsSaving:false,IsLoading:false,Files:[],Document:{Segments:[{Content:''}]},CodeContent:'',SaveStatus:'',SaveStatusClass:'',// Settings
5902
- AutoSegmentMarkdown:false,AutoSegmentDepth:1,AutoContentPreview:false,MarkdownEditingControls:true,MarkdownWordWrap:true,CodeWordWrap:false,SidebarCollapsed:false,SidebarWidth:250,AutoPreviewImages:true,AutoPreviewVideo:false,AutoPreviewAudio:false,ShowHiddenFiles:false,TopicsFilePath:'.pict_documentation_topics.json'};// Restore persisted settings from localStorage
6059
+ AutoSegmentMarkdown:false,AutoSegmentDepth:1,AutoContentPreview:true,MarkdownEditingControls:true,MarkdownWordWrap:true,CodeWordWrap:false,SidebarCollapsed:false,SidebarWidth:250,AutoPreviewImages:true,AutoPreviewVideo:false,AutoPreviewAudio:false,ShowHiddenFiles:false,TopicsFilePath:'.pict_documentation_topics.json'};// Restore persisted settings from localStorage
5903
6060
  this._loadSettings();// Render the layout shell
5904
6061
  this.pict.views['ContentEditor-Layout'].render();// Wire up file selection from the file browser
5905
6062
  let tmpSelf=this;let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(tmpListProvider){let tmpOriginalSelectFile=tmpListProvider.selectFile.bind(tmpListProvider);tmpListProvider.selectFile=function(pFileEntry){tmpOriginalSelectFile(pFileEntry);if(pFileEntry&&pFileEntry.Type==='file'){tmpSelf.navigateToFile(pFileEntry.Path);}};}// Wire up folder navigation to fetch subfolder contents from the server
@@ -6020,10 +6177,15 @@ this._cleanupEditors();// Re-render top bar
6020
6177
  this.pict.views['ContentEditor-TopBar'].render();// Binary files: show preview card without loading content
6021
6178
  if(tmpEditorType==='binary'){this._showBinaryPreview(pFilePath);this.updateStats();return;}// Text files: load content from the server
6022
6179
  let tmpProvider=this.pict.providers['ContentEditor-Provider'];this.pict.AppData.ContentEditor.IsLoading=true;tmpProvider.loadFile(pFilePath,(pError,pContent)=>{tmpSelf.pict.AppData.ContentEditor.IsLoading=false;if(pError){tmpSelf.pict.AppData.ContentEditor.SaveStatus='Error loading file: '+pError;tmpSelf.pict.AppData.ContentEditor.SaveStatusClass='content-editor-status-error';tmpSelf.pict.views['ContentEditor-TopBar'].render();return;}if(tmpEditorType==='markdown'){// Markdown editor: uses segments (auto-segment if enabled)
6023
- tmpSelf.pict.AppData.ContentEditor.Document.Segments=tmpSelf.segmentMarkdownContent(pContent);let tmpEditorView=tmpSelf.pict.views['ContentEditor-MarkdownEditor'];if(tmpEditorView){tmpEditorView.render();tmpEditorView.marshalToView();// Apply the Auto Content Preview setting via the
6024
- // library's own toggle so the per-segment button
6025
- // continues to work.
6026
- tmpEditorView.togglePreview(tmpSelf.pict.AppData.ContentEditor.AutoContentPreview);// Apply the Editing Controls setting (line numbers
6180
+ tmpSelf.pict.AppData.ContentEditor.Document.Segments=tmpSelf.segmentMarkdownContent(pContent);let tmpEditorView=tmpSelf.pict.views['ContentEditor-MarkdownEditor'];if(tmpEditorView){// Set the image base URL so relative image references
6181
+ // resolve through the /content/ static route.
6182
+ let tmpImageBase='/content/';let tmpLastSlash=pFilePath.lastIndexOf('/');if(tmpLastSlash>0){tmpImageBase='/content/'+pFilePath.substring(0,tmpLastSlash)+'/';}tmpEditorView.options.ImageBaseURL=tmpImageBase;tmpEditorView.render();tmpEditorView.marshalToView();// Always ensure the global preview class is clear so
6183
+ // per-segment toggles work.
6184
+ tmpEditorView.togglePreview(true);// Set per-segment preview visibility based on the
6185
+ // Auto Content Preview setting. We must always loop
6186
+ // to clear any stale _hiddenPreviewSegments state
6187
+ // from previous file loads.
6188
+ let tmpShowPreviews=!!tmpSelf.pict.AppData.ContentEditor.AutoContentPreview;for(let tmpIdx in tmpEditorView._segmentEditors){tmpEditorView.toggleSegmentPreview(parseInt(tmpIdx,10),tmpShowPreviews);}// Apply the Editing Controls setting (line numbers
6027
6189
  // and right sidebar) via the library's toggleControls.
6028
6190
  tmpEditorView.toggleControls(tmpSelf.pict.AppData.ContentEditor.MarkdownEditingControls);}tmpSelf.updateStats();}else{// Code editor: uses CodeContent
6029
6191
  tmpSelf.pict.AppData.ContentEditor.CodeContent=pContent;// Detect language from file extension
@@ -6065,6 +6227,24 @@ let tmpDefaultContent='';if(pFilePath.endsWith('.md')){tmpDefaultContent='# '+pF
6065
6227
  tmpSelf.loadFileList(null,()=>{tmpSelf.navigateToFile(pFilePath);});}});}/**
6066
6228
  * Prompt the user for a new file name and create it.
6067
6229
  */promptNewFile(){let tmpFileName=prompt('Enter a name for the new file (e.g. my-page.md, script.js, style.css):');if(tmpFileName&&tmpFileName.trim()){this.createNewFile(tmpFileName.trim());}}/**
6230
+ * Prompt the user for a folder name and create it in the current
6231
+ * browse location via the server API.
6232
+ */promptNewFolder(){let tmpFolderName=prompt('Enter a name for the new folder:');if(!tmpFolderName||!tmpFolderName.trim()){return;}tmpFolderName=tmpFolderName.trim();// Build the full relative path inside the current browse location
6233
+ let tmpCurrentLocation='';if(this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation){tmpCurrentLocation=this.pict.AppData.PictFileBrowser.CurrentLocation;}let tmpPath=tmpCurrentLocation?tmpCurrentLocation+'/'+tmpFolderName:tmpFolderName;let tmpSelf=this;fetch('/api/content/mkdir',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({Path:tmpPath})}).then(pResponse=>pResponse.json()).then(pData=>{if(pData&&pData.Success){tmpSelf.log.info(`Folder created: ${tmpPath}`);// Refresh the file list to show the new folder
6234
+ tmpSelf.loadFileList();}else{alert('Could not create folder: '+(pData?pData.Error:'Unknown error'));}}).catch(pError=>{alert('Error creating folder: '+pError.message);});}/**
6235
+ * Insert a file reference into the active markdown editor segment.
6236
+ *
6237
+ * Called from the "+" button on file browser rows. For image files
6238
+ * this inserts markdown image syntax; for other files a markdown link.
6239
+ *
6240
+ * @param {string} pFilename - The filename to insert
6241
+ */insertFileReference(pFilename){if(!pFilename){return;}let tmpEditorView=this.pict.views['ContentEditor-MarkdownEditor'];if(!tmpEditorView||this.pict.AppData.ContentEditor.ActiveEditor!=='markdown'){return;}// Determine the active segment (the one with focus, or the last one)
6242
+ let tmpSegmentIndex=tmpEditorView._activeSegmentIndex;if(tmpSegmentIndex<0){// Fall back to the first segment
6243
+ let tmpIndices=tmpEditorView._getOrderedSegmentIndices();if(tmpIndices.length>0){tmpSegmentIndex=tmpIndices[0];}}if(tmpSegmentIndex<0){return;}// Build alt text from the filename (strip extension and timestamp prefix)
6244
+ let tmpAltText=pFilename.replace(/\.[^.]+$/,'');tmpAltText=tmpAltText.replace(/^\d{10,}-/,'');tmpAltText=tmpAltText.replace(/[-_]+/g,' ').trim()||'image';// Check if this is an image file
6245
+ let tmpExt=pFilename.substring(pFilename.lastIndexOf('.')).toLowerCase();let tmpImageExts=['.png','.jpg','.jpeg','.gif','.webp','.svg','.bmp','.avif','.apng','.ico','.tiff','.tif','.jfif'];if(tmpImageExts.indexOf(tmpExt)>=0){// Insert markdown image syntax — relative filename resolved by ImageBaseURL
6246
+ tmpEditorView._insertImageMarkdown(tmpSegmentIndex,pFilename,tmpAltText);}else{// Insert a markdown link for non-image files
6247
+ let tmpEditor=tmpEditorView._segmentEditors[tmpSegmentIndex];if(tmpEditor){let tmpInsert='['+tmpAltText+']('+pFilename+')';let tmpCursorPos=tmpEditor.state.selection.main.head;tmpEditor.dispatch({changes:{from:tmpCursorPos,insert:tmpInsert},selection:{anchor:tmpCursorPos+tmpInsert.length}});tmpEditor.focus();}}}/**
6068
6248
  * Handle F4 / Cmd+Shift+T: context-aware topic creation.
6069
6249
  *
6070
6250
  * If the markdown editor is active and has focus, creates a new
@@ -6106,7 +6286,7 @@ tmpStatsEl.textContent='';return;}tmpStatsEl.textContent=tmpLines+' lines \u00B7
6106
6286
  */saveSettings(){if(typeof window==='undefined'||!window.localStorage){return;}let tmpSettings=this.pict.AppData.ContentEditor;let tmpData={AutoSegmentMarkdown:tmpSettings.AutoSegmentMarkdown,AutoSegmentDepth:tmpSettings.AutoSegmentDepth,AutoContentPreview:tmpSettings.AutoContentPreview,MarkdownEditingControls:tmpSettings.MarkdownEditingControls,MarkdownWordWrap:tmpSettings.MarkdownWordWrap,CodeWordWrap:tmpSettings.CodeWordWrap,SidebarCollapsed:tmpSettings.SidebarCollapsed,SidebarWidth:tmpSettings.SidebarWidth,AutoPreviewImages:tmpSettings.AutoPreviewImages,AutoPreviewVideo:tmpSettings.AutoPreviewVideo,AutoPreviewAudio:tmpSettings.AutoPreviewAudio,ShowHiddenFiles:tmpSettings.ShowHiddenFiles,TopicsFilePath:tmpSettings.TopicsFilePath};try{window.localStorage.setItem(this._settingsKey,JSON.stringify(tmpData));}catch(pError){this.log.warn('Failed to save settings: '+pError.message);}}/**
6107
6287
  * Load editor settings from localStorage, overwriting the
6108
6288
  * current defaults for any keys that are present.
6109
- */_loadSettings(){if(typeof window==='undefined'||!window.localStorage){return;}try{let tmpRaw=window.localStorage.getItem(this._settingsKey);if(!tmpRaw){return;}let tmpStored=JSON.parse(tmpRaw);let tmpSettings=this.pict.AppData.ContentEditor;if(typeof tmpStored.AutoSegmentMarkdown==='boolean'){tmpSettings.AutoSegmentMarkdown=tmpStored.AutoSegmentMarkdown;}if(typeof tmpStored.AutoSegmentDepth==='number'){tmpSettings.AutoSegmentDepth=tmpStored.AutoSegmentDepth;}if(typeof tmpStored.AutoContentPreview==='boolean'){tmpSettings.AutoContentPreview=tmpStored.AutoContentPreview;}if(typeof tmpStored.MarkdownEditingControls==='boolean'){tmpSettings.MarkdownEditingControls=tmpStored.MarkdownEditingControls;}if(typeof tmpStored.MarkdownWordWrap==='boolean'){tmpSettings.MarkdownWordWrap=tmpStored.MarkdownWordWrap;}if(typeof tmpStored.CodeWordWrap==='boolean'){tmpSettings.CodeWordWrap=tmpStored.CodeWordWrap;}if(typeof tmpStored.SidebarCollapsed==='boolean'){tmpSettings.SidebarCollapsed=tmpStored.SidebarCollapsed;}if(typeof tmpStored.SidebarWidth==='number'){tmpSettings.SidebarWidth=tmpStored.SidebarWidth;}if(typeof tmpStored.AutoPreviewImages==='boolean'){tmpSettings.AutoPreviewImages=tmpStored.AutoPreviewImages;}if(typeof tmpStored.AutoPreviewVideo==='boolean'){tmpSettings.AutoPreviewVideo=tmpStored.AutoPreviewVideo;}if(typeof tmpStored.AutoPreviewAudio==='boolean'){tmpSettings.AutoPreviewAudio=tmpStored.AutoPreviewAudio;}if(typeof tmpStored.ShowHiddenFiles==='boolean'){tmpSettings.ShowHiddenFiles=tmpStored.ShowHiddenFiles;}if(typeof tmpStored.TopicsFilePath==='string'){tmpSettings.TopicsFilePath=tmpStored.TopicsFilePath;}}catch(pError){this.log.warn('Failed to load settings: '+pError.message);}}}module.exports=ContentEditorApplication;module.exports.default_configuration=require('./Pict-Application-ContentEditor-Configuration.json');},{"./Pict-Application-ContentEditor-Configuration.json":96,"./providers/Pict-Provider-ContentEditor.js":101,"./views/PictView-Editor-CodeEditor.js":102,"./views/PictView-Editor-Layout.js":103,"./views/PictView-Editor-MarkdownEditor.js":104,"./views/PictView-Editor-MarkdownReference.js":105,"./views/PictView-Editor-SettingsPanel.js":106,"./views/PictView-Editor-TopBar.js":107,"./views/PictView-Editor-Topics.js":108,"pict-application":44,"pict-section-filebrowser":54}],98:[function(require,module,exports){module.exports={"Name":"Retold Content Reader","Hash":"ContentReader","MainViewportViewIdentifier":"Docuserve-Layout","AutoSolveAfterInitialize":true,"AutoRenderMainViewportViewAfterInitialize":false,"AutoRenderViewsAfterInitialize":false,"pict_configuration":{"Product":"ContentReader-Pict-Application"}};},{}],99:[function(require,module,exports){const libDocuserveApplication=require('pict-docuserve');/**
6289
+ */_loadSettings(){if(typeof window==='undefined'||!window.localStorage){return;}try{let tmpRaw=window.localStorage.getItem(this._settingsKey);if(!tmpRaw){return;}let tmpStored=JSON.parse(tmpRaw);let tmpSettings=this.pict.AppData.ContentEditor;if(typeof tmpStored.AutoSegmentMarkdown==='boolean'){tmpSettings.AutoSegmentMarkdown=tmpStored.AutoSegmentMarkdown;}if(typeof tmpStored.AutoSegmentDepth==='number'){tmpSettings.AutoSegmentDepth=tmpStored.AutoSegmentDepth;}if(typeof tmpStored.AutoContentPreview==='boolean'){tmpSettings.AutoContentPreview=tmpStored.AutoContentPreview;}if(typeof tmpStored.MarkdownEditingControls==='boolean'){tmpSettings.MarkdownEditingControls=tmpStored.MarkdownEditingControls;}if(typeof tmpStored.MarkdownWordWrap==='boolean'){tmpSettings.MarkdownWordWrap=tmpStored.MarkdownWordWrap;}if(typeof tmpStored.CodeWordWrap==='boolean'){tmpSettings.CodeWordWrap=tmpStored.CodeWordWrap;}if(typeof tmpStored.SidebarCollapsed==='boolean'){tmpSettings.SidebarCollapsed=tmpStored.SidebarCollapsed;}if(typeof tmpStored.SidebarWidth==='number'){tmpSettings.SidebarWidth=tmpStored.SidebarWidth;}if(typeof tmpStored.AutoPreviewImages==='boolean'){tmpSettings.AutoPreviewImages=tmpStored.AutoPreviewImages;}if(typeof tmpStored.AutoPreviewVideo==='boolean'){tmpSettings.AutoPreviewVideo=tmpStored.AutoPreviewVideo;}if(typeof tmpStored.AutoPreviewAudio==='boolean'){tmpSettings.AutoPreviewAudio=tmpStored.AutoPreviewAudio;}if(typeof tmpStored.ShowHiddenFiles==='boolean'){tmpSettings.ShowHiddenFiles=tmpStored.ShowHiddenFiles;}if(typeof tmpStored.TopicsFilePath==='string'){tmpSettings.TopicsFilePath=tmpStored.TopicsFilePath;}}catch(pError){this.log.warn('Failed to load settings: '+pError.message);}}}module.exports=ContentEditorApplication;module.exports.default_configuration=require('./Pict-Application-ContentEditor-Configuration.json');},{"./Pict-Application-ContentEditor-Configuration.json":96,"./providers/Pict-Provider-ContentEditor.js":101,"./views/PictView-Editor-CodeEditor.js":102,"./views/PictView-Editor-Layout.js":103,"./views/PictView-Editor-MarkdownEditor.js":104,"./views/PictView-Editor-MarkdownReference.js":105,"./views/PictView-Editor-SettingsPanel.js":106,"./views/PictView-Editor-TopBar.js":107,"./views/PictView-Editor-Topics.js":108,"pict-application":44,"pict-section-filebrowser":51}],98:[function(require,module,exports){module.exports={"Name":"Retold Content Reader","Hash":"ContentReader","MainViewportViewIdentifier":"Docuserve-Layout","AutoSolveAfterInitialize":true,"AutoRenderMainViewportViewAfterInitialize":false,"AutoRenderViewsAfterInitialize":false,"pict_configuration":{"Product":"ContentReader-Pict-Application"}};},{}],99:[function(require,module,exports){const libDocuserveApplication=require('pict-docuserve');/**
6110
6290
  * Content Reader Application
6111
6291
  *
6112
6292
  * Extends pict-docuserve to serve standalone markdown content.
@@ -6156,11 +6336,7 @@ if(typeof window!=='undefined'){window.PictContentReader=module.exports.PictCont
6156
6336
  * @param {File} pFile - The image file to upload
6157
6337
  * @param {Function} fCallback - Callback receiving (error, url)
6158
6338
  */uploadImage(pFile,fCallback){let tmpCallback=typeof fCallback==='function'?fCallback:()=>{};// Determine the target folder from the currently open file
6159
- let tmpUploadPath='';let tmpCurrentFile=this.pict.AppData.ContentEditor.CurrentFile;if(tmpCurrentFile){let tmpLastSlash=tmpCurrentFile.lastIndexOf('/');if(tmpLastSlash>0){tmpUploadPath=tmpCurrentFile.substring(0,tmpLastSlash);}}else if(this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation){tmpUploadPath=this.pict.AppData.PictFileBrowser.CurrentLocation;}let tmpHeaders={'Content-Type':pFile.type,'x-filename':pFile.name};if(tmpUploadPath){tmpHeaders['x-upload-path']=tmpUploadPath;}fetch('/api/content/upload-image',{method:'POST',body:pFile,headers:tmpHeaders}).then(pResponse=>pResponse.json()).then(pData=>{if(pData&&pData.Success&&pData.URL){return tmpCallback(null,pData.URL);}return tmpCallback(pData?pData.Error:'Upload failed');}).catch(pError=>{this.log.warn(`ContentEditor: Image upload failed: ${pError}`);return tmpCallback(pError.message);});}/**
6160
- * List uploaded images.
6161
- *
6162
- * @param {Function} fCallback - Callback receiving (error, filesArray)
6163
- */listUploads(fCallback){let tmpCallback=typeof fCallback==='function'?fCallback:()=>{};fetch('/api/content/uploads').then(pResponse=>pResponse.json()).then(pData=>{if(pData&&pData.Success){return tmpCallback(null,pData.Files||[]);}return tmpCallback(pData?pData.Error:'Unknown error',[]);}).catch(pError=>{this.log.warn(`ContentEditor: Error listing uploads: ${pError}`);return tmpCallback(pError.message,[]);});}}module.exports=ContentEditorProvider;module.exports.default_configuration={ProviderIdentifier:"ContentEditor-Provider",AutoInitialize:true,AutoInitializeOrdinal:0};},{"pict-provider":46}],102:[function(require,module,exports){const libPictSectionCode=require('pict-section-code');/**
6339
+ let tmpUploadPath='';let tmpCurrentFile=this.pict.AppData.ContentEditor.CurrentFile;if(tmpCurrentFile){let tmpLastSlash=tmpCurrentFile.lastIndexOf('/');if(tmpLastSlash>0){tmpUploadPath=tmpCurrentFile.substring(0,tmpLastSlash);}}else if(this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation){tmpUploadPath=this.pict.AppData.PictFileBrowser.CurrentLocation;}let tmpHeaders={'Content-Type':pFile.type,'x-filename':pFile.name};if(tmpUploadPath){tmpHeaders['x-upload-path']=tmpUploadPath;}fetch('/api/content/upload-image',{method:'POST',body:pFile,headers:tmpHeaders}).then(pResponse=>pResponse.json()).then(pData=>{if(pData&&pData.Success&&pData.URL){return tmpCallback(null,pData.URL);}return tmpCallback(pData?pData.Error:'Upload failed');}).catch(pError=>{this.log.warn(`ContentEditor: Image upload failed: ${pError}`);return tmpCallback(pError.message);});}}module.exports=ContentEditorProvider;module.exports.default_configuration={ProviderIdentifier:"ContentEditor-Provider",AutoInitialize:true,AutoInitializeOrdinal:0};},{"pict-provider":46}],102:[function(require,module,exports){const libPictSectionCode=require('pict-section-code');/**
6164
6340
  * Map of file extensions to highlight.js language identifiers.
6165
6341
  *
6166
6342
  * highlight.js supports 190+ languages. This map covers the most common
@@ -6388,6 +6564,38 @@ if(this.pict.PictApplication){this.pict.PictApplication.markDirty();this.pict.Pi
6388
6564
  {
6389
6565
  display: none;
6390
6566
  }
6567
+ /* Breadcrumb bar: flex wrapper with create-folder button */
6568
+ .pict-fb-breadcrumb-bar
6569
+ {
6570
+ display: flex;
6571
+ align-items: center;
6572
+ background: #F5F0E8;
6573
+ border-bottom: 1px solid #DDD6CA;
6574
+ }
6575
+ .pict-fb-breadcrumb-bar .pict-fb-breadcrumb
6576
+ {
6577
+ flex: 1;
6578
+ border-bottom: none;
6579
+ }
6580
+ .pict-fb-breadcrumb-addfolder
6581
+ {
6582
+ flex-shrink: 0;
6583
+ background: transparent;
6584
+ border: none;
6585
+ font-size: 1.1rem;
6586
+ font-weight: 600;
6587
+ color: #8A7F72;
6588
+ cursor: pointer;
6589
+ padding: 4px 10px;
6590
+ line-height: 1;
6591
+ border-radius: 4px;
6592
+ margin-right: 4px;
6593
+ }
6594
+ .pict-fb-breadcrumb-addfolder:hover
6595
+ {
6596
+ color: #2E7D74;
6597
+ background: #EAE3D8;
6598
+ }
6391
6599
  #ContentEditor-Editor-Container
6392
6600
  {
6393
6601
  flex: 1;
@@ -6716,13 +6924,178 @@ if(this.pict.PictApplication){this.pict.PictApplication.markDirty();this.pict.Pi
6716
6924
  border-radius: 3px;
6717
6925
  color: #5E5549;
6718
6926
  }
6719
- .content-editor-upload-footer
6927
+ .content-editor-upload-footer
6928
+ {
6929
+ padding: 10px 18px;
6930
+ border-top: 1px solid #EDE9E3;
6931
+ font-size: 0.72rem;
6932
+ color: #8A7F72;
6933
+ text-align: center;
6934
+ }
6935
+
6936
+ /* File browser row insert button — hidden by default, shown on
6937
+ hover for image file rows via CSS attribute selectors. */
6938
+ .pict-fb-insert-btn
6939
+ {
6940
+ display: none;
6941
+ position: absolute;
6942
+ right: 6px;
6943
+ top: 50%;
6944
+ transform: translateY(-50%);
6945
+ background: #2E7D74;
6946
+ color: #FFF;
6947
+ border: none;
6948
+ border-radius: 4px;
6949
+ font-size: 0.78rem;
6950
+ font-weight: 700;
6951
+ line-height: 1;
6952
+ padding: 2px 7px;
6953
+ cursor: pointer;
6954
+ }
6955
+ .pict-fb-insert-btn:hover
6956
+ {
6957
+ background: #3A9E92;
6958
+ }
6959
+ /* Make the row position:relative so the button can be absolutely placed */
6960
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row
6961
+ {
6962
+ position: relative;
6963
+ }
6964
+ /* Show the insert button on hover for image file extensions.
6965
+ CSS attribute selector [data-name$=".ext" i] matches
6966
+ the end of the data-name attribute, case-insensitive. */
6967
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".png" i]:hover .pict-fb-insert-btn,
6968
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".jpg" i]:hover .pict-fb-insert-btn,
6969
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".jpeg" i]:hover .pict-fb-insert-btn,
6970
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".gif" i]:hover .pict-fb-insert-btn,
6971
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".webp" i]:hover .pict-fb-insert-btn,
6972
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".svg" i]:hover .pict-fb-insert-btn,
6973
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".bmp" i]:hover .pict-fb-insert-btn,
6974
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".avif" i]:hover .pict-fb-insert-btn,
6975
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".apng" i]:hover .pict-fb-insert-btn,
6976
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".ico" i]:hover .pict-fb-insert-btn,
6977
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".tiff" i]:hover .pict-fb-insert-btn,
6978
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".tif" i]:hover .pict-fb-insert-btn,
6979
+ #ContentEditor-Sidebar-Container .pict-fb-detail-row[data-name$=".jfif" i]:hover .pict-fb-insert-btn
6720
6980
  {
6721
- padding: 10px 18px;
6722
- border-top: 1px solid #EDE9E3;
6723
- font-size: 0.72rem;
6724
- color: #8A7F72;
6725
- text-align: center;
6981
+ display: inline-block;
6982
+ }
6983
+
6984
+ /* ============================================
6985
+ RESPONSIVE: Tablet / Phone (max-width: 768px)
6986
+ ============================================ */
6987
+ @media (max-width: 768px)
6988
+ {
6989
+ /* Prevent horizontal scroll on the whole app */
6990
+ #ContentEditor-Application-Container
6991
+ {
6992
+ overflow-x: hidden;
6993
+ width: 100%;
6994
+ max-width: 100vw;
6995
+ }
6996
+
6997
+ /* Stack sidebar ABOVE editor instead of side-by-side */
6998
+ .content-editor-body
6999
+ {
7000
+ flex-direction: column;
7001
+ }
7002
+
7003
+ /* Sidebar becomes a horizontal strip at the top */
7004
+ .content-editor-sidebar-wrap
7005
+ {
7006
+ width: 100% !important;
7007
+ max-height: 40vh;
7008
+ flex-shrink: 0;
7009
+ border-bottom: 1px solid #DDD6CA;
7010
+ border-right: none;
7011
+ }
7012
+
7013
+ /* When collapsed on mobile, hide the inner content but keep the
7014
+ toggle button visible (it's positioned below the sidebar strip) */
7015
+ .content-editor-sidebar-wrap.collapsed
7016
+ {
7017
+ width: 100% !important;
7018
+ max-height: 0;
7019
+ overflow: visible;
7020
+ }
7021
+ .content-editor-sidebar-wrap.collapsed .content-editor-sidebar-inner
7022
+ {
7023
+ display: none;
7024
+ }
7025
+
7026
+ /* Hide the resize handle (desktop-only interaction) */
7027
+ .content-editor-resize-handle
7028
+ {
7029
+ display: none;
7030
+ }
7031
+
7032
+ /* Reposition the sidebar toggle for horizontal layout —
7033
+ place it at the bottom-center of the sidebar strip */
7034
+ .content-editor-sidebar-toggle
7035
+ {
7036
+ position: absolute;
7037
+ top: auto;
7038
+ bottom: -20px;
7039
+ right: auto;
7040
+ left: 50%;
7041
+ transform: translateX(-50%);
7042
+ width: 28px;
7043
+ height: 20px;
7044
+ border-radius: 0 0 4px 4px;
7045
+ border: 1px solid #DDD6CA;
7046
+ border-top: none;
7047
+ z-index: 10;
7048
+ }
7049
+ .content-editor-sidebar-wrap.collapsed .content-editor-sidebar-toggle
7050
+ {
7051
+ bottom: -20px;
7052
+ right: auto;
7053
+ left: 50%;
7054
+ transform: translateX(-50%);
7055
+ }
7056
+
7057
+ /* Reduce editor container padding (less gutters) */
7058
+ #ContentEditor-Editor-Container
7059
+ {
7060
+ padding: 24px 8px 8px 8px;
7061
+ }
7062
+
7063
+ /* Reduce binary preview padding */
7064
+ .binary-preview-image
7065
+ {
7066
+ padding: 12px;
7067
+ }
7068
+ .binary-preview-card
7069
+ {
7070
+ padding: 12px;
7071
+ gap: 12px;
7072
+ }
7073
+
7074
+ /* Upload panel: fill more of the screen */
7075
+ .content-editor-upload-panel
7076
+ {
7077
+ width: 95vw;
7078
+ max-width: 95vw;
7079
+ }
7080
+
7081
+ }
7082
+
7083
+ /* ============================================
7084
+ RESPONSIVE: Small phone (max-width: 480px)
7085
+ ============================================ */
7086
+ @media (max-width: 480px)
7087
+ {
7088
+ /* Even tighter editor padding */
7089
+ #ContentEditor-Editor-Container
7090
+ {
7091
+ padding: 20px 4px 4px 4px;
7092
+ }
7093
+
7094
+ /* Sidebar gets a smaller max height */
7095
+ .content-editor-sidebar-wrap
7096
+ {
7097
+ max-height: 35vh;
7098
+ }
6726
7099
  }
6727
7100
  `,Templates:[{Hash:"ContentEditor-Layout-Shell-Template",Template:/*html*/`
6728
7101
  <div id="ContentEditor-TopBar-Container"></div>
@@ -6779,7 +7152,10 @@ if(this.pict.PictApplication){this.pict.PictApplication.markDirty();this.pict.Pi
6779
7152
  this.pict.views['ContentEditor-TopBar'].render();// Show welcome message in editor area if no file loaded
6780
7153
  let tmpEditorContainer=this.pict.ContentAssignment.getElement('#ContentEditor-Editor-Container');if(tmpEditorContainer&&tmpEditorContainer[0]&&!this.pict.AppData.ContentEditor.CurrentFile){tmpEditorContainer[0].innerHTML='<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#8A7F72;font-size:1.1em;">Select a file from the sidebar to begin editing</div>';}// Inject CSS
6781
7154
  this.pict.CSSMap.injectCSS();// Apply persisted sidebar state
6782
- let tmpSettings=this.pict.AppData.ContentEditor;let tmpWrap=document.getElementById('ContentEditor-SidebarWrap');let tmpToggle=document.getElementById('ContentEditor-SidebarToggle');if(tmpWrap){tmpWrap.style.width=tmpSettings.SidebarWidth+'px';if(tmpSettings.SidebarCollapsed){tmpWrap.classList.add('collapsed');if(tmpToggle)tmpToggle.innerHTML='&#x25B6;';}}// Wire up sidebar toggle
7155
+ let tmpSettings=this.pict.AppData.ContentEditor;let tmpWrap=document.getElementById('ContentEditor-SidebarWrap');let tmpToggle=document.getElementById('ContentEditor-SidebarToggle');if(tmpWrap){let tmpIsMobile=window.innerWidth<=768;tmpWrap.style.width=tmpIsMobile?'100%':tmpSettings.SidebarWidth+'px';if(tmpSettings.SidebarCollapsed){tmpWrap.classList.add('collapsed');if(tmpToggle)tmpToggle.innerHTML=tmpIsMobile?'&#x25BC;':'&#x25B6;';}else if(tmpIsMobile){// Auto-collapse sidebar on narrow viewports.
7156
+ // Sync the setting so toggleSidebar() works correctly,
7157
+ // but don't persist — the desktop preference stays in localStorage.
7158
+ tmpSettings.SidebarCollapsed=true;tmpWrap.classList.add('collapsed');if(tmpToggle)tmpToggle.innerHTML='&#x25BC;';}}// Wire up sidebar toggle
6783
7159
  let tmpSelf=this;if(tmpToggle){tmpToggle.addEventListener('click',()=>{tmpSelf.toggleSidebar();});}// Wire up resize handle
6784
7160
  this._wireResizeHandle();// Listen for hash changes
6785
7161
  window.addEventListener('hashchange',()=>{tmpSelf.pict.PictApplication.resolveHash();});// Keyboard shortcuts
@@ -6796,7 +7172,8 @@ let tmpUploadOverlay=document.getElementById('ContentEditor-UploadOverlay');if(t
6796
7172
  let tmpConfirmOverlay=document.getElementById('ContentEditor-ConfirmOverlay');if(tmpConfirmOverlay&&tmpConfirmOverlay.classList.contains('open')){return;}// Close the current file
6797
7173
  if(tmpSelf.pict.AppData.ContentEditor.CurrentFile){pEvent.preventDefault();tmpSelf.pict.PictApplication.closeCurrentFile();return;}}});return super.onAfterRender(pRenderable,pRenderDestinationAddress,pRecord,pContent);}/**
6798
7174
  * Toggle the sidebar collapsed/expanded state.
6799
- */toggleSidebar(){let tmpWrap=document.getElementById('ContentEditor-SidebarWrap');let tmpToggle=document.getElementById('ContentEditor-SidebarToggle');if(!tmpWrap){return;}let tmpSettings=this.pict.AppData.ContentEditor;tmpSettings.SidebarCollapsed=!tmpSettings.SidebarCollapsed;if(tmpSettings.SidebarCollapsed){tmpWrap.classList.add('collapsed');if(tmpToggle)tmpToggle.innerHTML='&#x25B6;';}else{tmpWrap.classList.remove('collapsed');tmpWrap.style.width=tmpSettings.SidebarWidth+'px';if(tmpToggle)tmpToggle.innerHTML='&#x25C0;';}this.pict.PictApplication.saveSettings();}/**
7175
+ */toggleSidebar(){let tmpWrap=document.getElementById('ContentEditor-SidebarWrap');let tmpToggle=document.getElementById('ContentEditor-SidebarToggle');if(!tmpWrap){return;}let tmpSettings=this.pict.AppData.ContentEditor;tmpSettings.SidebarCollapsed=!tmpSettings.SidebarCollapsed;// Use vertical arrows on narrow viewports, horizontal on wide
7176
+ let tmpIsMobile=window.innerWidth<=768;if(tmpSettings.SidebarCollapsed){tmpWrap.classList.add('collapsed');if(tmpToggle)tmpToggle.innerHTML=tmpIsMobile?'&#x25BC;':'&#x25B6;';}else{tmpWrap.classList.remove('collapsed');tmpWrap.style.width=tmpIsMobile?'100%':tmpSettings.SidebarWidth+'px';if(tmpToggle)tmpToggle.innerHTML=tmpIsMobile?'&#x25B2;':'&#x25C0;';}this.pict.PictApplication.saveSettings();}/**
6800
7177
  * Switch the active sidebar tab.
6801
7178
  *
6802
7179
  * @param {string} pTab - 'files', 'reference', or 'topics'
@@ -7550,6 +7927,40 @@ if(tmpLastIndex<tmpText.length){tmpFragment.appendChild(document.createTextNode(
7550
7927
  color: #5E5549;
7551
7928
  white-space: nowrap;
7552
7929
  }
7930
+
7931
+ /* ============================================
7932
+ RESPONSIVE: Tablet / Phone (max-width: 768px)
7933
+ ============================================ */
7934
+ @media (max-width: 768px)
7935
+ {
7936
+ /* Settings flyout: position from left edge for more room */
7937
+ .content-editor-settings-flyout
7938
+ {
7939
+ right: -8px;
7940
+ width: 260px;
7941
+ }
7942
+ }
7943
+
7944
+ /* ============================================
7945
+ RESPONSIVE: Small phone (max-width: 480px)
7946
+ ============================================ */
7947
+ @media (max-width: 480px)
7948
+ {
7949
+ /* Full-width settings flyout on small phones */
7950
+ .content-editor-settings-flyout
7951
+ {
7952
+ position: fixed;
7953
+ top: 48px;
7954
+ right: 0;
7955
+ left: 0;
7956
+ width: 100%;
7957
+ border-radius: 0 0 8px 8px;
7958
+ }
7959
+ .content-editor-settings-flyout::before
7960
+ {
7961
+ display: none;
7962
+ }
7963
+ }
7553
7964
  `,Templates:[{Hash:"ContentEditor-SettingsPanel-Template",Template:/*html*/`
7554
7965
  <div class="content-editor-settings-wrap">
7555
7966
  <button class="content-editor-settings-gear" id="ContentEditor-SettingsGear"
@@ -7665,7 +8076,10 @@ if(tmpLastIndex<tmpText.length){tmpFragment.appendChild(document.createTextNode(
7665
8076
  let tmpSettings=this.pict.AppData.ContentEditor;let tmpMdWrapCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-MarkdownWordWrap');if(tmpMdWrapCheckbox&&tmpMdWrapCheckbox[0]){tmpMdWrapCheckbox[0].checked=tmpSettings.MarkdownWordWrap;}let tmpCodeWrapCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-CodeWordWrap');if(tmpCodeWrapCheckbox&&tmpCodeWrapCheckbox[0]){tmpCodeWrapCheckbox[0].checked=tmpSettings.CodeWordWrap;}let tmpControlsCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-EditingControls');if(tmpControlsCheckbox&&tmpControlsCheckbox[0]){tmpControlsCheckbox[0].checked=tmpSettings.MarkdownEditingControls;}let tmpPreviewCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-AutoPreview');if(tmpPreviewCheckbox&&tmpPreviewCheckbox[0]){tmpPreviewCheckbox[0].checked=tmpSettings.AutoContentPreview;}let tmpSegmentCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-AutoSegment');if(tmpSegmentCheckbox&&tmpSegmentCheckbox[0]){tmpSegmentCheckbox[0].checked=tmpSettings.AutoSegmentMarkdown;}let tmpSelect=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-SegmentDepth');if(tmpSelect&&tmpSelect[0]){tmpSelect[0].value=String(tmpSettings.AutoSegmentDepth);tmpSelect[0].disabled=!tmpSettings.AutoSegmentMarkdown;}let tmpImgCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-AutoPreviewImages');if(tmpImgCheckbox&&tmpImgCheckbox[0]){tmpImgCheckbox[0].checked=tmpSettings.AutoPreviewImages;}let tmpVideoCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-AutoPreviewVideo');if(tmpVideoCheckbox&&tmpVideoCheckbox[0]){tmpVideoCheckbox[0].checked=tmpSettings.AutoPreviewVideo;}let tmpAudioCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-AutoPreviewAudio');if(tmpAudioCheckbox&&tmpAudioCheckbox[0]){tmpAudioCheckbox[0].checked=tmpSettings.AutoPreviewAudio;}let tmpHiddenCheckbox=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-ShowHiddenFiles');if(tmpHiddenCheckbox&&tmpHiddenCheckbox[0]){tmpHiddenCheckbox[0].checked=tmpSettings.ShowHiddenFiles;}return super.onAfterRender(pRenderable,pRenderDestinationAddress,pRecord,pContent);}togglePanel(){if(this._isOpen){this.closePanel();}else{this.openPanel();}}openPanel(){this._isOpen=true;let tmpFlyout=this.pict.ContentAssignment.getElement('#ContentEditor-SettingsFlyout');let tmpOverlay=this.pict.ContentAssignment.getElement('#ContentEditor-SettingsOverlay');let tmpGear=this.pict.ContentAssignment.getElement('#ContentEditor-SettingsGear');if(tmpFlyout&&tmpFlyout[0])tmpFlyout[0].classList.add('open');if(tmpOverlay&&tmpOverlay[0])tmpOverlay[0].classList.add('open');if(tmpGear&&tmpGear[0])tmpGear[0].classList.add('active');}closePanel(){this._isOpen=false;let tmpFlyout=this.pict.ContentAssignment.getElement('#ContentEditor-SettingsFlyout');let tmpOverlay=this.pict.ContentAssignment.getElement('#ContentEditor-SettingsOverlay');let tmpGear=this.pict.ContentAssignment.getElement('#ContentEditor-SettingsGear');if(tmpFlyout&&tmpFlyout[0])tmpFlyout[0].classList.remove('open');if(tmpOverlay&&tmpOverlay[0])tmpOverlay[0].classList.remove('open');if(tmpGear&&tmpGear[0])tmpGear[0].classList.remove('active');}onMarkdownWordWrapChanged(pChecked){this.pict.AppData.ContentEditor.MarkdownWordWrap=pChecked;this.pict.PictApplication.saveSettings();// Live-apply to all CodeMirror segment editors if markdown is active
7666
8077
  let tmpEditorView=this.pict.views['ContentEditor-MarkdownEditor'];if(tmpEditorView&&this.pict.AppData.ContentEditor.ActiveEditor==='markdown'&&tmpEditorView._segmentEditors){for(let tmpKey in tmpEditorView._segmentEditors){let tmpEditor=tmpEditorView._segmentEditors[tmpKey];if(tmpEditor&&tmpEditor.contentDOM){if(pChecked){tmpEditor.contentDOM.classList.add('cm-lineWrapping');}else{tmpEditor.contentDOM.classList.remove('cm-lineWrapping');}}}}}onCodeWordWrapChanged(pChecked){this.pict.AppData.ContentEditor.CodeWordWrap=pChecked;this.pict.PictApplication.saveSettings();// Live-apply to the code editor if it's currently active
7667
8078
  let tmpCodeEditorView=this.pict.views['ContentEditor-CodeEditor'];if(tmpCodeEditorView&&tmpCodeEditorView._editorElement&&this.pict.AppData.ContentEditor.ActiveEditor==='code'){if(pChecked){tmpCodeEditorView._editorElement.style.whiteSpace='pre-wrap';tmpCodeEditorView._editorElement.style.overflowWrap='break-word';}else{tmpCodeEditorView._editorElement.style.whiteSpace='pre';tmpCodeEditorView._editorElement.style.overflowWrap='normal';}}}onEditingControlsChanged(pChecked){this.pict.AppData.ContentEditor.MarkdownEditingControls=pChecked;this.pict.PictApplication.saveSettings();// Live-apply to the markdown editor if it's currently active
7668
- let tmpEditorView=this.pict.views['ContentEditor-MarkdownEditor'];if(tmpEditorView&&this.pict.AppData.ContentEditor.ActiveEditor==='markdown'){tmpEditorView.toggleControls(pChecked);}}onAutoPreviewChanged(pChecked){this.pict.AppData.ContentEditor.AutoContentPreview=pChecked;this.pict.PictApplication.saveSettings();}onAutoSegmentChanged(pChecked){this.pict.AppData.ContentEditor.AutoSegmentMarkdown=pChecked;this.pict.PictApplication.saveSettings();// Enable/disable the depth dropdown
8079
+ let tmpEditorView=this.pict.views['ContentEditor-MarkdownEditor'];if(tmpEditorView&&this.pict.AppData.ContentEditor.ActiveEditor==='markdown'){tmpEditorView.toggleControls(pChecked);}}onAutoPreviewChanged(pChecked){this.pict.AppData.ContentEditor.AutoContentPreview=pChecked;this.pict.PictApplication.saveSettings();let tmpEditorView=this.pict.views['ContentEditor-MarkdownEditor'];if(tmpEditorView&&this.pict.AppData.ContentEditor.ActiveEditor==='markdown'){if(pChecked){// Turning ON: clear global hidden class and show all previews
8080
+ tmpEditorView.togglePreview(true);for(let tmpIdx in tmpEditorView._segmentEditors){tmpEditorView.toggleSegmentPreview(parseInt(tmpIdx,10),true);}}else{// Turning OFF: hide each segment individually so
8081
+ // per-segment toggle buttons still work
8082
+ for(let tmpIdx in tmpEditorView._segmentEditors){tmpEditorView.toggleSegmentPreview(parseInt(tmpIdx,10),false);}}}}onAutoSegmentChanged(pChecked){this.pict.AppData.ContentEditor.AutoSegmentMarkdown=pChecked;this.pict.PictApplication.saveSettings();// Enable/disable the depth dropdown
7669
8083
  let tmpSelect=this.pict.ContentAssignment.getElement('#ContentEditor-Setting-SegmentDepth');if(tmpSelect&&tmpSelect[0]){tmpSelect[0].disabled=!pChecked;}}onSegmentDepthChanged(pValue){this.pict.AppData.ContentEditor.AutoSegmentDepth=parseInt(pValue,10)||1;this.pict.PictApplication.saveSettings();}onAutoPreviewImagesChanged(pChecked){this.pict.AppData.ContentEditor.AutoPreviewImages=pChecked;this.pict.PictApplication.saveSettings();}onAutoPreviewVideoChanged(pChecked){this.pict.AppData.ContentEditor.AutoPreviewVideo=pChecked;this.pict.PictApplication.saveSettings();}onAutoPreviewAudioChanged(pChecked){this.pict.AppData.ContentEditor.AutoPreviewAudio=pChecked;this.pict.PictApplication.saveSettings();}onShowHiddenFilesChanged(pChecked){this.pict.AppData.ContentEditor.ShowHiddenFiles=pChecked;this.pict.PictApplication.saveSettings();// Tell the server to include/exclude hidden files, then refresh
7670
8084
  let tmpSelf=this;this.pict.PictApplication.syncHiddenFilesSetting(()=>{tmpSelf.pict.PictApplication.loadFileList();});}}module.exports=ContentEditorSettingsPanelView;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],107:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"ContentEditor-TopBar",DefaultRenderable:"ContentEditor-TopBar-Display",DefaultDestinationAddress:"#ContentEditor-TopBar-Container",AutoRender:false,CSS:/*css*/`
7671
8085
  .content-editor-topbar
@@ -7896,6 +8310,99 @@ let tmpSelf=this;this.pict.PictApplication.syncHiddenFilesSetting(()=>{tmpSelf.p
7896
8310
  border-radius: 3px;
7897
8311
  color: #5E5549;
7898
8312
  }
8313
+
8314
+ /* ============================================
8315
+ RESPONSIVE: Tablet / Phone (max-width: 768px)
8316
+ ============================================ */
8317
+ @media (max-width: 768px)
8318
+ {
8319
+ .content-editor-topbar
8320
+ {
8321
+ height: auto;
8322
+ min-height: 44px;
8323
+ flex-wrap: wrap;
8324
+ padding: 4px 0;
8325
+ }
8326
+
8327
+ /* Brand text: shrink */
8328
+ .content-editor-topbar-brand
8329
+ {
8330
+ padding: 0 8px;
8331
+ font-size: 0.85rem;
8332
+ }
8333
+
8334
+ /* File name: switch from absolute centering to flow layout */
8335
+ .content-editor-topbar-file
8336
+ {
8337
+ position: static;
8338
+ transform: none;
8339
+ max-width: none;
8340
+ flex: 1;
8341
+ min-width: 0;
8342
+ padding: 0 4px;
8343
+ }
8344
+
8345
+ /* Hide the spacer (not needed when file is in-flow) */
8346
+ .content-editor-topbar-spacer
8347
+ {
8348
+ display: none;
8349
+ }
8350
+
8351
+ /* Hide word/line count stats on mobile to save space */
8352
+ .content-editor-topbar-stats
8353
+ {
8354
+ display: none;
8355
+ }
8356
+
8357
+ /* Compact action buttons */
8358
+ .content-editor-topbar-actions
8359
+ {
8360
+ gap: 4px;
8361
+ padding: 0 6px;
8362
+ }
8363
+
8364
+ .content-editor-topbar-btn
8365
+ {
8366
+ padding: 5px 10px;
8367
+ font-size: 0.75rem;
8368
+ }
8369
+
8370
+ /* Status text */
8371
+ .content-editor-topbar-status
8372
+ {
8373
+ padding: 0 6px;
8374
+ font-size: 0.72rem;
8375
+ }
8376
+
8377
+ /* Confirm dialog: tighter for mobile */
8378
+ .content-editor-confirm-panel
8379
+ {
8380
+ width: 95vw;
8381
+ max-width: 95vw;
8382
+ }
8383
+ }
8384
+
8385
+ /* ============================================
8386
+ RESPONSIVE: Small phone (max-width: 480px)
8387
+ ============================================ */
8388
+ @media (max-width: 480px)
8389
+ {
8390
+ /* Hide brand text entirely on very small screens */
8391
+ .content-editor-topbar-brand
8392
+ {
8393
+ display: none;
8394
+ }
8395
+
8396
+ .content-editor-topbar-filename
8397
+ {
8398
+ font-size: 0.8rem;
8399
+ }
8400
+
8401
+ .content-editor-topbar-actions
8402
+ {
8403
+ padding: 0 4px;
8404
+ }
8405
+ }
7899
8406
  `,Templates:[{Hash:"ContentEditor-TopBar-Template",Template:/*html*/`
7900
8407
  <div class="content-editor-topbar">
7901
8408
  <div class="content-editor-topbar-brand">Content Editor</div>
@@ -8541,7 +9048,7 @@ if(psychotic){result.hostname=isAbsolute?'':srcPath.length?srcPath.shift():'';re
8541
9048
  if(result.pathname!==null||result.search!==null){result.path=(result.pathname?result.pathname:'')+(result.search?result.search:'');}result.auth=relative.auth||result.auth;result.slashes=result.slashes||relative.slashes;result.href=result.format();return result;};Url.prototype.parseHost=function(){var host=this.host;var port=portPattern.exec(host);if(port){port=port[0];if(port!==':'){this.port=port.substr(1);}host=host.substr(0,host.length-port.length);}if(host){this.hostname=host;}};exports.parse=urlParse;exports.resolve=urlResolve;exports.resolveObject=urlResolveObject;exports.format=urlFormat;exports.Url=Url;},{"punycode/":78,"qs":80}],114:[function(require,module,exports){module.exports={"Name":"Retold Remote","MainViewportViewIdentifier":"ContentEditor-Layout","AutoSolveAfterInitialize":true,"AutoRenderMainViewportViewAfterInitialize":false,"AutoRenderViewsAfterInitialize":false};},{}],115:[function(require,module,exports){const libContentEditorApplication=require('retold-content-system').PictContentEditor;const libPictSectionFileBrowser=require('pict-section-filebrowser');// Providers
8542
9049
  const libProviderRetoldRemote=require('./providers/Pict-Provider-RetoldRemote.js');const libProviderGalleryNavigation=require('./providers/Pict-Provider-GalleryNavigation.js');const libProviderGalleryFilterSort=require('./providers/Pict-Provider-GalleryFilterSort.js');const libProviderRetoldRemoteIcons=require('./providers/Pict-Provider-RetoldRemoteIcons.js');const libProviderRetoldRemoteTheme=require('./providers/Pict-Provider-RetoldRemoteTheme.js');// Views (replace parent views)
8543
9050
  const libViewLayout=require('./views/PictView-Remote-Layout.js');const libViewTopBar=require('./views/PictView-Remote-TopBar.js');const libViewSettingsPanel=require('./views/PictView-Remote-SettingsPanel.js');// Views (new)
8544
- const libViewGallery=require('./views/PictView-Remote-Gallery.js');const libViewMediaViewer=require('./views/PictView-Remote-MediaViewer.js');const libViewImageViewer=require('./views/PictView-Remote-ImageViewer.js');const libViewVideoExplorer=require('./views/PictView-Remote-VideoExplorer.js');const libViewAudioExplorer=require('./views/PictView-Remote-AudioExplorer.js');// Application configuration
9051
+ const libViewGallery=require('./views/PictView-Remote-Gallery.js');const libViewMediaViewer=require('./views/PictView-Remote-MediaViewer.js');const libViewImageViewer=require('./views/PictView-Remote-ImageViewer.js');const libViewVideoExplorer=require('./views/PictView-Remote-VideoExplorer.js');const libViewAudioExplorer=require('./views/PictView-Remote-AudioExplorer.js');const libViewVLCSetup=require('./views/PictView-Remote-VLCSetup.js');// Application configuration
8545
9052
  const _DefaultConfiguration=require('./Pict-Application-RetoldRemote-Configuration.json');/**
8546
9053
  * Retold Remote Application
8547
9054
  *
@@ -8552,7 +9059,7 @@ const _DefaultConfiguration=require('./Pict-Application-RetoldRemote-Configurati
8552
9059
  */class RetoldRemoteApplication extends libContentEditorApplication{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_DefaultConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);// Replace parent views with media-focused versions.
8553
9060
  // Re-registering with the same ViewIdentifier replaces the parent's view.
8554
9061
  this.pict.addView('ContentEditor-Layout',libViewLayout.default_configuration,libViewLayout);this.pict.addView('ContentEditor-TopBar',libViewTopBar.default_configuration,libViewTopBar);// Add new views
8555
- this.pict.addView('RetoldRemote-Gallery',libViewGallery.default_configuration,libViewGallery);this.pict.addView('RetoldRemote-MediaViewer',libViewMediaViewer.default_configuration,libViewMediaViewer);this.pict.addView('RetoldRemote-ImageViewer',libViewImageViewer.default_configuration,libViewImageViewer);this.pict.addView('RetoldRemote-SettingsPanel',libViewSettingsPanel.default_configuration,libViewSettingsPanel);this.pict.addView('RetoldRemote-VideoExplorer',libViewVideoExplorer.default_configuration,libViewVideoExplorer);this.pict.addView('RetoldRemote-AudioExplorer',libViewAudioExplorer.default_configuration,libViewAudioExplorer);// Add new providers
9062
+ this.pict.addView('RetoldRemote-Gallery',libViewGallery.default_configuration,libViewGallery);this.pict.addView('RetoldRemote-MediaViewer',libViewMediaViewer.default_configuration,libViewMediaViewer);this.pict.addView('RetoldRemote-ImageViewer',libViewImageViewer.default_configuration,libViewImageViewer);this.pict.addView('RetoldRemote-SettingsPanel',libViewSettingsPanel.default_configuration,libViewSettingsPanel);this.pict.addView('RetoldRemote-VideoExplorer',libViewVideoExplorer.default_configuration,libViewVideoExplorer);this.pict.addView('RetoldRemote-AudioExplorer',libViewAudioExplorer.default_configuration,libViewAudioExplorer);this.pict.addView('RetoldRemote-VLCSetup',libViewVLCSetup.default_configuration,libViewVLCSetup);// Add new providers
8556
9063
  this.pict.addProvider('RetoldRemote-Provider',libProviderRetoldRemote.default_configuration,libProviderRetoldRemote);this.pict.addProvider('RetoldRemote-GalleryNavigation',libProviderGalleryNavigation.default_configuration,libProviderGalleryNavigation);this.pict.addProvider('RetoldRemote-GalleryFilterSort',libProviderGalleryFilterSort.default_configuration,libProviderGalleryFilterSort);this.pict.addProvider('RetoldRemote-Icons',libProviderRetoldRemoteIcons.default_configuration,libProviderRetoldRemoteIcons);this.pict.addProvider('RetoldRemote-Theme',libProviderRetoldRemoteTheme.default_configuration,libProviderRetoldRemoteTheme);}onAfterInitializeAsync(fCallback){// Expose pict on window for inline onclick handlers
8557
9064
  if(typeof window!=='undefined'){window.pict=this.pict;}// Initialize RetoldRemote-specific state
8558
9065
  this.pict.AppData.RetoldRemote={ActiveMode:'gallery',// 'gallery' or 'viewer'
@@ -8611,6 +9118,14 @@ let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewe
8611
9118
  let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(pFilePath,'document');}}else{// Default: use media viewer
8612
9119
  let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(pFilePath,tmpMediaType);}}// Update the topbar
8613
9120
  let tmpTopBar=this.pict.views['ContentEditor-TopBar'];if(tmpTopBar){tmpTopBar.updateInfo();}}/**
9121
+ * Navigate to a file with an explicit media type override, bypassing
9122
+ * extension-based detection.
9123
+ *
9124
+ * @param {string} pFilePath - Relative file path
9125
+ * @param {string} pMediaType - 'image', 'video', 'audio', or 'text'
9126
+ */navigateToFileAs(pFilePath,pMediaType){if(!pFilePath){return;}let tmpRemote=this.pict.AppData.RetoldRemote;// Update the hash
9127
+ let tmpFragProvider=this.pict.providers['RetoldRemote-Provider'];let tmpFragId=tmpFragProvider?tmpFragProvider.getFragmentIdentifier(pFilePath):pFilePath;window.location.hash='#/view/'+tmpFragId;// Update parent state for compatibility
9128
+ this.pict.AppData.ContentEditor.CurrentFile=pFilePath;this.pict.AppData.ContentEditor.ActiveEditor='binary';let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(pFilePath,pMediaType);}let tmpTopBar=this.pict.views['ContentEditor-TopBar'];if(tmpTopBar){tmpTopBar.updateInfo();}}/**
8614
9129
  * Override loadFileList to also populate the gallery and fetch folder summary.
8615
9130
  */loadFileList(pPath,fCallback){let tmpCallback=typeof fCallback==='function'?fCallback:typeof pPath==='function'?pPath:()=>{};let tmpSelf=this;let tmpPath=typeof pPath==='string'?pPath:null;if(tmpPath===null&&this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation){tmpPath=this.pict.AppData.PictFileBrowser.CurrentLocation;}let tmpURL='/api/filebrowser/list';if(tmpPath&&tmpPath.length>0){let tmpMediaProvider=tmpSelf.pict.providers['RetoldRemote-Provider'];if(tmpMediaProvider){tmpURL+='?path='+tmpMediaProvider._getPathParam(tmpPath);}else{tmpURL+='?path='+encodeURIComponent(tmpPath);}}fetch(tmpURL).then(pResponse=>{// Capture the folder hash header before parsing JSON
8616
9131
  let tmpFolderHash=pResponse.headers.get('X-Retold-Folder-Hash');if(tmpFolderHash&&tmpPath){let tmpHashProvider=tmpSelf.pict.providers['RetoldRemote-Provider'];if(tmpHashProvider){tmpHashProvider.registerHash(tmpPath,tmpFolderHash);}}return pResponse.json();}).then(pFileList=>{tmpSelf.pict.AppData.PictFileBrowser=tmpSelf.pict.AppData.PictFileBrowser||{};tmpSelf.pict.AppData.PictFileBrowser.FileList=pFileList||[];tmpSelf.pict.AppData.PictFileBrowser.CurrentLocation=typeof pPath==='string'?pPath:tmpSelf.pict.AppData.PictFileBrowser.CurrentLocation||'';// Register hashes from file list entries (when hashed filenames is on)
@@ -8632,11 +9147,11 @@ let tmpFilePath=tmpFragProvider?tmpFragProvider.resolveFragmentIdentifier(tmpRaw
8632
9147
  }}/**
8633
9148
  * Load RetoldRemote settings from localStorage.
8634
9149
  */_loadRemoteSettings(){try{let tmpStored=localStorage.getItem('retold-remote-settings');if(tmpStored){let tmpSettings=JSON.parse(tmpStored);let tmpRemote=this.pict.AppData.RetoldRemote;if(tmpSettings.Theme)tmpRemote.Theme=tmpSettings.Theme;if(tmpSettings.ViewMode)tmpRemote.ViewMode=tmpSettings.ViewMode;if(tmpSettings.ThumbnailSize)tmpRemote.ThumbnailSize=tmpSettings.ThumbnailSize;if(tmpSettings.GalleryFilter){tmpRemote.GalleryFilter=tmpSettings.GalleryFilter;tmpRemote.FilterState.MediaType=tmpSettings.GalleryFilter;}if(typeof tmpSettings.ShowHiddenFiles==='boolean')tmpRemote.ShowHiddenFiles=tmpSettings.ShowHiddenFiles;if(typeof tmpSettings.DistractionFreeShowNav==='boolean')tmpRemote.DistractionFreeShowNav=tmpSettings.DistractionFreeShowNav;if(tmpSettings.ImageFitMode)tmpRemote.ImageFitMode=tmpSettings.ImageFitMode;if(typeof tmpSettings.SidebarCollapsed==='boolean')tmpRemote.SidebarCollapsed=tmpSettings.SidebarCollapsed;if(tmpSettings.SidebarWidth)tmpRemote.SidebarWidth=tmpSettings.SidebarWidth;if(tmpSettings.SortField)tmpRemote.SortField=tmpSettings.SortField;if(tmpSettings.SortDirection)tmpRemote.SortDirection=tmpSettings.SortDirection;if(Array.isArray(tmpSettings.FilterPresets))tmpRemote.FilterPresets=tmpSettings.FilterPresets;if(typeof tmpSettings.FilterPanelOpen==='boolean')tmpRemote.FilterPanelOpen=tmpSettings.FilterPanelOpen;if(typeof tmpSettings.AutoplayVideo==='boolean')tmpRemote.AutoplayVideo=tmpSettings.AutoplayVideo;if(typeof tmpSettings.AutoplayAudio==='boolean')tmpRemote.AutoplayAudio=tmpSettings.AutoplayAudio;}}catch(pError){// localStorage may not be available
8635
- }}}module.exports=RetoldRemoteApplication;},{"./Pict-Application-RetoldRemote-Configuration.json":114,"./providers/Pict-Provider-GalleryFilterSort.js":117,"./providers/Pict-Provider-GalleryNavigation.js":118,"./providers/Pict-Provider-RetoldRemote.js":119,"./providers/Pict-Provider-RetoldRemoteIcons.js":120,"./providers/Pict-Provider-RetoldRemoteTheme.js":121,"./views/PictView-Remote-AudioExplorer.js":122,"./views/PictView-Remote-Gallery.js":123,"./views/PictView-Remote-ImageViewer.js":124,"./views/PictView-Remote-Layout.js":125,"./views/PictView-Remote-MediaViewer.js":126,"./views/PictView-Remote-SettingsPanel.js":127,"./views/PictView-Remote-TopBar.js":128,"./views/PictView-Remote-VideoExplorer.js":129,"pict-section-filebrowser":54,"retold-content-system":100}],116:[function(require,module,exports){/**
9150
+ }}}module.exports=RetoldRemoteApplication;},{"./Pict-Application-RetoldRemote-Configuration.json":114,"./providers/Pict-Provider-GalleryFilterSort.js":117,"./providers/Pict-Provider-GalleryNavigation.js":118,"./providers/Pict-Provider-RetoldRemote.js":119,"./providers/Pict-Provider-RetoldRemoteIcons.js":120,"./providers/Pict-Provider-RetoldRemoteTheme.js":121,"./views/PictView-Remote-AudioExplorer.js":122,"./views/PictView-Remote-Gallery.js":123,"./views/PictView-Remote-ImageViewer.js":124,"./views/PictView-Remote-Layout.js":125,"./views/PictView-Remote-MediaViewer.js":126,"./views/PictView-Remote-SettingsPanel.js":127,"./views/PictView-Remote-TopBar.js":128,"./views/PictView-Remote-VLCSetup.js":129,"./views/PictView-Remote-VideoExplorer.js":130,"pict-section-filebrowser":51,"retold-content-system":100}],116:[function(require,module,exports){/**
8636
9151
  * Retold Remote -- Browser Bundle Entry
8637
9152
  *
8638
9153
  * Exports the RetoldRemote application class for browser consumption.
8639
- */module.exports={RetoldRemoteApplication:require('./Pict-Application-RetoldRemote.js')};if(typeof window!=='undefined'){window.RetoldRemoteApplication=module.exports.RetoldRemoteApplication;}},{"./Pict-Application-RetoldRemote.js":115}],117:[function(require,module,exports){const libPictProvider=require('pict-provider');const _ImageExtensions={'png':true,'jpg':true,'jpeg':true,'gif':true,'webp':true,'svg':true,'bmp':true,'ico':true,'avif':true,'tiff':true,'tif':true};const _VideoExtensions={'mp4':true,'webm':true,'mov':true,'mkv':true,'avi':true,'wmv':true,'flv':true,'m4v':true};const _AudioExtensions={'mp3':true,'wav':true,'ogg':true,'flac':true,'aac':true,'m4a':true,'wma':true};const _DocumentExtensions={'pdf':true,'epub':true,'mobi':true};const _DefaultProviderConfiguration={ProviderIdentifier:'RetoldRemote-GalleryFilterSort',AutoInitialize:true,AutoInitializeOrdinal:0,AutoSolveWithApp:false};class GalleryFilterSortProvider extends libPictProvider{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}// ──────────────────────────────────────────────
9154
+ */module.exports={RetoldRemoteApplication:require('./Pict-Application-RetoldRemote.js')};if(typeof window!=='undefined'){window.RetoldRemoteApplication=module.exports.RetoldRemoteApplication;}},{"./Pict-Application-RetoldRemote.js":115}],117:[function(require,module,exports){const libPictProvider=require('pict-provider');const _ImageExtensions={'png':true,'jpg':true,'jpeg':true,'gif':true,'webp':true,'svg':true,'bmp':true,'ico':true,'avif':true,'tiff':true,'tif':true};const _VideoExtensions={'mp4':true,'webm':true,'mov':true,'mkv':true,'avi':true,'wmv':true,'flv':true,'m4v':true,'ogv':true,'mpg':true,'mpeg':true,'mpe':true,'mpv':true,'m2v':true,'ts':true,'mts':true,'m2ts':true,'vob':true,'3gp':true,'3g2':true,'f4v':true,'rm':true,'rmvb':true,'divx':true,'asf':true,'mxf':true,'dv':true,'nsv':true,'nuv':true,'y4m':true,'wtv':true,'swf':true,'dat':true};const _AudioExtensions={'mp3':true,'wav':true,'ogg':true,'flac':true,'aac':true,'m4a':true,'wma':true};const _DocumentExtensions={'pdf':true,'epub':true,'mobi':true};const _DefaultProviderConfiguration={ProviderIdentifier:'RetoldRemote-GalleryFilterSort',AutoInitialize:true,AutoInitializeOrdinal:0,AutoSolveWithApp:false};class GalleryFilterSortProvider extends libPictProvider{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}// ──────────────────────────────────────────────
8640
9155
  // Pipeline
8641
9156
  // ──────────────────────────────────────────────
8642
9157
  /**
@@ -8748,7 +9263,7 @@ if(pEvent.key==='Escape'&&pEvent.target.id==='RetoldRemote-Gallery-Search'){pEve
8748
9263
  if(pEvent.target.tagName==='INPUT'||pEvent.target.tagName==='TEXTAREA'||pEvent.target.isContentEditable){return;}// If the help panel is visible, Escape closes it
8749
9264
  if(tmpSelf._helpPanelVisible&&pEvent.key==='Escape'){pEvent.preventDefault();tmpSelf._toggleHelpPanel();return;}let tmpRemote=tmpSelf.pict.AppData.RetoldRemote;let tmpActiveMode=tmpRemote.ActiveMode;if(tmpActiveMode==='gallery'&&tmpSelf._sidebarFocused){tmpSelf._handleSidebarKey(pEvent);}else if(tmpActiveMode==='gallery'){tmpSelf._handleGalleryKey(pEvent);}else if(tmpActiveMode==='video-explorer'){tmpSelf._handleVideoExplorerKey(pEvent);}else if(tmpActiveMode==='audio-explorer'){tmpSelf._handleAudioExplorerKey(pEvent);}else if(tmpActiveMode==='viewer'){tmpSelf._handleViewerKey(pEvent);}};document.addEventListener('keydown',this._keydownHandler);this._keydownBound=true;}/**
8750
9265
  * Handle keyboard events in gallery mode.
8751
- */_handleGalleryKey(pEvent){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpItems=tmpRemote.GalleryItems||[];let tmpIndex=tmpRemote.GalleryCursorIndex||0;switch(pEvent.key){case'ArrowRight':pEvent.preventDefault();this.moveCursor(Math.min(tmpIndex+1,tmpItems.length-1));break;case'ArrowLeft':pEvent.preventDefault();this.moveCursor(Math.max(tmpIndex-1,0));break;case'ArrowDown':pEvent.preventDefault();this.moveCursor(Math.min(tmpIndex+this._columnsPerRow,tmpItems.length-1));break;case'ArrowUp':pEvent.preventDefault();this.moveCursor(Math.max(tmpIndex-this._columnsPerRow,0));break;case'Enter':pEvent.preventDefault();this.openCurrent();break;case'Escape':pEvent.preventDefault();this.navigateUp();break;case'g':pEvent.preventDefault();this._toggleViewMode();break;case'x':pEvent.preventDefault();this._clearAllFilters();break;case'Home':pEvent.preventDefault();this.moveCursor(0);break;case'End':pEvent.preventDefault();this.moveCursor(tmpItems.length-1);break;case'f':pEvent.preventDefault();{// Ensure the filter bar is visible first
9266
+ */_handleGalleryKey(pEvent){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpItems=tmpRemote.GalleryItems||[];let tmpIndex=tmpRemote.GalleryCursorIndex||0;switch(pEvent.key){case'ArrowRight':pEvent.preventDefault();this.moveCursor(Math.min(tmpIndex+1,tmpItems.length-1));break;case'ArrowLeft':pEvent.preventDefault();this.moveCursor(Math.max(tmpIndex-1,0));break;case'ArrowDown':pEvent.preventDefault();this.moveCursor(Math.min(tmpIndex+this._columnsPerRow,tmpItems.length-1));break;case'ArrowUp':pEvent.preventDefault();this.moveCursor(Math.max(tmpIndex-this._columnsPerRow,0));break;case'Enter':pEvent.preventDefault();this.openCurrent();break;case'Escape':pEvent.preventDefault();this.navigateUp();break;case'g':pEvent.preventDefault();this._toggleViewMode();break;case'x':pEvent.preventDefault();this._clearAllFilters();break;case'Home':pEvent.preventDefault();this.moveCursor(0);break;case'End':pEvent.preventDefault();this.moveCursor(tmpItems.length-1);break;case'1':pEvent.preventDefault();this.openCurrentAs('image');break;case'2':pEvent.preventDefault();this.openCurrentAs('video');break;case'3':pEvent.preventDefault();this.openCurrentAs('audio');break;case'4':pEvent.preventDefault();this.openCurrentAs('text');break;case'f':pEvent.preventDefault();{// Ensure the filter bar is visible first
8752
9267
  this._showFilterBar();let tmpGalleryView=this.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tmpGalleryView.toggleFilterPanel();}}break;case's':pEvent.preventDefault();{// Ensure the filter bar is visible first
8753
9268
  this._showFilterBar();setTimeout(()=>{let tmpSortSelect=document.getElementById('RetoldRemote-Gallery-Sort');if(tmpSortSelect){tmpSortSelect.focus();}},50);}break;case'c':pEvent.preventDefault();this._toggleSettingsPanel();break;case'd':pEvent.preventDefault();this._toggleDistractionFree();break;}}/**
8754
9269
  * Handle keyboard events when the sidebar file list has focus.
@@ -8769,7 +9284,7 @@ if(this._sidebarCursorIndex<tmpRows.length){tmpRows[this._sidebarCursorIndex].cl
8769
9284
  if(pIndex<tmpRows.length){tmpRows[pIndex].classList.add('sidebar-focused');tmpRows[pIndex].scrollIntoView({block:'nearest',behavior:'smooth'});}}/**
8770
9285
  * Handle keyboard events in viewer mode.
8771
9286
  */_handleViewerKey(pEvent){let tmpRemote=this.pict.AppData.RetoldRemote;// Video action menu mode — intercept keys for menu options
8772
- if(tmpRemote.VideoMenuActive&&tmpRemote.CurrentViewerMediaType==='video'){switch(pEvent.key){case'Escape':pEvent.preventDefault();this.closeViewer();return;case'ArrowRight':case'j':pEvent.preventDefault();this.nextFile();return;case'ArrowLeft':case'k':pEvent.preventDefault();this.prevFile();return;case'e':pEvent.preventDefault();let tmpVEX=this.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.showExplorer(tmpRemote.CurrentViewerFile);}return;case' ':case'Enter':pEvent.preventDefault();let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.playVideo();}return;case't':pEvent.preventDefault();let tmpMediaViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpMediaViewer){tmpMediaViewer.loadVideoMenuFrame();}return;case'v':pEvent.preventDefault();this._openWithVLC();return;}return;}switch(pEvent.key){case'Escape':pEvent.preventDefault();this.closeViewer();break;case'ArrowRight':case'j':pEvent.preventDefault();this.nextFile();break;case'ArrowLeft':case'k':pEvent.preventDefault();this.prevFile();break;case'f':pEvent.preventDefault();this._toggleFullscreen();break;case'i':pEvent.preventDefault();this._toggleFileInfo();break;case' ':pEvent.preventDefault();this._togglePlayPause();break;case'+':case'=':pEvent.preventDefault();this._zoomIn();break;case'-':pEvent.preventDefault();this._zoomOut();break;case'0':pEvent.preventDefault();this._zoomReset();break;case'z':pEvent.preventDefault();this._cycleFitMode();break;case'Enter':pEvent.preventDefault();this._openWithVLC();break;case'd':pEvent.preventDefault();this._toggleDistractionFree();break;}}/**
9287
+ if(tmpRemote.VideoMenuActive&&tmpRemote.CurrentViewerMediaType==='video'){switch(pEvent.key){case'Escape':pEvent.preventDefault();this.closeViewer();return;case'ArrowRight':case'j':pEvent.preventDefault();this.nextFile();return;case'ArrowLeft':case'k':pEvent.preventDefault();this.prevFile();return;case'e':pEvent.preventDefault();let tmpVEX=this.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.showExplorer(tmpRemote.CurrentViewerFile);}return;case' ':case'Enter':pEvent.preventDefault();let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.playVideo();}return;case't':pEvent.preventDefault();let tmpMediaViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpMediaViewer){tmpMediaViewer.loadVideoMenuFrame();}return;case'v':pEvent.preventDefault();this._streamWithVLC();return;}return;}switch(pEvent.key){case'Escape':pEvent.preventDefault();this.closeViewer();break;case'ArrowRight':case'j':pEvent.preventDefault();this.nextFile();break;case'ArrowLeft':case'k':pEvent.preventDefault();this.prevFile();break;case'f':pEvent.preventDefault();this._toggleFullscreen();break;case'i':pEvent.preventDefault();this._toggleFileInfo();break;case' ':pEvent.preventDefault();this._togglePlayPause();break;case'+':case'=':pEvent.preventDefault();this._zoomIn();break;case'-':pEvent.preventDefault();this._zoomOut();break;case'0':pEvent.preventDefault();this._zoomReset();break;case'z':pEvent.preventDefault();this._cycleFitMode();break;case'Enter':pEvent.preventDefault();this._streamWithVLC();break;case'v':pEvent.preventDefault();this._streamWithVLC();break;case'd':pEvent.preventDefault();this._toggleDistractionFree();break;case'1':pEvent.preventDefault();this.switchViewerType('image');break;case'2':pEvent.preventDefault();this.switchViewerType('video');break;case'3':pEvent.preventDefault();this.switchViewerType('audio');break;case'4':pEvent.preventDefault();this.switchViewerType('text');break;}}/**
8773
9288
  * Handle keyboard events in video explorer mode.
8774
9289
  */_handleVideoExplorerKey(pEvent){switch(pEvent.key){case'Escape':pEvent.preventDefault();let tmpVEX=this.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.goBack();}break;}}/**
8775
9290
  * Handle keyboard events in audio explorer mode.
@@ -8785,6 +9300,15 @@ tmpNewTile.scrollIntoView({block:'nearest',behavior:'smooth'});}}/**
8785
9300
  let tmpCurrentLocation=this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation||'';tmpRemote.FolderCursorHistory[tmpCurrentLocation]=tmpIndex;// Navigate into the folder or archive
8786
9301
  let tmpApp=this.pict.PictApplication;if(tmpApp&&tmpApp.loadFileList){tmpApp.loadFileList(tmpItem.Path);}}else{// Open the file in the viewer
8787
9302
  let tmpApp=this.pict.PictApplication;if(tmpApp&&tmpApp.navigateToFile){tmpApp.navigateToFile(tmpItem.Path);}}}/**
9303
+ * Open the currently selected gallery item, forcing a specific media type
9304
+ * regardless of its file extension.
9305
+ *
9306
+ * @param {string} pMediaType - 'image', 'video', 'audio', or 'text'
9307
+ */openCurrentAs(pMediaType){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpItems=tmpRemote.GalleryItems||[];let tmpIndex=tmpRemote.GalleryCursorIndex||0;if(tmpIndex>=tmpItems.length){return;}let tmpItem=tmpItems[tmpIndex];if(tmpItem.Type==='folder'||tmpItem.Type==='archive'){return;}let tmpApp=this.pict.PictApplication;if(tmpApp&&tmpApp.navigateToFileAs){tmpApp.navigateToFileAs(tmpItem.Path,pMediaType);}}/**
9308
+ * Re-open the currently viewed file with a different media type.
9309
+ *
9310
+ * @param {string} pMediaType - 'image', 'video', 'audio', or 'text'
9311
+ */switchViewerType(pMediaType){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilePath=tmpRemote.CurrentViewerFile;if(!tmpFilePath){return;}let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(tmpFilePath,pMediaType);}}/**
8788
9312
  * Navigate up one directory level.
8789
9313
  */navigateUp(){let tmpCurrentLocation=this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation||'';if(!tmpCurrentLocation){return;}// Remember cursor position in the current folder before navigating away
8790
9314
  let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.FolderCursorHistory[tmpCurrentLocation]=tmpRemote.GalleryCursorIndex||0;let tmpParent=tmpCurrentLocation.indexOf('/')>=0?tmpCurrentLocation.replace(/\/[^/]+\/?$/,''):'';let tmpApp=this.pict.PictApplication;if(tmpApp&&tmpApp.loadFileList){tmpApp.loadFileList(tmpParent);}}/**
@@ -8864,6 +9388,12 @@ if(tmpRemote.CurrentViewerMediaType!=='video'){return;}// Check if VLC is availa
8864
9388
  let tmpCapabilities=tmpRemote.ServerCapabilities||{};if(!tmpCapabilities.vlc){return;}let tmpFilePath=tmpRemote.CurrentViewerFile;if(!tmpFilePath){return;}// Show a brief toast
8865
9389
  this._showToast('Opening in VLC...');// POST to the server to open the file
8866
9390
  fetch('/api/media/open',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({path:tmpFilePath})}).then(pResponse=>{return pResponse.json();}).then(pData=>{if(!pData.Success){this._showToast('Failed to open: '+(pData.Error||'Unknown error'));}}).catch(pError=>{this._showToast('Failed to open: '+pError.message);});}/**
9391
+ * Stream the current media file to VLC on the client device via vlc:// protocol link.
9392
+ */_streamWithVLC(){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpMediaType=tmpRemote.CurrentViewerMediaType;if(tmpMediaType!=='video'&&tmpMediaType!=='audio'){return;}let tmpFilePath=tmpRemote.CurrentViewerFile;if(!tmpFilePath){return;}let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpContentPath=tmpProvider?tmpProvider.getContentURL(tmpFilePath):'/content/'+encodeURIComponent(tmpFilePath);let tmpStreamURL=window.location.origin+tmpContentPath;// On Windows, VLC's native handler expects the raw URL.
9393
+ // On macOS/Linux our custom handlers URL-decode, so we encode.
9394
+ let tmpIsWindows=/Windows/.test(navigator.userAgent);let tmpVLCURL=tmpIsWindows?'vlc://'+tmpStreamURL:'vlc://'+encodeURIComponent(tmpStreamURL);this._showToast('Opening VLC...');// Use a temporary anchor element to trigger the protocol handler
9395
+ // without navigating the current page away
9396
+ let tmpLink=document.createElement('a');tmpLink.href=tmpVLCURL;tmpLink.style.display='none';document.body.appendChild(tmpLink);tmpLink.click();document.body.removeChild(tmpLink);}/**
8867
9397
  * Show a brief toast notification in the viewer.
8868
9398
  *
8869
9399
  * @param {string} pMessage - Text to display
@@ -9089,7 +9619,7 @@ return this.getIcon('file',tmpSize);}/**
9089
9619
  * @returns {Object} Extension-to-icon name map
9090
9620
  */getExtensionMap(){return Object.assign({},this._extensionMap);}/**
9091
9621
  * Inject CSS classes for icon sizing into the pict CSSMap.
9092
- */injectCSS(){if(this._cssInjected){return;}if(this.pict&&this.pict.CSSMap){this.pict.CSSMap.addCSS('RetoldRemoteIcons','.retold-remote-icon { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; }\n'+'.retold-remote-icon svg { display: block; }\n'+'.retold-remote-icon-sm svg { width: 16px; height: 16px; }\n'+'.retold-remote-icon-md svg { width: 48px; height: 48px; }\n'+'.retold-remote-icon-lg svg { width: 64px; height: 64px; }\n'+'.retold-remote-icon-xl svg { width: 96px; height: 96px; }\n');this._cssInjected=true;}}onAfterInitialize(){this.injectCSS();return super.onAfterInitialize();}}module.exports=RetoldRemoteIconProvider;module.exports.default_configuration=_DefaultProviderConfiguration;module.exports.DefaultColors=_DefaultColors;},{"pict-provider":46,"pict-section-filebrowser/source/providers/Pict-Provider-FileBrowserIcons.js":56}],121:[function(require,module,exports){const libPictProvider=require('pict-provider');const _ProviderConfiguration={ProviderIdentifier:'RetoldRemote-Theme',AutoInitialize:true,AutoInitializeOrdinal:0};/**
9622
+ */injectCSS(){if(this._cssInjected){return;}if(this.pict&&this.pict.CSSMap){this.pict.CSSMap.addCSS('RetoldRemoteIcons','.retold-remote-icon { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; }\n'+'.retold-remote-icon svg { display: block; }\n'+'.retold-remote-icon-sm svg { width: 16px; height: 16px; }\n'+'.retold-remote-icon-md svg { width: 48px; height: 48px; }\n'+'.retold-remote-icon-lg svg { width: 64px; height: 64px; }\n'+'.retold-remote-icon-xl svg { width: 96px; height: 96px; }\n');this._cssInjected=true;}}onAfterInitialize(){this.injectCSS();return super.onAfterInitialize();}}module.exports=RetoldRemoteIconProvider;module.exports.default_configuration=_DefaultProviderConfiguration;module.exports.DefaultColors=_DefaultColors;},{"pict-provider":46,"pict-section-filebrowser/source/providers/Pict-Provider-FileBrowserIcons.js":53}],121:[function(require,module,exports){const libPictProvider=require('pict-provider');const _ProviderConfiguration={ProviderIdentifier:'RetoldRemote-Theme',AutoInitialize:true,AutoInitializeOrdinal:0};/**
9093
9623
  * Theme provider for retold-remote.
9094
9624
  *
9095
9625
  * Manages 15 themes (5 grey-only + 10 fun) via CSS custom properties.
@@ -10968,19 +11498,21 @@ if(tmpCapabilities.ffmpeg){tmpHTML+='<div id="RetoldRemote-VideoActionThumb" cla
10968
11498
  setTimeout(()=>{this.loadVideoMenuFrame();},0);}// Explore option (e)
10969
11499
  if(tmpCapabilities.ffmpeg){tmpHTML+='<button class="retold-remote-video-action-btn" '+'onclick="pict.views[\'RetoldRemote-VideoExplorer\'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile)" '+'title="Explore frames from this video">'+'<span class="retold-remote-video-action-key">e</span>'+'Explore Video Frames'+'</button>';}// Play option (space/enter)
10970
11500
  tmpHTML+='<button class="retold-remote-video-action-btn selected" '+'onclick="pict.views[\'RetoldRemote-MediaViewer\'].playVideo()" '+'title="Play video in browser">'+'<span class="retold-remote-video-action-key">Space</span>'+'Play in Browser'+'</button>';// Thumbnail option (t)
10971
- if(tmpCapabilities.ffmpeg){tmpHTML+='<button class="retold-remote-video-action-btn" '+'onclick="pict.views[\'RetoldRemote-MediaViewer\'].loadVideoMenuFrame()" '+'title="Extract a frame from the midpoint of this video">'+'<span class="retold-remote-video-action-key">t</span>'+'Thumbnail'+'</button>';}// VLC option (v)
10972
- if(tmpCapabilities.vlc){tmpHTML+='<button class="retold-remote-video-action-btn" '+'onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._openWithVLC()" '+'title="Open with VLC media player">'+'<span class="retold-remote-video-action-key">v</span>'+'Open with VLC'+'</button>';}tmpHTML+='</div>';return tmpHTML;}/**
11501
+ if(tmpCapabilities.ffmpeg){tmpHTML+='<button class="retold-remote-video-action-btn" '+'onclick="pict.views[\'RetoldRemote-MediaViewer\'].loadVideoMenuFrame()" '+'title="Extract a frame from the midpoint of this video">'+'<span class="retold-remote-video-action-key">t</span>'+'Thumbnail'+'</button>';}// VLC streaming option (v) — always available, streams from server to client VLC
11502
+ tmpHTML+='<button class="retold-remote-video-action-btn" '+'onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._streamWithVLC()" '+'title="Stream to VLC on this device">'+'<span class="retold-remote-video-action-key">v</span>'+'Stream with VLC'+'</button>';tmpHTML+='</div>';return tmpHTML;}/**
10973
11503
  * Launch the in-browser video player (from the video action menu).
10974
11504
  */playVideo(){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilePath=tmpRemote.CurrentViewerFile;if(!tmpFilePath)return;let tmpFileName=tmpFilePath.replace(/^.*\//,'');let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpContentURL=tmpProvider?tmpProvider.getContentURL(tmpFilePath):'/content/'+encodeURIComponent(tmpFilePath);let tmpCapabilities=tmpRemote.ServerCapabilities||{};let tmpHTML='<div class="retold-remote-video-wrap">';let tmpAutoplayVideo=tmpRemote.AutoplayVideo?' autoplay':'';tmpHTML+='<video controls'+tmpAutoplayVideo+' preload="metadata" '+'id="RetoldRemote-VideoPlayer">'+'<source src="'+tmpContentURL+'">'+'Your browser does not support the video tag.'+'</video>';// Stats bar below the video
10975
- tmpHTML+='<div class="retold-remote-video-stats" id="RetoldRemote-VideoStats">';tmpHTML+='<span class="retold-remote-video-stat-label">Loading info...</span>';if(tmpCapabilities.ffmpeg){tmpHTML+='<button class="retold-remote-explore-btn" '+'onclick="pict.views[\'RetoldRemote-VideoExplorer\'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile)" '+'title="Explore frames from this video">'+'Explore Video'+'</button>';}if(tmpCapabilities.vlc){tmpHTML+='<button class="retold-remote-vlc-btn" '+'onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._openWithVLC()" '+'title="Open with VLC">'+'Open with VLC'+'</button>';}tmpHTML+='</div>';// end stats
11505
+ tmpHTML+='<div class="retold-remote-video-stats" id="RetoldRemote-VideoStats">';tmpHTML+='<span class="retold-remote-video-stat-label">Loading info...</span>';if(tmpCapabilities.ffmpeg){tmpHTML+='<button class="retold-remote-explore-btn" '+'onclick="pict.views[\'RetoldRemote-VideoExplorer\'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile)" '+'title="Explore frames from this video">'+'Explore Video'+'</button>';}tmpHTML+='<button class="retold-remote-vlc-btn" '+'onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._streamWithVLC()" '+'title="Stream to VLC on this device">'+'Stream with VLC'+'</button>';tmpHTML+='</div>';// end stats
10976
11506
  tmpHTML+='</div>';// end wrap
10977
11507
  // Replace the action menu with the player
10978
11508
  let tmpMenu=document.getElementById('RetoldRemote-VideoActionMenu');if(tmpMenu){tmpMenu.outerHTML=tmpHTML;}// Mark that we are now in player mode (not menu mode)
10979
11509
  tmpRemote.VideoMenuActive=false;}/**
10980
11510
  * Extract and display a single full-resolution frame from the midpoint of the current video.
10981
11511
  */loadVideoMenuFrame(){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilePath=tmpRemote.CurrentViewerFile;if(!tmpFilePath)return;let tmpThumbWrap=document.getElementById('RetoldRemote-VideoActionThumb');if(!tmpThumbWrap)return;tmpThumbWrap.innerHTML='<div class="retold-remote-video-action-thumb-loading">Extracting frame...</div>';let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(tmpFilePath):encodeURIComponent(tmpFilePath);fetch('/api/media/video-frames?path='+tmpPathParam+'&count=1').then(pResponse=>pResponse.json()).then(pData=>{// Verify we are still on the same file and still in the menu
10982
- if(tmpRemote.CurrentViewerFile!==tmpFilePath)return;let tmpWrap=document.getElementById('RetoldRemote-VideoActionThumb');if(!tmpWrap)return;if(pData&&pData.Frames&&pData.Frames.length>0){let tmpFrame=pData.Frames[0];let tmpFrameURL='/api/media/video-frame/'+pData.CacheKey+'/'+tmpFrame.Filename;tmpWrap.innerHTML='<img src="'+tmpFrameURL+'" '+'alt="'+this._escapeHTML(tmpFilePath.replace(/^.*\//,''))+'" '+'onerror="this.parentNode.innerHTML=\'\'">';}else{tmpWrap.innerHTML='';}}).catch(()=>{let tmpWrap=document.getElementById('RetoldRemote-VideoActionThumb');if(tmpWrap)tmpWrap.innerHTML='';});}_buildAudioHTML(pURL,pFileName){let tmpIconProvider=this.pict.providers['RetoldRemote-Icons'];let tmpIconHTML=tmpIconProvider?'<span class="retold-remote-icon retold-remote-icon-lg">'+tmpIconProvider.getIcon('music-note',64)+'</span>':'&#127925;';let tmpHTML='<div style="text-align: center; padding: 40px;">'+'<div style="margin-bottom: 24px;">'+tmpIconHTML+'</div>'+'<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">'+this._escapeHTML(pFileName)+'</div>'+'<audio controls'+(this.pict.AppData.RetoldRemote.AutoplayAudio?' autoplay':'')+' preload="metadata" id="RetoldRemote-AudioPlayer" style="width: 100%; max-width: 500px;">'+'<source src="'+pURL+'">'+'Your browser does not support the audio tag.'+'</audio>';// Explore Audio button (available when ffprobe is present)
10983
- let tmpCapabilities=this.pict.AppData.RetoldRemote.ServerCapabilities||{};if(tmpCapabilities.ffprobe||tmpCapabilities.ffmpeg){tmpHTML+='<div style="margin-top: 20px;">'+'<button class="retold-remote-explore-btn" '+'onclick="pict.views[\'RetoldRemote-AudioExplorer\'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile)" '+'title="Explore waveform and extract segments from this audio">'+'&#128202; Explore Audio'+'</button>'+'</div>';}tmpHTML+='</div>';return tmpHTML;}_buildDocumentHTML(pURL,pFileName,pFilePath){let tmpExtension=pFilePath.replace(/^.*\./,'').toLowerCase();if(tmpExtension==='pdf'){return'<iframe src="'+pURL+'" '+'style="width: 100%; height: 100%; border: none;">'+'</iframe>';}if(tmpExtension==='epub'||tmpExtension==='mobi'){return this._buildEbookHTML(pURL,pFileName,pFilePath);}// For other document types, show a download link
11512
+ if(tmpRemote.CurrentViewerFile!==tmpFilePath)return;let tmpWrap=document.getElementById('RetoldRemote-VideoActionThumb');if(!tmpWrap)return;if(pData&&pData.Frames&&pData.Frames.length>0){let tmpFrame=pData.Frames[0];let tmpFrameURL='/api/media/video-frame/'+pData.CacheKey+'/'+tmpFrame.Filename;tmpWrap.innerHTML='<img src="'+tmpFrameURL+'" '+'alt="'+this._escapeHTML(tmpFilePath.replace(/^.*\//,''))+'" '+'onerror="this.parentNode.innerHTML=\'\'">';}else{tmpWrap.innerHTML='';}}).catch(()=>{let tmpWrap=document.getElementById('RetoldRemote-VideoActionThumb');if(tmpWrap)tmpWrap.innerHTML='';});}_buildAudioHTML(pURL,pFileName){let tmpIconProvider=this.pict.providers['RetoldRemote-Icons'];let tmpIconHTML=tmpIconProvider?'<span class="retold-remote-icon retold-remote-icon-lg">'+tmpIconProvider.getIcon('music-note',64)+'</span>':'&#127925;';let tmpHTML='<div style="text-align: center; padding: 40px;">'+'<div style="margin-bottom: 24px;">'+tmpIconHTML+'</div>'+'<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">'+this._escapeHTML(pFileName)+'</div>'+'<audio controls'+(this.pict.AppData.RetoldRemote.AutoplayAudio?' autoplay':'')+' preload="metadata" id="RetoldRemote-AudioPlayer" style="width: 100%; max-width: 500px;">'+'<source src="'+pURL+'">'+'Your browser does not support the audio tag.'+'</audio>';// Action buttons below the player
11513
+ tmpHTML+='<div style="margin-top: 20px; display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;">';// Explore Audio button (available when ffprobe is present)
11514
+ let tmpCapabilities=this.pict.AppData.RetoldRemote.ServerCapabilities||{};if(tmpCapabilities.ffprobe||tmpCapabilities.ffmpeg){tmpHTML+='<button class="retold-remote-explore-btn" '+'onclick="pict.views[\'RetoldRemote-AudioExplorer\'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile)" '+'title="Explore waveform and extract segments from this audio">'+'Explore Audio'+'</button>';}// Stream with VLC
11515
+ tmpHTML+='<button class="retold-remote-vlc-btn" '+'onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._streamWithVLC()" '+'title="Stream to VLC on this device (v)">'+'Stream with VLC'+'</button>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildDocumentHTML(pURL,pFileName,pFilePath){let tmpExtension=pFilePath.replace(/^.*\./,'').toLowerCase();if(tmpExtension==='pdf'){return'<iframe src="'+pURL+'" '+'style="width: 100%; height: 100%; border: none;">'+'</iframe>';}if(tmpExtension==='epub'||tmpExtension==='mobi'){return this._buildEbookHTML(pURL,pFileName,pFilePath);}// For other document types, show a download link
10984
11516
  let tmpIconProvider=this.pict.providers['RetoldRemote-Icons'];let tmpDocIconHTML=tmpIconProvider?'<span class="retold-remote-icon retold-remote-icon-lg">'+tmpIconProvider.getIcon('document-large',64)+'</span>':'&#128196;';return'<div style="text-align: center; padding: 40px;">'+'<div style="margin-bottom: 24px;">'+tmpDocIconHTML+'</div>'+'<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">'+this._escapeHTML(pFileName)+'</div>'+'<a href="'+pURL+'" target="_blank" style="color: var(--retold-accent); font-size: 0.9rem;">Open in new tab</a>'+'</div>';}_buildTextHTML(pURL,pFileName,pFilePath){return'<div class="retold-remote-code-viewer-container" id="RetoldRemote-CodeViewer-Container">'+'<div class="retold-remote-code-viewer-loading">Loading...</div>'+'</div>';}/**
10985
11517
  * Map a file extension to a pict-section-code highlight language.
10986
11518
  *
@@ -11112,6 +11644,26 @@ let tmpExploreBtn=tmpStatsBar.querySelector('.retold-remote-explore-btn');let tm
11112
11644
  {
11113
11645
  color: var(--retold-danger-muted);
11114
11646
  }
11647
+ .retold-remote-settings-vlc-btn
11648
+ {
11649
+ display: block;
11650
+ width: 100%;
11651
+ padding: 8px 12px;
11652
+ border: 1px solid var(--retold-border);
11653
+ border-radius: 4px;
11654
+ background: var(--retold-bg-secondary);
11655
+ color: var(--retold-text-secondary);
11656
+ font-size: 0.75rem;
11657
+ font-family: inherit;
11658
+ cursor: pointer;
11659
+ text-align: left;
11660
+ transition: background 0.15s, color 0.15s;
11661
+ }
11662
+ .retold-remote-settings-vlc-btn:hover
11663
+ {
11664
+ background: var(--retold-bg-hover);
11665
+ color: var(--retold-text-primary);
11666
+ }
11115
11667
  .retold-remote-settings-shortcut-group
11116
11668
  {
11117
11669
  margin-bottom: 10px;
@@ -11163,8 +11715,9 @@ tmpHTML+='<div class="retold-remote-settings-row">';tmpHTML+='<span class="retol
11163
11715
  // Server capabilities
11164
11716
  tmpHTML+='<div class="retold-remote-settings-section">';tmpHTML+='<div class="retold-remote-settings-section-title">Server Capabilities</div>';tmpHTML+='<div class="retold-remote-settings-capabilities">';let tmpTools=[{key:'sharp',label:'Sharp (image thumbnails)'},{key:'imagemagick',label:'ImageMagick (image fallback)'},{key:'ffmpeg',label:'ffmpeg (video thumbnails)'},{key:'ffprobe',label:'ffprobe (media metadata)'}];for(let i=0;i<tmpTools.length;i++){let tmpTool=tmpTools[i];let tmpAvailable=tmpCapabilities[tmpTool.key];tmpHTML+='<div class="retold-remote-settings-cap-row">';tmpHTML+='<span class="retold-remote-settings-cap-label">'+tmpTool.label+'</span>';tmpHTML+='<span class="'+(tmpAvailable?'retold-remote-settings-cap-yes':'retold-remote-settings-cap-no')+'">'+(tmpAvailable?'Available':'Not found')+'</span>';tmpHTML+='</div>';}// Hashed filenames status
11165
11717
  tmpHTML+='<div class="retold-remote-settings-cap-row" style="margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--retold-border);">';tmpHTML+='<span class="retold-remote-settings-cap-label">Hashed filenames</span>';tmpHTML+='<span class="'+(tmpRemote.HashedFilenames?'retold-remote-settings-cap-yes':'retold-remote-settings-cap-no')+'">'+(tmpRemote.HashedFilenames?'Enabled':'Disabled')+'</span>';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='</div>';// end capabilities section
11166
- // Keyboard shortcuts
11167
- tmpHTML+='<div class="retold-remote-settings-section">';tmpHTML+='<div class="retold-remote-settings-section-title">Keyboard Shortcuts</div>';tmpHTML+=this._buildShortcutGroup('Global',[{key:'F1',desc:'Help panel'},{key:'F9',desc:'Focus sidebar'},{key:'/',desc:'Search / filter bar'},{key:'Esc',desc:'Close overlay / back'}]);tmpHTML+=this._buildShortcutGroup('Gallery',[{key:'\u2190 \u2191 \u2192 \u2193',desc:'Navigate items'},{key:'Enter',desc:'Open item'},{key:'Esc',desc:'Go up one folder'},{key:'Home',desc:'Jump to first item'},{key:'End',desc:'Jump to last item'},{key:'g',desc:'Toggle grid / list'},{key:'f',desc:'Advanced filter panel'},{key:'s',desc:'Focus sort dropdown'},{key:'x',desc:'Clear all filters'},{key:'c',desc:'Settings panel'},{key:'d',desc:'Distraction-free mode'}]);tmpHTML+=this._buildShortcutGroup('Sidebar (F9)',[{key:'\u2191 \u2193',desc:'Navigate file list'},{key:'Home',desc:'Jump to first'},{key:'End',desc:'Jump to last'},{key:'Enter',desc:'Open item'},{key:'Esc',desc:'Return to gallery'}]);tmpHTML+=this._buildShortcutGroup('Media Viewer',[{key:'Esc',desc:'Back to gallery'},{key:'\u2192 j',desc:'Next file'},{key:'\u2190 k',desc:'Previous file'},{key:'Space',desc:'Play / pause'},{key:'f',desc:'Fullscreen'},{key:'i',desc:'File info overlay'},{key:'+ -',desc:'Zoom in / out'},{key:'0',desc:'Reset zoom'},{key:'z',desc:'Cycle fit mode'},{key:'d',desc:'Distraction-free mode'}]);tmpHTML+=this._buildShortcutGroup('Video Menu',[{key:'Space',desc:'Play in browser'},{key:'Enter',desc:'Play in browser'},{key:'e',desc:'Explore video frames'},{key:'t',desc:'Extract thumbnail'},{key:'v',desc:'Open with VLC'},{key:'\u2192 j',desc:'Next file'},{key:'\u2190 k',desc:'Previous file'},{key:'Esc',desc:'Back to gallery'}]);tmpHTML+=this._buildShortcutGroup('Video Explorer',[{key:'Esc',desc:'Back'}]);tmpHTML+=this._buildShortcutGroup('Audio Explorer',[{key:'Space',desc:'Play selection'},{key:'+ -',desc:'Zoom in / out'},{key:'0',desc:'Zoom to fit'},{key:'z',desc:'Zoom to selection'},{key:'Esc',desc:'Clear selection / back'}]);tmpHTML+='</div>';// end shortcuts section
11718
+ // VLC Setup
11719
+ tmpHTML+='<div class="retold-remote-settings-section">';tmpHTML+='<div class="retold-remote-settings-section-title">VLC Streaming</div>';tmpHTML+='<button class="retold-remote-settings-vlc-btn" onclick="pict.views[\'RetoldRemote-VLCSetup\'].openModal()">';tmpHTML+='VLC Protocol Setup';tmpHTML+='</button>';tmpHTML+='</div>';// Keyboard shortcuts
11720
+ tmpHTML+='<div class="retold-remote-settings-section">';tmpHTML+='<div class="retold-remote-settings-section-title">Keyboard Shortcuts</div>';tmpHTML+=this._buildShortcutGroup('Global',[{key:'F1',desc:'Help panel'},{key:'F9',desc:'Focus sidebar'},{key:'/',desc:'Search / filter bar'},{key:'Esc',desc:'Close overlay / back'}]);tmpHTML+=this._buildShortcutGroup('Gallery',[{key:'\u2190 \u2191 \u2192 \u2193',desc:'Navigate items'},{key:'Enter',desc:'Open item'},{key:'1 2 3 4',desc:'Open as image / video / audio / text'},{key:'Esc',desc:'Go up one folder'},{key:'Home',desc:'Jump to first item'},{key:'End',desc:'Jump to last item'},{key:'g',desc:'Toggle grid / list'},{key:'f',desc:'Advanced filter panel'},{key:'s',desc:'Focus sort dropdown'},{key:'x',desc:'Clear all filters'},{key:'c',desc:'Settings panel'},{key:'d',desc:'Distraction-free mode'}]);tmpHTML+=this._buildShortcutGroup('Sidebar (F9)',[{key:'\u2191 \u2193',desc:'Navigate file list'},{key:'Home',desc:'Jump to first'},{key:'End',desc:'Jump to last'},{key:'Enter',desc:'Open item'},{key:'Esc',desc:'Return to gallery'}]);tmpHTML+=this._buildShortcutGroup('Media Viewer',[{key:'Esc',desc:'Back to gallery'},{key:'\u2192 j',desc:'Next file'},{key:'\u2190 k',desc:'Previous file'},{key:'1 2 3 4',desc:'View as image / video / audio / text'},{key:'Space',desc:'Play / pause'},{key:'f',desc:'Fullscreen'},{key:'i',desc:'File info overlay'},{key:'v',desc:'Stream with VLC'},{key:'+ -',desc:'Zoom in / out'},{key:'0',desc:'Reset zoom'},{key:'z',desc:'Cycle fit mode'},{key:'d',desc:'Distraction-free mode'}]);tmpHTML+=this._buildShortcutGroup('Video Menu',[{key:'Space',desc:'Play in browser'},{key:'Enter',desc:'Play in browser'},{key:'e',desc:'Explore video frames'},{key:'t',desc:'Extract thumbnail'},{key:'v',desc:'Stream with VLC'},{key:'\u2192 j',desc:'Next file'},{key:'\u2190 k',desc:'Previous file'},{key:'Esc',desc:'Back to gallery'}]);tmpHTML+=this._buildShortcutGroup('Video Explorer',[{key:'Esc',desc:'Back'}]);tmpHTML+=this._buildShortcutGroup('Audio Explorer',[{key:'Space',desc:'Play selection'},{key:'+ -',desc:'Zoom in / out'},{key:'0',desc:'Zoom to fit'},{key:'z',desc:'Zoom to selection'},{key:'Esc',desc:'Clear selection / back'}]);tmpHTML+='</div>';// end shortcuts section
11168
11721
  tmpHTML+='</div>';// end settings
11169
11722
  tmpContainer.innerHTML=tmpHTML;}_buildShortcutGroup(pTitle,pShortcuts){let tmpHTML='<div class="retold-remote-settings-shortcut-group">';tmpHTML+='<div class="retold-remote-settings-shortcut-group-title">'+pTitle+'</div>';for(let i=0;i<pShortcuts.length;i++){tmpHTML+='<div class="retold-remote-settings-shortcut-row">';tmpHTML+='<span class="retold-remote-settings-shortcut-desc">'+pShortcuts[i].desc+'</span>';tmpHTML+='<span class="retold-remote-settings-shortcut-key">'+pShortcuts[i].key+'</span>';tmpHTML+='</div>';}tmpHTML+='</div>';return tmpHTML;}changeTheme(pThemeKey){let tmpThemeProvider=this.pict.providers['RetoldRemote-Theme'];if(tmpThemeProvider){tmpThemeProvider.applyTheme(pThemeKey);this.pict.PictApplication.saveSettings();// Re-render settings to update dropdown selection
11170
11723
  this._renderSettingsContent();}}changeSetting(pKey,pValue){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote[pKey]=pValue;this.pict.PictApplication.saveSettings();// Re-render gallery if visible
@@ -11313,7 +11866,259 @@ tmpBtn.classList.add('filter-active');tmpBtn.innerHTML='&#9683;<span class="reto
11313
11866
  tmpBtn.classList.add('filter-bar-open');tmpBtn.innerHTML='&#9698;';tmpBtn.title='Hide filter bar (/)';}else{// Default: no filters, bar hidden
11314
11867
  tmpBtn.innerHTML='&#9698;';tmpBtn.title='Toggle filter bar (/)';}}/**
11315
11868
  * Update the info display with folder summary.
11316
- */updateInfo(){let tmpInfoEl=document.getElementById('RetoldRemote-TopBar-Info');if(!tmpInfoEl){return;}let tmpRemote=this.pict.AppData.RetoldRemote;let tmpSummary=tmpRemote.FolderSummary;if(tmpRemote.ActiveMode==='viewer'){let tmpItems=tmpRemote.GalleryItems||[];let tmpIndex=tmpRemote.GalleryCursorIndex||0;let tmpItem=tmpItems[tmpIndex];if(tmpItem){tmpInfoEl.textContent=tmpItem.Name;}return;}if(!tmpSummary){tmpInfoEl.textContent='';return;}let tmpParts=[];if(tmpSummary.Folders>0)tmpParts.push(tmpSummary.Folders+' folders');if(tmpSummary.Images>0)tmpParts.push(tmpSummary.Images+' images');if(tmpSummary.Videos>0)tmpParts.push(tmpSummary.Videos+' videos');if(tmpSummary.Audio>0)tmpParts.push(tmpSummary.Audio+' audio');if(tmpSummary.Documents>0)tmpParts.push(tmpSummary.Documents+' docs');if(tmpSummary.Other>0)tmpParts.push(tmpSummary.Other+' other');tmpInfoEl.textContent=tmpParts.join(' \u00b7 ');}}RetoldRemoteTopBarView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteTopBarView;},{"pict-view":76}],129:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VideoExplorer",DefaultRenderable:"RetoldRemote-VideoExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:/*css*/`
11869
+ */updateInfo(){let tmpInfoEl=document.getElementById('RetoldRemote-TopBar-Info');if(!tmpInfoEl){return;}let tmpRemote=this.pict.AppData.RetoldRemote;let tmpSummary=tmpRemote.FolderSummary;if(tmpRemote.ActiveMode==='viewer'){let tmpItems=tmpRemote.GalleryItems||[];let tmpIndex=tmpRemote.GalleryCursorIndex||0;let tmpItem=tmpItems[tmpIndex];if(tmpItem){tmpInfoEl.textContent=tmpItem.Name;}return;}if(!tmpSummary){tmpInfoEl.textContent='';return;}let tmpParts=[];if(tmpSummary.Folders>0)tmpParts.push(tmpSummary.Folders+' folders');if(tmpSummary.Images>0)tmpParts.push(tmpSummary.Images+' images');if(tmpSummary.Videos>0)tmpParts.push(tmpSummary.Videos+' videos');if(tmpSummary.Audio>0)tmpParts.push(tmpSummary.Audio+' audio');if(tmpSummary.Documents>0)tmpParts.push(tmpSummary.Documents+' docs');if(tmpSummary.Other>0)tmpParts.push(tmpSummary.Other+' other');tmpInfoEl.textContent=tmpParts.join(' \u00b7 ');}}RetoldRemoteTopBarView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteTopBarView;},{"pict-view":76}],129:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VLCSetup",DefaultRenderable:"RetoldRemote-VLCSetup",DefaultDestinationAddress:"#ContentEditor-Application-Container",AutoRender:false,CSS:/*css*/`
11870
+ .retold-remote-vlc-modal-backdrop
11871
+ {
11872
+ position: fixed;
11873
+ top: 0;
11874
+ left: 0;
11875
+ right: 0;
11876
+ bottom: 0;
11877
+ background: rgba(0, 0, 0, 0.6);
11878
+ z-index: 9000;
11879
+ display: flex;
11880
+ align-items: center;
11881
+ justify-content: center;
11882
+ }
11883
+ .retold-remote-vlc-modal
11884
+ {
11885
+ background: var(--retold-bg-tertiary);
11886
+ border: 1px solid var(--retold-border);
11887
+ border-radius: 8px;
11888
+ width: 600px;
11889
+ max-width: 90vw;
11890
+ max-height: 85vh;
11891
+ display: flex;
11892
+ flex-direction: column;
11893
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
11894
+ }
11895
+ .retold-remote-vlc-modal-header
11896
+ {
11897
+ display: flex;
11898
+ align-items: center;
11899
+ justify-content: space-between;
11900
+ padding: 14px 18px;
11901
+ border-bottom: 1px solid var(--retold-border);
11902
+ flex-shrink: 0;
11903
+ }
11904
+ .retold-remote-vlc-modal-title
11905
+ {
11906
+ font-size: 0.85rem;
11907
+ font-weight: 700;
11908
+ color: var(--retold-text-primary);
11909
+ }
11910
+ .retold-remote-vlc-modal-close
11911
+ {
11912
+ border: none;
11913
+ background: transparent;
11914
+ color: var(--retold-text-muted);
11915
+ font-size: 1.1rem;
11916
+ cursor: pointer;
11917
+ padding: 2px 6px;
11918
+ border-radius: 3px;
11919
+ font-family: inherit;
11920
+ line-height: 1;
11921
+ }
11922
+ .retold-remote-vlc-modal-close:hover
11923
+ {
11924
+ background: var(--retold-bg-hover);
11925
+ color: var(--retold-text-primary);
11926
+ }
11927
+ .retold-remote-vlc-modal-body
11928
+ {
11929
+ flex: 1;
11930
+ overflow-y: auto;
11931
+ padding: 18px;
11932
+ }
11933
+ .retold-remote-vlc-setup-section
11934
+ {
11935
+ margin-bottom: 18px;
11936
+ }
11937
+ .retold-remote-vlc-setup-section-title
11938
+ {
11939
+ font-size: 0.7rem;
11940
+ font-weight: 700;
11941
+ text-transform: uppercase;
11942
+ letter-spacing: 0.5px;
11943
+ color: var(--retold-text-dim);
11944
+ margin-bottom: 8px;
11945
+ }
11946
+ .retold-remote-vlc-setup-desc
11947
+ {
11948
+ font-size: 0.75rem;
11949
+ color: var(--retold-text-secondary);
11950
+ line-height: 1.5;
11951
+ margin-bottom: 8px;
11952
+ }
11953
+ .retold-remote-vlc-setup-status
11954
+ {
11955
+ display: flex;
11956
+ align-items: center;
11957
+ gap: 8px;
11958
+ padding: 8px;
11959
+ border-radius: 4px;
11960
+ background: var(--retold-bg-secondary);
11961
+ margin-bottom: 12px;
11962
+ font-size: 0.75rem;
11963
+ color: var(--retold-text-secondary);
11964
+ }
11965
+ .retold-remote-vlc-setup-status-dot
11966
+ {
11967
+ width: 8px;
11968
+ height: 8px;
11969
+ border-radius: 50%;
11970
+ flex-shrink: 0;
11971
+ }
11972
+ .retold-remote-vlc-setup-status-dot.detected
11973
+ {
11974
+ background: var(--retold-accent);
11975
+ }
11976
+ .retold-remote-vlc-setup-status-dot.unknown
11977
+ {
11978
+ background: var(--retold-text-dim);
11979
+ }
11980
+ .retold-remote-vlc-setup-platform
11981
+ {
11982
+ display: none;
11983
+ }
11984
+ .retold-remote-vlc-setup-platform.active
11985
+ {
11986
+ display: block;
11987
+ }
11988
+ .retold-remote-vlc-setup-platform-tabs
11989
+ {
11990
+ display: flex;
11991
+ gap: 0;
11992
+ margin-bottom: 12px;
11993
+ border-bottom: 1px solid var(--retold-border);
11994
+ }
11995
+ .retold-remote-vlc-setup-platform-tab
11996
+ {
11997
+ padding: 6px 12px;
11998
+ border: none;
11999
+ background: transparent;
12000
+ font-size: 0.72rem;
12001
+ font-weight: 600;
12002
+ color: var(--retold-text-muted);
12003
+ cursor: pointer;
12004
+ border-bottom: 2px solid transparent;
12005
+ font-family: inherit;
12006
+ }
12007
+ .retold-remote-vlc-setup-platform-tab:hover
12008
+ {
12009
+ color: var(--retold-text-secondary);
12010
+ }
12011
+ .retold-remote-vlc-setup-platform-tab.active
12012
+ {
12013
+ color: var(--retold-accent);
12014
+ border-bottom-color: var(--retold-accent);
12015
+ }
12016
+ .retold-remote-vlc-setup-code
12017
+ {
12018
+ background: var(--retold-bg-primary);
12019
+ border: 1px solid var(--retold-border);
12020
+ border-radius: 4px;
12021
+ padding: 10px;
12022
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
12023
+ font-size: 0.68rem;
12024
+ color: var(--retold-text-secondary);
12025
+ line-height: 1.6;
12026
+ overflow-x: auto;
12027
+ white-space: pre;
12028
+ margin-bottom: 8px;
12029
+ tab-size: 4;
12030
+ }
12031
+ .retold-remote-vlc-setup-btn
12032
+ {
12033
+ display: inline-block;
12034
+ padding: 6px 14px;
12035
+ border: 1px solid var(--retold-border);
12036
+ border-radius: 4px;
12037
+ background: var(--retold-bg-secondary);
12038
+ color: var(--retold-text-secondary);
12039
+ font-size: 0.72rem;
12040
+ font-family: inherit;
12041
+ cursor: pointer;
12042
+ transition: background 0.15s, color 0.15s;
12043
+ margin-right: 6px;
12044
+ margin-bottom: 6px;
12045
+ }
12046
+ .retold-remote-vlc-setup-btn:hover
12047
+ {
12048
+ background: var(--retold-bg-hover);
12049
+ color: var(--retold-text-primary);
12050
+ }
12051
+ .retold-remote-vlc-setup-btn.primary
12052
+ {
12053
+ background: var(--retold-accent);
12054
+ border-color: var(--retold-accent);
12055
+ color: #fff;
12056
+ }
12057
+ .retold-remote-vlc-setup-btn.primary:hover
12058
+ {
12059
+ opacity: 0.85;
12060
+ }
12061
+ .retold-remote-vlc-setup-step
12062
+ {
12063
+ display: flex;
12064
+ gap: 10px;
12065
+ margin-bottom: 10px;
12066
+ }
12067
+ .retold-remote-vlc-setup-step-num
12068
+ {
12069
+ flex-shrink: 0;
12070
+ width: 20px;
12071
+ height: 20px;
12072
+ border-radius: 50%;
12073
+ background: var(--retold-accent);
12074
+ color: #fff;
12075
+ font-size: 0.65rem;
12076
+ font-weight: 700;
12077
+ display: flex;
12078
+ align-items: center;
12079
+ justify-content: center;
12080
+ }
12081
+ .retold-remote-vlc-setup-step-content
12082
+ {
12083
+ flex: 1;
12084
+ font-size: 0.75rem;
12085
+ color: var(--retold-text-secondary);
12086
+ line-height: 1.5;
12087
+ }
12088
+ .retold-remote-vlc-setup-note
12089
+ {
12090
+ font-size: 0.7rem;
12091
+ color: var(--retold-text-dim);
12092
+ font-style: italic;
12093
+ margin-top: 4px;
12094
+ }
12095
+ .retold-remote-vlc-setup-toast
12096
+ {
12097
+ position: fixed;
12098
+ bottom: 20px;
12099
+ left: 50%;
12100
+ transform: translateX(-50%);
12101
+ background: var(--retold-bg-secondary);
12102
+ color: var(--retold-accent);
12103
+ padding: 8px 16px;
12104
+ border-radius: 4px;
12105
+ font-size: 0.75rem;
12106
+ z-index: 10000;
12107
+ pointer-events: none;
12108
+ border: 1px solid var(--retold-border);
12109
+ }
12110
+ `};class RetoldRemoteVLCSetupView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._activePlatformTab=this._detectPlatform();this._modalVisible=false;this._boundKeyHandler=null;}_detectPlatform(){let tmpUA=typeof navigator!=='undefined'?navigator.userAgent:'';if(/Macintosh|Mac OS X/.test(tmpUA)){return'macos';}if(/Windows/.test(tmpUA)){return'windows';}return'linux';}openModal(){if(this._modalVisible){return;}this._modalVisible=true;// Create the backdrop
12111
+ let tmpBackdrop=document.createElement('div');tmpBackdrop.className='retold-remote-vlc-modal-backdrop';tmpBackdrop.id='RetoldRemote-VLCSetup-Backdrop';tmpBackdrop.onclick=pEvent=>{if(pEvent.target===tmpBackdrop){this.closeModal();}};// Create the modal
12112
+ let tmpModal=document.createElement('div');tmpModal.className='retold-remote-vlc-modal';// Header
12113
+ let tmpHeader=document.createElement('div');tmpHeader.className='retold-remote-vlc-modal-header';tmpHeader.innerHTML='<span class="retold-remote-vlc-modal-title">VLC Protocol Setup</span>'+'<button class="retold-remote-vlc-modal-close" onclick="pict.views[\'RetoldRemote-VLCSetup\'].closeModal()">X</button>';// Body
12114
+ let tmpBody=document.createElement('div');tmpBody.className='retold-remote-vlc-modal-body';tmpBody.id='RetoldRemote-VLCSetup-Container';tmpModal.appendChild(tmpHeader);tmpModal.appendChild(tmpBody);tmpBackdrop.appendChild(tmpModal);document.body.appendChild(tmpBackdrop);// Render content into the body
12115
+ this._renderVLCSetupContent();// Escape key handler
12116
+ this._boundKeyHandler=pEvent=>{if(pEvent.key==='Escape'){pEvent.preventDefault();pEvent.stopPropagation();this.closeModal();}};document.addEventListener('keydown',this._boundKeyHandler,true);}closeModal(){if(!this._modalVisible){return;}this._modalVisible=false;let tmpBackdrop=document.getElementById('RetoldRemote-VLCSetup-Backdrop');if(tmpBackdrop){tmpBackdrop.remove();}if(this._boundKeyHandler){document.removeEventListener('keydown',this._boundKeyHandler,true);this._boundKeyHandler=null;}}switchPlatformTab(pTab){this._activePlatformTab=pTab;this._renderVLCSetupContent();}_renderVLCSetupContent(){let tmpContainer=document.getElementById('RetoldRemote-VLCSetup-Container');if(!tmpContainer){return;}let tmpPlatform=this._activePlatformTab;let tmpHTML='';// Description
12117
+ tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Stream media directly in VLC from the browser. Press <b>v</b> in the media viewer to launch VLC with the current file.';tmpHTML+='</div>';tmpHTML+='</div>';// Platform status
12118
+ tmpHTML+='<div class="retold-remote-vlc-setup-status">';tmpHTML+='<div class="retold-remote-vlc-setup-status-dot '+(this._detectPlatform()===tmpPlatform?'detected':'unknown')+'"></div>';tmpHTML+='<span>Detected platform: <b>'+this._getPlatformLabel(this._detectPlatform())+'</b></span>';tmpHTML+='</div>';// Platform tabs
12119
+ tmpHTML+='<div class="retold-remote-vlc-setup-platform-tabs">';tmpHTML+=this._buildPlatformTab('macos','macOS',tmpPlatform);tmpHTML+=this._buildPlatformTab('windows','Windows',tmpPlatform);tmpHTML+=this._buildPlatformTab('linux','Linux',tmpPlatform);tmpHTML+='</div>';// Platform-specific content
12120
+ tmpHTML+=this._buildMacOSContent(tmpPlatform);tmpHTML+=this._buildWindowsContent(tmpPlatform);tmpHTML+=this._buildLinuxContent(tmpPlatform);// Test section
12121
+ tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Test</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Click below to test whether the vlc:// protocol handler is registered. VLC should open.';tmpHTML+='</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn" onclick="pict.views[\'RetoldRemote-VLCSetup\'].testProtocol()">Test VLC Protocol</button>';tmpHTML+='</div>';tmpContainer.innerHTML=tmpHTML;}_buildPlatformTab(pKey,pLabel,pActive){let tmpClass='retold-remote-vlc-setup-platform-tab';if(pKey===pActive){tmpClass+=' active';}return'<button class="'+tmpClass+'" onclick="pict.views[\'RetoldRemote-VLCSetup\'].switchPlatformTab(\''+pKey+'\')">'+pLabel+'</button>';}_getPlatformLabel(pKey){if(pKey==='macos')return'macOS';if(pKey==='windows')return'Windows';return'Linux';}_buildMacOSContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='macos'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="macos">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (macOS)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC on macOS does not register a vlc:// protocol handler by default. ';tmpHTML+='An AppleScript app bundle is needed to bridge vlc:// links to VLC. ';tmpHTML+='Run the command below in Terminal to create and register the handler automatically.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Automatic Setup</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Copy and paste this into Terminal:';tmpHTML+='</div>';let tmpScript=this._getMacSetupScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this._escapeHTML(tmpScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyMacSetup()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">What This Does</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Creates an AppleScript at <code>/tmp/VLCProtocol.applescript</code> that handles vlc:// URLs</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Compiles it into an app bundle at <code>/Applications/VLCProtocol.app</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">3</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Adds the vlc:// URL scheme to the app\'s Info.plist</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">4</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Registers the protocol handler with macOS Launch Services</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">Requires VLC installed at /Applications/VLC.app and Python 3 (included with macOS).</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildWindowsContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='windows'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="windows">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (Windows)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC on Windows registers the vlc:// protocol handler during installation. ';tmpHTML+='If it is not working, you can re-register it by saving and running the registry file below.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option A: Reinstall VLC</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Reinstall VLC and ensure "Register VLC as handler for vlc:// protocol" is checked during installation.</div>';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option B: Registry File</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Save this as <code>vlc-protocol.reg</code> and double-click to import. ';tmpHTML+='Adjust the VLC path if yours differs.';tmpHTML+='</div>';let tmpRegFile=this._getWindowsRegFile();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this._escapeHTML(tmpRegFile)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyWindowsReg()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option C: Batch Script</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Alternatively, save this as <code>vlc-protocol-setup.bat</code> and run as Administrator. ';tmpHTML+='This creates a wrapper script that URL-decodes the vlc:// link before passing it to VLC.';tmpHTML+='</div>';let tmpBatchScript=this._getWindowsBatchScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this._escapeHTML(tmpBatchScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyWindowsBatch()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildLinuxContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='linux'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="linux">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (Linux)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Register a vlc:// protocol handler using a .desktop file and xdg-mime. ';tmpHTML+='Run the command below in a terminal.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup Command</div>';let tmpScript=this._getLinuxSetupScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this._escapeHTML(tmpScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyLinuxSetup()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">What This Does</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Creates a handler script at <code>~/.local/bin/vlc-protocol</code> that URL-decodes and opens VLC</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Creates a .desktop file at <code>~/.local/share/applications/vlc-protocol.desktop</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">3</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Registers vlc:// as a URL scheme via <code>xdg-mime</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">Requires VLC and Python 3 installed.</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_getMacSetupScript(){return["# Create the AppleScript handler","cat > /tmp/VLCProtocol.applescript << 'EOF'","on open location theURL","\tset theURL to text 7 thru -1 of theURL","\tset theURL to do shell script \"python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))' \" & quoted form of theURL","\tdo shell script \"open -a VLC \" & quoted form of theURL","end open location","EOF","","# Compile into app bundle","osacompile -o /Applications/VLCProtocol.app /tmp/VLCProtocol.applescript","","# Add vlc:// URL scheme to Info.plist","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes array\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0 dict\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLName string 'VLC Protocol'\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes array\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string vlc\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","","# Register with Launch Services","/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \\"," -f /Applications/VLCProtocol.app","","echo \"VLC protocol handler installed successfully.\""].join('\n');}_getWindowsRegFile(){return["Windows Registry Editor Version 5.00","","[HKEY_CLASSES_ROOT\\vlc]","@=\"URL:VLC Protocol\"","\"URL Protocol\"=\"\"","","[HKEY_CLASSES_ROOT\\vlc\\shell]","","[HKEY_CLASSES_ROOT\\vlc\\shell\\open]","","[HKEY_CLASSES_ROOT\\vlc\\shell\\open\\command]","@=\"\\\"C:\\\\Program Files\\\\VideoLAN\\\\VLC\\\\vlc.exe\\\" \\\"%1\\\"\""].join('\n');}_getWindowsBatchScript(){return["@echo off","REM VLC Protocol Handler Setup for Windows","REM Run this as Administrator","","REM Create the handler script","mkdir \"%APPDATA%\\VLCProtocol\" 2>nul","(","echo import sys, urllib.parse, subprocess","echo url = sys.argv[1] if len(sys.argv^) ^> 1 else ''","echo if url.startswith('vlc://'^): url = url[6:]","echo url = urllib.parse.unquote(url^)","echo subprocess.Popen(['C:\\\\Program Files\\\\VideoLAN\\\\VLC\\\\vlc.exe', url]^)",") > \"%APPDATA%\\VLCProtocol\\handler.py\"","","REM Register the protocol in the registry","reg add \"HKCU\\Software\\Classes\\vlc\" /ve /d \"URL:VLC Protocol\" /f","reg add \"HKCU\\Software\\Classes\\vlc\" /v \"URL Protocol\" /d \"\" /f","reg add \"HKCU\\Software\\Classes\\vlc\\shell\\open\\command\" /ve /d \"pythonw \\\"%APPDATA%\\VLCProtocol\\handler.py\\\" \\\"%%1\\\"\" /f","","echo VLC protocol handler installed successfully.","pause"].join('\n');}_getLinuxSetupScript(){return["# Create handler script","mkdir -p ~/.local/bin","cat > ~/.local/bin/vlc-protocol << 'EOF'","#!/bin/bash","URL=\"$1\"","URL=\"${URL#vlc://}\"","URL=$(python3 -c \"import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))\" \"$URL\")","exec vlc \"$URL\" &","EOF","chmod +x ~/.local/bin/vlc-protocol","","# Create .desktop file","cat > ~/.local/share/applications/vlc-protocol.desktop << 'EOF'","[Desktop Entry]","Name=VLC Protocol Handler","Exec=bash -c '~/.local/bin/vlc-protocol %u'","Type=Application","NoDisplay=true","MimeType=x-scheme-handler/vlc;","EOF","","# Register the handler","xdg-mime default vlc-protocol.desktop x-scheme-handler/vlc","update-desktop-database ~/.local/share/applications/","","echo \"VLC protocol handler installed successfully.\""].join('\n');}_escapeHTML(pStr){return pStr.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}_copyToClipboard(pText,pLabel){if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(pText).then(()=>{this._showToast(pLabel+' copied to clipboard');}).catch(()=>{this._fallbackCopy(pText,pLabel);});}else{this._fallbackCopy(pText,pLabel);}}_fallbackCopy(pText,pLabel){let tmpTextarea=document.createElement('textarea');tmpTextarea.value=pText;tmpTextarea.style.position='fixed';tmpTextarea.style.left='-9999px';document.body.appendChild(tmpTextarea);tmpTextarea.select();try{document.execCommand('copy');this._showToast(pLabel+' copied to clipboard');}catch(pErr){this._showToast('Failed to copy - please select and copy manually');}document.body.removeChild(tmpTextarea);}_showToast(pMessage){let tmpExisting=document.querySelector('.retold-remote-vlc-setup-toast');if(tmpExisting){tmpExisting.remove();}let tmpToast=document.createElement('div');tmpToast.className='retold-remote-vlc-setup-toast';tmpToast.textContent=pMessage;document.body.appendChild(tmpToast);setTimeout(()=>{if(tmpToast.parentNode){tmpToast.remove();}},2000);}copyMacSetup(){this._copyToClipboard(this._getMacSetupScript(),'macOS setup script');}copyWindowsReg(){this._copyToClipboard(this._getWindowsRegFile(),'Registry file');}copyWindowsBatch(){this._copyToClipboard(this._getWindowsBatchScript(),'Batch script');}copyLinuxSetup(){this._copyToClipboard(this._getLinuxSetupScript(),'Linux setup script');}testProtocol(){let tmpIsWindows=/Windows/.test(navigator.userAgent);let tmpSampleURL='https://www.sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4';let tmpTestURL=tmpIsWindows?'vlc://'+tmpSampleURL:'vlc://'+encodeURIComponent(tmpSampleURL);let tmpLink=document.createElement('a');tmpLink.href=tmpTestURL;tmpLink.style.display='none';document.body.appendChild(tmpLink);tmpLink.click();document.body.removeChild(tmpLink);}}RetoldRemoteVLCSetupView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteVLCSetupView;},{"pict-view":76}],130:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VideoExplorer",DefaultRenderable:"RetoldRemote-VideoExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:/*css*/`
11317
12122
  .retold-remote-vex
11318
12123
  {
11319
12124
  display: flex;