296 lines
7.9 KiB
Plaintext
296 lines
7.9 KiB
Plaintext
[2026-05-14T20:18:40.440Z] PROMPT
|
|
============================================================
|
|
Analyze this codebase for performance optimizations:
|
|
- Identify N+1 query patterns
|
|
- Find unnecessary re-renders in React
|
|
- Suggest caching opportunities
|
|
- Identify memory leaks
|
|
- Find redundant computations
|
|
|
|
Provide actionable suggestions with code examples.
|
|
|
|
## Codebase Context
|
|
|
|
--- src/canvas/types.ts ---
|
|
export type CardId = string;
|
|
|
|
export type CardKind = "note" | "terminal" | "app" | "thumbnail";
|
|
|
|
export interface BaseCard {
|
|
id: CardId;
|
|
kind: CardKind;
|
|
x: number;
|
|
y: number;
|
|
width: number;
|
|
height: number;
|
|
z: number;
|
|
}
|
|
|
|
export interface NoteCard extends BaseCard {
|
|
kind: "note";
|
|
text: string;
|
|
}
|
|
|
|
export interface TerminalCard extends BaseCard {
|
|
kind: "terminal";
|
|
ptyId: string;
|
|
}
|
|
|
|
export interface AppCard extends BaseCard {
|
|
kind: "app";
|
|
xWindowId: number;
|
|
command: string;
|
|
title?: string;
|
|
}
|
|
|
|
export interface ThumbnailCard extends BaseCard {
|
|
kind: "thumbnail";
|
|
refCardId: CardId;
|
|
label: string;
|
|
}
|
|
|
|
export type Card = NoteCard | TerminalCard | AppCard | ThumbnailCard;
|
|
|
|
export interface Viewport {
|
|
x: number;
|
|
y: number;
|
|
scale: number;
|
|
}
|
|
|
|
|
|
--- src/App.tsx ---
|
|
import { Canvas } from "./canvas/Canvas";
|
|
import type { Card } from "./canvas/types";
|
|
|
|
const initialCards: Card[] = [
|
|
{
|
|
id: "welcome",
|
|
kind: "note",
|
|
x: 200,
|
|
y: 200,
|
|
width: 320,
|
|
height: 180,
|
|
z: 0,
|
|
text: "Welcome to Infinite.\n\nPan: middle-drag or space+drag.\nZoom: Ctrl+wheel.",
|
|
},
|
|
{
|
|
id: "todo",
|
|
kind: "note",
|
|
x: 600,
|
|
y: 320,
|
|
width: 260,
|
|
height: 140,
|
|
z: 0,
|
|
text: "Next: terminal cards, then X11 embedding.",
|
|
},
|
|
];
|
|
|
|
export function App() {
|
|
return <Canvas initialCards={initialCards} />;
|
|
}
|
|
|
|
|
|
--- src/canvas/Canvas.tsx ---
|
|
import { useEffect, useRef, useState, useCallback } from "react";
|
|
import type { Card, Viewport } from "./types";
|
|
import { NoteCardView } from "./cards/NoteCardView";
|
|
import "./canvas.css";
|
|
|
|
const MIN_SCALE = 0.1;
|
|
const MAX_SCALE = 4;
|
|
const ZOOM_SENSITIVITY = 0.0015;
|
|
|
|
interface CanvasProps {
|
|
initialCards: Card[];
|
|
}
|
|
|
|
export function Canvas({ initialCards }: CanvasProps) {
|
|
const [cards, setCards] = useState<Card[]>(initialCards);
|
|
const [vp, setVp] = useState<Viewport>({ x: 0, y: 0, scale: 1 });
|
|
const [spaceHeld, setSpaceHeld] = useState(false);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const panState = useRef<{ startX: number; startY: number; vpX: number; vpY: number } | null>(
|
|
null,
|
|
);
|
|
|
|
useEffect(() => {
|
|
const onKeyDown = (e: KeyboardEvent) => {
|
|
if (e.code === "Space" && !e.repeat) setSpaceHeld(true);
|
|
};
|
|
const onKeyUp = (e: KeyboardEvent) => {
|
|
if (e.code === "Space") setSpaceHeld(false);
|
|
};
|
|
window.addEventListener("keydown", onKeyDown);
|
|
window.addEventListener("keyup", onKeyUp);
|
|
return () => {
|
|
window.removeEventListener("keydown", onKeyDown);
|
|
window.removeEventListener("keyup", onKeyUp);
|
|
};
|
|
}, []);
|
|
|
|
const onWheel = useCallback(
|
|
(e: React.WheelEvent) => {
|
|
if (!e.ctrlKey && !e.metaKey) return;
|
|
e.preventDefault();
|
|
const rect = containerRef.current!.getBoundingClientRect();
|
|
const mx = e.clientX - rect.left;
|
|
const my = e.clientY - rect.top;
|
|
setVp((prev) => {
|
|
const factor = Math.exp(-e.deltaY * ZOOM_SENSITIVITY);
|
|
const next = Math.max(MIN_SCALE, Math.min(MAX_SCALE, prev.scale * factor));
|
|
const k = next / prev.scale;
|
|
return { x: mx - (mx - prev.x) * k, y: my - (my - prev.y) * k, scale: next };
|
|
});
|
|
},
|
|
[],
|
|
);
|
|
|
|
const onPointerDown = (e: React.PointerEvent) => {
|
|
const isPan = e.button === 1 || (e.button === 0 && spaceHeld);
|
|
if (!isPan) return;
|
|
e.preventDefault();
|
|
(e.target as Element).setPointerCapture(e.pointerId);
|
|
panState.current = { startX: e.clientX, startY: e.clientY, vpX: vp.x, vpY: vp.y };
|
|
};
|
|
|
|
const onPointerMove = (e: React.PointerEvent) => {
|
|
if (!panState.current) return;
|
|
const dx = e.clientX - panState.current.startX;
|
|
const dy = e.clientY - panState.current.startY;
|
|
setVp((prev) => ({ ...prev, x: panState.current!.vpX + dx, y: panState.current!.vpY + dy }));
|
|
};
|
|
|
|
const onPointerUp = (e: React.PointerEvent) => {
|
|
if (panState.current) {
|
|
(e.target as Element).releasePointerCapture(e.pointerId);
|
|
panState.current = null;
|
|
}
|
|
};
|
|
|
|
const updateCard = (id: string, patch: Partial<Card>) => {
|
|
setCards((cs) => cs.map((c) => (c.id === id ? ({ ...c, ...patch } as Card) : c)));
|
|
};
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
className={`canvas-container ${spaceHeld ? "pan-mode" : ""}`}
|
|
onWheel={onWheel}
|
|
onPointerDown={onPointerDown}
|
|
onPointerMove={onPointerMove}
|
|
onPointerUp={onPointerUp}
|
|
onPointerCancel={onPointerUp}
|
|
>
|
|
<div
|
|
className="canvas-grid"
|
|
style={{
|
|
backgroundPosition: `${vp.x}px ${vp.y}px`,
|
|
backgroundSize: `${40 * vp.scale}px ${40 * vp.scale}px`,
|
|
}}
|
|
/>
|
|
<div
|
|
className="canvas-world"
|
|
style={{ transform: `translate(${vp.x}px, ${vp.y}px) scale(${vp.scale})` }}
|
|
>
|
|
{cards.map((c) => {
|
|
if (c.kind === "note") {
|
|
return <NoteCardView key={c.id} card={c} onUpdate={(p) => updateCard(c.id, p)} />;
|
|
}
|
|
return null;
|
|
})}
|
|
</div>
|
|
<div className="canvas-hud">
|
|
<span>x {vp.x.toFixed(0)}</span>
|
|
<span>y {vp.y.toFixed(0)}</span>
|
|
<span>{(vp.scale * 100).toFixed(0)}%</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
|
|
--- src/canvas/cards/NoteCardView.tsx ---
|
|
import { useRef } from "react";
|
|
import type { NoteCard } from "../types";
|
|
|
|
interface Props {
|
|
card: NoteCard;
|
|
onUpdate: (patch: Partial<NoteCard>) => void;
|
|
}
|
|
|
|
export function NoteCardView({ card, onUpdate }: Props) {
|
|
const dragState = useRef<{ startX: number; startY: number; cardX: number; cardY: number } | null>(
|
|
null,
|
|
);
|
|
|
|
const onHeaderPointerDown = (e: React.PointerEvent) => {
|
|
if (e.button !== 0) return;
|
|
e.stopPropagation();
|
|
(e.target as Element).setPointerCapture(e.pointerId);
|
|
dragState.current = { startX: e.clientX, startY: e.clientY, cardX: card.x, cardY: card.y };
|
|
};
|
|
|
|
const onHeaderPointerMove = (e: React.PointerEvent) => {
|
|
if (!dragState.current) return;
|
|
const worldEl = (e.currentTarget as HTMLElement).closest(".canvas-world") as HTMLElement;
|
|
const scale = worldEl ? parseTransformScale(worldEl.style.transform) : 1;
|
|
const dx = (e.clientX - dragState.current.startX) / scale;
|
|
const dy = (e.clientY - dragState.current.startY) / scale;
|
|
onUpdate({ x: dragState.current.cardX + dx, y: dragState.current.cardY + dy });
|
|
};
|
|
|
|
const onHeaderPointerUp = (e: React.PointerEvent) => {
|
|
if (dragState.current) {
|
|
(e.target as Element).releasePointerCapture(e.pointerId);
|
|
dragState.current = null;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className="card note-card"
|
|
style={{ left: card.x, top: card.y, width: card.width, height: card.height, zIndex: card.z }}
|
|
>
|
|
<div
|
|
className="card-header"
|
|
onPointerDown={onHeaderPointerDown}
|
|
onPointerMove={onHeaderPointerMove}
|
|
onPointerUp={onHeaderPointerUp}
|
|
onPointerCancel={onHeaderPointerUp}
|
|
>
|
|
note
|
|
</div>
|
|
<div className="card-body">
|
|
<textarea
|
|
value={card.text}
|
|
onChange={(e) => onUpdate({ text: e.target.value })}
|
|
onPointerDown={(e) => e.stopPropagation()}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function parseTransformScale(transform: string): number {
|
|
const m = transform.match(/scale\(([^)]+)\)/);
|
|
return m ? parseFloat(m[1]) : 1;
|
|
}
|
|
|
|
|
|
--- src/main.tsx ---
|
|
import React from "react";
|
|
import ReactDOM from "react-dom/client";
|
|
import { App } from "./App";
|
|
import "./styles.css";
|
|
|
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
<React.StrictMode>
|
|
<App />
|
|
</React.StrictMode>,
|
|
);
|
|
|
|
|
|
## Instructions
|
|
|
|
Analyze the above codebase context and provide your response following the format specified in the task.
|