Tactical Media Broadcast Hub — initial release

Self-hosted multi-platform broadcast newsroom for activist collectives:
publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style
column wall with hostile-content-safe rendering, live control room with Auto
Director, bridged chat governance with polls and quorum, distributed sandboxed
transcode workers, encrypted content-addressed archive, monthly analytics.

MIT licensed. See README.md and docs/.
This commit is contained in:
glass beadian 2026-10-10 11:56:42 +00:00
commit aa5e262f3a
880 changed files with 111042 additions and 0 deletions

View file

@ -0,0 +1,75 @@
import { chromium } from "playwright";
import { mkdirSync } from "node:fs";
const BASE = process.env.QA_BASE ?? "http://localhost:3111";
const OUT = "runs/qa";
mkdirSync(OUT, { recursive: true });
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
async function shot(name, { fullPage = false } = {}) {
await page.waitForTimeout(350);
await page.screenshot({ path: `${OUT}/${name}.png`, fullPage });
console.log(`✓ ${name}`);
}
console.log("console errors will be collected at the end");
const errors = [];
page.on("console", (msg) => {
if (msg.type() === "error") errors.push(msg.text().slice(0, 200));
});
page.on("pageerror", (err) => errors.push(`PAGEERROR: ${String(err).slice(0, 200)}`));
await page.goto(`${BASE}/streams`);
await shot("01-streams");
await page.keyboard.press("j");
await page.keyboard.press("j");
await page.keyboard.press("j");
await shot("02-streams-selected");
await page.keyboard.press("r");
await shot("03-streams-repost-toast");
await page.keyboard.press("q");
await shot("04-streams-quote-popover");
await page.keyboard.press("Escape");
await page.keyboard.press("j");
await page.keyboard.press("k");
await page.keyboard.press("k");
await shot("05-streams-keyboard-moved");
await page.goto(`${BASE}/compose`);
await page.locator(".ProseMirror").click();
await page.keyboard.type("Shipping the visual QA pass — capability-gated amplify, keyboard-first streams.");
await shot("06-compose-typed");
await page.goto(`${BASE}/connections`);
await shot("07-connections");
await page.goto(`${BASE}/inbox`);
await shot("08-inbox");
await page.goto(`${BASE}/team`);
await shot("09-team");
await page.goto(`${BASE}/audit`);
await shot("10-audit");
await page.locator("tr", { hasText: "credential.rotated" }).first().click();
await shot("11-audit-diff-open");
await page.goto(`${BASE}/onboarding`);
await shot("12-onboarding-step1");
await page.getByPlaceholder("Studio, Newsroom, Acme Social…").fill("Studio");
await page.getByRole("button", { name: "Continue" }).click();
await shot("13-onboarding-step2");
await page.goto(`${BASE}/streams`);
await page.keyboard.press("Meta+k");
await shot("14-command-palette");
await browser.close();
console.log(`\nconsole errors: ${errors.length}`);
for (const e of errors.slice(0, 10)) console.log(` - ${e}`);