Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Real-time [Polymarket](https://polymarket.com) prediction market visualization d

### Prerequisites

- Node.js 18+
- Node.js 20+
- npm

### Setup
Expand All @@ -41,9 +41,12 @@ cd PolyWorld
# Install dependencies
npm install

# Generate Fumadocs collections
npm run docs:gen

# Configure environment variables
cp .env.example .env
# Edit .env with your API keys
# Edit .env with your API keys / optional data source credentials

# Start dev server
npm run dev
Expand All @@ -59,8 +62,28 @@ Open [http://localhost:3000](http://localhost:3000).
| `AI_API_KEY` | Yes | Anthropic API key (for summaries, news matching, sentiment) |
| `AI_FALLBACK_BASE_URL` | No | Fallback API base URL |
| `AI_FALLBACK_API_KEY` | No | Fallback API key (used if primary fails) |
| `DATA_DIR` | No | Directory for runtime data and SQLite files |
| `DB_PATH` | No | SQLite database path |
| `POLYGON_RPC_URLS` | No | Comma-separated server-side Polygon RPC endpoints |
| `NEXT_PUBLIC_POLYGON_RPC_URL` | No | Browser-side Polygon RPC endpoint for wagmi |
| `UCDP_TOKEN` | No | Enables UCDP-backed conflict and military overlays |
| `TICKETMASTER_KEY` | No | Enables live sports overlay |
| `CLOUDFLARE_TOKEN` | No | Enables internet outages overlay |
| `ACLED_EMAIL` | No | ACLED OAuth email for protests/unrest overlay |
| `ACLED_PASSWORD` | No | ACLED OAuth password for protests/unrest overlay |

The app works without most optional keys. Missing keys only disable the related AI or overlay features.

### Local Verification

The app works without AI keys — summaries and sentiment will be disabled, but all market data, charts, and trading features remain functional.
After dependency install, regenerate docs collections before type-checking or building:

```bash
npm run docs:gen
npm run lint
npm run typecheck
npm test
```

## Project Structure

Expand All @@ -77,10 +100,14 @@ src/
## Scripts

```bash
npm run docs:gen # Generate Fumadocs collections
npm run dev # Development server
npm run build # Production build
npm run start # Production server
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test # Vitest unit tests
npm run test:e2e # Playwright end-to-end tests
```

## Acknowledgements
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
"dev": "WATCHPACK_POLLING=true next dev --webpack",
"build": "next build",
"start": "next start",
"docs:gen": "fumadocs-mdx",
"lint": "eslint",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest"
"test:watch": "vitest",
"test:e2e": "playwright test"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.78.0",
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/health/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function GET() {
}

return NextResponse.json({ status: "ok", timestamp: new Date().toISOString() });
} catch (err) {
} catch {
return NextResponse.json(
{ status: "error", reason: "internal" },
{ status: 503 }
Expand Down
2 changes: 2 additions & 0 deletions src/components/MapToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export default function MapToolbar({
activeLayers,
onToggleLayer,
}: MapToolbarProps) {
void onToggleFullscreen;
void isFullscreen;
const { t } = useI18n();
const [categoriesOpen, setCategoriesOpen] = useState(false);
const [layersOpen, setLayersOpen] = useState(false);
Expand Down
15 changes: 0 additions & 15 deletions src/components/NewsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,6 @@ interface NewsPanelProps {
onSourcesChange?: (sources: string[]) => void;
}

const SOURCE_ABBREVS: Record<string, string> = {
Reuters: "R",
"BBC World": "BBC",
"Al Jazeera": "AJ",
Bloomberg: "BL",
"AP News": "AP",
NPR: "NPR",
"France 24": "F24",
"DW News": "DW",
CNBC: "CNBC",
"The Guardian": "GU",
"NHK World": "NHK",
CNA: "CNA",
};

function timeAgo(dateStr: string): string {
const diff = Date.now() - new Date(dateStr).getTime();
const mins = Math.floor(diff / 60_000);
Expand Down
1 change: 0 additions & 1 deletion src/components/OrderBook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export default function OrderBookPanel({ selectedMarket }: OrderBookPanelProps)
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [bookSide, setBookSide] = useState<BookSide>("YES");
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const retryCount = useRef(0);
const scrollRef = useRef<HTMLDivElement>(null);
const midRef = useRef<HTMLDivElement>(null);
Expand Down
1 change: 0 additions & 1 deletion src/components/SignalPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ function SignalCard({
signal,
markets,
onSelectMarket,
onSelectWallet: _onSelectWallet,
onOpenTrade,
onTrade,
}: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/TweetsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useState, useEffect, useCallback, useMemo, useRef } from "react";
import type { ProcessedMarket, TweetItem } from "@/types";
import { TWEET_SOURCES, HANDLE_ABBREVS } from "@/lib/tweetSources";
import { TWEET_SOURCES } from "@/lib/tweetSources";
import { useVisibilityPolling } from "@/hooks/useVisibilityPolling";
import { useI18n } from "@/i18n";

Expand Down