Skip to content

Commit db31d2a

Browse files
committed
fix metrics
1 parent 735028c commit db31d2a

13 files changed

Lines changed: 86 additions & 21 deletions

File tree

apps/web/src/App.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { MenuBar } from "./shared/components/MenuBar";
77
import { EngineErrorNotice } from "./shared/components/EngineErrorNotice";
88
import { ExplorerSidebar } from "./features/explorer/components/ExplorerSidebar";
99
import { DesktopShortcut } from "./shared/components/DesktopShortcut";
10-
import { CompletionHint } from "./features/session/components/CompletionHint";
1110
import { localPacks } from "./content/localPacks";
1211
import type { PromptPackItem } from "./content/types";
1312
import { useSessionStore } from "./features/session/state/sessionStore";
@@ -215,7 +214,6 @@ export default function App() {
215214
}}
216215
/>
217216
</div>
218-
<CompletionHint visible={status === "completed"} />
219217
<BottomStatsPanel
220218
engineSummary={engineSummary}
221219
metrics={metrics}

apps/web/src/app/App.styles.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ export const appStyles = {
1313
editorColumn: "flex flex-1 min-h-0",
1414
editorInner: "flex-1 min-h-0",
1515
explorerPane: (open: boolean) =>
16-
`absolute inset-y-0 right-0 transform transition-transform duration-300 ease-out will-change-transform backdrop-blur-2xl backdrop-saturate-150 ${
17-
open ? "translate-x-0 pointer-events-auto" : "translate-x-full pointer-events-none"
16+
`absolute top-0 right-0 bottom-14 transform transition-transform duration-300 ease-out will-change-transform backdrop-blur-2xl backdrop-saturate-150 ${open ? "translate-x-0 pointer-events-auto" : "translate-x-full pointer-events-none"
1817
}`,
1918
desktopShortcutWrapper: "pointer-events-auto fixed left-6 top-24 z-0",
2019
};

apps/web/src/features/editor/components/EditorSurface.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { SyntaxToken } from "../../../shared/syntax/syntax";
44
import type { SyntaxThemeId } from "../../../shared/syntax/themes";
55
import { TypingFeedback } from "./TypingFeedback";
66
import { editorStyles } from "./EditorSurface.styles";
7+
import { CompletionHint } from "../../session/components/CompletionHint";
78

89
type EditorSurfaceProps = {
910
textareaRef: RefObject<HTMLTextAreaElement>;
@@ -42,10 +43,13 @@ export function EditorSurface({
4243
if (event.defaultPrevented) {
4344
return;
4445
}
45-
if (event.key === "Tab" && !event.shiftKey && onTabAdvance) {
46-
const handled = onTabAdvance();
46+
if (event.key === "Tab" && !event.shiftKey) {
47+
const handled = onTabAdvance ? onTabAdvance() : false;
48+
// Always prevent default tabbing so focus stays in the editor and
49+
// does not jump to UI controls (like explorer toggle).
50+
event.preventDefault();
4751
if (handled) {
48-
event.preventDefault();
52+
return;
4953
}
5054
}
5155
};
@@ -61,6 +65,7 @@ export function EditorSurface({
6165
fontSize={fontSize}
6266
syntaxThemeId={syntaxThemeId}
6367
/>
68+
<CompletionHint visible={status === "completed"} />
6469
<textarea
6570
ref={textareaRef}
6671
value={typedValue}

apps/web/src/features/session/engine/engineClient.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface EngineSummary {
88
incorrect: number;
99
accuracy: number;
1010
completion: number;
11+
keystrokes: number;
1112
feedback: CharacterFeedback[];
1213
}
1314

@@ -94,13 +95,20 @@ async function loadWasmEngine(): Promise<void> {
9495

9596
evaluateFn = (target: string, typed: string, attempts: Uint32Array, language: string) => {
9697
const value = wasm.evaluate_typed!(target, typed, attempts, language) as EngineSummary;
98+
let computedKeystrokes = 0;
99+
for (let i = 0; i < attempts.length; i += 1) {
100+
computedKeystrokes += attempts[i];
101+
}
102+
const keystrokes = typeof (value as any).keystrokes === "number" ? (value as any).keystrokes : computedKeystrokes;
103+
const accuracy = keystrokes > 0 ? Math.min(1, Math.max(0, value.correct / keystrokes)) : 0;
97104
return {
98105
target_length: value.target_length,
99106
typed_length: value.typed_length,
100107
correct: value.correct,
101108
incorrect: value.incorrect,
102-
accuracy: value.accuracy,
109+
accuracy,
103110
completion: value.completion,
111+
keystrokes,
104112
feedback: Array.isArray(value.feedback) ? value.feedback : [],
105113
};
106114
};

apps/web/src/features/session/hooks/usePersistCompletedSession.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function usePersistCompletedSession({
3434
if (!engineReady) {
3535
return;
3636
}
37-
if (status !== "completed" || completedAt == null || engineSummary.completion < 1) {
37+
if (status !== "completed" || completedAt == null) {
3838
return;
3939
}
4040
if (lastPersistedAt.current === completedAt) {
@@ -61,15 +61,29 @@ export function usePersistCompletedSession({
6161
promptPreview,
6262
targetLength: engineSummary.target_length,
6363
typedLength: engineSummary.typed_length,
64+
keystrokes: (engineSummary as any).keystrokes,
6465
correct: engineSummary.correct,
6566
incorrect: engineSummary.incorrect,
6667
accuracy: engineSummary.accuracy,
68+
completion: engineSummary.completion,
6769
wpm: metrics.wpm,
6870
elapsedMs: metrics.elapsedMs,
6971
startedAt: startedAtEpoch,
7072
completedAt: finishedAtEpoch,
7173
attempts: attemptsSnapshot,
7274
});
75+
try {
76+
const event = new CustomEvent("devkeys:session-saved", {
77+
detail: {
78+
id: sessionId,
79+
promptId,
80+
language,
81+
},
82+
});
83+
window.dispatchEvent(event);
84+
} catch {
85+
// no-op in non-DOM environments
86+
}
7387
})();
7488
}, [
7589
attempts,

apps/web/src/features/session/hooks/useSessionMetrics.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const EMPTY_ENGINE_SUMMARY: EngineSummary = {
1010
typed_length: 0,
1111
correct: 0,
1212
incorrect: 0,
13+
keystrokes: 0, // Added missing property
1314
accuracy: 0,
1415
completion: 0,
1516
feedback: [],

apps/web/src/features/session/storage/sessionRepository.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ export interface SessionSummaryRecord {
2222
promptPreview: string;
2323
targetLength: number;
2424
typedLength: number;
25+
keystrokes?: number;
2526
correct: number;
2627
incorrect: number;
2728
accuracy: number;
29+
completion: number;
2830
wpm: number;
2931
elapsedMs: number;
3032
startedAt: number;

apps/web/src/features/stats/components/BottomStatsPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export function BottomStatsPanel({
118118
correct={engineSummary.correct}
119119
incorrect={engineSummary.incorrect}
120120
targetLength={engineSummary.target_length}
121-
typedLength={engineSummary.typed_length}
121+
keystrokes={engineSummary.keystrokes}
122122
/>
123123
)}
124124

apps/web/src/features/stats/components/SessionHistory.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ function SessionCard({ session }: { session: SessionSummaryRecord }) {
6464
const fileName = `${session.language.toLowerCase()}/${session.promptId.replace(/-/g, "_")}.${getFileExtension(session.language)}`;
6565
const accuracyPct = `${(session.accuracy * 100).toFixed(0)}%`;
6666
const duration = formatDuration(session.elapsedMs);
67-
const completion = session.targetLength > 0 ? Math.min(1, Math.max(0, session.typedLength / session.targetLength)) : 0;
67+
const fallbackCompletion = session.targetLength > 0 ? Math.min(1, Math.max(0, session.typedLength / session.targetLength)) : 0;
68+
const completion = typeof (session as any).completion === "number" ? (session as any).completion : fallbackCompletion;
6869

6970
return (
7071
<li className={sessionCardStyles.item}>
@@ -99,7 +100,7 @@ function SessionCard({ session }: { session: SessionSummaryRecord }) {
99100
correct={session.correct}
100101
incorrect={session.incorrect}
101102
targetLength={session.targetLength}
102-
typedLength={session.typedLength}
103+
keystrokes={(session as any).keystrokes ?? session.typedLength}
103104
/>
104105
</div>
105106
)}

apps/web/src/features/stats/components/StatsSummary.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface StatsSummaryProps {
2222
correct: number;
2323
incorrect: number;
2424
targetLength: number;
25-
typedLength: number;
25+
keystrokes: number;
2626
}
2727

2828
export function StatsSummary({
@@ -33,7 +33,7 @@ export function StatsSummary({
3333
correct,
3434
incorrect,
3535
targetLength,
36-
typedLength,
36+
keystrokes,
3737
}: StatsSummaryProps) {
3838
const accuracyPct = `${(accuracy * 100).toFixed(1)}%`;
3939
const completionPct = `${(completion * 100).toFixed(1)}%`;
@@ -48,7 +48,7 @@ export function StatsSummary({
4848
<StatItem label="✔️ Correct" value={`${correct}`} />
4949
<StatItem label="✖️ Incorrect" value={`${incorrect}`} />
5050
<StatItem label="🎯 Target" value={`${targetLength}`} />
51-
<StatItem label="⌨️ Typed" value={`${typedLength}`} />
51+
<StatItem label="⌨️ Keystrokes" value={`${keystrokes}`} />
5252
</div>
5353
);
5454
}

0 commit comments

Comments
 (0)