Spaces:
Runtime error
Runtime error
Commit ·
f1ab9e4
1
Parent(s): 9499163
LETS GOOOOOOOOOOOOOOOOOOOOOOOOO
Browse files- frontend/index.html +6 -1
- frontend/public/site.webmanifest +14 -0
- frontend/scripts/make_favicons.py +77 -0
frontend/index.html
CHANGED
|
@@ -5,7 +5,12 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<meta name="theme-color" content="#000000" />
|
| 7 |
<meta name="description" content="Notestack turns any blog, newsletter, site or markdown files into a grounded research notebook, audio overviews, videos and a launch kit, all in your own voice." />
|
| 8 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
<!-- Link previews (X, LinkedIn, Bluesky, Threads, Facebook, Slack, Discord, iMessage). %SITE_URL% is filled in at build. -->
|
| 10 |
<meta property="og:type" content="website" />
|
| 11 |
<meta property="og:site_name" content="Notestack" />
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<meta name="theme-color" content="#000000" />
|
| 7 |
<meta name="description" content="Notestack turns any blog, newsletter, site or markdown files into a grounded research notebook, audio overviews, videos and a launch kit, all in your own voice." />
|
| 8 |
+
<!-- Favicons: regenerate with python frontend/scripts/make_favicons.py after changing public/logo.svg. -->
|
| 9 |
+
<link rel="icon" href="/favicon.ico" sizes="48x48" />
|
| 10 |
+
<link rel="icon" href="/logo.svg" type="image/svg+xml" />
|
| 11 |
+
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32" />
|
| 12 |
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
| 13 |
+
<link rel="manifest" href="/site.webmanifest" />
|
| 14 |
<!-- Link previews (X, LinkedIn, Bluesky, Threads, Facebook, Slack, Discord, iMessage). %SITE_URL% is filled in at build. -->
|
| 15 |
<meta property="og:type" content="website" />
|
| 16 |
<meta property="og:site_name" content="Notestack" />
|
frontend/public/site.webmanifest
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "Notestack",
|
| 3 |
+
"short_name": "Notestack",
|
| 4 |
+
"description": "Your knowledge, in orbit. The ultimate NotebookLM alternative for writers.",
|
| 5 |
+
"start_url": "/app",
|
| 6 |
+
"display": "standalone",
|
| 7 |
+
"background_color": "#000000",
|
| 8 |
+
"theme_color": "#000000",
|
| 9 |
+
"icons": [
|
| 10 |
+
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
| 11 |
+
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" },
|
| 12 |
+
{ "src": "/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
| 13 |
+
]
|
| 14 |
+
}
|
frontend/scripts/make_favicons.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Favicons from the logo mark (public/logo.svg), rendered with the renderer's headless Chrome.
|
| 2 |
+
|
| 3 |
+
Writes to public/: favicon.ico (16, 32, 48), favicon-32.png, apple-touch-icon.png (180, full bleed:
|
| 4 |
+
iOS rounds the corners itself), icon-192.png, icon-512.png and icon-maskable-512.png (planet inside
|
| 5 |
+
Android's 80% safe zone). Run from the repo root: python frontend/scripts/make_favicons.py
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import struct
|
| 9 |
+
import subprocess
|
| 10 |
+
import tempfile
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
ROOT = Path(__file__).resolve().parents[2]
|
| 14 |
+
PUBLIC = ROOT / "frontend" / "public"
|
| 15 |
+
CHROME = next((ROOT / "renderer" / "node_modules" / ".remotion").rglob("chrome-headless-shell.exe"))
|
| 16 |
+
MARK = (PUBLIC / "logo.svg").read_text(encoding="utf-8")
|
| 17 |
+
|
| 18 |
+
# The planet and sparkle without the rounded tile, to place on a full bleed square.
|
| 19 |
+
ART = """
|
| 20 |
+
<circle cx="26" cy="36" r="13" fill="#000000"/>
|
| 21 |
+
<ellipse cx="26" cy="36" rx="21" ry="5" fill="none" stroke="#000000" stroke-width="3" transform="rotate(-18 26 36)"/>
|
| 22 |
+
<path d="M46 10 l2.2 5.8 5.8 2.2 -5.8 2.2 -2.2 5.8 -2.2 -5.8 -5.8 -2.2 5.8 -2.2z" fill="#000000"/>
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def full_bleed(scale: float) -> str:
|
| 27 |
+
"""White square, artwork scaled about the centre (iOS and Android apply their own masks)."""
|
| 28 |
+
off = 32 * (1 - scale)
|
| 29 |
+
return (
|
| 30 |
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">'
|
| 31 |
+
'<rect width="64" height="64" fill="#ffffff"/>'
|
| 32 |
+
f'<g transform="translate({off} {off}) scale({scale})">{ART}</g></svg>'
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def render(svg: str, size: int, out: Path) -> bytes:
|
| 37 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 38 |
+
page = Path(tmp) / "icon.html"
|
| 39 |
+
page.write_text(
|
| 40 |
+
"<!doctype html><html><body style='margin:0;background:transparent'>"
|
| 41 |
+
f"<div style='width:{size}px;height:{size}px'>{svg.replace('<svg ', f'<svg width=\"{size}\" height=\"{size}\" ', 1)}</div>"
|
| 42 |
+
"</body></html>",
|
| 43 |
+
encoding="utf-8",
|
| 44 |
+
)
|
| 45 |
+
shot = Path(tmp) / "shot.png"
|
| 46 |
+
subprocess.run(
|
| 47 |
+
[str(CHROME), "--headless", "--disable-gpu", "--hide-scrollbars", "--force-device-scale-factor=1",
|
| 48 |
+
"--default-background-color=00000000", f"--window-size={size},{size}",
|
| 49 |
+
f"--screenshot={shot}", page.as_uri()],
|
| 50 |
+
check=True, capture_output=True,
|
| 51 |
+
)
|
| 52 |
+
data = shot.read_bytes()
|
| 53 |
+
out.write_bytes(data)
|
| 54 |
+
return data
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def ico(images: dict[int, bytes]) -> bytes:
|
| 58 |
+
"""ICO with PNG payloads (supported by every browser since IE Vista era)."""
|
| 59 |
+
header = struct.pack("<HHH", 0, 1, len(images))
|
| 60 |
+
entries, blobs, offset = b"", b"", 6 + 16 * len(images)
|
| 61 |
+
for size, png in sorted(images.items()):
|
| 62 |
+
entries += struct.pack("<BBBBHHII", size % 256, size % 256, 0, 0, 1, 32, len(png), offset)
|
| 63 |
+
blobs += png
|
| 64 |
+
offset += len(png)
|
| 65 |
+
return header + entries + blobs
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
tmp_dir = Path(tempfile.mkdtemp())
|
| 69 |
+
small = {s: render(MARK, s, tmp_dir / f"f{s}.png") for s in (16, 32, 48)}
|
| 70 |
+
(PUBLIC / "favicon.ico").write_bytes(ico(small))
|
| 71 |
+
(PUBLIC / "favicon-32.png").write_bytes(small[32])
|
| 72 |
+
render(full_bleed(0.86), 180, PUBLIC / "apple-touch-icon.png")
|
| 73 |
+
render(MARK, 192, PUBLIC / "icon-192.png")
|
| 74 |
+
render(MARK, 512, PUBLIC / "icon-512.png")
|
| 75 |
+
render(full_bleed(0.7), 512, PUBLIC / "icon-maskable-512.png")
|
| 76 |
+
for name in ("favicon.ico", "favicon-32.png", "apple-touch-icon.png", "icon-192.png", "icon-512.png", "icon-maskable-512.png"):
|
| 77 |
+
print(name, (PUBLIC / name).stat().st_size)
|