Skip to content

Commit 1c06eda

Browse files
authored
Merge pull request #64 from cloudtoid/codex/favicon-no-outline
2 parents 27256ae + b0ca0fc commit 1c06eda

23 files changed

Lines changed: 823 additions & 159 deletions

‎.github/workflows/website.yml‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,8 @@ jobs:
1717
with:
1818
python-version: '3.14'
1919
- run: python src/website/build.py
20-
- run: node --check src/website/site.js
20+
- run: python src/website/validate.py
21+
- run: |
22+
node --check src/website/site.js
23+
node --check src/website/theme.js
24+
node --check src/website/docs.js

‎src/website/.openai/hosting.json‎

Lines changed: 0 additions & 1 deletion
This file was deleted.

‎src/website/README.md‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
# Interprocess website
22

3-
A static landing page with language examples and links to the library and protocol. No runtime framework or external dependencies.
3+
A static landing page and developer documentation for all six language APIs. No runtime framework or external dependencies.
44

55
```sh
66
python3 src/website/build.py
77
python3 -m http.server --directory src/website/dist
88
```
99

10-
The Website workflow validates the static build on pull requests and main. The public site at cloudtoid.com uses Sites hosting; `.openai/hosting.json` identifies it. Keep registry availability and the preview/release notice accurate when publishing packages.
10+
Cloudflare Pages publishes this site directly from `cloudtoid/interprocess`. Changes under `src/website/**` on `main` trigger production deployments; other branches receive previews linked from GitHub. Project: `cloudtoid`; build command: `python3 src/website/build.py`; output directory: `src/website/dist`. The Website GitHub Actions workflow also validates the build and JavaScript syntax. Failed builds do not replace the last successful deployment.
11+
12+
Keep package availability and installation commands current when releasing packages. Benchmarks change only after new measurements.
1113

1214
The header and footer use the official blue wordmarks from [cloudtoid/assets](https://github.com/cloudtoid/assets/tree/master/logos), served locally. The black and white variants follow the selected color theme.
15+
16+
## Developer documentation
17+
18+
`docs/pages.json` defines page titles, descriptions, navigation, and URLs. Edit the corresponding HTML fragments under `docs/`; `docs/template.html` supplies the shared layout. `build.py` renders the pages into `dist/docs/`, creates the sitemap and robots.txt, and adds canonical, social, and structured metadata. API content is authored against the public implementations; it is not generated from source comments. Update the reference alongside API changes, including waiting, error, ownership, and truncation behavior. Link to generated ecosystem references where available.
19+
20+
Run `python3 src/website/validate.py` after building to check local links, anchors, metadata, and sitemap coverage. CI runs this check. All reference text and navigation work without JavaScript; `docs.js` enhances code blocks with highlighting and copy buttons. The homepage's language guide links point to these pages.
21+
22+
The social preview uses `assets/social-card.png`; its editable SVG source is alongside it. SEO metadata uses `https://cloudtoid.com` as the canonical origin. Publishing makes the sitemap available at `/sitemap.xml`; search-engine indexing happens independently of deployment.

‎src/website/assets/social-card.png‎

50.6 KB
Loading

‎src/website/assets/social-card.svg‎

Lines changed: 11 additions & 0 deletions
Loading

‎src/website/build.py‎

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,94 @@
1-
"""Build the static site for Sites, without runtime dependencies."""
1+
"""Build the static marketing site and developer reference, without dependencies."""
2+
from html import escape
3+
import json
24
from pathlib import Path
5+
import re
36
import shutil
7+
from string import Template
8+
49
root = Path(__file__).resolve().parent
510
output = root / 'dist'
611
output.mkdir(exist_ok=True)
7-
for name in ('index.html', 'style.css', 'site.js', 'theme.js'):
12+
base = 'https://cloudtoid.com'
13+
pages = json.loads((root / 'docs/pages.json').read_text())
14+
home = (root / 'index.html').read_text()
15+
favicon = re.search(r'<link rel="icon"[^>]+>', home).group()
16+
17+
18+
def metadata(title, description, path, kind='website'):
19+
url = base + path
20+
tags = [f'<link rel="canonical" href="{url}">']
21+
for key, value in {
22+
'og:type': kind, 'og:site_name': 'Cloudtoid Interprocess',
23+
'og:title': title, 'og:description': description, 'og:url': url,
24+
'og:image': base + '/assets/social-card.png',
25+
'og:image:width': '1200', 'og:image:height': '630',
26+
'og:image:alt': 'Cloudtoid Interprocess: fast shared-memory queues across six languages',
27+
}.items():
28+
tags.append(f'<meta property="{key}" content="{escape(value, quote=True)}">')
29+
tags.append('<meta name="twitter:card" content="summary_large_image">')
30+
return '\n'.join(tags)
31+
32+
33+
home_title = re.search(r'<title>(.*?)</title>', home).group(1)
34+
home_description = re.search(r'<meta name="description" content="([^"]+)"', home).group(1)
35+
home = re.sub(r'<link rel="canonical"[^>]+>', metadata(home_title, home_description, '/'), home)
36+
software = {
37+
'@context': 'https://schema.org', '@type': 'SoftwareSourceCode',
38+
'name': 'Cloudtoid Interprocess', 'url': base + '/',
39+
'description': home_description,
40+
'codeRepository': 'https://github.com/cloudtoid/interprocess',
41+
'programmingLanguage': ['Rust', 'C', 'Python', 'JavaScript', 'Go', 'C#'],
42+
'runtimePlatform': ['Linux', 'macOS', 'Windows'],
43+
'license': 'https://github.com/cloudtoid/interprocess/blob/main/LICENSE',
44+
}
45+
home = home.replace('</head>', '<script type="application/ld+json">' + json.dumps(software) + '</script>\n</head>')
46+
(output / 'index.html').write_text(home)
47+
for name in ('style.css', 'site.js', 'theme.js', 'docs.css', 'docs.js'):
848
shutil.copyfile(root / name, output / name)
949
for name in ('assets', 'benchmarks', 'vendor'):
1050
shutil.copytree(root / name, output / name, dirs_exist_ok=True)
11-
print(f'Built {output}')
51+
52+
53+
def page_path(page):
54+
return '/docs/' + (page['slug'] + '/' if page['slug'] else '')
55+
56+
57+
template = Template((root / 'docs/template.html').read_text())
58+
for page in pages:
59+
path = page_path(page)
60+
content = (root / 'docs' / ((page['slug'] or 'overview') + '.html')).read_text()
61+
navigation = ''.join(
62+
'<a href="{}"{}>{}</a>'.format(page_path(item), ' aria-current="page"' if item == page else '', escape(item['label']))
63+
for item in pages
64+
)
65+
headings = re.findall(r'<h2 id="([^"]+)">(.*?)</h2>', content)
66+
content = re.sub(r'<h([23]) id="([^"]+)">(.*?)</h\1>',
67+
lambda match: f'<h{match[1]} id="{match[2]}"><a class="heading-link" href="#{match[2]}">{match[3]}</a></h{match[1]}>', content)
68+
toc = ''.join(f'<a href="#{key}">{title}</a>' for key, title in headings)
69+
breadcrumbs = [{'@type': 'ListItem', 'position': 1, 'name': 'Documentation', 'item': base + '/docs/'}]
70+
if page['slug']:
71+
breadcrumbs.append({'@type': 'ListItem', 'position': 2, 'name': page['title'], 'item': base + path})
72+
data = [
73+
{'@context': 'https://schema.org', '@type': 'TechArticle', 'headline': page['title'],
74+
'description': page['description'], 'url': base + path, 'inLanguage': 'en',
75+
'author': {'@type': 'Organization', 'name': 'Cloudtoid', 'url': base + '/'}},
76+
{'@context': 'https://schema.org', '@type': 'BreadcrumbList', 'itemListElement': breadcrumbs},
77+
]
78+
rendered = template.substitute(
79+
title=escape(page['title']), description=escape(page['description'], quote=True),
80+
metadata=metadata(page['title'] + ' | Cloudtoid Interprocess', page['description'], path, 'article')
81+
+ '\n<script type="application/ld+json">' + json.dumps(data) + '</script>',
82+
favicon=favicon, navigation=navigation, toc=toc, content=content,
83+
breadcrumb=('<span>/</span><span>' + escape(page['label']) + '</span>') if page['slug'] else '',
84+
)
85+
target = output / path.strip('/') / 'index.html'
86+
target.parent.mkdir(parents=True, exist_ok=True)
87+
target.write_text(rendered)
88+
89+
urls = [base + '/'] + [base + page_path(page) for page in pages]
90+
(output / 'sitemap.xml').write_text('<?xml version="1.0" encoding="UTF-8"?>\n'
91+
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
92+
+ '\n'.join(f'<url><loc>{url}</loc></url>' for url in urls) + '\n</urlset>\n')
93+
(output / 'robots.txt').write_text('User-agent: *\nAllow: /\n\nSitemap: ' + base + '/sitemap.xml\n')
94+
print(f'Built {output}: homepage and {len(pages)} documentation pages')

‎src/website/docs.css‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
.docs-layout { display: grid; grid-template-columns: 170px minmax(0, 1fr) 145px; gap: 38px; padding-top: 40px; padding-bottom: 70px; }
2+
.docs-sidebar, .docs-toc { align-self: start; position: sticky; top: 24px; max-height: calc(100vh - 48px); overflow-y: auto; }
3+
.docs-sidebar .eyebrow, .docs-toc .eyebrow { font-size: 10px; margin-bottom: 16px; }
4+
.docs-sidebar nav, .docs-toc nav { display: flex; flex-direction: column; align-items: stretch; gap: 3px; }
5+
.docs-sidebar nav a { display: block; padding: 7px 10px; border-radius: 5px; color: var(--muted); font-size: 14px; }
6+
.docs-sidebar a[aria-current=page] { background: var(--hero-background); color: var(--accent); font-weight: 650; }
7+
.docs-sidebar .protocol-link { display: block; margin-top: 25px; padding-top: 18px; border-top: 1px solid var(--line); color: var(--muted); font-size: 12px; }
8+
.docs-toc a { font-size: 12px; color: var(--muted); padding: 4px 0; line-height: 1.5; }
9+
.doc-content { min-width: 0; }
10+
.doc-breadcrumb { display: flex; flex-wrap: wrap; gap: 9px; font-size: 12px; color: var(--muted); margin-bottom: 24px; }
11+
.doc-content h1 { font-size: clamp(32px, 3.5vw, 45px); letter-spacing: -.045em; line-height: 1.15; margin-bottom: 20px; }
12+
.doc-content h2 { font-size: 25px; letter-spacing: -.7px; margin: 48px 0 20px; padding-top: 8px; border-top: 1px solid var(--line); }
13+
.doc-content h3 { font-size: 16px; font-weight: 600; margin: 26px 0 10px; line-height: 1.7; scroll-margin-top: 25px; }
14+
.doc-content h3 code { background: var(--soft); color: var(--ink); padding: 3px 5px; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
15+
.doc-content p, .doc-content li, .doc-content dd { font-size: 15px; line-height: 1.8; color: var(--muted); }
16+
.doc-content .doc-lead { font-size: 19px; line-height: 1.65; margin-bottom: 22px; }
17+
.doc-content .doc-source { font-size: 12px; }
18+
.doc-content a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
19+
.doc-content code { font: .86em/1.7 var(--mono); overflow-wrap: anywhere; }
20+
.doc-content p code, .doc-content li code, .api-list dt code { background: var(--soft); color: var(--ink); border-radius: 3px; padding: 2px 4px; }
21+
.doc-content pre { min-height: 0; font-size: 13px; padding: 22px; margin: 20px 0; border: 1px solid var(--line); border-radius: 7px; background: var(--surface); line-height: 1.75; }
22+
.doc-content pre code { font-size: inherit; overflow-wrap: normal; }
23+
.doc-code { position: relative; }
24+
.doc-code pre { padding-top: 47px; }
25+
.doc-copy { position: absolute; top: 10px; right: 10px; border: 1px solid var(--line); border-radius: 4px; padding: 3px 9px; font-size: 11px; color: var(--muted); background: var(--soft); cursor: pointer; }
26+
.doc-copy:hover { color: var(--accent); border-color: var(--accent); }
27+
.doc-note { padding: 18px 20px; margin: 24px 0; border-left: 3px solid var(--accent); background: var(--hero-background); border-radius: 0 6px 6px 0; font-size: 15px; color: var(--ink); }
28+
.doc-note strong { display: block; }
29+
.doc-table { overflow-x: auto; border: 1px solid var(--line); border-radius: 6px; margin: 24px 0; }
30+
.doc-table td, .doc-table th { padding: 12px 15px; }
31+
.doc-table td { font-size: 13px; font-family: inherit; font-weight: 400; line-height: 1.6; white-space: normal; }
32+
.api-list dt { margin-top: 15px; font-weight: 600; }
33+
.api-list dd { margin: 5px 0 16px; }
34+
.doc-cards { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 14px; }
35+
.doc-cards a { display: flex; flex-direction: column; gap: 10px; padding: 20px; background: var(--surface); border: 1px solid var(--line); border-radius: 7px; text-decoration: none; }
36+
.doc-cards a:hover { border-color: var(--accent); background: var(--soft); }
37+
.doc-cards strong { font-size: 17px; color: var(--ink); }
38+
.doc-cards span { font-size: 13px; color: var(--muted); }
39+
.doc-cards code { font-size: 11px; margin-top: auto; }
40+
.doc-bottom { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 18px; margin-top: 55px; padding-top: 20px; border-top: 1px solid var(--line); font-size: 12px; }
41+
.docs-page header nav a { display: inline; }
42+
@media (max-width:1100px) {
43+
.docs-layout { grid-template-columns: 155px minmax(0,1fr); gap: 28px; }
44+
.docs-toc { display: none; }
45+
}
46+
@media (max-width:800px) {
47+
.docs-layout { grid-template-columns: 1fr; padding-top: 25px; }
48+
.docs-sidebar { position: static; max-height: none; padding-bottom: 20px; border-bottom: 1px solid var(--line); }
49+
.docs-sidebar nav { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 4px; }
50+
.docs-sidebar nav a { display: block; padding: 6px; font-size: 12px; }
51+
.docs-sidebar .protocol-link { display: none; }
52+
.docs-sidebar .eyebrow { margin-bottom: 10px; }
53+
}
54+
@media (max-width:520px) {
55+
.docs-page header nav a:nth-child(2), .docs-page header nav a:nth-child(3) { display: none; }
56+
.doc-cards { grid-template-columns: 1fr; }
57+
.doc-content h1 { font-size: 34px; }
58+
.doc-content pre { font-size: 12px; padding-left: 14px; padding-right: 14px; }
59+
.doc-content .doc-lead { font-size: 17px; }
60+
}
61+
62+
.doc-content .heading-link { color: inherit; text-decoration: none; }
63+
.doc-content .heading-link:hover { text-decoration: underline; }

‎src/website/docs.js‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
document.querySelectorAll('.doc-content pre').forEach((pre, index) => {
2+
const code = pre.querySelector('code');
3+
if (!code) return;
4+
if (window.hljs) hljs.highlightElement(code);
5+
const wrapper = document.createElement('div');
6+
wrapper.className = 'doc-code';
7+
pre.before(wrapper);
8+
wrapper.append(pre);
9+
const button = document.createElement('button');
10+
button.type = 'button';
11+
button.className = 'doc-copy';
12+
button.textContent = 'Copy';
13+
button.setAttribute('aria-label', `Copy code example ${index + 1}`);
14+
const status = document.createElement('span');
15+
status.className = 'sr-only';
16+
status.setAttribute('role', 'status');
17+
wrapper.append(button, status);
18+
button.addEventListener('click', async () => {
19+
try {
20+
await navigator.clipboard.writeText(code.textContent);
21+
button.textContent = 'Copied';
22+
status.textContent = 'Code copied to clipboard';
23+
} catch {
24+
button.textContent = 'Select to copy';
25+
status.textContent = 'Clipboard unavailable. Select the code to copy it.';
26+
}
27+
});
28+
});

0 commit comments

Comments
 (0)