Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ jobs:
version: 11.2.2
- uses: actions/setup-node@v7
with:
node-version: 22
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run vp check
run: pnpm exec vp check
- name: Run quality checks
run: pnpm quality
Comment thread
Geczy marked this conversation as resolved.
- name: Run tests
run: pnpm test
20 changes: 4 additions & 16 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Code Style

- TypeScript for all code with strict typing
- Formatting/linting via Vite+ (oxfmt + oxlint): 2-space indentation, single quotes, avoid semicolons. Run `pnpm exec vp check` (or `vp check --fix`).
- Formatting/linting via standalone Oxfmt + Oxlint: 2-space indentation, single quotes, avoid semicolons. Run `pnpm check` (or `pnpm format:fix && pnpm lint:fix`). Run `pnpm quality` for the full gate, including Knip's unused-code/dependency analysis.
- Use React functional components with hooks
- Use `@/` imports with paths configured in tsconfig.json
- Follow mobile-first responsive design with Tailwind CSS
Expand All @@ -24,7 +24,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Test Guidelines

- Place tests in `__tests__` folders next to source files
- Use Vitest's `vi.mock()` at the top of test files
- Test through dependency seams and injected fakes instead of `vi.mock()`; legacy module mocks are migration violations under the anti-slop rules
- Follow Arrange-Act-Assert pattern
- Use `vi.stubEnv()` instead of direct environment variable assignment
- Reset mocks between tests with `vi.resetAllMocks()`
Expand All @@ -37,19 +37,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- Use redux for state management
- Follow folder structure conventions

<!--VITE PLUS START-->

# Using Vite+, the Unified Toolchain for the Web

This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, and it invokes Vite through `vp dev` and `vp build`. Run `vp help` to print a list of commands and `vp <command> --help` for information about a specific command.

Docs are local at `node_modules/vite-plus/docs` or online at https://viteplus.dev/guide/.

## Review Checklist

- [ ] Run `vp install` after pulling remote changes and before getting started.
- [ ] Run `vp check` and `vp test` to format, lint, type check and test changes.
- [ ] Check if there are `vite.config.ts` tasks or `package.json` scripts necessary for validation, run via `vp run <script>`.
- [ ] If setup, runtime, or package-manager behavior looks wrong, run `vp env doctor` and include its output when asking for help.

<!--VITE PLUS END-->
- [ ] Run `pnpm install` after pulling remote changes.
- [ ] Run `pnpm quality` and `pnpm test` to format, lint, type check, detect unused code/dependencies, and test changes.
4 changes: 1 addition & 3 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,7 @@ State tones are used as a triplet: border + tinted background (40% alpha on the

## 3. Typography

**Body Font:** Inter (variable, weights 100–900, loaded from `/fonts/Inter-roman.var.woff2`).
**Display Font:** Inter (same family; hierarchy is built from weight and size, not from a second family).
**Reserved fonts:** Radiance and RadianceM are loaded for the OBS overlay's Dota-themed UI only. They must not appear in the dashboard.
**Body Font:** Inter (variable, weights 100–900, loaded from `/fonts/Inter-roman.var.woff2`). **Display Font:** Inter (same family; hierarchy is built from weight and size, not from a second family). **Reserved fonts:** Radiance and RadianceM are loaded for the OBS overlay's Dota-themed UI only. They must not appear in the dashboard.

**Character:** A single neutral sans speaking at a moderate scale. Inter does the work — tight numerals, level x-height, no italic flourish. Hierarchy comes from weight contrast (400 / 500 / 600) and size jumps, not from family switching.

Expand Down
9 changes: 3 additions & 6 deletions features.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,15 @@ This is optional but useful if you want to make your stream look unique for diff

#### Minimap blocker

`[dotabod] blocking minimap`
Whenever the minimap is first shown, switch to this scene
`[dotabod] blocking minimap` Whenever the minimap is first shown, switch to this scene

#### Picks blocker

`[dotabod] blocking picks`
As soon as picks are shown and heroes are able to be selected, switch to this scene
`[dotabod] blocking picks` As soon as picks are shown and heroes are able to be selected, switch to this scene

#### Game disconnected

`[dotabod] game disconnected`
Switch to this scene when you disconnect and leave a Dota game
`[dotabod] game disconnected` Switch to this scene when you disconnect and leave a Dota game

## Managers

Expand Down
5 changes: 3 additions & 2 deletions knip.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://unpkg.com/knip@6/schema.json",
"ignore": ["public/styles/obs-compat.css"],
"ignore": [".agents/**", "public/styles/obs-compat.css"],
"ignoreBinaries": ["doppler"],
"entry": ["scripts/**/*.{ts,mjs}"]
"ignoreDependencies": ["npm"],
"entry": ["scripts/**/*.{ts,mjs}", "supabase/functions/**/*.ts"]
}
18 changes: 7 additions & 11 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,17 @@ pre-commit:
fmt:
glob: '*.{js,jsx,ts,tsx,mjs,cjs,json,md,yml,yaml,css}'
run: |
npx vp fmt {staged_files} \
npx oxfmt {staged_files} \
&& git add {staged_files}
lint-fix:
glob: '*.{js,jsx,ts,tsx,mjs,cjs}'
run: |
npx vp lint --fix {staged_files} \
&& git add {staged_files}
check:
run: npx vp check
# Full project tsc — catches Prisma/generated-type errors that oxlint
# (`vp check`) doesn't analyze. Vercel's `next build` runs the real
# The full, strict Oxlint ratchet runs in CI via `pnpm quality`. Running
# raw Oxlint on a staged legacy file here would reject an unrelated commit
# for existing baselined findings that `--fix` cannot repair.
# Full project tsc — catches Prisma/generated-type errors that Oxlint
# doesn't analyze. Vercel's `next build` runs the real
# TypeScript compiler too; this hook surfaces the same class of error
# locally before it red-lights the deploy. tsc is incremental
# (tsconfig.json sets `incremental: true`), so warm runs are fast.
typecheck:
run: npm run typecheck
test:
run: npx vp test run
run: npx vitest run
3 changes: 2 additions & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";
import "./.next/types/routes.d.ts";
import "./.next/types/root-params.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
91 changes: 43 additions & 48 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// https://nextjs.org/docs/api-reference/next.config.js/introduction
// https://docs.sentry.io/platforms/javascript/guides/nextjs/

import { withSentryConfig } from '@sentry/nextjs'
import { withSentryConfig } from '@sentry/nextjs/config'

/**
* @type {import('next').NextConfig}
Expand Down Expand Up @@ -160,59 +160,54 @@ const nextConfig = {
},
}

export default withSentryConfig(
withSentryConfig(nextConfig, {
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options

org: 'mgates-llc',
project: 'dotabod-frontend',

release: {
name: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
export default withSentryConfig(nextConfig, {
// Annotate React components in breadcrumbs and session replay.
// Turbopack equivalent of webpack.reactComponentAnnotation (Sentry >= 10.43.0).
_experimental: {
turbopackReactComponentAnnotation: {
enabled: true,
},
},

// Only print logs for uploading source maps in CI
silent: !process.env.CI,

// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options

// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
org: 'mgates-llc',
project: 'dotabod-frontend',

// Comment out the property that's causing the linter error
// TranspileClientSDK: true,
release: {
name: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
},

// Hides source maps from generated client bundles
sourcemaps: {
assets: './**/*.map',
deleteSourcemapsAfterUpload: true,
},
// Only print logs for uploading source maps in CI
silent: !process.env.CI,

// Annotate React components in breadcrumbs and session replay.
// Turbopack equivalent of webpack.reactComponentAnnotation (Sentry >= 10.43.0).
_experimental: {
turbopackReactComponentAnnotation: {
enabled: true,
},
},
// Comment out the property that's causing the linter error
// TranspileClientSDK: true,

// Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// Side errors will fail.
// TunnelRoute: "/monitoring",
// Hides source maps from generated client bundles
sourcemaps: {
assets: './**/*.map',
deleteSourcemapsAfterUpload: true,
},

// Webpack-only options; ignored under the default Turbopack build (Next 16),
// So they only take effect with `next build --webpack`.
webpack: {
// Tree-shake Sentry logger statements to reduce bundle size
treeshake: {
removeDebugLogging: true,
},
// Automatic instrumentation of Vercel Cron Monitors
automaticVercelMonitors: true,
// Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// Side errors will fail.
// TunnelRoute: "/monitoring",

// Webpack-only options; ignored under the default Turbopack build (Next 16),
// So they only take effect with `next build --webpack`.
webpack: {
// Automatic instrumentation of Vercel Cron Monitors
automaticVercelMonitors: true,
// Tree-shake Sentry logger statements to reduce bundle size
treeshake: {
removeDebugLogging: true,
},
}),
)
},

// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
})
19 changes: 19 additions & 0 deletions oxfmt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defineConfig } from 'oxfmt'
import ultracite from 'ultracite/oxfmt'

export default defineConfig({
...ultracite,
ignorePatterns: [
...(ultracite.ignorePatterns ?? []),
'.agents/**',
'.claude/**',
'.cursor/**',
'.impeccable/**',
'supabase/.temp/**',
],
jsxSingleQuote: true,
printWidth: 100,
semi: false,
singleQuote: true,
trailingComma: 'all',
})
45 changes: 45 additions & 0 deletions oxlint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { defineConfig } from 'oxlint'
import antiSlop from 'ultracite/oxlint/anti-slop'
import core from 'ultracite/oxlint/core'
import { jsPluginSettings, selectJsPlugins } from 'ultracite/oxlint/js-plugins'
import next from 'ultracite/oxlint/next'
import nextJsPlugins from 'ultracite/oxlint/next/js-plugins'
import react from 'ultracite/oxlint/react'
import vitest from 'ultracite/oxlint/vitest'

// eslint-plugin-github currently pulls typescript-eslint, whose parser crashes
// under TypeScript 7. Keep SonarJS and React Doctor active until that stack is
// TS7-compatible again.
const jsPlugins = selectJsPlugins(['sonarjs', 'react-doctor'])

export default defineConfig({
extends: [core, react, next, vitest, antiSlop, jsPlugins, nextJsPlugins],
ignorePatterns: [
...(core.ignorePatterns ?? []),
'.agents/**',
'.claude/**',
'.cursor/**',
'.impeccable/**',
'supabase/.temp/**',
],
jsPlugins: jsPlugins.jsPlugins ?? [],
options: {
reportUnusedDisableDirectives: 'error',
respectEslintDisableDirectives: true,
typeAware: true,
typeCheck: true,
},
rules: {
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'typescript/no-explicit-any': 'error',
},
settings: jsPluginSettings,
})
50 changes: 50 additions & 0 deletions oxlint.deno.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { defineConfig } from 'oxlint'
import antiSlop from 'ultracite/oxlint/anti-slop'
import core from 'ultracite/oxlint/core'
import { jsPluginSettings, selectJsPlugins } from 'ultracite/oxlint/js-plugins'
import next from 'ultracite/oxlint/next'
import nextJsPlugins from 'ultracite/oxlint/next/js-plugins'
import react from 'ultracite/oxlint/react'
import vitest from 'ultracite/oxlint/vitest'

const jsPlugins = selectJsPlugins(['sonarjs', 'react-doctor'])

// Supabase Edge Functions run in Deno rather than the app's Node/tsgo runtime.
// Keep the same Ultracite policy but skip only incompatible type analysis.
export default defineConfig({
env: {
worker: true,
},
extends: [core, react, next, vitest, antiSlop, jsPlugins, nextJsPlugins],
globals: {
Deno: 'readonly',
},
ignorePatterns: [
...(core.ignorePatterns ?? []),
'.agents/**',
'.claude/**',
'.cursor/**',
'.impeccable/**',
'supabase/.temp/**',
],
jsPlugins: jsPlugins.jsPlugins ?? [],
options: {
reportUnusedDisableDirectives: 'error',
respectEslintDisableDirectives: true,
typeAware: false,
typeCheck: false,
},
rules: {
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'typescript/no-explicit-any': 'error',
},
settings: jsPluginSettings,
})
Loading
Loading