(input: { name?: string })
| 319 | /** Scaffold a new theme folder from a neutral starter palette. Returns the slug |
| 320 | * (unique within the themes dir) or null on failure. */ |
| 321 | export async function createCustomTheme(input: { name?: string }): Promise<string | null> { |
| 322 | const dir = await ensureCustomThemesDir() |
| 323 | const name = (input?.name && input.name.trim()) || 'My Theme' |
| 324 | const base = slugify(name) |
| 325 | let slug = base |
| 326 | let n = 2 |
| 327 | while (fsSync.existsSync(path.join(dir, slug))) slug = `${base}-${n++}` |
| 328 | if (path.dirname(path.resolve(path.join(dir, slug))) !== path.resolve(dir)) return null |
| 329 | try { |
| 330 | const manifest = |
| 331 | JSON.stringify( |
| 332 | { name, author: '', version: '1.0.0', description: '', modes: 'both' }, |
| 333 | null, |
| 334 | 2 |
| 335 | ) + '\n' |
| 336 | const css = scaffoldThemeCss({ name, slug, light: NEW_THEME_LIGHT, dark: NEW_THEME_DARK }) |
| 337 | await writeThemeFolder(dir, slug, manifest, css) |
| 338 | return slug |
| 339 | } catch { |
| 340 | return null |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | /** |
| 345 | * Resolve a `zen-theme://<slug>/<relPath>` asset to an absolute file path, |
no test coverage detected