feat(web): add privacy-friendly Umami custom events

Instrument the play flow with 9 content-free custom events (game_start,
art_style_select, style_image_upload, scene_reached, choice_select,
vision_click, tts_toggle, fullscreen_toggle, play_heartbeat) to measure
retention, engagement depth and session duration.

Privacy is enforced by construction, not convention:
- lib/analytics.ts types each event with a discriminated union, so a
  payload has no slot for free text — prompts, world guides, uploaded
  images and vision output can never reach analytics (compile-time
  guarantee, not a comment).
- track() no-ops without window.umami and never throws into the app.
- coarse 30s heartbeat fires only while the tab is visible.
- script stays gated on NEXT_PUBLIC_UMAMI_* env (blank → no script),
  honours Do-Not-Track, and locks to an exact data-domains allowlist.
- one-line on-site disclosure with a link, shown only when tracking is on.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
yuanzonghao
2026-06-04 10:14:08 +08:00
parent 9f4dcc097b
commit 4bf05f6784
6 changed files with 163 additions and 7 deletions
+2
View File
@@ -2,6 +2,7 @@
import { useRouter } from "next/navigation";
import { useState } from "react";
import { track } from "@/lib/analytics";
export function CustomForm() {
const router = useRouter();
@@ -22,6 +23,7 @@ export function CustomForm() {
"infiplot:custom",
JSON.stringify({ worldSetting, styleGuide }),
);
track("game_start", { source: "custom" });
router.push("/play?custom=1");
}