|
1 | 1 | # Copilot Instructions for java.evolved |
2 | 2 |
|
3 | | -## Project Overview |
| 3 | +## Build & Serve |
4 | 4 |
|
5 | | -This is a static site showcasing modern Java patterns vs legacy approaches. |
6 | | -It is hosted on GitHub Pages at https://javaevolved.github.io. |
| 5 | +```bash |
| 6 | +jbang html-generators/generate.java # Rebuild all locales |
| 7 | +jbang html-generators/generate.java --locale es # Rebuild single locale |
| 8 | +jwebserver -b 0.0.0.0 -d site -p 8090 # Serve locally |
| 9 | +``` |
| 10 | + |
| 11 | +Requires **Java 25+** and **JBang**. No npm, Maven, or Gradle. |
| 12 | + |
| 13 | +**Validation:** There is no test suite. Validate changes by running the generator and confirming it completes without errors. The `proof/` directory contains one JBang script per pattern that proves the modern code compiles: |
| 14 | + |
| 15 | +```bash |
| 16 | +jbang proof/language/TypeInferenceWithVar.java # Run single proof |
| 17 | +``` |
7 | 18 |
|
8 | 19 | ## Architecture |
9 | 20 |
|
10 | | -### Source of Truth: JSON Files |
| 21 | +Static site generator: YAML content → JBang generator → HTML pages. |
11 | 22 |
|
12 | | -Each pattern is defined as a JSON file under **`content/category/`**: |
| 23 | +- **`content/`** — Source of truth. One YAML/JSON file per pattern, organized by category. |
| 24 | +- **`templates/`** — HTML templates with `{{placeholder}}` tokens. The generator replaces tokens with content fields and UI strings. |
| 25 | +- **`html-generators/generate.java`** — JBang script that reads content + translations + templates and produces all HTML under `site/`. |
| 26 | +- **`site/app.js`** and **`site/styles.css`** — Manually maintained client-side code (vanilla JS, no frameworks). |
| 27 | +- **`translations/strings/{locale}.yaml`** — UI string translations (labels, nav, footer). Tokens use dotted keys: `{{nav.allPatterns}}`. |
| 28 | +- **`translations/content/{locale}/`** — Partial content translations (only translatable fields; structural data always comes from English source). |
| 29 | +- **`proof/{category}/{PascalCaseSlug}.java`** — JBang scripts proving each pattern's modern code compiles on Java 25. |
13 | 30 |
|
14 | | -``` |
15 | | -content/language/type-inference-with-var.json |
16 | | -content/collections/immutable-list-creation.json |
17 | | -content/streams/stream-tolist.json |
18 | | -... |
19 | | -``` |
| 31 | +### Generated files — DO NOT EDIT directly |
20 | 32 |
|
21 | | -**Categories:** `language`, `collections`, `strings`, `streams`, `concurrency`, `io`, `errors`, `datetime`, `security`, `tooling`, `enterprise` |
| 33 | +Everything under `site/` except `app.js` and `styles.css` is generated: |
| 34 | +- `site/index.html`, `site/{category}/*.html`, `site/data/snippets.json` |
| 35 | +- `site/{locale}/index.html`, `site/{locale}/{category}/*.html`, `site/{locale}/data/snippets.json` |
22 | 36 |
|
23 | | -### Generated Files (DO NOT EDIT) |
| 37 | +Run the generator to rebuild after any content, template, or translation change. |
24 | 38 |
|
25 | | -The following are **generated by `html-generators/generate.java`** and must not be edited directly: |
| 39 | +## Content Schema |
26 | 40 |
|
27 | | -- `site/index.html` — English homepage with preview cards (generated from `templates/index.html`) |
28 | | -- `site/language/*.html`, `site/collections/*.html`, etc. — English detail pages |
29 | | -- `site/data/snippets.json` — English aggregated search index |
30 | | -- `site/{locale}/index.html` — localized homepage (e.g., `site/es/index.html`) |
31 | | -- `site/{locale}/language/*.html`, etc. — localized detail pages |
32 | | -- `site/{locale}/data/snippets.json` — localized search index |
| 41 | +Content files are YAML (preferred) or JSON under `content/{category}/{slug}.yaml`. The generator auto-detects format by extension (`.yaml`, `.yml`, `.json`). |
33 | 42 |
|
34 | | -Run `jbang html-generators/generate.java` to rebuild all generated files from the JSON sources and translations. |
| 43 | +### Required fields |
35 | 44 |
|
36 | | -### Manually Maintained Files |
| 45 | +| Field | Constraint | |
| 46 | +|-------|-----------| |
| 47 | +| `slug` | Must match filename (without extension) | |
| 48 | +| `category` | Must match parent folder name | |
| 49 | +| `whyModernWins` | Exactly **3** entries, each with `icon`, `title`, `desc` | |
| 50 | +| `related` | Exactly **3** entries as `category/slug` paths (cross-category OK) | |
| 51 | +| `docs` | At least **1** entry with `title` and `href` | |
| 52 | +| `prev` / `next` | `category/slug` path or `null` for first/last in the global chain | |
| 53 | +| `jdkVersion` | The JDK version where the feature became **final** (not preview) | |
| 54 | +| `difficulty` | One of: `beginner`, `intermediate`, `advanced` | |
| 55 | +| `support.state` | One of: `available`, `preview`, `experimental` | |
37 | 56 |
|
38 | | -- `site/app.js` — client-side search, filtering, code highlighting, locale detection |
39 | | -- `site/styles.css` — all styling |
40 | | -- `templates/slug-template.html` — HTML template with `{{placeholder}}` tokens (content + UI strings) used by the generator |
41 | | -- `templates/index.html` — homepage template with `{{tipCards}}`, `{{snippetCount}}`, and UI string placeholders |
42 | | -- `templates/index-card.html` — preview card template for the homepage grid |
43 | | -- `html-generators/categories.properties` — category ID → display name mapping |
44 | | -- `html-generators/locales.properties` — supported locales registry (locale=Display name) |
45 | | -- `translations/strings/{locale}.yaml` — UI strings per locale (labels, nav, footer, etc.) |
46 | | -- `translations/content/{locale}/` — translated pattern JSON files (partial, translatable fields only) |
| 57 | +### Adding a new pattern |
47 | 58 |
|
48 | | -### Project Structure |
| 59 | +1. Create `content/{category}/new-slug.yaml` with all required fields (use `content/template.json` as reference). |
| 60 | +2. Update `prev`/`next` in the adjacent patterns to maintain the navigation chain. |
| 61 | +3. Create `proof/{category}/{PascalCaseSlug}.java` — JBang script wrapping the modern code. |
| 62 | +4. Run `jbang html-generators/generate.java` and verify it completes. |
| 63 | +5. Translations are optional — the AI translation workflow handles them, or create partial files under `translations/content/{locale}/`. |
49 | 64 |
|
50 | | -``` |
51 | | -content/ # English content JSON files (source of truth, one per pattern) |
52 | | -translations/ # All i18n artifacts |
53 | | - strings/ # UI strings per locale (en.yaml, es.yaml, pt-BR.yaml) |
54 | | - content/ # Translated pattern files per locale (partial, translatable fields only) |
55 | | - es/ # Spanish translations (mirrors content/ folder structure) |
56 | | - pt-BR/ # Brazilian Portuguese translations |
57 | | -site/ # Deployable site (static assets + generated HTML) |
58 | | - es/ # Generated Spanish pages |
59 | | - pt-BR/ # Generated Portuguese pages |
60 | | -templates/ # HTML templates with {{…}} tokens for content + UI strings |
61 | | -html-generators/ # Build scripts, categories.properties, locales.properties |
62 | | -specs/ # Feature specifications (e.g., i18n-spec.md) |
63 | | -``` |
| 65 | +### Removing or reordering a pattern |
64 | 66 |
|
65 | | -## JSON Snippet Schema |
66 | | - |
67 | | -Each `content/category/slug.json` file has this structure: |
68 | | - |
69 | | -```json |
70 | | -{ |
71 | | - "id": 1, |
72 | | - "slug": "type-inference-with-var", |
73 | | - "title": "Type inference with var", |
74 | | - "category": "language", |
75 | | - "difficulty": "beginner|intermediate|advanced", |
76 | | - "jdkVersion": "10", |
77 | | - "oldLabel": "Java 8", |
78 | | - "modernLabel": "Java 10+", |
79 | | - "oldApproach": "Explicit Types", |
80 | | - "modernApproach": "var keyword", |
81 | | - "oldCode": "// old way...", |
82 | | - "modernCode": "// modern way...", |
83 | | - "summary": "One-line description.", |
84 | | - "explanation": "How it works paragraph.", |
85 | | - "whyModernWins": [ |
86 | | - { "icon": "⚡", "title": "Short title", "desc": "One sentence." }, |
87 | | - { "icon": "👁", "title": "Short title", "desc": "One sentence." }, |
88 | | - { "icon": "🔒", "title": "Short title", "desc": "One sentence." } |
89 | | - ], |
90 | | - "support": { |
91 | | - "state": "available", |
92 | | - "description": "Widely available since JDK 10 (March 2018)" |
93 | | - }, |
94 | | - "prev": "category/slug-of-previous", |
95 | | - "next": "category/slug-of-next", |
96 | | - "related": [ |
97 | | - "category/slug-1", |
98 | | - "category/slug-2", |
99 | | - "category/slug-3" |
100 | | - ], |
101 | | - "docs": [ |
102 | | - { "title": "Javadoc or Guide Title", "href": "https://docs.oracle.com/..." } |
103 | | - ] |
104 | | -} |
105 | | -``` |
| 67 | +Update `prev`/`next` in adjacent patterns. Search for the slug in other patterns' `related` arrays and replace with an appropriate alternative. |
| 68 | + |
| 69 | +## Internationalization |
| 70 | + |
| 71 | +Full spec: `specs/i18n/i18n-spec.md`. Key rules: |
| 72 | + |
| 73 | +- All locales (including English) go through the same build pipeline. |
| 74 | +- UI strings: `translations/strings/{locale}.yaml`. Missing keys fall back to English with a build-time warning. |
| 75 | +- Content translations contain **only** translatable fields: `title`, `summary`, `explanation`, `oldApproach`, `modernApproach`, `whyModernWins`, `support.description`. Code, slugs, navigation, and docs are never translated. |
| 76 | +- `oldCode`/`modernCode` in translation files are **always overwritten** with English values at build time to prevent hallucinated code. |
| 77 | +- Locale registry: `html-generators/locales.properties` (format: `locale=Display Name`). |
| 78 | +- When adding a new UI string key, add it to `en.yaml` first, then to all other locale files. The generator warns on missing keys but doesn't fail. |
| 79 | +- YAML colons in string values must be quoted (Jackson parser is stricter than PyYAML). |
| 80 | + |
| 81 | +## Proof Files |
106 | 82 |
|
107 | | -### Key Rules |
108 | | - |
109 | | -- `slug` must match the filename (without `.json`) |
110 | | -- `category` must match the parent folder name |
111 | | -- `whyModernWins` must have exactly **3** entries |
112 | | -- `related` must have exactly **3** entries (as `category/slug` paths) |
113 | | -- `docs` must have at least **1** entry linking to Javadoc or Oracle documentation |
114 | | -- `prev`/`next` are `category/slug` paths or `null` for first/last |
115 | | -- Code in `oldCode`/`modernCode` uses `\n` for newlines |
116 | | - |
117 | | -## Category Display Names |
118 | | - |
119 | | -Categories and their display names are defined in `html-generators/categories.properties`: |
120 | | - |
121 | | -| ID | Display | |
122 | | -|----|---------| |
123 | | -| `language` | Language | |
124 | | -| `collections` | Collections | |
125 | | -| `strings` | Strings | |
126 | | -| `streams` | Streams | |
127 | | -| `concurrency` | Concurrency | |
128 | | -| `io` | I/O | |
129 | | -| `errors` | Errors | |
130 | | -| `datetime` | Date/Time | |
131 | | -| `security` | Security | |
132 | | -| `tooling` | Tooling | |
133 | | -| `enterprise` | Enterprise | |
134 | | - |
135 | | -## Adding a New Pattern |
136 | | - |
137 | | -1. Create `content/category/new-slug.json` with all required fields |
138 | | -2. Update `prev`/`next` in the adjacent patterns' JSON files |
139 | | -3. Run `jbang html-generators/generate.java` |
140 | | -4. (Optional) Create translated content files under `translations/content/{locale}/category/new-slug.json` with only translatable fields — or let the AI translation workflow handle it |
141 | | - |
142 | | -## Internationalization (i18n) |
143 | | - |
144 | | -The site supports multiple languages. See `specs/i18n/i18n-spec.md` for the full specification. |
145 | | - |
146 | | -### Key Concepts |
147 | | - |
148 | | -- **UI strings:** Hard-coded template text (labels, nav, footer) is extracted into `translations/strings/{locale}.yaml`. Templates use `{{dotted.key}}` tokens (e.g., `{{nav.allPatterns}}`, `{{sections.codeComparison}}`). Missing keys fall back to the English value with a build-time warning. |
149 | | -- **Content translations:** Translated pattern files under `translations/content/{locale}/` contain **only** translatable fields (`title`, `summary`, `explanation`, `oldApproach`, `modernApproach`, `whyModernWins`, `support.description`). All other fields (`oldCode`, `modernCode`, `slug`, `id`, `prev`, `next`, `related`, `docs`, etc.) are always taken from the English source. |
150 | | -- **Locale registry:** `html-generators/locales.properties` lists supported locales (format: `locale=Display name`). The first entry is the default. |
151 | | -- **English is a first-class locale:** All locales — including English — go through the same build pipeline. |
152 | | -- **Fallback:** If a pattern has no translation file for a locale, the English content is used and an "untranslated" banner is shown. |
153 | | - |
154 | | -### Supported Locales |
155 | | - |
156 | | -Defined in `html-generators/locales.properties`: |
157 | | - |
158 | | -| Locale | Display Name | |
159 | | -|--------|-------------| |
160 | | -| `en` | English | |
161 | | -| `es` | Español | |
162 | | -| `pt-BR` | Português (Brasil) | |
163 | | - |
164 | | -### Content Translation File Example |
165 | | - |
166 | | -Translation files contain **only** translatable fields — no structural data: |
167 | | - |
168 | | -```json |
169 | | -// translations/content/pt-BR/language/type-inference-with-var.json |
170 | | -{ |
171 | | - "title": "Inferência de tipo com var", |
172 | | - "oldApproach": "Tipos explícitos", |
173 | | - "modernApproach": "Palavra-chave var", |
174 | | - "summary": "Use var para deixar o compilador inferir o tipo local.", |
175 | | - "explanation": "...", |
176 | | - "whyModernWins": [ |
177 | | - { "icon": "⚡", "title": "Menos ruído", "desc": "..." }, |
178 | | - { "icon": "👁", "title": "Mais legível", "desc": "..." }, |
179 | | - { "icon": "🔒", "title": "Seguro", "desc": "..." } |
180 | | - ], |
181 | | - "support": { |
182 | | - "description": "Amplamente disponível desde o JDK 10 (março de 2018)" |
183 | | - } |
| 83 | +Each pattern has a corresponding proof file: `proof/{category}/{PascalCaseSlug}.java`. |
| 84 | + |
| 85 | +```java |
| 86 | +///usr/bin/env jbang "$0" "$@" ; exit $? |
| 87 | +//JAVA 25+ |
| 88 | + |
| 89 | +/// Proof: slug-name |
| 90 | +/// Source: content/category/slug-name.yaml |
| 91 | +void main() { |
| 92 | + // modern code only — no old code, no assertions |
184 | 93 | } |
185 | 94 | ``` |
186 | 95 |
|
187 | | -## Local Development |
| 96 | +Uses Java 25 implicit classes (`void main()`, not `static void main`). Add minimal scaffolding (imports, dummy variables) to make the modern code compile. |
188 | 97 |
|
189 | | -```bash |
190 | | -jbang html-generators/generate.java # Build HTML pages + snippets.json |
191 | | -jwebserver -d site -p 8090 # Serve locally |
192 | | -``` |
| 98 | +## Key Conventions |
| 99 | + |
| 100 | +- **Vanilla JS only** — `site/app.js` uses no frameworks or build tools. |
| 101 | +- **Category display names** are defined in `html-generators/categories.properties`, not hardcoded. |
| 102 | +- **JDK filter ranges** in `app.js` map LTS versions to ranges: `11→[9-11]`, `17→[12-17]`, `21→[18-21]`, `25→[22-25]`. |
| 103 | +- **JetBrains Mono ligatures** are disabled on `.code-text` elements to prevent operators like `->` from rendering as special characters. |
| 104 | +- **Dark theme** uses CSS custom properties (`--modern-bg`, `--old-bg`). Theme state is in `localStorage.theme` and `data-theme` on `<html>`. |
| 105 | +- **RTL support** — Arabic (`ar`) locale sets `dir="rtl"` on the page. |
| 106 | +- When both old and modern approaches are from the same JDK version, use descriptive labels (e.g., "Full syntax" / "Compact") instead of version numbers. |
0 commit comments