mosaicmap/CLAUDE.md
2025-08-19 12:17:11 +08:00

8.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Development Commands

  • Development server: npm run dev - Starts Next.js development server on http://localhost:3000
  • Build: npm run build - Creates production build
  • Production server: npm run start - Starts production server (requires build first)
  • Lint: npm run lint - Runs Next.js ESLint checks

Note: No test framework is currently configured in this project.

High-Level Architecture

This is a Next.js 15 application built with React 19 that creates an interactive map visualization with a custom WebGL timeline component.

Core Architecture Components

Map System:

  • Uses MapLibre GL JS for interactive mapping
  • app/map-context.tsx provides global map state management via React Context
  • components/map-component.tsx handles map rendering and initialization
  • Default map center: Singapore (103.851959, 1.290270) at zoom level 11
  • Custom map style from MapTiler API

Timeline Visualization:

  • app/timeline.tsx is a complex WebGL-powered timeline component using custom shaders
  • Uses vesica piscis (lens-shaped) geometry rendered via WebGL2
  • Custom GLSL shaders in app/glsl/timeline/ for vertex and fragment processing
  • Supports interactive features: dragging, zooming, panning, custom time markers
  • Dual-canvas architecture: WebGL canvas for vesica shapes, 2D canvas overlay for UI elements

UI Framework:

  • Tailwind CSS with Radix UI components
  • Theme system with dark/light mode support via components/theme-provider.tsx
  • shadcn/ui component library in components/ui/
  • Sidebar layout using components/ui/sidebar.tsx

Admin System:

  • Comprehensive admin interface in app/admin/ with dashboard, analytics, user management, and content editor
  • Admin pages include dynamic configuration system and navigation components
  • Uses both Apollo Client and GraphQL Request for data fetching

Data Layer:

  • GraphQL API integration using Apollo Client and GraphQL Request
  • Backend communication via /api/bff endpoint (configured in lib/gr-client.ts)
  • Page-based content management with block-based architecture
  • Authentication support with JWT tokens

Key Technical Details

WebGL Timeline:

  • Renders vesica piscis shapes as timeline markers
  • Supports high-DPI displays with proper pixel ratio handling
  • Interactive controls for zoom (mouse wheel), pan (drag), and custom time selection
  • Real-time shader uniform updates for responsive interactions

State Management:

  • Map state centralized in MapProvider context
  • Timeline uses internal React state with refs for performance-critical interactions
  • Custom hooks in hooks/ for map location, zoom, timeline, and mobile detection

Styling:

  • Tailwind CSS v4 with PostCSS
  • Custom animations via tw-animate-css
  • Component styling via class-variance-authority and clsx

Build Configuration:

  • Next.js 15 with App Router
  • Custom webpack config for GLSL file loading via raw-loader (see next.config.ts)
  • TypeScript with strict mode enabled
  • Absolute imports using @/* path mapping

GraphQL Integration:

  • Uses both Apollo Client (@apollo/client) and GraphQL Request (graphql-request) for different use cases
  • Page content fetching via lib/fetchers.ts with support for authentication
  • Block-based content architecture supporting TextBlock, ChartBlock, SettingsBlock, and HeroBlock types
  • Environment variables: GRAPHQL_BACKEND_URL and NEXTAUTH_URL

File Structure Notes

  • app/ - Next.js App Router pages and components
    • app/admin/ - Complete admin interface with dashboard, analytics, users, and content editor
    • app/glsl/timeline/ - Custom GLSL shaders for WebGL timeline rendering
  • components/ - Reusable React components
    • components/ui/ - shadcn/ui component library
  • hooks/ - Custom React hooks for map, timeline, and mobile detection
  • lib/ - Utility functions including GraphQL clients and data fetchers
  • types/ - TypeScript type definitions
  • public/ - Static assets

The application combines modern web mapping with custom WebGL visualization to create an interactive timeline-driven map interface, complemented by a full-featured admin system for content management.

These examples should be used as guidance when configuring Sentry functionality within a project.

Exception Catching

Use Sentry.captureException(error) to capture an exception and log the error in Sentry. Use this in try catch blocks or areas where exceptions are expected

Tracing Examples

Spans should be created for meaningful actions within an applications like button clicks, API calls, and function calls Use the Sentry.startSpan function to create a span Child spans can exist within a parent span

Custom Span instrumentation in component actions

The name and op properties should be meaninful for the activities in the call. Attach attributes based on relevant information and metrics from the request

function TestComponent() {
  const handleTestButtonClick = () => {
    // Create a transaction/span to measure performance
    Sentry.startSpan(
      {
        op: "ui.click",
        name: "Test Button Click",
      },
      (span) => {
        const value = "some config";
        const metric = "some metric";

        // Metrics can be added to the span
        span.setAttribute("config", value);
        span.setAttribute("metric", metric);

        doSomething();
      },
    );
  };

  return (
    <button type="button" onClick={handleTestButtonClick}>
      Test Sentry
    </button>
  );
}

Custom span instrumentation in API calls

The name and op properties should be meaninful for the activities in the call. Attach attributes based on relevant information and metrics from the request

async function fetchUserData(userId) {
  return Sentry.startSpan(
    {
      op: "http.client",
      name: `GET /api/users/${userId}`,
    },
    async () => {
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      return data;
    },
  );
}

Logs

Where logs are used, ensure Sentry is imported using import * as Sentry from "@sentry/nextjs" Enable logging in Sentry using Sentry.init({ _experiments: { enableLogs: true } }) Reference the logger using const { logger } = Sentry Sentry offers a consoleLoggingIntegration that can be used to log specific console error types automatically without instrumenting the individual logger calls

Configuration

In NextJS the client side Sentry initialization is in instrumentation-client.ts, the server initialization is in sentry.edge.config.ts and the edge initialization is in sentry.server.config.ts Initialization does not need to be repeated in other files, it only needs to happen the files mentioned above. You should use import * as Sentry from "@sentry/nextjs" to reference Sentry functionality

Baseline

import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: "https://109bcfcc2d1cdd643e0af61409016900@o4505647824109568.ingest.us.sentry.io/4509868655181824",

  _experiments: {
    enableLogs: true,
  },
});

Logger Integration

Sentry.init({
  dsn: "https://109bcfcc2d1cdd643e0af61409016900@o4505647824109568.ingest.us.sentry.io/4509868655181824",
  integrations: [
    // send console.log, console.warn, and console.error calls as logs to Sentry
    Sentry.consoleLoggingIntegration({ levels: ["log", "warn", "error"] }),
  ],
});

Logger Examples

logger.fmt is a template literal function that should be used to bring variables into the structured logs.

logger.trace("Starting database connection", { database: "users" });
logger.debug(logger.fmt`Cache miss for user: ${userId}`);
logger.info("Updated profile", { profileId: 345 });
logger.warn("Rate limit reached for endpoint", {
  endpoint: "/api/results/",
  isEnterprise: false,
});
logger.error("Failed to process payment", {
  orderId: "order_123",
  amount: 99.99,
});
logger.fatal("Database connection pool exhausted", {
  database: "users",
  activeConnections: 100,
});