This application provides a modern, offline-first experience for viewing and analyzing chess games stored in the Portable Game Notation (PGN) format. It’s built as a Progressive Web Application (PWA), leveraging a robust template that includes features like internationalization, common UI components based on Bootstrap 5, and seamless cloud synchronization via Dropbox.
A live version of the app is available here, and the full code on GitHub here.
chess.js.
// Example: Loading PGN data (from game-logic.js)
import { Chess } from './lib/chess-1.2.0.min.js';
// ...
export function loadPgn(pgnString) {
  // ...
  try {
    chess = new Chess();
    chess.loadPgn(pgnString);
    initialFen = chess.getHeaders().FEN || new Chess().fen();
    moveHistory = chess.history({ verbose: true }) || [];
    currentMoveIndex = -1;
    return true;
  } catch (error) {
    console.error(`Error loading PGN: ${error}`);
    initializeGame(); // Reset to default state on error
    return false;
  }
}
// Example: Rendering the board (from board-ui.js)
export function renderBoard(boardContainer, fen) {
  // ... clear previous board ...
  // ... parse FEN piece placement ...
  rankIteration.forEach((fenRank, rankIndex) => {
    // ... iterate through rank characters ...
    if (isNaN(parseInt(char, 10))) { // It's a piece
      const algebraic = indicesToAlgebraic(rankIndex, currentFileIndex);
      const square = createSquareElement(rankIndex, currentFileIndex, algebraic);
      const pieceData = pieceMap[char];
      if (pieceData) {
        const pieceImage = document.createElement('img');
        pieceImage.src = `${getCurrentPieceImagePath()}${pieceData}`;
        // ... add piece to square ...
      }
      boardGrid.appendChild(square);
      currentFileIndex++;
    } else { // It's empty squares
      // ... create empty squares ...
    }
  });
  boardContainer.appendChild(boardGrid);
}
* Supports standard piece sets and allows cycling through different visual styles.
* Flip the board orientation (White/Black at the bottom) with a single click.
// Example: Navigating moves (from game-logic.js)
export function goToNextMove() {
  if (currentMoveIndex >= moveHistory.length - 1) return null;
  return goToMoveIndex(currentMoveIndex + 1);
}
export function goToMoveIndex(index) {
  // ... reset board to initial FEN ...
  for (let i = 0; i <= index; i++) {
    if (!chess.move(moveHistory[i].san)) return null; // Apply moves up to index
  }
  currentMoveIndex = index;
  return chess.fen(); // Return FEN of the target position
}Built upon a flexible PWA template, this viewer offers:
// Example: Service Worker registration (from index.html)
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js', { scope: '/' })
      .catch(error => console.error('Service Worker registration failed:', error));
}
// Example: Initializing Dropbox Sync (from dropbox-sync.js)
async function initializeDropboxSync() {
  logVerbose('Initializing Dropbox Sync System...');
  initializeSyncCoordinator(); // Handles sync logic
  initializeOfflineHandling(); // Manages online/offline state changes
  await initializeAuthentication(); // Handles login and token management
  // ...
}This PGN viewer combines standard chess game viewing functionalities with the convenience of a modern web application, accessible anywhere, anytime, even offline, with optional cloud backup and sync. The source code is publicly available on GitHub for those interested in the technical details.