Skip to content

Commit 2880d2f

Browse files
GiniGini
authored andcommitted
feat: generate responsive website scaffold
1 parent 505ed54 commit 2880d2f

4 files changed

Lines changed: 31 additions & 4 deletions

File tree

docs/IMPLEMENTATION-LOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,5 @@
5656
- Performed a live Azure API attempt through the ONEVibe runtime. API health and sandbox route reachability were verified, and the provider began a real Kasm bootstrap; the request was cancelled before it returned a persisted sandbox ID. The test container was removed and no sandbox record remained. The required provider lifecycle remediation and success criteria are recorded in `ONECOMPUTER-LIVE-E2E.md`.
5757
- Added bounded website-reference context. Users can attach HTTP(S) references through the composer; these are stored with the task, attached to agent context as untrusted material, and represented in evidence only by redacted origin/path metadata. ONEVibe does not fetch them automatically.
5858
- Added bounded local-file task attachments. The browser reads selected files into the task request, the API validates/sanitizes and path-confines them under `inputs/`, and execution emits metadata-only evidence before presenting those files to the agent as untrusted input.
59+
- Upgraded Website mode from a generic state demo to a responsive, portable enterprise landing-page starter with operating-boundary messaging, capability cards, and semantic FAQ disclosure. The next quality gate is agent-directed visual generation plus isolated browser/screenshot review.
60+
- Elevated the Manus-style side artifact trail as a P0 interaction contract: every agent action should project a chronological terminal, browser/screenshot, file, diff, or artifact panel into the evidence-bound Computer rail. This remains observation-only: the browser must not receive X11, VNC, CDP, shell, or sandbox credentials.

docs/MANUS-PARITY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ This is the implementation gate, not a marketing checklist. **I** means behavior
4343
## Creation modes and templates
4444

4545
29. **I** Create Slides mode — mode-specific plan, eight-slide outline, interactive preview, notes, and genuine PPTX output. ONEVibe also has explicit portable Document and Data-story modes with inspectable source/metadata.
46-
30. **P** Build Website mode — website artifacts and isolated preview work; mode-specific workflow is pending.
46+
30. **P** Build Website mode — portable React/Vite output now includes a responsive, accessible enterprise landing-page starter with clear operating-boundary messaging and semantic FAQ disclosure. AI-directed design, screenshot/browser review, richer template packs, and deployment remain pending.
4747
31. **P** Design mode — concept rationale and design-token artifacts exist; richer visual generation is pending.
4848
32. **P** Create Games mode — dedicated plan and a portable playable React interaction loop now exist; richer mechanics, game assets, and runtime play-testing remain pending.
4949
33. **P** Expandable mode catalogue — the composer exposes nine purpose-specific modes with output descriptions and persists the chosen mode. A compact starter gallery now preconfigures editable Website, Briefing, Research, and Internal-tool tasks; deeper per-mode template packs remain pending.
@@ -109,7 +109,7 @@ This is the implementation gate, not a marketing checklist. **I** means behavior
109109
84. **P** Sandbox-dashboard mockup — security context and a headless visual-runtime timeline are visible; inventory and management interaction are incomplete.
110110
85. **P** Mobile-wallet mockup — deep link and approval card exist; wallet simulator/client is pending.
111111
86. **I** Navigation interaction.
112-
87. **M** FAQ accordion interaction as generated output.
112+
87. **P** FAQ accordion interaction as generated output — Website mode includes semantic `details`/`summary` disclosure; agent-directed content and browser-reviewed interaction remain pending.
113113
88. **M** Development observability collector.
114114
89. **I** Icon-system integration — Lucide.
115115
90. **M** Generated-asset storage proxy.

server/mode-artifacts.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ describe('mode artifacts', () => {
6363
expect(await store.readWorkspaceFile(task.id, 'app/src/styles.css')).toContain('.signal')
6464
})
6565

66+
it('generates a responsive website with an accessible FAQ interaction', async () => {
67+
const root = await mkdtemp(path.join(tmpdir(), 'onevibe-website-mode-'))
68+
temporaryRoots.push(root)
69+
const { TaskStore } = await import('./store.js')
70+
const { writeModeArtifacts } = await import('./mode-artifacts.js')
71+
const store = new TaskStore(root)
72+
await store.initialize()
73+
const task = await store.createTask('A secure home for enterprise agents', 'demo', 'website')
74+
await writeModeArtifacts(task, store)
75+
76+
expect(await store.readWorkspaceFile(task.id, 'app/src/App.tsx')).toContain('<details')
77+
expect(await store.readWorkspaceFile(task.id, 'app/src/App.tsx')).toContain('Workspace boundary active')
78+
expect(await store.readWorkspaceFile(task.id, 'app/src/styles.css')).toContain('@media(max-width:700px)')
79+
})
80+
6681
it('writes an explicit static validation report without claiming runtime verification', async () => {
6782
const root = await mkdtemp(path.join(tmpdir(), 'onevibe-validation-'))
6883
temporaryRoots.push(root)

server/mode-artifacts.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,26 @@ const writeSlides = async (task: Task, store: TaskStore) => {
4747

4848
const writeScaffold = async (task: Task, store: TaskStore) => {
4949
const game = task.mode === 'game'
50+
const website = task.mode === 'website'
51+
const safeTitle = JSON.stringify(task.title)
5052
const appSource = game
5153
? `import { useState } from 'react'
5254
export default function App(){const [score,setScore]=useState(0);const [round,setRound]=useState(1);const [message,setMessage]=useState('Find the signal');const catchSignal=()=>{const next=score+round;setScore(next);setMessage(next>=12?'Signal secured — start a new round':'Signal captured');if(next>=12){setRound(1);setScore(0)}else setRound(round+1)};return <main className="game"><p className="eyebrow">ONEVibe playable prototype</p><h1>${task.title.replaceAll('`', '')}</h1><section><div><span>Round</span><strong>{round}</strong></div><div><span>Signal</span><strong>{score}/12</strong></div></section><button className="signal" onClick={catchSignal}>✦</button><p>{message}</p><small>This local interaction is portable source. Any publish action remains subject to an external approval.</small></main>}
5355
`
54-
: `import { useState } from 'react'
56+
: website
57+
? `const title=${safeTitle}
58+
const safeguards=['Isolated workspaces','Clear policy checkpoints','Evidence that travels with the work']
59+
const questions=[['Can teams bring their own agent?','Yes. This starter is designed around an operating boundary, not a single agent vendor.'],['Who approves consequential actions?','A separate approval system should decide. The generated site never treats the browser session as approval authority.'],['What happens to the work?','Portable source, files, and task evidence remain available for review and handoff.']]
60+
export default function App(){return <main><nav><a className="brand" href="#top"><i>◈</i> ONEVibe</a><div className="links"><a href="#approach">Approach</a><a href="#safeguards">Safeguards</a><a href="#faq">FAQ</a></div><a className="nav-cta" href="#start">Explore the workspace <span>↗</span></a></nav><section id="top" className="hero"><p className="eyebrow">A GOVERNED WORKSPACE FOR CAPABLE AGENTS</p><h1>{title}</h1><p className="lede">Give people room to create with the agents they choose—while keeping meaningful controls visible, deliberate, and outside the browser.</p><div className="actions"><a className="primary" href="#start">See the operating model <span>↓</span></a><a className="secondary" href="#faq">Read the questions <span>→</span></a></div><div className="signal-card"><span className="pulse"/><p>Workspace boundary active</p><b>Policy and evidence connected</b></div></section><section id="approach" className="statement"><p className="eyebrow">BUILT FOR THE MOMENT AFTER THE PROMPT</p><h2>Useful work deserves a trustworthy place to happen.</h2><p>ONEVibe keeps the task, its workspace, and the evidence of what happened in one calm, reviewable surface.</p></section><section id="safeguards" className="safeguards">{safeguards.map((item,index)=><article key={item}><small>0{index+1}</small><h3>{item}</h3><p>{index===0?'One task receives a bounded place to work.':index===1?'Sensitive steps can pause for an independent decision.':'Source and context are ready for the next person to inspect.'}</p></article>)}</section><section id="start" className="callout"><p className="eyebrow">THE WORKSPACE</p><h2>Move quickly. Know where the boundary is.</h2><p>Start with a focused task. Keep the operational record close. Escalate only the actions that need a human decision.</p><a className="primary" href="#faq">Open the task brief <span>→</span></a></section><section id="faq" className="faq"><div><p className="eyebrow">COMMON QUESTIONS</p><h2>Designed to be understood.</h2></div><div>{questions.map(([question,answer])=><details key={question}><summary>{question}<span>+</span></summary><p>{answer}</p></details>)}</div></section><footer><span>ONEVIBE / PORTABLE WEBSITE STARTER</span><a href="#top">Back to top ↑</a></footer></main>}
61+
`
62+
: `import { useState } from 'react'
5563
export default function App(){const [approved,setApproved]=useState(false);return <main><h1>${task.title.replaceAll('`', '')}</h1><p>Generated inside ONEVibe.</p><button onClick={()=>setApproved(!approved)}>{approved?'Approved demo':'Request approval demo'}</button></main>}
5664
`
5765
const styles = game
5866
? ':root{font-family:system-ui;color:#eef3ef;background:#090b0a}*{box-sizing:border-box}body{margin:0}.game{min-height:100vh;padding:10vw;display:grid;place-content:center;gap:18px;text-align:center;background:radial-gradient(circle at 50% 25%,#163c26,transparent 35%),#090b0a}.eyebrow{color:#38dc7d;font:11px ui-monospace;letter-spacing:.12em;text-transform:uppercase}.game h1{max-width:720px;margin:0;font-size:clamp(38px,6vw,72px);letter-spacing:-.06em}.game section{display:flex;justify-content:center;gap:30px}.game section div{display:grid;gap:3px}.game span,.game small{color:#9ba79f;font-size:12px}.game strong{font-size:26px}.signal{width:110px;height:110px;margin:auto;border:1px solid #57df91;border-radius:50%;background:#143723;color:#b8ffcf;font-size:42px;cursor:pointer;box-shadow:0 0 55px #1a8b4c66;transition:transform .15s}.signal:hover{transform:scale(1.07)}'
59-
: ':root{font-family:system-ui;color:#eef3ef;background:#090b0a}body{margin:0}main{padding:10vw}button{padding:10px}'
67+
: website
68+
? ':root{font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:#edf3ef;background:#0a0c0b}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:radial-gradient(ellipse 75% 40% at 85% 0,#17372255,transparent 70%),#0a0c0b}a{color:inherit;text-decoration:none}main{overflow:hidden}nav,.hero,.statement,.safeguards,.callout,.faq,footer{width:min(1160px,calc(100% - 48px));margin:auto}nav{height:82px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #ffffff13}.brand{font-size:14px;font-weight:750;letter-spacing:-.04em}.brand i{display:inline-grid;place-items:center;width:23px;height:23px;margin-right:8px;border:1px solid #39d87d;border-radius:6px;color:#39d87d;font-style:normal}.links{display:flex;gap:25px;color:#9aa49d;font-size:13px}.nav-cta{padding:10px 13px;border:1px solid #ffffff1c;border-radius:8px;font-size:12px;font-weight:650}.nav-cta span,.primary span,.secondary span{margin-left:7px;color:#52df8c}.hero{position:relative;min-height:650px;padding:138px 0 100px}.eyebrow{margin:0;color:#5be391;font:10px ui-monospace,SFMono-Regular,monospace;letter-spacing:.15em}.hero h1{max-width:780px;margin:20px 0;font-size:clamp(56px,8vw,116px);line-height:.9;letter-spacing:-.075em}.lede{max-width:575px;color:#a6afa9;font-size:18px;line-height:1.65}.actions{display:flex;gap:12px;margin-top:36px}.primary,.secondary{display:inline-flex;align-items:center;padding:13px 17px;border-radius:8px;font-size:13px;font-weight:700}.primary{background:#40d978;color:#08100a}.secondary{border:1px solid #ffffff22;color:#dfe6e1}.signal-card{position:absolute;right:6%;bottom:105px;width:255px;padding:18px;border:1px solid #47d77d77;border-radius:12px;background:#0d1710cc;box-shadow:0 20px 60px #0008;backdrop-filter:blur(12px)}.signal-card p{display:inline;margin-left:8px;color:#b7c1bb;font:10px ui-monospace}.signal-card b{display:block;margin-top:12px;font-size:14px}.pulse{display:inline-block;width:8px;height:8px;border-radius:50%;background:#4ce18a;box-shadow:0 0 0 5px #4ce18a20}.statement{padding:118px 0 76px;border-top:1px solid #ffffff12}.statement h2,.callout h2,.faq h2{max-width:770px;margin:17px 0;font-size:clamp(34px,5vw,66px);line-height:.98;letter-spacing:-.055em}.statement>p:last-child,.callout>p:not(.eyebrow){max-width:570px;color:#9fa9a2;line-height:1.7}.safeguards{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:#ffffff14;border:1px solid #ffffff14}.safeguards article{min-height:245px;padding:29px;background:#0e1110}.safeguards small{color:#56dd8e;font:11px ui-monospace}.safeguards h3{margin:54px 0 11px;font-size:21px;letter-spacing:-.04em}.safeguards p{color:#99a39c;font-size:14px;line-height:1.6}.callout{margin-top:96px;padding:74px;position:relative;border:1px solid #ffffff16;border-radius:16px;background:linear-gradient(120deg,#153721,#0d1310 55%)}.callout h2{max-width:620px}.faq{display:grid;grid-template-columns:1fr 1.15fr;gap:80px;padding:122px 0}.faq h2{font-size:46px}.faq details{border-top:1px solid #ffffff1b}.faq details:last-child{border-bottom:1px solid #ffffff1b}.faq summary{display:flex;justify-content:space-between;gap:20px;padding:21px 0;cursor:pointer;font-size:16px;font-weight:650;list-style:none}.faq summary::-webkit-details-marker{display:none}.faq summary span{color:#55df8d;font-size:22px;font-weight:400}.faq details[open] summary span{transform:rotate(45deg)}.faq details p{max-width:520px;margin:0;padding:0 0 21px;color:#9fa9a2;line-height:1.7;font-size:14px}footer{display:flex;justify-content:space-between;padding:23px 0 35px;border-top:1px solid #ffffff14;color:#77827a;font:10px ui-monospace;letter-spacing:.07em}footer a{color:#b6c0b9}@media(max-width:700px){nav{height:68px}.links{display:none}.nav-cta{display:none}.hero{min-height:auto;padding:92px 0 74px}.hero h1{font-size:clamp(52px,15vw,78px)}.signal-card{position:relative;right:auto;bottom:auto;margin-top:54px}.statement{padding-top:76px}.safeguards,.faq{grid-template-columns:1fr}.safeguards article{min-height:190px}.callout{margin-top:60px;padding:40px 27px}.faq{gap:35px;padding:82px 0}footer{gap:15px}.actions{flex-wrap:wrap}}@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}'
69+
: ':root{font-family:system-ui;color:#eef3ef;background:#090b0a}body{margin:0}main{padding:10vw}button{padding:10px}'
6070
const files: Record<string, string> = {
6171
'app/package.json': `${JSON.stringify({ private: true, scripts: { dev: 'vite', build: 'tsc -b && vite build', preview: 'vite preview' }, dependencies: { react: '^19.2.7', 'react-dom': '^19.2.7' }, devDependencies: { '@types/react': '^19.2.17', '@types/react-dom': '^19.2.3', '@vitejs/plugin-react': '^6.0.3', typescript: '~6.0.2', vite: '^8.1.1' } }, null, 2)}\n`,
6272
'app/vite.config.ts': "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nexport default defineConfig({ plugins: [react()] })\n",

0 commit comments

Comments
 (0)