diff --git a/static/bte.js b/static/bte.js
index 33b0c2a..4695512 100644
--- a/static/bte.js
+++ b/static/bte.js
@@ -48,6 +48,10 @@
selected: null, // asset id
pollTimer: null,
log: [],
+ // SKU options surfaced in the toolbar dropdown. Defaults to the bundled
+ // placeholders until _fetchSkuOptions replaces them with real top-selling
+ // offerings from Adwright's TopRecipes RPC.
+ skus: PLACEHOLDER_SKUS.slice(),
};
// ── Public namespace ─────────────────────────────────────────────────────
@@ -91,6 +95,37 @@
}
overlay.hidden = false;
_refreshGrid();
+ _fetchSkuOptions();
+ }
+
+ // Pull top-selling offerings from Adwright (TopRecipes) so the SKU
+ // dropdown reflects what's actually shipping, not the bundled PLACEHOLDER
+ // list. Best-effort — falls through to placeholders on any failure.
+ function _fetchSkuOptions() {
+ fetch("/api/adwright/last-panel-update?session_id=&since=0&tool=adwright_list_recipes")
+ .then((r) => r.json())
+ .then((r) => {
+ const recipes = r && r.update && r.update.payload && r.update.payload.recipes;
+ if (!Array.isArray(recipes) || !recipes.length) return;
+ state.skus = recipes.map((rec) => ({
+ id: String(rec.product_id || rec.id || ""),
+ name: rec.name || "",
+ })).filter((s) => s.id && s.name);
+ _refreshSkuOptions();
+ })
+ .catch(() => { /* fallback: keep placeholders */ });
+ }
+
+ function _refreshSkuOptions() {
+ const sel = document.getElementById("svrntyBteSku");
+ if (!sel) return;
+ const current = sel.value;
+ let html = '';
+ state.skus.forEach((s) => {
+ html += '";
+ });
+ sel.innerHTML = html;
+ if (current) sel.value = current;
}
function _closeOverlay() {
@@ -148,9 +183,10 @@
state.family = v; _updateToolbarPressed(); _updateGenerateEnabled(); _refreshGrid();
}));
- // SKU dropdown
+ // SKU dropdown — populated from state.skus (placeholder at boot,
+ // replaced by real top-selling offerings via _fetchSkuOptions).
const skuOpts = [{ value: "", label: "— pick SKU —" }].concat(
- PLACEHOLDER_SKUS.map((s) => ({ value: s.id, label: s.name })));
+ state.skus.map((s) => ({ value: s.id, label: s.name })));
bar.appendChild(_labeled("SKU", _select("svrntyBteSku", skuOpts, state.skuId || "", (v) => {
state.skuId = v || null; _updateGenerateEnabled();
})));
@@ -315,7 +351,7 @@
// ── Generate flow ────────────────────────────────────────────────────────
function _onGenerate() {
if (!state.family || !state.skuId) return;
- const sku = PLACEHOLDER_SKUS.find((s) => s.id === state.skuId);
+ const sku = state.skus.find((s) => s.id === state.skuId);
const recipeLabel = `${state.family}__${state.mode}__${state.media}`;
const body = {
brandId: state.brand,
@@ -496,13 +532,35 @@
// ── CMO chat handoff ─────────────────────────────────────────────────────
function _openCmoChat() {
- // Honest minimum: surface the WebUI chat panel (whichever profile is
- // active). Full CMO-scoped iframe / component reuse is COMMAND-CENTER-PRD
- // §6.5 Phase E — not v1 of this panel.
+ // Pre-fill the chat textarea with BTE state context so the operator
+ // doesn't have to retype "I'm in BTE, brand Plan B, mode Polished, …".
+ // Don't auto-send — user reviews + edits + hits send. Full CMO-scoped
+ // iframe is COMMAND-CENTER-PRD §6.5 Phase E (not v1).
+ const modeL = MODES.find((m) => m.slug === state.mode);
+ const familyL = FAMILIES.find((f) => f.slug === state.family);
+ const sku = state.skus.find((s) => s.id === state.skuId);
+ const lines = ["I'm working in BTE Command Center. Help me iterate."];
+ lines.push("- Brand: " + state.brand);
+ if (modeL) lines.push("- Mode: " + modeL.label);
+ lines.push("- Media: " + state.media);
+ if (familyL) lines.push("- Recipe family: " + familyL.label);
+ if (sku) lines.push("- SKU: " + sku.name);
+ lines.push("- Variants: " + state.variants);
+ if (state.selected) lines.push("- Selected asset: " + state.selected);
+ const prompt = lines.join("\n") + "\n\nWhat should I change next?";
+
_closeOverlay();
if (typeof window.switchPanel === "function") {
try { window.switchPanel("chat", { fromRailClick: true }); } catch (_) { /* ignore */ }
}
+ // Defer pre-fill so the chat panel + #msg textarea mount first.
+ setTimeout(() => {
+ const msg = document.getElementById("msg");
+ if (!msg) return;
+ msg.value = prompt;
+ try { msg.dispatchEvent(new Event("input", { bubbles: true })); } catch (_) {}
+ try { msg.focus(); } catch (_) {}
+ }, 200);
}
// ── Proxy helpers ────────────────────────────────────────────────────────