| 223 | } |
| 224 | |
| 225 | func copyStaticAssets(staticFS fs.FS, outputDir string) error { |
| 226 | return fs.WalkDir(staticFS, ".", func(path string, d fs.DirEntry, err error) error { |
| 227 | if err != nil { |
| 228 | return err |
| 229 | } |
| 230 | |
| 231 | if path == "index.html" { |
| 232 | return nil // Skip — we patch and write it separately |
| 233 | } |
| 234 | |
| 235 | dest := filepath.Join(outputDir, path) |
| 236 | |
| 237 | if d.IsDir() { |
| 238 | return os.MkdirAll(dest, 0755) |
| 239 | } |
| 240 | |
| 241 | src, err := staticFS.Open(path) |
| 242 | if err != nil { |
| 243 | return fmt.Errorf("failed to read embedded file %s: %w", path, err) |
| 244 | } |
| 245 | defer src.Close() |
| 246 | |
| 247 | if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { |
| 248 | return err |
| 249 | } |
| 250 | |
| 251 | out, err := os.Create(dest) |
| 252 | if err != nil { |
| 253 | return fmt.Errorf("failed to create %s: %w", dest, err) |
| 254 | } |
| 255 | defer out.Close() |
| 256 | |
| 257 | if _, err := io.Copy(out, src); err != nil { |
| 258 | return fmt.Errorf("failed to copy %s: %w", dest, err) |
| 259 | } |
| 260 | |
| 261 | return nil |
| 262 | }) |
| 263 | } |
| 264 | |
| 265 | // patchIndexHTML injects the fetch interceptor and adjusts paths for static hosting. |
| 266 | func patchIndexHTML(html string, basePath string) string { |