diff --git a/src/components/Componentes/dev-tap-instrumentation.tsx b/src/components/Componentes/dev-tap-instrumentation.tsx
index 9423a5a..6bcbca2 100644
--- a/src/components/Componentes/dev-tap-instrumentation.tsx
+++ b/src/components/Componentes/dev-tap-instrumentation.tsx
@@ -4,38 +4,79 @@ import { useEffect } from "react";
/**
* Development-only capture-phase instrumentation for debugging section-card
- * tap responsiveness. Records pointerdown → pointerup → click timing and
- * whether the event reaches the DOM at all (vs being swallowed by a native
- * overlay such as Flutter's loading cover).
+ * and question flow tap/focus responsiveness. Records pointerdown → pointerup → click,
+ * touchstart → touchmove → touchend, and focusin/focusout lifecycle events.
*
- * Mount this inside the questions-list page during development. Remove or
- * gate behind process.env.NODE_ENV check for production.
+ * Mount this inside the questions-list and question detail flows during development.
+ * Automatically inactive outside of development.
*/
export default function DevTapInstrumentation() {
useEffect(() => {
if (process.env.NODE_ENV !== "development") return;
- const events = ["pointerdown", "pointerup", "pointercancel", "click"] as const;
+ const events = [
+ "pointerdown",
+ "pointerup",
+ "pointercancel",
+ "click",
+ "touchstart",
+ "touchmove",
+ "touchend",
+ "touchcancel",
+ "focusin",
+ "focusout",
+ ] as const;
+
const startTime = performance.now();
let sequenceId = 0;
+ let touchStartY: number | null = null;
const handler = (event: Event) => {
- const e = event as PointerEvent;
- const target = e.target as HTMLElement | null;
+ const target = event.target as HTMLElement | null;
const anchor = target?.closest?.("a");
const elapsed = (performance.now() - startTime).toFixed(1);
sequenceId += 1;
+ let deltaY: number | null = null;
+ if (event.type === "touchstart") {
+ touchStartY = (event as TouchEvent).touches?.[0]?.clientY ?? null;
+ } else if (event.type === "touchmove" || event.type === "touchend") {
+ const currentY =
+ (event as TouchEvent).touches?.[0]?.clientY ??
+ (event as TouchEvent).changedTouches?.[0]?.clientY ??
+ null;
+ if (touchStartY !== null && currentY !== null) {
+ deltaY = Math.round(touchStartY - currentY);
+ }
+ }
+
+ const isInteractive = Boolean(
+ target?.closest?.(
+ 'input, textarea, select, button, label, a, [role="button"], [role="option"], [role="checkbox"], [role="radio"], [role="switch"], [role="combobox"], [role="listbox"], [contenteditable="true"]',
+ ),
+ );
+
+ const color = event.type.startsWith("focus")
+ ? "#0EB13C"
+ : event.type.startsWith("touch")
+ ? "#3B82F6"
+ : "#E03950";
+
console.debug(
- `[tap-debug #${sequenceId}] %c${e.type}%c @ ${elapsed}ms`,
- "color: #E03950; font-weight: bold",
+ `[tap-debug #${sequenceId}] %c${event.type}%c @ ${elapsed}ms`,
+ `color: ${color}; font-weight: bold`,
"color: inherit",
{
- pointerType: (e as PointerEvent).pointerType || "n/a",
+ type: event.type,
target: target?.tagName,
targetId: target?.id,
+ targetClass: target?.className,
+ activeElement: document.activeElement?.tagName,
+ activeElementId: document.activeElement?.id,
+ isInteractive,
+ deltaY,
closestAnchorHref: anchor?.getAttribute("href") || null,
- defaultPrevented: e.defaultPrevented,
+ defaultPrevented: event.defaultPrevented,
pathname: window.location.pathname,
timestamp: performance.now(),
},
diff --git a/src/components/Componentes/question-section-flow.tsx b/src/components/Componentes/question-section-flow.tsx
index 9570803..054814a 100644
--- a/src/components/Componentes/question-section-flow.tsx
+++ b/src/components/Componentes/question-section-flow.tsx
@@ -17,6 +17,7 @@ import AnswerPaceSheet from "@/app/questions-list/[slug]/answer-pace-sheet";
import { FixToTheEnd } from "./fix-to-the-end";
import Button from "./button";
import { markFirstEntryCompleted } from "@/lib/first-entry-helper";
+import DevTapInstrumentation from "./dev-tap-instrumentation";
type QuestionSectionFlowProps = {
children: ReactNode;
@@ -133,6 +134,7 @@ function SectionFlowContent({
{children}
+ {process.env.NODE_ENV === "development" ? : null}