Files
magpie/site/public/import.html
T
yetone 209dc69b59 providers: Tencent Cloud is one preset, its regions Plan · China, Pay as you go · China and Pay as you go · Global (Discord, Jorben: Tencent Cloud's Token Plan, TokenHub and TokenHub China were three tiles of one vendor) — tencent-token-plan, tencent-tokenhub-cn and tencent-tokenhub become tencent-cloud's regions plan, cn and intl, as Xiaomi MiMo's are, each made as its own preset made it: the plan's /plan endpoints with no Responses, its own models and no catalog; TokenHub's hosts with Responses, models.dev's tencent-tokenhub and its own keys page and docs. A region now carries its Website, Catalog and Models, and a saved provider's region is found by its endpoints (regionOf, by exact URL before path, since TokenHub China and global share their paths): normalize gives it that region's keys page, docs and catalog (a catalog the user picked is kept), and planModels its models. The old ids are aliases, as qianfan-token-plan is of baidu-qianfan: Preset and FromPreset take them, FromPreset builds at their region, normalize rewrites a saved provider's preset to tencent-cloud, and a provider's own id never changes, so its key, models, routing groups, usage and prices stay where they were; magpie://import links by an old id land at its region, and region= now also gives a region's docs and catalog. Imports from other apps match a region's endpoints. The editor's docs link follows the region picked with the segmented control, en and zh ("按量 · 中国", "按量 · 国际"); the add sheet has one Tencent Cloud tile; the site's import docs list tencent-cloud with its regions and import.html still names the old ids. TestTencentCloud (each region, old ids, FromPreset, imports, import links) and TestTencentCloudOldPresetsKeepWorking (a providers.json saved by an older magpie under all three old presets, with a routing group over them, read, re-saved as the editor saves and switched between regions) fail on the old code; TestTencentTokenHubRoutes routes through old ids and tencent-cloud alike. tencent-cloud.test.cjs (WebKit en and zh: one tile, the three regions, no select, the docs and Get a key links following the region, an old id's provider opening at its region and saved under its id) fails on the old app.js; add-sheet, zhipu-team, system-one, strip-scrollbar and click-scroll pass (Chromium doesn't launch in this sandbox). Not tried with a Tencent account. go vet, builds (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass.
2026-10-04 19:38:14 +08:00

169 lines
9.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Add to magpie</title>
<meta name="description" content="Open this link in magpie to add a model provider to every agent you use.">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#fafafa">
<meta name="referrer" content="no-referrer">
<meta name="robots" content="noindex">
<script>
(() => {
let mode = "system";
try { mode = localStorage.getItem("theme") || "system"; } catch {}
const q = matchMedia("(prefers-color-scheme: dark)");
const apply = () => {
const t = mode === "system" ? (q.matches ? "dark" : "light") : mode;
document.documentElement.dataset.theme = t;
document.querySelector('meta[name="theme-color"]').content = t === "dark" ? "#0b0b0c" : "#fafafa";
};
q.addEventListener("change", () => mode === "system" && apply());
apply();
})();
</script>
<script>try{if(/Linux/.test(navigator.userAgent)&&!/Android/.test(navigator.userAgent)||localStorage.getItem("omarchy")||/[?&]omarchy=/.test(location.search))document.write('<script src="/omarchy.js"><\/script>')}catch(e){}</script>
<link rel="icon" href="/favicon.png" type="image/png">
<link rel="stylesheet" href="/docs.css">
<meta property="og:title" content="Add to magpie">
<meta property="og:description" content="Open this link in magpie to add a model provider to every agent you use.">
<meta property="og:image" content="https://usemagpie.ai/img/og.png">
<meta name="twitter:card" content="summary_large_image">
<style>
body { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; }
.stage { flex: 1; display: grid; place-items: center; padding: 48px 16px 64px; }
.card { width: 100%; max-width: 440px; background: var(--card); border: 1px solid var(--line); border-radius: 20px; box-shadow: var(--shadow); padding: 34px 30px 26px; text-align: center; }
.mark { display: flex; align-items: center; justify-content: center; gap: 14px; margin-bottom: 22px; }
.mark .tile { width: 52px; height: 52px; border-radius: 14px; border: 1px solid var(--line); display: grid; place-items: center; background: var(--bg); font-weight: 650; font-size: 20px; color: var(--ink); overflow: hidden; }
.mark .tile img { width: 30px; height: 30px; }
.mark .plus { color: var(--ink3); font-size: 18px; }
h1 { font-size: 23px; letter-spacing: -.02em; line-height: 1.25; margin: 0 0 8px; font-weight: 660; overflow-wrap: anywhere; }
.sub { color: var(--ink2); font-size: 15px; margin: 0 0 22px; }
dl { text-align: left; margin: 0 0 24px; border-top: 1px solid var(--line); }
dl > div { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
dt { color: var(--ink3); }
dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; }
dd code { font-size: 13px; }
dd .m { display: inline-block; font-size: 12.5px; border: 1px solid var(--line); border-radius: 999px; padding: 0 8px; margin: 2px 4px 2px 0; color: var(--ink2); }
.actions { display: grid; gap: 10px; }
.actions .btn { width: 100%; height: 44px; }
.fine { color: var(--ink3); font-size: 12.5px; margin: 18px 0 0; line-height: 1.55; }
.fine a { color: var(--ink2); }
.foot { text-align: center; font-size: 13px; color: var(--ink3); padding: 0 16px 28px; }
.foot a { text-decoration: none; color: var(--ink2); }
.foot a:hover { color: var(--ink); }
@media (max-width: 480px) { .card { padding: 28px 20px 22px; border-radius: 16px; } }
</style>
</head>
<body>
<div class="stage">
<div class="card" id="card">
<div class="mark"><div class="tile" id="tile">?</div><span class="plus">→</span><div class="tile"><img class="bird" src="/img/magpie-small.svg" alt="magpie"></div></div>
<h1 id="title">Add to magpie</h1>
<p class="sub" id="sub">Opening magpie…</p>
<dl id="facts"></dl>
<div class="actions" id="actions">
<a class="btn" id="open" href="#"><img class="bird-inv" src="/img/magpie-small.svg" alt="">Open in magpie</a>
<a class="btn ghost" id="dl" href="/#get">Don’t have magpie? Download it</a>
</div>
<p class="fine" id="fine">magpie shows you everything before it adds anything. Needs magpie 0.1.8 or later.</p>
</div>
</div>
<div class="foot"><a href="/">usemagpie.ai</a> · <a href="/docs/import">Add magpie links to your site</a></div>
<script>
(() => {
// The link's parameters are in the fragment, which never reaches a
// server; this page only turns them into magpie://import?… and shows
// what they describe. The key is never displayed.
const raw = location.hash.slice(1);
const q = new URLSearchParams(raw);
const $ = (id) => document.getElementById(id);
const text = (tag, s, cls) => { const e = document.createElement(tag); e.textContent = s; if (cls) e.className = cls; return e; };
// Download for this system.
const ua = navigator.userAgent;
const os = /Windows/.test(ua) ? "windows" : /Linux|X11|CrOS/.test(ua) && !/Android/.test(ua) ? "linux" : /Mac/.test(ua) ? "mac" : "";
if (os === "windows") $("dl").href = "/download/windows";
else if (os === "linux") $("dl").href = "/#get";
else if (os === "mac") $("dl").href = "/download/mac-arm64";
// Presets by their proper names, with the icons the home page has.
const PRESETS = {
anthropic: ["Anthropic", "claude-color.svg"], openai: ["OpenAI", "openai.svg"], google: ["Google Gemini", "gemini-color.svg"],
deepseek: ["DeepSeek", "deepseek-color.svg"], xai: ["xAI", "xai.svg"], moonshot: ["Kimi", "kimi.svg"], "moonshot-cn": ["Kimi (China)", "kimi.svg"],
zhipu: ["Zhipu GLM", "zhipu-color.svg"], zai: ["Z.ai", "zhipu-color.svg"], minimax: ["MiniMax", "minimax-color.svg"],
"minimax-cn": ["MiniMax (China)", "minimax-color.svg"], qwen: ["Qwen", "qwen-color.svg"], "qwen-cn": ["Qwen (China)", "qwen-color.svg"],
"baidu-qianfan": ["Baidu Qianfan", "baiducloud-color.svg"],
"qianfan-token-plan": ["Baidu Qianfan", "baiducloud-color.svg"],
"tencent-cloud": ["Tencent Cloud", "tencentcloud-color.svg"],
"tencent-token-plan": ["Tencent Cloud", "tencentcloud-color.svg"], "tencent-tokenhub": ["Tencent Cloud", "tencentcloud-color.svg"], "tencent-tokenhub-cn": ["Tencent Cloud", "tencentcloud-color.svg"],
huaweicloud: ["Huawei Cloud MaaS", "huaweicloud-color.svg"], volcengine: ["Volcengine Ark", "volcengine-color.svg"],
mistral: ["Mistral", "mistral-color.svg"], groq: ["Groq", "groq.svg"], openrouter: ["OpenRouter", "openrouter.svg"],
"opencode-go": ["OpenCode Go", "opencode.svg"], "opencode-zen": ["OpenCode Zen", "opencode.svg"], together: ["Together AI"],
fireworks: ["Fireworks"], siliconflow: ["SiliconFlow"], nvidia: ["NVIDIA NIM", "nvidia-color.svg"], modelscope: ["ModelScope", "modelscope-color.svg"], aihubmix: ["AiHubMix"], "302ai": ["302.AI"], yylx: ["鱼鱼连线"],
"ollama-cloud": ["Ollama Cloud", "ollama.svg"], ollama: ["Ollama", "ollama.svg"], lmstudio: ["LM Studio"],
};
const preset = (q.get("preset") || "").toLowerCase();
const known = PRESETS[preset];
const name = q.get("name") || (known ? known[0] : "");
if (!raw || (!name && !preset)) {
$("title").textContent = "Add to magpie";
$("sub").textContent = "This page opens magpie import links, and this one carries nothing to import.";
$("tile").textContent = "?";
$("open").href = "/docs/import";
$("open").lastChild.textContent = "How import links work";
$("fine").textContent = "";
return;
}
const label = name || preset;
$("title").textContent = "Add " + label + " to magpie";
$("tile").textContent = label.trim().charAt(0).toUpperCase() || "?";
if (known && known[1] && !q.get("name")) {
const img = document.createElement("img");
img.src = "/icons/" + known[1]; img.alt = "";
$("tile").replaceChildren(img);
}
document.title = "Add " + label + " to magpie";
const hosts = [];
for (const k of ["chat", "responses", "anthropic"]) {
try { const h = new URL(q.get(k)).host; if (h && !hosts.includes(h)) hosts.push(h); } catch {}
}
const row = (k, v) => { const d = document.createElement("div"); d.append(text("dt", k)); const dd = document.createElement("dd"); typeof v === "string" ? (dd.textContent = v) : dd.append(...v); d.append(dd); $("facts").append(d); };
if (preset) row("Preset", [text("code", preset)].concat(q.get("region") ? [text("span", " · " + q.get("region"))] : []));
if (hosts.length) row("Sends to", hosts.map((h, i) => text("code", (i ? ", " : "") + h)));
row("API key", q.get("key") ? "Included" : "Not included; you’ll paste one");
const models = (q.get("models") || "").split(",").map((s) => s.trim()).filter(Boolean);
if (models.length) row("Models", models.slice(0, 12).map((m) => text("span", m, "m")).concat(models.length > 12 ? [text("span", "+" + (models.length - 12))] : []));
const link = "magpie://import?" + raw;
$("open").href = link;
$("sub").textContent = "magpie will ask before adding it.";
// magpie runs on computers; on a phone, say where to open this.
if (/iPhone|iPad|iPod|Android/.test(ua)) {
$("sub").textContent = "Open this link on the computer where you use magpie.";
$("actions").remove();
$("fine").innerHTML = "";
$("fine").append("magpie is a desktop app for macOS, Windows and Linux. ", Object.assign(document.createElement("a"), { href: "/", textContent: "Learn more" }));
return;
}
// One attempt on arrival; the button is there when the browser wants a
// click first, or when magpie isn't installed yet.
let left = false;
addEventListener("blur", () => (left = true));
document.addEventListener("visibilitychange", () => document.hidden && (left = true));
setTimeout(() => { location.href = link; }, 350);
setTimeout(() => {
if (!left) $("sub").textContent = "Nothing happened? Open it below, or install magpie first.";
}, 2600);
})();
</script>
</body>
</html>