feat(metrics): compiles and runs carry the editor BoardKind; ads learn to stay quiet
Several boards share one FQBN (the Pimoroni RP2350 trio all compile as rpipico2), so FQBN-keyed metrics can't tell them apart. The compile request and the run event now carry board_kind — analytics only, rides into the metric hook's extra. suppressOnlineOnlyAds(): overlay seam so the hosted build can silence 'available in the online editor' ad cards for items it manages — an embargoed item must vanish, not advertise the site the user is on. Pure OSS builds never call it; ads behave exactly as before.
This commit is contained in:
parent
f791536007
commit
8b2715affd
|
|
@ -163,6 +163,11 @@ class CompileRequest(BaseModel):
|
|||
board_fqbn: str = "arduino:avr:uno"
|
||||
# Optional: associate this compile with a project for analytics
|
||||
project_id: str | None = None
|
||||
# Optional: the editor's BoardKind (e.g. 'badger-2350'). Purely for
|
||||
# analytics — several distinct boards can share one FQBN (the Pimoroni
|
||||
# RP2350 boards all compile as rpipico2), and only the kind can tell
|
||||
# them apart. Rides into the metric hook's `extra`.
|
||||
board_kind: str | None = None
|
||||
# Per-board ESP32 build options (Partition Scheme, CPU Freq, Flash Mode,
|
||||
# PSRAM, etc.). Loose dict so the frontend can add fields without a
|
||||
# backend deploy — espidf_compiler.compile validates known keys and
|
||||
|
|
@ -514,6 +519,7 @@ async def _compile_job(
|
|||
"has_wifi": response.has_wifi,
|
||||
"async": True,
|
||||
"initiated_by": request.initiated_by,
|
||||
"board_kind": request.board_kind,
|
||||
"partition_scheme": (request.board_options or {}).get("partitionScheme"),
|
||||
"spiffs_file_count": len(request.spiffs_files or []),
|
||||
},
|
||||
|
|
@ -535,7 +541,7 @@ async def _compile_job(
|
|||
success=False,
|
||||
duration_ms=int((time.monotonic() - started) * 1000),
|
||||
error_kind="exception",
|
||||
extra={"file_count": len(files), "exception": str(exc)[:200], "async": True, "initiated_by": request.initiated_by},
|
||||
extra={"file_count": len(files), "exception": str(exc)[:200], "async": True, "initiated_by": request.initiated_by, "board_kind": request.board_kind},
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -568,7 +574,7 @@ async def compile_sketch(
|
|||
success=False,
|
||||
duration_ms=int((time.monotonic() - started) * 1000),
|
||||
error_kind="exception",
|
||||
extra={"file_count": len(files), "exception": str(e)[:200], "initiated_by": request.initiated_by},
|
||||
extra={"file_count": len(files), "exception": str(e)[:200], "initiated_by": request.initiated_by, "board_kind": request.board_kind},
|
||||
request=http_request,
|
||||
)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
@ -585,6 +591,7 @@ async def compile_sketch(
|
|||
"file_count": len(files),
|
||||
"has_wifi": response.has_wifi,
|
||||
"initiated_by": request.initiated_by,
|
||||
"board_kind": request.board_kind,
|
||||
"partition_scheme": (request.board_options or {}).get("partitionScheme"),
|
||||
"spiffs_file_count": len(request.spiffs_files or []),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import {
|
|||
ONLINE_ONLY_BOARD_ADS,
|
||||
ONLINE_ONLY_COMPONENT_ADS,
|
||||
ONLINE_EDITOR_URL,
|
||||
isOnlineOnlyAdSuppressed,
|
||||
type OnlineOnlyBoardAd,
|
||||
type OnlineOnlyComponentAd,
|
||||
} from '../lib/onlineOnlyBoards';
|
||||
|
|
@ -293,6 +294,7 @@ export const ComponentPickerModal: React.FC<ComponentPickerModalProps> = ({
|
|||
return ONLINE_ONLY_COMPONENT_ADS.filter(
|
||||
(ad) =>
|
||||
!registry.getById(ad.id) &&
|
||||
!isOnlineOnlyAdSuppressed(ad.id) &&
|
||||
(selectedCategory === 'all' || ad.category === selectedCategory) &&
|
||||
(!q || ad.label.toLowerCase().includes(q)),
|
||||
);
|
||||
|
|
@ -858,7 +860,10 @@ const BoardCard: React.FC<BoardCardProps> = ({ kind, onSelect, hoverApi }) => {
|
|||
// Hidden automatically in any build that registers the real BoardKind.
|
||||
/** Recomputed on access (not module load): overlay board registration patches
|
||||
* BOARD_KIND_LABELS at mount, which must hide the corresponding ad. */
|
||||
const visibleBoardAds = () => ONLINE_ONLY_BOARD_ADS.filter((ad) => !(ad.id in BOARD_KIND_LABELS));
|
||||
const visibleBoardAds = () =>
|
||||
ONLINE_ONLY_BOARD_ADS.filter(
|
||||
(ad) => !(ad.id in BOARD_KIND_LABELS) && !isOnlineOnlyAdSuppressed(ad.id),
|
||||
);
|
||||
|
||||
/** Teal "ONLINE" pill: the board runs (free) in the hosted editor. */
|
||||
const OnlineBadge: React.FC = () => (
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ export const EditorToolbar = ({
|
|||
void reportRunEvent({
|
||||
project_id: currentProject?.id ?? null,
|
||||
board_fqbn: fqbn ?? null,
|
||||
board_kind: boardKind ?? null,
|
||||
engine: engine ?? null,
|
||||
});
|
||||
},
|
||||
|
|
@ -571,6 +572,7 @@ export const EditorToolbar = ({
|
|||
{
|
||||
boardOptions: activeBoard?.boardOptions,
|
||||
spiffsFiles: activeBoard?.spiffsFiles,
|
||||
boardKind: kind ?? undefined,
|
||||
// P2.4 — THIS board's declared manifest (compile scope). Per-board so
|
||||
// two boards can use different libraries without clashing.
|
||||
libraries: activeBoard?.libraries?.length ? activeBoard.libraries : null,
|
||||
|
|
@ -1077,7 +1079,7 @@ export const EditorToolbar = ({
|
|||
})),
|
||||
]);
|
||||
},
|
||||
{ boardOptions: board.boardOptions, spiffsFiles: board.spiffsFiles, libraries: board.libraries?.length ? board.libraries : null, language: board.languageMode === 'espidf' ? 'espidf' : undefined },
|
||||
{ boardOptions: board.boardOptions, spiffsFiles: board.spiffsFiles, boardKind: board.boardKind, libraries: board.libraries?.length ? board.libraries : null, language: board.languageMode === 'espidf' ? 'espidf' : undefined },
|
||||
);
|
||||
|
||||
const resultLogs = parseCompileResult(result, label, boardTarget, lastStreamedLen > 0);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,27 @@ export interface OnlineOnlyBoardAd {
|
|||
/** Where the ad cards send the user. */
|
||||
export const ONLINE_EDITOR_URL = 'https://velxio.com';
|
||||
|
||||
/**
|
||||
* Ad suppression — the hosted overlay's escape hatch.
|
||||
*
|
||||
* The "ad disappears when the real thing registers" contract breaks down in
|
||||
* the hosted build itself: an item the overlay knows about but chose NOT to
|
||||
* register (launch embargo) must not fall back to an ad card that says
|
||||
* "available in the online editor" — the user is already IN the online
|
||||
* editor. The overlay calls this once at mount with every id it manages;
|
||||
* items it registers replace their ad anyway, items it embargoes simply
|
||||
* vanish. Pure OSS builds never call it, so ads behave exactly as before.
|
||||
*/
|
||||
const suppressedAdIds = new Set<string>();
|
||||
|
||||
export function suppressOnlineOnlyAds(ids: string[]): void {
|
||||
for (const id of ids) suppressedAdIds.add(id);
|
||||
}
|
||||
|
||||
export function isOnlineOnlyAdSuppressed(id: string): boolean {
|
||||
return suppressedAdIds.has(id);
|
||||
}
|
||||
|
||||
export const ONLINE_ONLY_BOARD_ADS: OnlineOnlyBoardAd[] = [
|
||||
{
|
||||
id: 'esp32-c6',
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ export interface CompileExtras {
|
|||
// Who triggered the compile — 'agent' when the AI assistant's tool did.
|
||||
// Threads through to backend metrics; omitted = manual user action.
|
||||
initiatedBy?: 'agent';
|
||||
// The editor's BoardKind — analytics only. Distinct boards can share one
|
||||
// FQBN (Pimoroni RP2350 boards all compile as rpipico2); the kind is the
|
||||
// only identifier that tells them apart in the metrics.
|
||||
boardKind?: string;
|
||||
}
|
||||
|
||||
export interface CompileResult {
|
||||
|
|
@ -122,6 +126,7 @@ export async function compileCode(
|
|||
project_id: projectId ?? null,
|
||||
board_options,
|
||||
spiffs_files,
|
||||
board_kind: extras?.boardKind ?? null,
|
||||
libraries,
|
||||
language: extras?.language ?? null,
|
||||
initiated_by: extras?.initiatedBy ?? null,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ api.interceptors.request.use((config) => {
|
|||
export interface RunEventPayload {
|
||||
project_id?: string | null;
|
||||
board_fqbn?: string | null;
|
||||
/** Editor BoardKind — disambiguates boards that share an FQBN (analytics). */
|
||||
board_kind?: string | null;
|
||||
/** Which engine took this run — 'instant' (browser) or 'linux' (guest).
|
||||
* Absent for boards where the question doesn't apply. */
|
||||
engine?: 'instant' | 'linux' | null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue