Skip to content

Commit ea1cf3c

Browse files
committed
fix nav.js - restore logo, search, errors link
1 parent 676f268 commit ea1cf3c

1 file changed

Lines changed: 86 additions & 167 deletions

File tree

assets/nav.js

Lines changed: 86 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,86 @@
1-
"""
2-
Retries thumbnail generation for any example that failed or was skipped
3-
in the last run of generate_example_thumbnails.py.
4-
5-
An example is considered "needs retry" if:
6-
- It exists in examples_js/ but has no entry in manifest.json at all
7-
(crashed before it could be recorded, or was never attempted)
8-
- Its manifest entry has placeholder=True with reason="non_js_source"
9-
is NOT retried -- that's a real content bug, not a transient failure
10-
- Its manifest entry has placeholder=True without a reason IS retried
11-
if --retry-placeholders flag is passed (off by default, since most
12-
placeholders are intentional skips for data-loading sketches)
13-
14-
Usage:
15-
python3 retry_thumbnails.py
16-
python3 retry_thumbnails.py --retry-placeholders
17-
"""
18-
19-
import os
20-
import sys
21-
import json
22-
import argparse
23-
24-
# ── reuse everything from the main script ──────────────────────────────────
25-
sys.path.insert(0, os.path.dirname(__file__))
26-
import generate_example_thumbnails as gen
27-
from playwright.sync_api import sync_playwright
28-
29-
MANIFEST_PATH = os.path.join(gen.OUT_DIR, "manifest.json")
30-
31-
32-
def load_manifest():
33-
if not os.path.exists(MANIFEST_PATH):
34-
print("No manifest found -- run generate_example_thumbnails.py first.")
35-
return {}
36-
with open(MANIFEST_PATH) as f:
37-
entries = json.load(f)
38-
# Key on (section, slug) matching the new multi-section format
39-
return {
40-
(e.get("section", "Basics"), e["slug"]): e
41-
for e in entries
42-
}
43-
44-
45-
def main():
46-
parser = argparse.ArgumentParser()
47-
parser.add_argument(
48-
"--retry-placeholders",
49-
action="store_true",
50-
help="Also retry intentional placeholder entries (sketches that load "
51-
"external assets). Off by default since most are genuine skips.",
52-
)
53-
args = parser.parse_args()
54-
55-
manifest = load_manifest()
56-
all_examples = gen.discover_examples()
57-
58-
to_retry = []
59-
for ex in all_examples:
60-
key = (ex["section"], ex["slug"])
61-
entry = manifest.get(key)
62-
63-
if entry is None:
64-
# Never made it into the manifest at all -- definite failure
65-
to_retry.append((ex, "not in manifest"))
66-
continue
67-
68-
if entry.get("placeholder"):
69-
reason = entry.get("reason", "")
70-
if reason == "non_js_source":
71-
# Real content bug -- skip, can't fix by retrying
72-
continue
73-
if args.retry_placeholders:
74-
to_retry.append((ex, "placeholder retry requested"))
75-
76-
if not to_retry:
77-
print("Nothing to retry. All examples are in the manifest.")
78-
return
79-
80-
print(f"Retrying {len(to_retry)} example(s):")
81-
for ex, reason in to_retry:
82-
print(f" {ex['section']}/{ex['category']}/{ex['slug']} ({reason})")
83-
print()
84-
85-
# Load the full manifest to update it in place
86-
all_entries = list(manifest.values())
87-
all_keys = set(manifest.keys())
88-
89-
asset_base_url, asset_httpd = gen.start_local_asset_server(gen.ASSETS_DIR)
90-
print(f"Serving assets locally at {asset_base_url}")
91-
92-
succeeded = 0
93-
still_failed = []
94-
95-
try:
96-
with sync_playwright() as p:
97-
browser = p.chromium.launch()
98-
99-
for ex, _ in to_retry:
100-
cat_out_dir = os.path.join(gen.OUT_DIR, ex["section"], ex["category"])
101-
os.makedirs(cat_out_dir, exist_ok=True)
102-
out_path = os.path.join(cat_out_dir, ex["slug"] + ".png")
103-
104-
with open(ex["js_path"], errors="replace") as f:
105-
js_code = gen.strip_comment(f.read())
106-
107-
if gen._non_js_source_re.search(js_code):
108-
print(f" SKIP (unconverted source): {ex['section']}/{ex['slug']}")
109-
continue
110-
111-
if gen._data_loading_re.search(js_code):
112-
svg = gen.make_placeholder_svg(ex["name"], gen.THUMB_SIZE)
113-
with open(out_path.replace(".png", ".svg"), "w") as f:
114-
f.write(svg)
115-
new_entry = {**ex, "thumb": ex["slug"] + ".svg", "placeholder": True}
116-
_upsert(all_entries, all_keys, ex, new_entry)
117-
print(f" placeholder: {ex['section']}/{ex['slug']}")
118-
continue
119-
120-
is_no_canvas = bool(gen._no_canvas_re.search(js_code))
121-
122-
try:
123-
ok = gen.render_thumbnail(
124-
browser, js_code, out_path, asset_base_url,
125-
debug_label=f"{ex['section']}/{ex['category']}/{ex['slug']}",
126-
is_no_canvas=is_no_canvas,
127-
)
128-
if ok:
129-
new_entry = {**ex, "thumb": ex["slug"] + ".png", "placeholder": False}
130-
_upsert(all_entries, all_keys, ex, new_entry)
131-
succeeded += 1
132-
print(f" rendered: {ex['section']}/{ex['slug']}")
133-
else:
134-
still_failed.append(ex["slug"])
135-
print(f" FAILED: {ex['section']}/{ex['slug']}")
136-
except Exception as e:
137-
still_failed.append(ex["slug"])
138-
print(f" FAILED ({e}): {ex['section']}/{ex['slug']}")
139-
140-
browser.close()
141-
finally:
142-
asset_httpd.shutdown()
143-
144-
with open(MANIFEST_PATH, "w") as f:
145-
json.dump(all_entries, f, indent=2)
146-
147-
print()
148-
print(f"Succeeded: {succeeded}")
149-
print(f"Still failed: {len(still_failed)}")
150-
for slug in still_failed:
151-
print(f" - {slug}")
152-
print(f"Manifest updated: {MANIFEST_PATH}")
153-
154-
155-
def _upsert(all_entries, all_keys, ex, new_entry):
156-
"""Replace an existing manifest entry in-place, or append if new."""
157-
key = (ex.get("section", "Basics"), ex["slug"])
158-
for i, e in enumerate(all_entries):
159-
if (e.get("section", "Basics"), e["slug"]) == key:
160-
all_entries[i] = new_entry
161-
return
162-
all_entries.append(new_entry)
163-
all_keys.add(key)
164-
165-
166-
if __name__ == "__main__":
167-
main()
1+
(function() {
2+
const SITE = 'https://processing-cpp.github.io';
3+
const path = window.location.pathname;
4+
const parts = path.replace(/\/$/, '').split('/').filter(Boolean);
5+
const isRoot = parts.length === 0 || (parts.length === 1 && parts[0] === 'index.html');
6+
// Depth = number of directory levels below site root. A trailing
7+
// "index.html" or "<file>.html" segment doesn't count as a directory level.
8+
const hasTrailingFile = parts.length > 0 && parts[parts.length - 1].endsWith('.html');
9+
const depth = isRoot ? 0 : (hasTrailingFile ? parts.length - 1 : parts.length);
10+
const prefix = depth === 0 ? '/' : '../'.repeat(depth);
11+
12+
function isActive(name) { return path.includes('/' + name); }
13+
14+
function link(href, label, style, activeKey) {
15+
const active = isActive(activeKey || href) ? ' class="active"' : '';
16+
const s = style ? ` style="${style}"` : '';
17+
return `<a href="${prefix}${href}"${active}${s}>${label}</a>`;
18+
}
19+
20+
const nav = document.getElementById('site-nav');
21+
if (nav) {
22+
nav.innerHTML = `
23+
<a href="${SITE}" class="nav-logo">
24+
<img src="${prefix}assets/cpp-logo.png" alt="Processing for C++">
25+
<div class="nav-title">
26+
<span class="nav-title-top">Processing</span>
27+
<span class="nav-title-bottom">C++</span>
28+
</div>
29+
</a>
30+
<button class="hamburger" onclick="
31+
var s = document.querySelector('.sidebar-outer, .sidebar');
32+
if(s) s.classList.toggle('open');
33+
">☰</button>
34+
<a href="${prefix}error/index.html" id="nav-errors-link"${isActive('error') ? ' class="active"' : ''}>Errors</a>
35+
`;
36+
}
37+
38+
const sidebar = document.getElementById('site-sidebar') || document.querySelector('.sidebar');
39+
if (sidebar) {
40+
sidebar.innerHTML = `
41+
${link('whats-new', "What's New", 'color:#e8b400;font-weight:700;')}
42+
<div style="height:1px;background:#e0e0e0;margin:0.5rem 0;"></div>
43+
${link('libraries', 'Libraries')}
44+
${link('downloads', 'Downloads')}
45+
${link('tutorials', 'Tutorials')}
46+
${link('reference', 'Reference')}
47+
${link('examples', 'Examples')}
48+
${link('about', 'About')}
49+
`;
50+
}
51+
52+
if (!document.getElementById('nav-shared-style')) {
53+
const style = document.createElement('style');
54+
style.id = 'nav-shared-style';
55+
style.textContent = `
56+
#site-nav {
57+
border-bottom: 1px solid #e0e0e0;
58+
padding: 0 2rem;
59+
display: flex;
60+
align-items: center;
61+
justify-content: space-between;
62+
height: 60px;
63+
position: sticky;
64+
top: 0;
65+
background: #fff;
66+
z-index: 100;
67+
}
68+
.nav-logo { display: flex; align-items: center; gap: 10px; text-decoration: none; }
69+
.nav-logo img { width: 28px; height: 28px; }
70+
.nav-title { display: flex; flex-direction: column; line-height: 1.15; }
71+
.nav-title-top { font-size: 14px; font-weight: 700; color: #e8b400; }
72+
.nav-title-bottom { font-size: 14px; font-weight: 700; color: #e8b400; }
73+
#nav-errors-link { margin-left: auto; font-size: 14px; font-weight: 700; color: #e8b400; text-decoration: none; }
74+
#nav-errors-link:hover { color: #c99700; }
75+
.hamburger { background: none; border: none; cursor: pointer; font-size: 22px; padding: 4px 8px; display: none; margin-left: 0.5rem; }
76+
@media (max-width: 768px) { .hamburger { display: block; } }
77+
`;
78+
document.head.appendChild(style);
79+
}
80+
81+
if (!document.getElementById('search-wrap')) {
82+
const s = document.createElement('script');
83+
s.src = SITE + '/assets/search.js';
84+
document.head.appendChild(s);
85+
}
86+
})();

0 commit comments

Comments
 (0)