update booru tag prompter node
This commit is contained in:
@@ -17,6 +17,8 @@ tests/
|
|||||||
/.wildcard_settings.json
|
/.wildcard_settings.json
|
||||||
/favorites/
|
/favorites/
|
||||||
/.favorite_settings.json
|
/.favorite_settings.json
|
||||||
|
/nodes/data/danbooru_wiki.sqlite3
|
||||||
|
/nodes/data/.danbooru_wiki_*.download
|
||||||
|
|
||||||
# Local agent instructions
|
# Local agent instructions
|
||||||
/AGENTS.md
|
/AGENTS.md
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ Enter tags in `tags` and connect the `tags` output to a text encoder. Autocomple
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **W** (Wildcards) | Lists files in `wildcards/`. Click a file to insert `__name__`; each execution replaces it with one random non-empty line from that file. **Folder** opens the folder and **↻** refreshes the list. |
|
| **W** (Wildcards) | Lists files in `wildcards/`. Click a file to insert `__name__`; each execution replaces it with one random non-empty line from that file. **Folder** opens the folder and **↻** refreshes the list. |
|
||||||
| **F** (Favorites) | **Save** stores the entire current prompt. Click an entry to insert it at the cursor; right-click to edit or delete it. **↻** refreshes the list. |
|
| **F** (Favorites) | **Save** stores the entire current prompt. Click an entry to insert it at the cursor; right-click to edit or delete it. **↻** refreshes the list. |
|
||||||
| **Wiki** | Opens a movable offline browser. Browse categories, search, follow wiki links, and use **Insert** on a tag entry to add it at the cursor. Drag the title bar to move the window or its bottom-right corner to resize it. |
|
| **Wiki** | On first use, asks to download the offline database from Hugging Face (about 272 MiB). Later uses work offline. Browse categories, search, follow wiki links, and use **Insert** on a tag entry to add it at the cursor. Drag the title bar to move the window or its bottom-right corner to resize it. |
|
||||||
| `camera` | Optionally connect `booru tag camera`; its selected camera guidance is appended after your text. |
|
| `camera` | Optionally connect `booru tag camera`; its selected camera guidance is appended after your text. |
|
||||||
|
|
||||||
The node does not use Qwen or invent additional tags. Wildcard file contents and manually entered prompt text are preserved as written.
|
The node does not use Qwen or invent additional tags. Wildcard file contents and manually entered prompt text are preserved as written.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from .booru_wildcards import TOKEN, expand_wildcards, wildcard_files, wildcard_p
|
|||||||
from .booru_favorites import (FavoriteConflictError, active_favorite_root,
|
from .booru_favorites import (FavoriteConflictError, active_favorite_root,
|
||||||
browse_favorite_folder, change_favorite,
|
browse_favorite_folder, change_favorite,
|
||||||
list_favorites, save_favorite, set_favorite_path)
|
list_favorites, save_favorite, set_favorite_path)
|
||||||
from .booru_wiki import search_wiki, wiki_categories, wiki_page
|
from .booru_wiki import download_wiki_db, search_wiki, wiki_categories, wiki_download_status, wiki_page
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@@ -115,6 +115,23 @@ def register_routes():
|
|||||||
origin = request.headers.get("Origin")
|
origin = request.headers.get("Origin")
|
||||||
return request.remote in ("127.0.0.1", "::1") and (not origin or urlsplit(origin).netloc == request.host)
|
return request.remote in ("127.0.0.1", "::1") and (not origin or urlsplit(origin).netloc == request.host)
|
||||||
|
|
||||||
|
@PromptServer.instance.routes.get("/toyxyz/booru-tags/wiki/status")
|
||||||
|
async def wiki_status_route(request):
|
||||||
|
if not local_request(request):
|
||||||
|
return web.json_response({"error": "Wiki status requires local access."}, status=403)
|
||||||
|
return web.json_response(await asyncio.to_thread(wiki_download_status))
|
||||||
|
|
||||||
|
@PromptServer.instance.routes.post("/toyxyz/booru-tags/wiki/download")
|
||||||
|
async def wiki_download_route(request):
|
||||||
|
if not local_request(request):
|
||||||
|
return web.json_response({"error": "Wiki download requires local access."}, status=403)
|
||||||
|
try:
|
||||||
|
await asyncio.to_thread(download_wiki_db)
|
||||||
|
except OSError as exc:
|
||||||
|
LOG.warning("Wiki download failed: %s", exc)
|
||||||
|
return web.json_response({"error": str(exc)}, status=503)
|
||||||
|
return web.json_response({"ready": True})
|
||||||
|
|
||||||
@PromptServer.instance.routes.get("/toyxyz/booru-tags/wiki/categories")
|
@PromptServer.instance.routes.get("/toyxyz/booru-tags/wiki/categories")
|
||||||
async def wiki_categories_route(request):
|
async def wiki_categories_route(request):
|
||||||
if not local_request(request):
|
if not local_request(request):
|
||||||
|
|||||||
+79
-2
@@ -5,19 +5,96 @@ from __future__ import annotations
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import threading
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_DB = Path(__file__).resolve().parent / "data" / "danbooru_wiki.sqlite3"
|
DEFAULT_DB = Path(__file__).resolve().parent / "data" / "danbooru_wiki.sqlite3"
|
||||||
|
WIKI_DOWNLOAD_URL = "https://huggingface.co/toyxyz/backup_models/resolve/main/danbooru_wiki.sqlite3"
|
||||||
|
WIKI_SHA256 = "c17b31e0f6468d2a0d5094452085925643d8442a2433f798f74f26923758bac6"
|
||||||
|
WIKI_SIZE = 284_766_208
|
||||||
|
MAX_DOWNLOAD_SIZE = 400 * 1024 * 1024
|
||||||
|
_download_lock = threading.Lock()
|
||||||
|
_progress_lock = threading.Lock()
|
||||||
|
_download_progress = {"downloading": False, "bytes": 0}
|
||||||
PAGE_SIZE = 30
|
PAGE_SIZE = 30
|
||||||
WIKI_LINK = re.compile(r"\[\[([^\]]+)\]\]")
|
WIKI_LINK = re.compile(r"\[\[([^\]]+)\]\]")
|
||||||
|
|
||||||
|
|
||||||
|
def wiki_db_ready() -> bool:
|
||||||
|
"""Reject missing files and Git LFS pointers without reading the entire DB."""
|
||||||
|
try:
|
||||||
|
if DEFAULT_DB.stat().st_size < 100:
|
||||||
|
return False
|
||||||
|
with DEFAULT_DB.open("rb") as source:
|
||||||
|
return source.read(16) == b"SQLite format 3\x00"
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def wiki_download_status() -> dict:
|
||||||
|
ready = wiki_db_ready()
|
||||||
|
with _progress_lock:
|
||||||
|
downloading = _download_progress["downloading"]
|
||||||
|
received = _download_progress["bytes"]
|
||||||
|
return {"ready": ready, "downloading": downloading,
|
||||||
|
"percent": 100 if ready else min(99, received * 100 // WIKI_SIZE) if downloading else 0}
|
||||||
|
|
||||||
|
|
||||||
|
def download_wiki_db() -> Path:
|
||||||
|
"""Install the pinned snapshot atomically after verifying its SHA-256."""
|
||||||
|
with _download_lock:
|
||||||
|
if wiki_db_ready():
|
||||||
|
return DEFAULT_DB
|
||||||
|
DEFAULT_DB.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
temporary = None
|
||||||
|
with _progress_lock:
|
||||||
|
_download_progress.update(downloading=True, bytes=0)
|
||||||
|
try:
|
||||||
|
with tempfile.NamedTemporaryFile(dir=DEFAULT_DB.parent, prefix=".danbooru_wiki_",
|
||||||
|
suffix=".download", delete=False) as target:
|
||||||
|
temporary = Path(target.name)
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
size = 0
|
||||||
|
request = Request(WIKI_DOWNLOAD_URL, headers={"User-Agent": "toyxyz-booru-wiki/1.0"})
|
||||||
|
with urlopen(request, timeout=60) as source:
|
||||||
|
if source.status != 200:
|
||||||
|
raise OSError(f"Wiki download failed (HTTP {source.status}).")
|
||||||
|
while chunk := source.read(1024 * 1024):
|
||||||
|
size += len(chunk)
|
||||||
|
if size > MAX_DOWNLOAD_SIZE:
|
||||||
|
raise OSError("Wiki download exceeded the expected size.")
|
||||||
|
digest.update(chunk)
|
||||||
|
target.write(chunk)
|
||||||
|
with _progress_lock:
|
||||||
|
_download_progress["bytes"] = size
|
||||||
|
target.flush()
|
||||||
|
os.fsync(target.fileno())
|
||||||
|
if size != WIKI_SIZE or digest.hexdigest() != WIKI_SHA256:
|
||||||
|
raise OSError("Downloaded wiki database does not match the expected snapshot.")
|
||||||
|
with closing(sqlite3.connect(f"file:{quote(temporary.resolve().as_posix(), safe='/:')}?mode=ro",
|
||||||
|
uri=True)) as con:
|
||||||
|
con.execute("SELECT name FROM sqlite_master LIMIT 1").fetchone()
|
||||||
|
os.replace(temporary, DEFAULT_DB)
|
||||||
|
return DEFAULT_DB
|
||||||
|
except (OSError, sqlite3.Error, ValueError) as exc:
|
||||||
|
raise OSError(str(exc) or "Wiki download failed. Please retry.") from exc
|
||||||
|
finally:
|
||||||
|
with _progress_lock:
|
||||||
|
_download_progress.update(downloading=False, bytes=0)
|
||||||
|
if temporary is not None:
|
||||||
|
temporary.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def active_wiki_db() -> Path:
|
def active_wiki_db() -> Path:
|
||||||
if not DEFAULT_DB.is_file():
|
if not wiki_db_ready():
|
||||||
raise OSError("Bundled wiki database is missing from nodes/data.")
|
raise OSError("Wiki database is unavailable. Open Wiki to download it.")
|
||||||
return DEFAULT_DB
|
return DEFAULT_DB
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ This pinned snapshot contains 125,312 rows. It validates generated tag names
|
|||||||
offline; it does not establish that every tag is understood by a given Anima
|
offline; it does not establish that every tag is understood by a given Anima
|
||||||
checkpoint. Refresh the snapshot and revision together when updating it.
|
checkpoint. Refresh the snapshot and revision together when updating it.
|
||||||
|
|
||||||
`danbooru_wiki.sqlite3` is the bundled offline Danbooru wiki snapshot used by
|
`danbooru_wiki.sqlite3` is downloaded on first Wiki use from
|
||||||
the `booru tag prompter` Wiki panel. It is opened read-only and is not modified
|
`https://huggingface.co/toyxyz/backup_models/resolve/main/danbooru_wiki.sqlite3`
|
||||||
by node execution. SHA-256:
|
into this directory. It is not tracked in Git. Subsequent Wiki use is offline;
|
||||||
|
the file is opened read-only and is not modified by node execution. SHA-256:
|
||||||
`c17b31e0f6468d2a0d5094452085925643d8442a2433f798f74f26923758bac6`.
|
`c17b31e0f6468d2a0d5094452085925643d8442a2433f798f74f26923758bac6`.
|
||||||
The snapshot is separate from the tag autocomplete CSV and does not update
|
The snapshot is separate from the tag autocomplete CSV and does not update
|
||||||
automatically.
|
automatically.
|
||||||
|
|||||||
@@ -73,6 +73,20 @@
|
|||||||
.toyxyz-wildcard-tabs button.toyxyz-tab-active { background:#315b84; border-color:#71b8ff; color:#fff; box-shadow:inset 0 0 0 1px #71b8ff; }
|
.toyxyz-wildcard-tabs button.toyxyz-tab-active { background:#315b84; border-color:#71b8ff; color:#fff; box-shadow:inset 0 0 0 1px #71b8ff; }
|
||||||
.toyxyz-wiki-panel { position:fixed; z-index:10001; display:flex; flex-direction:column; box-sizing:border-box; height:min(760px,calc(100vh - 16px)); overflow:hidden; border:1px solid #647991; border-radius:9px; background:#222a34; color:#e9f0f7; box-shadow:0 16px 38px #000b; font:13px/1.5 sans-serif; }
|
.toyxyz-wiki-panel { position:fixed; z-index:10001; display:flex; flex-direction:column; box-sizing:border-box; height:min(760px,calc(100vh - 16px)); overflow:hidden; border:1px solid #647991; border-radius:9px; background:#222a34; color:#e9f0f7; box-shadow:0 16px 38px #000b; font:13px/1.5 sans-serif; }
|
||||||
.toyxyz-wiki-panel[hidden] { display:none; }
|
.toyxyz-wiki-panel[hidden] { display:none; }
|
||||||
|
.toyxyz-wiki-download-backdrop { position:fixed; z-index:10002; inset:0; display:flex; align-items:center; justify-content:center; padding:16px; background:#0009; }
|
||||||
|
.toyxyz-wiki-download-backdrop[hidden] { display:none; }
|
||||||
|
.toyxyz-wiki-download-box { box-sizing:border-box; width:min(440px,100%); padding:20px; border:1px solid #647991; border-radius:9px; background:#222a34; color:#e9f0f7; box-shadow:0 16px 38px #000b; font:13px/1.5 sans-serif; }
|
||||||
|
.toyxyz-wiki-download-box h2 { margin:0 0 10px; font-size:18px; }
|
||||||
|
.toyxyz-wiki-download-box p { margin:0 0 12px; }
|
||||||
|
.toyxyz-wiki-download-progress { display:flex; align-items:center; gap:10px; margin:0 0 12px; }
|
||||||
|
.toyxyz-wiki-download-progress[hidden] { display:none; }
|
||||||
|
.toyxyz-wiki-download-progress progress { flex:1; height:14px; accent-color:#75b8f5; }
|
||||||
|
.toyxyz-wiki-download-progress output { min-width:36px; text-align:right; font-variant-numeric:tabular-nums; }
|
||||||
|
.toyxyz-wiki-download-error { color:#ffbd9d; overflow-wrap:anywhere; }
|
||||||
|
.toyxyz-wiki-download-error:empty { display:none; }
|
||||||
|
.toyxyz-wiki-download-actions { display:flex; justify-content:flex-end; gap:8px; }
|
||||||
|
.toyxyz-wiki-download-actions button { padding:7px 12px; border:1px solid #637b91; border-radius:5px; background:#35485a; color:#eee; cursor:pointer; }
|
||||||
|
.toyxyz-wiki-download-actions button:disabled { opacity:.5; cursor:default; }
|
||||||
.toyxyz-wiki-panel button { border:1px solid #536a80; border-radius:5px; padding:5px 8px; background:#304155; color:#edf4fa; font:inherit; cursor:pointer; }
|
.toyxyz-wiki-panel button { border:1px solid #536a80; border-radius:5px; padding:5px 8px; background:#304155; color:#edf4fa; font:inherit; cursor:pointer; }
|
||||||
.toyxyz-wiki-panel button:hover { background:#415c77; }
|
.toyxyz-wiki-panel button:hover { background:#415c77; }
|
||||||
.toyxyz-wiki-panel button:disabled { opacity:.45; cursor:default; }
|
.toyxyz-wiki-panel button:disabled { opacity:.45; cursor:default; }
|
||||||
|
|||||||
+83
-6
@@ -116,13 +116,33 @@ export function installWiki(nodeRef,widget,input,api) {
|
|||||||
resizeGrip.tabIndex=0;
|
resizeGrip.tabIndex=0;
|
||||||
layout.append(sidebar,browse,detail);panel.append(header,layout,resizeGrip);document.body.append(panel);
|
layout.append(sidebar,browse,detail);panel.append(header,layout,resizeGrip);document.body.append(panel);
|
||||||
|
|
||||||
|
const downloadDialog=node('div','toyxyz-wiki-download-backdrop');downloadDialog.hidden=true;
|
||||||
|
const downloadBox=node('section','toyxyz-wiki-download-box');
|
||||||
|
downloadBox.setAttribute('role','dialog');downloadBox.setAttribute('aria-modal','true');
|
||||||
|
downloadBox.setAttribute('aria-label','Download Tag Wiki');
|
||||||
|
downloadBox.append(node('h2','','Download Tag Wiki'));
|
||||||
|
const downloadMessage=node('p','','The offline Tag Wiki database is not installed. Download approximately 272 MiB from Hugging Face? The file will be saved in this custom node folder.');
|
||||||
|
const progressRow=node('div','toyxyz-wiki-download-progress');progressRow.hidden=true;
|
||||||
|
const progressBar=node('progress');progressBar.max=100;progressBar.value=0;
|
||||||
|
progressBar.setAttribute('aria-label','Wiki download progress');
|
||||||
|
const progressLabel=node('output','','0%');
|
||||||
|
progressRow.append(progressBar,progressLabel);
|
||||||
|
const downloadError=node('p','toyxyz-wiki-download-error');downloadError.setAttribute('role','alert');
|
||||||
|
const downloadActions=node('div','toyxyz-wiki-download-actions');
|
||||||
|
const dismissDownload=node('button','','Cancel'),startDownload=node('button','','Download');
|
||||||
|
dismissDownload.type=startDownload.type='button';
|
||||||
|
downloadActions.append(dismissDownload,startDownload);
|
||||||
|
downloadBox.append(downloadMessage,progressRow,downloadError,downloadActions);
|
||||||
|
downloadDialog.append(downloadBox);document.body.append(downloadDialog);
|
||||||
|
|
||||||
const state={open:false,disposed:false,category:'',query:'',page:1,
|
const state={open:false,disposed:false,category:'',query:'',page:1,
|
||||||
searchToken:0,detailToken:0,history:[],frame:0,lastRect:'',timer:0,
|
searchToken:0,detailToken:0,history:[],frame:0,lastRect:'',timer:0,
|
||||||
manualPosition:false,dragging:null,resizing:null};
|
manualPosition:false,dragging:null,resizing:null,opening:false,downloading:false,
|
||||||
const readJSON=async url=>{
|
progressTimer:0,progressPending:false};
|
||||||
const response=await api.fetchApi(url);
|
const readJSON=async(url,options)=>{
|
||||||
|
const response=await api.fetchApi(url,options);
|
||||||
const data=await response.json().catch(()=>({}));
|
const data=await response.json().catch(()=>({}));
|
||||||
if(!response.ok)throw new Error(response.status===404&&url.includes('/categories')
|
if(!response.ok)throw new Error(response.status===404&&url.includes('/wiki/')
|
||||||
?'Restart ComfyUI to activate the Tag Wiki Browser.'
|
?'Restart ComfyUI to activate the Tag Wiki Browser.'
|
||||||
:data.error||`Wiki request failed (HTTP ${response.status}).`);
|
:data.error||`Wiki request failed (HTTP ${response.status}).`);
|
||||||
return data;
|
return data;
|
||||||
@@ -353,6 +373,63 @@ export function installWiki(nodeRef,widget,input,api) {
|
|||||||
++state.searchToken;++state.detailToken;
|
++state.searchToken;++state.detailToken;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const showDownload=()=>{
|
||||||
|
downloadDialog.hidden=false;
|
||||||
|
downloadMessage.textContent='The offline Tag Wiki database is not installed. Download approximately 272 MiB from Hugging Face? The file will be saved in this custom node folder.';
|
||||||
|
downloadError.textContent='';
|
||||||
|
dismissDownload.disabled=false;startDownload.disabled=false;
|
||||||
|
startDownload.textContent='Download';
|
||||||
|
progressBar.value=0;progressLabel.textContent='0%';progressRow.hidden=true;
|
||||||
|
};
|
||||||
|
dismissDownload.addEventListener('click',()=>{if(!state.downloading)downloadDialog.hidden=true;});
|
||||||
|
startDownload.addEventListener('click',async()=>{
|
||||||
|
if(state.downloading||state.disposed)return;
|
||||||
|
state.downloading=true;dismissDownload.disabled=true;startDownload.disabled=true;
|
||||||
|
downloadError.textContent='';
|
||||||
|
downloadMessage.textContent='Downloading and verifying the Tag Wiki database. Please wait; the wiki will open when ready.';
|
||||||
|
progressBar.value=0;progressLabel.textContent='0%';progressRow.hidden=false;
|
||||||
|
const refreshProgress=async()=>{
|
||||||
|
if(state.progressPending||!state.downloading||state.disposed)return;
|
||||||
|
state.progressPending=true;
|
||||||
|
try{
|
||||||
|
const result=await readJSON('/toyxyz/booru-tags/wiki/status');
|
||||||
|
if(!state.downloading||state.disposed)return;
|
||||||
|
const percent=Math.max(0,Math.min(99,Number(result.percent)||0));
|
||||||
|
progressBar.value=percent;progressLabel.textContent=`${percent}%`;
|
||||||
|
}catch{ /* The download request reports a final error if status is unavailable. */ }
|
||||||
|
finally{state.progressPending=false;}
|
||||||
|
};
|
||||||
|
state.progressTimer=setInterval(refreshProgress,250);
|
||||||
|
try{
|
||||||
|
await readJSON('/toyxyz/booru-tags/wiki/download', {method:'POST'});
|
||||||
|
if(state.disposed)return;
|
||||||
|
progressBar.value=100;progressLabel.textContent='100%';
|
||||||
|
downloadDialog.hidden=true;setOpen(true);
|
||||||
|
}catch(error){
|
||||||
|
if(!state.disposed){
|
||||||
|
downloadMessage.textContent='The download did not complete. Your prompt remains available.';
|
||||||
|
downloadError.textContent=error.message;
|
||||||
|
dismissDownload.disabled=false;startDownload.disabled=false;
|
||||||
|
startDownload.textContent='Retry';
|
||||||
|
}
|
||||||
|
}finally{
|
||||||
|
state.downloading=false;
|
||||||
|
clearInterval(state.progressTimer);state.progressTimer=0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const openWiki=async()=>{
|
||||||
|
if(state.open){setOpen(false);return;}
|
||||||
|
if(state.opening||state.downloading)return;
|
||||||
|
state.opening=true;toggle.disabled=true;
|
||||||
|
try{
|
||||||
|
const result=await readJSON('/toyxyz/booru-tags/wiki/status');
|
||||||
|
if(state.disposed)return;
|
||||||
|
if(result.ready)setOpen(true);
|
||||||
|
else showDownload();
|
||||||
|
}catch(error){
|
||||||
|
if(!state.disposed){showDownload();downloadError.textContent=error.message;}
|
||||||
|
}finally{state.opening=false;toggle.disabled=false;}
|
||||||
|
};
|
||||||
search.addEventListener('input',()=>{
|
search.addEventListener('input',()=>{
|
||||||
clearTimeout(state.timer);state.timer=setTimeout(()=>{
|
clearTimeout(state.timer);state.timer=setTimeout(()=>{
|
||||||
state.query=search.value.trim();state.page=1;loadResults();
|
state.query=search.value.trim();state.page=1;loadResults();
|
||||||
@@ -367,7 +444,7 @@ export function installWiki(nodeRef,widget,input,api) {
|
|||||||
});
|
});
|
||||||
previous.addEventListener('click',()=>{if(state.page>1){--state.page;loadResults();}});
|
previous.addEventListener('click',()=>{if(state.page>1){--state.page;loadResults();}});
|
||||||
next.addEventListener('click',()=>{++state.page;loadResults();});
|
next.addEventListener('click',()=>{++state.page;loadResults();});
|
||||||
toggle.addEventListener('click',()=>setOpen(!state.open));close.addEventListener('click',()=>setOpen(false));
|
toggle.addEventListener('click',openWiki);close.addEventListener('click',()=>setOpen(false));
|
||||||
const removed=nodeRef.onRemoved;
|
const removed=nodeRef.onRemoved;
|
||||||
nodeRef.onRemoved=function(){state.disposed=true;setOpen(false);panel.remove();return removed?.apply(this,arguments);};
|
nodeRef.onRemoved=function(){state.disposed=true;clearInterval(state.progressTimer);setOpen(false);panel.remove();downloadDialog.remove();return removed?.apply(this,arguments);};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user