Lorenzo SanseverinoThe Last Developer
IT EN
Portfolio

Frontend

Iodid

Not yet another stats site: League of Legends inside a virtual operating system, with a filesystem, terminal and windows.

STACK
Next.js, React, TypeScript
BACKEND
Beebot Java API
MODEL
Virtual operating system
MOBILE
App, no windows

Origins

Beebot data, inside an interface built for exploration.

Iodid has a history of its own: its first commit is dated 9 July 2026. It already starts with a virtual operating-system idea: desktop shell, dock, windows, terminal and an initial filesystem model. It is a separate interface over the same data domain, rather than a port of the Discord bot.

The design problem is more than displaying a statistics table. A profile must open from a URL, command or window without becoming three separate products. Virtual path, player identity and API request must keep the same meaning through every entry point.

The OS metaphor also has a practical boundary: on mobile, the windowed desktop is not simply shrunk. Navigation surfaces change while domain concepts, queries, data and content remain shared.

How it works

A filesystem instead of pages.

Profiles, champions and leaderboards are VFS resources. ls and cd navigate paths; profile opens a viewer inside the terminal. Public resources also have shareable URLs.

iodid / terminal
Scenario
  1. Shell
  2. VFS
  3. Beebot API
  4. Window
  1. iodid:~ $ cd /
  2. iodid:/ $ ls
  3. champions/ summoners/ match/ leaderboard/ tier-list/ records/ arena/ htop/ home/
  4. iodid:/ $ cd summoners/Chiaretta-0117
  5. iodid:/summoners/Chiaretta-0117 $ ls
  6. champions/ activity/ matchups/ lp-gains/ matches/ records/ livegame/
  1. Shell
  2. VFS
  3. Beebot API
  4. Window
  1. iodid:/summoners/Chiaretta-0117 $ profile
  2. → GET /api/lol/EUW1/profile-by-name/Chiaretta/0117
  3. ← 200 SummonerView 148 ms
  4. ▣ terminal viewer · Chiaretta#0117
  5. ↗ url /euw1/profile/Chiaretta-0117
  1. Shell
  2. VFS
  3. Beebot API
  4. Window
  1. iodid:/summoners/Chiaretta-0117 $ tree -L 1
  2. ├── champions/ ├── activity/ ├── matchups/
  3. ├── lp-gains/ ├── matches/ ├── records/
  4. └── livegame/
  5. iodid:/summoners/Chiaretta-0117 $ cd lp-gains
  6. → GET /api/lol/EUW1/profile/{puuid}/rank-history
  7. ← 200 Solo/Duo · Flex · Premade 96 ms
  8. ▣ terminal viewer · lp-gains

Every command resolves a VFS node; the data comes from the Beebot APIs.

Framework

An operating system on top of Next.js.

Every piece of content is a node in a unified virtual filesystem: profiles, champions and leaderboards open from windows, the terminal or indexable URLs. Page navigation stays separate from UI-only actions.

SHELL

Desktop with windows, terminal, persistent shortcuts and themes.

VFS

A single namespace with resource nodes, permissions and route intents.

PAGES

Next.js modules with SEO metadata, JSON-LD and sitemaps.

DATA

Beebot’s API, the system’s only backend.

Logic

Three entry points, one path to the data.

  1. A public route opens a context, not a second application

    A page such as /euw1/profile/Sunyx-0117 is resolved on the server and opens the correct shell context. Public rendering and the desktop reuse data from that resolution, including initial content and metadata before JavaScript interaction.

  2. The filesystem owns paths

    The VFS distinguishes semantic directories, resources and materialized files. Terminal commands, Hexplorer and window navigation use those nodes. A generated artifact must not replace a semantic directory or introduce another identity-resolution path.

  3. Commands express intent

    A command interprets what the user wants and delegates to the domain owners. It does not build API URLs or call Axios. LeagueApiEndpoint owns HTTP paths, LeagueApiClient transport, LeagueApi validation and mapping, and LeagueApiQuery cache keys and policies.

  4. Public HTML, prepared data and explicit states

    Catalogs and selected public pages are prepared as daily generations and reusable HTML files. Dynamic profiles and unprepared variants retain the SSR flow. Pending, empty and unavailable remain distinct states: a temporary API failure does not automatically become a 404.

API & examples

From the browser to the terminal and backend.

Paths illustrate the project’s contracts. The terminal operates on a virtual browser filesystem, not on the visitor’s computer filesystem.

Example 01 / text

A shareable URL must contain a real page

Region remains part of the context. The public URL uses a readable Riot ID; after resolution, requests for the same identity use PUUID. Content must exist in HTML before someone manually opens a window.

Public URL
/euw1/profile/Sunyx-0117

SSR resolution → shared queries → HTML + metadata
Hydration → the same shell context

Example 02 / shell

Explore the system without duplicating pages

help and pwd expose terminal context, ls explores the namespace and windows lists open windows. Theme changes go through the shared runtime while preserving product content.

help
pwd
ls /
windows
theme dracula

Example 03 / text

Separate resource paths from transport

This is an architecture flow, not a new API introduced by the case study. Profile-by-name and match-list endpoints belong to Beebot; Iodid validates their responses and maps them to immutable domain models before rendering.

Command / URL / window
  → VFS context and navigation intent
  → LeagueApiQuery
  → LeagueApi + LeagueApiEndpoint
  → LeagueApiClient
  → Beebot API
  → domain models → shared view

Example 04 / text

From a champion page to an API request

Build resolves a canonical role from champion data. An Ahri MID view requests the backend using champion name, queue and role codes. Patch, rank and region are additional filters; omitting region requests the all-regions aggregate.

Public page
/champions/ahri/build

Beebot API, example with MID role
GET /api/lol/champion/Ahri?queue=RANKED_SOLO_5X5&role=MID

Timeline

From an initial shell to a system with explicit boundaries.

This timeline follows Iodid’s repository history. League data comes from Beebot, but the two Git histories remain separate.

  1. The first shell

    The first commit already includes desktop, dock, window manager, terminal and virtual-path resolvers. The OS metaphor is the starting point, rather than decoration added at the end.

  2. Connecting the API

    API work connects the shell to backend data. Identity, mapping and request state must stay consistent across the different entry points.

  3. Leaderboards and initial indexable paths

    Leaderboard and sitemap work appears. Operating-system navigation must coexist with understandable URLs and content that search engines can discover.

  4. Prerendering and SEO

    Commits introduce work on static sitemaps and prerendered pages. Titles, champion paths and a first mobile prototype follow over the next days.

  5. The design system becomes a contract

    A migration sequence separates tokens, themes, shell, window chrome and material recipes. Validators and browser tools accompany the work: a theme is a shared capability, not a copied conditional in every component.

  6. A new VFS foundation

    The migration unifies materialized directories, artifacts, commands and autocomplete. Initial resolvers give way to a model that owns paths and their consumers.

  7. Hexplorer and real navigation links

    Navigation-intent work preserves hrefs and adds Hexplorer for virtual directories. Opening a resource must preserve shareable links and native new-tab behavior.

  8. One SSR resolution for HTML and metadata

    The SEO refactor revisits the public representation of resources. Content, canonical URLs, data states and hydration must come from the same resolved route.

  9. Daily caches and prepared HTML

    Isolated snapshots, atomic publication and off-request HTML preparation reduce repeated work. The same period fixes timers retaining SSR QueryClients after requests complete.

  10. The visual experience keeps evolving

    The latest inspected commits continue the redesign after mobile and homepage work. Shared data and navigation contracts let those surfaces evolve without rewriting the backend.

Current state

An operating system as an interaction model.

Iodid does not run an operating system on the visitor’s computer. Windows, commands and a virtual filesystem provide a coherent model for exploring League of Legends statistics. The important part is the resource remaining consistent across those interfaces.

Public content coexists with a rich UI. SSR, HTML links and indexing policies make pages readable before hydration. Prepared snapshots do not make the entire product static: matches, profiles and unprepared requests keep their own flows.

Design choices

The choices shaping the product.

VFS first, routes later

Filesystem stability comes before any routing migration, with the terminal and window manager always working.

CostBroad migrations wait until the VFS holds.

One owner for every piece of logic

Mappers, resolvers, parsers and caches have a single origin. No second API path.

CostYou have to find the right place before writing code.

Mobile as an app, not a port

No windows or terminal: each page is an app with an icon bar and shared components.

CostTwo experiences to design, over the same data.

Scope

Included

  • Profiles and matches
  • Champions, builds and matchups
  • Leaderboards and tier lists
  • Terminal, windows and themes
  • Indexable pages

In progress

  • VFS consolidation
  • Mobile experience refinement

Out of scope

  • Data collection and processing: they live in Beebot