"""
BlockchainToken Explorer — Gradio 6 gr.HTML Single-File App
Deploy: huggingface-cli repo create blockchaintoken-explorer-v2 --type space --space-sdk gradio
"""
import gradio as gr
import urllib.request
import urllib.parse
import json
API = "https://blockchaintoken.com/api.php"
def fetch_api(action, **params):
params["action"] = action
qs = "&".join(f"{k}={urllib.parse.quote(str(v))}" for k, v in params.items())
url = f"{API}?{qs}"
try:
req = urllib.request.Request(url, headers={"User-Agent": "BlockchainToken-Explorer/2.0"})
with urllib.request.urlopen(req, timeout=15) as r:
return json.loads(r.read())
except Exception as e:
return {"error": str(e)}
def search_assets(query, network):
if not query or len(query) < 2:
return "
Enter at least 2 characters to search
"
data = fetch_api("search", q=query, network=network)
if "error" in data:
return f"Error: {data['error']}
"
results = data.get("results", [])
if not results:
return "No results found
"
rows = ""
for r in results:
name = r.get("asset", "?")
desc = r.get("description", "—")[:80]
supply = r.get("supply", "?")
rows += f"""
{name}
{desc}
Supply: {supply}
"""
return f"{rows}
"
def get_stats(network):
data = fetch_api("stats", network=network)
if "error" in data:
return f"Error: {data['error']}
"
total = data.get("total", "?")
named = data.get("named", "?")
numeric = data.get("numeric", "?")
return f"""
"""
CSS = """
.gradio-container { background: #0a0a0a !important; }
.asset-card {
background: #141414; border: 1px solid #2a2a2a; border-radius: 8px;
padding: 16px; margin: 8px 0; transition: border-color 0.2s;
}
.asset-card:hover { border-color: #d4a017; }
.asset-name { color: #d4a017; font-family: 'JetBrains Mono', monospace; font-size: 1.1em; font-weight: bold; }
.asset-desc { color: #aaa; margin-top: 4px; font-size: 0.9em; }
.asset-supply { color: #666; margin-top: 4px; font-size: 0.8em; }
.results-grid { max-height: 500px; overflow-y: auto; }
.stats-container { display: flex; gap: 16px; justify-content: center; margin: 16px 0; }
.stat-box {
background: #141414; border: 1px solid #2a2a2a; border-radius: 8px;
padding: 24px 32px; text-align: center; min-width: 120px;
}
.stat-number { color: #d4a017; font-size: 2em; font-weight: bold; font-family: 'JetBrains Mono', monospace; }
.stat-label { color: #888; margin-top: 4px; }
"""
with gr.Blocks(css=CSS, title="BlockchainToken Explorer", theme=gr.themes.Base()) as demo:
gr.Markdown("# 🔗 BlockchainToken Explorer\n*Dogeparty & Counterparty Asset Search*")
with gr.Row():
network = gr.Radio(["dogeparty", "counterparty"], value="dogeparty", label="Network")
stats_html = gr.HTML()
network.change(get_stats, inputs=[network], outputs=[stats_html])
demo.load(get_stats, inputs=[network], outputs=[stats_html])
with gr.Row():
query = gr.Textbox(label="Search Assets", placeholder="PEPECASH, FLDC, RARE...", scale=4)
search_btn = gr.Button("🔍 Search", scale=1)
results_html = gr.HTML()
search_btn.click(search_assets, inputs=[query, network], outputs=[results_html])
query.submit(search_assets, inputs=[query, network], outputs=[results_html])
if __name__ == "__main__":
demo.launch()