1/** 2 * dashboard-report-stage1.js â Dashboard å ±åé Stage 1 渲æ 3 * 4 * æå ¥é»ï¼`dashboard.html` å¨ demo.js ä¹å¾è¼å ¥æ¬æªï¼demo.js ç loadDashboard() 5 * è§£é `data-dashboard-content` å¾ï¼ç± MutationObserver è§¸ç¼æ¬æªç init()ï¼ 6 * é¿å è dashboard-client.js çæ¢æ bundle åå§åç«¶åï¼é åºä¸åè¨ï¼åªè§å¯ï¼ã 7 * 8 * åå¡ï¼ 9 * - report-hero â Hero 總覽å¡ï¼ç¸½å + Î éå + freshness + agentic browsingï¼ 10 * + AI åç峿¬ï¼å併鲿¢æ identity-panel ä½ç½®ï¼ 11 * - report-cc-band â Common Crawl èªæåå¨å¸¶ 12 * 13 * åè¦æ¿çï¼ä¸å¯ç§»é¤ï¼ï¼ 14 * 1. pending_not_zeroï¼è³æç¼ºå¤±ä¸å¾ pending chipï¼ä¸é¡¯ç¤º 0 æç©ºç½æ¸åã 15 * 2. Î éåå±ç¤ºï¼comparison.comparable çº false æä¸åè£æè®å趨å¢ï¼ 16 * 顯示 reason çç½è©±è§£éï¼å docs/dashboard-frontend-architecture rev.2 §9.3ï¼ã 17 * 3. claim limitsï¼æ¬æªä¸æ°å¢ä»»ä½ KPI 話è¡ï¼ææææ¡åªè® payload èªææ¬ä½ã 18 */ 19(() => { 20 "use strict"; 21 22 /* Language follows document.body.dataset.lang at call time; demo.js rewrites 23 * that attribute from the visitor's preference, so a top-level snapshot would
24 * go stale. Copy resolves through the shared eight-locale catalog, keyed by 25 * the English string; the Chinese argument is the fallback when the catalog 26 * has no entry. A missing English argument means the string is Chinese-only. */ 27 const currentLang = () => document.body.dataset.lang || document.documentElement.lang || "en"; 28 const t = (c, e) => { 29 const catalog = window.XAgentI18nCatalog; 30 const source = e !== undefined ? e : c; 31 const key = catalog?.aliases?.[source] || source; 32 const translated = catalog?.messages?.[key]?.[currentLang()]; 33 if (translated) return translated; 34 return currentLang() === "zh-Hant" ? c : source; 35 }; 36 37 /* helpers ------------------------------------------------------------- */ 38 39 const el = (sel, root) => (root || document).querySelector(sel); 40 const pendingChip = (c, e) => `<span class="pending-chip">${t(c, e)}</span>`; 41 const escapeText = (v) => String(v ?? "").replace(/[&<>"']/g, (m) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[m])); 42 43 const read = (obj, ...path) => path.reduce((acc, k) => (acc && typeof acc === "object" ? acc[k] : undefined), obj); 44 45 /* åå¡ 1+2ï¼Hero ç¸½è¦½å¡ -------------------------------------------------- */ 46 47 const renderHero = (p) => { 48 const overall = read(p, "readiness", "overall"); 49 const comp = read(p, "comparison") || {}; 50 const fresh = read(p, "readiness", "freshness") || {}; 51 const agentic = read(p, "readiness", "agentic_browsing"); 52 const id = read(p, "public_state", "identity") || {}; 53 const cust = read(p, "customer") || {}; 54 const inst = read(p, "installation") || {}; 55 const ageHours = fresh.age_seconds != null ? Math.round(read(fresh, "age_seconds") / 3600) : null; 56 57 /* Î éåå±ç¤ºï¼comparable çº false æèªªæåå ï¼ä¸æ¸²æåè¶¨å¢ */ 58 let deltaHtml; 59 if (comp.comparable && typeof comp.overall_delta === "number") { 60 const up = comp.overall_delta > 0; 61 deltaHtml = `<span class="hero-delta ${up ? "up" : "down"}"> 62 <span aria-hidden="true">${up ? "â²" : "â¼"}</span>${Math.abs(comp.overall_delta).toFixed(1)} 63 <span class="delta-note">${t("vs 䏿¬¡å¯æ¯é測", "vs last comparable audit")}</span></span>`; 64 } else { 65 const reason = { 66 baseline_has_no_capture_time: t("馿¬¡å¯æ¯é測å¾å³èªå建ç«åºç·", "Auto baseline after first comparable audit"), 67 comparable_baseline_pending: t("çå¾ å¯æ¯åºç·å»ºç«", "Comparable baseline pending"), 68 same_deterministic_public_audit_contract: t("使ç¨åä¸å ¬é巡檢åç´", "Same deterministic public audit contract"), 69 }[comp.reason] || t("æ«ç¡å¯æ¯åºç·", "No comparable baseline yet"); 70 deltaHtml = `<span class="hero-delta pending">â<span class="delta-note">${escapeText(reason)}</span></span>`; 71 } 72 73 const freshPill = fresh.status === "fresh" 74 ? `<span class="freshness-pill fresh">${t(`${ageHours != null ? `${ageHours}h å §` : "å·²æ´æ°"} · è³ææ°é®®`, `${ageHours != null ? `within ${ageHours}h` : "updated"} · data fresh`)}</span>` 75 : `<span class="freshness-pill">${t("è³ææ°é®®åº¦æªç¢ºèª", "Freshness unverified")}</span>`; 76 77 const agenticHtml = agentic && typeof read(agentic, "score") === "number" 78 ? `<p class="hero-note">${t("Agentic Browsing", "Agentic Browsing")} <b>${read(agentic, "score")}</b> â ${t("Lighthouse å ¬éä¿¡èï¼é Google 宿¹æ¸æ", "Public Lighthouse signal, not official Google data")}</p>` 79 : ""; 80 81 const verified = id.verified === true || id.status === "verified"; 82 return ` 83 <section class="report-hero" data-report-hero> 84 <article class="panel hero-card"> 85 <div class="hero-top"> 86 <div> 87 <p class="hero-score-label"><span class="lang-zh">AI æºå度總å</span><span class="lang-en">AI readiness score</span></p> 88 <div class="hero-score-wrap"><span class="hero-score">${overall != null ? escapeText(overall) : "â"}</span> 89 ${deltaHtml}</div> 90 </div> 91 </div> 92 <p class="hero-meta-row">${freshPill} 93 ${comp.comparable ? `<span>${t("趨å¢å¯æ¯", "Trend comparable")}</span>` : ""}</p> 94 ${agenticHtml} 95 <p class="hero-note">${t("總åå æè¿°å ¬éé é¢çå¯è®åæ§ï¼åæ¸æåä¸ä»£è¡¨ AI æåãå¼ç¨ææäº¤æåã", "The score only describes public-page readability; it never proves ranking, citation, or revenue lift.")}</p> 96 </article> 97 <aside class="panel identity-panel"> 98 <p class="label">AgentX portable alias</p> 99 <h2 style="margin:12px 0 18px;font-size:22px"><span class="lang-zh">AI åççæ </span><span class="lang-en">Agent card status</span></h2> 100 <div class="identity-row"><span>Agent ID</span><b>${escapeText(cust.agent_id || "â")}</b></div> 101 <div class="identity-row"><span>Status</span><b>${verified ? t("å·²é©è", "Verified") : escapeText(read(cust, "verification_status") || id.status || "pending")}</b></div> 102 <div class="identity-row"><span>Public proof</span><a href="${escapeText(read(cust, "public_identity_url") || "/.well-known/agent-id.json")}" target="_blank" rel="noopener">/.well-known/agent-id.json â</a></div> 103 <div class="identity-row"><span>${t("å®è£", "Installation")}</span><b>${escapeText(read(inst, "package") || "â")}${inst.version ? ` @${escapeText(inst.version)}` : ""}</b></div> 104 <div class="identity-row"><span>Authority</span><b>local_policy_only</b></div> 105 </aside> 106 </section>`; 107 }; 108 109 /* åå¡ 3ï¼Common Crawl åå¨å¸¶ ------------------------------------------- */ 110 111 const renderCommonCrawl = (cc) => { 112 if (!cc || cc.error) { 113 return `<section class="cc-band" data-report-cc data-report-block="common-crawl"> 114 <p class="report-pending-block">${t("Common Crawl èªææ¥è©¢ç®åä¸å¯ç¨", "Common Crawl presence query unavailable")} â ${pendingChip(t("ç¶æå¾ åå¾ï¼ä¸è¦çºç¡æ¶é", "Treated as pending, never as absent"))}</p></section>`; 115 } 116 const statusZh = cc.status === "present" ? t("å·²æ¶éæ¼ AI è¨ç·´èªæ", "Present in AI training corpora") 117 : cc.status === "absent" ? t("å°æªè§æ¸¬å°æ¶é", "No observed captures") : ""; 118 const extras = [];
119 if (typeof cc.captures === "number") extras.push(`${cc.captures} ${t("次æ¶é", "captures")}`); 120 if (typeof cc.http_200_pages === "number") extras.push(`${cc.http_200_pages} ${t("é æ£å¸¸åå³", "HTTP 200 pages")}`); 121 122 return ` 123 <section class="cc-band" data-report-cc data-report-block="common-crawl"> 124 <div class="cc-head"> 125 <div> 126 <h2><span class="lang-zh">AI è¨ç·´èªæè£¡æä½ å</span><span class="lang-en">Common Crawl presence</span></h2> 127 <p class="cc-answer"><span class="cc-status ${cc.status}">${escapeText(statusZh)}</span> 128 ${extras.map(escapeText).join(" · ")}</p> 129 </div> 130 ${cc.crawl_id ? `<p class="cc-meta">crawl: ${escapeText(cc.crawl_id)}</p>` : ""} 131 </div> 132 <details class="cc-verify"> 133 <summary><span class="lang-zh">èªå·±é©è â è¤è£½å³è·</span><span class="lang-en">Verify yourself â copy & run</span></summary> 134 <pre><code>${escapeText(cc.verify_command || "")}</code></pre> 135 <p class="cc-meaning">${escapeText(read(cc, "meaning") || "")}</p> 136 </details> 137 <p class="cc-nonclaims">${(read(cc, "non_claims") || []).map((c) => pendingChip(c)).join(" ")}</p> 138 </section>`; 139 }; 140 141 /* æè¼ ------------------------------------------------------------------ */ 142 143 const mountSections = (p, cc) => { 144 const root = el("[data-dashboard-content]"); 145 if (!root) return false; 146 147 /* Hero æå ¥ dash-grid åï¼å ¨å¯¬æäºèµ·é»ï¼ */ 148 const hero = el(".dash-grid", root); 149 if (hero) { 150 const wrap = document.createElement("div"); 151 wrap.innerHTML = renderHero(p); 152 hero.before(wrap.firstElementChild); 153 } 154 155 /* Common Crawl 帶æ dash-grid å¾ãbilling å */ 156 const ccEl = document.createElement("div"); 157 ccEl.innerHTML = renderCommonCrawl(cc); 158 const billing = el("[data-billing-panel]", root); 159 if (billing) billing.before(ccEl.firstElementChild); 160 else root.append(ccEl.firstElementChild); 161 return true; 162 }; 163 164 let generation = 0; 165 const init = async () => { 166 const epoch = ++generation; 167 const root = el("[data-dashboard-content]"); 168 if (!root) return; 169 root.querySelectorAll("[data-report-hero], [data-report-cc]").forEach((node) => node.remove()); 170 const context = window.AgentIdReportApi?.current(); 171 if (!context) return; 172 mountSections(context.data, context.data.common_crawl || {error: "not_observed"}); 173 if (context.data.common_crawl) return; 174 const cc = await window.AgentIdReportApi.fetchCommonCrawl(`https://${context.host}`); 175 if (epoch !== generation || window.AgentIdReportApi.current() !== context) return; 176 root.querySelectorAll("[data-report-hero], [data-report-cc]").forEach((node) => node.remove()); 177 mountSections(context.data, cc.ok ? cc.data : {error: cc.error}); 178 }; 179 180 /* è§å¯è³æåå¡è§£é â è§¸ç¼ initï¼ä¸æ¬¡ï¼ */ 181 window.addEventListener("dashboard-report-change", init); 182 183 window.AgentIdReportStage1 = Object.freeze({ init, renderHero, renderCommonCrawl }); 184 185 /* é é¢å·²è§£éçç´æ¥è§¸ç¼ï¼script defer å¯è½ææ¼è§£éï¼ */ 186 init(); 187})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.