Skip to content

Commit eb18787

Browse files
brunoborgesCopilot
andcommitted
Templatize index.html — auto-generate preview cards from JSON
- Create templates/index.html with {{tipCards}} and {{snippetCount}} placeholders - Create templates/index-card.html for preview card rendering - Update Java and Python generators to render index.html from templates - Remove site/index.html from git tracking (now fully generated) - Update .gitignore, copilot-instructions, rebuild fat JAR - Adding a new pattern no longer requires manual index.html edits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 96d2a22 commit eb18787

File tree

8 files changed

+364
-3132
lines changed

8 files changed

+364
-3132
lines changed

.github/copilot-instructions.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ content/streams/stream-tolist.json
2424

2525
The following are **generated by `html-generators/generate.java`** and must not be edited directly:
2626

27+
- `site/index.html` — homepage with preview cards (generated from `templates/index.html`)
2728
- `site/language/*.html`, `site/collections/*.html`, etc. — detail pages
2829
- `site/data/snippets.json` — aggregated search index
2930

3031
Run `java -jar html-generators/generate.jar` to rebuild all generated files from the JSON sources.
3132

3233
### Manually Maintained Files
3334

34-
- `site/index.html` — homepage with preview cards (must be updated manually when adding/removing patterns)
3535
- `site/app.js` — client-side search, filtering, code highlighting
3636
- `site/styles.css` — all styling
3737
- `templates/slug-template.html` — HTML template with `{{placeholder}}` tokens used by the generator
38+
- `templates/index.html` — homepage template with `{{tipCards}}` and `{{snippetCount}}` placeholders
39+
- `templates/index-card.html` — preview card template for the homepage grid
3840

3941
### Project Structure
4042

@@ -112,8 +114,7 @@ Each `content/category/slug.json` file has this structure:
112114

113115
1. Create `content/category/new-slug.json` with all required fields
114116
2. Update `prev`/`next` in the adjacent patterns' JSON files
115-
3. Add a preview card in `site/index.html`
116-
4. Run `java -jar html-generators/generate.jar`
117+
3. Run `java -jar html-generators/generate.jar`
117118

118119
## Local Development
119120

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ site/tooling/*.html
1313
# Generated aggregate file (built from individual JSON sources by html-generators/)
1414
site/data/snippets.json
1515

16+
# Generated index page (built from templates/index.html by html-generators/)
17+
site/index.html
18+
1619
# Platform-specific CDS/AOT cache (generated by build-cds.sh)
1720
html-generators/generate.aot
1821
html-generators/generate.jsa

html-generators/generate.jar

324 Bytes
Binary file not shown.

html-generators/generate.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,16 @@ List<String> related() {
6161
}
6262
}
6363

64-
record Templates(String page, String whyCard, String relatedCard, String socialShare) {
64+
record Templates(String page, String whyCard, String relatedCard, String socialShare,
65+
String index, String indexCard) {
6566
static Templates load() throws IOException {
6667
return new Templates(
6768
Files.readString(Path.of("templates/slug-template.html")),
6869
Files.readString(Path.of("templates/why-card.html")),
6970
Files.readString(Path.of("templates/related-card.html")),
70-
Files.readString(Path.of("templates/social-share.html")));
71+
Files.readString(Path.of("templates/social-share.html")),
72+
Files.readString(Path.of("templates/index.html")),
73+
Files.readString(Path.of("templates/index-card.html")));
7174
}
7275
}
7376

@@ -97,10 +100,15 @@ void main() throws IOException {
97100
Files.writeString(Path.of(SITE_DIR, "data", "snippets.json"), prettyMapper.writeValueAsString(snippetsList) + "\n");
98101
IO.println("Rebuilt data/snippets.json with %d entries".formatted(snippetsList.size()));
99102

100-
// Patch index.html with the current snippet count
101-
var indexPath = Path.of(SITE_DIR, "index.html");
102-
Files.writeString(indexPath, Files.readString(indexPath).replace("{{snippetCount}}", String.valueOf(allSnippets.size())));
103-
IO.println("Patched index.html with snippet count: %d".formatted(allSnippets.size()));
103+
// Generate index.html from template
104+
var tipCards = allSnippets.values().stream()
105+
.map(s -> renderIndexCard(templates.indexCard(), s))
106+
.collect(Collectors.joining("\n"));
107+
var indexHtml = replaceTokens(templates.index(), Map.of(
108+
"tipCards", tipCards,
109+
"snippetCount", String.valueOf(allSnippets.size())));
110+
Files.writeString(Path.of(SITE_DIR, "index.html"), indexHtml);
111+
IO.println("Generated index.html with %d cards".formatted(allSnippets.size()));
104112
}
105113

106114
SequencedMap<String, Snippet> loadAllSnippets() throws IOException {
@@ -166,6 +174,14 @@ String renderNavArrows(Snippet snippet) {
166174
return prev + "\n " + next;
167175
}
168176

177+
String renderIndexCard(String tpl, Snippet s) {
178+
return replaceTokens(tpl, Map.ofEntries(
179+
Map.entry("category", s.category()), Map.entry("slug", s.slug()),
180+
Map.entry("catDisplay", s.catDisplay()), Map.entry("title", escape(s.title())),
181+
Map.entry("oldCode", escape(s.oldCode())), Map.entry("modernCode", escape(s.modernCode())),
182+
Map.entry("jdkVersion", s.jdkVersion())));
183+
}
184+
169185
String renderWhyCards(String tpl, JsonNode whyList) {
170186
var cards = new ArrayList<String>();
171187
for (var w : whyList)

html-generators/generate.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
WHY_CARD_TEMPLATE = "templates/why-card.html"
1414
RELATED_CARD_TEMPLATE = "templates/related-card.html"
1515
SOCIAL_SHARE_TEMPLATE = "templates/social-share.html"
16+
INDEX_TEMPLATE = "templates/index.html"
17+
INDEX_CARD_TEMPLATE = "templates/index-card.html"
1618
CONTENT_DIR = "content"
1719
SITE_DIR = "site"
1820

@@ -159,6 +161,20 @@ def _support_badge_class(state):
159161
return {"preview": "preview", "experimental": "experimental"}.get(state, "widely")
160162

161163

164+
def render_index_card(index_card_template, data):
165+
"""Render a single index page preview card."""
166+
cat = data["category"]
167+
return replace_tokens(index_card_template, {
168+
"category": cat,
169+
"slug": data["slug"],
170+
"catDisplay": CATEGORY_DISPLAY[cat],
171+
"title": escape(data["title"]),
172+
"oldCode": escape(data["oldCode"]),
173+
"modernCode": escape(data["modernCode"]),
174+
"jdkVersion": data["jdkVersion"],
175+
})
176+
177+
162178
def generate_html(template, why_card_template, related_card_template,
163179
social_share_template, data, all_snippets):
164180
"""Generate the full HTML page for a snippet by rendering the template."""
@@ -204,6 +220,8 @@ def main():
204220
why_card_template = open(WHY_CARD_TEMPLATE).read()
205221
related_card_template = open(RELATED_CARD_TEMPLATE).read()
206222
social_share_template = open(SOCIAL_SHARE_TEMPLATE).read()
223+
index_template = open(INDEX_TEMPLATE).read()
224+
index_card_template = open(INDEX_CARD_TEMPLATE).read()
207225
all_snippets = load_all_snippets()
208226
print(f"Loaded {len(all_snippets)} snippets")
209227

@@ -234,16 +252,20 @@ def main():
234252

235253
print(f"Rebuilt data/snippets.json with {len(snippets_list)} entries")
236254

237-
# Patch index.html with the current snippet count
255+
# Generate index.html from template
256+
tip_cards = "\n".join(
257+
render_index_card(index_card_template, data)
258+
for data in all_snippets.values()
259+
)
238260
count = len(all_snippets)
239-
index_path = os.path.join(SITE_DIR, "index.html")
240-
with open(index_path) as f:
241-
index_content = f.read()
242-
index_content = index_content.replace("{{snippetCount}}", str(count))
243-
with open(index_path, "w") as f:
244-
f.write(index_content)
245-
246-
print(f"Patched index.html with snippet count: {count}")
261+
index_html = replace_tokens(index_template, {
262+
"tipCards": tip_cards,
263+
"snippetCount": str(count),
264+
})
265+
with open(os.path.join(SITE_DIR, "index.html"), "w") as f:
266+
f.write(index_html)
267+
268+
print(f"Generated index.html with {count} cards")
247269

248270

249271
if __name__ == "__main__":

0 commit comments

Comments
 (0)