mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 13:16:16 -08:00
Publish "How to Add an Xtream Codes Account to IPTVnator" as the first evergreen guide: what the server URL, username and password are, the Add playlist flow with the connection test and its four verdicts, the Auto-detect method for pasted provider messages, what the import syncs, Account info, refresh, troubleshooting and a seven-question FAQ. The guide is cross-linked from the three download pages and llms.txt. Blog posts gain an optional `faq` frontmatter list: BlogPost.astro renders it as an accordion after the body and emits FAQPage JSON-LD next to the BlogPosting entry. LinkCards and PostButton keep internal links in the same tab. Guide screenshots come from the release capture script: manifest shots may carry a `group`, `--group guides` captures only those into apps/website/public/blog/guides/screenshots/, and a release run skips them. New setup actions open the Add playlist dialog with the mock's fictional Xtream credentials (connection test shown), the Auto-detect method with a labeled hand-out, and the Xtream Live TV view. Dialog helpers and fixture identities move into shared modules so the driver and the navigation actions cannot import each other cyclically. tools/testing/website-guides.test.mjs checks the FAQPage schema, the download-hub link and the shipped screenshots of every guide; screenshot-guards.test.mjs covers group validation and output routing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
22 lines
757 B
TypeScript
22 lines
757 B
TypeScript
import { glob } from 'astro/loaders';
|
|
import { defineCollection, z } from 'astro:content';
|
|
|
|
const blog = defineCollection({
|
|
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: './src/content/blog' }),
|
|
schema: z.object({
|
|
title: z.string(),
|
|
description: z.string(),
|
|
pubDate: z.coerce.date(),
|
|
updatedDate: z.coerce.date().optional(),
|
|
author: z.string().default('4gray'),
|
|
featured: z.boolean().default(false),
|
|
heroImage: z.string().optional(),
|
|
tags: z.array(z.string()).default([]),
|
|
draft: z.boolean().default(false),
|
|
/** Rendered as an accordion after the post and emitted as FAQPage JSON-LD. */
|
|
faq: z.array(z.object({ q: z.string(), a: z.string() })).optional(),
|
|
}),
|
|
});
|
|
|
|
export const collections = { blog };
|