whynot-design/designbook/_ds_bundle.js

1397 lines
41 KiB
JavaScript
Raw Normal View History

feat(designbook): technology-neutral IR + stack-adapter pipeline (WHYNOT-WP-0002 T01-T06) Author the design language once in the canonical React designbook and project it one-way onto each stack: React -> designbook/ -> ir/ -> adapters/<stack>/. Phase 0 — contracts & governance (T01-T03): - ir/SCHEMA.md + ir/schema/{component,tokens}.schema.json — neutral IR contract (W3C DTCG tokens; React prop -> HTML attribute mapping; non-portable props flagged). - adapters/ADAPTER_CONTRACT.md — inputs, drift-report + parity-result shapes, idempotency rules, CI exit codes (0 ok / 2 usage / 3 drift / 4 parity / 5 internal). - .claude/rules/designbook-propagation.md + DesignSystemIntroduction.md §5.1 — one-way directionality + drift-resolution workflow. T04 — canonical React designbook + the missing pull tool: - The bundled /design-sync skill only PUSHES repo->cloud; it cannot populate designbook/. Added scripts/designbook_pull.py + `make designbook-pull`, which drives the local claude binary headless (acceptEdits) so DesignSync fetch+write runs in a subprocess (contents never hit the orchestrator's context). Pulled 44 files; excludes the _whynot-design-seed/ self-copy. Corrected the docs that wrongly called /design-sync the pull. T05 — IR extractor (scripts/ir-extract.mjs + `make ir`): - ir/tokens.json (80 tokens, DTCG, var() -> {ref} alias resolution); ir/components/*.json (10 contracts parsed from .jsx signatures: enum/boolean/number inference, prop->attr map, style/callback marked non-portable); ir/exemplars/*. T06 — Lit token adapter (adapters/lit/ + `make adapt-lit`): - Full-gen tokens into src/styles/colors_and_type.css :root (marker-bounded, idempotent no-op on re-run; hand-authored type CSS preserved). NOTE: token regen synced Lit to canonical React — fonts IBM Plex -> system stacks and 8 status tokens added. This is a VISUAL change: review and run `pnpm test:visual:update` before merge. Remaining: T07 scaffold+drift, T08 parity, T09 runbook, T10 2nd-adapter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 12:36:24 +02:00
/* @ds-bundle: {"format":3,"namespace":"WhyNotDesignSystem_fb2eef","components":[],"sourceHashes":{"ui_kits/whynot-control/Atoms.jsx":"79a33f57ef80","ui_kits/whynot-control/Chrome.jsx":"31e0ba69d22d","ui_kits/whynot-control/DocView.jsx":"636cbc6e3422","ui_kits/whynot-control/Screens.jsx":"c21f0206c6c7","ui_kits/whynot-control/data.jsx":"16b3caf0bced"},"inlinedExternals":[],"unexposedExports":[]} */
(() => {
const __ds_ns = (window.WhyNotDesignSystem_fb2eef = window.WhyNotDesignSystem_fb2eef || {});
const __ds_scope = {};
(__ds_ns.__errors = __ds_ns.__errors || []);
// ui_kits/whynot-control/Atoms.jsx
try { (() => {
// =============================================================
// Atoms — Eyebrow, Tag, Button, StageDot, Stamp, IconBtn
// =============================================================
function Eyebrow({
children,
style
}) {
return /*#__PURE__*/React.createElement("span", {
style: {
font: '500 11px/1.2 var(--ff-mono)',
letterSpacing: '0.08em',
textTransform: 'uppercase',
color: 'var(--fg-3)',
...style
}
}, children);
}
function Tag({
children,
active,
draft,
style
}) {
const base = {
font: '500 10px/1 var(--ff-mono)',
letterSpacing: '0.1em',
textTransform: 'uppercase',
padding: '5px 10px',
borderRadius: 'var(--r-pill)',
border: '1px solid var(--border)',
color: 'var(--fg-2)',
background: 'var(--paper)',
display: 'inline-block'
};
if (active) Object.assign(base, {
background: 'var(--ink)',
color: 'var(--paper)',
borderColor: 'var(--ink)'
});
if (draft) Object.assign(base, {
background: 'var(--hi)',
color: 'var(--hi-ink)',
borderColor: 'transparent'
});
return /*#__PURE__*/React.createElement("span", {
style: {
...base,
...style
}
}, children);
}
function Button({
children,
variant = 'secondary',
onClick,
style,
icon
}) {
const base = {
font: '500 13px var(--ff-sans)',
letterSpacing: '-0.005em',
padding: '9px 14px',
borderRadius: 'var(--r-2)',
border: '1px solid var(--border)',
background: 'var(--paper)',
color: 'var(--ink)',
cursor: 'pointer',
display: 'inline-flex',
alignItems: 'center',
gap: 8,
whiteSpace: 'nowrap',
transition: 'background 120ms ease, border-color 120ms ease'
};
if (variant === 'primary') Object.assign(base, {
background: 'var(--ink)',
color: 'var(--paper)',
borderColor: 'var(--ink)'
});
if (variant === 'ghost') Object.assign(base, {
background: 'transparent',
borderColor: 'transparent',
padding: '7px 10px'
});
return /*#__PURE__*/React.createElement("button", {
onClick: onClick,
style: {
...base,
...style
}
}, icon && /*#__PURE__*/React.createElement("i", {
"data-lucide": icon,
style: {
width: 14,
height: 14,
strokeWidth: 1.5
}
}), children);
}
const STAGE_COLORS = {
S0: '#B5B5B3',
S1: '#8A8A8A',
S2: '#5C5C5C',
S3: '#0A0A0A',
S4: '#FFD400'
};
function StageDot({
level = 'S2',
label,
style
}) {
return /*#__PURE__*/React.createElement("span", {
style: {
font: '500 10px/1 var(--ff-mono)',
letterSpacing: '0.1em',
textTransform: 'uppercase',
color: 'var(--fg-2)',
display: 'inline-flex',
alignItems: 'center',
gap: 6,
...style
}
}, /*#__PURE__*/React.createElement("span", {
style: {
width: 8,
height: 8,
borderRadius: 999,
background: STAGE_COLORS[level]
}
}), label || level);
}
function Stamp({
children,
style
}) {
return /*#__PURE__*/React.createElement("span", {
style: {
display: 'inline-block',
background: 'var(--hi)',
color: 'var(--hi-ink)',
padding: '5px 10px 3px',
font: '500 10px/1 var(--ff-mono)',
letterSpacing: '0.12em',
textTransform: 'uppercase',
transform: 'rotate(-1.5deg)',
...style
}
}, children);
}
function Icon({
name,
size = 16,
style
}) {
return /*#__PURE__*/React.createElement("i", {
"data-lucide": name,
style: {
width: size,
height: size,
strokeWidth: 1.5,
...style
}
});
}
Object.assign(window, {
Eyebrow,
Tag,
Button,
StageDot,
Stamp,
Icon,
STAGE_COLORS
});
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/whynot-control/Atoms.jsx", error: String((e && e.message) || e) }); }
// ui_kits/whynot-control/Chrome.jsx
try { (() => {
// =============================================================
// Chrome — TopNav, Sidebar, PageHeader, PipelineStrip
// =============================================================
function TopNav({
onNew
}) {
return /*#__PURE__*/React.createElement("nav", {
style: {
height: 56,
background: 'rgba(255,255,255,0.92)',
borderBottom: '1px solid var(--border)',
display: 'flex',
alignItems: 'center',
gap: 28,
padding: '0 24px',
position: 'sticky',
top: 0,
zIndex: 10
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
alignItems: 'center',
gap: 10
}
}, /*#__PURE__*/React.createElement("img", {
src: "../../assets/whynot-logo.png",
alt: "",
style: {
width: 22,
height: 22
}
}), /*#__PURE__*/React.createElement("span", {
style: {
font: '500 14px var(--ff-sans)'
}
}, "whynot"), /*#__PURE__*/React.createElement("span", {
style: {
font: '400 12px var(--ff-mono)',
color: 'var(--fg-3)',
letterSpacing: '0.04em'
}
}, "/ control")), /*#__PURE__*/React.createElement("div", {
style: {
marginLeft: 'auto',
display: 'flex',
alignItems: 'center',
gap: 12
}
}, /*#__PURE__*/React.createElement("div", {
style: {
font: '400 12px var(--ff-mono)',
color: 'var(--fg-3)',
border: '1px solid var(--border)',
padding: '6px 10px',
borderRadius: 'var(--r-1)',
display: 'flex',
alignItems: 'center',
gap: 10,
minWidth: 240
}
}, /*#__PURE__*/React.createElement(Icon, {
name: "search",
size: 14
}), /*#__PURE__*/React.createElement("span", null, "Search ideas, prototypes, signals…"), /*#__PURE__*/React.createElement("span", {
style: {
marginLeft: 'auto',
padding: '1px 5px',
border: '1px solid var(--border)',
borderRadius: 2,
fontSize: 10
}
}, "⌘ K")), /*#__PURE__*/React.createElement(Button, {
variant: "primary",
icon: "plus",
onClick: onNew
}, "New idea")));
}
const NAV_ITEMS = [{
key: 'inbox',
label: 'Inbox',
icon: 'inbox',
count: 7
}, {
key: 'prototypes',
label: 'Prototypes',
icon: 'flask-conical',
count: 4
}, {
key: 'signals',
label: 'Signals',
icon: 'activity',
count: 12
}, {
key: 'betas',
label: 'Betas',
icon: 'users',
count: 1
}, {
key: 'decisions',
label: 'Decisions',
icon: 'check-square',
count: 3
}];
const DOC_ITEMS = [{
key: 'intent',
label: 'INTENT.md'
}, {
key: 'scope',
label: 'SCOPE.md'
}, {
key: 'operating',
label: 'OPERATING_MODEL.md'
}, {
key: 'pipeline',
label: 'PROTOTYPE_PIPELINE.md'
}, {
key: 'agent',
label: 'AGENT_RULES.md'
}];
function Sidebar({
current,
onNav
}) {
const itemStyle = active => ({
display: 'flex',
alignItems: 'center',
gap: 10,
padding: '6px 10px',
color: active ? 'var(--fg-1)' : 'var(--fg-3)',
background: 'transparent',
borderLeft: active ? '2px solid var(--ink)' : '2px solid transparent',
paddingLeft: active ? 10 : 12,
font: active ? '500 13px var(--ff-sans)' : '400 13px var(--ff-sans)',
cursor: 'pointer',
textDecoration: 'none',
transition: 'color 120ms ease, border-color 120ms ease'
});
return /*#__PURE__*/React.createElement("aside", {
style: {
width: 200,
flex: 'none',
background: 'transparent',
borderRight: 'none',
padding: '32px 0 32px 8px',
display: 'flex',
flexDirection: 'column',
gap: 32,
height: 'calc(100vh - 56px)',
position: 'sticky',
top: 56,
overflowY: 'auto'
}
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Eyebrow, {
style: {
paddingLeft: 12,
marginBottom: 10,
display: 'block',
opacity: 0.7
}
}, "Work"), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column',
gap: 1
}
}, NAV_ITEMS.map(item => /*#__PURE__*/React.createElement("a", {
key: item.key,
onClick: () => onNav(item.key),
style: itemStyle(current === item.key)
}, /*#__PURE__*/React.createElement("span", null, item.label), /*#__PURE__*/React.createElement("span", {
style: {
marginLeft: 'auto',
font: '400 11px var(--ff-mono)',
color: 'var(--ink-5)'
}
}, item.count))))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Eyebrow, {
style: {
paddingLeft: 12,
marginBottom: 10,
display: 'block',
opacity: 0.7
}
}, "Control docs"), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column',
gap: 1
}
}, DOC_ITEMS.map(item => /*#__PURE__*/React.createElement("a", {
key: item.key,
onClick: () => onNav('doc:' + item.key),
style: {
...itemStyle(current === 'doc:' + item.key),
font: current === 'doc:' + item.key ? '500 12px var(--ff-mono)' : '400 12px var(--ff-mono)'
}
}, /*#__PURE__*/React.createElement("span", null, item.label))))), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 'auto',
padding: '0 12px'
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
alignItems: 'center',
gap: 8
}
}, /*#__PURE__*/React.createElement("span", {
style: {
width: 5,
height: 5,
borderRadius: 999,
background: 'var(--ink-4)'
}
}), /*#__PURE__*/React.createElement("span", {
style: {
font: '400 11px var(--ff-mono)',
letterSpacing: '0.06em',
textTransform: 'uppercase',
color: 'var(--fg-3)'
}
}, "A1 \xB7 Incubating"))));
}
function PageHeader({
eyebrow,
title,
lede,
actions
}) {
return /*#__PURE__*/React.createElement("header", {
style: {
marginBottom: 48,
display: 'flex',
flexDirection: 'column',
gap: 10
}
}, eyebrow && /*#__PURE__*/React.createElement(Eyebrow, null, eyebrow), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
alignItems: 'flex-end',
gap: 24
}
}, /*#__PURE__*/React.createElement("h1", {
style: {
font: '400 36px/1.1 var(--ff-sans)',
letterSpacing: '-0.02em',
margin: 0,
flex: 1
}
}, title), actions && /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
gap: 8
}
}, actions)), lede && /*#__PURE__*/React.createElement("p", {
style: {
font: '400 16px/1.6 var(--ff-sans)',
color: 'var(--fg-2)',
margin: '4px 0 0',
maxWidth: '56ch'
}
}, lede));
}
function PipelineStrip({
activeIdx = 3
}) {
const stages = [{
num: 'Stage 0',
name: 'Raw idea',
meta: 'inbox/'
}, {
num: 'Stage 1',
name: 'Triage',
meta: '2026-02-12'
}, {
num: 'Stage 2',
name: 'Prototype card',
meta: 'prototypes/'
}, {
num: 'Stage 3',
name: 'Experiment',
meta: 'ends 2026-04-01'
}, {
num: 'Stage 4',
name: 'Signal review',
meta: '— pending'
}];
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'grid',
gridTemplateColumns: 'repeat(5, 1fr)',
gap: 0,
position: 'relative',
margin: '0 0 32px'
}
}, stages.map((s, i) => {
const state = i < activeIdx ? 'done' : i === activeIdx ? 'active' : 'pending';
const topColor = state === 'done' ? 'var(--ink)' : state === 'active' ? 'var(--hi-2)' : 'var(--border)';
return /*#__PURE__*/React.createElement("div", {
key: i,
style: {
padding: '10px 12px 14px',
borderTop: `2px solid ${topColor}`,
display: 'flex',
flexDirection: 'column',
gap: 4,
position: 'relative'
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: '500 10px/1 var(--ff-mono)',
letterSpacing: '0.1em',
textTransform: 'uppercase',
color: state === 'pending' ? 'var(--fg-3)' : 'var(--fg-1)'
}
}, s.num), /*#__PURE__*/React.createElement("span", {
style: {
font: '500 14px/1.25 var(--ff-sans)',
color: state === 'pending' ? 'var(--fg-3)' : 'var(--fg-1)'
}
}, s.name), /*#__PURE__*/React.createElement("span", {
style: {
font: '400 11px/1.35 var(--ff-mono)',
color: 'var(--fg-3)'
}
}, s.meta), i > 0 && /*#__PURE__*/React.createElement("span", {
style: {
position: 'absolute',
top: -8,
right: -7,
font: '400 14px var(--ff-mono)',
color: state === 'pending' ? 'var(--ink-5)' : 'var(--ink)'
}
}, "→"));
}));
}
Object.assign(window, {
TopNav,
Sidebar,
PageHeader,
PipelineStrip,
NAV_ITEMS,
DOC_ITEMS
});
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/whynot-control/Chrome.jsx", error: String((e && e.message) || e) }); }
// ui_kits/whynot-control/DocView.jsx
try { (() => {
// =============================================================
// Document viewer — renders one of the control docs
// =============================================================
const DOC_CONTENT = {
intent: {
title: 'INTENT.md',
eyebrow: 'whynot-control · control document',
sections: [{
h: 'Purpose',
p: 'whynot-control exists to serve as the control repository for the whynot organisation: a prototype, feedback, and market-signal space for discovering the weird and the useful.'
}, {
h: 'Primary utility',
list: ['capture unusual but potentially useful ideas;', 'distinguish curiosity from commitment;', 'shape rough ideas into testable prototypes;', 'collect early feedback and market signals;', 'run closed beta concepts in a controlled way;', 'identify which ideas should move toward Helix, Coulomb, Sloppers, Plenitude, Binky, or Tegwick;', 'prevent premature productisation.']
}, {
h: 'Operating principle',
quote: 'A prototype is a question made tangible. The purpose of a prototype is not to prove that an idea is brilliant. The purpose is to learn what is actually useful, desirable, feasible, or irrelevant.'
}]
},
scope: {
title: 'SCOPE.md',
eyebrow: 'whynot-control · control document',
sections: [{
h: 'Current reality',
p: 'whynot-control is the control repository for organising prototype exploration and early market-signal capture.'
}, {
h: 'In scope',
list: ['Prototype idea capture.', 'Prototype classification.', 'Early user feedback notes.', 'Market-signal tracking.', 'Closed beta planning.', 'Experiment records.', 'Promotion recommendations.', 'Agent-assisted drafting and analysis.']
}, {
h: 'Out of scope',
list: ['Production implementation.', 'Long-term product maintenance.', 'Payment processing.', 'Legal investment documentation.', 'Public launch operations.', 'Binding financial, legal, or tax conclusions.']
}, {
h: 'Scope guardrail',
quote: 'whynot-control explores and validates. It does not absorb all product development.'
}]
},
operating: {
title: 'OPERATING_MODEL.md',
eyebrow: 'whynot-control · control document',
sections: [{
h: 'Core rules',
list: ['Prototypes are questions. Each prototype should express a question about usefulness, desirability, feasibility, or willingness to pay.', 'Signal beats enthusiasm. An idea should not be promoted only because it is exciting.', 'Low-cost learning first. Prefer sketches, mockups, demos, landing pages, conversations.', 'Closed beta before broad launch.', 'Promotion requires criteria.']
}, {
h: 'Burnout guardrail',
quote: 'A prototype can be interesting and still be parked. whynot exists to reduce uncertainty, not to create more obligations.'
}]
},
pipeline: {
title: 'PROTOTYPE_PIPELINE.md',
eyebrow: 'whynot-control · control document',
sections: [{
h: 'Stage 0 — Raw capture',
p: 'Capture ideas without judging them immediately. Located in inbox/. Done when the idea is saved and no longer needs to be held in memory.'
}, {
h: 'Stage 1 — Triage',
p: 'Decide whether an idea deserves a prototype card. Outcomes: create card, park, merge, reject.'
}, {
h: 'Stage 2 — Prototype card',
p: 'Turn the idea into a structured prototype candidate. Located in prototypes/.'
}, {
h: 'Stage 3 — Experiment',
p: 'Test the idea with minimal cost: concept note, landing page, clickable mockup, CLI/demo script, Wizard-of-Oz, manual concierge test, closed conversation, private beta.'
}, {
h: 'Stage 4 — Signal review',
p: 'Evaluate what was learned. Interest, usefulness, retention, referral, payment, contribution, strategic fit.'
}, {
h: 'Stage 5 — Decision',
p: 'Park, iterate, promote, reject, or merge. Promotion requires an explicit record in DECISIONS.md.'
}]
},
agent: {
title: 'AGENT_RULES.md',
eyebrow: 'whynot-control · control document',
sections: [{
h: 'General principle',
p: 'Agents may help clarify, structure, draft, compare, and analyse prototype ideas. They must not silently turn experiments into product commitments.'
}, {
h: 'Allowed',
list: ['draft prototype cards', 'classify ideas by lifecycle stage', 'propose smallest useful tests', 'summarise feedback', 'compare prototype candidates', 'improve wording and structure']
}, {
h: 'Forbidden',
list: ['create artificial urgency', 'treat all prototype ideas as products', 'infer willingness to pay without evidence', 'present weak signals as strong validation', 'create legal, financial, or investment commitments']
}, {
h: 'Preferred output style',
quote: 'Agent outputs should be concise, evidence-oriented, explicit about uncertainty, and careful to separate idea, hypothesis, signal, and decision.'
}]
}
};
function DocView({
docKey
}) {
const doc = DOC_CONTENT[docKey];
if (!doc) return /*#__PURE__*/React.createElement("div", null, "Doc not found.");
return /*#__PURE__*/React.createElement("article", {
style: {
maxWidth: 680
}
}, /*#__PURE__*/React.createElement(Eyebrow, null, doc.eyebrow), /*#__PURE__*/React.createElement("h1", {
style: {
font: '600 36px/1.1 var(--ff-mono)',
letterSpacing: '-0.01em',
margin: '12px 0 28px'
}
}, doc.title), doc.sections.map((s, i) => /*#__PURE__*/React.createElement("section", {
key: i,
style: {
marginBottom: 36
}
}, /*#__PURE__*/React.createElement("h2", {
style: {
font: '500 22px/1.25 var(--ff-sans)',
letterSpacing: '-0.005em',
margin: '0 0 14px'
}
}, s.h), s.p && /*#__PURE__*/React.createElement("p", {
style: {
margin: 0,
font: '400 15px/1.65 var(--ff-sans)',
color: 'var(--fg-1)'
}
}, s.p), s.list && /*#__PURE__*/React.createElement("ul", {
style: {
margin: 0,
paddingLeft: 18,
color: 'var(--fg-1)',
font: '400 15px/1.7 var(--ff-sans)'
}
}, s.list.map((li, j) => /*#__PURE__*/React.createElement("li", {
key: j,
style: {
marginBottom: 6
}
}, li))), s.quote && /*#__PURE__*/React.createElement("blockquote", {
style: {
margin: 0,
paddingLeft: 16,
borderLeft: '1px solid var(--border-strong)'
}
}, /*#__PURE__*/React.createElement("p", {
style: {
margin: 0,
font: '400 italic 17px/1.55 var(--ff-serif)',
color: 'var(--fg-2)'
}
}, s.quote)))), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 48,
padding: '14px 0',
borderTop: '1px solid var(--border)',
display: 'flex',
justifyContent: 'space-between',
font: '400 11px var(--ff-mono)',
color: 'var(--fg-3)'
}
}, /*#__PURE__*/React.createElement("span", null, "whynot-control / ", doc.title), /*#__PURE__*/React.createElement("span", null, "A1 \xB7 Incubating \xB7 2026")));
}
Object.assign(window, {
DocView,
DOC_CONTENT
});
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/whynot-control/DocView.jsx", error: String((e && e.message) || e) }); }
// ui_kits/whynot-control/Screens.jsx
try { (() => {
// =============================================================
// Screens — Inbox, PrototypesIndex, PrototypeDetail, SignalsIndex, DocView, BetasIndex, DecisionsIndex
// =============================================================
function Inbox({
onCapture
}) {
const [draft, setDraft] = React.useState('');
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(PageHeader, {
eyebrow: "whynot-control / inbox",
title: "Inbox",
lede: "Temporary capture for rough ideas, weird observations, user comments, market hints, and product fragments. Capture is not commitment."
}), /*#__PURE__*/React.createElement("div", {
style: {
border: '1px solid var(--border)',
borderRadius: 'var(--r-2)',
padding: 16,
background: 'var(--paper)',
marginBottom: 28,
display: 'flex',
flexDirection: 'column',
gap: 10
}
}, /*#__PURE__*/React.createElement(Eyebrow, null, "Capture"), /*#__PURE__*/React.createElement("textarea", {
value: draft,
onChange: e => setDraft(e.target.value),
placeholder: "An idea, an observation, a fragment. No filter, no judgement, no commitment.",
style: {
font: '400 14px/1.5 var(--ff-sans)',
border: 'none',
outline: 'none',
resize: 'none',
minHeight: 64,
padding: 0,
background: 'transparent',
color: 'var(--fg-1)'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
alignItems: 'center',
gap: 8
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: '400 11px var(--ff-mono)',
color: 'var(--fg-3)',
marginRight: 'auto'
}
}, "⌘ ↵ to capture \xB7 stored in ", /*#__PURE__*/React.createElement("code", {
className: "mono"
}, "inbox/")), /*#__PURE__*/React.createElement(Button, {
variant: "ghost",
onClick: () => setDraft('')
}, "Discard"), /*#__PURE__*/React.createElement(Button, {
variant: "primary",
icon: "inbox",
onClick: () => {
onCapture && onCapture(draft);
setDraft('');
}
}, "Capture"))), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
alignItems: 'center',
gap: 12,
marginBottom: 14
}
}, /*#__PURE__*/React.createElement(Eyebrow, null, "Recent \xB7 7"), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
borderTop: '1px solid var(--border-soft)'
}
}), /*#__PURE__*/React.createElement("span", {
style: {
font: '400 11px var(--ff-mono)',
color: 'var(--fg-3)'
}
}, "↓ newest first")), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column'
}
}, INBOX.map(item => /*#__PURE__*/React.createElement("div", {
key: item.id,
style: {
display: 'grid',
gridTemplateColumns: '120px 1fr',
gap: '4px 24px',
padding: '20px 0',
borderBottom: '1px solid var(--border-soft)',
alignItems: 'baseline'
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: '400 11px var(--ff-mono)',
color: 'var(--fg-3)'
}
}, item.ts), /*#__PURE__*/React.createElement("span", {
style: {
font: '500 10px/1 var(--ff-mono)',
letterSpacing: '0.08em',
textTransform: 'uppercase',
color: 'var(--fg-3)'
}
}, item.from), /*#__PURE__*/React.createElement("p", {
style: {
gridColumn: '1 / -1',
margin: '4px 0 0',
font: '400 15px/1.6 var(--ff-sans)',
color: 'var(--fg-1)',
maxWidth: '60ch'
}
}, item.text)))));
}
function PrototypeListCard({
p,
onOpen
}) {
const [hover, setHover] = React.useState(false);
return /*#__PURE__*/React.createElement("article", {
onClick: () => onOpen(p.id),
onMouseEnter: () => setHover(true),
onMouseLeave: () => setHover(false),
style: {
background: 'var(--paper)',
borderTop: '1px solid var(--border-soft)',
padding: '24px 0 28px',
display: 'flex',
flexDirection: 'column',
gap: 12,
position: 'relative',
cursor: 'pointer',
transition: 'background 120ms ease'
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'baseline'
}
}, /*#__PURE__*/React.createElement(Eyebrow, {
style: {
color: hover ? 'var(--fg-1)' : 'var(--fg-3)'
}
}, p.id, " \xB7 Prototype"), /*#__PURE__*/React.createElement(StageDot, {
level: p.signal,
label: p.stageLabel
})), /*#__PURE__*/React.createElement("h3", {
style: {
font: '400 22px/1.3 var(--ff-sans)',
letterSpacing: '-0.01em',
margin: '2px 0 8px',
color: 'var(--fg-1)',
maxWidth: '52ch'
}
}, p.pitch), /*#__PURE__*/React.createElement("div", {
style: {
display: 'grid',
gridTemplateColumns: '140px 1fr',
gap: '10px 16px',
fontSize: 14,
color: 'var(--fg-2)',
maxWidth: '60ch'
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: '500 11px/1.7 var(--ff-mono)',
letterSpacing: '0.06em',
textTransform: 'uppercase',
color: 'var(--fg-3)'
}
}, "Learning q."), /*#__PURE__*/React.createElement("span", {
style: {
lineHeight: 1.55
}
}, p.learning), /*#__PURE__*/React.createElement("span", {
style: {
font: '500 11px/1.7 var(--ff-mono)',
letterSpacing: '0.06em',
textTransform: 'uppercase',
color: 'var(--fg-3)'
}
}, "Smallest test"), /*#__PURE__*/React.createElement("span", {
style: {
lineHeight: 1.55
}
}, p.test)), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
gap: 24,
marginTop: 4,
font: '500 11px var(--ff-mono)',
letterSpacing: '0.06em',
textTransform: 'uppercase',
color: 'var(--fg-3)'
}
}, /*#__PURE__*/React.createElement("span", null, "→ ", p.target), /*#__PURE__*/React.createElement("span", null, p.signal, " signal"), /*#__PURE__*/React.createElement("span", {
style: {
marginLeft: 'auto',
color: hover ? 'var(--fg-1)' : 'var(--fg-3)'
}
}, "Open →")));
}
function PrototypesIndex({
onOpen
}) {
const [filter, setFilter] = React.useState('All');
const filters = ['All', 'Experiment', 'Signal review', 'Parked'];
const list = filter === 'All' ? PROTOTYPES : PROTOTYPES.filter(p => p.stageLabel === filter);
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(PageHeader, {
eyebrow: "whynot-control / prototypes",
title: "Prototypes",
lede: "Structured prototype cards. A prototype card defines a learning question and the smallest useful test.",
actions: /*#__PURE__*/React.createElement(Button, {
variant: "primary",
icon: "plus"
}, "New prototype")
}), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
gap: 10,
marginBottom: 8,
alignItems: 'center'
}
}, filters.map(f => /*#__PURE__*/React.createElement(Tag, {
key: f,
active: filter === f,
style: {
cursor: 'pointer'
}
}, /*#__PURE__*/React.createElement("span", {
onClick: () => setFilter(f)
}, f))), /*#__PURE__*/React.createElement("span", {
style: {
marginLeft: 'auto',
font: '400 11px var(--ff-mono)',
color: 'var(--fg-3)'
}
}, list.length, " of ", PROTOTYPES.length)), /*#__PURE__*/React.createElement("div", null, list.map(p => /*#__PURE__*/React.createElement(PrototypeListCard, {
key: p.id,
p: p,
onOpen: onOpen
}))));
}
function PrototypeDetail({
id,
onBack
}) {
const p = PROTOTYPES.find(p => p.id === id) || PROTOTYPES[0];
const stageIdx = {
'parked': 0,
'experiment': 3,
'signal': 4,
'experiment-active': 3
}[p.stage] ?? 3;
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("a", {
onClick: onBack,
style: {
display: 'inline-flex',
alignItems: 'center',
gap: 6,
font: '400 12px var(--ff-mono)',
color: 'var(--fg-2)',
textDecoration: 'none',
marginBottom: 18,
cursor: 'pointer'
}
}, /*#__PURE__*/React.createElement(Icon, {
name: "arrow-left",
size: 14
}), " Back to prototypes"), /*#__PURE__*/React.createElement(PageHeader, {
eyebrow: `${p.id} · Prototype`,
title: p.pitch,
actions: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
variant: "secondary",
icon: "archive"
}, "Park"), /*#__PURE__*/React.createElement(Button, {
variant: "primary",
icon: "arrow-right"
}, "Promote → ", p.target))
}), /*#__PURE__*/React.createElement(PipelineStrip, {
activeIdx: stageIdx
}), /*#__PURE__*/React.createElement("div", {
style: {
display: 'grid',
gridTemplateColumns: '1.4fr 1fr',
gap: 32
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column',
gap: 22
}
}, /*#__PURE__*/React.createElement(Field, {
label: "Learning question",
value: p.learning
}), /*#__PURE__*/React.createElement(Field, {
label: "Smallest useful test",
value: p.test
}), /*#__PURE__*/React.createElement(Field, {
label: "Expected signal",
value: "At least one person asks for a concrete next step, gives specific use-case feedback, or identifies a realistic context where the idea would matter."
}), /*#__PURE__*/React.createElement(Field, {
label: "Risks",
value: p.risks
})), /*#__PURE__*/React.createElement("aside", {
style: {
display: 'flex',
flexDirection: 'column',
gap: 18
}
}, /*#__PURE__*/React.createElement(SidebarField, {
label: "Stage",
value: /*#__PURE__*/React.createElement(Tag, {
active: true
}, p.stageLabel)
}), /*#__PURE__*/React.createElement(SidebarField, {
label: "Signal",
value: /*#__PURE__*/React.createElement(StageDot, {
level: p.signal
})
}), /*#__PURE__*/React.createElement(SidebarField, {
label: "Target",
value: /*#__PURE__*/React.createElement("code", {
className: "mono"
}, "→ ", p.target)
}), /*#__PURE__*/React.createElement(SidebarField, {
label: "Audience",
value: "Potential early users, collaborators, or customers."
}), /*#__PURE__*/React.createElement(SidebarField, {
label: "Agentic suitability",
value: "Agents may help turn rough notes into a sharper prototype card."
}), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 6,
border: '1px dashed var(--border-strong)',
borderRadius: 4,
padding: 14
}
}, /*#__PURE__*/React.createElement(Eyebrow, {
style: {
display: 'block',
marginBottom: 8
}
}, "Caveat"), /*#__PURE__*/React.createElement("p", {
style: {
margin: 0,
font: '400 13px/1.55 var(--ff-sans)',
color: 'var(--fg-2)'
}
}, "A prototype can be interesting and still be parked. ", /*#__PURE__*/React.createElement("code", {
className: "mono"
}, "whynot"), " exists to reduce uncertainty, not create more obligations.")))));
}
function Field({
label,
value
}) {
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column',
gap: 6
}
}, /*#__PURE__*/React.createElement(Eyebrow, null, label), /*#__PURE__*/React.createElement("p", {
style: {
margin: 0,
font: '400 15px/1.55 var(--ff-sans)',
color: 'var(--fg-1)'
}
}, value));
}
function SidebarField({
label,
value
}) {
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column',
gap: 6
}
}, /*#__PURE__*/React.createElement(Eyebrow, null, label), /*#__PURE__*/React.createElement("div", {
style: {
font: '400 13px/1.5 var(--ff-sans)',
color: 'var(--fg-1)'
}
}, value));
}
function SignalsIndex() {
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(PageHeader, {
eyebrow: "whynot-control / signals",
title: "Signals",
lede: "Market-signal and feedback records. A signal is evidence. Record what happened, who did it, and how strong the evidence is.",
actions: /*#__PURE__*/React.createElement(Button, {
variant: "primary",
icon: "plus"
}, "Record signal")
}), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column'
}
}, SIGNALS.map(s => /*#__PURE__*/React.createElement("div", {
key: s.id,
style: {
display: 'grid',
gridTemplateColumns: '90px 90px 1fr',
gap: '6px 24px',
padding: '20px 0',
borderBottom: '1px solid var(--border-soft)',
alignItems: 'baseline'
}
}, /*#__PURE__*/React.createElement("code", {
className: "mono",
style: {
background: 'none',
padding: 0,
color: 'var(--fg-3)',
font: '400 11px var(--ff-mono)'
}
}, s.id), /*#__PURE__*/React.createElement("code", {
className: "mono",
style: {
background: 'none',
padding: 0,
color: 'var(--fg-2)',
font: '400 11px var(--ff-mono)'
}
}, s.proto), /*#__PURE__*/React.createElement(StageDot, {
level: s.level
}), /*#__PURE__*/React.createElement("p", {
style: {
gridColumn: '1 / -1',
margin: '4px 0 0',
font: '400 14px/1.55 var(--ff-sans)',
color: 'var(--fg-1)',
maxWidth: '60ch'
}
}, s.what), /*#__PURE__*/React.createElement("span", {
style: {
gridColumn: '1 / -1',
font: '400 11px var(--ff-mono)',
color: 'var(--fg-3)'
}
}, s.source, " \xB7 ", s.date)))));
}
function BetasIndex() {
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(PageHeader, {
eyebrow: "whynot-control / betas",
title: "Betas",
lede: "Closed beta plans and beta review notes. A beta should have a clear learning question, entry criteria, and exit outcome."
}), /*#__PURE__*/React.createElement("div", {
style: {
border: '1px dashed var(--border-strong)',
padding: 32,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 8,
textAlign: 'center',
color: 'var(--fg-3)',
borderRadius: 4
}
}, /*#__PURE__*/React.createElement(Icon, {
name: "users",
size: 20
}), /*#__PURE__*/React.createElement("div", {
style: {
font: '500 14px var(--ff-sans)',
color: 'var(--fg-2)'
}
}, "One beta plan in draft."), /*#__PURE__*/React.createElement("div", {
style: {
font: '400 12px var(--ff-mono)',
color: 'var(--fg-3)'
}
}, "WNO-021 \xB7 Concierge triage \xB7 pending Binky approval"), /*#__PURE__*/React.createElement("a", {
href: "#",
style: {
font: '500 12px var(--ff-mono)',
color: 'var(--fg-1)',
marginTop: 4
}
}, "Open draft →")));
}
function DecisionsIndex() {
const decisions = [{
id: 'DEC-001',
title: 'Shorten organisation name from whywhynot to whynot.',
status: 'Accepted',
date: '2026-01-08'
}, {
id: 'DEC-002',
title: 'Maintain A1 Incubating until first prototype candidates review.',
status: 'Open',
date: '—'
}, {
id: 'DEC-003',
title: 'Initial promotion targets: Helix, Coulomb, Sloppers, Plenitude, Binky, Tegwick.',
status: 'Open',
date: '—'
}];
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(PageHeader, {
eyebrow: "whynot-control / decisions",
title: "Decisions",
lede: "A promotion record is required before any prototype moves to Helix, Coulomb, Sloppers, Plenitude, Binky, or Tegwick."
}), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column'
}
}, decisions.map(d => /*#__PURE__*/React.createElement("div", {
key: d.id,
style: {
display: 'grid',
gridTemplateColumns: '90px 1fr 130px 100px',
gap: 20,
alignItems: 'baseline',
padding: '16px 4px',
borderBottom: '1px solid var(--border-soft)'
}
}, /*#__PURE__*/React.createElement("code", {
className: "mono",
style: {
background: 'none',
padding: 0,
color: 'var(--fg-1)'
}
}, d.id), /*#__PURE__*/React.createElement("span", {
style: {
font: '500 15px var(--ff-sans)',
color: 'var(--fg-1)'
}
}, d.title), /*#__PURE__*/React.createElement(Tag, {
active: d.status === 'Accepted',
draft: d.status === 'Open'
}, d.status), /*#__PURE__*/React.createElement("span", {
style: {
font: '400 12px var(--ff-mono)',
color: 'var(--fg-3)',
textAlign: 'right'
}
}, d.date)))));
}
Object.assign(window, {
Inbox,
PrototypesIndex,
PrototypeDetail,
SignalsIndex,
BetasIndex,
DecisionsIndex,
Field,
SidebarField,
PrototypeListCard
});
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/whynot-control/Screens.jsx", error: String((e && e.message) || e) }); }
// ui_kits/whynot-control/data.jsx
try { (() => {
// =============================================================
// Sample data — prototypes, signals, inbox items
// =============================================================
const PROTOTYPES = [{
id: 'WNO-014',
pitch: 'A field-notebook for catching weird ideas before they evaporate.',
learning: 'Do people return to capture more than once?',
test: 'One-page landing + email capture, 14 days.',
target: 'Coulomb',
stage: 'experiment',
stageLabel: 'Experiment',
signal: 'S1',
risks: 'Confused with note-taking apps.'
}, {
id: 'WNO-017',
pitch: 'A LEGO-brick mood board for engineers who dont think in mood boards.',
learning: 'Will engineers attach metaphors to their tickets?',
test: 'Slack bot, three teams, two weeks.',
target: 'Helix',
stage: 'signal',
stageLabel: 'Signal review',
signal: 'S3',
risks: 'Cute but unused after a week.'
}, {
id: 'WNO-021',
pitch: 'Concierge-style “prototype triage” for indie hackers.',
learning: 'Will three founders pay for a one-hour triage call?',
test: 'Offer beta · 3 calls · listed price.',
target: 'Plenitude',
stage: 'experiment',
stageLabel: 'Experiment',
signal: 'S2',
risks: 'Time-cost outruns signal value.'
}, {
id: 'WNO-024',
pitch: 'A relevant-#CoronaPolitics timeline, re-released with one editor.',
learning: 'Is there residual demand five years on?',
test: 'Static preview page, 30 days, count returns.',
target: 'None yet',
stage: 'parked',
stageLabel: 'Parked',
signal: 'S0',
risks: 'Topical relevance has clearly faded.'
}];
const INBOX = [{
id: 1,
ts: '2026-03-02 14:21',
text: 'Idea: “subway map” view of the prototype pipeline. People understand transit maps; they dont understand kanban boards.',
from: 'Tegwick'
}, {
id: 2,
ts: '2026-03-01 09:08',
text: 'Weird observation from yesterdays call: three founders independently asked for “something to capture the half-formed stuff”.',
from: 'note-to-self'
}, {
id: 3,
ts: '2026-02-28 23:55',
text: 'Could the LEGO-brick metaphor extend to a public “build log” format? One brick = one decision.',
from: 'Tegwick'
}, {
id: 4,
ts: '2026-02-27 11:34',
text: 'Park idea: realtime sentiment dashboard for prototype landing pages. Probably worse than reading the comments.',
from: 'note-to-self'
}, {
id: 5,
ts: '2026-02-26 17:02',
text: 'Conversation with R. about closed-beta etiquette. Useful: pre-write the exit email before the beta opens.',
from: 'note-to-self'
}, {
id: 6,
ts: '2026-02-25 08:12',
text: 'fuerindifferenz shirts: residual interest from old whywhynot.de page. Could a yearly drop work?',
from: 'Tegwick'
}, {
id: 7,
ts: '2026-02-24 15:40',
text: 'Tiny idea: a “reject log” that publishes the ideas you said no to, with one-sentence reasons.',
from: 'note-to-self'
}];
const SIGNALS = [{
id: 'SIG-031',
proto: 'WNO-017',
level: 'S3',
what: 'Two teams shipped public README sections labelled “brick: scope” after using the bot for a week.',
source: 'usage log',
date: '2026-03-04'
}, {
id: 'SIG-030',
proto: 'WNO-017',
level: 'S2',
what: 'Three engineers DMd asking for an export-to-Notion option.',
source: 'Slack',
date: '2026-03-03'
}, {
id: 'SIG-029',
proto: 'WNO-014',
level: 'S1',
what: 'Landing page: 34 visits, 7 emails, 0 returns in week 1.',
source: 'Plausible',
date: '2026-03-01'
}, {
id: 'SIG-028',
proto: 'WNO-021',
level: 'S2',
what: 'First triage call booked at listed price; second declined on price.',
source: 'Stripe / email',
date: '2026-02-28'
}, {
id: 'SIG-027',
proto: 'WNO-021',
level: 'S1',
what: '“Interesting but Id want a free first one” ×2.',
source: 'interview',
date: '2026-02-26'
}, {
id: 'SIG-026',
proto: 'WNO-024',
level: 'S0',
what: 'Static preview: 12 visits in 30 days, 0 returns.',
source: 'Plausible',
date: '2026-02-24'
}];
Object.assign(window, {
PROTOTYPES,
INBOX,
SIGNALS
});
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/whynot-control/data.jsx", error: String((e && e.message) || e) }); }
})();