Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions javascript/markdown_browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

var markdown_browser_subFilePath = undefined;

function markdown_browser_openSubFile_(dummy, extPath, extName) {
return [markdown_browser_subFilePath, extPath, extName];
}


function markdown_browser_openSubFile(filePath) {
markdown_browser_subFilePath = decodeURI(filePath);
let button = gradioApp().getElementById('markdown_browser_openSubFileButton');
button.click();
}




function markdown_browser_alreadyHasAnchor(h) {
let elem = h.previousSibling;
if (!elem?.classList?.contains('markdown_browser_h_anchor')) return false;
return true;
}


function markdown_browser_afterRender() {
let file = gradioApp().getElementById('markdown_browser_file');
let hElements = [...file.querySelectorAll("h1, h2, h3, h4, h5, h6")];
let anchorNumbers = {};
hElements.forEach((h) => {
if (markdown_browser_alreadyHasAnchor(h)) return;
if (!h.innerHTML) return;
let anchor = document.createElement('a');
let anchorID = h.innerText.toLowerCase().replaceAll(' ', '-').replace(/[^a-zA-Z0-9-_]/g, '');
if (anchorID in anchorNumbers) {
let key = anchorID;
anchorID += '-' + anchorNumbers[key];
anchorNumbers[key] += 1;
} else {
anchorNumbers[anchorID] = 1;
}
anchor.setAttribute('id', anchorID);
anchor.classList.add('markdown_browser_h_anchor');
h.parentNode.insertBefore(anchor, h);
});


let aElements = [...file.getElementsByTagName('a')];
aElements.forEach((a) => {
if (!a.href) return;
const url = new URL(a.href);
if (url.origin === window.location.origin && a.href.indexOf('#') !== -1) {
a.setAttribute('target', '');
return;
}
const prefix = 'markdown_browser_javascript_';
const prefixIndex = a.href.indexOf(prefix);
if (prefixIndex !== -1) {
let onClick = a.href.slice(prefixIndex + prefix.length);
onClick = decodeURI(onClick).replaceAll("%2C", ",");
a.setAttribute('onclick', onClick);
a.setAttribute('target', '');
a.href = '#markdown_browser_top_anchor';
}
});
}


95 changes: 95 additions & 0 deletions replacer/other/markdown_browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import os
import urllib.parse
import gradio as gr
from replacer.other.markdown_browser_tools import ( getURLsFromFile, JS_PREFIX, isLocalURL, isAnchor, isMarkdown,
replaceURLInFile, getAllDocuments,
)
from replacer.options import EXT_ROOT_DIRECTORY


def renderMarkdownFile(filePath: str, extDir: str):
with open(filePath, mode='r', encoding="utf-8-sig") as f:
file = f.read()

for url in getURLsFromFile(file):
originalURL = url
replacementUrl = None
if JS_PREFIX in originalURL:
file = file.replace(originalURL, "***")
continue

if isLocalURL(url):
if isAnchor(url): continue
if '#' in url:
url = url.removesuffix('#' + url.split('#')[-1])

if url[0] == '/':
urlFullPath = os.path.join(extDir, url[1:])
else:
urlFullPath = os.path.join(os.path.dirname(filePath), url)

if os.path.exists(urlFullPath):
if isMarkdown(url):
replacementUrl = f"{JS_PREFIX}markdown_browser_openSubFile('{urlFullPath}')"
else:
replacementUrl = f'file={urlFullPath}'

if replacementUrl is not None:
replacementUrl = urllib.parse.quote(replacementUrl)
file = replaceURLInFile(file, originalURL, replacementUrl)

return file


def openSubFile(filePath: str):
file = renderMarkdownFile(filePath, EXT_ROOT_DIRECTORY)
return file

def openDocument(docName: str):
if not docName: return ""
file = renderMarkdownFile(g_documents[docName], EXT_ROOT_DIRECTORY)
return file


markdownFile = gr.Markdown("", elem_classes=['markdown-browser-file'], elem_id='markdown_browser_file')
g_documents = None

def getDocsTabUI():
global g_documents
g_documents = getAllDocuments()

with gr.Blocks() as tab:
dummy_component = gr.Textbox("", visible=False)

with gr.Row():
selectedDocument = gr.Dropdown(
label="Document",
value="",
choices=[""] + list(g_documents.keys())
)
selectButton = gr.Button('Select')
selectButton.click(
fn=openDocument,
inputs=[selectedDocument],
outputs=[markdownFile],
).then(
fn=None,
_js='markdown_browser_afterRender',
)

with gr.Row():
markdownFile.render()

openSubFileButton = gr.Button("", visible=False, elem_id="markdown_browser_openSubFileButton")
openSubFileButton.click(
fn=openSubFile,
_js="markdown_browser_openSubFile_",
inputs=[dummy_component],
outputs=[markdownFile]
).then(
fn=None,
_js='markdown_browser_afterRender',
)

return tab

89 changes: 89 additions & 0 deletions replacer/other/markdown_browser_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import re, os
from dataclasses import dataclass
from replacer.options import EXT_ROOT_DIRECTORY
from modules import shared

JS_PREFIX = 'markdown_browser_javascript_'


@dataclass
class Anchor:
name: str
id: str
depth: int


def getURLsFromFile(file: str) -> list[str]:
urls = set()

MDLinks = re.findall(r'\[.*?\]\((.+?)\)', file)
for link in MDLinks:
urls.add(link)

srcLinks = re.findall(r'src="(.+?)"', file)
for link in srcLinks:
urls.add(link)

hrefLinks = re.findall(r'href="(.+?)"', file)
for link in hrefLinks:
urls.add(link)

httpsLinks = re.findall(r'(^|\s)(https://.+?)($|\s)', file, re.MULTILINE)
for link in httpsLinks:
link = link[1].removesuffix('.')
urls.add(link)

return urls


def replaceURLInFile(file: str, oldUrl: str, newUrl: str) -> str:
foundIdx = file.find(oldUrl)
while foundIdx != -1:
try:
needReplaceLeft = False
if file[foundIdx-len('href="'):foundIdx] == 'href="':
needReplaceLeft = True
elif file[foundIdx-len('src="'):foundIdx] == 'src="':
needReplaceLeft = True
elif file[foundIdx-len(']('):foundIdx] == '](':
needReplaceLeft = True
elif oldUrl.lower().startswith('https://'):
needReplaceLeft = True
newUrl = f'[{newUrl}]({newUrl})'

needReplaceRight = False
if file[foundIdx+len(oldUrl)] in ')]}>"\' \\\n.,':
needReplaceRight = True

if needReplaceLeft and needReplaceRight:
file = file[0:foundIdx] + newUrl + file[foundIdx+len(oldUrl):]

except IndexError:
pass

foundIdx = file.find(oldUrl, foundIdx+1)

return file


def isLocalURL(url: str):
return not ('://' in url or url.startswith('//'))

def isAnchor(url: str):
return url.startswith('#')

def isMarkdown(url: str):
if '#' in url:
url = url.removesuffix('#' + url.split('#')[-1])
return url.endswith('.md')


def getAllDocuments() -> dict[str, str]:
docs = dict()
files = shared.listfiles(os.path.join(EXT_ROOT_DIRECTORY, 'docs'))
for file in files:
if not file.endswith(".md"): continue
fileName = os.path.basename(file).removesuffix(".md").capitalize()
docs[fileName] = file
docs["Readme"] = os.path.join(EXT_ROOT_DIRECTORY, 'README.md')
return docs
3 changes: 3 additions & 0 deletions scripts/replacer_main_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from replacer.tools import getReplacerFooter
from replacer.ui.tools_ui import watchOutputPanel, watchSetCustomScriptSourceForComponents
from replacer.extensions import replacer_extensions
from replacer.other.markdown_browser import getDocsTabUI



Expand Down Expand Up @@ -50,6 +51,8 @@ def mountDedicatedPage(demo, app):
with gr.Blocks(analytics_enabled=False) as extras_interface:
ui_postprocessing.create_ui()
replacer_extensions.image_comparison.mountImageComparisonTab()
with gr.Tab(label="Docs", elem_id=f"tab_docs"):
tab_docs = getDocsTabUI()

footer = getReplacerFooter()
gr.HTML(footer, elem_id="footer")
Expand Down
60 changes: 60 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,63 @@ button.replacer-pause-button{
#replacer_video_gallery {
height: 440px;
}





.markdown-browser-file
{
max-width: 1012px;
margin-right: auto !important;
margin-left: auto !important;
font-size: 16px !important;
line-height: 1.1 !important;
overflow-wrap: break-word;
}

.markdown-browser-file table
{
display: block;
overflow-x: auto;
}

.markdown-browser-file strong
{
font-size: 105% !important;
}

.markdown-browser-file a[href]
{
text-decoration: underline dotted !important;
}

.markdown-browser-file a[href]:where([href="#markdown_browser_top_anchor"])::after
{
content: '↪';
font-size: 60% !important;
font-family: monospace;
}

.markdown-browser-file a[href]:where([href^="#"]:not([href="#markdown_browser_top_anchor"]))::after
{
content: '#';
font-size: 65% !important;
font-family: monospace;
}

.markdown-browser-file p, .markdown-browser-file ol, .markdown-browser-file ul
{
margin-bottom: 22px !important;
}

.markdown-browser-file details
{
margin-bottom: 8px !important;
}

.markdown-browser-file a[href]:not([href*="://"]):not([href^="#"])::before
{
content: '📎';
font-size: 88% !important;
}