retold-remote 0.0.3 → 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.
- package/package.json +2 -2
- package/source/Pict-Application-RetoldRemote.js +43 -1
- package/source/providers/Pict-Provider-GalleryFilterSort.js +13 -2
- package/source/providers/Pict-Provider-GalleryNavigation.js +208 -1
- package/source/server/RetoldRemote-MediaService.js +2 -2
- package/source/server/RetoldRemote-VideoFrameService.js +2 -1
- package/source/views/PictView-Remote-Layout.js +1 -0
- package/source/views/PictView-Remote-MediaViewer.js +231 -20
- package/source/views/PictView-Remote-SettingsPanel.js +154 -18
- package/source/views/PictView-Remote-VLCSetup.js +797 -0
- package/web-application/retold-remote.js +1606 -667
- package/web-application/retold-remote.js.map +1 -1
- package/web-application/retold-remote.min.js +5 -5
- package/web-application/retold-remote.min.js.map +1 -1
|
@@ -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){
|
|
1736
|
-
//
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');}}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
|
-
|
|
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
|
-
|
|
1831
|
-
|
|
1832
|
-
min-
|
|
1833
|
-
color: #8A7F72;
|
|
1834
|
-
font-size: 1em;
|
|
1778
|
+
flex-direction: column;
|
|
1779
|
+
flex: 1;
|
|
1780
|
+
min-width: 0;
|
|
1835
1781
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
margin-top: 0;
|
|
1782
|
+
|
|
1783
|
+
.pict-filebrowser-list-pane {
|
|
1784
|
+
flex: 1;
|
|
1785
|
+
overflow-y: auto;
|
|
1786
|
+
overflow-x: hidden;
|
|
1842
1787
|
}
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
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
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1794
|
+
|
|
1795
|
+
/* --- Browsing: Tree --- */
|
|
1796
|
+
.pict-fb-tree {
|
|
1797
|
+
padding: 8px 0;
|
|
1854
1798
|
}
|
|
1855
|
-
.pict-
|
|
1856
|
-
|
|
1857
|
-
|
|
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-
|
|
1860
|
-
|
|
1861
|
-
color: #423D37;
|
|
1862
|
-
margin: 0.75em 0;
|
|
1807
|
+
.pict-fb-tree-node:hover {
|
|
1808
|
+
background: #EAE3D8;
|
|
1863
1809
|
}
|
|
1864
|
-
.pict-
|
|
1865
|
-
|
|
1866
|
-
|
|
1810
|
+
.pict-fb-tree-node.selected {
|
|
1811
|
+
background: #DDD6CA;
|
|
1812
|
+
font-weight: 600;
|
|
1867
1813
|
}
|
|
1868
|
-
.pict-
|
|
1869
|
-
|
|
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-
|
|
1872
|
-
|
|
1873
|
-
|
|
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-
|
|
1882
|
-
|
|
1883
|
-
|
|
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-
|
|
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
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
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-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
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
|
-
`};},{}],
|
|
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":
|
|
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}],
|
|
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}],
|
|
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}],
|
|
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}],
|
|
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}],
|
|
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}],
|
|
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}],
|
|
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}],
|
|
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}],
|
|
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>
|
|
@@ -2972,107 +2662,419 @@ tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Current-Template'
|
|
|
2972
2662
|
<div class="pict-fb-icon-graphic">{~D:Record.Icon~}</div>
|
|
2973
2663
|
<div class="pict-fb-icon-label">{~D:Record.Name~}</div>
|
|
2974
2664
|
</div>
|
|
2975
|
-
`},{"Hash":"FileBrowser-ListIcons-Empty-Template","Template":/*html*/`<div class="pict-fb-empty">{~D:Record.Message~}</div>`}],"Renderables":[{"RenderableHash":"ListIcons-Container","TemplateHash":"FileBrowser-ListIcons-Container-Template","DestinationAddress":"#Pict-FileBrowser-ListPane","RenderMethod":"replace"}]};/**
|
|
2976
|
-
* Listing view that shows files as an icon grid.
|
|
2977
|
-
*
|
|
2978
|
-
* Each file or folder is displayed as a large icon with a label beneath it.
|
|
2979
|
-
* Single-click selects, double-click opens folders or selects files.
|
|
2980
|
-
*/class PictViewFileBrowserListIcons extends libPictView{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_ViewConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);this._cachedFileList=[];}/**
|
|
2981
|
-
* After rendering the container, populate the icon grid.
|
|
2982
|
-
*/onAfterRender(pRenderable){this.rebuildGrid();this.rebuildBreadcrumb();this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
|
|
2983
|
-
* Rebuild the icon grid.
|
|
2984
|
-
*/rebuildGrid(){let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(!tmpListProvider){return;}let tmpFileList=tmpListProvider.getSortedFileList();this._cachedFileList=tmpFileList;let tmpSelectedFile=tmpListProvider.getSelectedFile();if(tmpFileList.length===0){let tmpEmptyHTML=this.pict.parseTemplateByHash('FileBrowser-ListIcons-Empty-Template',{Message:'This folder is empty'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-IconGrid',tmpEmptyHTML);return;}let tmpHTML='';for(let i=0;i<tmpFileList.length;i++){let tmpEntry=tmpFileList[i];let tmpIsSelected=tmpSelectedFile&&tmpSelectedFile.Name===tmpEntry.Name&&tmpSelectedFile.Path===tmpEntry.Path;let tmpRecord={Index:i,Name:tmpEntry.Name,Icon:tmpListProvider.getEntryIcon(tmpEntry),SelectedClass:tmpIsSelected?' selected':'',ClickHandler:"pict.views['"+this.Hash+"'].selectEntry("+i+")",DblClickHandler:"pict.views['"+this.Hash+"'].openEntry("+i+")"};tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-ListIcons-Item-Template',tmpRecord);}this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-IconGrid',tmpHTML);}/**
|
|
2985
|
-
* Rebuild the breadcrumb navigation bar.
|
|
2986
|
-
*/rebuildBreadcrumb(){let tmpCurrentLocation=this.getCurrentLocation();let tmpHTML='';// Root segment
|
|
2987
|
-
tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Segment-Template',{Label:'\uD83C\uDFE0',ClickHandler:"pict.views['"+this.Hash+"'].navigateToPath('')"});if(tmpCurrentLocation){let tmpParts=tmpCurrentLocation.split('/');let tmpAccumulatedPath='';for(let i=0;i<tmpParts.length;i++){tmpAccumulatedPath=tmpAccumulatedPath?tmpAccumulatedPath+'/'+tmpParts[i]:tmpParts[i];tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Separator-Template',{});if(i===tmpParts.length-1){tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Current-Template',{Label:tmpParts[i]});}else{let tmpPath=tmpAccumulatedPath;tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Segment-Template',{Label:tmpParts[i],ClickHandler:"pict.views['"+this.Hash+"'].navigateToPath('"+tmpPath+"')"});}}}this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-IconBreadcrumb',tmpHTML);}/**
|
|
2988
|
-
* Select a file entry by index.
|
|
2989
|
-
*
|
|
2990
|
-
* @param {number} pIndex - The index in the cached file list
|
|
2991
|
-
*/selectEntry(pIndex){if(pIndex<0||pIndex>=this._cachedFileList.length){return;}let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(tmpListProvider){tmpListProvider.selectFile(this._cachedFileList[pIndex]);}this.rebuildGrid();}/**
|
|
2992
|
-
* Open an entry (navigate into folder or select file).
|
|
2993
|
-
*
|
|
2994
|
-
* @param {number} pIndex - The index in the cached file list
|
|
2995
|
-
*/openEntry(pIndex){if(pIndex<0||pIndex>=this._cachedFileList.length){return;}let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(tmpListProvider){tmpListProvider.openEntry(this._cachedFileList[pIndex]);}this.rebuildGrid();this.rebuildBreadcrumb();}/**
|
|
2996
|
-
* Navigate to a path via breadcrumb.
|
|
2665
|
+
`},{"Hash":"FileBrowser-ListIcons-Empty-Template","Template":/*html*/`<div class="pict-fb-empty">{~D:Record.Message~}</div>`}],"Renderables":[{"RenderableHash":"ListIcons-Container","TemplateHash":"FileBrowser-ListIcons-Container-Template","DestinationAddress":"#Pict-FileBrowser-ListPane","RenderMethod":"replace"}]};/**
|
|
2666
|
+
* Listing view that shows files as an icon grid.
|
|
2667
|
+
*
|
|
2668
|
+
* Each file or folder is displayed as a large icon with a label beneath it.
|
|
2669
|
+
* Single-click selects, double-click opens folders or selects files.
|
|
2670
|
+
*/class PictViewFileBrowserListIcons extends libPictView{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_ViewConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);this._cachedFileList=[];}/**
|
|
2671
|
+
* After rendering the container, populate the icon grid.
|
|
2672
|
+
*/onAfterRender(pRenderable){this.rebuildGrid();this.rebuildBreadcrumb();this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
|
|
2673
|
+
* Rebuild the icon grid.
|
|
2674
|
+
*/rebuildGrid(){let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(!tmpListProvider){return;}let tmpFileList=tmpListProvider.getSortedFileList();this._cachedFileList=tmpFileList;let tmpSelectedFile=tmpListProvider.getSelectedFile();if(tmpFileList.length===0){let tmpEmptyHTML=this.pict.parseTemplateByHash('FileBrowser-ListIcons-Empty-Template',{Message:'This folder is empty'});this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-IconGrid',tmpEmptyHTML);return;}let tmpHTML='';for(let i=0;i<tmpFileList.length;i++){let tmpEntry=tmpFileList[i];let tmpIsSelected=tmpSelectedFile&&tmpSelectedFile.Name===tmpEntry.Name&&tmpSelectedFile.Path===tmpEntry.Path;let tmpRecord={Index:i,Name:tmpEntry.Name,Icon:tmpListProvider.getEntryIcon(tmpEntry),SelectedClass:tmpIsSelected?' selected':'',ClickHandler:"pict.views['"+this.Hash+"'].selectEntry("+i+")",DblClickHandler:"pict.views['"+this.Hash+"'].openEntry("+i+")"};tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-ListIcons-Item-Template',tmpRecord);}this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-IconGrid',tmpHTML);}/**
|
|
2675
|
+
* Rebuild the breadcrumb navigation bar.
|
|
2676
|
+
*/rebuildBreadcrumb(){let tmpCurrentLocation=this.getCurrentLocation();let tmpHTML='';// Root segment
|
|
2677
|
+
tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Segment-Template',{Label:'\uD83C\uDFE0',ClickHandler:"pict.views['"+this.Hash+"'].navigateToPath('')"});if(tmpCurrentLocation){let tmpParts=tmpCurrentLocation.split('/');let tmpAccumulatedPath='';for(let i=0;i<tmpParts.length;i++){tmpAccumulatedPath=tmpAccumulatedPath?tmpAccumulatedPath+'/'+tmpParts[i]:tmpParts[i];tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Separator-Template',{});if(i===tmpParts.length-1){tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Current-Template',{Label:tmpParts[i]});}else{let tmpPath=tmpAccumulatedPath;tmpHTML+=this.pict.parseTemplateByHash('FileBrowser-Breadcrumb-Segment-Template',{Label:tmpParts[i],ClickHandler:"pict.views['"+this.Hash+"'].navigateToPath('"+tmpPath+"')"});}}}this.pict.ContentAssignment.assignContent('#Pict-FileBrowser-IconBreadcrumb',tmpHTML);}/**
|
|
2678
|
+
* Select a file entry by index.
|
|
2679
|
+
*
|
|
2680
|
+
* @param {number} pIndex - The index in the cached file list
|
|
2681
|
+
*/selectEntry(pIndex){if(pIndex<0||pIndex>=this._cachedFileList.length){return;}let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(tmpListProvider){tmpListProvider.selectFile(this._cachedFileList[pIndex]);}this.rebuildGrid();}/**
|
|
2682
|
+
* Open an entry (navigate into folder or select file).
|
|
2683
|
+
*
|
|
2684
|
+
* @param {number} pIndex - The index in the cached file list
|
|
2685
|
+
*/openEntry(pIndex){if(pIndex<0||pIndex>=this._cachedFileList.length){return;}let tmpListProvider=this.pict.providers['Pict-FileBrowser-List'];if(tmpListProvider){tmpListProvider.openEntry(this._cachedFileList[pIndex]);}this.rebuildGrid();this.rebuildBreadcrumb();}/**
|
|
2686
|
+
* Navigate to a path via breadcrumb.
|
|
2687
|
+
*
|
|
2688
|
+
* @param {string} pPath - The target path
|
|
2689
|
+
*/navigateToPath(pPath){let tmpBrowseProvider=this.pict.providers['Pict-FileBrowser-Browse'];if(tmpBrowseProvider){tmpBrowseProvider.navigateToFolder(pPath);}this.rebuildGrid();this.rebuildBreadcrumb();}/**
|
|
2690
|
+
* Get the current location from state.
|
|
2691
|
+
*
|
|
2692
|
+
* @returns {string} The current location path
|
|
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*/`
|
|
2694
|
+
<div class="pict-fb-fileinfo-title">{~D:Record.Name~}</div>
|
|
2695
|
+
<table class="pict-fb-fileinfo-table">
|
|
2696
|
+
<tr>
|
|
2697
|
+
<td class="pict-fb-fileinfo-label">Type</td>
|
|
2698
|
+
<td class="pict-fb-fileinfo-value">{~D:Record.TypeDescription~}</td>
|
|
2699
|
+
</tr>
|
|
2700
|
+
<tr>
|
|
2701
|
+
<td class="pict-fb-fileinfo-label">Size</td>
|
|
2702
|
+
<td class="pict-fb-fileinfo-value">{~D:Record.SizeFormatted~}</td>
|
|
2703
|
+
</tr>
|
|
2704
|
+
<tr>
|
|
2705
|
+
<td class="pict-fb-fileinfo-label">Modified</td>
|
|
2706
|
+
<td class="pict-fb-fileinfo-value">{~D:Record.ModifiedFormatted~}</td>
|
|
2707
|
+
</tr>
|
|
2708
|
+
<tr>
|
|
2709
|
+
<td class="pict-fb-fileinfo-label">Extension</td>
|
|
2710
|
+
<td class="pict-fb-fileinfo-value">{~D:Record.Extension~}</td>
|
|
2711
|
+
</tr>
|
|
2712
|
+
<tr>
|
|
2713
|
+
<td class="pict-fb-fileinfo-label">Path</td>
|
|
2714
|
+
<td class="pict-fb-fileinfo-value">{~D:Record.Path~}</td>
|
|
2715
|
+
</tr>
|
|
2716
|
+
</table>
|
|
2717
|
+
`},{"Hash":"FileBrowser-ViewFileInfo-Empty-Template","Template":/*html*/`<div class="pict-fb-fileinfo-none">No file selected</div>`}],"Renderables":[{"RenderableHash":"ViewFileInfo-Container","TemplateHash":"FileBrowser-ViewFileInfo-Container-Template","DestinationAddress":"#Pict-FileBrowser-ViewPane","RenderMethod":"replace"}]};/**
|
|
2718
|
+
* Viewing view that shows file metadata/stats for the currently selected file.
|
|
2719
|
+
*
|
|
2720
|
+
* Displays name, type description, size, modification date, extension, and path
|
|
2721
|
+
* in a simple table layout. Shows "No file selected" when nothing is selected.
|
|
2722
|
+
*/class PictViewFileBrowserViewFileInfo extends libPictView{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_ViewConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);}/**
|
|
2723
|
+
* After rendering the container, populate with file info.
|
|
2724
|
+
*/onAfterRender(pRenderable){this.rebuildFileInfo();this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
|
|
2725
|
+
* Rebuild the file info display.
|
|
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"}]};/**
|
|
2727
|
+
* Viewing view that displays an image preview for the currently selected file.
|
|
2728
|
+
*
|
|
2729
|
+
* If the selected file is an image and has a URL or ThumbnailURL, it renders
|
|
2730
|
+
* an <img> tag. Otherwise shows a "not an image" or "no file selected" message.
|
|
2731
|
+
*/class PictViewFileBrowserViewImage extends libPictView{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_ViewConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);}/**
|
|
2732
|
+
* After rendering the container, populate with the image or message.
|
|
2733
|
+
*/onAfterRender(pRenderable){this.rebuildImageView();this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
|
|
2734
|
+
* Rebuild the image viewer display.
|
|
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');/**
|
|
2736
|
+
* Main FileBrowser view.
|
|
2737
|
+
*
|
|
2738
|
+
* Renders the outer container layout (browse pane, list pane, view pane)
|
|
2739
|
+
* and manages the lifecycle of the three base providers.
|
|
2740
|
+
*
|
|
2741
|
+
* The consuming application registers this view and then adds whichever
|
|
2742
|
+
* browsing, listing, and viewing sub-views they want (tree, search,
|
|
2743
|
+
* detail list, icon grid, file info, image viewer, etc.).
|
|
2744
|
+
*
|
|
2745
|
+
* On initialization, this view ensures the three providers are registered
|
|
2746
|
+
* and that default state values are populated in AppData.
|
|
2747
|
+
*/class PictViewFileBrowser extends libPictView{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},JSON.parse(JSON.stringify(_DefaultConfiguration)),pOptions);super(pFable,tmpOptions,pServiceHash);// Register providers if not already registered
|
|
2748
|
+
if(!this.pict.providers['Pict-FileBrowser-Browse']){this.pict.addProvider('Pict-FileBrowser-Browse',libBrowseProvider.default_configuration,libBrowseProvider);}if(!this.pict.providers['Pict-FileBrowser-List']){this.pict.addProvider('Pict-FileBrowser-List',libListProvider.default_configuration,libListProvider);}if(!this.pict.providers['Pict-FileBrowser-View']){this.pict.addProvider('Pict-FileBrowser-View',libViewProvider.default_configuration,libViewProvider);}if(!this.pict.providers['Pict-FileBrowser-Layout']){this.pict.addProvider('Pict-FileBrowser-Layout',libLayoutProvider.default_configuration,libLayoutProvider);}if(!this.pict.providers['Pict-FileBrowser-Icons']){this.pict.addProvider('Pict-FileBrowser-Icons',libIconProvider.default_configuration,libIconProvider);}// Ensure default state in AppData
|
|
2749
|
+
this.ensureDefaultState();}/**
|
|
2750
|
+
* Populate AppData with default state values if not already set.
|
|
2751
|
+
*/ensureDefaultState(){let tmpDefaultState=this.options.DefaultState||{};let tmpStateAddresses=this.options.StateAddresses||{};let tmpScope={AppData:this.pict.AppData,Pict:this.pict};// Ensure the PictFileBrowser namespace exists
|
|
2752
|
+
if(!this.pict.AppData.PictFileBrowser){this.pict.AppData.PictFileBrowser={};}for(let tmpKey in tmpStateAddresses){let tmpAddress=tmpStateAddresses[tmpKey];let tmpCurrentValue=this.pict.manifest.getValueByHash(tmpScope,tmpAddress);if(tmpCurrentValue===undefined||tmpCurrentValue===null){let tmpDefault=tmpDefaultState[tmpKey];if(tmpDefault!==undefined){this.pict.manifest.setValueByHash(tmpScope,tmpAddress,tmpDefault);}}}// Ensure FileList and FolderTree arrays exist
|
|
2753
|
+
if(!this.pict.AppData.PictFileBrowser.FileList){this.pict.AppData.PictFileBrowser.FileList=[];}if(!this.pict.AppData.PictFileBrowser.FolderTree){this.pict.AppData.PictFileBrowser.FolderTree=[];}}/**
|
|
2754
|
+
* After rendering the container, inject CSS.
|
|
2755
|
+
*/onAfterRender(pRenderable){this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
|
|
2756
|
+
* Get a state value by key.
|
|
2757
|
+
*
|
|
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');}}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.
|
|
2997
3052
|
*
|
|
2998
|
-
* @param {string}
|
|
2999
|
-
|
|
3000
|
-
|
|
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.
|
|
3001
3061
|
*
|
|
3002
|
-
* @
|
|
3003
|
-
*/
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
<td class="pict-fb-fileinfo-value">{~D:Record.TypeDescription~}</td>
|
|
3009
|
-
</tr>
|
|
3010
|
-
<tr>
|
|
3011
|
-
<td class="pict-fb-fileinfo-label">Size</td>
|
|
3012
|
-
<td class="pict-fb-fileinfo-value">{~D:Record.SizeFormatted~}</td>
|
|
3013
|
-
</tr>
|
|
3014
|
-
<tr>
|
|
3015
|
-
<td class="pict-fb-fileinfo-label">Modified</td>
|
|
3016
|
-
<td class="pict-fb-fileinfo-value">{~D:Record.ModifiedFormatted~}</td>
|
|
3017
|
-
</tr>
|
|
3018
|
-
<tr>
|
|
3019
|
-
<td class="pict-fb-fileinfo-label">Extension</td>
|
|
3020
|
-
<td class="pict-fb-fileinfo-value">{~D:Record.Extension~}</td>
|
|
3021
|
-
</tr>
|
|
3022
|
-
<tr>
|
|
3023
|
-
<td class="pict-fb-fileinfo-label">Path</td>
|
|
3024
|
-
<td class="pict-fb-fileinfo-value">{~D:Record.Path~}</td>
|
|
3025
|
-
</tr>
|
|
3026
|
-
</table>
|
|
3027
|
-
`},{"Hash":"FileBrowser-ViewFileInfo-Empty-Template","Template":/*html*/`<div class="pict-fb-fileinfo-none">No file selected</div>`}],"Renderables":[{"RenderableHash":"ViewFileInfo-Container","TemplateHash":"FileBrowser-ViewFileInfo-Container-Template","DestinationAddress":"#Pict-FileBrowser-ViewPane","RenderMethod":"replace"}]};/**
|
|
3028
|
-
* Viewing view that shows file metadata/stats for the currently selected file.
|
|
3029
|
-
*
|
|
3030
|
-
* Displays name, type description, size, modification date, extension, and path
|
|
3031
|
-
* in a simple table layout. Shows "No file selected" when nothing is selected.
|
|
3032
|
-
*/class PictViewFileBrowserViewFileInfo extends libPictView{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_ViewConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);}/**
|
|
3033
|
-
* After rendering the container, populate with file info.
|
|
3034
|
-
*/onAfterRender(pRenderable){this.rebuildFileInfo();this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
|
|
3035
|
-
* 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"}]};/**
|
|
3037
|
-
* Viewing view that displays an image preview for the currently selected file.
|
|
3038
|
-
*
|
|
3039
|
-
* If the selected file is an image and has a URL or ThumbnailURL, it renders
|
|
3040
|
-
* an <img> tag. Otherwise shows a "not an image" or "no file selected" message.
|
|
3041
|
-
*/class PictViewFileBrowserViewImage extends libPictView{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_ViewConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);}/**
|
|
3042
|
-
* After rendering the container, populate with the image or message.
|
|
3043
|
-
*/onAfterRender(pRenderable){this.rebuildImageView();this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
|
|
3044
|
-
* 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');/**
|
|
3046
|
-
* Main FileBrowser view.
|
|
3047
|
-
*
|
|
3048
|
-
* Renders the outer container layout (browse pane, list pane, view pane)
|
|
3049
|
-
* and manages the lifecycle of the three base providers.
|
|
3050
|
-
*
|
|
3051
|
-
* The consuming application registers this view and then adds whichever
|
|
3052
|
-
* browsing, listing, and viewing sub-views they want (tree, search,
|
|
3053
|
-
* detail list, icon grid, file info, image viewer, etc.).
|
|
3054
|
-
*
|
|
3055
|
-
* On initialization, this view ensures the three providers are registered
|
|
3056
|
-
* and that default state values are populated in AppData.
|
|
3057
|
-
*/class PictViewFileBrowser extends libPictView{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},JSON.parse(JSON.stringify(_DefaultConfiguration)),pOptions);super(pFable,tmpOptions,pServiceHash);// Register providers if not already registered
|
|
3058
|
-
if(!this.pict.providers['Pict-FileBrowser-Browse']){this.pict.addProvider('Pict-FileBrowser-Browse',libBrowseProvider.default_configuration,libBrowseProvider);}if(!this.pict.providers['Pict-FileBrowser-List']){this.pict.addProvider('Pict-FileBrowser-List',libListProvider.default_configuration,libListProvider);}if(!this.pict.providers['Pict-FileBrowser-View']){this.pict.addProvider('Pict-FileBrowser-View',libViewProvider.default_configuration,libViewProvider);}if(!this.pict.providers['Pict-FileBrowser-Layout']){this.pict.addProvider('Pict-FileBrowser-Layout',libLayoutProvider.default_configuration,libLayoutProvider);}if(!this.pict.providers['Pict-FileBrowser-Icons']){this.pict.addProvider('Pict-FileBrowser-Icons',libIconProvider.default_configuration,libIconProvider);}// Ensure default state in AppData
|
|
3059
|
-
this.ensureDefaultState();}/**
|
|
3060
|
-
* Populate AppData with default state values if not already set.
|
|
3061
|
-
*/ensureDefaultState(){let tmpDefaultState=this.options.DefaultState||{};let tmpStateAddresses=this.options.StateAddresses||{};let tmpScope={AppData:this.pict.AppData,Pict:this.pict};// Ensure the PictFileBrowser namespace exists
|
|
3062
|
-
if(!this.pict.AppData.PictFileBrowser){this.pict.AppData.PictFileBrowser={};}for(let tmpKey in tmpStateAddresses){let tmpAddress=tmpStateAddresses[tmpKey];let tmpCurrentValue=this.pict.manifest.getValueByHash(tmpScope,tmpAddress);if(tmpCurrentValue===undefined||tmpCurrentValue===null){let tmpDefault=tmpDefaultState[tmpKey];if(tmpDefault!==undefined){this.pict.manifest.setValueByHash(tmpScope,tmpAddress,tmpDefault);}}}// Ensure FileList and FolderTree arrays exist
|
|
3063
|
-
if(!this.pict.AppData.PictFileBrowser.FileList){this.pict.AppData.PictFileBrowser.FileList=[];}if(!this.pict.AppData.PictFileBrowser.FolderTree){this.pict.AppData.PictFileBrowser.FolderTree=[];}}/**
|
|
3064
|
-
* After rendering the container, inject CSS.
|
|
3065
|
-
*/onAfterRender(pRenderable){this.pict.CSSMap.injectCSS();return super.onAfterRender(pRenderable);}/**
|
|
3066
|
-
* Get a state value by key.
|
|
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">`
|
|
3067
3068
|
*
|
|
3068
|
-
* @param {string}
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
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.
|
|
3072
3074
|
*
|
|
3073
|
-
* @param {string}
|
|
3074
|
-
* @param {
|
|
3075
|
-
*/
|
|
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=[];//
|
|
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);}}));//
|
|
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":
|
|
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
|
|
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,
|
|
@@ -3885,14 +3926,190 @@ let tmpContentContainer=tmpContainer.querySelector(`#${tmpRenderedViewID} .pict-
|
|
|
3885
3926
|
white-space: nowrap;
|
|
3886
3927
|
}
|
|
3887
3928
|
|
|
3888
|
-
/* ---- Line number / controls toggle: gutters hidden by default ---- */
|
|
3889
|
-
.pict-mde .cm-editor .cm-gutters
|
|
3890
|
-
{
|
|
3891
|
-
display: none;
|
|
3929
|
+
/* ---- Line number / controls toggle: gutters hidden by default ---- */
|
|
3930
|
+
.pict-mde .cm-editor .cm-gutters
|
|
3931
|
+
{
|
|
3932
|
+
display: none;
|
|
3933
|
+
}
|
|
3934
|
+
.pict-mde.pict-mde-controls-on .cm-editor .cm-gutters
|
|
3935
|
+
{
|
|
3936
|
+
display: flex;
|
|
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
|
+
}
|
|
3892
4036
|
}
|
|
3893
|
-
|
|
4037
|
+
|
|
4038
|
+
/* ============================================
|
|
4039
|
+
RESPONSIVE: Small phone (max-width: 480px)
|
|
4040
|
+
============================================ */
|
|
4041
|
+
@media (max-width: 480px)
|
|
3894
4042
|
{
|
|
3895
|
-
|
|
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
|
+
}
|
|
3896
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)
|
|
@@ -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=
|
|
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
|
-
|
|
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')
|
|
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":
|
|
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(/<small>/gi,'<small>').replace(/<\/small>/gi,'</small>');}/**
|
|
5797
6016
|
* Escape HTML special characters.
|
|
5798
|
-
*/escapeHTML(pText){if(!pText){return'';}return pText.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');}}module.exports=DocuserveTopBarView;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":76}],93:[function(require,module,exports){arguments[4][
|
|
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');}}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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');}}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
|
-
|
|
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:
|
|
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){
|
|
6024
|
-
//
|
|
6025
|
-
|
|
6026
|
-
|
|
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":
|
|
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;
|
|
@@ -6724,6 +6932,171 @@ if(this.pict.PictApplication){this.pict.PictApplication.markDirty();this.pict.Pi
|
|
|
6724
6932
|
color: #8A7F72;
|
|
6725
6933
|
text-align: center;
|
|
6726
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
|
|
6980
|
+
{
|
|
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
|
+
}
|
|
7099
|
+
}
|
|
6727
7100
|
`,Templates:[{Hash:"ContentEditor-Layout-Shell-Template",Template:/*html*/`
|
|
6728
7101
|
<div id="ContentEditor-TopBar-Container"></div>
|
|
6729
7102
|
<div class="content-editor-body">
|
|
@@ -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='▶';}
|
|
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?'▼':'▶';}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='▼';}}// 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
|
|
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?'▼':'▶';}else{tmpWrap.classList.remove('collapsed');tmpWrap.style.width=tmpIsMobile?'100%':tmpSettings.SidebarWidth+'px';if(tmpToggle)tmpToggle.innerHTML=tmpIsMobile?'▲':'◀';}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();
|
|
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'
|
|
@@ -8562,6 +9069,7 @@ ThumbnailSize:'medium',// 'small', 'medium', 'large'
|
|
|
8562
9069
|
RawFileList:[],// Unfiltered server response
|
|
8563
9070
|
GalleryItems:[],// Filtered+sorted file list (single source of truth)
|
|
8564
9071
|
GalleryCursorIndex:0,// Currently highlighted item
|
|
9072
|
+
FolderCursorHistory:{},// Map of folder path -> last cursor index
|
|
8565
9073
|
GalleryFilter:'all',// 'all', 'images', 'video', 'audio', 'documents'
|
|
8566
9074
|
SearchQuery:'',SearchCaseSensitive:false,SearchRegex:false,ServerCapabilities:{},// From /api/media/capabilities
|
|
8567
9075
|
FolderSummary:null,// From /api/media/folder-summary
|
|
@@ -8610,6 +9118,14 @@ let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewe
|
|
|
8610
9118
|
let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(pFilePath,'document');}}else{// Default: use media viewer
|
|
8611
9119
|
let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(pFilePath,tmpMediaType);}}// Update the topbar
|
|
8612
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();}}/**
|
|
8613
9129
|
* Override loadFileList to also populate the gallery and fetch folder summary.
|
|
8614
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
|
|
8615
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)
|
|
@@ -8619,7 +9135,7 @@ let tmpRemote=tmpSelf.pict.AppData.RetoldRemote;tmpRemote.RawFileList=pFileList|
|
|
|
8619
9135
|
tmpRemote.FilterState={MediaType:tmpRemote.FilterState.MediaType,Extensions:[],SizeMin:null,SizeMax:null,DateModifiedAfter:null,DateModifiedBefore:null,DateCreatedAfter:null,DateCreatedBefore:null};// Show the gallery, hide the viewer
|
|
8620
9136
|
let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');if(tmpGalleryContainer)tmpGalleryContainer.style.display='';if(tmpViewerContainer)tmpViewerContainer.style.display='none';// Run the filter+sort pipeline (sets GalleryItems, resets cursor, renders gallery)
|
|
8621
9137
|
let tmpFilterSort=tmpSelf.pict.providers['RetoldRemote-GalleryFilterSort'];if(tmpFilterSort){tmpFilterSort.applyFilterSort();}else{// Fallback if provider not ready
|
|
8622
|
-
tmpRemote.GalleryItems=pFileList||[];tmpRemote.GalleryCursorIndex=0;let tmpGalleryView=tmpSelf.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tmpGalleryView.renderGallery();}}// Update the hash (use hashed identifier when available)
|
|
9138
|
+
tmpRemote.GalleryItems=pFileList||[];let tmpSavedIndex=tmpRemote.FolderCursorHistory&&tmpRemote.FolderCursorHistory[tmpSelf.pict.AppData.PictFileBrowser.CurrentLocation||''];tmpRemote.GalleryCursorIndex=typeof tmpSavedIndex==='number'&&tmpSavedIndex<(pFileList||[]).length?tmpSavedIndex:0;let tmpGalleryView=tmpSelf.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tmpGalleryView.renderGallery();}}// Update the hash (use hashed identifier when available)
|
|
8623
9139
|
let tmpCurrentPath=tmpSelf.pict.AppData.PictFileBrowser.CurrentLocation||'';let tmpBrowseFragProvider=tmpSelf.pict.providers['RetoldRemote-Provider'];let tmpBrowseFragId=tmpBrowseFragProvider&&tmpCurrentPath?tmpBrowseFragProvider.getFragmentIdentifier(tmpCurrentPath):tmpCurrentPath;window.location.hash=tmpBrowseFragId?'#/browse/'+tmpBrowseFragId:'#/browse/';// Fetch folder summary for topbar info (skip for archive paths — they are not filesystem directories)
|
|
8624
9140
|
let tmpMediaProvider=tmpSelf.pict.providers['RetoldRemote-Provider'];let tmpIsArchivePath=/\.(zip|7z|rar|tar|tgz|cbz|cbr|tar\.gz|tar\.bz2|tar\.xz)(\/|$)/i.test(tmpCurrentPath);if(tmpMediaProvider&&!tmpIsArchivePath){tmpMediaProvider.fetchFolderSummary(tmpCurrentPath,(pError,pSummary)=>{if(!pError&&pSummary){tmpRemote.FolderSummary=pSummary;let tmpTopBar=tmpSelf.pict.views['ContentEditor-TopBar'];if(tmpTopBar){tmpTopBar.updateLocation();tmpTopBar.updateInfo();}}});}else{let tmpTopBar=tmpSelf.pict.views['ContentEditor-TopBar'];if(tmpTopBar){tmpTopBar.updateLocation();tmpTopBar.updateInfo();}}return tmpCallback();}).catch(pError=>{tmpSelf.log.error(`Failed to load file list: ${pError.message}`);return tmpCallback();});}/**
|
|
8625
9141
|
* Override resolveHash to handle gallery and viewer routes.
|
|
@@ -8631,11 +9147,11 @@ let tmpFilePath=tmpFragProvider?tmpFragProvider.resolveFragmentIdentifier(tmpRaw
|
|
|
8631
9147
|
}}/**
|
|
8632
9148
|
* Load RetoldRemote settings from localStorage.
|
|
8633
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
|
|
8634
|
-
}}}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-
|
|
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){/**
|
|
8635
9151
|
* Retold Remote -- Browser Bundle Entry
|
|
8636
9152
|
*
|
|
8637
9153
|
* Exports the RetoldRemote application class for browser consumption.
|
|
8638
|
-
*/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);}// ──────────────────────────────────────────────
|
|
8639
9155
|
// Pipeline
|
|
8640
9156
|
// ──────────────────────────────────────────────
|
|
8641
9157
|
/**
|
|
@@ -8649,7 +9165,8 @@ tmpItems=this._applyExtensionFilter(tmpItems,tmpFilterState.Extensions||[]);// 4
|
|
|
8649
9165
|
tmpItems=this._applySizeFilter(tmpItems,tmpFilterState.SizeMin,tmpFilterState.SizeMax);// 5. Date range filter
|
|
8650
9166
|
tmpItems=this._applyDateFilter(tmpItems,tmpFilterState);// 6. Sort
|
|
8651
9167
|
tmpItems=this._sortItems(tmpItems,tmpRemote.SortField||'folder-first',tmpRemote.SortDirection||'asc');// Write result
|
|
8652
|
-
tmpRemote.GalleryItems=tmpItems
|
|
9168
|
+
tmpRemote.GalleryItems=tmpItems;// Restore cursor position if we have a saved one for this folder
|
|
9169
|
+
let tmpCurrentLocation=this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation||'';let tmpSavedIndex=tmpRemote.FolderCursorHistory&&tmpRemote.FolderCursorHistory[tmpCurrentLocation];if(typeof tmpSavedIndex==='number'&&tmpSavedIndex<tmpItems.length){tmpRemote.GalleryCursorIndex=tmpSavedIndex;}else{tmpRemote.GalleryCursorIndex=0;}// Re-render gallery
|
|
8653
9170
|
let tmpGalleryView=this.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tmpGalleryView.renderGallery();}}// ──────────────────────────────────────────────
|
|
8654
9171
|
// Filter stages
|
|
8655
9172
|
// ──────────────────────────────────────────────
|
|
@@ -8746,7 +9263,7 @@ if(pEvent.key==='Escape'&&pEvent.target.id==='RetoldRemote-Gallery-Search'){pEve
|
|
|
8746
9263
|
if(pEvent.target.tagName==='INPUT'||pEvent.target.tagName==='TEXTAREA'||pEvent.target.isContentEditable){return;}// If the help panel is visible, Escape closes it
|
|
8747
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;}/**
|
|
8748
9265
|
* Handle keyboard events in gallery mode.
|
|
8749
|
-
*/_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
|
|
8750
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
|
|
8751
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;}}/**
|
|
8752
9269
|
* Handle keyboard events when the sidebar file list has focus.
|
|
@@ -8766,7 +9283,8 @@ let tmpRows=document.querySelectorAll('#Pict-FileBrowser-DetailRows .pict-fb-det
|
|
|
8766
9283
|
if(this._sidebarCursorIndex<tmpRows.length){tmpRows[this._sidebarCursorIndex].classList.remove('sidebar-focused');}this._sidebarCursorIndex=pIndex;// Apply new highlight and scroll into view
|
|
8767
9284
|
if(pIndex<tmpRows.length){tmpRows[pIndex].classList.add('sidebar-focused');tmpRows[pIndex].scrollIntoView({block:'nearest',behavior:'smooth'});}}/**
|
|
8768
9285
|
* Handle keyboard events in viewer mode.
|
|
8769
|
-
*/_handleViewerKey(pEvent){
|
|
9286
|
+
*/_handleViewerKey(pEvent){let tmpRemote=this.pict.AppData.RetoldRemote;// Video action menu mode — intercept keys for menu options
|
|
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;}}/**
|
|
8770
9288
|
* Handle keyboard events in video explorer mode.
|
|
8771
9289
|
*/_handleVideoExplorerKey(pEvent){switch(pEvent.key){case'Escape':pEvent.preventDefault();let tmpVEX=this.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.goBack();}break;}}/**
|
|
8772
9290
|
* Handle keyboard events in audio explorer mode.
|
|
@@ -8778,11 +9296,22 @@ if(pIndex<tmpRows.length){tmpRows[pIndex].classList.add('sidebar-focused');tmpRo
|
|
|
8778
9296
|
let tmpOldTile=document.querySelector(`.retold-remote-tile[data-index="${tmpOldIndex}"], .retold-remote-list-row[data-index="${tmpOldIndex}"]`);let tmpNewTile=document.querySelector(`.retold-remote-tile[data-index="${pNewIndex}"], .retold-remote-list-row[data-index="${pNewIndex}"]`);if(tmpOldTile){tmpOldTile.classList.remove('selected');}if(tmpNewTile){tmpNewTile.classList.add('selected');// Scroll the tile into view if needed
|
|
8779
9297
|
tmpNewTile.scrollIntoView({block:'nearest',behavior:'smooth'});}}/**
|
|
8780
9298
|
* Open the currently selected gallery item.
|
|
8781
|
-
*/openCurrent(){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'){//
|
|
9299
|
+
*/openCurrent(){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'){// Remember cursor position in the current folder before navigating away
|
|
9300
|
+
let tmpCurrentLocation=this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation||'';tmpRemote.FolderCursorHistory[tmpCurrentLocation]=tmpIndex;// Navigate into the folder or archive
|
|
8782
9301
|
let tmpApp=this.pict.PictApplication;if(tmpApp&&tmpApp.loadFileList){tmpApp.loadFileList(tmpItem.Path);}}else{// Open the file in the viewer
|
|
8783
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);}}/**
|
|
8784
9312
|
* Navigate up one directory level.
|
|
8785
|
-
*/navigateUp(){let tmpCurrentLocation=this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation||'';if(!tmpCurrentLocation){return;}
|
|
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
|
|
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);}}/**
|
|
8786
9315
|
* Close the viewer and return to gallery mode.
|
|
8787
9316
|
*/closeViewer(){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='gallery';let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');if(tmpGalleryContainer)tmpGalleryContainer.style.display='';if(tmpViewerContainer)tmpViewerContainer.style.display='none';// Restore the hash to the browse route (use hashed identifier when available)
|
|
8788
9317
|
let tmpCurrentLocation=this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation||'';let tmpFragProvider=this.pict.providers['RetoldRemote-Provider'];let tmpFragId=tmpFragProvider&&tmpCurrentLocation?tmpFragProvider.getFragmentIdentifier(tmpCurrentLocation):tmpCurrentLocation;window.location.hash=tmpFragId?'#/browse/'+tmpFragId:'#/browse/';// Re-render gallery to ensure cursor is visible
|
|
@@ -8859,6 +9388,12 @@ if(tmpRemote.CurrentViewerMediaType!=='video'){return;}// Check if VLC is availa
|
|
|
8859
9388
|
let tmpCapabilities=tmpRemote.ServerCapabilities||{};if(!tmpCapabilities.vlc){return;}let tmpFilePath=tmpRemote.CurrentViewerFile;if(!tmpFilePath){return;}// Show a brief toast
|
|
8860
9389
|
this._showToast('Opening in VLC...');// POST to the server to open the file
|
|
8861
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);}/**
|
|
8862
9397
|
* Show a brief toast notification in the viewer.
|
|
8863
9398
|
*
|
|
8864
9399
|
* @param {string} pMessage - Text to display
|
|
@@ -9084,7 +9619,7 @@ return this.getIcon('file',tmpSize);}/**
|
|
|
9084
9619
|
* @returns {Object} Extension-to-icon name map
|
|
9085
9620
|
*/getExtensionMap(){return Object.assign({},this._extensionMap);}/**
|
|
9086
9621
|
* Inject CSS classes for icon sizing into the pict CSSMap.
|
|
9087
|
-
*/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":
|
|
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};/**
|
|
9088
9623
|
* Theme provider for retold-remote.
|
|
9089
9624
|
*
|
|
9090
9625
|
* Manages 15 themes (5 grey-only + 10 fun) via CSS custom properties.
|
|
@@ -10735,10 +11270,89 @@ tmpHandle.addEventListener('dblclick',function(pEvent){pEvent.preventDefault();t
|
|
|
10735
11270
|
font-family: inherit;
|
|
10736
11271
|
white-space: nowrap;
|
|
10737
11272
|
}
|
|
10738
|
-
.retold-remote-vlc-btn:hover
|
|
11273
|
+
.retold-remote-vlc-btn:hover
|
|
11274
|
+
{
|
|
11275
|
+
background: var(--retold-accent);
|
|
11276
|
+
color: var(--retold-bg-primary);
|
|
11277
|
+
}
|
|
11278
|
+
/* Video action menu */
|
|
11279
|
+
.retold-remote-video-action-menu
|
|
11280
|
+
{
|
|
11281
|
+
display: flex;
|
|
11282
|
+
flex-direction: column;
|
|
11283
|
+
align-items: center;
|
|
11284
|
+
justify-content: center;
|
|
11285
|
+
gap: 12px;
|
|
11286
|
+
width: 100%;
|
|
11287
|
+
height: 100%;
|
|
11288
|
+
}
|
|
11289
|
+
.retold-remote-video-action-menu-title
|
|
11290
|
+
{
|
|
11291
|
+
font-size: 0.85rem;
|
|
11292
|
+
color: var(--retold-text-secondary);
|
|
11293
|
+
margin-bottom: 4px;
|
|
11294
|
+
text-align: center;
|
|
11295
|
+
overflow: hidden;
|
|
11296
|
+
text-overflow: ellipsis;
|
|
11297
|
+
max-width: 80%;
|
|
11298
|
+
}
|
|
11299
|
+
.retold-remote-video-action-thumb-wrap
|
|
11300
|
+
{
|
|
11301
|
+
margin-bottom: 4px;
|
|
11302
|
+
text-align: center;
|
|
11303
|
+
}
|
|
11304
|
+
.retold-remote-video-action-thumb-wrap img
|
|
11305
|
+
{
|
|
11306
|
+
max-width: 640px;
|
|
11307
|
+
max-height: 360px;
|
|
11308
|
+
border-radius: 6px;
|
|
11309
|
+
border: 1px solid var(--retold-border);
|
|
11310
|
+
object-fit: contain;
|
|
11311
|
+
background: var(--retold-bg-primary);
|
|
11312
|
+
}
|
|
11313
|
+
.retold-remote-video-action-thumb-wrap .retold-remote-video-action-thumb-loading
|
|
11314
|
+
{
|
|
11315
|
+
color: var(--retold-text-dim);
|
|
11316
|
+
font-size: 0.78rem;
|
|
11317
|
+
font-style: italic;
|
|
11318
|
+
padding: 8px;
|
|
11319
|
+
}
|
|
11320
|
+
.retold-remote-video-action-btn
|
|
11321
|
+
{
|
|
11322
|
+
display: flex;
|
|
11323
|
+
align-items: center;
|
|
11324
|
+
gap: 12px;
|
|
11325
|
+
padding: 12px 24px;
|
|
11326
|
+
min-width: 280px;
|
|
11327
|
+
border: 1px solid var(--retold-border);
|
|
11328
|
+
border-radius: 6px;
|
|
11329
|
+
background: var(--retold-bg-secondary);
|
|
11330
|
+
color: var(--retold-text-secondary);
|
|
11331
|
+
font-size: 0.85rem;
|
|
11332
|
+
cursor: pointer;
|
|
11333
|
+
transition: border-color 0.15s, color 0.15s, background 0.15s;
|
|
11334
|
+
font-family: inherit;
|
|
11335
|
+
text-align: left;
|
|
11336
|
+
}
|
|
11337
|
+
.retold-remote-video-action-btn:hover,
|
|
11338
|
+
.retold-remote-video-action-btn.selected
|
|
11339
|
+
{
|
|
11340
|
+
border-color: var(--retold-accent);
|
|
11341
|
+
color: var(--retold-text-primary);
|
|
11342
|
+
background: var(--retold-bg-tertiary);
|
|
11343
|
+
}
|
|
11344
|
+
.retold-remote-video-action-key
|
|
10739
11345
|
{
|
|
10740
|
-
|
|
10741
|
-
|
|
11346
|
+
display: inline-block;
|
|
11347
|
+
padding: 2px 8px;
|
|
11348
|
+
border: 1px solid var(--retold-border);
|
|
11349
|
+
border-radius: 3px;
|
|
11350
|
+
background: var(--retold-bg-primary);
|
|
11351
|
+
color: var(--retold-text-dim);
|
|
11352
|
+
font-size: 0.72rem;
|
|
11353
|
+
font-family: var(--retold-font-mono, monospace);
|
|
11354
|
+
min-width: 24px;
|
|
11355
|
+
text-align: center;
|
|
10742
11356
|
}
|
|
10743
11357
|
/* Ebook reader */
|
|
10744
11358
|
.retold-remote-ebook-wrap
|
|
@@ -10867,7 +11481,7 @@ tmpHandle.addEventListener('dblclick',function(pEvent){pEvent.preventDefault();t
|
|
|
10867
11481
|
*
|
|
10868
11482
|
* @param {string} pFilePath - Relative file path
|
|
10869
11483
|
* @param {string} pMediaType - 'image', 'video', 'audio', 'document', or 'other'
|
|
10870
|
-
*/showMedia(pFilePath,pMediaType){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='viewer';tmpRemote.CurrentViewerFile=pFilePath;tmpRemote.CurrentViewerMediaType=pMediaType;// Show viewer, hide gallery
|
|
11484
|
+
*/showMedia(pFilePath,pMediaType){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='viewer';tmpRemote.CurrentViewerFile=pFilePath;tmpRemote.CurrentViewerMediaType=pMediaType;tmpRemote.VideoMenuActive=pMediaType==='video';// Show viewer, hide gallery
|
|
10871
11485
|
let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');if(tmpGalleryContainer)tmpGalleryContainer.style.display='none';if(tmpViewerContainer)tmpViewerContainer.style.display='block';let tmpFileName=pFilePath.replace(/^.*\//,'');let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpContentURL=tmpProvider?tmpProvider.getContentURL(pFilePath):'/content/'+encodeURIComponent(pFilePath);// Build the viewer HTML
|
|
10872
11486
|
let tmpHTML='<div class="retold-remote-viewer">';// Header with nav
|
|
10873
11487
|
tmpHTML+='<div class="retold-remote-viewer-header">';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].closeViewer()" title="Back (Esc)">← Back</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].prevFile()" title="Previous (k)">‹ Prev</button>';tmpHTML+='<div class="retold-remote-viewer-title">'+this._escapeHTML(tmpFileName)+'</div>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].nextFile()" title="Next (j)">Next ›</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleFileInfo()" title="Info (i)">ⓘ</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleFullscreen()" title="Fullscreen (f)">▢</button>';tmpHTML+='</div>';// Body with media content
|
|
@@ -10878,13 +11492,27 @@ if(tmpViewerContainer){tmpViewerContainer.innerHTML=tmpHTML;}// Fetch and popula
|
|
|
10878
11492
|
this._loadFileInfo(pFilePath);// Fetch text content and initialize code viewer
|
|
10879
11493
|
if(pMediaType==='text'){this._loadCodeViewer(tmpContentURL,pFilePath);}// Load ebook viewer for epub/mobi
|
|
10880
11494
|
if(pMediaType==='document'){let tmpExt=pFilePath.replace(/^.*\./,'').toLowerCase();if(tmpExt==='epub'||tmpExt==='mobi'){this._loadEbookViewer(tmpContentURL,pFilePath);}}// Update topbar
|
|
10881
|
-
let tmpTopBar=this.pict.views['ContentEditor-TopBar'];if(tmpTopBar){tmpTopBar.updateInfo();}}_buildImageHTML(pURL,pFileName){return'<img src="'+pURL+'" alt="'+this._escapeHTML(pFileName)+'" '+'style="max-width: 100%; max-height: 100%; object-fit: contain; cursor: zoom-in;" '+'id="RetoldRemote-ImageViewer-Img" '+'onload="pict.views[\'RetoldRemote-ImageViewer\'].initImage()" '+'onclick="pict.views[\'RetoldRemote-ImageViewer\'].toggleZoom()">';}_buildVideoHTML(pURL,pFileName){let
|
|
10882
|
-
tmpHTML
|
|
10883
|
-
|
|
10884
|
-
|
|
11495
|
+
let tmpTopBar=this.pict.views['ContentEditor-TopBar'];if(tmpTopBar){tmpTopBar.updateInfo();}}_buildImageHTML(pURL,pFileName){return'<img src="'+pURL+'" alt="'+this._escapeHTML(pFileName)+'" '+'style="max-width: 100%; max-height: 100%; object-fit: contain; cursor: zoom-in;" '+'id="RetoldRemote-ImageViewer-Img" '+'onload="pict.views[\'RetoldRemote-ImageViewer\'].initImage()" '+'onclick="pict.views[\'RetoldRemote-ImageViewer\'].toggleZoom()">';}_buildVideoHTML(pURL,pFileName){let tmpCapabilities=this.pict.AppData.RetoldRemote.ServerCapabilities||{};let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilePath=tmpRemote.CurrentViewerFile;// Build the action menu (shown by default instead of the player)
|
|
11496
|
+
let tmpHTML='<div class="retold-remote-video-action-menu" id="RetoldRemote-VideoActionMenu">';tmpHTML+='<div class="retold-remote-video-action-menu-title">'+this._escapeHTML(pFileName)+'</div>';// Frame preview container (loaded on demand via t key or automatically)
|
|
11497
|
+
if(tmpCapabilities.ffmpeg){tmpHTML+='<div id="RetoldRemote-VideoActionThumb" class="retold-remote-video-action-thumb-wrap"></div>';// Kick off frame extraction automatically
|
|
11498
|
+
setTimeout(()=>{this.loadVideoMenuFrame();},0);}// Explore option (e)
|
|
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)
|
|
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)
|
|
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;}/**
|
|
11503
|
+
* Launch the in-browser video player (from the video action menu).
|
|
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
|
|
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
|
|
10885
11506
|
tmpHTML+='</div>';// end wrap
|
|
10886
|
-
|
|
10887
|
-
let
|
|
11507
|
+
// Replace the action menu with the player
|
|
11508
|
+
let tmpMenu=document.getElementById('RetoldRemote-VideoActionMenu');if(tmpMenu){tmpMenu.outerHTML=tmpHTML;}// Mark that we are now in player mode (not menu mode)
|
|
11509
|
+
tmpRemote.VideoMenuActive=false;}/**
|
|
11510
|
+
* Extract and display a single full-resolution frame from the midpoint of the current video.
|
|
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
|
|
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>':'🎵';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
|
|
10888
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>':'📄';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>';}/**
|
|
10889
11517
|
* Map a file extension to a pict-section-code highlight language.
|
|
10890
11518
|
*
|
|
@@ -11016,6 +11644,64 @@ let tmpExploreBtn=tmpStatsBar.querySelector('.retold-remote-explore-btn');let tm
|
|
|
11016
11644
|
{
|
|
11017
11645
|
color: var(--retold-danger-muted);
|
|
11018
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
|
+
}
|
|
11667
|
+
.retold-remote-settings-shortcut-group
|
|
11668
|
+
{
|
|
11669
|
+
margin-bottom: 10px;
|
|
11670
|
+
}
|
|
11671
|
+
.retold-remote-settings-shortcut-group-title
|
|
11672
|
+
{
|
|
11673
|
+
font-size: 0.68rem;
|
|
11674
|
+
font-weight: 600;
|
|
11675
|
+
color: var(--retold-text-muted);
|
|
11676
|
+
margin-bottom: 4px;
|
|
11677
|
+
padding-bottom: 2px;
|
|
11678
|
+
border-bottom: 1px solid var(--retold-border);
|
|
11679
|
+
}
|
|
11680
|
+
.retold-remote-settings-shortcut-row
|
|
11681
|
+
{
|
|
11682
|
+
display: flex;
|
|
11683
|
+
justify-content: space-between;
|
|
11684
|
+
align-items: center;
|
|
11685
|
+
padding: 2px 0;
|
|
11686
|
+
}
|
|
11687
|
+
.retold-remote-settings-shortcut-desc
|
|
11688
|
+
{
|
|
11689
|
+
color: var(--retold-text-dim);
|
|
11690
|
+
font-size: 0.72rem;
|
|
11691
|
+
}
|
|
11692
|
+
.retold-remote-settings-shortcut-key
|
|
11693
|
+
{
|
|
11694
|
+
display: inline-block;
|
|
11695
|
+
padding: 1px 6px;
|
|
11696
|
+
border: 1px solid var(--retold-border);
|
|
11697
|
+
border-radius: 3px;
|
|
11698
|
+
background: var(--retold-bg-primary);
|
|
11699
|
+
color: var(--retold-accent);
|
|
11700
|
+
font-size: 0.68rem;
|
|
11701
|
+
font-family: var(--retold-font-mono, monospace);
|
|
11702
|
+
min-width: 18px;
|
|
11703
|
+
text-align: center;
|
|
11704
|
+
}
|
|
11019
11705
|
`};class RetoldRemoteSettingsPanelView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}onAfterRender(){super.onAfterRender();this._renderSettingsContent();}_renderSettingsContent(){let tmpContainer=document.getElementById('RetoldRemote-Settings-Container');if(!tmpContainer){return;}let tmpRemote=this.pict.AppData.RetoldRemote;let tmpCapabilities=tmpRemote.ServerCapabilities||{};let tmpHTML='<div class="retold-remote-settings">';// Appearance section (theme dropdown)
|
|
11020
11706
|
tmpHTML+='<div class="retold-remote-settings-section">';tmpHTML+='<div class="retold-remote-settings-section-title">Appearance</div>';tmpHTML+='<div class="retold-remote-settings-row">';tmpHTML+='<span class="retold-remote-settings-label">Theme</span>';tmpHTML+='<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeTheme(this.value)">';let tmpThemeProvider=this.pict.providers['RetoldRemote-Theme'];if(tmpThemeProvider){let tmpThemes=tmpThemeProvider.getThemeList();let tmpCurrentTheme=tmpThemeProvider.getCurrentTheme();let tmpCurrentCategory='';for(let i=0;i<tmpThemes.length;i++){let tmpTheme=tmpThemes[i];if(tmpTheme.category!==tmpCurrentCategory){if(tmpCurrentCategory){tmpHTML+='</optgroup>';}tmpHTML+='<optgroup label="'+tmpTheme.category+'">';tmpCurrentCategory=tmpTheme.category;}tmpHTML+='<option value="'+tmpTheme.key+'"'+(tmpTheme.key===tmpCurrentTheme?' selected':'')+'>'+tmpTheme.name+'</option>';}if(tmpCurrentCategory){tmpHTML+='</optgroup>';}}tmpHTML+='</select>';tmpHTML+='</div>';tmpHTML+='</div>';// end appearance section
|
|
11021
11707
|
// Gallery section
|
|
@@ -11029,10 +11715,11 @@ tmpHTML+='<div class="retold-remote-settings-row">';tmpHTML+='<span class="retol
|
|
|
11029
11715
|
// Server capabilities
|
|
11030
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
|
|
11031
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
|
|
11032
|
-
//
|
|
11033
|
-
tmpHTML+='<div class="retold-remote-settings-section">';tmpHTML+='<div class="retold-remote-settings-section-title">
|
|
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
|
|
11034
11721
|
tmpHTML+='</div>';// end settings
|
|
11035
|
-
tmpContainer.innerHTML=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
|
|
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
|
|
11036
11723
|
this._renderSettingsContent();}}changeSetting(pKey,pValue){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote[pKey]=pValue;this.pict.PictApplication.saveSettings();// Re-render gallery if visible
|
|
11037
11724
|
if(tmpRemote.ActiveMode==='gallery'){let tmpGalleryView=this.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tmpGalleryView.renderGallery();}}}toggleHiddenFiles(pChecked){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ShowHiddenFiles=pChecked;this.pict.PictApplication.saveSettings();this.pict.PictApplication.syncHiddenFilesSetting(()=>{this.pict.PictApplication.loadFileList();});}toggleAutoplay(pKey,pChecked){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote[pKey]=pChecked;this.pict.PictApplication.saveSettings();}toggleDistractionFreeNav(pChecked){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.DistractionFreeShowNav=pChecked;this.pict.PictApplication.saveSettings();// If currently in distraction-free mode, apply immediately
|
|
11038
11725
|
if(tmpRemote._distractionFreeMode){let tmpViewerHeader=document.querySelector('.retold-remote-viewer-header');if(tmpViewerHeader){tmpViewerHeader.style.display=pChecked?'':'none';}}}}RetoldRemoteSettingsPanelView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteSettingsPanelView;},{"pict-view":76}],128:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"ContentEditor-TopBar",DefaultRenderable:"RetoldRemote-TopBar",DefaultDestinationAddress:"#ContentEditor-TopBar-Container",AutoRender:false,CSS:/*css*/`
|
|
@@ -11179,7 +11866,259 @@ tmpBtn.classList.add('filter-active');tmpBtn.innerHTML='◓<span class="reto
|
|
|
11179
11866
|
tmpBtn.classList.add('filter-bar-open');tmpBtn.innerHTML='◢';tmpBtn.title='Hide filter bar (/)';}else{// Default: no filters, bar hidden
|
|
11180
11867
|
tmpBtn.innerHTML='◢';tmpBtn.title='Toggle filter bar (/)';}}/**
|
|
11181
11868
|
* Update the info display with folder summary.
|
|
11182
|
-
*/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-
|
|
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}_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*/`
|
|
11183
12122
|
.retold-remote-vex
|
|
11184
12123
|
{
|
|
11185
12124
|
display: flex;
|