fuzzyworld

when-am-i / commit

Deploy to apps.fuzzyworld.net with fuzzyworld-tools

c2d75cfd16c3bd30c18acd5d907518c51565f266 ยท Pavel Safronov ยท 2026-10-26 18:26:59 -0700 ยท parents 68dcf3edc63e

Deploy to apps.fuzzyworld.net with fuzzyworld-tools

Adds the fuzzyworld package.json section, a deploy script (fw deploy) and the fuzzyworld-tools dependency, and points gitlab.io / gitlab.com links at apps.fuzzyworld.net and code.fuzzyworld.net.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
 README.md                      | 12 ++++++++----
 package-lock.json              | 18 ++++++++++++++++--
 package.json                   | 17 ++++++++++++++++-
 scripts/gen-badges.mts         |  6 ++----
 src/components/Attribution.tsx |  2 +-
 src/components/ResultPanel.tsx |  6 +++---
 src/utils/report.ts            |  4 ++--
 vite.config.ts                 |  4 ++--
 8 files changed, 50 insertions(+), 19 deletions(-)

diff --git a/README.md b/README.md
index bff3d09..c6d78ab 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,8 @@
-# ๐Ÿš€ [Live site](https://pavelsafronov.gitlab.io/when-am-i)
+# ๐Ÿš€ [Live site](https://apps.fuzzyworld.net/when-am-i/)
 
 <!-- badges:start -->
 <!-- generated by `npm run gen:badges` โ€” do not edit by hand -->
-[![pipeline status](https://gitlab.com/PavelSafronov/when-am-i/badges/main/pipeline.svg)](https://gitlab.com/PavelSafronov/when-am-i/-/pipelines)
-[![live demo](https://img.shields.io/badge/demo-live-success)](https://pavelsafronov.gitlab.io/when-am-i)
+[![live demo](https://img.shields.io/badge/demo-live-success)](https://apps.fuzzyworld.net/when-am-i/)
 ![version](https://img.shields.io/badge/version-v0.1.0-blue)
 ![calendar data](https://img.shields.io/badge/calendar%20data-20-orange)
 ![license](https://img.shields.io/badge/license-MIT-green)
@@ -166,7 +165,7 @@ needs to read at a glance.
 
 ## Reporting issues
 
-The app footer links to the [source repository](https://gitlab.com/PavelSafronov/when-am-i)
+The app footer links to the [source repository](https://code.fuzzyworld.net/when-am-i/)
 and a **Report an issue** link. The latter opens a pre-addressed email to the
 project's GitLab Service Desk (no GitLab account needed) prefilled with context
 โ€” build version/commit, browser, URL, and the current selection. If the app
@@ -246,6 +245,11 @@ See `src/data/calendars/SCHEMA.md` (human-readable field docs) and
 
 ### How to contribute
 
+> **Paused for now.** The project moved from GitLab to
+> [code.fuzzyworld.net](https://code.fuzzyworld.net/when-am-i/), which can't accept
+> merge requests yet. Please hold on to new calendars until contributions reopen.
+> The steps below still describe what a contribution needs.
+
 - Add or edit files in `src/data/calendars/` via pull request.
 - Use [geojson.io](https://geojson.io/) or similar tools to draw and export region polygons.
 - Include sources/references for each era (and for every anchor event).
diff --git a/package-lock.json b/package-lock.json
index c024883..9276138 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,13 +1,14 @@
 {
-    "name": "what-year-is-it",
+    "name": "when-am-i",
     "version": "0.1.0",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
-            "name": "what-year-is-it",
+            "name": "when-am-i",
             "version": "0.1.0",
             "hasInstallScript": true,
+            "license": "MIT",
             "dependencies": {
                 "@js-temporal/polyfill": "^0.5.1",
                 "leaflet": "^1.9.4",
@@ -24,6 +25,7 @@
                 "eslint": "^10.5.0",
                 "eslint-plugin-react-hooks": "^7.1.1",
                 "eslint-plugin-react-refresh": "^0.5.3",
+                "fuzzyworld-tools": "git+https://code.fuzzyworld.net/fuzzyworld-tools.git#semver:^1.0.0",
                 "globals": "^17.6.0",
                 "playwright": "^1.61.0",
                 "tsx": "^4.22.4",
@@ -2799,6 +2801,18 @@
                 "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
             }
         },
+        "node_modules/fuzzyworld-tools": {
+            "version": "1.0.1",
+            "resolved": "git+https://code.fuzzyworld.net/fuzzyworld-tools.git#45b1fe3805ad7ec86b864e4604b7ad89a0e25a78",
+            "dev": true,
+            "license": "MIT",
+            "bin": {
+                "fw": "bin/fw"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/gensync": {
             "version": "1.0.0-beta.2",
             "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
diff --git a/package.json b/package.json
index 5093ff0..9e9a1b4 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,8 @@
         "usage": "tsx scripts/usage.mts",
         "gen:favicon": "sips -z 64 64 calendar-source.png --out src/assets/favicon.png",
         "copy:leaflet-icons": "mkdir -p public && cp node_modules/leaflet/dist/images/marker-icon.png public/ && cp node_modules/leaflet/dist/images/marker-icon-2x.png public/ && cp node_modules/leaflet/dist/images/marker-shadow.png public/",
-        "postinstall": "npm run copy:leaflet-icons"
+        "postinstall": "npm run copy:leaflet-icons",
+        "deploy": "fw deploy"
     },
     "dependencies": {
         "@js-temporal/polyfill": "^0.5.1",
@@ -42,6 +43,7 @@
         "eslint": "^10.5.0",
         "eslint-plugin-react-hooks": "^7.1.1",
         "eslint-plugin-react-refresh": "^0.5.3",
+        "fuzzyworld-tools": "git+https://code.fuzzyworld.net/fuzzyworld-tools.git#semver:^1.0.0",
         "globals": "^17.6.0",
         "playwright": "^1.61.0",
         "tsx": "^4.22.4",
@@ -49,5 +51,18 @@
         "typescript-eslint": "^8.61.1",
         "vite": "^5.0.0",
         "vitest": "^4.1.9"
+    },
+    "fuzzyworld": {
+        "app": "when-am-i",
+        "title": "When am I?",
+        "prebuild": [
+            "npm run gen:badges -- --check"
+        ],
+        "checks": [
+            "validate:data",
+            "lint",
+            "typecheck",
+            "test"
+        ]
     }
 }
diff --git a/scripts/gen-badges.mts b/scripts/gen-badges.mts
index 0fa1343..c1167b6 100644
--- a/scripts/gen-badges.mts
+++ b/scripts/gen-badges.mts
@@ -6,7 +6,7 @@
 //
 // The block lives between the <!-- badges:start --> and <!-- badges:end -->
 // markers in README.md. Computed values (app version, number of calendar data
-// files) come from disk; the static badges (pipeline, demo, tech stack) are
+// files) come from disk; the static badges (demo, tech stack) are
 // emitted here too so the whole row has a single source of truth.
 
 import { readFile, writeFile, readdir } from 'node:fs/promises';
@@ -16,8 +16,7 @@ import { fileURLToPath } from 'node:url';
 const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
 const README = path.join(REPO_ROOT, 'README.md');
 
-const GITLAB_PROJECT = 'PavelSafronov/when-am-i';
-const PAGES_URL = 'https://pavelsafronov.gitlab.io/when-am-i';
+const PAGES_URL = 'https://apps.fuzzyworld.net/when-am-i/';
 const START = '<!-- badges:start -->';
 const END = '<!-- badges:end -->';
 const PREAMBLE = '<!-- generated by `npm run gen:badges` โ€” do not edit by hand -->';
@@ -38,7 +37,6 @@ async function buildBlock(): Promise<string> {
 
     return [
         PREAMBLE,
-        `[![pipeline status](https://gitlab.com/${GITLAB_PROJECT}/badges/main/pipeline.svg)](https://gitlab.com/${GITLAB_PROJECT}/-/pipelines)`,
         `[![live demo](${shield('demo', 'live', 'success')})](${PAGES_URL})`,
         `![version](${shield('version', `v${pkg.version}`, 'blue')})`,
         `![calendar data](${shield('calendar data', String(calendars), 'orange')})`,
diff --git a/src/components/Attribution.tsx b/src/components/Attribution.tsx
index 323e83f..288ff2b 100644
--- a/src/components/Attribution.tsx
+++ b/src/components/Attribution.tsx
@@ -4,7 +4,7 @@ import { REPO_URL, reportMailtoHref, type ReportContext } from '../utils/report'
 const Attribution: React.FC<{ reportContext?: ReportContext }> = ({ reportContext }) => (
     <footer style={{ marginTop: 32, color: '#555' }}>
         <p style={{ display: 'flex', gap: 16, flexWrap: 'wrap', fontSize: '0.95em' }}>
-            <a href={REPO_URL} target="_blank" rel="noopener noreferrer">Source code</a>
+            <a href={`${REPO_URL}/`} target="_blank" rel="noopener noreferrer">Source code</a>
             <a href={reportMailtoHref(undefined, reportContext)}>Report an issue</a>
         </p>
         <p
diff --git a/src/components/ResultPanel.tsx b/src/components/ResultPanel.tsx
index 206dba5..52276e8 100644
--- a/src/components/ResultPanel.tsx
+++ b/src/components/ResultPanel.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import type { Answer, Era, HistoricalDate, Resolution } from '../types';
-import { REPO_URL, repoFileUrl } from '../utils/report';
+import { repoFileUrl } from '../utils/report';
 import { parseGregorianDate } from '../utils/julianDay';
 
 interface ResultPanelProps {
@@ -209,8 +209,8 @@ const ResultPanel: React.FC<ResultPanelProps> = ({ picked, date, resolution, col
                 <strong>No calendar data for this spot.</strong>
                 <p style={{ margin: '8px 0 0', fontSize: '0.9em' }}>
                     No region in the dataset covers this exact point. The <strong>shaded areas</strong>{' '}
-                    on the map have calendar data for this date โ€” pin one of those, or{' '}
-                    <a href={REPO_URL} target="_blank" rel="noopener noreferrer">add a calendar on GitLab</a>.
+                    on the map have calendar data for this date โ€” pin one of those. New calendar
+                    contributions are paused for now, while the project moves to a new home.
                 </p>
             </div>
         );
diff --git a/src/utils/report.ts b/src/utils/report.ts
index e196dbc..7c4c9bd 100644
--- a/src/utils/report.ts
+++ b/src/utils/report.ts
@@ -6,11 +6,11 @@
 // for crashes โ€” the error message + a trimmed stack). Kept short because some
 // mail clients truncate long mailto bodies.
 
-export const REPO_URL = 'https://gitlab.com/PavelSafronov/when-am-i';
+export const REPO_URL = 'https://code.fuzzyworld.net/when-am-i';
 
 /** Link to a repo-relative file on the default branch (for data-source links). */
 export function repoFileUrl(path: string): string {
-    return `${REPO_URL}/-/blob/main/${path}`;
+    return `${REPO_URL}/blob/${path}.html`;
 }
 
 export const SERVICE_DESK_EMAIL =
diff --git a/vite.config.ts b/vite.config.ts
index 0174122..c672ce5 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -3,6 +3,7 @@ import { execSync } from 'node:child_process';
 import { readFileSync } from 'node:fs';
 import { defineConfig } from 'vite';
 import react from '@vitejs/plugin-react';
+import fuzzyworld from 'fuzzyworld-tools/vite';
 
 // Build metadata, surfaced in error reports so we know which deployed build a
 // report came from. Commit falls back to "unknown" outside a git checkout (CI
@@ -18,8 +19,7 @@ const gitCommit = (() => {
 })();
 
 export default defineConfig({
-    base: '/when-am-i/',
-    plugins: [react()],
+    plugins: [fuzzyworld(), react()],
     define: {
         __APP_VERSION__: JSON.stringify(pkg.version),
         __GIT_COMMIT__: JSON.stringify(gitCommit),