support localhost installation/caching
Hunter
Monday, November 10th, 2025 at 11:59 PM
8 import re
9 from pathlib import Path
10
-def get_configuration():
- """Prompt user for configuration values"""
11 +def get_configuration(localhost=False):
12 + """Prompt user for configuration values
13 +
14 + Args:
15 + localhost: If True, assumes root path and only asks for app name.
16 + This enables PWA installation on localhost (iOS 18+).
17 + """
18 print("=" * 60)
19 print("PWA Configuration")
20 print("=" * 60)
21 print()
22
23 # Get app name
- app_name = input("Enter a name for your mixapp: ").strip()
- if not app_name:
- print("Error: App name is required")
- exit(1)
-
- # Get base path with smart default
- default_path = app_name.lower().replace(" ", "_")
- print()
- print(f"Enter the deployment path (or press Return/Enter for default)")
- print(f"Default: /{default_path}/")
- base_path_input = input("Path: ").strip()
-
- if base_path_input:
- # User provided a path - ensure it has leading/trailing slashes
- base_path = base_path_input
- if not base_path.startswith("/"):
- base_path = "/" + base_path
- if not base_path.endswith("/"):
- base_path = base_path + "/"
24 + if localhost:
25 + app_name = input("Enter a name for your mixapp (or press Return/Enter for 'vibe capsule'): ").strip()
26 + if not app_name:
27 + app_name = "vibe capsule"
28 + print(f"Using default: {app_name}")
29 else:
- # Use default
- base_path = f"/{default_path}/"
- print(f"Using default path: {base_path}")
30 + app_name = input("Enter a name for your mixapp: ").strip()
31 + if not app_name:
32 + print("Error: App name is required")
33 + exit(1)
34 +
35 + # For localhost mode, use root path
36 + if localhost:
37 + base_path = "/"
38 + print()
39 + print("Localhost mode: Using root path for PWA installation")
40 + else:
41 + # Get base path with smart default
42 + default_path = app_name.lower().replace(" ", "_")
43 + print()
44 + print(f"Enter the deployment path (or press Return/Enter for default)")
45 + print(f"Default: /{default_path}/")
46 + base_path_input = input("Path: ").strip()
47 +
48 + if base_path_input:
49 + # User provided a path - ensure it has leading/trailing slashes
50 + base_path = base_path_input
51 + if not base_path.startswith("/"):
52 + base_path = "/" + base_path
53 + if not base_path.endswith("/"):
54 + base_path = base_path + "/"
55 + else:
56 + # Use default
57 + base_path = f"/{default_path}/"
58 + print(f"Using default path: {base_path}")
59
60 print()
61 print(f"Configuration:")
65
66 return app_name, base_path
67
-# Get configuration from user
-APP_NAME, BASE_PATH = get_configuration()
-
-# Derived values (can be manually overridden if desired)
-SHORT_NAME = APP_NAME
-CACHE_NAME = APP_NAME
-APP_DESCRIPTION = f"{APP_NAME} · vibe capsule"
-
68 # File paths (no need to edit these)
69 SCRIPT_DIR = Path(__file__).parent.absolute()
70 TRACKS_JSON = SCRIPT_DIR / "tracks" / "tracks.json"
94 return "#080a0c"
95
96
-def generate_pwa_manifests():
- """Generate PWA manifest files based on tracks.json"""
97 +def generate_pwa_manifests(app_name=None, base_path=None):
98 + """Generate PWA manifest files based on tracks.json
99 +
100 + Args:
101 + app_name: Name of the app. If None, will be prompted via get_configuration()
102 + base_path: Base path for the app. If None, will be prompted via get_configuration()
103 + """
104 + # Get configuration if not provided
105 + if app_name is None or base_path is None:
106 + app_name, base_path = get_configuration()
107 +
108 + # Derived values
109 + short_name = app_name
110 + cache_name = app_name
111 + app_description = f"{app_name} · vibe capsule"
112 +
113 print("Generating PWA manifests...")
114
115 # Load tracks.json
125
126 # Generate manifest.json
127 manifest = {
- "id": BASE_PATH,
- "name": APP_NAME,
- "short_name": SHORT_NAME,
- "description": APP_DESCRIPTION,
- "start_url": BASE_PATH,
- "scope": BASE_PATH,
128 + "id": base_path,
129 + "name": app_name,
130 + "short_name": short_name,
131 + "description": app_description,
132 + "start_url": base_path,
133 + "scope": base_path,
134 "display": "standalone",
135 "background_color": background_color,
136 "theme_color": background_color,
- "cache_name": CACHE_NAME, # Custom field for script.js to use
137 + "cache_name": cache_name, # Custom field for script.js to use
138 "icons": [
139 {
140 "src": "resources/icon.png",
173
174 # Generate service-worker.js
175 static_files = resource_manifest["static_files"]
- service_worker_content = f'''// Auto-generated service worker for {APP_NAME} PWA
-const CACHE_NAME = '{CACHE_NAME}';
176 + service_worker_content = f'''// Auto-generated service worker for {app_name} PWA
177 +const CACHE_NAME = '{cache_name}';
178 const staticFilesToCache = {json.dumps(static_files, indent=2)};
179
180 // Get the base path from the service worker location
306
307
308 if __name__ == "__main__":
- generate_pwa_manifests()
309 + # When run directly, get configuration and generate manifests
310 + app_name, base_path = get_configuration()
311 + generate_pwa_manifests(app_name, base_path)
116 print(f"\nCould not generate QR code: {e}")
117
118
119 +def generate_localhost_manifests():
120 + """Generate PWA manifests for localhost usage"""
121 + try:
122 + # Import the generate_manifests module
123 + sys.path.insert(0, str(SCRIPT_DIR))
124 + from generate_manifests import get_configuration, generate_pwa_manifests
125 +
126 + # Get configuration with localhost mode enabled
127 + print()
128 + app_name, base_path = get_configuration(localhost=True)
129 +
130 + # Generate the manifests
131 + generate_pwa_manifests(app_name, base_path)
132 + print()
133 +
134 + return app_name
135 +
136 + except ImportError as e:
137 + print(f"Error: Could not import generate_manifests.py: {e}")
138 + print("Make sure generate_manifests.py is in the same directory.")
139 + sys.exit(1)
140 + except Exception as e:
141 + print(f"Error generating manifests: {e}")
142 + sys.exit(1)
143 +
144 +
145 def start_server():
146 """Start the HTTP server (runs after venv is set up)"""
147 # Change to script directory
148 os.chdir(SCRIPT_DIR)
149
150 + # Generate manifests for localhost
151 + app_name = generate_localhost_manifests()
152 +
153 # Find an available port
154 port = find_available_port(DEFAULT_PORT)
155
182 network_url = f"http://{local_ip}:{port}"
183
184 print("=" * 60)
- print("💿 vibe capsule")
185 + print(f"💿 {app_name}")
186 print("=" * 60)
187 print(f"\nServer running on port {port}")
188