diff --git a/web/js/i18n/de.js b/web/js/i18n/de.js index 462f362..314fe2c 100644 --- a/web/js/i18n/de.js +++ b/web/js/i18n/de.js @@ -644,4 +644,6 @@ export default { "keys.budgetOf": "Budget {b}/Mon.", "dash.kpi.orBalance": "OpenRouter-Guthaben", "keys.h.install": "Installieren:", + "keys.snip.keyInputPh": "API-Schlüssel einfügen", + "keys.snip.apply": "Schlüssel einsetzen", }; diff --git a/web/js/i18n/en.js b/web/js/i18n/en.js index f2144f9..673b5e2 100644 --- a/web/js/i18n/en.js +++ b/web/js/i18n/en.js @@ -637,4 +637,6 @@ export default { "keys.budgetOf": "budget {b}/mo", "dash.kpi.orBalance": "OpenRouter balance", "keys.h.install": "Install:", + "keys.snip.keyInputPh": "paste your API key", + "keys.snip.apply": "Insert key", }; diff --git a/web/js/i18n/es.js b/web/js/i18n/es.js index 4b36254..fcdd7eb 100644 --- a/web/js/i18n/es.js +++ b/web/js/i18n/es.js @@ -644,4 +644,6 @@ export default { "keys.budgetOf": "presupuesto {b}/mes", "dash.kpi.orBalance": "Saldo OpenRouter", "keys.h.install": "Instalar:", + "keys.snip.keyInputPh": "pega tu clave API", + "keys.snip.apply": "Insertar clave", }; diff --git a/web/js/i18n/fr.js b/web/js/i18n/fr.js index a02aa72..d64802f 100644 --- a/web/js/i18n/fr.js +++ b/web/js/i18n/fr.js @@ -644,4 +644,6 @@ export default { "keys.budgetOf": "budget {b}/mois", "dash.kpi.orBalance": "Solde OpenRouter", "keys.h.install": "Installer :", + "keys.snip.keyInputPh": "collez votre clé API", + "keys.snip.apply": "Insérer la clé", }; diff --git a/web/js/i18n/ru.js b/web/js/i18n/ru.js index 6b43dfa..ddfabde 100644 --- a/web/js/i18n/ru.js +++ b/web/js/i18n/ru.js @@ -636,4 +636,6 @@ export default { "keys.budgetOf": "бюджет {b}/мес", "dash.kpi.orBalance": "Баланс OpenRouter", "keys.h.install": "Установить:", + "keys.snip.keyInputPh": "вставьте ваш API-ключ", + "keys.snip.apply": "Вставить ключ", }; diff --git a/web/js/i18n/tr.js b/web/js/i18n/tr.js index 5869986..13bfd91 100644 --- a/web/js/i18n/tr.js +++ b/web/js/i18n/tr.js @@ -644,4 +644,6 @@ export default { "keys.budgetOf": "bütçe {b}/ay", "dash.kpi.orBalance": "OpenRouter bakiyesi", "keys.h.install": "Kurulum:", + "keys.snip.keyInputPh": "API anahtarınızı yapıştırın", + "keys.snip.apply": "Anahtarı ekle", }; diff --git a/web/js/i18n/zh.js b/web/js/i18n/zh.js index d1296e9..c9c1f08 100644 --- a/web/js/i18n/zh.js +++ b/web/js/i18n/zh.js @@ -644,4 +644,6 @@ export default { "keys.budgetOf": "预算 {b}/月", "dash.kpi.orBalance": "OpenRouter 余额", "keys.h.install": "安装:", + "keys.snip.keyInputPh": "粘贴你的 API 密钥", + "keys.snip.apply": "插入密钥", }; diff --git a/web/js/views/keys.js b/web/js/views/keys.js index 2475c89..83a7cd6 100644 --- a/web/js/views/keys.js +++ b/web/js/views/keys.js @@ -54,7 +54,7 @@ export async function renderKeys() { function snippetsCard(openMode) { const origin = location.origin; const base = origin + "/v1"; - const key = lastCreatedKey || (openMode ? "sk-anything" : "<" + t("keys.snip.keyPh") + ">"); + let key = lastCreatedKey || (openMode ? "sk-anything" : "<" + t("keys.snip.keyPh") + ">"); // Every scripty snippet exists in two OS flavors: bash heredocs for // Linux/macOS and PowerShell here-strings for Windows. SDK snippets // (python/js) and GUI instructions are OS-independent. @@ -195,11 +195,15 @@ const resp = await client.chat.completions.create({ }); console.log(resp.choices[0].message.content);`, }; - const shSnips = buildSnips("sh"); - const psSnips = buildSnips("ps"); - // python/js/Cline одинаковы — дополняем ps-набор из sh - for (const k of Object.keys(shSnips)) if (!psSnips[k]) psSnips[k] = shSnips[k]; - let snips = os === "ps" ? psSnips : shSnips; + let shSnips, psSnips, snips; + function rebuildSnips() { + shSnips = buildSnips("sh"); + psSnips = buildSnips("ps"); + // python/js/Cline одинаковы — дополняем ps-набор из sh + for (const k of Object.keys(shSnips)) if (!psSnips[k]) psSnips[k] = shSnips[k]; + snips = os === "ps" ? psSnips : shSnips; + } + rebuildSnips(); let cur = "Claude Code"; const pre = h("pre", { class: "conv-log snippet" }, snips[cur]); const seg = h( @@ -289,12 +293,22 @@ console.log(resp.choices[0].message.content);`, osSeg.querySelectorAll("button").forEach((b, i) => b.addEventListener("click", () => { os = i === 0 ? "sh" : "ps"; - snips = os === "ps" ? psSnips : shSnips; + rebuildSnips(); osSeg.querySelectorAll("button").forEach((x, j) => x.classList.toggle("active", j === (os === "sh" ? 0 : 1))); pre.textContent = snips[cur]; paintStatus(); })); + // paste-your-key: substitutes the key into every snippet immediately + const keyIn = h("input", { class: "mono", placeholder: t("keys.snip.keyInputPh"), style: "max-width:230px; font-size:12px", value: lastCreatedKey || "" }); + const applyKey = () => { + key = keyIn.value.trim() || (openMode ? "sk-anything" : "<" + t("keys.snip.keyPh") + ">"); + rebuildSnips(); + pre.textContent = snips[cur]; + }; + const applyBtn = h("button", { class: "btn ghost sm", onClick: applyKey }, "⤵ " + t("keys.snip.apply")); + keyIn.addEventListener("keydown", (e) => { if (e.key === "Enter") applyKey(); }); + const copy = h("button", { class: "btn ghost sm" }, "⧉ " + t("common.copy")); copy.addEventListener("click", async () => { await navigator.clipboard.writeText(pre.textContent); @@ -306,7 +320,7 @@ console.log(resp.choices[0].message.content);`, h("div", { class: "card-title" }, "🔌 " + t("keys.snip.title")), h("div", { class: "muted small", style: "margin-bottom:8px" }, lastCreatedKey ? t("keys.snip.withKey") : openMode ? t("keys.snip.openMode") : t("keys.snip.hint")), - h("div", { class: "flex", style: "gap:8px; margin-bottom:8px; flex-wrap:wrap" }, seg, h("div", { class: "grow" }), osSeg, copy), + h("div", { class: "flex", style: "gap:8px; margin-bottom:8px; flex-wrap:wrap" }, seg, h("div", { class: "grow" }), keyIn, applyBtn, osSeg, copy), pre, statusHost, protoWarn,