| """ |
| 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 "<p style='color:#888'>Enter at least 2 characters to search</p>" |
| data = fetch_api("search", q=query, network=network) |
| if "error" in data: |
| return f"<p style='color:#f66'>Error: {data['error']}</p>" |
| results = data.get("results", []) |
| if not results: |
| return "<p style='color:#888'>No results found</p>" |
| rows = "" |
| for r in results: |
| name = r.get("asset", "?") |
| desc = r.get("description", "β")[:80] |
| supply = r.get("supply", "?") |
| rows += f""" |
| <div class="asset-card"> |
| <div class="asset-name">{name}</div> |
| <div class="asset-desc">{desc}</div> |
| <div class="asset-supply">Supply: {supply}</div> |
| </div>""" |
| return f"<div class='results-grid'>{rows}</div>" |
|
|
| def get_stats(network): |
| data = fetch_api("stats", network=network) |
| if "error" in data: |
| return f"<p style='color:#f66'>Error: {data['error']}</p>" |
| total = data.get("total", "?") |
| named = data.get("named", "?") |
| numeric = data.get("numeric", "?") |
| return f""" |
| <div class="stats-container"> |
| <div class="stat-box"> |
| <div class="stat-number">{total}</div> |
| <div class="stat-label">Total Assets</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-number">{named}</div> |
| <div class="stat-label">Named</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-number">{numeric}</div> |
| <div class="stat-label">Numeric</div> |
| </div> |
| </div>""" |
|
|
| 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() |
|
|