#!/usr/bin/env python3
"""Orbit's next interface: custom icons, main menu, grid guide and watching screen.
Writes SVG sources, then native VectorCraft icons and FigCraft screens. Programmes shown are fictional.
Yellow stays reserved for remote focus, as in the existing app.
"""
from pathlib import Path
import base64, glob, html, math, os, re, subprocess
P = Path(__file__).resolve().parent
ROOT = P.parent.parent
COLLECTION = ROOT.parent / '2026-10-31/referenced-chatgpt-conversation-this-is-an/Orbit-Channel-Designs'
ENV = dict(os.environ, XDG_DATA_HOME=str(ROOT / '.tooling/font-data'))
NAVY, PANEL, BLUE, FOCUS, MUTED, SILVER = '#071632', '#12356B', '#194C91', '#FFDE59', '#B7CBE8', '#E8EDF5'
(P / 'icons').mkdir(exist_ok=True); (P / 'sources').mkdir(exist_ok=True); (P / 'review').mkdir(exist_ok=True)
def svg(w, h, body):
return f''
def text(x, y, value, size=24, fill='white', weight='400', family='Barlow', anchor='start', extra=''):
return f'{html.escape(value)}'
def rect(x, y, w, h, fill, r=0, extra=''):
return f''
def gear_path():
points, teeth = [], 8
for i in range(teeth * 4):
a = 2 * math.pi * i / (teeth * 4) - math.pi / 8
r = 40 if i % 4 in (0, 1) else 31
points.append(f'{48 + r * math.cos(a):.1f},{48 + r * math.sin(a):.1f}')
return ' '.join(points)
def icon(name, c, accent):
s = f'fill="none" stroke="{c}" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"'
a = f'fill="none" stroke="{accent}" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"'
return {
'all': f'',
'entertainment': f'',
'movies': f'',
'comedy': f'',
'kids': f'',
'documentaries': f'',
'sport': f'',
'gear': f'',
'profile': f'',
'office': f'',
}[name]
ICONS = ['all', 'entertainment', 'movies', 'comedy', 'kids', 'documentaries', 'sport', 'gear', 'office', 'profile']
for name in ICONS:
src = P / 'icons' / f'orbit_icon_{name}.svg'
src.write_text(svg(96, 96, icon(name, SILVER, '#7FB2FF')))
for out, options in ((src.with_suffix('.vectorcraft'), []), (src.with_suffix('.png'), ['--scale', '2'])):
subprocess.run(['vectorcraft-cli', 'convert', str(src), str(out), *options], env=ENV, check=True, stdout=subprocess.DEVNULL)
# Orbit's own mark: the same crescent and orbit every channel carries, in the network's blue and silver.
ORBIT_DEFS = ''
ORBIT_MARK = ''
def orbit_logo(x, y, height):
"""Mark and wordmark, height tall. Drawn 640 by 160."""
return (f'' + ORBIT_DEFS + f'{ORBIT_MARK}' +
text(168, 118, 'ORBIT', 104, 'url(#orbitsilver)', '500', 'Barlow Condensed', extra='letter-spacing="14"') + '')
for name, body in (('orbit_logo', svg(640, 160, orbit_logo(0, 0, 160))),
('orbit_mark', svg(512, 512, ORBIT_DEFS + f'{ORBIT_MARK}'))):
src = P / 'icons' / f'{name}.svg'
src.write_text(body)
for out, options in ((src.with_suffix('.vectorcraft'), []), (src.with_suffix('.png'), [])):
subprocess.run(['vectorcraft-cli', 'convert', str(src), str(out), *options], env=ENV, check=True, stdout=subprocess.DEVNULL)
def use_icon(name, x, y, size, colour=SILVER, accent='#7FB2FF'):
return f'{icon(name, colour, accent)}'
counter = [0]
def logo(folder, x, y, width):
"""Inline a channel's outlined guide logo, with its ids made unique for this page."""
counter[0] += 1
found = glob.glob(str(COLLECTION / folder / 'exports/*_epg.svg'))[0]
s = open(found).read()
inner = s[s.index('>', s.index('