✨ feat(playground): show execution time in footer#1284
Conversation
…ion time in the footer after running or generating AST\n- Style execution time for better visibility\n\nCloses #XXX (replace with actual issue if applicable)
There was a problem hiding this comment.
Pull request overview
Adds execution timing feedback to the mq-playground UI so users can see how long a run (and currently AST generation) takes.
Changes:
- Add
executionTimestate and measure duration aroundmq.run(...)andmq.toAst(...). - Render an execution-time badge in the playground footer.
- Add CSS styling for the new footer badge.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/mq-playground/src/index.css | Adds .execution-time styling for the new footer badge. |
| packages/mq-playground/src/Playground.tsx | Captures execution timing and renders it in the footer; also changes AST tab behavior to trigger AST generation on tab click. |
Comments suppressed due to low confidence (1)
packages/mq-playground/src/Playground.tsx:312
handleRunreadsisFirstRuninside auseCallback, butisFirstRunis not included in the dependency array. This callback will keep the initial value (likely alwaystrue), so the UI may keep showing "Initializing..." even on subsequent runs. IncludeisFirstRunin the deps or switch to auseRef/functional pattern to track first-run without stale closure issues.
const handleRun = useCallback(async () => {
setIsFirstRun(false);
if (!code || !markdown) {
return;
}
setResult(isFirstRun ? "Initializing..." : "Running...");
setAstResult("");
setExecutionTime(null);
const startTime = performance.now();
try {
setResult(
await mq.run(code, markdown, {
isUpdate,
inputFormat,
listStyle,
linkTitleStyle,
linkUrlStyle,
}),
);
} catch (e) {
setResult((e as Error).toString());
} finally {
const endTime = performance.now();
setExecutionTime(endTime - startTime);
}
}, [
code,
markdown,
inputFormat,
isUpdate,
listStyle,
linkUrlStyle,
linkTitleStyle,
]);
| {executionTime && ( | ||
| <div className="footer-right"> | ||
| <div className="execution-time"> | ||
| {executionTime.toFixed(2)} ms | ||
| </div> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
The conditional render uses {executionTime && (...)}. Since executionTime is a number | null, a measured value of 0 will be treated as falsy and the footer badge will not render. Prefer an explicit null check (e.g. executionTime !== null) so 0 ms still displays.
| className={`tab ${activeTab === "ast" ? "active" : ""}`} | ||
| onClick={() => setActiveTab("ast")} | ||
| onClick={() => { | ||
| handleGenerateAst(); | ||
| setActiveTab("ast"); | ||
| }} |
There was a problem hiding this comment.
Clicking the "AST" tab now triggers handleGenerateAst() automatically, while there is still a separate "Generate AST" button shown when the AST tab is active. This is a behavioral change unrelated to the PR title and creates two different entry points (and repeated re-generation on repeated tab clicks). Consider either reverting to just tab switching, or removing the extra button / gating auto-generation (e.g. only generate if astResult is empty or code changed).
| setExecutionTime(null); | ||
|
|
||
| const startTime = performance.now(); | ||
|
|
||
| try { | ||
| const ast = await mq.toAst(code); | ||
| setAstResult(JSON.stringify(JSON.parse(ast), null, " ")); | ||
| } catch (e) { | ||
| setAstResult((e as Error).toString()); | ||
| } finally { | ||
| const endTime = performance.now(); | ||
| setExecutionTime(endTime - startTime); |
There was a problem hiding this comment.
executionTime is updated by both handleRun and handleGenerateAst, but the footer just renders a generic "ms" value. After generating an AST (or if operations overlap), the footer can show the AST timing while the Output tab/result is shown, which makes the value misleading. Consider tracking separate timings (e.g., runExecutionTime vs astExecutionTime) or storing { kind, ms } and only displaying the timing relevant to the active tab/last completed action.
| setExecutionTime(null); | |
| const startTime = performance.now(); | |
| try { | |
| const ast = await mq.toAst(code); | |
| setAstResult(JSON.stringify(JSON.parse(ast), null, " ")); | |
| } catch (e) { | |
| setAstResult((e as Error).toString()); | |
| } finally { | |
| const endTime = performance.now(); | |
| setExecutionTime(endTime - startTime); | |
| try { | |
| const ast = await mq.toAst(code); | |
| setAstResult(JSON.stringify(JSON.parse(ast), null, " ")); | |
| } catch (e) { | |
| setAstResult((e as Error).toString()); |
No description provided.