Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions apps/website/src/components/landing/EmbedFrame.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
'use client';
import { useState } from 'react';
import { tokens } from '@cacheplane/design-tokens';

interface EmbedFrameProps {
src: string;
title: string;
height?: number;
}

function ExpandIcon() {
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M6 2H2v4M14 10v4h-4M2 2l5 5M14 14l-5-5" />
</svg>
);
}

function CloseIcon() {
return (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M5 5l10 10M15 5L5 15" />
</svg>
);
}

export function EmbedFrame({ src, title, height = 400 }: EmbedFrameProps) {
const [fullscreen, setFullscreen] = useState(false);

if (fullscreen) {
return (
<div style={{
position: 'fixed', inset: 0, zIndex: 99999,
background: 'rgba(0,0,0,0.85)',
display: 'flex', flexDirection: 'column',
}}>
<div style={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '12px 16px',
background: 'rgba(255,255,255,0.06)',
borderBottom: '1px solid rgba(255,255,255,0.1)',
}}>
<span style={{
fontFamily: 'Inter, sans-serif', fontSize: 14, fontWeight: 500,
color: 'rgba(255,255,255,0.8)',
}}>
{title}
</span>
<button
onClick={() => setFullscreen(false)}
aria-label="Close fullscreen"
style={{
background: 'none', border: 'none', cursor: 'pointer',
color: 'rgba(255,255,255,0.6)', padding: 4,
}}
>
<CloseIcon />
</button>
</div>
<iframe
src={src}
title={title}
style={{ flex: 1, width: '100%', border: 'none', background: '#fff' }}
sandbox="allow-scripts allow-same-origin"
/>
</div>
);
}

return (
<div style={{ position: 'relative' }}>
<div style={{
borderTop: `1px solid ${tokens.glass.border}`,
background: 'rgba(0,0,0,0.02)',
margin: '0 -32px',
}}
className="embed-frame-mobile"
>
<iframe
src={src}
title={title}
style={{ width: '100%', height, border: 'none', display: 'block' }}
loading="lazy"
sandbox="allow-scripts allow-same-origin"
/>
</div>
<button
onClick={() => setFullscreen(true)}
aria-label="Expand to fullscreen"
style={{
position: 'absolute', top: 8, right: -24,
background: 'rgba(255,255,255,0.9)',
border: `1px solid ${tokens.glass.border}`,
borderRadius: 6, padding: '6px',
cursor: 'pointer', color: tokens.colors.textMuted,
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}
>
<ExpandIcon />
</button>
<style>{`
@media (min-width: 768px) {
.embed-frame-mobile { margin: 0 !important; }
}
`}</style>
</div>
);
}
2 changes: 1 addition & 1 deletion apps/website/src/components/landing/HighlightedCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function HighlightedCode({ code, lang = 'typescript' }: Highlighted
return (
<div
className="shiki"
style={{ margin: 0, borderRadius: 0 }}
style={{ margin: 0, borderRadius: 0, padding: '16px 20px', fontSize: '0.78rem', lineHeight: 1.65 }}
dangerouslySetInnerHTML={{ __html: html }}
/>
);
Expand Down
22 changes: 11 additions & 11 deletions apps/website/src/components/landing/angular/AngularComparison.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { motion } from 'framer-motion';
import { tokens } from '@cacheplane/design-tokens';

const ROWS = [
{ capability: 'SSE streaming', theirs: 'Manual wiring', ours: 'Signal-native via agent()' },
{ capability: 'State management', theirs: 'Custom signals', ours: 'Built-in reactive state' },
{ capability: 'Thread persistence', theirs: 'DIY localStorage', ours: 'Built-in threadId signal + restore' },
{ capability: 'Interrupt handling', theirs: 'Manual Command.RESUME', ours: 'interrupt() signal + approve/edit/cancel' },
{ capability: 'Tool call rendering', theirs: 'Raw events', ours: 'Structured tool call state' },
{ capability: 'Time travel', theirs: 'Not included', ours: 'Built-in state history' },
{ capability: 'Testing', theirs: 'Against live API', ours: 'MockStreamTransport, offline, <100ms' },
{ capability: 'OnPush compatibility', theirs: 'Requires workarounds', ours: 'Native signal support' },
{ capability: 'DeepAgent support', theirs: 'Not included', ours: 'Full multi-agent orchestration' },
{ capability: 'SSE streaming', theirs: 'Raw Client + for-await loop', ours: 'Signal-native via agent()' },
{ capability: 'State management', theirs: 'Manual BehaviorSubject → Signal', ours: 'Built-in reactive state' },
{ capability: 'Thread persistence', theirs: 'DIY localStorage + API calls', ours: 'Built-in threadId signal + restore' },
{ capability: 'Interrupt handling', theirs: 'Manual Command.RESUME wiring', ours: 'interrupt() signal + approve/edit/cancel' },
{ capability: 'Tool call rendering', theirs: 'Parse raw SSE events', ours: 'Structured tool call state' },
{ capability: 'Time travel', theirs: 'Build from scratch', ours: 'Built-in state history' },
{ capability: 'Testing', theirs: 'Against live API', ours: 'MockAgentTransport, offline, <100ms' },
{ capability: 'OnPush compatibility', theirs: 'Custom change detection workarounds', ours: 'Native signal support' },
{ capability: 'React parity', theirs: 'useStream() is React-only', ours: 'agent() — full useStream() parity for Angular' },
];

export function AngularComparison() {
Expand All @@ -37,7 +37,7 @@ export function AngularComparison() {
fontSize: 'clamp(26px,3.5vw,42px)', fontWeight: 800, lineHeight: 1.1,
color: tokens.colors.textPrimary,
}}>
LangGraph Angular SDK vs @cacheplane/angular
@langchain/langgraph-sdk vs @cacheplane/angular
</h2>
</motion.div>

Expand All @@ -57,7 +57,7 @@ export function AngularComparison() {
display: 'grid', gridTemplateColumns: 'minmax(100px, 1fr) minmax(120px, 1fr) minmax(120px, 1fr)',
background: 'rgba(255,255,255,.3)', borderBottom: `1px solid ${tokens.glass.border}`, padding: '14px 24px',
}}>
{['Capability', 'LangGraph Angular SDK', '@cacheplane/angular'].map((h, i) => (
{['Capability', '@langchain/langgraph-sdk', '@cacheplane/angular'].map((h, i) => (
<div key={h} style={{
fontFamily: 'var(--font-mono,"JetBrains Mono",monospace)',
fontSize: '0.62rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
'use client';
import { motion } from 'framer-motion';
import { tokens } from '@cacheplane/design-tokens';
import { EmbedFrame } from '../EmbedFrame';

const FEATURES = [
{ title: 'agent() API', desc: 'Signal-native streaming with automatic state management. One function to connect your Angular app to LangGraph.', iframePath: 'langgraph/core-capabilities/streaming/overview/python' },
Expand Down Expand Up @@ -69,15 +70,11 @@ export function AngularFeaturesGrid() {
{feat.desc}
</p>
</div>
<div style={{ borderTop: `1px solid ${tokens.glass.border}`, background: 'rgba(0,0,0,0.02)' }}>
<iframe
src={`https://cockpit.cacheplane.ai/${feat.iframePath}`}
title={feat.title}
style={{ width: '100%', height: 320, border: 'none', display: 'block' }}
loading="lazy"
sandbox="allow-scripts allow-same-origin"
/>
</div>
<EmbedFrame
src={`https://cockpit.cacheplane.ai/${feat.iframePath}`}
title={feat.title}
height={400}
/>
</motion.div>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { motion } from 'framer-motion';
import { tokens } from '@cacheplane/design-tokens';

const PAIN_POINTS = [
'Zone.js interference with SSE event streams',
'Manual subscription management and cleanup',
'Custom SSE wiring that breaks under load',
'Incompatibility with OnPush change detection',
'No deterministic test story for streaming',
'Raw Client + for-await SSE loop — no Angular integration',
'Manual BehaviorSubject → Signal wiring for each state slice',
'Thread persistence, interrupt handling built from scratch',
'No OnPush-compatible signal API — custom change detection workarounds',
'Testing against live LangGraph API — slow, flaky, non-deterministic',
];

const SOLUTIONS = [
Expand Down Expand Up @@ -42,13 +42,13 @@ export function AngularProblemSolution() {
fontSize: '0.58rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em',
padding: '2px 9px', borderRadius: 5, color: '#fff', background: '#b71c1c', marginBottom: 16,
}}>
Without @cacheplane/angular
With @langchain/langgraph-sdk alone
</span>
<h3 style={{
fontFamily: "'EB Garamond', serif", fontSize: '1.3rem', fontWeight: 700,
color: tokens.colors.textPrimary, lineHeight: 1.25, marginBottom: 16,
}}>
Your LangGraph agent works. Your Angular frontend doesn't stream it.
The JS SDK gives you a Client. You build everything else.
</h3>
<ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: 10 }}>
{PAIN_POINTS.map(p => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
'use client';
import { motion } from 'framer-motion';
import { tokens } from '@cacheplane/design-tokens';
import { EmbedFrame } from '../EmbedFrame';

const FEATURES = [
{ title: 'Messages', desc: 'Streaming message display with token-by-token rendering. Markdown support, auto-scroll, and accessible message list.', iframePath: 'chat/core-capabilities/messages/overview/python' },
Expand Down Expand Up @@ -68,15 +69,11 @@ export function ChatLandingFeaturesGrid() {
{feat.desc}
</p>
</div>
<div style={{ borderTop: `1px solid ${tokens.glass.border}`, background: 'rgba(0,0,0,0.02)' }}>
<iframe
src={`https://cockpit.cacheplane.ai/${feat.iframePath}`}
title={feat.title}
style={{ width: '100%', height: 320, border: 'none', display: 'block' }}
loading="lazy"
sandbox="allow-scripts allow-same-origin"
/>
</div>
<EmbedFrame
src={`https://cockpit.cacheplane.ai/${feat.iframePath}`}
title={feat.title}
height={400}
/>
</motion.div>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
'use client';
import { motion } from 'framer-motion';
import { tokens } from '@cacheplane/design-tokens';
import { EmbedFrame } from '../EmbedFrame';

const FEATURES = [
{ title: 'Spec Rendering', desc: 'Render declarative UI specs from agent output. The agent emits JSON, your Angular components materialize it.', iframePath: 'render/core-capabilities/spec-rendering/overview/python' },
Expand Down Expand Up @@ -69,15 +70,11 @@ export function RenderFeaturesGrid() {
{feat.desc}
</p>
</div>
<div style={{ borderTop: `1px solid ${tokens.glass.border}`, background: 'rgba(0,0,0,0.02)' }}>
<iframe
src={`https://cockpit.cacheplane.ai/${feat.iframePath}`}
title={feat.title}
style={{ width: '100%', height: 320, border: 'none', display: 'block' }}
loading="lazy"
sandbox="allow-scripts allow-same-origin"
/>
</div>
<EmbedFrame
src={`https://cockpit.cacheplane.ai/${feat.iframePath}`}
title={feat.title}
height={400}
/>
</motion.div>
))}
</div>
Expand Down
Loading