simpleEDEP
Language

Developer guide

How to continue building simple-edep — with or without an AI coding tool. The second half of this guide focuses on working with Claude Code specifically, with concrete examples drawn from this project's own development history.

Source: pleuston/simple-edep (app) · pleuston/simple-edep-edh (data)

1 · Architecture

simple-edep is a no-build-step static site. There is no bundler, no transpiler, no framework — plain HTML, CSS, and ES5 JavaScript served directly by GitHub Pages. The only exception is the jinn-codemirror web component, which is loaded as a native ES module from a vendored bundle.

Two-repo split

The corpus data (82 k EpiDoc records, index, people, photos, bibliography, geodata) lives in a separate data repo (simple-edep-edh) and is served by jsDelivr, a free CDN (Content Delivery Network — a global network of servers that cache and distribute files faster than a single origin server). jsDelivr mirrors public GitHub repos automatically; the app fetches data from cdn.jsdelivr.net/gh/pleuston/simple-edep-edh@main/… rather than from GitHub directly. This keeps the app repo lean (no large binary blobs, fast Pages builds) while jsDelivr provides CORS headers, gzip compression, and global caching.

RepoWhat it containsHow it is served
simple-edepHTML, CSS, JS, vendor libraries, build scriptsGitHub Pages (direct)
simple-edep-edhEpiDoc XML records, JSON indexes, people, photos, geojsDelivr CDN (CORS, gzip)

jsDelivr cache TTL: the @main tag has a roughly 12-hour CDN cache. After pushing to the data repo, changes may not be visible on the live site for up to 12 hours. To bypass the cache during development, use a commit SHA in the URL, e.g. @a1b2c3d. During local development, point collections.js directly at the data repo's local path instead of jsDelivr.

2 · Key files

FileRole
styles.cssAll styles. Single flat file, no preprocessor. EDEP typographic theme (two serif variables + accent red).
auth.jsGitHub session. Checks localStorage for a stored PAT and username. Loaded first on every page via <script src="auth.js"> (synchronous, before DOMContentLoaded). Exposes EpiAuth.isSignedIn(), EpiAuth.getUser(), EpiAuth.signOut().
collections.jsCollection registry. Defines EDH and I.Sicily base URLs, URL resolvers (EpiCollections.recordUrl()), the merged catalog loader (EpiCollections.loadCatalog()), and EpiCollections.getJSON() for cached fetches.
catalog.jsInscriptions register: load index, filter/sort/paginate, row HTML, open-in-editor logic, XML→form parser (parseRecord()).
people.jsPersons register: load people.json, filter by name components (praenomen, nomen, cognomen…), sex, social state, occupation.
map.jsGeodata: 3-column layout (filter sidebar, entry list, Leaflet map). Filters sync between list and map markers.
app.jsEditor form. A SECTIONS array drives the entire metadata form via render() / fieldControl(). To add a metadata field, add an entry to the array.
generator.jsEpiDoc/TEI serializer. EpiDocGen.buildEpiDoc(state) turns the form state object into well-formed XML.
reading.jsEpiDoc→HTML renderer. Converts the EpiDoc XML to a reading view with critical apparatus, translation, find-spot, and bibliography.
panel.js / recordview.jsRight-side reading panel used by the Inscriptions and People registers. RecordPanel.open(id, col) fetches and displays a record without leaving the page.
favorites.jslocalStorage-backed starred inscriptions and named collections. EpiFav.button() renders the ★ button inline in register rows.
scripts/import-edh.jsNode build script. Reads EDH CSV/JSON exports from ~/Downloads/edh/ and writes slim JSON to the data repo. Run once when source data changes.
scripts/build-index.jsNode build script. Scans records/*.xml and writes collections/edh/index-*.json. Run after adding or editing EpiDoc records.

3 · Running locally

Because the site uses fetch() calls that require an HTTP context (not file://), run a local server from the app repo root:

cd ~/repos/simple-edep
python3 -m http.server 8765
# open http://localhost:8765

By default, collections.js reads the data from jsDelivr. To test data changes before pushing, temporarily swap the base URL:

// in collections.js, change:
base: "https://github.com/pleuston/simple-edep-edh"
// to:
base: "http://localhost:8766"  // second server on the data repo
cd ~/repos/simple-edep-edh
python3 -m http.server 8766

Build scripts

# regenerate all JSON registers from the EDH CSVs in ~/Downloads/edh/
node scripts/import-edh.js

# regenerate the inscription index from records/*.xml
node scripts/build-index.js

Both scripts write into ../simple-edep-edh/ (the sibling data repo directory). Commit and push the data repo after running them.

4 · Working with Claude Code

Claude Code is Anthropic's agentic coding tool. It can read files, run shell commands, edit code, and commit to git — all from a conversation. You describe what you want; it investigates, proposes, and implements.

Setup

  1. Install the Claude desktop app or the CLI: npm i -g @anthropic/claude-code
  2. Open a terminal in the app repo root and run claude.
  3. Claude reads the directory structure and git history automatically. No project configuration file is required, though a CLAUDE.md at the repo root can pre-load important constraints (like "no bundler, no build step").

Memory

Claude Code maintains a persistent memory in ~/.claude/projects/<repo-path>/memory/. Across sessions it remembers project facts, constraints, and your preferences. You can ask it to "remember" anything important, or it records things automatically when you correct its behaviour.

What it can do in this project

5 · Prompting effectively

Describe what you see, not what to code

You do not need to know the implementation. Describe the problem or the desired outcome in plain language and let Claude read the code to figure out how to achieve it.

Less effectiveMore effective
"add filter dropdowns to people.html and update people.js to filter by nomen and cognomen" "the people section is still very hard to navigate and understand. allow to filter and search like on the EDEP site form (screenshot attached) and make it much more compact"
"change the CSS grid to three columns" "geodata also needs a dedicated search and filter form left to the map, then entries list, then map"
"gate the edit button on EpiAuth.isSignedIn()" "each entry and person needs to be editable after login"

Attach screenshots

Claude can read images. A screenshot of another site you want to emulate (like the original EDEP person form) is worth a paragraph of prose. Drop it into the conversation alongside your message. Claude will inspect the UI, infer the fields and layout, and implement a matching version.

One change at a time

Multi-part requests ("redesign the layout AND add filters AND change the nav AND push") work, but each part is done sequentially. For complex tasks, it is easier to verify each step before moving on. In practice, short telegraphic follow-ups work well:

Let it read before it acts

Claude reads the relevant files first before writing anything. If you interrupt a reading step, it may propose a change without the full context. Let it finish the investigation before saying "go ahead".

6 · Worked examples from this project

Example A — People register: name-component filters

Prompt: "for the persons also filter for the values of Deity, Praenomen, Nomen Gentile, Cognomen, Supernomen, Tribus, Origo, Sex — like in P000001 some are empty" (with an EDEP screenshot attached)

What Claude did:

  1. Read people.js and found the current data only stored the combined name string — no individual components.
  2. Read scripts/import-edh.js to see how the CSV is parsed.
  3. Checked the CSV headers (praenomen, nomen, cognomen, supernomen, tribus, origo columns exist).
  4. Updated the import script to write those six fields to each JSON record.
  5. Re-ran node scripts/import-edh.js — regenerated 93,643 records (18 MB).
  6. Committed and pushed the data repo.
  7. Updated people.html to add nine filter inputs/selects.
  8. Rewrote people.js to wire each field, filter, and display the structured name components in rows.
  9. Committed and pushed the app repo.

Total clock time: a few minutes. Without an AI tool, steps 1–9 would each require opening files, understanding the data model, writing code, and testing — easily an hour or more.

Example B — Geodata: 3-column layout

Prompt: "geodata also needs a dedicated search and filter form left to the map then entries list then map"

What Claude did:

  1. Read map.html (single <div id="map">, no filters) and map.js (markers loaded once, no interaction with a list).
  2. Checked the catalog and people pages for the existing filter sidebar CSS pattern (.with-facets, .facet-side).
  3. Rewrote map.html: three-column grid — filter sidebar, scrollable list, map panel.
  4. Rewrote map.js: filter state, filter predicate, paginated list render, cluster marker sync (filtering hides markers for non-matching results), bidirectional link (clicking a list row pans the map; clicking a marker scrolls the list).
  5. Added CSS for the .map-layout grid and responsive collapse.
  6. Took a browser screenshot to verify the layout before committing.

Example C — Secondary nav restructure

Prompt: "Favorites / Log / About / Instructions [shall go to the right]. favorites only an icon. instructions shall become Guide"

What Claude did:

  1. Listed all HTML pages (12 files).
  2. Used a single perl one-liner to remove the four links from every primary nav and inject the new secondary <nav class="sitenav-right"> block into every .topbar-right — one command, all pages.
  3. Added CSS for .sitenav-right and the icon-only Favorites button.
  4. Delegated active-state marking to auth.js, which already had the page variable derived from pathname.split("/").pop().

Lesson: when the same change needs to be applied to many files, Claude will reach for sed/perl/awk rather than editing each file manually. Tell it the number of pages if it is not obvious from a directory listing.

7 · Common development recipes

Add a filter to a register page (People / Inscriptions / Geodata)

  1. Check whether the data field already exists in the JSON (read a sample record in the browser console: EpiCollections.getJSON(…).then(d => console.log(d[0]))).
  2. If not: add the field to scripts/import-edh.js, run node scripts/import-edh.js, push the data repo.
  3. Add an <input> or <select> to the facet sidebar in the HTML.
  4. In the JS: grab the element, add an event listener that calls render(), extend the matches() function.
  5. For a select: populate it after data loads by collecting unique values with forEach and appending <option> elements.

Add a metadata field to the editor

  1. Add an entry to the SECTIONS array in app.js. Each entry has a key (state property name), a label, and a type (text, select, textarea, datalist, row, etc.).
  2. Add the corresponding serialization in generator.js — find where the parent element is built and add the new child element.
  3. Add the corresponding deserialization in catalog.js's parseRecord() function, so records round-trip correctly when opened for editing.

Update the corpus data

# 1. Place updated CSVs / zips in ~/Downloads/edh/
# 2. Regenerate JSON registers
node scripts/import-edh.js

# 3. Regenerate inscription index (if EpiDoc XML changed)
node scripts/build-index.js

# 4. Commit and push data repo
cd ../simple-edep-edh
git add collections/
git commit -m "Refresh EDH data"
git push

Test a data change without waiting for jsDelivr

# Terminal A — data repo
cd ~/repos/simple-edep-edh
python3 -m http.server 8766

# Terminal B — app repo
cd ~/repos/simple-edep
# Temporarily edit collections.js: change the base URL to http://localhost:8766
python3 -m http.server 8765
# Open http://localhost:8765 in the browser
# Remember to revert collections.js before pushing!

8 · Constraints to keep in mind

9 · Tips from experience

Give Claude the "why", not just the "what"

This codebase's constraints (no bundler, two-repo split, jsDelivr cache) are not obvious from the code alone. State them explicitly in your first message of a session, or keep them in the project memory. Claude will otherwise sometimes propose solutions that introduce a build step or rely on Node at runtime.

Use browser screenshots to verify UI changes

The Claude desktop app has a preview panel that can take screenshots of a running local server. After a UI change, ask Claude to "take a screenshot and check it" before committing. This catches layout bugs (collapsed maps, hidden elements, wrong column widths) without leaving the conversation.

Ask for a plan before complex changes

For multi-file changes — like adding a new register page or restructuring the editor form — ask Claude to "outline what files you'll change and why" before it starts editing. This surfaces misunderstandings early and lets you redirect without undoing committed work.

Commit frequently, push when stable

Claude commits after each logical unit of work. You can push manually ("push when ready") or say "push" at the end of a session. The git log becomes a readable history of each feature step.

The memory system is your handoff document

Claude stores project facts, your preferences, and non-obvious constraints in ~/.claude/projects/…/memory/. Before ending a session, ask it to "save anything new you learned about the project." The next session will start with that context already loaded.

Check jsDelivr cache after data pushes

After pushing to the data repo, the live site may show stale data for up to 12 hours. To force a refresh during development, append ?v=1 to fetch calls (jsDelivr ignores it but the browser treats it as a new URL). For a permanent fix, use a specific commit SHA in the CDN URL instead of @main.

This guide was written with Claude Code and reflects the actual development history of this site. See the commit log for a step-by-step record.