Add visual companion Prime Radiant branding

This commit is contained in:
Drew Ritter
2026-06-15 16:32:55 -07:00
committed by Jesse Vincent
parent 985434ddb0
commit 529e192c32
8 changed files with 417 additions and 60 deletions

View File

@@ -0,0 +1,309 @@
/**
* Tests for the visual companion's Superpowers/Prime Radiant branding.
*/
const { spawn } = require('child_process');
const http = require('http');
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const REPO_ROOT = path.join(__dirname, '../..');
const SERVER_PATH = path.join(REPO_ROOT, 'skills/brainstorming/scripts/server.cjs');
const PACKAGE_VERSION = JSON.parse(
fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf-8')
).version;
const TOKEN = 'testtoken-branding-0123456789abcdef';
const ASSET_URL = 'https://primeradiant.com/brand/superpowers-visual-brainstorming-logo.png';
function cleanup(dir) {
if (fs.existsSync(dir)) {
fs.rmSync(dir, { recursive: true });
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function startServer({ port, dir, env = {} }) {
cleanup(dir);
return spawn('node', [SERVER_PATH], {
env: {
...process.env,
BRAINSTORM_PORT: String(port),
BRAINSTORM_DIR: dir,
BRAINSTORM_TOKEN: TOKEN,
...env
}
});
}
function waitForServer(server) {
let stdout = '';
let stderr = '';
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error(`Server did not start. stderr: ${stderr}`)), 5000);
server.stdout.on('data', (data) => {
stdout += data.toString();
if (stdout.includes('server-started')) {
clearTimeout(timeout);
resolve();
}
});
server.stderr.on('data', (data) => { stderr += data.toString(); });
server.on('error', reject);
});
}
function fetchHtml(port) {
return new Promise((resolve, reject) => {
const headers = { Cookie: `brainstorm-key-${port}=${TOKEN}` };
http.get(`http://localhost:${port}/`, { headers }, (res) => {
let body = '';
res.on('data', chunk => { body += chunk; });
res.on('end', () => resolve(body));
}).on('error', reject);
});
}
function writeFragment(dir) {
const contentDir = path.join(dir, 'content');
fs.mkdirSync(contentDir, { recursive: true });
fs.writeFileSync(path.join(contentDir, 'screen.html'), '<h2>Pick a layout</h2>');
}
async function withServer(options, fn) {
const server = startServer(options);
try {
await waitForServer(server);
await fn();
} finally {
if (server.exitCode === null && server.signalCode === null) {
server.kill();
await new Promise(resolve => server.once('exit', resolve));
}
await sleep(100);
cleanup(options.dir);
}
}
let passed = 0;
let failed = 0;
async function test(name, fn) {
try {
await fn();
console.log(` PASS: ${name}`);
passed++;
} catch (e) {
console.log(` FAIL: ${name}`);
console.log(` ${e.message}`);
failed++;
}
}
function assertBrandedWithLogo(html) {
assert(
html.includes(`Superpowers v${PACKAGE_VERSION}`),
'branding text should include dynamic package version'
);
assert(
!html.includes(`Superpowers v${PACKAGE_VERSION} by`),
'branding text should not include "by" when the logo is visible'
);
assert(
/<img class="brand-logo"[^>]*>\s*<span class="brand-copy">Superpowers v/.test(html),
'visible logo should appear before the Superpowers version text'
);
assert(
/\.brand a\s*\{[^}]*line-height:\s*1/i.test(html),
'brand row should align the logo and version text by their visual height'
);
assert(
/\.brand a\s*\{[^}]*gap:\s*0\.5rem/i.test(html),
'brand row should keep the logo and version text close together'
);
assert(
/\.brand a\s*\{[^}]*max-width:\s*100%/i.test(html),
'brand link should be constrained so it cannot overlap the status column'
);
assert(
/\.brand\s*\{[^}]*line-height:\s*1/i.test(html),
'brand wrapper should not inherit the page line height'
);
assert(
/\.brand\s*\{[^}]*overflow:\s*hidden/i.test(html),
'brand wrapper should clip before it reaches the status column'
);
}
function assertBrandedFallbackText(html) {
assert(
html.includes(`Prime Radiant Superpowers v${PACKAGE_VERSION}`),
'disabled telemetry should keep plain text Prime Radiant/Superpowers branding'
);
}
function assertTelemetryImage(html) {
const expectedUrl = `${ASSET_URL}?v=${encodeURIComponent(PACKAGE_VERSION)}`;
assert(html.includes(`src="${expectedUrl}"`), 'remote image should use the dedicated main-domain asset with only v=');
assert(!html.includes('event='), 'remote image URL must not include event=');
assert(!html.includes('surface='), 'remote image URL must not include surface=');
assert(!html.includes('launch_id='), 'remote image URL must not include launch_id=');
assert(!html.includes('lid='), 'remote image URL must not include lid=');
}
function assertLogoKeepsTransparentBackground(html) {
assert(
/\.brand-logo\s*\{[^}]*height:\s*1em/i.test(html),
'logo should match the surrounding brand text size'
);
assert(
/\.brand-logo\s*\{[^}]*display:\s*block/i.test(html),
'logo should not reserve inline-image descender space'
);
assert(
/\.brand-copy\s*\{[^}]*line-height:\s*1/i.test(html),
'version text should use the same compact line height as the logo'
);
assert(
/\.brand-copy\s*\{[^}]*min-width:\s*0/i.test(html),
'version text should be allowed to shrink inside the brand row'
);
assert(
/\.brand-copy\s*\{[^}]*transform:\s*translateY\(-1px\)/i.test(html),
'version text should compensate for bottom padding inside the logo asset'
);
assert(
/\.brand-logo\s*\{[^}]*filter:\s*invert\(1\)/i.test(html),
'white logo asset should invert on light backgrounds'
);
assert(
!/\.brand-logo\s*\{[^}]*background:/i.test(html),
'logo should keep its transparent background'
);
assert(
!/\.brand-logo\s*\{[^}]*padding:/i.test(html),
'logo should not rely on a padded backing'
);
}
function assertFramedLogoSupportsDarkTheme(html) {
assert(
/@media\s*\(prefers-color-scheme:\s*dark\)[\s\S]*\.brand-logo\s*\{[^}]*filter:\s*none/i.test(html),
'framed screens should leave the white logo unfiltered in dark mode'
);
}
function assertFramedScreenUsesBrandHeader(html) {
const logoCount = (html.match(/class="brand-logo"/g) || []).length;
assert.strictEqual(logoCount, 1, 'framed screens should render the logo only in the header');
assert(!html.includes('<div class="indicator-bar">'), 'framed screens should not render footer chrome');
assert(
/<div class="header">[\s\S]*<div class="brand">[\s\S]*<div class="status">Connecting…<\/div>/.test(html),
'header should contain branding and connection status'
);
assert(!html.includes('id="indicator-text"'), 'header should not render the selection indicator text');
assert(!html.includes('Click an option above'), 'header should not render the selection instruction');
}
function assertHeaderAvoidsNarrowOverlap(html) {
assert(
/grid-template-columns:\s*minmax\(0,\s*1fr\)\s*auto/i.test(html),
'header should allocate shrinkable space to branding before the status column'
);
assert(
/\.header \.status\s*\{[^}]*grid-column:\s*2/i.test(html),
'status should live in the final fixed-width grid column'
);
assert(
/\.header \.brand\s*\{[^}]*width:\s*100%/i.test(html),
'header brand should fill its grid track so overflow clipping prevents overlap'
);
}
async function main() {
console.log('\n--- Visual Companion Branding ---');
await test('framed screens render versioned Prime Radiant logo by default', async () => {
const port = 3451;
const dir = '/tmp/brainstorm-branding-default';
await withServer({ port, dir }, async () => {
writeFragment(dir);
await sleep(300);
const html = await fetchHtml(port);
assertBrandedWithLogo(html);
assertTelemetryImage(html);
assertLogoKeepsTransparentBackground(html);
assertFramedLogoSupportsDarkTheme(html);
assertFramedScreenUsesBrandHeader(html);
assertHeaderAvoidsNarrowOverlap(html);
});
});
await test('waiting screen renders versioned Prime Radiant logo by default', async () => {
const port = 3452;
const dir = '/tmp/brainstorm-branding-waiting';
await withServer({ port, dir }, async () => {
const html = await fetchHtml(port);
assert(html.includes('Waiting for the agent'), 'waiting page should still render');
assertBrandedWithLogo(html);
assertTelemetryImage(html);
assertLogoKeepsTransparentBackground(html);
});
});
await test('SUPERPOWERS_DISABLE_TELEMETRY=true omits remote image but keeps local branding', async () => {
const port = 3453;
const dir = '/tmp/brainstorm-branding-disabled';
await withServer({ port, dir, env: { SUPERPOWERS_DISABLE_TELEMETRY: 'true' } }, async () => {
writeFragment(dir);
await sleep(300);
const html = await fetchHtml(port);
assertBrandedFallbackText(html);
assert(!html.includes(ASSET_URL), 'disabled telemetry should omit the remote image');
});
});
await test('SUPERPOWERS_DISABLE_TELEMETRY=yes also omits the remote image on the waiting screen', async () => {
const port = 3454;
const dir = '/tmp/brainstorm-branding-disabled-waiting';
await withServer({ port, dir, env: { SUPERPOWERS_DISABLE_TELEMETRY: 'yes' } }, async () => {
const html = await fetchHtml(port);
assertBrandedFallbackText(html);
assert(!html.includes(ASSET_URL), 'disabled telemetry should omit the remote image');
});
});
await test('DISABLE_TELEMETRY=true omits remote image for Claude Code telemetry opt-out', async () => {
const port = 3455;
const dir = '/tmp/brainstorm-branding-claude-disable-telemetry';
await withServer({ port, dir, env: { DISABLE_TELEMETRY: 'true' } }, async () => {
writeFragment(dir);
await sleep(300);
const html = await fetchHtml(port);
assertBrandedFallbackText(html);
assert(!html.includes(ASSET_URL), 'Claude Code telemetry opt-out should omit the remote image');
});
});
await test('CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 omits remote image for Claude Code traffic opt-out', async () => {
const port = 3456;
const dir = '/tmp/brainstorm-branding-claude-disable-nonessential';
await withServer({ port, dir, env: { CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1' } }, async () => {
const html = await fetchHtml(port);
assertBrandedFallbackText(html);
assert(!html.includes(ASSET_URL), 'Claude Code non-essential traffic opt-out should omit the remote image');
});
});
console.log(`\n--- Results: ${passed} passed, ${failed} failed ---`);
if (failed > 0) process.exitCode = 1;
}
main().catch((err) => {
console.error('Test failed:', err);
process.exit(1);
});

View File

@@ -2,7 +2,7 @@
"name": "brainstorm-server-tests",
"version": "1.0.0",
"scripts": {
"test": "node ws-protocol.test.js && node helper.test.js && node browser-launcher.test.js && node auth.test.js && node server.test.js && node lifecycle.test.js && bash start-server.test.sh && bash stop-server.test.sh"
"test": "node ws-protocol.test.js && node helper.test.js && node browser-launcher.test.js && node auth.test.js && node branding.test.js && node server.test.js && node lifecycle.test.js && bash start-server.test.sh && bash stop-server.test.sh"
},
"dependencies": {
"ws": "^8.19.0"

View File

@@ -196,7 +196,7 @@ async function runTests() {
const res = await fetch(`http://localhost:${TEST_PORT}/`);
assert(res.body.includes('<h1>Custom Page</h1>'), 'Should contain original content');
assert(res.body.includes('WebSocket'), 'Should still inject helper.js');
assert(!res.body.includes('indicator-bar'), 'Should NOT wrap in frame template');
assert(!res.body.includes('<div class="header">'), 'Should NOT wrap in frame template');
});
await test('wraps content fragments in frame template', async () => {
@@ -205,7 +205,7 @@ async function runTests() {
await sleep(300);
const res = await fetch(`http://localhost:${TEST_PORT}/`);
assert(res.body.includes('indicator-bar'), 'Fragment should get indicator bar');
assert(res.body.includes('<div class="header">'), 'Fragment should get header chrome');
assert(!res.body.includes('<!-- CONTENT -->'), 'Placeholder should be replaced');
assert(res.body.includes('Pick a layout'), 'Fragment content should be present');
assert(res.body.includes('data-choice="a"'), 'Fragment interactive elements intact');
@@ -560,8 +560,16 @@ async function runTests() {
const template = fs.readFileSync(
path.join(__dirname, '../../skills/brainstorming/scripts/frame-template.html'), 'utf-8'
);
assert(template.includes('indicator-bar'), 'Should have indicator bar');
assert(template.includes('indicator-text'), 'Should have indicator text');
assert(template.includes('<div class="header">'), 'Should have top header markup');
assert(!template.includes('indicator-bar'), 'Should not have footer chrome');
assert(!template.includes('indicator-text'), 'Header should not render selection indicator text');
assert(template.includes('<!-- BRANDING -->'), 'Should have branding placeholder');
assert(template.includes('<div class="status">Connecting…</div>'), 'Header should include connection status');
assert(template.includes('grid-template-columns: minmax(0, 1fr) auto;'), 'Header should let brand text shrink before the status column');
assert(template.includes('padding: 0.5rem 1.5rem;'), 'Header should keep equal left and right edge padding');
assert(template.includes('.header .brand { justify-self: start; width: 100%; font-size: 0.75rem; line-height: 1; }'), 'Header brand should align left, fill its grid track, and match header text size');
assert(template.includes('.header .status { grid-column: 2; line-height: 1; }'), 'Header status should sit in the right column');
assert(!template.includes('<div></div>'), 'Header should not use an empty spacer before branding');
assert(template.includes('<!-- CONTENT -->'), 'Should have content placeholder');
assert(template.includes('frame-content'), 'Should have content container');
return Promise.resolve();