Skip to content

Initialize project connection to repository - #1

Open
ordokr wants to merge 1 commit into
mainfrom
v0/timothyvail-6603-072ec0c5
Open

Initialize project connection to repository#1
ordokr wants to merge 1 commit into
mainfrom
v0/timothyvail-6603-072ec0c5

Conversation

@ordokr

@ordokr ordokr commented Apr 2, 2026

Copy link
Copy Markdown
Owner
  • Established the initial connection between the development environment and the repository.
  • Initialized the project branch to enable version control and deployment workflows.

v0 Session

#VERCEL_SKIP
Copilot AI review requested due to automatic review settings April 2, 2026 00:53
@vercel vercel Bot added the v0 label Apr 2, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ccdd94d33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

position={[xPos, 0, 0]}
isPressed={isPressed}
onPress={() => {
const keyVal = keyData.code === "Space" ? " " : keyData.label

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Dispatch canonical Backspace key for clicked keycaps

The click handler synthesizes KeyboardEvent.key from keyData.label, so the backspace key emits "Bksp" instead of "Backspace". Because the typing handler only deletes on key === "Backspace", clicking the 3D backspace never removes characters, which breaks the virtual-keyboard interaction path for users not typing on a physical keyboard.

Useful? React with 👍 / 👎.

Comment thread app/api/chat/route.ts
import { nanoid } from 'nanoid'

export async function POST(req: Request) {
const ip = ipAddress(req) || 'unknown'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid sharing one rate-limit bucket for missing client IPs

Using 'unknown' as a fallback IP means every request without a resolved client address is rate-limited under the same ip:unknown key. In environments where ipAddress(req) is unavailable (e.g., local/self-hosted or certain proxy setups), one user can consume the shared quota and cause unrelated users to be throttled.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the previous standalone/legacy TypeScript + three/webgpu utilities with an initial Next.js (v0.app) application scaffold for a “3D Keyboard Chat” experience, including UI components, styling, assets, and an /api/chat route.

Changes:

  • Added a Next.js App Router app with a 3D keyboard scene, terminal UI, and chat integration.
  • Added Tailwind/PostCSS + TypeScript project configuration and shadcn-style UI primitives.
  • Added an /api/chat endpoint with Upstash Redis rate limiting; removed several legacy rendering/worker modules.

Reviewed changes

Copilot reviewed 31 out of 45 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
voxelPathfindingWorker.ts Removed legacy voxel pathfinding worker implementation.
PlanetScene.ts Removed legacy WebGPU/TSL planet scene implementation.
BoxFroxelPipeline.ts Removed legacy fog froxel compute pipeline implementation.
asciiMaterial.ts Removed legacy ASCII node-material implementation.
tsconfig.json Added TS config (strict/noEmit, path alias mapping).
next.config.mjs Added Next.js config (TS build settings, image config).
postcss.config.mjs Added Tailwind PostCSS pipeline config.
package.json Added Next.js app dependencies/scripts.
.gitignore Added Next/TS/env ignore rules.
README.md Replaced placeholder README with v0.app/Vercel sync info.
components.json Added shadcn/ui configuration metadata.
lib/utils.ts Added cn() utility for className composition.
lib/rate-limit.ts Added Upstash Redis + rate limiter instances.
app/layout.tsx Added root layout with fonts, metadata, analytics, and global CSS import.
app/page.tsx Added main page rendering the 3D keyboard scene.
app/globals.css Added Tailwind theme variables and global styles (incl. terminal scrollbar/cursor).
app/api/chat/route.ts Added chat streaming endpoint with cookie/IP rate limiting.
components/theme-provider.tsx Added NextThemes wrapper component (currently not wired in).
components/keyboard-scene.tsx Added 3D Canvas scene + physical keyboard input -> chat messaging.
components/keyboard-3d.tsx Added keyboard mesh + key components; mouse click simulates key events.
components/key.tsx Added individual key rendering/animation and key label text.
components/keyboard-layout.ts Added keyboard layout data model.
components/terminal.tsx Added terminal-style message rendering and “current input” line.
components/rate-limit-dialog.tsx Added modal shown when rate limit is hit.
components/ai-elements/conversation.tsx Added conversation container with stick-to-bottom behavior.
components/ai-elements/message.tsx Added message/attachments rendering primitives.
components/ai-elements/prompt-input.tsx Added large prompt-input component (currently references missing UI modules).
components/ui/button.tsx Added button primitive with variants.
components/ui/button-group.tsx Added button group primitive.
components/ui/dialog.tsx Added dialog primitive.
components/ui/separator.tsx Added separator primitive.
components/ui/tooltip.tsx Added tooltip primitive.
styles/globals.css Added a second global stylesheet (appears unused).
public/placeholder.svg Added placeholder asset.
public/placeholder.jpg Added placeholder asset.
public/placeholder-user.jpg Added placeholder asset.
public/placeholder-logo.svg Added placeholder asset.
public/placeholder-logo.png Added placeholder asset.
public/icon.svg Added app icon asset.
public/icon-light-32x32.png Added light icon asset.
public/icon-dark-32x32.png Added dark icon asset.
public/apple-icon.png Added Apple touch icon asset.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread next.config.mjs
Comment on lines +2 to +8
const nextConfig = {
typescript: {
ignoreBuildErrors: true,
},
images: {
unoptimized: true,
},

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typescript.ignoreBuildErrors: true disables type-checking during next build, which will mask real issues (e.g., missing imports) and can ship broken runtime code. Recommend removing this (or gating it to local/dev only) so CI/build fails on TypeScript errors.

Copilot uses AI. Check for mistakes.
Comment thread lib/rate-limit.ts
Comment on lines +7 to +19
// Cookie-based rate limiter: 10 requests per 10 seconds
export const cookieRateLimiter = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, "1 h"),
prefix: "ratelimit:cookie",
analytics: true,
})

// IP-based rate limiter: 50 requests per 10 seconds
export const ipRateLimiter = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, "1 d"),
prefix: "ratelimit:ip",

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rate-limit comments don’t match the actual limiter windows. slidingWindow(10, "1 h") is 10/hour (not 10 per 10s) and slidingWindow(10, "1 d") is 10/day (not 50 per 10s). Either update the comments or adjust the limiter durations/counts so the code matches the intended policy.

Copilot uses AI. Check for mistakes.
Comment thread app/api/chat/route.ts
)
}
} else {
cookieStore.set('session', nanoid())

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The session cookie is set without any attributes. Since it’s used for rate limiting, it should be set with safer defaults (e.g., httpOnly, sameSite: 'lax'/'strict', secure in production, and a reasonable maxAge/expires) to reduce tampering and cross-site leakage.

Suggested change
cookieStore.set('session', nanoid())
cookieStore.set('session', nanoid(), {
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 7, // 7 days
path: '/',
})

Copilot uses AI. Check for mistakes.
Comment thread app/api/chat/route.ts
Comment on lines +17 to +44
if (!cookieSuccess) {
return new Response(
JSON.stringify({
error: "Too many requests",
message: "Please wait before sending another message.",
}),
{
status: 429,
},
)
}
} else {
cookieStore.set('session', nanoid())
}

const { success: ipSuccess } = await ipRateLimiter.limit(`ip:${ip}`)


if (!ipSuccess) {
return new Response(
JSON.stringify({
error: "Too many requests",
message: "Please wait before sending another message.",
}),
{
status: 429,
},
)

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 429 JSON responses are returned without a Content-Type: application/json header. Some clients/middleware rely on this; consider setting it explicitly in the Response init headers for both rate-limit branches.

Copilot uses AI. Check for mistakes.
window.removeEventListener("keydown", handleKeyDown)
window.removeEventListener("keyup", handleKeyUp)
}
}, [capsLock, shiftPressed, typedText, sendMessage])

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The window keydown/keyup listeners are re-registered on every change to capsLock, shiftPressed, typedText, or sendMessage due to the effect dependencies. This can cause unnecessary churn and missed keystrokes under rapid input. Prefer stable handlers (e.g., useCallback + refs for mutable state) so the listeners are attached once.

Suggested change
}, [capsLock, shiftPressed, typedText, sendMessage])
}, [])

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +74
onPress={() => {
const keyVal = keyData.code === "Space" ? " " : keyData.label

// Simulate key press for mouse clicks
const event = new KeyboardEvent("keydown", {
key: keyVal,
code: keyData.code,
})
window.dispatchEvent(event)

setTimeout(() => {
const upEvent = new KeyboardEvent("keyup", {
key: keyVal,
code: keyData.code,
})
window.dispatchEvent(upEvent)
}, 100)
}}

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mouse-click simulated key events use key: keyData.label for most keys. For Backspace (label Bksp) and Escape (label Esc), this means KeyboardScene won’t recognize them (e.key won’t equal Backspace/Escape). Map KeyboardEvent.key to standard values for non-character keys (Backspace, Escape, Enter, Tab, etc.) so click interactions behave the same as physical keyboard input.

Copilot uses AI. Check for mistakes.
Comment thread components/key.tsx
rotation={[-Math.PI / 2, 0, 0]}
fontSize={data.fontSize || 0.25}
color={textColor}
font="https://raw.githubusercontent.com/vercel/geist-font/main/fonts/GeistMono/ttf/GeistMono-Bold.ttf"

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key label font is loaded from a GitHub raw URL at runtime. This is an external dependency that can be slow, blocked by CSP, or fail offline, leaving missing text. Prefer bundling the font locally (or using a project asset/CDN you control) and referencing it via a local URL.

Suggested change
font="https://raw.githubusercontent.com/vercel/geist-font/main/fonts/GeistMono/ttf/GeistMono-Bold.ttf"
font="/fonts/GeistMono-Bold.ttf"

Copilot uses AI. Check for mistakes.
Comment thread styles/globals.css
Comment on lines +1 to +5
@import 'tailwindcss';
@import 'tw-animate-css';

@custom-variant dark (&:is(.dark *));

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

styles/globals.css appears unused (no imports/references found), while app/layout.tsx imports app/globals.css. Keeping two global stylesheets with overlapping content risks divergence and confusion; consider removing this file or wiring it up intentionally.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +36
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupTextarea,
} from "@/components/ui/input-group";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file imports UI components that don’t exist in the repo (@/components/ui/command, dropdown-menu, hover-card, input-group, select). If typescript.ignoreBuildErrors is removed (recommended), next build/typecheck will fail. Either add the missing component files or remove/refactor these imports (and any unused PromptInput code) to keep the project buildable.

Copilot uses AI. Check for mistakes.
Comment thread package.json
Comment on lines +5 to +10
"scripts": {
"build": "next build",
"dev": "next dev",
"lint": "eslint .",
"start": "next start"
},

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lint script runs eslint ., but eslint (and a config) isn’t present in dependencies/devDependencies. As-is, pnpm lint will fail. Either add ESLint + config (e.g., next lint with eslint-config-next) or adjust/remove the lint script.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants