diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 13fc307..7b90652 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,8 +1,16 @@ -name: Deploy to GitHub Pages +name: Validate and deploy -# Publishes a copy of the deck with speaker notes stripped (see -# tools/strip-notes.py), so the live site never exposes the S speaker view -# notes. The repo keeps the notes; only the deployed artifact is stripped. +# Validation runs on every pull request and push to main; deployment happens +# only from main, and only after validation succeeds. +# +# build publishes an ALLOWLISTED, notes-free copy of the site (see +# tools/strip-notes.py) plus generated extras: a slides.pdf, a social-card.png +# and an offline.zip per deck. PDFs/cards are cached and regenerated only when +# the deck or the shared engine changed (content-hash check inside +# tools/export-pdf.mjs). +# +# All actions used here are first-party (actions/*), referenced by major +# version tag. If supply-chain policy tightens, pin each to a full commit SHA. # # One-time setup: repo Settings → Pages → Build and deployment → # Source = "GitHub Actions" (instead of "Deploy from a branch"). @@ -10,6 +18,7 @@ name: Deploy to GitHub Pages on: push: branches: [main] + pull_request: workflow_dispatch: permissions: @@ -19,24 +28,158 @@ permissions: # Allow one concurrent deployment; let an in-progress run finish. concurrency: - group: pages + group: pages-${{ github.ref }} cancel-in-progress: false jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v7 + + - name: Static checks (audit, strip-notes tests, landing-page sync) + run: | + python3 tools/test_strip_notes.py + python3 tools/build-index.py --check + python3 tools/audit.py --strict + python3 tools/strip-notes.py /tmp/site-check + python3 tools/audit.py --site /tmp/site-check --strict + + - uses: actions/setup-node@v6 + with: + node-version: 22 + + - name: Install Playwright + run: | + npm install --no-save playwright + npx playwright install --with-deps chromium + + - name: Browser checks (1280×720, 844×390, 390×844) + run: node tools/browser-check.mjs + + # Lightweight visual regression: only when the shared engine changed on a + # PR — capture the component catalogue from the merge-base and from the + # head, compare with a loose tolerance, and upload diff images as artifacts + # on failure. Skipped when the catalogue deck itself changed in the same + # PR: the baseline then shows different *content*, so a pixel comparison + # can only measure the intended edit, not engine drift. + visual: + runs-on: ubuntu-latest + timeout-minutes: 25 + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Check whether the shared engine changed (and the catalogue did not) + id: engine + run: | + BASE=$(git merge-base origin/${{ github.base_ref }} HEAD) + CHANGED=$(git diff --name-only "$BASE" HEAD) + engine=false; catalogue=false + echo "$CHANGED" | grep -qE '^shared/(theme\.css|deck\.js|reveal/)' && engine=true + echo "$CHANGED" | grep -qE '^talks/(_showcase|_template)/' && catalogue=true + if [ "$engine" = true ] && [ "$catalogue" = false ]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + else + echo "changed=false" >> "$GITHUB_OUTPUT" + [ "$engine" = true ] && echo "::notice::visual regression skipped — the catalogue deck changed in this PR, so the merge-base screenshots show different content" + fi + echo "base=$BASE" >> "$GITHUB_OUTPUT" + + - uses: actions/setup-node@v6 + if: steps.engine.outputs.changed == 'true' + with: + node-version: 22 + + - name: Install Playwright + if: steps.engine.outputs.changed == 'true' + run: | + npm install --no-save playwright pixelmatch pngjs + npx playwright install --with-deps chromium + + - name: Capture merge-base screenshots + if: steps.engine.outputs.changed == 'true' + run: | + git worktree add /tmp/base-tree ${{ steps.engine.outputs.base }} + node tools/browser-check.mjs --root /tmp/base-tree --decks _template --screenshots /tmp/shots-base || true + + - name: Capture head screenshots + if: steps.engine.outputs.changed == 'true' + run: node tools/browser-check.mjs --decks _template --screenshots /tmp/shots-head + + - name: Compare + if: steps.engine.outputs.changed == 'true' + run: node tools/visual-diff.mjs --before /tmp/shots-base --after /tmp/shots-head --out /tmp/visual-diff + + - name: Upload diff images + if: failure() + uses: actions/upload-artifact@v6 + with: + name: visual-diff + path: | + /tmp/visual-diff + /tmp/shots-base + /tmp/shots-head + if-no-files-found: ignore + build: + needs: validate runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v7 - uses: actions/configure-pages@v6 - - name: Strip speaker notes into ./_site - run: python3 tools/strip-notes.py _site + + - name: Build the allowlisted, notes-free site + run: | + python3 tools/strip-notes.py _site + python3 tools/audit.py --site _site + + - uses: actions/setup-node@v6 + with: + node-version: 22 + + - name: Install Playwright + run: | + npm install --no-save playwright + npx playwright install --with-deps chromium + + # PDFs + social cards are expensive: restore the last run's outputs and + # let export-pdf's content-hash check skip every unchanged deck. + - name: Restore PDF/social-card cache + uses: actions/cache@v5 + with: + path: .extras-cache + key: deck-extras-${{ hashFiles('talks/**', 'shared/**') }} + restore-keys: deck-extras- + + - name: Export PDFs and social cards + run: | + if [ -d .extras-cache ]; then cp -r .extras-cache/talks/. _site/talks/ 2>/dev/null || true; fi + node tools/export-pdf.mjs --root _site + mkdir -p .extras-cache + rm -rf .extras-cache/talks + (cd _site && find talks -maxdepth 2 \ + \( -name slides.pdf -o -name social-card.png -o -name .extras-hash \) \ + -exec install -D {} ../.extras-cache/{} \;) + + - name: Build offline bundles + run: | + python3 tools/build-offline.py --root _site + find _site -name .extras-hash -delete + - uses: actions/upload-pages-artifact@v5 with: path: _site deploy: needs: build + if: github.event_name != 'pull_request' runs-on: ubuntu-latest + timeout-minutes: 10 environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/.gitignore b/.gitignore index e59879d..1f02634 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,12 @@ __pycache__/ # Claude Code (local preview dev-server config, not shared) .claude/launch.json + +# Local build previews + CI-installed browser-check dependencies +_site/ +node_modules +package.json +package-lock.json + +# CI cache for generated PDFs/social cards +.extras-cache/ diff --git a/404.html b/404.html new file mode 100644 index 0000000..a037767 --- /dev/null +++ b/404.html @@ -0,0 +1,57 @@ + + + + + + Page not found — Slides · Frédérick Madore + + + + + + +
+

Slides · Frédérick Madore

+

Not found

+
+

This page doesn't exist — the talk may have moved or the address has a typo.

+ Back to all talks +
+ + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..7e2f2a8 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,52 @@ +# Licence + +This repository mixes code, scholarly content and third-party material, each +with its own status. + +## Code — MIT + +The original code in this repository — the shared engine (`shared/deck.js`, +`shared/theme.css`), the landing page, the build and validation tooling under +`tools/`, and `serve-deck.py` — is released under the MIT License: + +> Copyright (c) 2026 Frédérick Madore +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + +## Slide text and original figures — CC BY 4.0 + +The scholarly content of the presentations under `talks/` — slide text, +speaker notes and figures authored by the presenters — is licensed under +[Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/). +Please attribute **Frédérick Madore** (and the named co-presenter of the talk, +where applicable) with a link to . + +## What these licences do NOT cover + +- **Vendored software** (reveal.js, highlight.js) and the **fonts** + (EB Garamond, Libre Franklin) keep their own licences — see + [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md). +- **Institutional logos** (Africa Multiple Cluster of Excellence, University + of Bayreuth, King's College London, ORCID, GitHub, LinkedIn, MCP) remain the + marks of their owners and are used for identification only. +- **Externally sourced images** in `talks/*/assets/` and `shared/assets/` + (digitised newspaper pages, archival photographs, screenshots of external + websites and reports) remain under their original rights; their sources are + credited on the slides where they appear. They are reproduced here for + scholarly presentation and are not covered by the CC BY grant above. diff --git a/README.md b/README.md index b73e0ee..b16ab44 100644 --- a/README.md +++ b/README.md @@ -44,21 +44,35 @@ iteration this theme grew out of — live in [`.impeccable.md`](.impeccable.md). ``` slides/ -├── index.html ← landing page (lists talks — edit the TALKS array) +├── index.html ← landing page (GENERATED from talks/talks.json) ├── shared/ ← the reusable ENGINE, one copy shared by every talk │ ├── theme.css deck.js the “Broadsheet” theme + nav / chrome / TOC script -│ ├── logo-*.{png,webp,svg} Africa Multiple, Bayreuth, KCL marks +│ ├── highlight.min.js slim vendored highlight.js (see vendor-manifest.json) +│ ├── logo-*.{png,svg} Africa Multiple, Bayreuth, KCL marks +│ ├── assets/ images used by more than one talk │ ├── fonts/ self-hosted EB Garamond + Libre Franklin (offline) │ └── reveal/ vendored reveal.js v6 + plugins (offline) ├── talks/ -│ ├── _template/ ← copy this to start a new talk +│ ├── talks.json ← the TALK MANIFEST (one record per published talk) +│ ├── _template/ minimal starter (tools/new-talk.py copies this) +│ ├── _showcase/ the full layout catalogue (never published) │ └── YYYY-MM-DD-place-title/ │ ├── index.html (slides + DECK_CONFIG; points to ../../shared) │ └── assets/ (this talk's images / embedded files) -├── tools/ strip-notes.py · fetch-highlight.py (build helpers) +├── tools/ +│ ├── new-talk.py scaffold a talk (slug, metadata, manifest, QR) +│ ├── build-index.py render the landing page + sitemap from the manifest +│ ├── audit.py one-command repository audit (static checks) +│ ├── browser-check.mjs Playwright checks at 3 viewport sizes +│ ├── visual-diff.mjs tolerant screenshot comparison (CI) +│ ├── export-pdf.mjs per-deck slides.pdf + social-card.png (CI) +│ ├── build-offline.py per-deck offline.zip bundles (CI) +│ ├── strip-notes.py allowlisted, notes-free publication build (+ tests) +│ └── fetch-highlight.py regenerate the slim highlight.js bundle ├── serve-deck.py ← no-cache dev server (serves the whole repo) -├── .github/workflows/ pages.yml — deploy to GitHub Pages (notes stripped) -└── .nojekyll CNAME .impeccable.md README.md +├── .github/workflows/ pages.yml — validate, build and deploy +└── .nojekyll CNAME 404.html robots.txt sitemap.xml LICENSE.md + THIRD_PARTY_NOTICES.md .impeccable.md README.md ``` Each talk references the one shared engine via `../../shared/…`, so a fix to `theme.css` @@ -70,15 +84,25 @@ redirects to `slides.frederickmadore.com`. ## Add a new talk -1. **Copy the starter:** `talks/_template` → `talks/YYYY-MM-DD-place-short-title`. -2. Edit the **`DECK_CONFIG`** block (presenter, title, venue, links) and the slides. -3. Add one entry to the **`TALKS`** array in [`index.html`](index.html) (newest first): +One command scaffolds everything: -```js -{ date: "2026-09-01", event: "Conference · City", title: "My talk", - desc: "One-line description.", slug: "2026-09-01-city-my-talk" } +```bash +python3 tools/new-talk.py \ + --date 2026-09-01 --place bayreuth \ + --title "My talk" --venue "Conference · Bayreuth · 1 Sept 2026" \ + --event "Conference · Bayreuth" --desc "One-line description." --lang en ``` +It copies `talks/_template`, fills the metadata (DECK_CONFIG, cover, canonical +URL + social metadata), registers the talk in [`talks/talks.json`](talks/talks.json), +regenerates the landing page and writes a QR code pointing at the final URL +(needs `pip install "qrcode[pil]"` once — everything else works without it). +Then edit the slides; the full layout catalogue with copy-paste examples lives +in `talks/_showcase/` (preview at `/talks/_showcase/`). + +Doing it by hand instead: copy `talks/_template`, edit `DECK_CONFIG` + slides, +add an entry to `talks/talks.json` and run `python3 tools/build-index.py`. + --- ## Preview locally @@ -89,8 +113,9 @@ Decks must be served over HTTP (not `file://`). From the repo root: python serve-deck.py # no-cache server → http://localhost:8742 ``` -Open `http://localhost:8742/` for the landing page, or a talk directly at -`/talks//`. The no-cache server guarantees reloads always show your latest edits. +Open `http://localhost:8742/` for the landing page (rendered statically from +`talks/talks.json`, with client-side search and language/year/topic filters +whose state lives in the URL), or a talk directly at `/talks//`. The no-cache server guarantees reloads always show your latest edits. (For just viewing, any static server works, e.g. `python -m http.server`.) --- @@ -134,8 +159,10 @@ control and the markup carries no stray form elements. See `talks/_template/inde for a worked example of each layout. **Scrollable file embed** (e.g. a GitHub skill): a `
` loads and syntax-highlights a vendored file you can -scroll on stage. GitHub pages can't be ` - +
@@ -253,7 +248,7 @@

A network of five centres

-
+

Digital Research Environment (DRE) — the cluster's digital infrastructure unit

We build the cluster's data infrastructure

Four areas of work — one goal: research data that is findable, reusable and presentable.

@@ -314,11 +309,11 @@

We began with WissKI

- WissKI@UBT — searching and exploring the cluster's research data through a web interface. + WissKI@UBT — searching and exploring the cluster's research data through a web interface.
Searching and exploring research data across the centres.
- A WissKI knowledge graph — entities connected to each other and to shared ontologies through typed relations. + A WissKI knowledge graph — entities connected to each other and to shared ontologies through typed relations.
Records as a knowledge graph: entities linked by typed relations.
@@ -350,7 +345,7 @@

Enter AMIRA

- QR code linking to the AMIRA website + QR code linking to the AMIRA website Scan to explore AMIRAdata.africamultiple.uni-bayreuth.de
@@ -443,7 +438,7 @@

Connections, not records

-
+

Custom Omeka S module · built in-house

Spatial exploration

One of the DRE visualisation modules I build — every georeferenced place in the collection, on one map you can pan, zoom and open.

@@ -577,7 +572,6 @@

Who has the authority to decide how knowledge is described, and therefore ho - diff --git a/talks/_showcase/assets/.gitkeep b/talks/_showcase/assets/.gitkeep new file mode 100644 index 0000000..72b78ac --- /dev/null +++ b/talks/_showcase/assets/.gitkeep @@ -0,0 +1 @@ +# Talk-specific assets (images, embedded files) go here. diff --git a/talks/_template/assets/example-figure.png b/talks/_showcase/assets/example-figure.png similarity index 100% rename from talks/_template/assets/example-figure.png rename to talks/_showcase/assets/example-figure.png diff --git a/talks/_template/assets/example-media.png b/talks/_showcase/assets/example-media.png similarity index 100% rename from talks/_template/assets/example-media.png rename to talks/_showcase/assets/example-media.png diff --git a/talks/_template/assets/example-site.png b/talks/_showcase/assets/example-site.png similarity index 100% rename from talks/_template/assets/example-site.png rename to talks/_showcase/assets/example-site.png diff --git a/talks/_showcase/assets/qr-slides.png b/talks/_showcase/assets/qr-slides.png new file mode 100644 index 0000000..6acd23a Binary files /dev/null and b/talks/_showcase/assets/qr-slides.png differ diff --git a/talks/_showcase/index.html b/talks/_showcase/index.html new file mode 100644 index 0000000..dca9b74 --- /dev/null +++ b/talks/_showcase/index.html @@ -0,0 +1,518 @@ + + + + + + Component showcase — Broadsheet theme + + + + + + + + + + + + + +
+
+ + +
+

Event · Place, DD Month YYYY

+

Talk title

+

A one-line description of the talk.

+ + + + +
+ +
+ QR code linking to these slides online + Scan for these slidesslides.frederickmadore.com +
+ +
+ + +
+

In four movements

+

What this talk covers

+
    +
  1. The questionWhy keyword search fails the archive.
  2. +
  3. The collectionWhat the corpus holds, and what it omits.
  4. +
  5. A methodReading at scale without losing the text.
  6. +
  7. What it opensNew questions the corpus makes askable.
  8. +
+
+ + +
+

01

+

The first section

+

An optional italic subtitle.

+
+ + +
+

Optional eyebrow

+

A slide title

+

A larger opening line that sets up the slide.

+
    +
  • First point
  • +
  • Second point, with an emphasis and a highlight
  • +
  • Third point
  • +
+ +
+ + +
+

A comparison

+
+
+
Before
+
    +
  • A point on the left
  • +
  • Another point
  • +
+
+
+
After
+
    +
  • The corresponding point
  • +
  • One more
  • +
+
+
+
+ + +
+

A point, with an aside

+
+
+

The main argument runs in the wider column.

+
    +
  • Supporting detail
  • +
  • Supporting detail
  • +
  • Supporting detail
  • +
+
+
+
+

In brief

+

A ruled sidebar groups a secondary thought without a heavy card.

+

tag key

+
+
+
+
+ + +
+

A figure beside its reading

+
+
+
+ Describe the figure — this alt text shows in the lightbox +
A caption naming the source. Click the image to enlarge.
+
+
+
+

The text beside a documentary image. Because figures show whole, a map or manuscript keeps its edges.

+
    +
  • What to notice
  • +
  • Why it matters
  • +
+
+
+
+ + +
+

The collection

+

In figures

+
+
12,480documents
+
8countries
+
1953–2024date range
+
4languages
+
+
+ Note + Counts are indicative — the corpus grows monthly. +
+
+ + +
+

The headline figure

+

12,480

+

documents catalogued across eight countries — and the count climbs every month.

+
+ + +
+

A table

+ + + + + + + + + +
SourceTypeItems
Newspaper APeriodical3,204
Archive BPamphlet1,876
Collection CPhotograph942
+

Source: a line crediting the data.

+
+ + +
+

A query

+
from iwac import Collection
+
+col = Collection.load("islam-west-africa")
+hits = col.search("réforme", lang="fr", after=1990)
+print(f"{len(hits)} documents")
+
+ + +
+

Built incrementally

+

One point at a time

+
    +
  • First it appears on its own
  • +
  • Then the second joins it
  • +
  • This one rises as it fades in
  • +
  • And this one lands in green
  • +
+ +
+ + +
+

A claim, in motion

+
+
+

A claim, in motion

+

The heading slid up and shrank — no cut. Now the evidence arrives.

+
    +
  • The same element, two slides, one data-id
  • +
  • Reveal interpolates the difference
  • +
+ +
+ + + + + +
+

A claim worth pausing on, with one emphasised phrase.

+

A supporting line beneath it.

+
+ + +
+

The archive is not a neutral mirror; it is an argument about what was worth keeping.

+

— An attributable source

+
+ + +
+
Describe the scene
+
+
+

On location

+

A full-bleed image

+

A short caption in the protected left third.

+
+
+ + +
+

On the web

+

A framed website

+
+
+ + islam.zmo.de/s/westafrica + Open ↗ +
+
+ + Describe the page — this alt text shows in the lightbox + + +
+
+
+ + +
+

Make the data the design

+

A corpus has a shape — so draw it

+
+
+
+
Documents by decade · 14,700 total
+ +
’50s’60s’70s’80s’90s’00s’10s’20s
+

Indicative distribution · Source: a line crediting the data.

+
+
+
+
Share by country
+ +
In the marginThe thin bars are the bias disclosure — made visible.
+
+
+ +
+ + +
+

One coherent image treatment

+

Two image types, one system

+
+
+
A documentary photograph, rendered as a green duotone plate.
+
Fig. 1Documentary photos get a green duotone and a ruled plate caption — every one belongs.
+
+
+
+
islam.zmo.de/s/westafrica
+
An application screenshot, shown in true colour inside browser chrome.
+
+

App screenshots keep their true colour — but always inside the same chrome.

+
+
+ +
+ + +
+ + +

Plumbing, then intelligence

+
+
+

The server gives any AI assistant structured, read-only access to the collection; the skill — a plain-text file — encodes how to use it well.

+
+
+
In the marginThe serif counter-voice becomes a true sidenote — a scholar’s marginalia, exactly where the eye expects it on a printed page.
+
+
+ +
+ + +
+ +

A process, drawn — not bulleted

+
+
+ 1 · AskYour question +

laïcité au Burkina Faso

+

Plain language — and any language works.

+
+ +
+ 2 · EmbedTurned into coordinates +

An embedding model maps the text to 768 numbers — one point in a space of meaning.

+ laïcité → [0.07, −0.12, …] +
+ +
+ 3 · MatchNearest points win +

Cosine similarity ranks every record by how close it sits — in meaning, not wording.

+
+
+ +
+ + +
+

Thank you · Merci

+

A closing line.

+ + +
+ +
+ Type EB Garamond & Libre Franklin + Palette Bayreuth green · navy · gold + Built with reveal.js · fully offline +
+ + +
+ QR code linking to these slides online + Scan for these slidesslides.frederickmadore.com +
+
+ +
+
+ + + + + + + + + + + + + + diff --git a/talks/_template/index.html b/talks/_template/index.html index 5d31471..4ae0921 100644 --- a/talks/_template/index.html +++ b/talks/_template/index.html @@ -4,66 +4,43 @@ Talk title — Frédérick Madore + - + - - + + diff --git a/talks/talks.json b/talks/talks.json new file mode 100644 index 0000000..14a1e84 --- /dev/null +++ b/talks/talks.json @@ -0,0 +1,102 @@ +{ + "$comment": "Talk manifest — one structured record per published talk, newest first. The landing page's static list is generated from this file by tools/build-index.py (run it after editing), and tools/audit.py validates that every entry has a deck folder and every published deck has an entry. tools/new-talk.py appends entries automatically. Optional per-talk fields: video, pdf, eventUrl.", + "site": "https://slides.frederickmadore.com", + "talks": [ + { + "slug": "2026-07-01-rhodes-reconfiguring-archive", + "date": "2026-07-01", + "language": "en", + "event": "Reimagining the Archive · Rhodes University", + "venue": "Reimagining the Archive · Rhodes University · 1 July 2026", + "title": "Reconfiguring the Archive", + "shortTitle": "Reconfiguring the Archive", + "description": "Relationality and discovery in the Africa Multiple Interactive Research Atlas (AMIRA).", + "presenters": [ + "Frédérick Madore" + ], + "tags": [ + "digital humanities", + "archives", + "AMIRA" + ] + }, + { + "slug": "2026-06-30-paris-reaf-dh-ia-afrique", + "date": "2026-06-30", + "language": "fr", + "event": "Rencontres des études africaines en France · Paris", + "venue": "9es REAF · Paris · 30 juin 2026", + "title": "Humanités numériques, IA et études africaines", + "shortTitle": "DH, IA & études africaines", + "description": "Retour sur une note de synthèse collective (Hanovre, février 2026).", + "presenters": [ + "Vincent Hiribarren", + "Frédérick Madore" + ], + "tags": [ + "digital humanities", + "AI", + "African studies" + ] + }, + { + "slug": "2026-06-29-erlangen-islam-peripheries", + "date": "2026-06-29", + "language": "en", + "event": "Digital History in/of Central Asia · FAU Erlangen-Nürnberg", + "venue": "Digital History in/of Central Asia · FAU Erlangen-Nürnberg · 29 June 2026", + "title": "Islam's “Peripheries”", + "shortTitle": "Islam's “Peripheries”", + "description": "Comparing two locked multilingual archives — West Africa and Central Asia — with AI-driven DH.", + "presenters": [ + "Aksana Ismailbekova", + "Frédérick Madore" + ], + "tags": [ + "Islam", + "AI", + "archives", + "Central Asia", + "West Africa" + ] + }, + { + "slug": "2026-06-15-luxembourg-beyond-keywords", + "date": "2026-06-15", + "language": "en", + "event": "Digital Humanities & History · C²DH, Luxembourg", + "venue": "C²DH · Luxembourg · 15 June 2026", + "title": "Beyond Keywords", + "shortTitle": "Beyond Keywords", + "description": "Opening the Islam West Africa Collection to AI on its own terms.", + "presenters": [ + "Frédérick Madore" + ], + "tags": [ + "IWAC", + "AI", + "semantic search", + "MCP" + ] + }, + { + "slug": "2026-05-06-dga-dormant-collections", + "date": "2026-05-06", + "language": "en", + "event": "German Society of Asia Studies · Berlin", + "venue": "Deutsche Gesellschaft für Asienforschung · 6 May 2026", + "title": "Dormant Digital Collections, AI Triage", + "shortTitle": "Dormant Collections · AI Triage", + "description": "Lessons from the Islam West Africa Collection and beyond.", + "presenters": [ + "Frédérick Madore" + ], + "tags": [ + "IWAC", + "AI", + "OCR", + "collections" + ] + } + ] +} diff --git a/tools/audit.py b/tools/audit.py new file mode 100644 index 0000000..e466f70 --- /dev/null +++ b/tools/audit.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 +"""Repository audit — one command that checks the whole site's integrity. + +Static checks (no browser, stdlib only): + • missing local images / scripts / stylesheets / iframes / embedded files + • duplicate HTML ids + • without alt,